blob: d2e08530a789ba5ecd47283e2f4a52b60670fe62 [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 Walsh619aa332017-04-12 15:56:51 -050081# When a user calls get_state w/o specifying req_states, default_req_states
82# is used as its value.
83default_req_states = ['rest',
84 'chassis',
85 'bmc',
86 'boot_progress',
Michael Walsh56749222017-09-29 15:26:07 -050087 'operating_system',
Michael Walsh619aa332017-04-12 15:56:51 -050088 'host',
89 'os_ping',
90 'os_login',
91 'os_run_cmd']
Michael Walsh8fae6ea2017-02-20 16:14:44 -060092
Michael Walsh619aa332017-04-12 15:56:51 -050093# valid_req_states is a list of sub states supported by the get_state function.
94# valid_req_states, default_req_states and master_os_up_match are used by the
95# get_state function.
96valid_req_states = ['ping',
97 'packet_loss',
98 'uptime',
99 'epoch_seconds',
Michael Walsh2a0df682019-09-27 17:19:27 -0500100 'elapsed_boot_time',
Michael Walsh619aa332017-04-12 15:56:51 -0500101 'rest',
102 'chassis',
Michael Walsh56749222017-09-29 15:26:07 -0500103 'requested_chassis',
Michael Walsh619aa332017-04-12 15:56:51 -0500104 'bmc',
Michael Walsh56749222017-09-29 15:26:07 -0500105 'requested_bmc',
Michael Walsh619aa332017-04-12 15:56:51 -0500106 'boot_progress',
Michael Walsh56749222017-09-29 15:26:07 -0500107 'operating_system',
Michael Walsh619aa332017-04-12 15:56:51 -0500108 'host',
Michael Walsh56749222017-09-29 15:26:07 -0500109 'requested_host',
110 'attempts_left',
Michael Walsh619aa332017-04-12 15:56:51 -0500111 'os_ping',
112 'os_login',
113 'os_run_cmd']
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600114
115# valid_os_req_states and default_os_req_states are used by the os_get_state
116# function.
117# valid_os_req_states is a list of state information supported by the
118# get_os_state function.
119valid_os_req_states = ['os_ping',
120 'os_login',
121 'os_run_cmd']
122# When a user calls get_os_state w/o specifying req_states,
123# default_os_req_states is used as its value.
124default_os_req_states = ['os_ping',
125 'os_login',
126 'os_run_cmd']
127
128# Presently, some BMCs appear to not keep time very well. This environment
129# variable directs the get_state function to use either the BMC's epoch time
130# or the local epoch time.
131USE_BMC_EPOCH_TIME = int(os.environ.get('USE_BMC_EPOCH_TIME', 0))
Michael Walsh341c21e2017-01-17 16:25:20 -0600132
Michael Walsh619aa332017-04-12 15:56:51 -0500133# Useful state constant definition(s).
Michael Walsh619aa332017-04-12 15:56:51 -0500134# default_state is an initial value which may be of use to callers.
135default_state = DotDict([('rest', '1'),
136 ('chassis', 'On'),
137 ('bmc', 'Ready'),
Michael Walsh01975fa2017-08-20 20:51:36 -0500138 ('boot_progress', 'OSStart'),
Michael Walsh56749222017-09-29 15:26:07 -0500139 ('operating_system', 'BootComplete'),
Michael Walsh619aa332017-04-12 15:56:51 -0500140 ('host', 'Running'),
141 ('os_ping', '1'),
142 ('os_login', '1'),
143 ('os_run_cmd', '1')])
144
Michael Walsh7dc885b2018-03-14 17:51:59 -0500145# A match state for checking that the system is at "standby".
146standby_match_state = DotDict([('rest', '^1$'),
147 ('chassis', '^Off$'),
148 ('bmc', '^Ready$'),
George Keishing6a69d262019-04-03 03:45:27 -0500149 ('boot_progress', '^Off|Unspecified$'),
150 ('operating_system', '^Inactive$'),
151 ('host', '^Off$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500152
153# A match state for checking that the system is at "os running".
154os_running_match_state = DotDict([('chassis', '^On$'),
155 ('bmc', '^Ready$'),
156 ('boot_progress',
157 'FW Progress, Starting OS|OSStart'),
158 ('operating_system', 'BootComplete'),
159 ('host', '^Running$'),
160 ('os_ping', '^1$'),
161 ('os_login', '^1$'),
162 ('os_run_cmd', '^1$')])
163
Michael Walsh619aa332017-04-12 15:56:51 -0500164# A master dictionary to determine whether the os may be up.
165master_os_up_match = DotDict([('chassis', '^On$'),
166 ('bmc', '^Ready$'),
167 ('boot_progress',
Michael Walsh01975fa2017-08-20 20:51:36 -0500168 'FW Progress, Starting OS|OSStart'),
Michael Walsh56749222017-09-29 15:26:07 -0500169 ('operating_system', 'BootComplete'),
Michael Walsh192d5e72018-11-01 14:09:11 -0500170 ('host', '^Running|Quiesced$')])
Michael Walsh619aa332017-04-12 15:56:51 -0500171
Michael Walsh45ca6e42017-09-14 17:29:12 -0500172invalid_state_match = DotDict([('rest', '^$'),
173 ('chassis', '^$'),
174 ('bmc', '^$'),
175 ('boot_progress', '^$'),
Michael Walsh56749222017-09-29 15:26:07 -0500176 ('operating_system', '^$'),
Michael Walsh45ca6e42017-09-14 17:29:12 -0500177 ('host', '^$')])
178
Michael Walsh341c21e2017-01-17 16:25:20 -0600179
George Keishing36efbc02018-12-12 10:18:23 -0600180def return_state_constant(state_name='default_state'):
Michael Walsh619aa332017-04-12 15:56:51 -0500181 r"""
Michael Walsh7dc885b2018-03-14 17:51:59 -0500182 Return the named state dictionary constant.
Michael Walsh619aa332017-04-12 15:56:51 -0500183 """
184
George Keishing36efbc02018-12-12 10:18:23 -0600185 return eval(state_name)
Michael Walsh619aa332017-04-12 15:56:51 -0500186
Michael Walsh619aa332017-04-12 15:56:51 -0500187
Michael Walsh70369fd2016-11-22 11:25:57 -0600188def anchor_state(state):
Michael Walsh70369fd2016-11-22 11:25:57 -0600189 r"""
190 Add regular expression anchors ("^" and "$") to the beginning and end of
191 each item in the state dictionary passed in. Return the resulting
192 dictionary.
193
Michael Walsh2a0df682019-09-27 17:19:27 -0500194 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600195 state A dictionary such as the one returned by the get_state()
196 function.
197 """
198
Michael Walsh2ce067a2017-02-27 14:24:07 -0600199 anchored_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500200 for key in anchored_state.keys():
Michael Walsh70369fd2016-11-22 11:25:57 -0600201 anchored_state[key] = "^" + str(anchored_state[key]) + "$"
202
203 return anchored_state
204
Michael Walsh70369fd2016-11-22 11:25:57 -0600205
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600206def strip_anchor_state(state):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600207 r"""
208 Strip regular expression anchors ("^" and "$") from the beginning and end
209 of each item in the state dictionary passed in. Return the resulting
210 dictionary.
211
Michael Walsh2a0df682019-09-27 17:19:27 -0500212 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600213 state A dictionary such as the one returned by the get_state()
214 function.
215 """
216
Michael Walsh2ce067a2017-02-27 14:24:07 -0600217 stripped_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500218 for key in stripped_state.keys():
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600219 stripped_state[key] = stripped_state[key].strip("^$")
220
221 return stripped_state
222
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600223
Michael Walsh2a0df682019-09-27 17:19:27 -0500224def expressions_key():
225 r"""
226 Return expressions key constant.
227 """
228 return '<expressions>'
229
230
Michael Walsh70369fd2016-11-22 11:25:57 -0600231def compare_states(state,
Michael Walsh45ca6e42017-09-14 17:29:12 -0500232 match_state,
233 match_type='and'):
Michael Walsh70369fd2016-11-22 11:25:57 -0600234 r"""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600235 Compare 2 state dictionaries. Return True if they match and False if they
Michael Walsh70369fd2016-11-22 11:25:57 -0600236 don't. Note that the match_state dictionary does not need to have an entry
237 corresponding to each entry in the state dictionary. But for each entry
238 that it does have, the corresponding state entry will be checked for a
239 match.
240
Michael Walsh2a0df682019-09-27 17:19:27 -0500241 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600242 state A state dictionary such as the one returned by the
243 get_state function.
244 match_state A dictionary whose key/value pairs are "state field"/
245 "state value". The state value is interpreted as a
246 regular expression. Every value in this dictionary is
Michael Walsh45ca6e42017-09-14 17:29:12 -0500247 considered. When match_type is 'and', if each and every
248 comparison matches, the two dictionaries are considered to
249 be matching. If match_type is 'or', if any two of the
250 elements compared match, the two dictionaries are
251 considered to be matching.
Michael Walsh2a0df682019-09-27 17:19:27 -0500252
Michael Walsh7dc885b2018-03-14 17:51:59 -0500253 This value may also be any string accepted by
Michael Walsh2a0df682019-09-27 17:19:27 -0500254 return_state_constant (e.g. "standby_match_state"). In
255 such a case this function will call return_state_constant
256 to convert it to a proper dictionary as described above.
257
258 Finally, one special value is accepted for the key field:
259 expression_key(). If such an entry exists, its value is
260 taken to be a list of expressions to be evaluated. These
261 expressions may reference state dictionary entries by
262 simply coding them in standard python syntax (e.g.
263 state['key1']). What follows is an example expression:
264
265 "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
266
267 In this example, if the state dictionary's 'uptime' entry
268 is less than its 'elapsed_boot_time' entry, it would
269 qualify as a match.
Michael Walsh45ca6e42017-09-14 17:29:12 -0500270 match_type This may be 'and' or 'or'.
Michael Walsh70369fd2016-11-22 11:25:57 -0600271 """
272
Michael Walsh2a0df682019-09-27 17:19:27 -0500273 error_message = gv.valid_value(match_type, valid_values=['and', 'or'])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500274 if error_message != "":
275 BuiltIn().fail(gp.sprint_error(error_message))
276
George Keishing36efbc02018-12-12 10:18:23 -0600277 try:
Michael Walsh7dc885b2018-03-14 17:51:59 -0500278 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600279 except TypeError:
280 pass
Michael Walsh7dc885b2018-03-14 17:51:59 -0500281
Michael Walsh45ca6e42017-09-14 17:29:12 -0500282 default_match = (match_type == 'and')
Michael Walsh70369fd2016-11-22 11:25:57 -0600283 for key, match_state_value in match_state.items():
Michael Walsh97df71c2017-03-27 14:33:24 -0500284 # Blank match_state_value means "don't care".
285 if match_state_value == "":
286 continue
Michael Walsh2a0df682019-09-27 17:19:27 -0500287 if key == expressions_key():
288 for expr in match_state_value:
289 # Use python interpreter to evaluate the expression.
290 match = eval(expr)
291 if match != default_match:
292 return match
293 else:
294 try:
295 match = (re.match(match_state_value, str(state[key])) is not None)
296 except KeyError:
297 match = False
298 if match != default_match:
299 return match
Michael Walsh45ca6e42017-09-14 17:29:12 -0500300
301 return default_match
Michael Walsh70369fd2016-11-22 11:25:57 -0600302
Michael Walsh70369fd2016-11-22 11:25:57 -0600303
Michael Walsh70369fd2016-11-22 11:25:57 -0600304def get_os_state(os_host="",
305 os_username="",
306 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600307 req_states=default_os_req_states,
308 os_up=True,
Michael Walsh70369fd2016-11-22 11:25:57 -0600309 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600310 r"""
311 Get component states for the operating system such as ping, login,
312 etc, put them into a dictionary and return them to the caller.
313
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600314 Note that all substate values are strings.
315
Michael Walsh2a0df682019-09-27 17:19:27 -0500316 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600317 os_host The DNS name or IP address of the operating system.
318 This defaults to global ${OS_HOST}.
319 os_username The username to be used to login to the OS.
320 This defaults to global ${OS_USERNAME}.
321 os_password The password to be used to login to the OS.
322 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600323 req_states This is a list of states whose values are being requested by
324 the caller.
325 os_up If the caller knows that the os can't possibly be up, it can
326 improve performance by passing os_up=False. This function
327 will then simply return default values for all requested os
328 sub states.
Michael Walsh70369fd2016-11-22 11:25:57 -0600329 quiet Indicates whether status details (e.g. curl commands) should
330 be written to the console.
331 Defaults to either global value of ${QUIET} or to 1.
332 """
333
Michael Walsh619aa332017-04-12 15:56:51 -0500334 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600335
336 # Set parm defaults where necessary and validate all parms.
337 if os_host == "":
338 os_host = BuiltIn().get_variable_value("${OS_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500339 error_message = gv.valid_value(os_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600340 if error_message != "":
341 BuiltIn().fail(gp.sprint_error(error_message))
342
343 if os_username == "":
344 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500345 error_message = gv.valid_value(os_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600346 if error_message != "":
347 BuiltIn().fail(gp.sprint_error(error_message))
348
349 if os_password == "":
350 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500351 error_message = gv.valid_value(os_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600352 if error_message != "":
353 BuiltIn().fail(gp.sprint_error(error_message))
354
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600355 invalid_req_states = [sub_state for sub_state in req_states
356 if sub_state not in valid_os_req_states]
357 if len(invalid_req_states) > 0:
358 error_message = "The following req_states are not supported:\n" +\
359 gp.sprint_var(invalid_req_states)
360 BuiltIn().fail(gp.sprint_error(error_message))
Michael Walsh70369fd2016-11-22 11:25:57 -0600361
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600362 # Initialize all substate values supported by this function.
363 os_ping = 0
364 os_login = 0
365 os_run_cmd = 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600366
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600367 if os_up:
368 if 'os_ping' in req_states:
369 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600370 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + os_host,
371 print_output=0, show_err=0,
372 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600373 if rc == 0:
374 os_ping = 1
Michael Walsh70369fd2016-11-22 11:25:57 -0600375
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600376 # Programming note: All attributes which do not require an ssh login
377 # should have been processed by this point.
378 master_req_login = ['os_login', 'os_run_cmd']
379 req_login = [sub_state for sub_state in req_states if sub_state in
380 master_req_login]
Michael Walsh97df71c2017-03-27 14:33:24 -0500381 must_login = (len(req_login) > 0)
Michael Walsh70369fd2016-11-22 11:25:57 -0600382
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600383 if must_login:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500384 output, stderr, rc = bsu.os_execute_command("uptime", quiet=quiet,
Michael Walsh7fc33972018-08-07 14:55:03 -0500385 ignore_err=1,
386 time_out=20)
Michael Walsh6a9bd142018-07-24 16:10:29 -0500387 if rc == 0:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600388 os_login = 1
Michael Walsh6a9bd142018-07-24 16:10:29 -0500389 os_run_cmd = 1
Michael Walsh3eb50022017-03-21 11:27:30 -0500390 else:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500391 gp.dprint_vars(output, stderr)
392 gp.dprint_vars(rc, 1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600393
394 os_state = DotDict()
395 for sub_state in req_states:
396 cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")"
397 exec(cmd_buf)
Michael Walsh70369fd2016-11-22 11:25:57 -0600398
399 return os_state
400
Michael Walsh70369fd2016-11-22 11:25:57 -0600401
Michael Walsh70369fd2016-11-22 11:25:57 -0600402def get_state(openbmc_host="",
403 openbmc_username="",
404 openbmc_password="",
405 os_host="",
406 os_username="",
407 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600408 req_states=default_req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600409 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600410 r"""
Michael Walsh619aa332017-04-12 15:56:51 -0500411 Get component states such as chassis state, bmc state, etc, put them into a
Michael Walsh70369fd2016-11-22 11:25:57 -0600412 dictionary and return them to the caller.
413
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600414 Note that all substate values are strings.
415
Michael Walsh2a0df682019-09-27 17:19:27 -0500416 Note: If elapsed_boot_time is included in req_states, it is the caller's
417 duty to call set_start_boot_seconds() in order to set global
418 start_boot_seconds. elapsed_boot_time is the current time minus
419 start_boot_seconds.
420
421 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600422 openbmc_host The DNS name or IP address of the BMC.
423 This defaults to global ${OPENBMC_HOST}.
424 openbmc_username The username to be used to login to the BMC.
425 This defaults to global ${OPENBMC_USERNAME}.
426 openbmc_password The password to be used to login to the BMC.
427 This defaults to global ${OPENBMC_PASSWORD}.
428 os_host The DNS name or IP address of the operating system.
429 This defaults to global ${OS_HOST}.
430 os_username The username to be used to login to the OS.
431 This defaults to global ${OS_USERNAME}.
432 os_password The password to be used to login to the OS.
433 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600434 req_states This is a list of states whose values are being requested
435 by the caller.
Michael Walsh70369fd2016-11-22 11:25:57 -0600436 quiet Indicates whether status details (e.g. curl commands)
437 should be written to the console.
438 Defaults to either global value of ${QUIET} or to 1.
439 """
440
Michael Walsh619aa332017-04-12 15:56:51 -0500441 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600442
443 # Set parm defaults where necessary and validate all parms.
444 if openbmc_host == "":
445 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500446 error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600447 if error_message != "":
448 BuiltIn().fail(gp.sprint_error(error_message))
449
450 if openbmc_username == "":
451 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500452 error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600453 if error_message != "":
454 BuiltIn().fail(gp.sprint_error(error_message))
455
456 if openbmc_password == "":
457 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500458 error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600459 if error_message != "":
460 BuiltIn().fail(gp.sprint_error(error_message))
461
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600462 # NOTE: OS parms are optional.
Michael Walsh70369fd2016-11-22 11:25:57 -0600463 if os_host == "":
464 os_host = BuiltIn().get_variable_value("${OS_HOST}")
465 if os_host is None:
466 os_host = ""
467
468 if os_username is "":
469 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
470 if os_username is None:
471 os_username = ""
472
473 if os_password is "":
474 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
475 if os_password is None:
476 os_password = ""
477
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600478 invalid_req_states = [sub_state for sub_state in req_states
479 if sub_state not in valid_req_states]
480 if len(invalid_req_states) > 0:
481 error_message = "The following req_states are not supported:\n" +\
482 gp.sprint_var(invalid_req_states)
483 BuiltIn().fail(gp.sprint_error(error_message))
484
485 # Initialize all substate values supported by this function.
486 ping = 0
487 packet_loss = ''
488 uptime = ''
489 epoch_seconds = ''
Michael Walsh2a0df682019-09-27 17:19:27 -0500490 elapsed_boot_time = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500491 rest = ''
492 chassis = ''
493 requested_chassis = ''
494 bmc = ''
495 requested_bmc = ''
496 boot_progress = ''
497 operating_system = ''
498 host = ''
499 requested_host = ''
500 attempts_left = ''
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600501
Michael Walsh70369fd2016-11-22 11:25:57 -0600502 # Get the component states.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600503 if 'ping' in req_states:
504 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600505 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + openbmc_host,
506 print_output=0, show_err=0,
507 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600508 if rc == 0:
509 ping = 1
510
511 if 'packet_loss' in req_states:
512 # See if the OS pings.
513 cmd_buf = "ping -c 5 -w 5 " + openbmc_host +\
514 " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'"
George Keishing36efbc02018-12-12 10:18:23 -0600515 rc, out_buf = gc.shell_cmd(cmd_buf,
516 print_output=0, show_err=0,
517 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600518 if rc == 0:
519 packet_loss = out_buf.rstrip("\n")
520
Michael Walshe53e47a2017-06-30 17:03:24 -0500521 if 'uptime' in req_states:
Michael Walshfa765932017-10-13 14:07:22 -0500522 # Sometimes reading uptime results in a blank value. Call with
523 # wait_until_keyword_succeeds to ensure a non-blank value is obtained.
524 remote_cmd_buf = "read uptime filler 2>/dev/null < /proc/uptime" +\
525 " && [ ! -z \"${uptime}\" ] && echo ${uptime}"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500526 cmd_buf = ["BMC Execute Command",
Michael Walsh888d85a2019-04-18 11:03:28 -0500527 re.sub('\\$', '\\$', remote_cmd_buf), 'quiet=1',
528 'test_mode=0']
Michael Walsh2a0df682019-09-27 17:19:27 -0500529 gp.qprint_issuing(cmd_buf, 0)
530 gp.qprint_issuing(remote_cmd_buf, 0)
Michael Walshfa765932017-10-13 14:07:22 -0500531 try:
532 stdout, stderr, rc =\
Michael Walshcaccd852017-11-01 17:58:41 -0500533 BuiltIn().wait_until_keyword_succeeds("10 sec", "0 sec",
Michael Walshfa765932017-10-13 14:07:22 -0500534 *cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500535 if rc == 0 and stderr == "":
536 uptime = stdout
Michael Walshfa765932017-10-13 14:07:22 -0500537 except AssertionError as my_assertion_error:
538 pass
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600539
Michael Walsh2a0df682019-09-27 17:19:27 -0500540 if 'epoch_seconds' in req_states or 'elapsed_boot_time' in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600541 date_cmd_buf = "date -u +%s"
542 if USE_BMC_EPOCH_TIME:
Michael Walshe53e47a2017-06-30 17:03:24 -0500543 cmd_buf = ["BMC Execute Command", date_cmd_buf, 'quiet=${1}']
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600544 if not quiet:
Michael Walshedb5c942019-03-28 12:40:50 -0500545 gp.print_issuing(cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500546 status, ret_values = \
547 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
548 if status == "PASS":
549 stdout, stderr, rc = ret_values
550 if rc == 0 and stderr == "":
551 epoch_seconds = stdout.rstrip("\n")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600552 else:
553 shell_rc, out_buf = gc.cmd_fnc_u(date_cmd_buf,
Michael Walshfa765932017-10-13 14:07:22 -0500554 quiet=quiet,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600555 print_output=0)
556 if shell_rc == 0:
557 epoch_seconds = out_buf.rstrip("\n")
558
Michael Walsh2a0df682019-09-27 17:19:27 -0500559 if 'elapsed_boot_time' in req_states:
560 global start_boot_seconds
561 elapsed_boot_time = int(epoch_seconds) - start_boot_seconds
562
Michael Walsh56749222017-09-29 15:26:07 -0500563 master_req_rest = ['rest', 'host', 'requested_host', 'operating_system',
564 'attempts_left', 'boot_progress', 'chassis',
565 'requested_chassis' 'bmc' 'requested_bmc']
566
Michael Walshb95eb542017-03-31 09:39:20 -0500567 req_rest = [sub_state for sub_state in req_states if sub_state in
568 master_req_rest]
569 need_rest = (len(req_rest) > 0)
Michael Walsh56749222017-09-29 15:26:07 -0500570 state = DotDict()
571 if need_rest:
Michael Walsh940d6912017-10-27 12:32:33 -0500572 cmd_buf = ["Read Properties", SYSTEM_STATE_URI + "enumerate",
Michael Walsh56749222017-09-29 15:26:07 -0500573 "quiet=${" + str(quiet) + "}"]
Michael Walshedb5c942019-03-28 12:40:50 -0500574 gp.dprint_issuing(cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500575 status, ret_values = \
576 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
577 if status == "PASS":
Michael Walsh56749222017-09-29 15:26:07 -0500578 state['rest'] = '1'
Michael Walsh341c21e2017-01-17 16:25:20 -0600579 else:
Michael Walsh56749222017-09-29 15:26:07 -0500580 state['rest'] = '0'
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600581
Michael Walsh2b269de2017-10-09 11:18:11 -0500582 if int(state['rest']):
583 for url_path in ret_values:
584 for attr_name in ret_values[url_path]:
585 # Create a state key value based on the attr_name.
George Keishing36efbc02018-12-12 10:18:23 -0600586 try:
Michael Walsh2b269de2017-10-09 11:18:11 -0500587 ret_values[url_path][attr_name] = \
588 re.sub(r'.*\.', "",
589 ret_values[url_path][attr_name])
George Keishing36efbc02018-12-12 10:18:23 -0600590 except TypeError:
591 pass
Michael Walsh2b269de2017-10-09 11:18:11 -0500592 # Do some key name manipulations.
593 new_attr_name = re.sub(r'^Current|(State|Transition)$',
594 "", attr_name)
595 new_attr_name = re.sub(r'BMC', r'Bmc', new_attr_name)
596 new_attr_name = re.sub(r'([A-Z][a-z])', r'_\1',
597 new_attr_name)
598 new_attr_name = new_attr_name.lower().lstrip("_")
599 new_attr_name = re.sub(r'power', r'chassis', new_attr_name)
600 if new_attr_name in req_states:
601 state[new_attr_name] = ret_values[url_path][attr_name]
Michael Walshb95eb542017-03-31 09:39:20 -0500602
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600603 for sub_state in req_states:
Michael Walsh56749222017-09-29 15:26:07 -0500604 if sub_state in state:
605 continue
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600606 if sub_state.startswith("os_"):
607 # We pass "os_" requests on to get_os_state.
608 continue
609 cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")"
610 exec(cmd_buf)
611
612 if os_host == "":
613 # The caller has not specified an os_host so as far as we're concerned,
614 # it doesn't exist.
615 return state
616
617 os_req_states = [sub_state for sub_state in req_states
618 if sub_state.startswith('os_')]
619
620 if len(os_req_states) > 0:
621 # The caller has specified an os_host and they have requested
622 # information on os substates.
623
624 # Based on the information gathered on bmc, we'll try to make a
625 # determination of whether the os is even up. We'll pass the result
626 # of that assessment to get_os_state to enhance performance.
627 os_up_match = DotDict()
628 for sub_state in master_os_up_match:
629 if sub_state in req_states:
630 os_up_match[sub_state] = master_os_up_match[sub_state]
Michael Walsh70369fd2016-11-22 11:25:57 -0600631 os_up = compare_states(state, os_up_match)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600632 os_state = get_os_state(os_host=os_host,
633 os_username=os_username,
634 os_password=os_password,
635 req_states=os_req_states,
636 os_up=os_up,
637 quiet=quiet)
638 # Append os_state dictionary to ours.
639 state.update(os_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600640
641 return state
642
Michael Walsh70369fd2016-11-22 11:25:57 -0600643
Michael Walshfd5a8682019-02-01 14:28:42 -0600644exit_wait_early_message = ""
645
646
647def set_exit_wait_early_message(value):
648 r"""
649 Set global exit_wait_early_message to the indicated value.
650
651 This is a mechanism by which the programmer can do an early exit from
652 wait_until_keyword_succeeds() based on some special condition.
653
654 Description of argument(s):
655 value The value to assign to the global
656 exit_wait_early_message.
657 """
658
659 global exit_wait_early_message
660 exit_wait_early_message = value
661
662
Michael Walsh70369fd2016-11-22 11:25:57 -0600663def check_state(match_state,
664 invert=0,
665 print_string="",
666 openbmc_host="",
667 openbmc_username="",
668 openbmc_password="",
669 os_host="",
670 os_username="",
671 os_password="",
672 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600673 r"""
674 Check that the Open BMC machine's composite state matches the specified
675 state. On success, this keyword returns the machine's composite state as a
676 dictionary.
677
Michael Walsh2a0df682019-09-27 17:19:27 -0500678 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600679 match_state A dictionary whose key/value pairs are "state field"/
680 "state value". The state value is interpreted as a
681 regular expression. Example call from robot:
Michael Walsh341c21e2017-01-17 16:25:20 -0600682 ${match_state}= Create Dictionary chassis=^On$
683 ... bmc=^Ready$
Michael Walsh01975fa2017-08-20 20:51:36 -0500684 ... boot_progress=^OSStart$
Michael Walsh70369fd2016-11-22 11:25:57 -0600685 ${state}= Check State &{match_state}
686 invert If this flag is set, this function will succeed if the
687 states do NOT match.
688 print_string This function will print this string to the console prior
689 to getting the state.
690 openbmc_host The DNS name or IP address of the BMC.
691 This defaults to global ${OPENBMC_HOST}.
692 openbmc_username The username to be used to login to the BMC.
693 This defaults to global ${OPENBMC_USERNAME}.
694 openbmc_password The password to be used to login to the BMC.
695 This defaults to global ${OPENBMC_PASSWORD}.
696 os_host The DNS name or IP address of the operating system.
697 This defaults to global ${OS_HOST}.
698 os_username The username to be used to login to the OS.
699 This defaults to global ${OS_USERNAME}.
700 os_password The password to be used to login to the OS.
701 This defaults to global ${OS_PASSWORD}.
702 quiet Indicates whether status details should be written to the
703 console. Defaults to either global value of ${QUIET} or
704 to 1.
705 """
706
Michael Walsh619aa332017-04-12 15:56:51 -0500707 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600708
Michael Walshedb5c942019-03-28 12:40:50 -0500709 gp.gp_print(print_string)
710
711 try:
712 match_state = return_state_constant(match_state)
713 except TypeError:
714 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600715
Michael Walsh2a0df682019-09-27 17:19:27 -0500716 req_states = list(match_state.keys())
717 # Remove special-case match key from req_states.
718 if expressions_key() in req_states:
719 req_states.remove(expressions_key())
Michael Walsh70369fd2016-11-22 11:25:57 -0600720 # Initialize state.
721 state = get_state(openbmc_host=openbmc_host,
722 openbmc_username=openbmc_username,
723 openbmc_password=openbmc_password,
724 os_host=os_host,
725 os_username=os_username,
726 os_password=os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600727 req_states=req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600728 quiet=quiet)
729 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500730 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600731
Michael Walshfd5a8682019-02-01 14:28:42 -0600732 if exit_wait_early_message != "":
733 # The exit_wait_early_message has been set by a signal handler so we
734 # will exit "successfully". It is incumbent upon the calling function
735 # (e.g. wait_state) to check/clear this variable and to fail
736 # appropriately.
737 return state
738
Michael Walsh70369fd2016-11-22 11:25:57 -0600739 match = compare_states(state, match_state)
740
741 if invert and match:
742 fail_msg = "The current state of the machine matches the match" +\
743 " state:\n" + gp.sprint_varx("state", state)
744 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
745 elif not invert and not match:
746 fail_msg = "The current state of the machine does NOT match the" +\
747 " match state:\n" +\
748 gp.sprint_varx("state", state)
749 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
750
751 return state
752
Michael Walsh70369fd2016-11-22 11:25:57 -0600753
Michael Walshf893ba02017-01-10 10:28:05 -0600754def wait_state(match_state=(),
Michael Walsh70369fd2016-11-22 11:25:57 -0600755 wait_time="1 min",
756 interval="1 second",
757 invert=0,
758 openbmc_host="",
759 openbmc_username="",
760 openbmc_password="",
761 os_host="",
762 os_username="",
763 os_password="",
764 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600765 r"""
766 Wait for the Open BMC machine's composite state to match the specified
767 state. On success, this keyword returns the machine's composite state as
768 a dictionary.
769
Michael Walsh2a0df682019-09-27 17:19:27 -0500770 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600771 match_state A dictionary whose key/value pairs are "state field"/
772 "state value". See check_state (above) for details.
Michael Walsh619aa332017-04-12 15:56:51 -0500773 This value may also be any string accepted by
774 return_state_constant (e.g. "standby_match_state").
775 In such a case this function will call
776 return_state_constant to convert it to a proper
777 dictionary as described above.
Michael Walsh70369fd2016-11-22 11:25:57 -0600778 wait_time The total amount of time to wait for the desired state.
779 This value may be expressed in Robot Framework's time
780 format (e.g. 1 minute, 2 min 3 s, 4.5).
781 interval The amount of time between state checks.
782 This value may be expressed in Robot Framework's time
783 format (e.g. 1 minute, 2 min 3 s, 4.5).
784 invert If this flag is set, this function will for the state of
785 the machine to cease to match the match state.
786 openbmc_host The DNS name or IP address of the BMC.
787 This defaults to global ${OPENBMC_HOST}.
788 openbmc_username The username to be used to login to the BMC.
789 This defaults to global ${OPENBMC_USERNAME}.
790 openbmc_password The password to be used to login to the BMC.
791 This defaults to global ${OPENBMC_PASSWORD}.
792 os_host The DNS name or IP address of the operating system.
793 This defaults to global ${OS_HOST}.
794 os_username The username to be used to login to the OS.
795 This defaults to global ${OS_USERNAME}.
796 os_password The password to be used to login to the OS.
797 This defaults to global ${OS_PASSWORD}.
798 quiet Indicates whether status details should be written to the
799 console. Defaults to either global value of ${QUIET} or
800 to 1.
801 """
802
Michael Walsh619aa332017-04-12 15:56:51 -0500803 quiet = int(gp.get_var_value(quiet, 0))
804
George Keishing36efbc02018-12-12 10:18:23 -0600805 try:
Michael Walsh619aa332017-04-12 15:56:51 -0500806 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600807 except TypeError:
808 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600809
810 if not quiet:
811 if invert:
812 alt_text = "cease to "
813 else:
814 alt_text = ""
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500815 gp.print_timen("Checking every " + str(interval) + " for up to "
816 + str(wait_time) + " for the state of the machine to "
817 + alt_text + "match the state shown below.")
Michael Walsh3eb50022017-03-21 11:27:30 -0500818 gp.print_var(match_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600819
Michael Walshf893ba02017-01-10 10:28:05 -0600820 if quiet:
Michael Walsh341c21e2017-01-17 16:25:20 -0600821 print_string = ""
Michael Walshf893ba02017-01-10 10:28:05 -0600822 else:
Michael Walsh341c21e2017-01-17 16:25:20 -0600823 print_string = "#"
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600824
825 debug = int(BuiltIn().get_variable_value("${debug}", "0"))
826 if debug:
827 # In debug we print state so no need to print the "#".
828 print_string = ""
829 check_state_quiet = 1 - debug
Michael Walsh70369fd2016-11-22 11:25:57 -0600830 cmd_buf = ["Check State", match_state, "invert=${" + str(invert) + "}",
Michael Walshf893ba02017-01-10 10:28:05 -0600831 "print_string=" + print_string, "openbmc_host=" + openbmc_host,
Michael Walsh70369fd2016-11-22 11:25:57 -0600832 "openbmc_username=" + openbmc_username,
833 "openbmc_password=" + openbmc_password, "os_host=" + os_host,
834 "os_username=" + os_username, "os_password=" + os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600835 "quiet=${" + str(check_state_quiet) + "}"]
Michael Walshedb5c942019-03-28 12:40:50 -0500836 gp.dprint_issuing(cmd_buf)
Michael Walsh619aa332017-04-12 15:56:51 -0500837 try:
838 state = BuiltIn().wait_until_keyword_succeeds(wait_time, interval,
839 *cmd_buf)
840 except AssertionError as my_assertion_error:
841 gp.printn()
842 message = my_assertion_error.args[0]
843 BuiltIn().fail(message)
844
Michael Walshfd5a8682019-02-01 14:28:42 -0600845 if exit_wait_early_message:
846 # The global exit_wait_early_message was set by a signal handler
847 # indicating that we should fail.
848 message = exit_wait_early_message
849 # Clear the exit_wait_early_message variable for future use.
850 set_exit_wait_early_message("")
851 BuiltIn().fail(gp.sprint_error(message))
852
Michael Walsh70369fd2016-11-22 11:25:57 -0600853 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500854 gp.printn()
Michael Walsh70369fd2016-11-22 11:25:57 -0600855 if invert:
Michael Walsh3eb50022017-03-21 11:27:30 -0500856 gp.print_timen("The states no longer match:")
Michael Walsh70369fd2016-11-22 11:25:57 -0600857 else:
Michael Walsh3eb50022017-03-21 11:27:30 -0500858 gp.print_timen("The states match:")
859 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600860
861 return state
862
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600863
Michael Walsh2a0df682019-09-27 17:19:27 -0500864def set_start_boot_seconds(value=0):
865 global start_boot_seconds
866 start_boot_seconds = int(value)
867
868
869set_start_boot_seconds(0)
870
871
Michael Walsh619aa332017-04-12 15:56:51 -0500872def wait_for_comm_cycle(start_boot_seconds,
873 quiet=None):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600874 r"""
Michael Walsh2a0df682019-09-27 17:19:27 -0500875 Wait for the BMC uptime to be less than elapsed_boot_time.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600876
Michael Walsh2a0df682019-09-27 17:19:27 -0500877 This function will tolerate an expected loss of communication to the BMC.
878 This function is useful when some kind of reboot has been initiated by the
879 caller.
880
881 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600882 start_boot_seconds The time that the boot test started. The format is the
883 epoch time in seconds, i.e. the number of seconds since
884 1970-01-01 00:00:00 UTC. This value should be obtained
885 from the BMC so that it is not dependent on any kind of
886 synchronization between this machine and the target BMC
887 This will allow this program to work correctly even in
888 a simulated environment. This value should be obtained
889 by the caller prior to initiating a reboot. It can be
890 obtained as follows:
891 state = st.get_state(req_states=['epoch_seconds'])
892 """
893
Michael Walsh619aa332017-04-12 15:56:51 -0500894 quiet = int(gp.get_var_value(quiet, 0))
895
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600896 # Validate parms.
Michael Walsh2a0df682019-09-27 17:19:27 -0500897 error_message = gv.valid_integer(start_boot_seconds)
898 if error_message:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600899 BuiltIn().fail(gp.sprint_error(error_message))
900
Michael Walsh2a0df682019-09-27 17:19:27 -0500901 # Wait for uptime to be less than elapsed_boot_time.
902 set_start_boot_seconds(start_boot_seconds)
903 expr = 'int(float(state[\'uptime\'])) < int(state[\'elapsed_boot_time\'])'
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500904 match_state = DotDict([('uptime', '^[0-9\\.]+$'),
Michael Walsh2a0df682019-09-27 17:19:27 -0500905 ('elapsed_boot_time', '^[0-9]+$'),
906 (expressions_key(), [expr])])
907 wait_state(match_state, wait_time="8 mins", interval="5 seconds")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600908
Michael Walsh619aa332017-04-12 15:56:51 -0500909 gp.qprint_timen("Verifying that REST API interface is working.")
Michael Walshb95eb542017-03-31 09:39:20 -0500910 match_state = DotDict([('rest', '^1$')])
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600911 state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")