blob: c715ba0e782f6aa800f82a963e7433abe3217ce7 [file] [log] [blame]
Michael Walsh70369fd2016-11-22 11:25:57 -06001#!/usr/bin/env python
2
3r"""
4This module contains functions having to do with machine state: get_state,
5check_state, wait_state, etc.
6
7The 'State' is a composite of many pieces of data. Therefore, the functions
8in this module define state as an ordered dictionary. Here is an example of
9some test output showing machine state:
10
Michael Walsh341c21e2017-01-17 16:25:20 -060011default_state:
Michael Walsh65b12542017-02-03 15:34:38 -060012 default_state[chassis]: On
Michael Walsh01975fa2017-08-20 20:51:36 -050013 default_state[boot_progress]: OSStart
Michael Walsh56749222017-09-29 15:26:07 -050014 default_state[operating_system]: BootComplete
Michael Walsh65b12542017-02-03 15:34:38 -060015 default_state[host]: Running
Michael Walsh341c21e2017-01-17 16:25:20 -060016 default_state[os_ping]: 1
17 default_state[os_login]: 1
18 default_state[os_run_cmd]: 1
Michael Walsh70369fd2016-11-22 11:25:57 -060019
20Different users may very well have different needs when inquiring about
Michael Walsh8fae6ea2017-02-20 16:14:44 -060021state. Support for new pieces of state information may be added to this
22module as needed.
Michael Walsh70369fd2016-11-22 11:25:57 -060023
24By using the wait_state function, a caller can start a boot and then wait for
25a precisely defined state to indicate that the boot has succeeded. If
26the boot fails, they can see exactly why by looking at the current state as
27compared with the expected state.
28"""
29
30import gen_print as gp
Michael Walsh70369fd2016-11-22 11:25:57 -060031import gen_valid as gv
Michael Walsh16cbb7f2017-02-02 15:54:16 -060032import gen_robot_utils as gru
Michael Walsh8fae6ea2017-02-20 16:14:44 -060033import gen_cmd as gc
Michael Walsh6a9bd142018-07-24 16:10:29 -050034import bmc_ssh_utils as bsu
Michael Walsh70369fd2016-11-22 11:25:57 -060035
Michael Walsh70369fd2016-11-22 11:25:57 -060036from robot.libraries.BuiltIn import BuiltIn
Michael Walsh341c21e2017-01-17 16:25:20 -060037from robot.utils import DotDict
Michael Walsh70369fd2016-11-22 11:25:57 -060038
39import re
Michael Walsh341c21e2017-01-17 16:25:20 -060040import os
Michael Walsh56749222017-09-29 15:26:07 -050041import sys
42import imp
43
Michael Walsh70369fd2016-11-22 11:25:57 -060044
Michael Walsh5a5868a2018-10-31 15:20:04 -050045# NOTE: Avoid importing utils.robot because utils.robot imports state.py
46# (indirectly) which will cause failures.
47gru.my_import_resource("rest_client.robot")
Michael Walsh70369fd2016-11-22 11:25:57 -060048
Michael Walsh56749222017-09-29 15:26:07 -050049base_path = os.path.dirname(os.path.dirname(
50 imp.find_module("gen_robot_print")[1])) + os.sep
51sys.path.append(base_path + "data/")
Michael Walsh56749222017-09-29 15:26:07 -050052
Michael Walsh940d6912017-10-27 12:32:33 -050053# Previously, I had this coded:
54# import variables as var
55# However, we ran into a problem where a robot program did this...
56# Variables ../../lib/ras/variables.py
57# Prior to doing this...
58# Library ../lib/state.py
59
60# This caused the wrong variables.py file to be selected. Attempts to fix this
61# have failed so far. For the moment, we will hard-code the value we need from
62# the file.
63
64SYSTEM_STATE_URI = "/xyz/openbmc_project/state/"
Michael Walsh56749222017-09-29 15:26:07 -050065
Michael Walsh8fae6ea2017-02-20 16:14:44 -060066# The BMC code has recently been changed as far as what states are defined and
67# what the state values can be. This module now has a means of processing both
68# the old style state (i.e. OBMC_STATES_VERSION = 0) and the new style (i.e.
Michael Walsh16cbb7f2017-02-02 15:54:16 -060069# OBMC_STATES_VERSION = 1).
Michael Walsh341c21e2017-01-17 16:25:20 -060070# The caller can set environment variable OBMC_STATES_VERSION to dictate
71# whether we're processing old or new style states. If OBMC_STATES_VERSION is
Michael Walsh8fae6ea2017-02-20 16:14:44 -060072# not set it will default to 1.
Michael Walsh341c21e2017-01-17 16:25:20 -060073
Michael Walsh619aa332017-04-12 15:56:51 -050074# As of the present moment, OBMC_STATES_VERSION of 0 is for cold that is so old
75# that it is no longer worthwhile to maintain. The OBMC_STATES_VERSION 0 code
76# is being removed but the OBMC_STATES_VERSION value will stay for now in the
77# event that it is needed in the future.
78
Michael Walsh8fae6ea2017-02-20 16:14:44 -060079OBMC_STATES_VERSION = int(os.environ.get('OBMC_STATES_VERSION', 1))
Michael Walsh341c21e2017-01-17 16:25:20 -060080
Michael Sheposda40c1d2020-12-01 22:30:00 -060081redfish_support_trans_state = int(os.environ.get('REDFISH_SUPPORT_TRANS_STATE', 0)) or \
82 int(BuiltIn().get_variable_value("${REDFISH_SUPPORT_TRANS_STATE}", default=0))
83
Konstantin Aladyshevc056c2b2021-03-25 11:59:39 +030084platform_arch_type = os.environ.get('PLATFORM_ARCH_TYPE', '') or \
85 BuiltIn().get_variable_value("${PLATFORM_ARCH_TYPE}", default="power")
George Keishing1e2fbee2021-03-19 11:19:29 -050086
Michael Walsh8fae6ea2017-02-20 16:14:44 -060087# valid_os_req_states and default_os_req_states are used by the os_get_state
88# function.
89# valid_os_req_states is a list of state information supported by the
90# get_os_state function.
91valid_os_req_states = ['os_ping',
92 'os_login',
93 'os_run_cmd']
Michael Sheposbdd1dce2020-12-10 11:51:58 -060094
Michael Walsh8fae6ea2017-02-20 16:14:44 -060095# When a user calls get_os_state w/o specifying req_states,
96# default_os_req_states is used as its value.
97default_os_req_states = ['os_ping',
98 'os_login',
99 'os_run_cmd']
100
101# Presently, some BMCs appear to not keep time very well. This environment
102# variable directs the get_state function to use either the BMC's epoch time
103# or the local epoch time.
104USE_BMC_EPOCH_TIME = int(os.environ.get('USE_BMC_EPOCH_TIME', 0))
Michael Walsh341c21e2017-01-17 16:25:20 -0600105
Michael Walsh619aa332017-04-12 15:56:51 -0500106# Useful state constant definition(s).
Michael Sheposda40c1d2020-12-01 22:30:00 -0600107if not redfish_support_trans_state:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600108 # When a user calls get_state w/o specifying req_states, default_req_states
109 # is used as its value.
110 default_req_states = ['rest',
111 'chassis',
112 'bmc',
113 'boot_progress',
114 'operating_system',
115 'host',
116 'os_ping',
117 'os_login',
118 'os_run_cmd']
119
120 # valid_req_states is a list of sub states supported by the get_state function.
121 # valid_req_states, default_req_states and master_os_up_match are used by the
122 # get_state function.
123
124 valid_req_states = ['ping',
125 'packet_loss',
126 'uptime',
127 'epoch_seconds',
128 'elapsed_boot_time',
129 'rest',
130 'chassis',
131 'requested_chassis',
132 'bmc',
133 'requested_bmc',
134 'boot_progress',
135 'operating_system',
136 'host',
137 'requested_host',
138 'attempts_left',
139 'os_ping',
140 'os_login',
141 'os_run_cmd']
142
Michael Sheposda40c1d2020-12-01 22:30:00 -0600143 # default_state is an initial value which may be of use to callers.
144 default_state = DotDict([('rest', '1'),
145 ('chassis', 'On'),
146 ('bmc', 'Ready'),
147 ('boot_progress', 'OSStart'),
148 ('operating_system', 'BootComplete'),
149 ('host', 'Running'),
150 ('os_ping', '1'),
151 ('os_login', '1'),
152 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500153
Michael Sheposda40c1d2020-12-01 22:30:00 -0600154 # A match state for checking that the system is at "standby".
155 standby_match_state = DotDict([('rest', '^1$'),
156 ('chassis', '^Off$'),
157 ('bmc', '^Ready$'),
158 ('boot_progress', '^Off|Unspecified$'),
159 ('operating_system', '^Inactive$'),
160 ('host', '^Off$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500161
Michael Sheposda40c1d2020-12-01 22:30:00 -0600162 # A match state for checking that the system is at "os running".
163 os_running_match_state = DotDict([('chassis', '^On$'),
164 ('bmc', '^Ready$'),
165 ('boot_progress',
166 'FW Progress, Starting OS|OSStart'),
167 ('operating_system', 'BootComplete'),
168 ('host', '^Running$'),
169 ('os_ping', '^1$'),
170 ('os_login', '^1$'),
171 ('os_run_cmd', '^1$')])
172
173 # A master dictionary to determine whether the os may be up.
174 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Walsh7dc885b2018-03-14 17:51:59 -0500175 ('bmc', '^Ready$'),
176 ('boot_progress',
177 'FW Progress, Starting OS|OSStart'),
178 ('operating_system', 'BootComplete'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600179 ('host', '^Running|Quiesced$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500180
Michael Sheposda40c1d2020-12-01 22:30:00 -0600181 invalid_state_match = DotDict([('rest', '^$'),
182 ('chassis', '^$'),
183 ('bmc', '^$'),
184 ('boot_progress', '^$'),
185 ('operating_system', '^$'),
186 ('host', '^$')])
187else:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600188 # When a user calls get_state w/o specifying req_states, default_req_states
189 # is used as its value.
190 default_req_states = ['redfish',
191 'chassis',
192 'bmc',
193 'boot_progress',
194 'host',
195 'os_ping',
196 'os_login',
197 'os_run_cmd']
198
199 # valid_req_states is a list of sub states supported by the get_state function.
200 # valid_req_states, default_req_states and master_os_up_match are used by the
201 # get_state function.
202
203 valid_req_states = ['ping',
204 'packet_loss',
205 'uptime',
206 'epoch_seconds',
207 'elapsed_boot_time',
208 'redfish',
209 'chassis',
210 'requested_chassis',
211 'bmc',
212 'requested_bmc',
213 'boot_progress',
214 'host',
215 'requested_host',
216 'attempts_left',
217 'os_ping',
218 'os_login',
219 'os_run_cmd']
220
Michael Sheposda40c1d2020-12-01 22:30:00 -0600221 # default_state is an initial value which may be of use to callers.
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600222 default_state = DotDict([('redfish', '1'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600223 ('chassis', 'On'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600224 ('bmc', 'Enabled'),
George Keishing89c98372021-08-13 08:15:10 -0500225 ('boot_progress',
226 'SystemHardwareInitializationComplete|OSBootStarted|OSRunning'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600227 ('host', 'Enabled'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600228 ('os_ping', '1'),
229 ('os_login', '1'),
230 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500231
Michael Sheposda40c1d2020-12-01 22:30:00 -0600232 # A match state for checking that the system is at "standby".
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600233 standby_match_state = DotDict([('redfish', '^1$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600234 ('chassis', '^Off$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600235 ('bmc', '^Enabled$'),
236 ('boot_progress', '^None$'),
237 ('host', '^Disabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600238
239 # A match state for checking that the system is at "os running".
240 os_running_match_state = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600241 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600242 ('boot_progress',
George Keishing89c98372021-08-13 08:15:10 -0500243 'SystemHardwareInitializationComplete|OSBootStarted|OSRunning'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600244 ('host', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600245 ('os_ping', '^1$'),
246 ('os_login', '^1$'),
247 ('os_run_cmd', '^1$')])
248
249 # A master dictionary to determine whether the os may be up.
250 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600251 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600252 ('boot_progress',
George Keishing89c98372021-08-13 08:15:10 -0500253 'SystemHardwareInitializationComplete|OSBootStarted|OSRunning'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600254 ('host', '^Enabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600255
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600256 invalid_state_match = DotDict([('redfish', '^$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600257 ('chassis', '^$'),
258 ('bmc', '^$'),
259 ('boot_progress', '^$'),
260 ('host', '^$')])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500261
George Keishing1e2fbee2021-03-19 11:19:29 -0500262# Filter the states based on platform type.
263if platform_arch_type == "x86":
264 default_req_states.remove("operating_system")
265 default_req_states.remove("boot_progress")
266 valid_req_states.remove("operating_system")
267 valid_req_states.remove("boot_progress")
268 del default_state["operating_system"]
269 del default_state["boot_progress"]
270 del standby_match_state["operating_system"]
271 del standby_match_state["boot_progress"]
272 del os_running_match_state["operating_system"]
273 del os_running_match_state["boot_progress"]
274 del master_os_up_match["operating_system"]
275 del master_os_up_match["boot_progress"]
276 del invalid_state_match["operating_system"]
277 del invalid_state_match["boot_progress"]
278
Michael Walsh341c21e2017-01-17 16:25:20 -0600279
George Keishing36efbc02018-12-12 10:18:23 -0600280def return_state_constant(state_name='default_state'):
Michael Walsh619aa332017-04-12 15:56:51 -0500281 r"""
Michael Walsh7dc885b2018-03-14 17:51:59 -0500282 Return the named state dictionary constant.
Michael Walsh619aa332017-04-12 15:56:51 -0500283 """
284
George Keishing36efbc02018-12-12 10:18:23 -0600285 return eval(state_name)
Michael Walsh619aa332017-04-12 15:56:51 -0500286
Michael Walsh619aa332017-04-12 15:56:51 -0500287
Michael Walsh70369fd2016-11-22 11:25:57 -0600288def anchor_state(state):
Michael Walsh70369fd2016-11-22 11:25:57 -0600289 r"""
290 Add regular expression anchors ("^" and "$") to the beginning and end of
291 each item in the state dictionary passed in. Return the resulting
292 dictionary.
293
Michael Walsh2a0df682019-09-27 17:19:27 -0500294 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600295 state A dictionary such as the one returned by the get_state()
296 function.
297 """
298
Michael Walsh2ce067a2017-02-27 14:24:07 -0600299 anchored_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500300 for key in anchored_state.keys():
Michael Walsh70369fd2016-11-22 11:25:57 -0600301 anchored_state[key] = "^" + str(anchored_state[key]) + "$"
302
303 return anchored_state
304
Michael Walsh70369fd2016-11-22 11:25:57 -0600305
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600306def strip_anchor_state(state):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600307 r"""
308 Strip regular expression anchors ("^" and "$") from the beginning and end
309 of each item in the state dictionary passed in. Return the resulting
310 dictionary.
311
Michael Walsh2a0df682019-09-27 17:19:27 -0500312 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600313 state A dictionary such as the one returned by the get_state()
314 function.
315 """
316
Michael Walsh2ce067a2017-02-27 14:24:07 -0600317 stripped_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500318 for key in stripped_state.keys():
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600319 stripped_state[key] = stripped_state[key].strip("^$")
320
321 return stripped_state
322
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600323
Michael Walsh2a0df682019-09-27 17:19:27 -0500324def expressions_key():
325 r"""
326 Return expressions key constant.
327 """
328 return '<expressions>'
329
330
Michael Walsh70369fd2016-11-22 11:25:57 -0600331def compare_states(state,
Michael Walsh45ca6e42017-09-14 17:29:12 -0500332 match_state,
333 match_type='and'):
Michael Walsh70369fd2016-11-22 11:25:57 -0600334 r"""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600335 Compare 2 state dictionaries. Return True if they match and False if they
Michael Walsh70369fd2016-11-22 11:25:57 -0600336 don't. Note that the match_state dictionary does not need to have an entry
337 corresponding to each entry in the state dictionary. But for each entry
338 that it does have, the corresponding state entry will be checked for a
339 match.
340
Michael Walsh2a0df682019-09-27 17:19:27 -0500341 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600342 state A state dictionary such as the one returned by the
343 get_state function.
344 match_state A dictionary whose key/value pairs are "state field"/
345 "state value". The state value is interpreted as a
346 regular expression. Every value in this dictionary is
Michael Walsh45ca6e42017-09-14 17:29:12 -0500347 considered. When match_type is 'and', if each and every
348 comparison matches, the two dictionaries are considered to
349 be matching. If match_type is 'or', if any two of the
350 elements compared match, the two dictionaries are
351 considered to be matching.
Michael Walsh2a0df682019-09-27 17:19:27 -0500352
Michael Walsh7dc885b2018-03-14 17:51:59 -0500353 This value may also be any string accepted by
Michael Walsh2a0df682019-09-27 17:19:27 -0500354 return_state_constant (e.g. "standby_match_state"). In
355 such a case this function will call return_state_constant
356 to convert it to a proper dictionary as described above.
357
358 Finally, one special value is accepted for the key field:
359 expression_key(). If such an entry exists, its value is
360 taken to be a list of expressions to be evaluated. These
361 expressions may reference state dictionary entries by
362 simply coding them in standard python syntax (e.g.
363 state['key1']). What follows is an example expression:
364
365 "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
366
367 In this example, if the state dictionary's 'uptime' entry
368 is less than its 'elapsed_boot_time' entry, it would
369 qualify as a match.
Michael Walsh45ca6e42017-09-14 17:29:12 -0500370 match_type This may be 'and' or 'or'.
Michael Walsh70369fd2016-11-22 11:25:57 -0600371 """
372
Michael Walsh2a0df682019-09-27 17:19:27 -0500373 error_message = gv.valid_value(match_type, valid_values=['and', 'or'])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500374 if error_message != "":
375 BuiltIn().fail(gp.sprint_error(error_message))
376
George Keishing36efbc02018-12-12 10:18:23 -0600377 try:
Michael Walsh7dc885b2018-03-14 17:51:59 -0500378 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600379 except TypeError:
380 pass
Michael Walsh7dc885b2018-03-14 17:51:59 -0500381
Michael Walsh45ca6e42017-09-14 17:29:12 -0500382 default_match = (match_type == 'and')
Michael Walsh70369fd2016-11-22 11:25:57 -0600383 for key, match_state_value in match_state.items():
Michael Walsh97df71c2017-03-27 14:33:24 -0500384 # Blank match_state_value means "don't care".
385 if match_state_value == "":
386 continue
Michael Walsh2a0df682019-09-27 17:19:27 -0500387 if key == expressions_key():
388 for expr in match_state_value:
389 # Use python interpreter to evaluate the expression.
390 match = eval(expr)
391 if match != default_match:
392 return match
393 else:
394 try:
395 match = (re.match(match_state_value, str(state[key])) is not None)
396 except KeyError:
397 match = False
398 if match != default_match:
399 return match
Michael Walsh45ca6e42017-09-14 17:29:12 -0500400
401 return default_match
Michael Walsh70369fd2016-11-22 11:25:57 -0600402
Michael Walsh70369fd2016-11-22 11:25:57 -0600403
Michael Walsh70369fd2016-11-22 11:25:57 -0600404def get_os_state(os_host="",
405 os_username="",
406 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600407 req_states=default_os_req_states,
408 os_up=True,
Michael Walsh70369fd2016-11-22 11:25:57 -0600409 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600410 r"""
411 Get component states for the operating system such as ping, login,
412 etc, put them into a dictionary and return them to the caller.
413
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600414 Note that all substate values are strings.
415
Michael Walsh2a0df682019-09-27 17:19:27 -0500416 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600417 os_host The DNS name or IP address of the operating system.
418 This defaults to global ${OS_HOST}.
419 os_username The username to be used to login to the OS.
420 This defaults to global ${OS_USERNAME}.
421 os_password The password to be used to login to the OS.
422 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600423 req_states This is a list of states whose values are being requested by
424 the caller.
425 os_up If the caller knows that the os can't possibly be up, it can
426 improve performance by passing os_up=False. This function
427 will then simply return default values for all requested os
428 sub states.
Michael Walsh70369fd2016-11-22 11:25:57 -0600429 quiet Indicates whether status details (e.g. curl commands) should
430 be written to the console.
431 Defaults to either global value of ${QUIET} or to 1.
432 """
433
Michael Walsh619aa332017-04-12 15:56:51 -0500434 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600435
436 # Set parm defaults where necessary and validate all parms.
437 if os_host == "":
438 os_host = BuiltIn().get_variable_value("${OS_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500439 error_message = gv.valid_value(os_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600440 if error_message != "":
441 BuiltIn().fail(gp.sprint_error(error_message))
442
443 if os_username == "":
444 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500445 error_message = gv.valid_value(os_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600446 if error_message != "":
447 BuiltIn().fail(gp.sprint_error(error_message))
448
449 if os_password == "":
450 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500451 error_message = gv.valid_value(os_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600452 if error_message != "":
453 BuiltIn().fail(gp.sprint_error(error_message))
454
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600455 invalid_req_states = [sub_state for sub_state in req_states
456 if sub_state not in valid_os_req_states]
457 if len(invalid_req_states) > 0:
458 error_message = "The following req_states are not supported:\n" +\
459 gp.sprint_var(invalid_req_states)
460 BuiltIn().fail(gp.sprint_error(error_message))
Michael Walsh70369fd2016-11-22 11:25:57 -0600461
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600462 # Initialize all substate values supported by this function.
463 os_ping = 0
464 os_login = 0
465 os_run_cmd = 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600466
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600467 if os_up:
468 if 'os_ping' in req_states:
469 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600470 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + os_host,
471 print_output=0, show_err=0,
472 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600473 if rc == 0:
474 os_ping = 1
Michael Walsh70369fd2016-11-22 11:25:57 -0600475
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600476 # Programming note: All attributes which do not require an ssh login
477 # should have been processed by this point.
478 master_req_login = ['os_login', 'os_run_cmd']
479 req_login = [sub_state for sub_state in req_states if sub_state in
480 master_req_login]
Michael Walsh97df71c2017-03-27 14:33:24 -0500481 must_login = (len(req_login) > 0)
Michael Walsh70369fd2016-11-22 11:25:57 -0600482
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600483 if must_login:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500484 output, stderr, rc = bsu.os_execute_command("uptime", quiet=quiet,
Michael Walsh7fc33972018-08-07 14:55:03 -0500485 ignore_err=1,
Michael Shepos3390c852021-02-11 00:12:31 -0600486 time_out=20,
487 os_host=os_host,
488 os_username=os_username,
489 os_password=os_password)
Michael Walsh6a9bd142018-07-24 16:10:29 -0500490 if rc == 0:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600491 os_login = 1
Michael Walsh6a9bd142018-07-24 16:10:29 -0500492 os_run_cmd = 1
Michael Walsh3eb50022017-03-21 11:27:30 -0500493 else:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500494 gp.dprint_vars(output, stderr)
495 gp.dprint_vars(rc, 1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600496
497 os_state = DotDict()
498 for sub_state in req_states:
499 cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")"
500 exec(cmd_buf)
Michael Walsh70369fd2016-11-22 11:25:57 -0600501
502 return os_state
503
Michael Walsh70369fd2016-11-22 11:25:57 -0600504
Michael Walsh70369fd2016-11-22 11:25:57 -0600505def get_state(openbmc_host="",
506 openbmc_username="",
507 openbmc_password="",
508 os_host="",
509 os_username="",
510 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600511 req_states=default_req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600512 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600513 r"""
Michael Walsh619aa332017-04-12 15:56:51 -0500514 Get component states such as chassis state, bmc state, etc, put them into a
Michael Walsh70369fd2016-11-22 11:25:57 -0600515 dictionary and return them to the caller.
516
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600517 Note that all substate values are strings.
518
Michael Walsh2a0df682019-09-27 17:19:27 -0500519 Note: If elapsed_boot_time is included in req_states, it is the caller's
520 duty to call set_start_boot_seconds() in order to set global
521 start_boot_seconds. elapsed_boot_time is the current time minus
522 start_boot_seconds.
523
524 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600525 openbmc_host The DNS name or IP address of the BMC.
526 This defaults to global ${OPENBMC_HOST}.
527 openbmc_username The username to be used to login to the BMC.
528 This defaults to global ${OPENBMC_USERNAME}.
529 openbmc_password The password to be used to login to the BMC.
530 This defaults to global ${OPENBMC_PASSWORD}.
531 os_host The DNS name or IP address of the operating system.
532 This defaults to global ${OS_HOST}.
533 os_username The username to be used to login to the OS.
534 This defaults to global ${OS_USERNAME}.
535 os_password The password to be used to login to the OS.
536 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600537 req_states This is a list of states whose values are being requested
538 by the caller.
Michael Walsh70369fd2016-11-22 11:25:57 -0600539 quiet Indicates whether status details (e.g. curl commands)
540 should be written to the console.
541 Defaults to either global value of ${QUIET} or to 1.
542 """
543
Michael Walsh619aa332017-04-12 15:56:51 -0500544 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600545
546 # Set parm defaults where necessary and validate all parms.
547 if openbmc_host == "":
548 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500549 error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600550 if error_message != "":
551 BuiltIn().fail(gp.sprint_error(error_message))
552
553 if openbmc_username == "":
554 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500555 error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600556 if error_message != "":
557 BuiltIn().fail(gp.sprint_error(error_message))
558
559 if openbmc_password == "":
560 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500561 error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600562 if error_message != "":
563 BuiltIn().fail(gp.sprint_error(error_message))
564
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600565 # NOTE: OS parms are optional.
Michael Walsh70369fd2016-11-22 11:25:57 -0600566 if os_host == "":
567 os_host = BuiltIn().get_variable_value("${OS_HOST}")
568 if os_host is None:
569 os_host = ""
570
571 if os_username is "":
572 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
573 if os_username is None:
574 os_username = ""
575
576 if os_password is "":
577 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
578 if os_password is None:
579 os_password = ""
580
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600581 invalid_req_states = [sub_state for sub_state in req_states
582 if sub_state not in valid_req_states]
583 if len(invalid_req_states) > 0:
584 error_message = "The following req_states are not supported:\n" +\
585 gp.sprint_var(invalid_req_states)
586 BuiltIn().fail(gp.sprint_error(error_message))
587
588 # Initialize all substate values supported by this function.
589 ping = 0
590 packet_loss = ''
591 uptime = ''
592 epoch_seconds = ''
Michael Walsh2a0df682019-09-27 17:19:27 -0500593 elapsed_boot_time = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500594 rest = ''
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600595 redfish = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500596 chassis = ''
597 requested_chassis = ''
598 bmc = ''
599 requested_bmc = ''
George Keishing18f15dd2021-03-09 11:17:35 -0600600 # BootProgress state will get populated when state logic enumerates the
601 # state URI. This is to prevent state dictionary boot_progress value
602 # getting empty when the BootProgress is NOT found, making it optional.
603 boot_progress = 'NA'
Michael Walsh2b269de2017-10-09 11:18:11 -0500604 operating_system = ''
605 host = ''
606 requested_host = ''
607 attempts_left = ''
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600608
Michael Walsh70369fd2016-11-22 11:25:57 -0600609 # Get the component states.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600610 if 'ping' in req_states:
611 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600612 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + openbmc_host,
613 print_output=0, show_err=0,
614 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600615 if rc == 0:
616 ping = 1
617
618 if 'packet_loss' in req_states:
619 # See if the OS pings.
620 cmd_buf = "ping -c 5 -w 5 " + openbmc_host +\
621 " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'"
George Keishing36efbc02018-12-12 10:18:23 -0600622 rc, out_buf = gc.shell_cmd(cmd_buf,
623 print_output=0, show_err=0,
624 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600625 if rc == 0:
626 packet_loss = out_buf.rstrip("\n")
627
Michael Walshe53e47a2017-06-30 17:03:24 -0500628 if 'uptime' in req_states:
Michael Walshfa765932017-10-13 14:07:22 -0500629 # Sometimes reading uptime results in a blank value. Call with
630 # wait_until_keyword_succeeds to ensure a non-blank value is obtained.
631 remote_cmd_buf = "read uptime filler 2>/dev/null < /proc/uptime" +\
632 " && [ ! -z \"${uptime}\" ] && echo ${uptime}"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500633 cmd_buf = ["BMC Execute Command",
Michael Walsh888d85a2019-04-18 11:03:28 -0500634 re.sub('\\$', '\\$', remote_cmd_buf), 'quiet=1',
George Keishing4361d8d2021-03-24 02:59:52 -0500635 'test_mode=0', 'time_out=5']
Michael Walsh2a0df682019-09-27 17:19:27 -0500636 gp.qprint_issuing(cmd_buf, 0)
637 gp.qprint_issuing(remote_cmd_buf, 0)
Michael Walshfa765932017-10-13 14:07:22 -0500638 try:
639 stdout, stderr, rc =\
George Keishing4361d8d2021-03-24 02:59:52 -0500640 BuiltIn().wait_until_keyword_succeeds("10 sec", "5 sec",
Michael Walshfa765932017-10-13 14:07:22 -0500641 *cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500642 if rc == 0 and stderr == "":
643 uptime = stdout
Michael Walshfa765932017-10-13 14:07:22 -0500644 except AssertionError as my_assertion_error:
645 pass
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600646
Michael Walsh2a0df682019-09-27 17:19:27 -0500647 if 'epoch_seconds' in req_states or 'elapsed_boot_time' in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600648 date_cmd_buf = "date -u +%s"
649 if USE_BMC_EPOCH_TIME:
Michael Walshe53e47a2017-06-30 17:03:24 -0500650 cmd_buf = ["BMC Execute Command", date_cmd_buf, 'quiet=${1}']
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600651 if not quiet:
Michael Walshedb5c942019-03-28 12:40:50 -0500652 gp.print_issuing(cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500653 status, ret_values = \
654 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
655 if status == "PASS":
656 stdout, stderr, rc = ret_values
657 if rc == 0 and stderr == "":
658 epoch_seconds = stdout.rstrip("\n")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600659 else:
660 shell_rc, out_buf = gc.cmd_fnc_u(date_cmd_buf,
Michael Walshfa765932017-10-13 14:07:22 -0500661 quiet=quiet,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600662 print_output=0)
663 if shell_rc == 0:
664 epoch_seconds = out_buf.rstrip("\n")
665
Michael Walsh2a0df682019-09-27 17:19:27 -0500666 if 'elapsed_boot_time' in req_states:
667 global start_boot_seconds
668 elapsed_boot_time = int(epoch_seconds) - start_boot_seconds
669
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600670 if not redfish_support_trans_state:
671 master_req_rest = ['rest', 'host', 'requested_host', 'operating_system',
672 'attempts_left', 'boot_progress', 'chassis',
673 'requested_chassis' 'bmc' 'requested_bmc']
Michael Walsh56749222017-09-29 15:26:07 -0500674
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600675 req_rest = [sub_state for sub_state in req_states if sub_state in
676 master_req_rest]
677 need_rest = (len(req_rest) > 0)
678 state = DotDict()
679 if need_rest:
680 cmd_buf = ["Read Properties", SYSTEM_STATE_URI + "enumerate",
681 "quiet=${" + str(quiet) + "}", "timeout=30"]
682 gp.dprint_issuing(cmd_buf)
683 status, ret_values = \
684 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
685 if status == "PASS":
686 state['rest'] = '1'
687 else:
688 state['rest'] = '0'
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600689
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600690 if int(state['rest']):
691 for url_path in ret_values:
George Keishing3adda952021-02-11 13:23:51 -0600692 # Skip conflicting "CurrentHostState" URL from the enum
693 # /xyz/openbmc_project/state/hypervisor0
694 if "hypervisor0" in url_path:
695 continue
696
Konstantin Aladyshev8c9cf9c2021-03-21 23:33:40 +0300697 if platform_arch_type == "x86":
698 # Skip conflicting "CurrentPowerState" URL from the enum
699 # /xyz/openbmc_project/state/chassis_system0
700 if "chassis_system0" in url_path:
701 continue
702
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600703 for attr_name in ret_values[url_path]:
704 # Create a state key value based on the attr_name.
705 try:
706 ret_values[url_path][attr_name] = \
707 re.sub(r'.*\.', "",
708 ret_values[url_path][attr_name])
709 except TypeError:
710 pass
711 # Do some key name manipulations.
712 new_attr_name = re.sub(r'^Current|(State|Transition)$',
713 "", attr_name)
714 new_attr_name = re.sub(r'BMC', r'Bmc', new_attr_name)
715 new_attr_name = re.sub(r'([A-Z][a-z])', r'_\1',
716 new_attr_name)
717 new_attr_name = new_attr_name.lower().lstrip("_")
718 new_attr_name = re.sub(r'power', r'chassis', new_attr_name)
719 if new_attr_name in req_states:
720 state[new_attr_name] = ret_values[url_path][attr_name]
721 else:
722 master_req_rf = ['redfish', 'host', 'requested_host',
723 'attempts_left', 'boot_progress', 'chassis',
724 'requested_chassis' 'bmc' 'requested_bmc']
725
726 req_rf = [sub_state for sub_state in req_states if sub_state in
727 master_req_rf]
728 need_rf = (len(req_rf) > 0)
729 state = DotDict()
730 if need_rf:
731 cmd_buf = ["Redfish Get States"]
732 gp.dprint_issuing(cmd_buf)
George Keishing21e28b42021-05-27 08:10:29 -0500733 try:
734 status, ret_values = \
735 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
736 except Exception as ex:
737 # Robot raised UserKeywordExecutionFailed error exception.
738 gp.dprint_issuing("Retrying Redfish Get States")
739 status, ret_values = \
740 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
741
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600742 gp.dprint_vars(status, ret_values)
743 if status == "PASS":
744 state['redfish'] = '1'
745 else:
746 state['redfish'] = '0'
747
748 if int(state['redfish']):
749 state['chassis'] = ret_values['chassis']
750 state['boot_progress'] = ret_values['boot_progress']
751 state['host'] = ret_values['host']
752 state['bmc'] = ret_values['bmc']
Michael Walshb95eb542017-03-31 09:39:20 -0500753
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600754 for sub_state in req_states:
Michael Walsh56749222017-09-29 15:26:07 -0500755 if sub_state in state:
756 continue
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600757 if sub_state.startswith("os_"):
758 # We pass "os_" requests on to get_os_state.
759 continue
760 cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")"
761 exec(cmd_buf)
762
763 if os_host == "":
764 # The caller has not specified an os_host so as far as we're concerned,
765 # it doesn't exist.
766 return state
767
768 os_req_states = [sub_state for sub_state in req_states
769 if sub_state.startswith('os_')]
770
771 if len(os_req_states) > 0:
772 # The caller has specified an os_host and they have requested
773 # information on os substates.
774
775 # Based on the information gathered on bmc, we'll try to make a
776 # determination of whether the os is even up. We'll pass the result
777 # of that assessment to get_os_state to enhance performance.
778 os_up_match = DotDict()
779 for sub_state in master_os_up_match:
780 if sub_state in req_states:
781 os_up_match[sub_state] = master_os_up_match[sub_state]
Michael Walsh70369fd2016-11-22 11:25:57 -0600782 os_up = compare_states(state, os_up_match)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600783 os_state = get_os_state(os_host=os_host,
784 os_username=os_username,
785 os_password=os_password,
786 req_states=os_req_states,
787 os_up=os_up,
788 quiet=quiet)
789 # Append os_state dictionary to ours.
790 state.update(os_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600791
792 return state
793
Michael Walsh70369fd2016-11-22 11:25:57 -0600794
Michael Walshfd5a8682019-02-01 14:28:42 -0600795exit_wait_early_message = ""
796
797
798def set_exit_wait_early_message(value):
799 r"""
800 Set global exit_wait_early_message to the indicated value.
801
802 This is a mechanism by which the programmer can do an early exit from
803 wait_until_keyword_succeeds() based on some special condition.
804
805 Description of argument(s):
806 value The value to assign to the global
807 exit_wait_early_message.
808 """
809
810 global exit_wait_early_message
811 exit_wait_early_message = value
812
813
Michael Walsh70369fd2016-11-22 11:25:57 -0600814def check_state(match_state,
815 invert=0,
816 print_string="",
817 openbmc_host="",
818 openbmc_username="",
819 openbmc_password="",
820 os_host="",
821 os_username="",
822 os_password="",
823 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600824 r"""
825 Check that the Open BMC machine's composite state matches the specified
826 state. On success, this keyword returns the machine's composite state as a
827 dictionary.
828
Michael Walsh2a0df682019-09-27 17:19:27 -0500829 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600830 match_state A dictionary whose key/value pairs are "state field"/
831 "state value". The state value is interpreted as a
832 regular expression. Example call from robot:
Michael Walsh341c21e2017-01-17 16:25:20 -0600833 ${match_state}= Create Dictionary chassis=^On$
834 ... bmc=^Ready$
Michael Walsh01975fa2017-08-20 20:51:36 -0500835 ... boot_progress=^OSStart$
Michael Walsh70369fd2016-11-22 11:25:57 -0600836 ${state}= Check State &{match_state}
837 invert If this flag is set, this function will succeed if the
838 states do NOT match.
839 print_string This function will print this string to the console prior
840 to getting the state.
841 openbmc_host The DNS name or IP address of the BMC.
842 This defaults to global ${OPENBMC_HOST}.
843 openbmc_username The username to be used to login to the BMC.
844 This defaults to global ${OPENBMC_USERNAME}.
845 openbmc_password The password to be used to login to the BMC.
846 This defaults to global ${OPENBMC_PASSWORD}.
847 os_host The DNS name or IP address of the operating system.
848 This defaults to global ${OS_HOST}.
849 os_username The username to be used to login to the OS.
850 This defaults to global ${OS_USERNAME}.
851 os_password The password to be used to login to the OS.
852 This defaults to global ${OS_PASSWORD}.
853 quiet Indicates whether status details should be written to the
854 console. Defaults to either global value of ${QUIET} or
855 to 1.
856 """
857
Michael Walsh619aa332017-04-12 15:56:51 -0500858 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600859
Michael Walshedb5c942019-03-28 12:40:50 -0500860 gp.gp_print(print_string)
861
862 try:
863 match_state = return_state_constant(match_state)
864 except TypeError:
865 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600866
Michael Walsh2a0df682019-09-27 17:19:27 -0500867 req_states = list(match_state.keys())
868 # Remove special-case match key from req_states.
869 if expressions_key() in req_states:
870 req_states.remove(expressions_key())
Michael Walsh70369fd2016-11-22 11:25:57 -0600871 # Initialize state.
872 state = get_state(openbmc_host=openbmc_host,
873 openbmc_username=openbmc_username,
874 openbmc_password=openbmc_password,
875 os_host=os_host,
876 os_username=os_username,
877 os_password=os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600878 req_states=req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600879 quiet=quiet)
880 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500881 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600882
Michael Walshfd5a8682019-02-01 14:28:42 -0600883 if exit_wait_early_message != "":
884 # The exit_wait_early_message has been set by a signal handler so we
885 # will exit "successfully". It is incumbent upon the calling function
886 # (e.g. wait_state) to check/clear this variable and to fail
887 # appropriately.
888 return state
889
Michael Walsh70369fd2016-11-22 11:25:57 -0600890 match = compare_states(state, match_state)
891
892 if invert and match:
893 fail_msg = "The current state of the machine matches the match" +\
894 " state:\n" + gp.sprint_varx("state", state)
895 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
896 elif not invert and not match:
897 fail_msg = "The current state of the machine does NOT match the" +\
898 " match state:\n" +\
899 gp.sprint_varx("state", state)
900 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
901
902 return state
903
Michael Walsh70369fd2016-11-22 11:25:57 -0600904
Michael Walshf893ba02017-01-10 10:28:05 -0600905def wait_state(match_state=(),
Michael Walsh70369fd2016-11-22 11:25:57 -0600906 wait_time="1 min",
907 interval="1 second",
908 invert=0,
909 openbmc_host="",
910 openbmc_username="",
911 openbmc_password="",
912 os_host="",
913 os_username="",
914 os_password="",
915 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600916 r"""
917 Wait for the Open BMC machine's composite state to match the specified
918 state. On success, this keyword returns the machine's composite state as
919 a dictionary.
920
Michael Walsh2a0df682019-09-27 17:19:27 -0500921 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600922 match_state A dictionary whose key/value pairs are "state field"/
923 "state value". See check_state (above) for details.
Michael Walsh619aa332017-04-12 15:56:51 -0500924 This value may also be any string accepted by
925 return_state_constant (e.g. "standby_match_state").
926 In such a case this function will call
927 return_state_constant to convert it to a proper
928 dictionary as described above.
Michael Walsh70369fd2016-11-22 11:25:57 -0600929 wait_time The total amount of time to wait for the desired state.
930 This value may be expressed in Robot Framework's time
931 format (e.g. 1 minute, 2 min 3 s, 4.5).
932 interval The amount of time between state checks.
933 This value may be expressed in Robot Framework's time
934 format (e.g. 1 minute, 2 min 3 s, 4.5).
935 invert If this flag is set, this function will for the state of
936 the machine to cease to match the match state.
937 openbmc_host The DNS name or IP address of the BMC.
938 This defaults to global ${OPENBMC_HOST}.
939 openbmc_username The username to be used to login to the BMC.
940 This defaults to global ${OPENBMC_USERNAME}.
941 openbmc_password The password to be used to login to the BMC.
942 This defaults to global ${OPENBMC_PASSWORD}.
943 os_host The DNS name or IP address of the operating system.
944 This defaults to global ${OS_HOST}.
945 os_username The username to be used to login to the OS.
946 This defaults to global ${OS_USERNAME}.
947 os_password The password to be used to login to the OS.
948 This defaults to global ${OS_PASSWORD}.
949 quiet Indicates whether status details should be written to the
950 console. Defaults to either global value of ${QUIET} or
951 to 1.
952 """
953
Michael Walsh619aa332017-04-12 15:56:51 -0500954 quiet = int(gp.get_var_value(quiet, 0))
955
George Keishing36efbc02018-12-12 10:18:23 -0600956 try:
Michael Walsh619aa332017-04-12 15:56:51 -0500957 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600958 except TypeError:
959 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600960
961 if not quiet:
962 if invert:
963 alt_text = "cease to "
964 else:
965 alt_text = ""
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500966 gp.print_timen("Checking every " + str(interval) + " for up to "
967 + str(wait_time) + " for the state of the machine to "
968 + alt_text + "match the state shown below.")
Michael Walsh3eb50022017-03-21 11:27:30 -0500969 gp.print_var(match_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600970
Michael Walshf893ba02017-01-10 10:28:05 -0600971 if quiet:
Michael Walsh341c21e2017-01-17 16:25:20 -0600972 print_string = ""
Michael Walshf893ba02017-01-10 10:28:05 -0600973 else:
Michael Walsh341c21e2017-01-17 16:25:20 -0600974 print_string = "#"
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600975
976 debug = int(BuiltIn().get_variable_value("${debug}", "0"))
977 if debug:
978 # In debug we print state so no need to print the "#".
979 print_string = ""
980 check_state_quiet = 1 - debug
Michael Walsh70369fd2016-11-22 11:25:57 -0600981 cmd_buf = ["Check State", match_state, "invert=${" + str(invert) + "}",
Michael Walshf893ba02017-01-10 10:28:05 -0600982 "print_string=" + print_string, "openbmc_host=" + openbmc_host,
Michael Walsh70369fd2016-11-22 11:25:57 -0600983 "openbmc_username=" + openbmc_username,
984 "openbmc_password=" + openbmc_password, "os_host=" + os_host,
985 "os_username=" + os_username, "os_password=" + os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600986 "quiet=${" + str(check_state_quiet) + "}"]
Michael Walshedb5c942019-03-28 12:40:50 -0500987 gp.dprint_issuing(cmd_buf)
Michael Walsh619aa332017-04-12 15:56:51 -0500988 try:
989 state = BuiltIn().wait_until_keyword_succeeds(wait_time, interval,
990 *cmd_buf)
991 except AssertionError as my_assertion_error:
992 gp.printn()
993 message = my_assertion_error.args[0]
994 BuiltIn().fail(message)
995
Michael Walshfd5a8682019-02-01 14:28:42 -0600996 if exit_wait_early_message:
997 # The global exit_wait_early_message was set by a signal handler
998 # indicating that we should fail.
999 message = exit_wait_early_message
1000 # Clear the exit_wait_early_message variable for future use.
1001 set_exit_wait_early_message("")
1002 BuiltIn().fail(gp.sprint_error(message))
1003
Michael Walsh70369fd2016-11-22 11:25:57 -06001004 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -05001005 gp.printn()
Michael Walsh70369fd2016-11-22 11:25:57 -06001006 if invert:
Michael Walsh3eb50022017-03-21 11:27:30 -05001007 gp.print_timen("The states no longer match:")
Michael Walsh70369fd2016-11-22 11:25:57 -06001008 else:
Michael Walsh3eb50022017-03-21 11:27:30 -05001009 gp.print_timen("The states match:")
1010 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001011
1012 return state
1013
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001014
Michael Walsh2a0df682019-09-27 17:19:27 -05001015def set_start_boot_seconds(value=0):
1016 global start_boot_seconds
1017 start_boot_seconds = int(value)
1018
1019
1020set_start_boot_seconds(0)
1021
1022
Michael Walsh619aa332017-04-12 15:56:51 -05001023def wait_for_comm_cycle(start_boot_seconds,
1024 quiet=None):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001025 r"""
Michael Walsh2a0df682019-09-27 17:19:27 -05001026 Wait for the BMC uptime to be less than elapsed_boot_time.
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001027
Michael Walsh2a0df682019-09-27 17:19:27 -05001028 This function will tolerate an expected loss of communication to the BMC.
1029 This function is useful when some kind of reboot has been initiated by the
1030 caller.
1031
1032 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001033 start_boot_seconds The time that the boot test started. The format is the
1034 epoch time in seconds, i.e. the number of seconds since
1035 1970-01-01 00:00:00 UTC. This value should be obtained
1036 from the BMC so that it is not dependent on any kind of
1037 synchronization between this machine and the target BMC
1038 This will allow this program to work correctly even in
1039 a simulated environment. This value should be obtained
1040 by the caller prior to initiating a reboot. It can be
1041 obtained as follows:
1042 state = st.get_state(req_states=['epoch_seconds'])
1043 """
1044
Michael Walsh619aa332017-04-12 15:56:51 -05001045 quiet = int(gp.get_var_value(quiet, 0))
1046
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001047 # Validate parms.
Michael Walsh2a0df682019-09-27 17:19:27 -05001048 error_message = gv.valid_integer(start_boot_seconds)
1049 if error_message:
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001050 BuiltIn().fail(gp.sprint_error(error_message))
1051
Michael Walsh2a0df682019-09-27 17:19:27 -05001052 # Wait for uptime to be less than elapsed_boot_time.
1053 set_start_boot_seconds(start_boot_seconds)
1054 expr = 'int(float(state[\'uptime\'])) < int(state[\'elapsed_boot_time\'])'
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001055 match_state = DotDict([('uptime', '^[0-9\\.]+$'),
Michael Walsh2a0df682019-09-27 17:19:27 -05001056 ('elapsed_boot_time', '^[0-9]+$'),
1057 (expressions_key(), [expr])])
David Shawe9192562020-09-28 11:00:45 -05001058 wait_state(match_state, wait_time="12 mins", interval="5 seconds")
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001059
Michael Sheposbdd1dce2020-12-10 11:51:58 -06001060 gp.qprint_timen("Verifying that REST/Redfish API interface is working.")
1061 if not redfish_support_trans_state:
1062 match_state = DotDict([('rest', '^1$')])
1063 else:
1064 match_state = DotDict([('redfish', '^1$')])
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001065 state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")