blob: 3eb347326730dd3a10a6c852a9cd524304389b37 [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 Walsh8fae6ea2017-02-20 16:14:44 -060084# valid_os_req_states and default_os_req_states are used by the os_get_state
85# function.
86# valid_os_req_states is a list of state information supported by the
87# get_os_state function.
88valid_os_req_states = ['os_ping',
89 'os_login',
90 'os_run_cmd']
Michael Sheposbdd1dce2020-12-10 11:51:58 -060091
Michael Walsh8fae6ea2017-02-20 16:14:44 -060092# When a user calls get_os_state w/o specifying req_states,
93# default_os_req_states is used as its value.
94default_os_req_states = ['os_ping',
95 'os_login',
96 'os_run_cmd']
97
98# Presently, some BMCs appear to not keep time very well. This environment
99# variable directs the get_state function to use either the BMC's epoch time
100# or the local epoch time.
101USE_BMC_EPOCH_TIME = int(os.environ.get('USE_BMC_EPOCH_TIME', 0))
Michael Walsh341c21e2017-01-17 16:25:20 -0600102
Michael Walsh619aa332017-04-12 15:56:51 -0500103# Useful state constant definition(s).
Michael Sheposda40c1d2020-12-01 22:30:00 -0600104if not redfish_support_trans_state:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600105 # When a user calls get_state w/o specifying req_states, default_req_states
106 # is used as its value.
107 default_req_states = ['rest',
108 'chassis',
109 'bmc',
110 'boot_progress',
111 'operating_system',
112 'host',
113 'os_ping',
114 'os_login',
115 'os_run_cmd']
116
117 # valid_req_states is a list of sub states supported by the get_state function.
118 # valid_req_states, default_req_states and master_os_up_match are used by the
119 # get_state function.
120
121 valid_req_states = ['ping',
122 'packet_loss',
123 'uptime',
124 'epoch_seconds',
125 'elapsed_boot_time',
126 'rest',
127 'chassis',
128 'requested_chassis',
129 'bmc',
130 'requested_bmc',
131 'boot_progress',
132 'operating_system',
133 'host',
134 'requested_host',
135 'attempts_left',
136 'os_ping',
137 'os_login',
138 'os_run_cmd']
139
Michael Sheposda40c1d2020-12-01 22:30:00 -0600140 # default_state is an initial value which may be of use to callers.
141 default_state = DotDict([('rest', '1'),
142 ('chassis', 'On'),
143 ('bmc', 'Ready'),
144 ('boot_progress', 'OSStart'),
145 ('operating_system', 'BootComplete'),
146 ('host', 'Running'),
147 ('os_ping', '1'),
148 ('os_login', '1'),
149 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500150
Michael Sheposda40c1d2020-12-01 22:30:00 -0600151 # A match state for checking that the system is at "standby".
152 standby_match_state = DotDict([('rest', '^1$'),
153 ('chassis', '^Off$'),
154 ('bmc', '^Ready$'),
155 ('boot_progress', '^Off|Unspecified$'),
156 ('operating_system', '^Inactive$'),
157 ('host', '^Off$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500158
Michael Sheposda40c1d2020-12-01 22:30:00 -0600159 # A match state for checking that the system is at "os running".
160 os_running_match_state = DotDict([('chassis', '^On$'),
161 ('bmc', '^Ready$'),
162 ('boot_progress',
163 'FW Progress, Starting OS|OSStart'),
164 ('operating_system', 'BootComplete'),
165 ('host', '^Running$'),
166 ('os_ping', '^1$'),
167 ('os_login', '^1$'),
168 ('os_run_cmd', '^1$')])
169
170 # A master dictionary to determine whether the os may be up.
171 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Walsh7dc885b2018-03-14 17:51:59 -0500172 ('bmc', '^Ready$'),
173 ('boot_progress',
174 'FW Progress, Starting OS|OSStart'),
175 ('operating_system', 'BootComplete'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600176 ('host', '^Running|Quiesced$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500177
Michael Sheposda40c1d2020-12-01 22:30:00 -0600178 invalid_state_match = DotDict([('rest', '^$'),
179 ('chassis', '^$'),
180 ('bmc', '^$'),
181 ('boot_progress', '^$'),
182 ('operating_system', '^$'),
183 ('host', '^$')])
184else:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600185 # When a user calls get_state w/o specifying req_states, default_req_states
186 # is used as its value.
187 default_req_states = ['redfish',
188 'chassis',
189 'bmc',
190 'boot_progress',
191 'host',
192 'os_ping',
193 'os_login',
194 'os_run_cmd']
195
196 # valid_req_states is a list of sub states supported by the get_state function.
197 # valid_req_states, default_req_states and master_os_up_match are used by the
198 # get_state function.
199
200 valid_req_states = ['ping',
201 'packet_loss',
202 'uptime',
203 'epoch_seconds',
204 'elapsed_boot_time',
205 'redfish',
206 'chassis',
207 'requested_chassis',
208 'bmc',
209 'requested_bmc',
210 'boot_progress',
211 'host',
212 'requested_host',
213 'attempts_left',
214 'os_ping',
215 'os_login',
216 'os_run_cmd']
217
Michael Sheposda40c1d2020-12-01 22:30:00 -0600218 # default_state is an initial value which may be of use to callers.
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600219 default_state = DotDict([('redfish', '1'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600220 ('chassis', 'On'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600221 ('bmc', 'Enabled'),
222 ('boot_progress', 'SystemHardwareInitializationComplete|OSRunning'),
223 ('host', 'Enabled'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600224 ('os_ping', '1'),
225 ('os_login', '1'),
226 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500227
Michael Sheposda40c1d2020-12-01 22:30:00 -0600228 # A match state for checking that the system is at "standby".
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600229 standby_match_state = DotDict([('redfish', '^1$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600230 ('chassis', '^Off$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600231 ('bmc', '^Enabled$'),
232 ('boot_progress', '^None$'),
233 ('host', '^Disabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600234
235 # A match state for checking that the system is at "os running".
236 os_running_match_state = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600237 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600238 ('boot_progress',
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600239 'SystemHardwareInitializationComplete|OSRunning'),
240 ('host', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600241 ('os_ping', '^1$'),
242 ('os_login', '^1$'),
243 ('os_run_cmd', '^1$')])
244
245 # A master dictionary to determine whether the os may be up.
246 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600247 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600248 ('boot_progress',
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600249 'SystemHardwareInitializationComplete|OSRunning'),
250 ('host', '^Enabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600251
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600252 invalid_state_match = DotDict([('redfish', '^$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600253 ('chassis', '^$'),
254 ('bmc', '^$'),
255 ('boot_progress', '^$'),
256 ('host', '^$')])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500257
Michael Walsh341c21e2017-01-17 16:25:20 -0600258
George Keishing36efbc02018-12-12 10:18:23 -0600259def return_state_constant(state_name='default_state'):
Michael Walsh619aa332017-04-12 15:56:51 -0500260 r"""
Michael Walsh7dc885b2018-03-14 17:51:59 -0500261 Return the named state dictionary constant.
Michael Walsh619aa332017-04-12 15:56:51 -0500262 """
263
George Keishing36efbc02018-12-12 10:18:23 -0600264 return eval(state_name)
Michael Walsh619aa332017-04-12 15:56:51 -0500265
Michael Walsh619aa332017-04-12 15:56:51 -0500266
Michael Walsh70369fd2016-11-22 11:25:57 -0600267def anchor_state(state):
Michael Walsh70369fd2016-11-22 11:25:57 -0600268 r"""
269 Add regular expression anchors ("^" and "$") to the beginning and end of
270 each item in the state dictionary passed in. Return the resulting
271 dictionary.
272
Michael Walsh2a0df682019-09-27 17:19:27 -0500273 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600274 state A dictionary such as the one returned by the get_state()
275 function.
276 """
277
Michael Walsh2ce067a2017-02-27 14:24:07 -0600278 anchored_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500279 for key in anchored_state.keys():
Michael Walsh70369fd2016-11-22 11:25:57 -0600280 anchored_state[key] = "^" + str(anchored_state[key]) + "$"
281
282 return anchored_state
283
Michael Walsh70369fd2016-11-22 11:25:57 -0600284
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600285def strip_anchor_state(state):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600286 r"""
287 Strip regular expression anchors ("^" and "$") from the beginning and end
288 of each item in the state dictionary passed in. Return the resulting
289 dictionary.
290
Michael Walsh2a0df682019-09-27 17:19:27 -0500291 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600292 state A dictionary such as the one returned by the get_state()
293 function.
294 """
295
Michael Walsh2ce067a2017-02-27 14:24:07 -0600296 stripped_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500297 for key in stripped_state.keys():
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600298 stripped_state[key] = stripped_state[key].strip("^$")
299
300 return stripped_state
301
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600302
Michael Walsh2a0df682019-09-27 17:19:27 -0500303def expressions_key():
304 r"""
305 Return expressions key constant.
306 """
307 return '<expressions>'
308
309
Michael Walsh70369fd2016-11-22 11:25:57 -0600310def compare_states(state,
Michael Walsh45ca6e42017-09-14 17:29:12 -0500311 match_state,
312 match_type='and'):
Michael Walsh70369fd2016-11-22 11:25:57 -0600313 r"""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600314 Compare 2 state dictionaries. Return True if they match and False if they
Michael Walsh70369fd2016-11-22 11:25:57 -0600315 don't. Note that the match_state dictionary does not need to have an entry
316 corresponding to each entry in the state dictionary. But for each entry
317 that it does have, the corresponding state entry will be checked for a
318 match.
319
Michael Walsh2a0df682019-09-27 17:19:27 -0500320 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600321 state A state dictionary such as the one returned by the
322 get_state function.
323 match_state A dictionary whose key/value pairs are "state field"/
324 "state value". The state value is interpreted as a
325 regular expression. Every value in this dictionary is
Michael Walsh45ca6e42017-09-14 17:29:12 -0500326 considered. When match_type is 'and', if each and every
327 comparison matches, the two dictionaries are considered to
328 be matching. If match_type is 'or', if any two of the
329 elements compared match, the two dictionaries are
330 considered to be matching.
Michael Walsh2a0df682019-09-27 17:19:27 -0500331
Michael Walsh7dc885b2018-03-14 17:51:59 -0500332 This value may also be any string accepted by
Michael Walsh2a0df682019-09-27 17:19:27 -0500333 return_state_constant (e.g. "standby_match_state"). In
334 such a case this function will call return_state_constant
335 to convert it to a proper dictionary as described above.
336
337 Finally, one special value is accepted for the key field:
338 expression_key(). If such an entry exists, its value is
339 taken to be a list of expressions to be evaluated. These
340 expressions may reference state dictionary entries by
341 simply coding them in standard python syntax (e.g.
342 state['key1']). What follows is an example expression:
343
344 "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
345
346 In this example, if the state dictionary's 'uptime' entry
347 is less than its 'elapsed_boot_time' entry, it would
348 qualify as a match.
Michael Walsh45ca6e42017-09-14 17:29:12 -0500349 match_type This may be 'and' or 'or'.
Michael Walsh70369fd2016-11-22 11:25:57 -0600350 """
351
Michael Walsh2a0df682019-09-27 17:19:27 -0500352 error_message = gv.valid_value(match_type, valid_values=['and', 'or'])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500353 if error_message != "":
354 BuiltIn().fail(gp.sprint_error(error_message))
355
George Keishing36efbc02018-12-12 10:18:23 -0600356 try:
Michael Walsh7dc885b2018-03-14 17:51:59 -0500357 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600358 except TypeError:
359 pass
Michael Walsh7dc885b2018-03-14 17:51:59 -0500360
Michael Walsh45ca6e42017-09-14 17:29:12 -0500361 default_match = (match_type == 'and')
Michael Walsh70369fd2016-11-22 11:25:57 -0600362 for key, match_state_value in match_state.items():
Michael Walsh97df71c2017-03-27 14:33:24 -0500363 # Blank match_state_value means "don't care".
364 if match_state_value == "":
365 continue
Michael Walsh2a0df682019-09-27 17:19:27 -0500366 if key == expressions_key():
367 for expr in match_state_value:
368 # Use python interpreter to evaluate the expression.
369 match = eval(expr)
370 if match != default_match:
371 return match
372 else:
373 try:
374 match = (re.match(match_state_value, str(state[key])) is not None)
375 except KeyError:
376 match = False
377 if match != default_match:
378 return match
Michael Walsh45ca6e42017-09-14 17:29:12 -0500379
380 return default_match
Michael Walsh70369fd2016-11-22 11:25:57 -0600381
Michael Walsh70369fd2016-11-22 11:25:57 -0600382
Michael Walsh70369fd2016-11-22 11:25:57 -0600383def get_os_state(os_host="",
384 os_username="",
385 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600386 req_states=default_os_req_states,
387 os_up=True,
Michael Walsh70369fd2016-11-22 11:25:57 -0600388 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600389 r"""
390 Get component states for the operating system such as ping, login,
391 etc, put them into a dictionary and return them to the caller.
392
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600393 Note that all substate values are strings.
394
Michael Walsh2a0df682019-09-27 17:19:27 -0500395 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600396 os_host The DNS name or IP address of the operating system.
397 This defaults to global ${OS_HOST}.
398 os_username The username to be used to login to the OS.
399 This defaults to global ${OS_USERNAME}.
400 os_password The password to be used to login to the OS.
401 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600402 req_states This is a list of states whose values are being requested by
403 the caller.
404 os_up If the caller knows that the os can't possibly be up, it can
405 improve performance by passing os_up=False. This function
406 will then simply return default values for all requested os
407 sub states.
Michael Walsh70369fd2016-11-22 11:25:57 -0600408 quiet Indicates whether status details (e.g. curl commands) should
409 be written to the console.
410 Defaults to either global value of ${QUIET} or to 1.
411 """
412
Michael Walsh619aa332017-04-12 15:56:51 -0500413 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600414
415 # Set parm defaults where necessary and validate all parms.
416 if os_host == "":
417 os_host = BuiltIn().get_variable_value("${OS_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500418 error_message = gv.valid_value(os_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600419 if error_message != "":
420 BuiltIn().fail(gp.sprint_error(error_message))
421
422 if os_username == "":
423 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500424 error_message = gv.valid_value(os_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600425 if error_message != "":
426 BuiltIn().fail(gp.sprint_error(error_message))
427
428 if os_password == "":
429 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500430 error_message = gv.valid_value(os_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600431 if error_message != "":
432 BuiltIn().fail(gp.sprint_error(error_message))
433
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600434 invalid_req_states = [sub_state for sub_state in req_states
435 if sub_state not in valid_os_req_states]
436 if len(invalid_req_states) > 0:
437 error_message = "The following req_states are not supported:\n" +\
438 gp.sprint_var(invalid_req_states)
439 BuiltIn().fail(gp.sprint_error(error_message))
Michael Walsh70369fd2016-11-22 11:25:57 -0600440
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600441 # Initialize all substate values supported by this function.
442 os_ping = 0
443 os_login = 0
444 os_run_cmd = 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600445
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600446 if os_up:
447 if 'os_ping' in req_states:
448 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600449 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + os_host,
450 print_output=0, show_err=0,
451 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600452 if rc == 0:
453 os_ping = 1
Michael Walsh70369fd2016-11-22 11:25:57 -0600454
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600455 # Programming note: All attributes which do not require an ssh login
456 # should have been processed by this point.
457 master_req_login = ['os_login', 'os_run_cmd']
458 req_login = [sub_state for sub_state in req_states if sub_state in
459 master_req_login]
Michael Walsh97df71c2017-03-27 14:33:24 -0500460 must_login = (len(req_login) > 0)
Michael Walsh70369fd2016-11-22 11:25:57 -0600461
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600462 if must_login:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500463 output, stderr, rc = bsu.os_execute_command("uptime", quiet=quiet,
Michael Walsh7fc33972018-08-07 14:55:03 -0500464 ignore_err=1,
465 time_out=20)
Michael Walsh6a9bd142018-07-24 16:10:29 -0500466 if rc == 0:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600467 os_login = 1
Michael Walsh6a9bd142018-07-24 16:10:29 -0500468 os_run_cmd = 1
Michael Walsh3eb50022017-03-21 11:27:30 -0500469 else:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500470 gp.dprint_vars(output, stderr)
471 gp.dprint_vars(rc, 1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600472
473 os_state = DotDict()
474 for sub_state in req_states:
475 cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")"
476 exec(cmd_buf)
Michael Walsh70369fd2016-11-22 11:25:57 -0600477
478 return os_state
479
Michael Walsh70369fd2016-11-22 11:25:57 -0600480
Michael Walsh70369fd2016-11-22 11:25:57 -0600481def get_state(openbmc_host="",
482 openbmc_username="",
483 openbmc_password="",
484 os_host="",
485 os_username="",
486 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600487 req_states=default_req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600488 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600489 r"""
Michael Walsh619aa332017-04-12 15:56:51 -0500490 Get component states such as chassis state, bmc state, etc, put them into a
Michael Walsh70369fd2016-11-22 11:25:57 -0600491 dictionary and return them to the caller.
492
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600493 Note that all substate values are strings.
494
Michael Walsh2a0df682019-09-27 17:19:27 -0500495 Note: If elapsed_boot_time is included in req_states, it is the caller's
496 duty to call set_start_boot_seconds() in order to set global
497 start_boot_seconds. elapsed_boot_time is the current time minus
498 start_boot_seconds.
499
500 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600501 openbmc_host The DNS name or IP address of the BMC.
502 This defaults to global ${OPENBMC_HOST}.
503 openbmc_username The username to be used to login to the BMC.
504 This defaults to global ${OPENBMC_USERNAME}.
505 openbmc_password The password to be used to login to the BMC.
506 This defaults to global ${OPENBMC_PASSWORD}.
507 os_host The DNS name or IP address of the operating system.
508 This defaults to global ${OS_HOST}.
509 os_username The username to be used to login to the OS.
510 This defaults to global ${OS_USERNAME}.
511 os_password The password to be used to login to the OS.
512 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600513 req_states This is a list of states whose values are being requested
514 by the caller.
Michael Walsh70369fd2016-11-22 11:25:57 -0600515 quiet Indicates whether status details (e.g. curl commands)
516 should be written to the console.
517 Defaults to either global value of ${QUIET} or to 1.
518 """
519
Michael Walsh619aa332017-04-12 15:56:51 -0500520 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600521
522 # Set parm defaults where necessary and validate all parms.
523 if openbmc_host == "":
524 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500525 error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600526 if error_message != "":
527 BuiltIn().fail(gp.sprint_error(error_message))
528
529 if openbmc_username == "":
530 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500531 error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600532 if error_message != "":
533 BuiltIn().fail(gp.sprint_error(error_message))
534
535 if openbmc_password == "":
536 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500537 error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600538 if error_message != "":
539 BuiltIn().fail(gp.sprint_error(error_message))
540
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600541 # NOTE: OS parms are optional.
Michael Walsh70369fd2016-11-22 11:25:57 -0600542 if os_host == "":
543 os_host = BuiltIn().get_variable_value("${OS_HOST}")
544 if os_host is None:
545 os_host = ""
546
547 if os_username is "":
548 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
549 if os_username is None:
550 os_username = ""
551
552 if os_password is "":
553 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
554 if os_password is None:
555 os_password = ""
556
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600557 invalid_req_states = [sub_state for sub_state in req_states
558 if sub_state not in valid_req_states]
559 if len(invalid_req_states) > 0:
560 error_message = "The following req_states are not supported:\n" +\
561 gp.sprint_var(invalid_req_states)
562 BuiltIn().fail(gp.sprint_error(error_message))
563
564 # Initialize all substate values supported by this function.
565 ping = 0
566 packet_loss = ''
567 uptime = ''
568 epoch_seconds = ''
Michael Walsh2a0df682019-09-27 17:19:27 -0500569 elapsed_boot_time = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500570 rest = ''
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600571 redfish = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500572 chassis = ''
573 requested_chassis = ''
574 bmc = ''
575 requested_bmc = ''
576 boot_progress = ''
577 operating_system = ''
578 host = ''
579 requested_host = ''
580 attempts_left = ''
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600581
Michael Walsh70369fd2016-11-22 11:25:57 -0600582 # Get the component states.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600583 if 'ping' in req_states:
584 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600585 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + openbmc_host,
586 print_output=0, show_err=0,
587 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600588 if rc == 0:
589 ping = 1
590
591 if 'packet_loss' in req_states:
592 # See if the OS pings.
593 cmd_buf = "ping -c 5 -w 5 " + openbmc_host +\
594 " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'"
George Keishing36efbc02018-12-12 10:18:23 -0600595 rc, out_buf = gc.shell_cmd(cmd_buf,
596 print_output=0, show_err=0,
597 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600598 if rc == 0:
599 packet_loss = out_buf.rstrip("\n")
600
Michael Walshe53e47a2017-06-30 17:03:24 -0500601 if 'uptime' in req_states:
Michael Walshfa765932017-10-13 14:07:22 -0500602 # Sometimes reading uptime results in a blank value. Call with
603 # wait_until_keyword_succeeds to ensure a non-blank value is obtained.
604 remote_cmd_buf = "read uptime filler 2>/dev/null < /proc/uptime" +\
605 " && [ ! -z \"${uptime}\" ] && echo ${uptime}"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500606 cmd_buf = ["BMC Execute Command",
Michael Walsh888d85a2019-04-18 11:03:28 -0500607 re.sub('\\$', '\\$', remote_cmd_buf), 'quiet=1',
608 'test_mode=0']
Michael Walsh2a0df682019-09-27 17:19:27 -0500609 gp.qprint_issuing(cmd_buf, 0)
610 gp.qprint_issuing(remote_cmd_buf, 0)
Michael Walshfa765932017-10-13 14:07:22 -0500611 try:
612 stdout, stderr, rc =\
Michael Walshcaccd852017-11-01 17:58:41 -0500613 BuiltIn().wait_until_keyword_succeeds("10 sec", "0 sec",
Michael Walshfa765932017-10-13 14:07:22 -0500614 *cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500615 if rc == 0 and stderr == "":
616 uptime = stdout
Michael Walshfa765932017-10-13 14:07:22 -0500617 except AssertionError as my_assertion_error:
618 pass
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600619
Michael Walsh2a0df682019-09-27 17:19:27 -0500620 if 'epoch_seconds' in req_states or 'elapsed_boot_time' in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600621 date_cmd_buf = "date -u +%s"
622 if USE_BMC_EPOCH_TIME:
Michael Walshe53e47a2017-06-30 17:03:24 -0500623 cmd_buf = ["BMC Execute Command", date_cmd_buf, 'quiet=${1}']
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600624 if not quiet:
Michael Walshedb5c942019-03-28 12:40:50 -0500625 gp.print_issuing(cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500626 status, ret_values = \
627 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
628 if status == "PASS":
629 stdout, stderr, rc = ret_values
630 if rc == 0 and stderr == "":
631 epoch_seconds = stdout.rstrip("\n")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600632 else:
633 shell_rc, out_buf = gc.cmd_fnc_u(date_cmd_buf,
Michael Walshfa765932017-10-13 14:07:22 -0500634 quiet=quiet,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600635 print_output=0)
636 if shell_rc == 0:
637 epoch_seconds = out_buf.rstrip("\n")
638
Michael Walsh2a0df682019-09-27 17:19:27 -0500639 if 'elapsed_boot_time' in req_states:
640 global start_boot_seconds
641 elapsed_boot_time = int(epoch_seconds) - start_boot_seconds
642
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600643 if not redfish_support_trans_state:
644 master_req_rest = ['rest', 'host', 'requested_host', 'operating_system',
645 'attempts_left', 'boot_progress', 'chassis',
646 'requested_chassis' 'bmc' 'requested_bmc']
Michael Walsh56749222017-09-29 15:26:07 -0500647
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600648 req_rest = [sub_state for sub_state in req_states if sub_state in
649 master_req_rest]
650 need_rest = (len(req_rest) > 0)
651 state = DotDict()
652 if need_rest:
653 cmd_buf = ["Read Properties", SYSTEM_STATE_URI + "enumerate",
654 "quiet=${" + str(quiet) + "}", "timeout=30"]
655 gp.dprint_issuing(cmd_buf)
656 status, ret_values = \
657 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
658 if status == "PASS":
659 state['rest'] = '1'
660 else:
661 state['rest'] = '0'
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600662
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600663 if int(state['rest']):
664 for url_path in ret_values:
George Keishing3adda952021-02-11 13:23:51 -0600665 # Skip conflicting "CurrentHostState" URL from the enum
666 # /xyz/openbmc_project/state/hypervisor0
667 if "hypervisor0" in url_path:
668 continue
669
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600670 for attr_name in ret_values[url_path]:
671 # Create a state key value based on the attr_name.
672 try:
673 ret_values[url_path][attr_name] = \
674 re.sub(r'.*\.', "",
675 ret_values[url_path][attr_name])
676 except TypeError:
677 pass
678 # Do some key name manipulations.
679 new_attr_name = re.sub(r'^Current|(State|Transition)$',
680 "", attr_name)
681 new_attr_name = re.sub(r'BMC', r'Bmc', new_attr_name)
682 new_attr_name = re.sub(r'([A-Z][a-z])', r'_\1',
683 new_attr_name)
684 new_attr_name = new_attr_name.lower().lstrip("_")
685 new_attr_name = re.sub(r'power', r'chassis', new_attr_name)
686 if new_attr_name in req_states:
687 state[new_attr_name] = ret_values[url_path][attr_name]
688 else:
689 master_req_rf = ['redfish', 'host', 'requested_host',
690 'attempts_left', 'boot_progress', 'chassis',
691 'requested_chassis' 'bmc' 'requested_bmc']
692
693 req_rf = [sub_state for sub_state in req_states if sub_state in
694 master_req_rf]
695 need_rf = (len(req_rf) > 0)
696 state = DotDict()
697 if need_rf:
698 cmd_buf = ["Redfish Get States"]
699 gp.dprint_issuing(cmd_buf)
700 status, ret_values = \
701 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
702 gp.dprint_vars(status, ret_values)
703 if status == "PASS":
704 state['redfish'] = '1'
705 else:
706 state['redfish'] = '0'
707
708 if int(state['redfish']):
709 state['chassis'] = ret_values['chassis']
710 state['boot_progress'] = ret_values['boot_progress']
711 state['host'] = ret_values['host']
712 state['bmc'] = ret_values['bmc']
Michael Walshb95eb542017-03-31 09:39:20 -0500713
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600714 for sub_state in req_states:
Michael Walsh56749222017-09-29 15:26:07 -0500715 if sub_state in state:
716 continue
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600717 if sub_state.startswith("os_"):
718 # We pass "os_" requests on to get_os_state.
719 continue
720 cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")"
721 exec(cmd_buf)
722
723 if os_host == "":
724 # The caller has not specified an os_host so as far as we're concerned,
725 # it doesn't exist.
726 return state
727
728 os_req_states = [sub_state for sub_state in req_states
729 if sub_state.startswith('os_')]
730
731 if len(os_req_states) > 0:
732 # The caller has specified an os_host and they have requested
733 # information on os substates.
734
735 # Based on the information gathered on bmc, we'll try to make a
736 # determination of whether the os is even up. We'll pass the result
737 # of that assessment to get_os_state to enhance performance.
738 os_up_match = DotDict()
739 for sub_state in master_os_up_match:
740 if sub_state in req_states:
741 os_up_match[sub_state] = master_os_up_match[sub_state]
Michael Walsh70369fd2016-11-22 11:25:57 -0600742 os_up = compare_states(state, os_up_match)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600743 os_state = get_os_state(os_host=os_host,
744 os_username=os_username,
745 os_password=os_password,
746 req_states=os_req_states,
747 os_up=os_up,
748 quiet=quiet)
749 # Append os_state dictionary to ours.
750 state.update(os_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600751
752 return state
753
Michael Walsh70369fd2016-11-22 11:25:57 -0600754
Michael Walshfd5a8682019-02-01 14:28:42 -0600755exit_wait_early_message = ""
756
757
758def set_exit_wait_early_message(value):
759 r"""
760 Set global exit_wait_early_message to the indicated value.
761
762 This is a mechanism by which the programmer can do an early exit from
763 wait_until_keyword_succeeds() based on some special condition.
764
765 Description of argument(s):
766 value The value to assign to the global
767 exit_wait_early_message.
768 """
769
770 global exit_wait_early_message
771 exit_wait_early_message = value
772
773
Michael Walsh70369fd2016-11-22 11:25:57 -0600774def check_state(match_state,
775 invert=0,
776 print_string="",
777 openbmc_host="",
778 openbmc_username="",
779 openbmc_password="",
780 os_host="",
781 os_username="",
782 os_password="",
783 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600784 r"""
785 Check that the Open BMC machine's composite state matches the specified
786 state. On success, this keyword returns the machine's composite state as a
787 dictionary.
788
Michael Walsh2a0df682019-09-27 17:19:27 -0500789 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600790 match_state A dictionary whose key/value pairs are "state field"/
791 "state value". The state value is interpreted as a
792 regular expression. Example call from robot:
Michael Walsh341c21e2017-01-17 16:25:20 -0600793 ${match_state}= Create Dictionary chassis=^On$
794 ... bmc=^Ready$
Michael Walsh01975fa2017-08-20 20:51:36 -0500795 ... boot_progress=^OSStart$
Michael Walsh70369fd2016-11-22 11:25:57 -0600796 ${state}= Check State &{match_state}
797 invert If this flag is set, this function will succeed if the
798 states do NOT match.
799 print_string This function will print this string to the console prior
800 to getting the state.
801 openbmc_host The DNS name or IP address of the BMC.
802 This defaults to global ${OPENBMC_HOST}.
803 openbmc_username The username to be used to login to the BMC.
804 This defaults to global ${OPENBMC_USERNAME}.
805 openbmc_password The password to be used to login to the BMC.
806 This defaults to global ${OPENBMC_PASSWORD}.
807 os_host The DNS name or IP address of the operating system.
808 This defaults to global ${OS_HOST}.
809 os_username The username to be used to login to the OS.
810 This defaults to global ${OS_USERNAME}.
811 os_password The password to be used to login to the OS.
812 This defaults to global ${OS_PASSWORD}.
813 quiet Indicates whether status details should be written to the
814 console. Defaults to either global value of ${QUIET} or
815 to 1.
816 """
817
Michael Walsh619aa332017-04-12 15:56:51 -0500818 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600819
Michael Walshedb5c942019-03-28 12:40:50 -0500820 gp.gp_print(print_string)
821
822 try:
823 match_state = return_state_constant(match_state)
824 except TypeError:
825 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600826
Michael Walsh2a0df682019-09-27 17:19:27 -0500827 req_states = list(match_state.keys())
828 # Remove special-case match key from req_states.
829 if expressions_key() in req_states:
830 req_states.remove(expressions_key())
Michael Walsh70369fd2016-11-22 11:25:57 -0600831 # Initialize state.
832 state = get_state(openbmc_host=openbmc_host,
833 openbmc_username=openbmc_username,
834 openbmc_password=openbmc_password,
835 os_host=os_host,
836 os_username=os_username,
837 os_password=os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600838 req_states=req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600839 quiet=quiet)
840 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500841 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600842
Michael Walshfd5a8682019-02-01 14:28:42 -0600843 if exit_wait_early_message != "":
844 # The exit_wait_early_message has been set by a signal handler so we
845 # will exit "successfully". It is incumbent upon the calling function
846 # (e.g. wait_state) to check/clear this variable and to fail
847 # appropriately.
848 return state
849
Michael Walsh70369fd2016-11-22 11:25:57 -0600850 match = compare_states(state, match_state)
851
852 if invert and match:
853 fail_msg = "The current state of the machine matches the match" +\
854 " state:\n" + gp.sprint_varx("state", state)
855 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
856 elif not invert and not match:
857 fail_msg = "The current state of the machine does NOT match the" +\
858 " match state:\n" +\
859 gp.sprint_varx("state", state)
860 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
861
862 return state
863
Michael Walsh70369fd2016-11-22 11:25:57 -0600864
Michael Walshf893ba02017-01-10 10:28:05 -0600865def wait_state(match_state=(),
Michael Walsh70369fd2016-11-22 11:25:57 -0600866 wait_time="1 min",
867 interval="1 second",
868 invert=0,
869 openbmc_host="",
870 openbmc_username="",
871 openbmc_password="",
872 os_host="",
873 os_username="",
874 os_password="",
875 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600876 r"""
877 Wait for the Open BMC machine's composite state to match the specified
878 state. On success, this keyword returns the machine's composite state as
879 a dictionary.
880
Michael Walsh2a0df682019-09-27 17:19:27 -0500881 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600882 match_state A dictionary whose key/value pairs are "state field"/
883 "state value". See check_state (above) for details.
Michael Walsh619aa332017-04-12 15:56:51 -0500884 This value may also be any string accepted by
885 return_state_constant (e.g. "standby_match_state").
886 In such a case this function will call
887 return_state_constant to convert it to a proper
888 dictionary as described above.
Michael Walsh70369fd2016-11-22 11:25:57 -0600889 wait_time The total amount of time to wait for the desired state.
890 This value may be expressed in Robot Framework's time
891 format (e.g. 1 minute, 2 min 3 s, 4.5).
892 interval The amount of time between state checks.
893 This value may be expressed in Robot Framework's time
894 format (e.g. 1 minute, 2 min 3 s, 4.5).
895 invert If this flag is set, this function will for the state of
896 the machine to cease to match the match state.
897 openbmc_host The DNS name or IP address of the BMC.
898 This defaults to global ${OPENBMC_HOST}.
899 openbmc_username The username to be used to login to the BMC.
900 This defaults to global ${OPENBMC_USERNAME}.
901 openbmc_password The password to be used to login to the BMC.
902 This defaults to global ${OPENBMC_PASSWORD}.
903 os_host The DNS name or IP address of the operating system.
904 This defaults to global ${OS_HOST}.
905 os_username The username to be used to login to the OS.
906 This defaults to global ${OS_USERNAME}.
907 os_password The password to be used to login to the OS.
908 This defaults to global ${OS_PASSWORD}.
909 quiet Indicates whether status details should be written to the
910 console. Defaults to either global value of ${QUIET} or
911 to 1.
912 """
913
Michael Walsh619aa332017-04-12 15:56:51 -0500914 quiet = int(gp.get_var_value(quiet, 0))
915
George Keishing36efbc02018-12-12 10:18:23 -0600916 try:
Michael Walsh619aa332017-04-12 15:56:51 -0500917 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600918 except TypeError:
919 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600920
921 if not quiet:
922 if invert:
923 alt_text = "cease to "
924 else:
925 alt_text = ""
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500926 gp.print_timen("Checking every " + str(interval) + " for up to "
927 + str(wait_time) + " for the state of the machine to "
928 + alt_text + "match the state shown below.")
Michael Walsh3eb50022017-03-21 11:27:30 -0500929 gp.print_var(match_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600930
Michael Walshf893ba02017-01-10 10:28:05 -0600931 if quiet:
Michael Walsh341c21e2017-01-17 16:25:20 -0600932 print_string = ""
Michael Walshf893ba02017-01-10 10:28:05 -0600933 else:
Michael Walsh341c21e2017-01-17 16:25:20 -0600934 print_string = "#"
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600935
936 debug = int(BuiltIn().get_variable_value("${debug}", "0"))
937 if debug:
938 # In debug we print state so no need to print the "#".
939 print_string = ""
940 check_state_quiet = 1 - debug
Michael Walsh70369fd2016-11-22 11:25:57 -0600941 cmd_buf = ["Check State", match_state, "invert=${" + str(invert) + "}",
Michael Walshf893ba02017-01-10 10:28:05 -0600942 "print_string=" + print_string, "openbmc_host=" + openbmc_host,
Michael Walsh70369fd2016-11-22 11:25:57 -0600943 "openbmc_username=" + openbmc_username,
944 "openbmc_password=" + openbmc_password, "os_host=" + os_host,
945 "os_username=" + os_username, "os_password=" + os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600946 "quiet=${" + str(check_state_quiet) + "}"]
Michael Walshedb5c942019-03-28 12:40:50 -0500947 gp.dprint_issuing(cmd_buf)
Michael Walsh619aa332017-04-12 15:56:51 -0500948 try:
949 state = BuiltIn().wait_until_keyword_succeeds(wait_time, interval,
950 *cmd_buf)
951 except AssertionError as my_assertion_error:
952 gp.printn()
953 message = my_assertion_error.args[0]
954 BuiltIn().fail(message)
955
Michael Walshfd5a8682019-02-01 14:28:42 -0600956 if exit_wait_early_message:
957 # The global exit_wait_early_message was set by a signal handler
958 # indicating that we should fail.
959 message = exit_wait_early_message
960 # Clear the exit_wait_early_message variable for future use.
961 set_exit_wait_early_message("")
962 BuiltIn().fail(gp.sprint_error(message))
963
Michael Walsh70369fd2016-11-22 11:25:57 -0600964 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500965 gp.printn()
Michael Walsh70369fd2016-11-22 11:25:57 -0600966 if invert:
Michael Walsh3eb50022017-03-21 11:27:30 -0500967 gp.print_timen("The states no longer match:")
Michael Walsh70369fd2016-11-22 11:25:57 -0600968 else:
Michael Walsh3eb50022017-03-21 11:27:30 -0500969 gp.print_timen("The states match:")
970 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600971
972 return state
973
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600974
Michael Walsh2a0df682019-09-27 17:19:27 -0500975def set_start_boot_seconds(value=0):
976 global start_boot_seconds
977 start_boot_seconds = int(value)
978
979
980set_start_boot_seconds(0)
981
982
Michael Walsh619aa332017-04-12 15:56:51 -0500983def wait_for_comm_cycle(start_boot_seconds,
984 quiet=None):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600985 r"""
Michael Walsh2a0df682019-09-27 17:19:27 -0500986 Wait for the BMC uptime to be less than elapsed_boot_time.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600987
Michael Walsh2a0df682019-09-27 17:19:27 -0500988 This function will tolerate an expected loss of communication to the BMC.
989 This function is useful when some kind of reboot has been initiated by the
990 caller.
991
992 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600993 start_boot_seconds The time that the boot test started. The format is the
994 epoch time in seconds, i.e. the number of seconds since
995 1970-01-01 00:00:00 UTC. This value should be obtained
996 from the BMC so that it is not dependent on any kind of
997 synchronization between this machine and the target BMC
998 This will allow this program to work correctly even in
999 a simulated environment. This value should be obtained
1000 by the caller prior to initiating a reboot. It can be
1001 obtained as follows:
1002 state = st.get_state(req_states=['epoch_seconds'])
1003 """
1004
Michael Walsh619aa332017-04-12 15:56:51 -05001005 quiet = int(gp.get_var_value(quiet, 0))
1006
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001007 # Validate parms.
Michael Walsh2a0df682019-09-27 17:19:27 -05001008 error_message = gv.valid_integer(start_boot_seconds)
1009 if error_message:
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001010 BuiltIn().fail(gp.sprint_error(error_message))
1011
Michael Walsh2a0df682019-09-27 17:19:27 -05001012 # Wait for uptime to be less than elapsed_boot_time.
1013 set_start_boot_seconds(start_boot_seconds)
1014 expr = 'int(float(state[\'uptime\'])) < int(state[\'elapsed_boot_time\'])'
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001015 match_state = DotDict([('uptime', '^[0-9\\.]+$'),
Michael Walsh2a0df682019-09-27 17:19:27 -05001016 ('elapsed_boot_time', '^[0-9]+$'),
1017 (expressions_key(), [expr])])
David Shawe9192562020-09-28 11:00:45 -05001018 wait_state(match_state, wait_time="12 mins", interval="5 seconds")
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001019
Michael Sheposbdd1dce2020-12-10 11:51:58 -06001020 gp.qprint_timen("Verifying that REST/Redfish API interface is working.")
1021 if not redfish_support_trans_state:
1022 match_state = DotDict([('rest', '^1$')])
1023 else:
1024 match_state = DotDict([('redfish', '^1$')])
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001025 state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")