blob: 61223f0741cb8abd87fede975bae770f9676b2f1 [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
Michael Walsh619aa332017-04-12 15:56:51 -050084# When a user calls get_state w/o specifying req_states, default_req_states
85# is used as its value.
86default_req_states = ['rest',
87 'chassis',
88 'bmc',
89 'boot_progress',
Michael Walsh56749222017-09-29 15:26:07 -050090 'operating_system',
Michael Walsh619aa332017-04-12 15:56:51 -050091 'host',
92 'os_ping',
93 'os_login',
94 'os_run_cmd']
Michael Walsh8fae6ea2017-02-20 16:14:44 -060095
Michael Sheposda40c1d2020-12-01 22:30:00 -060096if redfish_support_trans_state:
97 default_req_states.remove('operating_system')
98
Michael Walsh619aa332017-04-12 15:56:51 -050099# valid_req_states is a list of sub states supported by the get_state function.
100# valid_req_states, default_req_states and master_os_up_match are used by the
101# get_state function.
Michael Sheposda40c1d2020-12-01 22:30:00 -0600102
Michael Walsh619aa332017-04-12 15:56:51 -0500103valid_req_states = ['ping',
104 'packet_loss',
105 'uptime',
106 'epoch_seconds',
Michael Walsh2a0df682019-09-27 17:19:27 -0500107 'elapsed_boot_time',
Michael Walsh619aa332017-04-12 15:56:51 -0500108 'rest',
109 'chassis',
Michael Walsh56749222017-09-29 15:26:07 -0500110 'requested_chassis',
Michael Walsh619aa332017-04-12 15:56:51 -0500111 'bmc',
Michael Walsh56749222017-09-29 15:26:07 -0500112 'requested_bmc',
Michael Walsh619aa332017-04-12 15:56:51 -0500113 'boot_progress',
Michael Walsh56749222017-09-29 15:26:07 -0500114 'operating_system',
Michael Walsh619aa332017-04-12 15:56:51 -0500115 'host',
Michael Walsh56749222017-09-29 15:26:07 -0500116 'requested_host',
117 'attempts_left',
Michael Walsh619aa332017-04-12 15:56:51 -0500118 'os_ping',
119 'os_login',
120 'os_run_cmd']
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600121
122# valid_os_req_states and default_os_req_states are used by the os_get_state
123# function.
124# valid_os_req_states is a list of state information supported by the
125# get_os_state function.
126valid_os_req_states = ['os_ping',
127 'os_login',
128 'os_run_cmd']
129# When a user calls get_os_state w/o specifying req_states,
130# default_os_req_states is used as its value.
131default_os_req_states = ['os_ping',
132 'os_login',
133 'os_run_cmd']
134
135# Presently, some BMCs appear to not keep time very well. This environment
136# variable directs the get_state function to use either the BMC's epoch time
137# or the local epoch time.
138USE_BMC_EPOCH_TIME = int(os.environ.get('USE_BMC_EPOCH_TIME', 0))
Michael Walsh341c21e2017-01-17 16:25:20 -0600139
Michael Walsh619aa332017-04-12 15:56:51 -0500140# Useful state constant definition(s).
Michael Sheposda40c1d2020-12-01 22:30:00 -0600141if not redfish_support_trans_state:
142 # default_state is an initial value which may be of use to callers.
143 default_state = DotDict([('rest', '1'),
144 ('chassis', 'On'),
145 ('bmc', 'Ready'),
146 ('boot_progress', 'OSStart'),
147 ('operating_system', 'BootComplete'),
148 ('host', 'Running'),
149 ('os_ping', '1'),
150 ('os_login', '1'),
151 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500152
Michael Sheposda40c1d2020-12-01 22:30:00 -0600153 # A match state for checking that the system is at "standby".
154 standby_match_state = DotDict([('rest', '^1$'),
155 ('chassis', '^Off$'),
156 ('bmc', '^Ready$'),
157 ('boot_progress', '^Off|Unspecified$'),
158 ('operating_system', '^Inactive$'),
159 ('host', '^Off$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500160
Michael Sheposda40c1d2020-12-01 22:30:00 -0600161 # A match state for checking that the system is at "os running".
162 os_running_match_state = DotDict([('chassis', '^On$'),
163 ('bmc', '^Ready$'),
164 ('boot_progress',
165 'FW Progress, Starting OS|OSStart'),
166 ('operating_system', 'BootComplete'),
167 ('host', '^Running$'),
168 ('os_ping', '^1$'),
169 ('os_login', '^1$'),
170 ('os_run_cmd', '^1$')])
171
172 # A master dictionary to determine whether the os may be up.
173 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Walsh7dc885b2018-03-14 17:51:59 -0500174 ('bmc', '^Ready$'),
175 ('boot_progress',
176 'FW Progress, Starting OS|OSStart'),
177 ('operating_system', 'BootComplete'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600178 ('host', '^Running|Quiesced$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500179
Michael Sheposda40c1d2020-12-01 22:30:00 -0600180 invalid_state_match = DotDict([('rest', '^$'),
181 ('chassis', '^$'),
182 ('bmc', '^$'),
183 ('boot_progress', '^$'),
184 ('operating_system', '^$'),
185 ('host', '^$')])
186else:
187 # default_state is an initial value which may be of use to callers.
188 default_state = DotDict([('rest', '1'),
189 ('chassis', 'On'),
190 ('bmc', 'Ready'),
191 ('boot_progress', 'SystemInitComplete|OSRunning'),
192 ('host', 'Running'),
193 ('os_ping', '1'),
194 ('os_login', '1'),
195 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500196
Michael Sheposda40c1d2020-12-01 22:30:00 -0600197 # A match state for checking that the system is at "standby".
198 standby_match_state = DotDict([('rest', '^1$'),
199 ('chassis', '^Off$'),
200 ('bmc', '^Ready$'),
201 ('boot_progress', '^Off|Unspecified$'),
202 ('host', '^Off$')])
203
204 # A match state for checking that the system is at "os running".
205 os_running_match_state = DotDict([('chassis', '^On$'),
206 ('bmc', '^Ready$'),
207 ('boot_progress',
208 'SystemInitComplete|OSRunning'),
209 ('host', '^Running$'),
210 ('os_ping', '^1$'),
211 ('os_login', '^1$'),
212 ('os_run_cmd', '^1$')])
213
214 # A master dictionary to determine whether the os may be up.
215 master_os_up_match = DotDict([('chassis', '^On$'),
216 ('bmc', '^Ready$'),
217 ('boot_progress',
218 'SystemInitComplete|OSRunning'),
219 ('host', '^Running|Quiesced$')])
220
221 invalid_state_match = DotDict([('rest', '^$'),
222 ('chassis', '^$'),
223 ('bmc', '^$'),
224 ('boot_progress', '^$'),
225 ('host', '^$')])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500226
Michael Walsh341c21e2017-01-17 16:25:20 -0600227
George Keishing36efbc02018-12-12 10:18:23 -0600228def return_state_constant(state_name='default_state'):
Michael Walsh619aa332017-04-12 15:56:51 -0500229 r"""
Michael Walsh7dc885b2018-03-14 17:51:59 -0500230 Return the named state dictionary constant.
Michael Walsh619aa332017-04-12 15:56:51 -0500231 """
232
George Keishing36efbc02018-12-12 10:18:23 -0600233 return eval(state_name)
Michael Walsh619aa332017-04-12 15:56:51 -0500234
Michael Walsh619aa332017-04-12 15:56:51 -0500235
Michael Walsh70369fd2016-11-22 11:25:57 -0600236def anchor_state(state):
Michael Walsh70369fd2016-11-22 11:25:57 -0600237 r"""
238 Add regular expression anchors ("^" and "$") to the beginning and end of
239 each item in the state dictionary passed in. Return the resulting
240 dictionary.
241
Michael Walsh2a0df682019-09-27 17:19:27 -0500242 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600243 state A dictionary such as the one returned by the get_state()
244 function.
245 """
246
Michael Walsh2ce067a2017-02-27 14:24:07 -0600247 anchored_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500248 for key in anchored_state.keys():
Michael Walsh70369fd2016-11-22 11:25:57 -0600249 anchored_state[key] = "^" + str(anchored_state[key]) + "$"
250
251 return anchored_state
252
Michael Walsh70369fd2016-11-22 11:25:57 -0600253
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600254def strip_anchor_state(state):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600255 r"""
256 Strip regular expression anchors ("^" and "$") from the beginning and end
257 of each item in the state dictionary passed in. Return the resulting
258 dictionary.
259
Michael Walsh2a0df682019-09-27 17:19:27 -0500260 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600261 state A dictionary such as the one returned by the get_state()
262 function.
263 """
264
Michael Walsh2ce067a2017-02-27 14:24:07 -0600265 stripped_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500266 for key in stripped_state.keys():
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600267 stripped_state[key] = stripped_state[key].strip("^$")
268
269 return stripped_state
270
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600271
Michael Walsh2a0df682019-09-27 17:19:27 -0500272def expressions_key():
273 r"""
274 Return expressions key constant.
275 """
276 return '<expressions>'
277
278
Michael Walsh70369fd2016-11-22 11:25:57 -0600279def compare_states(state,
Michael Walsh45ca6e42017-09-14 17:29:12 -0500280 match_state,
281 match_type='and'):
Michael Walsh70369fd2016-11-22 11:25:57 -0600282 r"""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600283 Compare 2 state dictionaries. Return True if they match and False if they
Michael Walsh70369fd2016-11-22 11:25:57 -0600284 don't. Note that the match_state dictionary does not need to have an entry
285 corresponding to each entry in the state dictionary. But for each entry
286 that it does have, the corresponding state entry will be checked for a
287 match.
288
Michael Walsh2a0df682019-09-27 17:19:27 -0500289 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600290 state A state dictionary such as the one returned by the
291 get_state function.
292 match_state A dictionary whose key/value pairs are "state field"/
293 "state value". The state value is interpreted as a
294 regular expression. Every value in this dictionary is
Michael Walsh45ca6e42017-09-14 17:29:12 -0500295 considered. When match_type is 'and', if each and every
296 comparison matches, the two dictionaries are considered to
297 be matching. If match_type is 'or', if any two of the
298 elements compared match, the two dictionaries are
299 considered to be matching.
Michael Walsh2a0df682019-09-27 17:19:27 -0500300
Michael Walsh7dc885b2018-03-14 17:51:59 -0500301 This value may also be any string accepted by
Michael Walsh2a0df682019-09-27 17:19:27 -0500302 return_state_constant (e.g. "standby_match_state"). In
303 such a case this function will call return_state_constant
304 to convert it to a proper dictionary as described above.
305
306 Finally, one special value is accepted for the key field:
307 expression_key(). If such an entry exists, its value is
308 taken to be a list of expressions to be evaluated. These
309 expressions may reference state dictionary entries by
310 simply coding them in standard python syntax (e.g.
311 state['key1']). What follows is an example expression:
312
313 "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
314
315 In this example, if the state dictionary's 'uptime' entry
316 is less than its 'elapsed_boot_time' entry, it would
317 qualify as a match.
Michael Walsh45ca6e42017-09-14 17:29:12 -0500318 match_type This may be 'and' or 'or'.
Michael Walsh70369fd2016-11-22 11:25:57 -0600319 """
320
Michael Walsh2a0df682019-09-27 17:19:27 -0500321 error_message = gv.valid_value(match_type, valid_values=['and', 'or'])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500322 if error_message != "":
323 BuiltIn().fail(gp.sprint_error(error_message))
324
George Keishing36efbc02018-12-12 10:18:23 -0600325 try:
Michael Walsh7dc885b2018-03-14 17:51:59 -0500326 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600327 except TypeError:
328 pass
Michael Walsh7dc885b2018-03-14 17:51:59 -0500329
Michael Walsh45ca6e42017-09-14 17:29:12 -0500330 default_match = (match_type == 'and')
Michael Walsh70369fd2016-11-22 11:25:57 -0600331 for key, match_state_value in match_state.items():
Michael Walsh97df71c2017-03-27 14:33:24 -0500332 # Blank match_state_value means "don't care".
333 if match_state_value == "":
334 continue
Michael Walsh2a0df682019-09-27 17:19:27 -0500335 if key == expressions_key():
336 for expr in match_state_value:
337 # Use python interpreter to evaluate the expression.
338 match = eval(expr)
339 if match != default_match:
340 return match
341 else:
342 try:
343 match = (re.match(match_state_value, str(state[key])) is not None)
344 except KeyError:
345 match = False
346 if match != default_match:
347 return match
Michael Walsh45ca6e42017-09-14 17:29:12 -0500348
349 return default_match
Michael Walsh70369fd2016-11-22 11:25:57 -0600350
Michael Walsh70369fd2016-11-22 11:25:57 -0600351
Michael Walsh70369fd2016-11-22 11:25:57 -0600352def get_os_state(os_host="",
353 os_username="",
354 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600355 req_states=default_os_req_states,
356 os_up=True,
Michael Walsh70369fd2016-11-22 11:25:57 -0600357 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600358 r"""
359 Get component states for the operating system such as ping, login,
360 etc, put them into a dictionary and return them to the caller.
361
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600362 Note that all substate values are strings.
363
Michael Walsh2a0df682019-09-27 17:19:27 -0500364 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600365 os_host The DNS name or IP address of the operating system.
366 This defaults to global ${OS_HOST}.
367 os_username The username to be used to login to the OS.
368 This defaults to global ${OS_USERNAME}.
369 os_password The password to be used to login to the OS.
370 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600371 req_states This is a list of states whose values are being requested by
372 the caller.
373 os_up If the caller knows that the os can't possibly be up, it can
374 improve performance by passing os_up=False. This function
375 will then simply return default values for all requested os
376 sub states.
Michael Walsh70369fd2016-11-22 11:25:57 -0600377 quiet Indicates whether status details (e.g. curl commands) should
378 be written to the console.
379 Defaults to either global value of ${QUIET} or to 1.
380 """
381
Michael Walsh619aa332017-04-12 15:56:51 -0500382 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600383
384 # Set parm defaults where necessary and validate all parms.
385 if os_host == "":
386 os_host = BuiltIn().get_variable_value("${OS_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500387 error_message = gv.valid_value(os_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600388 if error_message != "":
389 BuiltIn().fail(gp.sprint_error(error_message))
390
391 if os_username == "":
392 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500393 error_message = gv.valid_value(os_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600394 if error_message != "":
395 BuiltIn().fail(gp.sprint_error(error_message))
396
397 if os_password == "":
398 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500399 error_message = gv.valid_value(os_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600400 if error_message != "":
401 BuiltIn().fail(gp.sprint_error(error_message))
402
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600403 invalid_req_states = [sub_state for sub_state in req_states
404 if sub_state not in valid_os_req_states]
405 if len(invalid_req_states) > 0:
406 error_message = "The following req_states are not supported:\n" +\
407 gp.sprint_var(invalid_req_states)
408 BuiltIn().fail(gp.sprint_error(error_message))
Michael Walsh70369fd2016-11-22 11:25:57 -0600409
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600410 # Initialize all substate values supported by this function.
411 os_ping = 0
412 os_login = 0
413 os_run_cmd = 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600414
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600415 if os_up:
416 if 'os_ping' in req_states:
417 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600418 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + os_host,
419 print_output=0, show_err=0,
420 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600421 if rc == 0:
422 os_ping = 1
Michael Walsh70369fd2016-11-22 11:25:57 -0600423
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600424 # Programming note: All attributes which do not require an ssh login
425 # should have been processed by this point.
426 master_req_login = ['os_login', 'os_run_cmd']
427 req_login = [sub_state for sub_state in req_states if sub_state in
428 master_req_login]
Michael Walsh97df71c2017-03-27 14:33:24 -0500429 must_login = (len(req_login) > 0)
Michael Walsh70369fd2016-11-22 11:25:57 -0600430
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600431 if must_login:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500432 output, stderr, rc = bsu.os_execute_command("uptime", quiet=quiet,
Michael Walsh7fc33972018-08-07 14:55:03 -0500433 ignore_err=1,
434 time_out=20)
Michael Walsh6a9bd142018-07-24 16:10:29 -0500435 if rc == 0:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600436 os_login = 1
Michael Walsh6a9bd142018-07-24 16:10:29 -0500437 os_run_cmd = 1
Michael Walsh3eb50022017-03-21 11:27:30 -0500438 else:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500439 gp.dprint_vars(output, stderr)
440 gp.dprint_vars(rc, 1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600441
442 os_state = DotDict()
443 for sub_state in req_states:
444 cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")"
445 exec(cmd_buf)
Michael Walsh70369fd2016-11-22 11:25:57 -0600446
447 return os_state
448
Michael Walsh70369fd2016-11-22 11:25:57 -0600449
Michael Walsh70369fd2016-11-22 11:25:57 -0600450def get_state(openbmc_host="",
451 openbmc_username="",
452 openbmc_password="",
453 os_host="",
454 os_username="",
455 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600456 req_states=default_req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600457 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600458 r"""
Michael Walsh619aa332017-04-12 15:56:51 -0500459 Get component states such as chassis state, bmc state, etc, put them into a
Michael Walsh70369fd2016-11-22 11:25:57 -0600460 dictionary and return them to the caller.
461
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600462 Note that all substate values are strings.
463
Michael Walsh2a0df682019-09-27 17:19:27 -0500464 Note: If elapsed_boot_time is included in req_states, it is the caller's
465 duty to call set_start_boot_seconds() in order to set global
466 start_boot_seconds. elapsed_boot_time is the current time minus
467 start_boot_seconds.
468
469 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600470 openbmc_host The DNS name or IP address of the BMC.
471 This defaults to global ${OPENBMC_HOST}.
472 openbmc_username The username to be used to login to the BMC.
473 This defaults to global ${OPENBMC_USERNAME}.
474 openbmc_password The password to be used to login to the BMC.
475 This defaults to global ${OPENBMC_PASSWORD}.
476 os_host The DNS name or IP address of the operating system.
477 This defaults to global ${OS_HOST}.
478 os_username The username to be used to login to the OS.
479 This defaults to global ${OS_USERNAME}.
480 os_password The password to be used to login to the OS.
481 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600482 req_states This is a list of states whose values are being requested
483 by the caller.
Michael Walsh70369fd2016-11-22 11:25:57 -0600484 quiet Indicates whether status details (e.g. curl commands)
485 should be written to the console.
486 Defaults to either global value of ${QUIET} or to 1.
487 """
488
Michael Walsh619aa332017-04-12 15:56:51 -0500489 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600490
491 # Set parm defaults where necessary and validate all parms.
492 if openbmc_host == "":
493 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500494 error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600495 if error_message != "":
496 BuiltIn().fail(gp.sprint_error(error_message))
497
498 if openbmc_username == "":
499 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500500 error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600501 if error_message != "":
502 BuiltIn().fail(gp.sprint_error(error_message))
503
504 if openbmc_password == "":
505 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500506 error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600507 if error_message != "":
508 BuiltIn().fail(gp.sprint_error(error_message))
509
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600510 # NOTE: OS parms are optional.
Michael Walsh70369fd2016-11-22 11:25:57 -0600511 if os_host == "":
512 os_host = BuiltIn().get_variable_value("${OS_HOST}")
513 if os_host is None:
514 os_host = ""
515
516 if os_username is "":
517 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
518 if os_username is None:
519 os_username = ""
520
521 if os_password is "":
522 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
523 if os_password is None:
524 os_password = ""
525
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600526 invalid_req_states = [sub_state for sub_state in req_states
527 if sub_state not in valid_req_states]
528 if len(invalid_req_states) > 0:
529 error_message = "The following req_states are not supported:\n" +\
530 gp.sprint_var(invalid_req_states)
531 BuiltIn().fail(gp.sprint_error(error_message))
532
533 # Initialize all substate values supported by this function.
534 ping = 0
535 packet_loss = ''
536 uptime = ''
537 epoch_seconds = ''
Michael Walsh2a0df682019-09-27 17:19:27 -0500538 elapsed_boot_time = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500539 rest = ''
540 chassis = ''
541 requested_chassis = ''
542 bmc = ''
543 requested_bmc = ''
544 boot_progress = ''
545 operating_system = ''
546 host = ''
547 requested_host = ''
548 attempts_left = ''
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600549
Michael Walsh70369fd2016-11-22 11:25:57 -0600550 # Get the component states.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600551 if 'ping' in req_states:
552 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600553 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + openbmc_host,
554 print_output=0, show_err=0,
555 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600556 if rc == 0:
557 ping = 1
558
559 if 'packet_loss' in req_states:
560 # See if the OS pings.
561 cmd_buf = "ping -c 5 -w 5 " + openbmc_host +\
562 " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'"
George Keishing36efbc02018-12-12 10:18:23 -0600563 rc, out_buf = gc.shell_cmd(cmd_buf,
564 print_output=0, show_err=0,
565 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600566 if rc == 0:
567 packet_loss = out_buf.rstrip("\n")
568
Michael Walshe53e47a2017-06-30 17:03:24 -0500569 if 'uptime' in req_states:
Michael Walshfa765932017-10-13 14:07:22 -0500570 # Sometimes reading uptime results in a blank value. Call with
571 # wait_until_keyword_succeeds to ensure a non-blank value is obtained.
572 remote_cmd_buf = "read uptime filler 2>/dev/null < /proc/uptime" +\
573 " && [ ! -z \"${uptime}\" ] && echo ${uptime}"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500574 cmd_buf = ["BMC Execute Command",
Michael Walsh888d85a2019-04-18 11:03:28 -0500575 re.sub('\\$', '\\$', remote_cmd_buf), 'quiet=1',
576 'test_mode=0']
Michael Walsh2a0df682019-09-27 17:19:27 -0500577 gp.qprint_issuing(cmd_buf, 0)
578 gp.qprint_issuing(remote_cmd_buf, 0)
Michael Walshfa765932017-10-13 14:07:22 -0500579 try:
580 stdout, stderr, rc =\
Michael Walshcaccd852017-11-01 17:58:41 -0500581 BuiltIn().wait_until_keyword_succeeds("10 sec", "0 sec",
Michael Walshfa765932017-10-13 14:07:22 -0500582 *cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500583 if rc == 0 and stderr == "":
584 uptime = stdout
Michael Walshfa765932017-10-13 14:07:22 -0500585 except AssertionError as my_assertion_error:
586 pass
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600587
Michael Walsh2a0df682019-09-27 17:19:27 -0500588 if 'epoch_seconds' in req_states or 'elapsed_boot_time' in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600589 date_cmd_buf = "date -u +%s"
590 if USE_BMC_EPOCH_TIME:
Michael Walshe53e47a2017-06-30 17:03:24 -0500591 cmd_buf = ["BMC Execute Command", date_cmd_buf, 'quiet=${1}']
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600592 if not quiet:
Michael Walshedb5c942019-03-28 12:40:50 -0500593 gp.print_issuing(cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500594 status, ret_values = \
595 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
596 if status == "PASS":
597 stdout, stderr, rc = ret_values
598 if rc == 0 and stderr == "":
599 epoch_seconds = stdout.rstrip("\n")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600600 else:
601 shell_rc, out_buf = gc.cmd_fnc_u(date_cmd_buf,
Michael Walshfa765932017-10-13 14:07:22 -0500602 quiet=quiet,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600603 print_output=0)
604 if shell_rc == 0:
605 epoch_seconds = out_buf.rstrip("\n")
606
Michael Walsh2a0df682019-09-27 17:19:27 -0500607 if 'elapsed_boot_time' in req_states:
608 global start_boot_seconds
609 elapsed_boot_time = int(epoch_seconds) - start_boot_seconds
610
Michael Walsh56749222017-09-29 15:26:07 -0500611 master_req_rest = ['rest', 'host', 'requested_host', 'operating_system',
612 'attempts_left', 'boot_progress', 'chassis',
613 'requested_chassis' 'bmc' 'requested_bmc']
614
Michael Walshb95eb542017-03-31 09:39:20 -0500615 req_rest = [sub_state for sub_state in req_states if sub_state in
616 master_req_rest]
617 need_rest = (len(req_rest) > 0)
Michael Walsh56749222017-09-29 15:26:07 -0500618 state = DotDict()
619 if need_rest:
Michael Walsh940d6912017-10-27 12:32:33 -0500620 cmd_buf = ["Read Properties", SYSTEM_STATE_URI + "enumerate",
David Shaw54078472020-07-30 11:19:36 -0500621 "quiet=${" + str(quiet) + "}", "timeout=30"]
Michael Walshedb5c942019-03-28 12:40:50 -0500622 gp.dprint_issuing(cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500623 status, ret_values = \
624 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
625 if status == "PASS":
Michael Walsh56749222017-09-29 15:26:07 -0500626 state['rest'] = '1'
Michael Walsh341c21e2017-01-17 16:25:20 -0600627 else:
Michael Walsh56749222017-09-29 15:26:07 -0500628 state['rest'] = '0'
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600629
Michael Walsh2b269de2017-10-09 11:18:11 -0500630 if int(state['rest']):
631 for url_path in ret_values:
632 for attr_name in ret_values[url_path]:
633 # Create a state key value based on the attr_name.
George Keishing36efbc02018-12-12 10:18:23 -0600634 try:
Michael Walsh2b269de2017-10-09 11:18:11 -0500635 ret_values[url_path][attr_name] = \
636 re.sub(r'.*\.', "",
637 ret_values[url_path][attr_name])
George Keishing36efbc02018-12-12 10:18:23 -0600638 except TypeError:
639 pass
Michael Walsh2b269de2017-10-09 11:18:11 -0500640 # Do some key name manipulations.
641 new_attr_name = re.sub(r'^Current|(State|Transition)$',
642 "", attr_name)
643 new_attr_name = re.sub(r'BMC', r'Bmc', new_attr_name)
644 new_attr_name = re.sub(r'([A-Z][a-z])', r'_\1',
645 new_attr_name)
646 new_attr_name = new_attr_name.lower().lstrip("_")
647 new_attr_name = re.sub(r'power', r'chassis', new_attr_name)
648 if new_attr_name in req_states:
649 state[new_attr_name] = ret_values[url_path][attr_name]
Michael Walshb95eb542017-03-31 09:39:20 -0500650
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600651 for sub_state in req_states:
Michael Walsh56749222017-09-29 15:26:07 -0500652 if sub_state in state:
653 continue
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600654 if sub_state.startswith("os_"):
655 # We pass "os_" requests on to get_os_state.
656 continue
657 cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")"
658 exec(cmd_buf)
659
660 if os_host == "":
661 # The caller has not specified an os_host so as far as we're concerned,
662 # it doesn't exist.
663 return state
664
665 os_req_states = [sub_state for sub_state in req_states
666 if sub_state.startswith('os_')]
667
668 if len(os_req_states) > 0:
669 # The caller has specified an os_host and they have requested
670 # information on os substates.
671
672 # Based on the information gathered on bmc, we'll try to make a
673 # determination of whether the os is even up. We'll pass the result
674 # of that assessment to get_os_state to enhance performance.
675 os_up_match = DotDict()
676 for sub_state in master_os_up_match:
677 if sub_state in req_states:
678 os_up_match[sub_state] = master_os_up_match[sub_state]
Michael Walsh70369fd2016-11-22 11:25:57 -0600679 os_up = compare_states(state, os_up_match)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600680 os_state = get_os_state(os_host=os_host,
681 os_username=os_username,
682 os_password=os_password,
683 req_states=os_req_states,
684 os_up=os_up,
685 quiet=quiet)
686 # Append os_state dictionary to ours.
687 state.update(os_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600688
689 return state
690
Michael Walsh70369fd2016-11-22 11:25:57 -0600691
Michael Walshfd5a8682019-02-01 14:28:42 -0600692exit_wait_early_message = ""
693
694
695def set_exit_wait_early_message(value):
696 r"""
697 Set global exit_wait_early_message to the indicated value.
698
699 This is a mechanism by which the programmer can do an early exit from
700 wait_until_keyword_succeeds() based on some special condition.
701
702 Description of argument(s):
703 value The value to assign to the global
704 exit_wait_early_message.
705 """
706
707 global exit_wait_early_message
708 exit_wait_early_message = value
709
710
Michael Walsh70369fd2016-11-22 11:25:57 -0600711def check_state(match_state,
712 invert=0,
713 print_string="",
714 openbmc_host="",
715 openbmc_username="",
716 openbmc_password="",
717 os_host="",
718 os_username="",
719 os_password="",
720 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600721 r"""
722 Check that the Open BMC machine's composite state matches the specified
723 state. On success, this keyword returns the machine's composite state as a
724 dictionary.
725
Michael Walsh2a0df682019-09-27 17:19:27 -0500726 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600727 match_state A dictionary whose key/value pairs are "state field"/
728 "state value". The state value is interpreted as a
729 regular expression. Example call from robot:
Michael Walsh341c21e2017-01-17 16:25:20 -0600730 ${match_state}= Create Dictionary chassis=^On$
731 ... bmc=^Ready$
Michael Walsh01975fa2017-08-20 20:51:36 -0500732 ... boot_progress=^OSStart$
Michael Walsh70369fd2016-11-22 11:25:57 -0600733 ${state}= Check State &{match_state}
734 invert If this flag is set, this function will succeed if the
735 states do NOT match.
736 print_string This function will print this string to the console prior
737 to getting the state.
738 openbmc_host The DNS name or IP address of the BMC.
739 This defaults to global ${OPENBMC_HOST}.
740 openbmc_username The username to be used to login to the BMC.
741 This defaults to global ${OPENBMC_USERNAME}.
742 openbmc_password The password to be used to login to the BMC.
743 This defaults to global ${OPENBMC_PASSWORD}.
744 os_host The DNS name or IP address of the operating system.
745 This defaults to global ${OS_HOST}.
746 os_username The username to be used to login to the OS.
747 This defaults to global ${OS_USERNAME}.
748 os_password The password to be used to login to the OS.
749 This defaults to global ${OS_PASSWORD}.
750 quiet Indicates whether status details should be written to the
751 console. Defaults to either global value of ${QUIET} or
752 to 1.
753 """
754
Michael Walsh619aa332017-04-12 15:56:51 -0500755 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600756
Michael Walshedb5c942019-03-28 12:40:50 -0500757 gp.gp_print(print_string)
758
759 try:
760 match_state = return_state_constant(match_state)
761 except TypeError:
762 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600763
Michael Walsh2a0df682019-09-27 17:19:27 -0500764 req_states = list(match_state.keys())
765 # Remove special-case match key from req_states.
766 if expressions_key() in req_states:
767 req_states.remove(expressions_key())
Michael Walsh70369fd2016-11-22 11:25:57 -0600768 # Initialize state.
769 state = get_state(openbmc_host=openbmc_host,
770 openbmc_username=openbmc_username,
771 openbmc_password=openbmc_password,
772 os_host=os_host,
773 os_username=os_username,
774 os_password=os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600775 req_states=req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600776 quiet=quiet)
777 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500778 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600779
Michael Walshfd5a8682019-02-01 14:28:42 -0600780 if exit_wait_early_message != "":
781 # The exit_wait_early_message has been set by a signal handler so we
782 # will exit "successfully". It is incumbent upon the calling function
783 # (e.g. wait_state) to check/clear this variable and to fail
784 # appropriately.
785 return state
786
Michael Walsh70369fd2016-11-22 11:25:57 -0600787 match = compare_states(state, match_state)
788
789 if invert and match:
790 fail_msg = "The current state of the machine matches the match" +\
791 " state:\n" + gp.sprint_varx("state", state)
792 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
793 elif not invert and not match:
794 fail_msg = "The current state of the machine does NOT match the" +\
795 " match state:\n" +\
796 gp.sprint_varx("state", state)
797 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
798
799 return state
800
Michael Walsh70369fd2016-11-22 11:25:57 -0600801
Michael Walshf893ba02017-01-10 10:28:05 -0600802def wait_state(match_state=(),
Michael Walsh70369fd2016-11-22 11:25:57 -0600803 wait_time="1 min",
804 interval="1 second",
805 invert=0,
806 openbmc_host="",
807 openbmc_username="",
808 openbmc_password="",
809 os_host="",
810 os_username="",
811 os_password="",
812 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600813 r"""
814 Wait for the Open BMC machine's composite state to match the specified
815 state. On success, this keyword returns the machine's composite state as
816 a dictionary.
817
Michael Walsh2a0df682019-09-27 17:19:27 -0500818 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600819 match_state A dictionary whose key/value pairs are "state field"/
820 "state value". See check_state (above) for details.
Michael Walsh619aa332017-04-12 15:56:51 -0500821 This value may also be any string accepted by
822 return_state_constant (e.g. "standby_match_state").
823 In such a case this function will call
824 return_state_constant to convert it to a proper
825 dictionary as described above.
Michael Walsh70369fd2016-11-22 11:25:57 -0600826 wait_time The total amount of time to wait for the desired state.
827 This value may be expressed in Robot Framework's time
828 format (e.g. 1 minute, 2 min 3 s, 4.5).
829 interval The amount of time between state checks.
830 This value may be expressed in Robot Framework's time
831 format (e.g. 1 minute, 2 min 3 s, 4.5).
832 invert If this flag is set, this function will for the state of
833 the machine to cease to match the match state.
834 openbmc_host The DNS name or IP address of the BMC.
835 This defaults to global ${OPENBMC_HOST}.
836 openbmc_username The username to be used to login to the BMC.
837 This defaults to global ${OPENBMC_USERNAME}.
838 openbmc_password The password to be used to login to the BMC.
839 This defaults to global ${OPENBMC_PASSWORD}.
840 os_host The DNS name or IP address of the operating system.
841 This defaults to global ${OS_HOST}.
842 os_username The username to be used to login to the OS.
843 This defaults to global ${OS_USERNAME}.
844 os_password The password to be used to login to the OS.
845 This defaults to global ${OS_PASSWORD}.
846 quiet Indicates whether status details should be written to the
847 console. Defaults to either global value of ${QUIET} or
848 to 1.
849 """
850
Michael Walsh619aa332017-04-12 15:56:51 -0500851 quiet = int(gp.get_var_value(quiet, 0))
852
George Keishing36efbc02018-12-12 10:18:23 -0600853 try:
Michael Walsh619aa332017-04-12 15:56:51 -0500854 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600855 except TypeError:
856 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600857
858 if not quiet:
859 if invert:
860 alt_text = "cease to "
861 else:
862 alt_text = ""
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500863 gp.print_timen("Checking every " + str(interval) + " for up to "
864 + str(wait_time) + " for the state of the machine to "
865 + alt_text + "match the state shown below.")
Michael Walsh3eb50022017-03-21 11:27:30 -0500866 gp.print_var(match_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600867
Michael Walshf893ba02017-01-10 10:28:05 -0600868 if quiet:
Michael Walsh341c21e2017-01-17 16:25:20 -0600869 print_string = ""
Michael Walshf893ba02017-01-10 10:28:05 -0600870 else:
Michael Walsh341c21e2017-01-17 16:25:20 -0600871 print_string = "#"
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600872
873 debug = int(BuiltIn().get_variable_value("${debug}", "0"))
874 if debug:
875 # In debug we print state so no need to print the "#".
876 print_string = ""
877 check_state_quiet = 1 - debug
Michael Walsh70369fd2016-11-22 11:25:57 -0600878 cmd_buf = ["Check State", match_state, "invert=${" + str(invert) + "}",
Michael Walshf893ba02017-01-10 10:28:05 -0600879 "print_string=" + print_string, "openbmc_host=" + openbmc_host,
Michael Walsh70369fd2016-11-22 11:25:57 -0600880 "openbmc_username=" + openbmc_username,
881 "openbmc_password=" + openbmc_password, "os_host=" + os_host,
882 "os_username=" + os_username, "os_password=" + os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600883 "quiet=${" + str(check_state_quiet) + "}"]
Michael Walshedb5c942019-03-28 12:40:50 -0500884 gp.dprint_issuing(cmd_buf)
Michael Walsh619aa332017-04-12 15:56:51 -0500885 try:
886 state = BuiltIn().wait_until_keyword_succeeds(wait_time, interval,
887 *cmd_buf)
888 except AssertionError as my_assertion_error:
889 gp.printn()
890 message = my_assertion_error.args[0]
891 BuiltIn().fail(message)
892
Michael Walshfd5a8682019-02-01 14:28:42 -0600893 if exit_wait_early_message:
894 # The global exit_wait_early_message was set by a signal handler
895 # indicating that we should fail.
896 message = exit_wait_early_message
897 # Clear the exit_wait_early_message variable for future use.
898 set_exit_wait_early_message("")
899 BuiltIn().fail(gp.sprint_error(message))
900
Michael Walsh70369fd2016-11-22 11:25:57 -0600901 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500902 gp.printn()
Michael Walsh70369fd2016-11-22 11:25:57 -0600903 if invert:
Michael Walsh3eb50022017-03-21 11:27:30 -0500904 gp.print_timen("The states no longer match:")
Michael Walsh70369fd2016-11-22 11:25:57 -0600905 else:
Michael Walsh3eb50022017-03-21 11:27:30 -0500906 gp.print_timen("The states match:")
907 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600908
909 return state
910
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600911
Michael Walsh2a0df682019-09-27 17:19:27 -0500912def set_start_boot_seconds(value=0):
913 global start_boot_seconds
914 start_boot_seconds = int(value)
915
916
917set_start_boot_seconds(0)
918
919
Michael Walsh619aa332017-04-12 15:56:51 -0500920def wait_for_comm_cycle(start_boot_seconds,
921 quiet=None):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600922 r"""
Michael Walsh2a0df682019-09-27 17:19:27 -0500923 Wait for the BMC uptime to be less than elapsed_boot_time.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600924
Michael Walsh2a0df682019-09-27 17:19:27 -0500925 This function will tolerate an expected loss of communication to the BMC.
926 This function is useful when some kind of reboot has been initiated by the
927 caller.
928
929 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600930 start_boot_seconds The time that the boot test started. The format is the
931 epoch time in seconds, i.e. the number of seconds since
932 1970-01-01 00:00:00 UTC. This value should be obtained
933 from the BMC so that it is not dependent on any kind of
934 synchronization between this machine and the target BMC
935 This will allow this program to work correctly even in
936 a simulated environment. This value should be obtained
937 by the caller prior to initiating a reboot. It can be
938 obtained as follows:
939 state = st.get_state(req_states=['epoch_seconds'])
940 """
941
Michael Walsh619aa332017-04-12 15:56:51 -0500942 quiet = int(gp.get_var_value(quiet, 0))
943
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600944 # Validate parms.
Michael Walsh2a0df682019-09-27 17:19:27 -0500945 error_message = gv.valid_integer(start_boot_seconds)
946 if error_message:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600947 BuiltIn().fail(gp.sprint_error(error_message))
948
Michael Walsh2a0df682019-09-27 17:19:27 -0500949 # Wait for uptime to be less than elapsed_boot_time.
950 set_start_boot_seconds(start_boot_seconds)
951 expr = 'int(float(state[\'uptime\'])) < int(state[\'elapsed_boot_time\'])'
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500952 match_state = DotDict([('uptime', '^[0-9\\.]+$'),
Michael Walsh2a0df682019-09-27 17:19:27 -0500953 ('elapsed_boot_time', '^[0-9]+$'),
954 (expressions_key(), [expr])])
David Shawe9192562020-09-28 11:00:45 -0500955 wait_state(match_state, wait_time="12 mins", interval="5 seconds")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600956
Michael Walsh619aa332017-04-12 15:56:51 -0500957 gp.qprint_timen("Verifying that REST API interface is working.")
Michael Walshb95eb542017-03-31 09:39:20 -0500958 match_state = DotDict([('rest', '^1$')])
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600959 state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")