blob: 44dc414ce5b281405beefa345256fba7d8202cd8 [file] [log] [blame]
Michael Walsh70369fd2016-11-22 11:25:57 -06001#!/usr/bin/env python
2
3r"""
4This module contains functions having to do with machine state: get_state,
5check_state, wait_state, etc.
6
7The 'State' is a composite of many pieces of data. Therefore, the functions
8in this module define state as an ordered dictionary. Here is an example of
9some test output showing machine state:
10
Michael Walsh341c21e2017-01-17 16:25:20 -060011default_state:
Michael Walsh65b12542017-02-03 15:34:38 -060012 default_state[chassis]: On
Michael Walsh01975fa2017-08-20 20:51:36 -050013 default_state[boot_progress]: OSStart
Michael Walsh56749222017-09-29 15:26:07 -050014 default_state[operating_system]: BootComplete
Michael Walsh65b12542017-02-03 15:34:38 -060015 default_state[host]: Running
Michael Walsh341c21e2017-01-17 16:25:20 -060016 default_state[os_ping]: 1
17 default_state[os_login]: 1
18 default_state[os_run_cmd]: 1
Michael Walsh70369fd2016-11-22 11:25:57 -060019
20Different users may very well have different needs when inquiring about
Michael Walsh8fae6ea2017-02-20 16:14:44 -060021state. Support for new pieces of state information may be added to this
22module as needed.
Michael Walsh70369fd2016-11-22 11:25:57 -060023
24By using the wait_state function, a caller can start a boot and then wait for
25a precisely defined state to indicate that the boot has succeeded. If
26the boot fails, they can see exactly why by looking at the current state as
27compared with the expected state.
28"""
29
30import gen_print as gp
Michael Walsh70369fd2016-11-22 11:25:57 -060031import gen_valid as gv
Michael Walsh16cbb7f2017-02-02 15:54:16 -060032import gen_robot_utils as gru
Michael Walsh8fae6ea2017-02-20 16:14:44 -060033import gen_cmd as gc
Michael Walsh6a9bd142018-07-24 16:10:29 -050034import bmc_ssh_utils as bsu
Michael Walsh70369fd2016-11-22 11:25:57 -060035
Michael Walsh70369fd2016-11-22 11:25:57 -060036from robot.libraries.BuiltIn import BuiltIn
Michael Walsh341c21e2017-01-17 16:25:20 -060037from robot.utils import DotDict
Michael Walsh70369fd2016-11-22 11:25:57 -060038
39import re
Michael Walsh341c21e2017-01-17 16:25:20 -060040import os
Michael Walsh56749222017-09-29 15:26:07 -050041import sys
42import imp
43
Michael Walsh70369fd2016-11-22 11:25:57 -060044
Michael Walsh5a5868a2018-10-31 15:20:04 -050045# NOTE: Avoid importing utils.robot because utils.robot imports state.py
46# (indirectly) which will cause failures.
47gru.my_import_resource("rest_client.robot")
Michael Walsh70369fd2016-11-22 11:25:57 -060048
Michael Walsh56749222017-09-29 15:26:07 -050049base_path = os.path.dirname(os.path.dirname(
50 imp.find_module("gen_robot_print")[1])) + os.sep
51sys.path.append(base_path + "data/")
Michael Walsh56749222017-09-29 15:26:07 -050052
Michael Walsh940d6912017-10-27 12:32:33 -050053# Previously, I had this coded:
54# import variables as var
55# However, we ran into a problem where a robot program did this...
56# Variables ../../lib/ras/variables.py
57# Prior to doing this...
58# Library ../lib/state.py
59
60# This caused the wrong variables.py file to be selected. Attempts to fix this
61# have failed so far. For the moment, we will hard-code the value we need from
62# the file.
63
64SYSTEM_STATE_URI = "/xyz/openbmc_project/state/"
Michael Walsh56749222017-09-29 15:26:07 -050065
Michael Walsh8fae6ea2017-02-20 16:14:44 -060066# The BMC code has recently been changed as far as what states are defined and
67# what the state values can be. This module now has a means of processing both
68# the old style state (i.e. OBMC_STATES_VERSION = 0) and the new style (i.e.
Michael Walsh16cbb7f2017-02-02 15:54:16 -060069# OBMC_STATES_VERSION = 1).
Michael Walsh341c21e2017-01-17 16:25:20 -060070# The caller can set environment variable OBMC_STATES_VERSION to dictate
71# whether we're processing old or new style states. If OBMC_STATES_VERSION is
Michael Walsh8fae6ea2017-02-20 16:14:44 -060072# not set it will default to 1.
Michael Walsh341c21e2017-01-17 16:25:20 -060073
Michael Walsh619aa332017-04-12 15:56:51 -050074# As of the present moment, OBMC_STATES_VERSION of 0 is for cold that is so old
75# that it is no longer worthwhile to maintain. The OBMC_STATES_VERSION 0 code
76# is being removed but the OBMC_STATES_VERSION value will stay for now in the
77# event that it is needed in the future.
78
Michael Walsh8fae6ea2017-02-20 16:14:44 -060079OBMC_STATES_VERSION = int(os.environ.get('OBMC_STATES_VERSION', 1))
Michael Walsh341c21e2017-01-17 16:25:20 -060080
Michael Sheposda40c1d2020-12-01 22:30:00 -060081redfish_support_trans_state = int(os.environ.get('REDFISH_SUPPORT_TRANS_STATE', 0)) or \
82 int(BuiltIn().get_variable_value("${REDFISH_SUPPORT_TRANS_STATE}", default=0))
83
Konstantin Aladyshevc056c2b2021-03-25 11:59:39 +030084platform_arch_type = os.environ.get('PLATFORM_ARCH_TYPE', '') or \
85 BuiltIn().get_variable_value("${PLATFORM_ARCH_TYPE}", default="power")
George Keishing1e2fbee2021-03-19 11:19:29 -050086
Michael Walsh8fae6ea2017-02-20 16:14:44 -060087# valid_os_req_states and default_os_req_states are used by the os_get_state
88# function.
89# valid_os_req_states is a list of state information supported by the
90# get_os_state function.
91valid_os_req_states = ['os_ping',
92 'os_login',
93 'os_run_cmd']
Michael Sheposbdd1dce2020-12-10 11:51:58 -060094
Michael Walsh8fae6ea2017-02-20 16:14:44 -060095# When a user calls get_os_state w/o specifying req_states,
96# default_os_req_states is used as its value.
97default_os_req_states = ['os_ping',
98 'os_login',
99 'os_run_cmd']
100
101# Presently, some BMCs appear to not keep time very well. This environment
102# variable directs the get_state function to use either the BMC's epoch time
103# or the local epoch time.
104USE_BMC_EPOCH_TIME = int(os.environ.get('USE_BMC_EPOCH_TIME', 0))
Michael Walsh341c21e2017-01-17 16:25:20 -0600105
Michael Walsh619aa332017-04-12 15:56:51 -0500106# Useful state constant definition(s).
Michael Sheposda40c1d2020-12-01 22:30:00 -0600107if not redfish_support_trans_state:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600108 # When a user calls get_state w/o specifying req_states, default_req_states
109 # is used as its value.
110 default_req_states = ['rest',
111 'chassis',
112 'bmc',
113 'boot_progress',
114 'operating_system',
115 'host',
116 'os_ping',
117 'os_login',
118 'os_run_cmd']
119
120 # valid_req_states is a list of sub states supported by the get_state function.
121 # valid_req_states, default_req_states and master_os_up_match are used by the
122 # get_state function.
123
124 valid_req_states = ['ping',
125 'packet_loss',
126 'uptime',
127 'epoch_seconds',
128 'elapsed_boot_time',
129 'rest',
130 'chassis',
131 'requested_chassis',
132 'bmc',
133 'requested_bmc',
134 'boot_progress',
135 'operating_system',
136 'host',
137 'requested_host',
138 'attempts_left',
139 'os_ping',
140 'os_login',
141 'os_run_cmd']
142
Michael Sheposda40c1d2020-12-01 22:30:00 -0600143 # default_state is an initial value which may be of use to callers.
144 default_state = DotDict([('rest', '1'),
145 ('chassis', 'On'),
146 ('bmc', 'Ready'),
147 ('boot_progress', 'OSStart'),
148 ('operating_system', 'BootComplete'),
149 ('host', 'Running'),
150 ('os_ping', '1'),
151 ('os_login', '1'),
152 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500153
Michael Sheposda40c1d2020-12-01 22:30:00 -0600154 # A match state for checking that the system is at "standby".
155 standby_match_state = DotDict([('rest', '^1$'),
156 ('chassis', '^Off$'),
157 ('bmc', '^Ready$'),
158 ('boot_progress', '^Off|Unspecified$'),
159 ('operating_system', '^Inactive$'),
160 ('host', '^Off$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500161
Michael Sheposda40c1d2020-12-01 22:30:00 -0600162 # A match state for checking that the system is at "os running".
163 os_running_match_state = DotDict([('chassis', '^On$'),
164 ('bmc', '^Ready$'),
165 ('boot_progress',
166 'FW Progress, Starting OS|OSStart'),
167 ('operating_system', 'BootComplete'),
168 ('host', '^Running$'),
169 ('os_ping', '^1$'),
170 ('os_login', '^1$'),
171 ('os_run_cmd', '^1$')])
172
173 # A master dictionary to determine whether the os may be up.
174 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Walsh7dc885b2018-03-14 17:51:59 -0500175 ('bmc', '^Ready$'),
176 ('boot_progress',
177 'FW Progress, Starting OS|OSStart'),
178 ('operating_system', 'BootComplete'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600179 ('host', '^Running|Quiesced$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500180
Michael Sheposda40c1d2020-12-01 22:30:00 -0600181 invalid_state_match = DotDict([('rest', '^$'),
182 ('chassis', '^$'),
183 ('bmc', '^$'),
184 ('boot_progress', '^$'),
185 ('operating_system', '^$'),
186 ('host', '^$')])
187else:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600188 # When a user calls get_state w/o specifying req_states, default_req_states
189 # is used as its value.
190 default_req_states = ['redfish',
191 'chassis',
192 'bmc',
193 'boot_progress',
194 'host',
195 'os_ping',
196 'os_login',
197 'os_run_cmd']
198
199 # valid_req_states is a list of sub states supported by the get_state function.
200 # valid_req_states, default_req_states and master_os_up_match are used by the
201 # get_state function.
202
203 valid_req_states = ['ping',
204 'packet_loss',
205 'uptime',
206 'epoch_seconds',
207 'elapsed_boot_time',
208 'redfish',
209 'chassis',
210 'requested_chassis',
211 'bmc',
212 'requested_bmc',
213 'boot_progress',
214 'host',
215 'requested_host',
216 'attempts_left',
217 'os_ping',
218 'os_login',
219 'os_run_cmd']
220
Michael Sheposda40c1d2020-12-01 22:30:00 -0600221 # default_state is an initial value which may be of use to callers.
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600222 default_state = DotDict([('redfish', '1'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600223 ('chassis', 'On'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600224 ('bmc', 'Enabled'),
225 ('boot_progress', 'SystemHardwareInitializationComplete|OSRunning'),
226 ('host', 'Enabled'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600227 ('os_ping', '1'),
228 ('os_login', '1'),
229 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500230
Michael Sheposda40c1d2020-12-01 22:30:00 -0600231 # A match state for checking that the system is at "standby".
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600232 standby_match_state = DotDict([('redfish', '^1$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600233 ('chassis', '^Off$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600234 ('bmc', '^Enabled$'),
235 ('boot_progress', '^None$'),
236 ('host', '^Disabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600237
238 # A match state for checking that the system is at "os running".
239 os_running_match_state = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600240 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600241 ('boot_progress',
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600242 'SystemHardwareInitializationComplete|OSRunning'),
243 ('host', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600244 ('os_ping', '^1$'),
245 ('os_login', '^1$'),
246 ('os_run_cmd', '^1$')])
247
248 # A master dictionary to determine whether the os may be up.
249 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600250 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600251 ('boot_progress',
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600252 'SystemHardwareInitializationComplete|OSRunning'),
253 ('host', '^Enabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600254
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600255 invalid_state_match = DotDict([('redfish', '^$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600256 ('chassis', '^$'),
257 ('bmc', '^$'),
258 ('boot_progress', '^$'),
259 ('host', '^$')])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500260
George Keishing1e2fbee2021-03-19 11:19:29 -0500261# Filter the states based on platform type.
262if platform_arch_type == "x86":
263 default_req_states.remove("operating_system")
264 default_req_states.remove("boot_progress")
265 valid_req_states.remove("operating_system")
266 valid_req_states.remove("boot_progress")
267 del default_state["operating_system"]
268 del default_state["boot_progress"]
269 del standby_match_state["operating_system"]
270 del standby_match_state["boot_progress"]
271 del os_running_match_state["operating_system"]
272 del os_running_match_state["boot_progress"]
273 del master_os_up_match["operating_system"]
274 del master_os_up_match["boot_progress"]
275 del invalid_state_match["operating_system"]
276 del invalid_state_match["boot_progress"]
277
Michael Walsh341c21e2017-01-17 16:25:20 -0600278
George Keishing36efbc02018-12-12 10:18:23 -0600279def return_state_constant(state_name='default_state'):
Michael Walsh619aa332017-04-12 15:56:51 -0500280 r"""
Michael Walsh7dc885b2018-03-14 17:51:59 -0500281 Return the named state dictionary constant.
Michael Walsh619aa332017-04-12 15:56:51 -0500282 """
283
George Keishing36efbc02018-12-12 10:18:23 -0600284 return eval(state_name)
Michael Walsh619aa332017-04-12 15:56:51 -0500285
Michael Walsh619aa332017-04-12 15:56:51 -0500286
Michael Walsh70369fd2016-11-22 11:25:57 -0600287def anchor_state(state):
Michael Walsh70369fd2016-11-22 11:25:57 -0600288 r"""
289 Add regular expression anchors ("^" and "$") to the beginning and end of
290 each item in the state dictionary passed in. Return the resulting
291 dictionary.
292
Michael Walsh2a0df682019-09-27 17:19:27 -0500293 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600294 state A dictionary such as the one returned by the get_state()
295 function.
296 """
297
Michael Walsh2ce067a2017-02-27 14:24:07 -0600298 anchored_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500299 for key in anchored_state.keys():
Michael Walsh70369fd2016-11-22 11:25:57 -0600300 anchored_state[key] = "^" + str(anchored_state[key]) + "$"
301
302 return anchored_state
303
Michael Walsh70369fd2016-11-22 11:25:57 -0600304
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600305def strip_anchor_state(state):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600306 r"""
307 Strip regular expression anchors ("^" and "$") from the beginning and end
308 of each item in the state dictionary passed in. Return the resulting
309 dictionary.
310
Michael Walsh2a0df682019-09-27 17:19:27 -0500311 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600312 state A dictionary such as the one returned by the get_state()
313 function.
314 """
315
Michael Walsh2ce067a2017-02-27 14:24:07 -0600316 stripped_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500317 for key in stripped_state.keys():
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600318 stripped_state[key] = stripped_state[key].strip("^$")
319
320 return stripped_state
321
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600322
Michael Walsh2a0df682019-09-27 17:19:27 -0500323def expressions_key():
324 r"""
325 Return expressions key constant.
326 """
327 return '<expressions>'
328
329
Michael Walsh70369fd2016-11-22 11:25:57 -0600330def compare_states(state,
Michael Walsh45ca6e42017-09-14 17:29:12 -0500331 match_state,
332 match_type='and'):
Michael Walsh70369fd2016-11-22 11:25:57 -0600333 r"""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600334 Compare 2 state dictionaries. Return True if they match and False if they
Michael Walsh70369fd2016-11-22 11:25:57 -0600335 don't. Note that the match_state dictionary does not need to have an entry
336 corresponding to each entry in the state dictionary. But for each entry
337 that it does have, the corresponding state entry will be checked for a
338 match.
339
Michael Walsh2a0df682019-09-27 17:19:27 -0500340 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600341 state A state dictionary such as the one returned by the
342 get_state function.
343 match_state A dictionary whose key/value pairs are "state field"/
344 "state value". The state value is interpreted as a
345 regular expression. Every value in this dictionary is
Michael Walsh45ca6e42017-09-14 17:29:12 -0500346 considered. When match_type is 'and', if each and every
347 comparison matches, the two dictionaries are considered to
348 be matching. If match_type is 'or', if any two of the
349 elements compared match, the two dictionaries are
350 considered to be matching.
Michael Walsh2a0df682019-09-27 17:19:27 -0500351
Michael Walsh7dc885b2018-03-14 17:51:59 -0500352 This value may also be any string accepted by
Michael Walsh2a0df682019-09-27 17:19:27 -0500353 return_state_constant (e.g. "standby_match_state"). In
354 such a case this function will call return_state_constant
355 to convert it to a proper dictionary as described above.
356
357 Finally, one special value is accepted for the key field:
358 expression_key(). If such an entry exists, its value is
359 taken to be a list of expressions to be evaluated. These
360 expressions may reference state dictionary entries by
361 simply coding them in standard python syntax (e.g.
362 state['key1']). What follows is an example expression:
363
364 "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
365
366 In this example, if the state dictionary's 'uptime' entry
367 is less than its 'elapsed_boot_time' entry, it would
368 qualify as a match.
Michael Walsh45ca6e42017-09-14 17:29:12 -0500369 match_type This may be 'and' or 'or'.
Michael Walsh70369fd2016-11-22 11:25:57 -0600370 """
371
Michael Walsh2a0df682019-09-27 17:19:27 -0500372 error_message = gv.valid_value(match_type, valid_values=['and', 'or'])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500373 if error_message != "":
374 BuiltIn().fail(gp.sprint_error(error_message))
375
George Keishing36efbc02018-12-12 10:18:23 -0600376 try:
Michael Walsh7dc885b2018-03-14 17:51:59 -0500377 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600378 except TypeError:
379 pass
Michael Walsh7dc885b2018-03-14 17:51:59 -0500380
Michael Walsh45ca6e42017-09-14 17:29:12 -0500381 default_match = (match_type == 'and')
Michael Walsh70369fd2016-11-22 11:25:57 -0600382 for key, match_state_value in match_state.items():
Michael Walsh97df71c2017-03-27 14:33:24 -0500383 # Blank match_state_value means "don't care".
384 if match_state_value == "":
385 continue
Michael Walsh2a0df682019-09-27 17:19:27 -0500386 if key == expressions_key():
387 for expr in match_state_value:
388 # Use python interpreter to evaluate the expression.
389 match = eval(expr)
390 if match != default_match:
391 return match
392 else:
393 try:
394 match = (re.match(match_state_value, str(state[key])) is not None)
395 except KeyError:
396 match = False
397 if match != default_match:
398 return match
Michael Walsh45ca6e42017-09-14 17:29:12 -0500399
400 return default_match
Michael Walsh70369fd2016-11-22 11:25:57 -0600401
Michael Walsh70369fd2016-11-22 11:25:57 -0600402
Michael Walsh70369fd2016-11-22 11:25:57 -0600403def get_os_state(os_host="",
404 os_username="",
405 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600406 req_states=default_os_req_states,
407 os_up=True,
Michael Walsh70369fd2016-11-22 11:25:57 -0600408 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600409 r"""
410 Get component states for the operating system such as ping, login,
411 etc, put them into a dictionary and return them to the caller.
412
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600413 Note that all substate values are strings.
414
Michael Walsh2a0df682019-09-27 17:19:27 -0500415 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600416 os_host The DNS name or IP address of the operating system.
417 This defaults to global ${OS_HOST}.
418 os_username The username to be used to login to the OS.
419 This defaults to global ${OS_USERNAME}.
420 os_password The password to be used to login to the OS.
421 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600422 req_states This is a list of states whose values are being requested by
423 the caller.
424 os_up If the caller knows that the os can't possibly be up, it can
425 improve performance by passing os_up=False. This function
426 will then simply return default values for all requested os
427 sub states.
Michael Walsh70369fd2016-11-22 11:25:57 -0600428 quiet Indicates whether status details (e.g. curl commands) should
429 be written to the console.
430 Defaults to either global value of ${QUIET} or to 1.
431 """
432
Michael Walsh619aa332017-04-12 15:56:51 -0500433 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600434
435 # Set parm defaults where necessary and validate all parms.
436 if os_host == "":
437 os_host = BuiltIn().get_variable_value("${OS_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500438 error_message = gv.valid_value(os_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600439 if error_message != "":
440 BuiltIn().fail(gp.sprint_error(error_message))
441
442 if os_username == "":
443 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500444 error_message = gv.valid_value(os_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600445 if error_message != "":
446 BuiltIn().fail(gp.sprint_error(error_message))
447
448 if os_password == "":
449 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500450 error_message = gv.valid_value(os_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600451 if error_message != "":
452 BuiltIn().fail(gp.sprint_error(error_message))
453
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600454 invalid_req_states = [sub_state for sub_state in req_states
455 if sub_state not in valid_os_req_states]
456 if len(invalid_req_states) > 0:
457 error_message = "The following req_states are not supported:\n" +\
458 gp.sprint_var(invalid_req_states)
459 BuiltIn().fail(gp.sprint_error(error_message))
Michael Walsh70369fd2016-11-22 11:25:57 -0600460
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600461 # Initialize all substate values supported by this function.
462 os_ping = 0
463 os_login = 0
464 os_run_cmd = 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600465
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600466 if os_up:
467 if 'os_ping' in req_states:
468 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600469 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + os_host,
470 print_output=0, show_err=0,
471 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600472 if rc == 0:
473 os_ping = 1
Michael Walsh70369fd2016-11-22 11:25:57 -0600474
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600475 # Programming note: All attributes which do not require an ssh login
476 # should have been processed by this point.
477 master_req_login = ['os_login', 'os_run_cmd']
478 req_login = [sub_state for sub_state in req_states if sub_state in
479 master_req_login]
Michael Walsh97df71c2017-03-27 14:33:24 -0500480 must_login = (len(req_login) > 0)
Michael Walsh70369fd2016-11-22 11:25:57 -0600481
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600482 if must_login:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500483 output, stderr, rc = bsu.os_execute_command("uptime", quiet=quiet,
Michael Walsh7fc33972018-08-07 14:55:03 -0500484 ignore_err=1,
Michael Shepos3390c852021-02-11 00:12:31 -0600485 time_out=20,
486 os_host=os_host,
487 os_username=os_username,
488 os_password=os_password)
Michael Walsh6a9bd142018-07-24 16:10:29 -0500489 if rc == 0:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600490 os_login = 1
Michael Walsh6a9bd142018-07-24 16:10:29 -0500491 os_run_cmd = 1
Michael Walsh3eb50022017-03-21 11:27:30 -0500492 else:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500493 gp.dprint_vars(output, stderr)
494 gp.dprint_vars(rc, 1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600495
496 os_state = DotDict()
497 for sub_state in req_states:
498 cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")"
499 exec(cmd_buf)
Michael Walsh70369fd2016-11-22 11:25:57 -0600500
501 return os_state
502
Michael Walsh70369fd2016-11-22 11:25:57 -0600503
Michael Walsh70369fd2016-11-22 11:25:57 -0600504def get_state(openbmc_host="",
505 openbmc_username="",
506 openbmc_password="",
507 os_host="",
508 os_username="",
509 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600510 req_states=default_req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600511 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600512 r"""
Michael Walsh619aa332017-04-12 15:56:51 -0500513 Get component states such as chassis state, bmc state, etc, put them into a
Michael Walsh70369fd2016-11-22 11:25:57 -0600514 dictionary and return them to the caller.
515
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600516 Note that all substate values are strings.
517
Michael Walsh2a0df682019-09-27 17:19:27 -0500518 Note: If elapsed_boot_time is included in req_states, it is the caller's
519 duty to call set_start_boot_seconds() in order to set global
520 start_boot_seconds. elapsed_boot_time is the current time minus
521 start_boot_seconds.
522
523 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600524 openbmc_host The DNS name or IP address of the BMC.
525 This defaults to global ${OPENBMC_HOST}.
526 openbmc_username The username to be used to login to the BMC.
527 This defaults to global ${OPENBMC_USERNAME}.
528 openbmc_password The password to be used to login to the BMC.
529 This defaults to global ${OPENBMC_PASSWORD}.
530 os_host The DNS name or IP address of the operating system.
531 This defaults to global ${OS_HOST}.
532 os_username The username to be used to login to the OS.
533 This defaults to global ${OS_USERNAME}.
534 os_password The password to be used to login to the OS.
535 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600536 req_states This is a list of states whose values are being requested
537 by the caller.
Michael Walsh70369fd2016-11-22 11:25:57 -0600538 quiet Indicates whether status details (e.g. curl commands)
539 should be written to the console.
540 Defaults to either global value of ${QUIET} or to 1.
541 """
542
Michael Walsh619aa332017-04-12 15:56:51 -0500543 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600544
545 # Set parm defaults where necessary and validate all parms.
546 if openbmc_host == "":
547 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500548 error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600549 if error_message != "":
550 BuiltIn().fail(gp.sprint_error(error_message))
551
552 if openbmc_username == "":
553 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500554 error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600555 if error_message != "":
556 BuiltIn().fail(gp.sprint_error(error_message))
557
558 if openbmc_password == "":
559 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500560 error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600561 if error_message != "":
562 BuiltIn().fail(gp.sprint_error(error_message))
563
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600564 # NOTE: OS parms are optional.
Michael Walsh70369fd2016-11-22 11:25:57 -0600565 if os_host == "":
566 os_host = BuiltIn().get_variable_value("${OS_HOST}")
567 if os_host is None:
568 os_host = ""
569
570 if os_username is "":
571 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
572 if os_username is None:
573 os_username = ""
574
575 if os_password is "":
576 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
577 if os_password is None:
578 os_password = ""
579
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600580 invalid_req_states = [sub_state for sub_state in req_states
581 if sub_state not in valid_req_states]
582 if len(invalid_req_states) > 0:
583 error_message = "The following req_states are not supported:\n" +\
584 gp.sprint_var(invalid_req_states)
585 BuiltIn().fail(gp.sprint_error(error_message))
586
587 # Initialize all substate values supported by this function.
588 ping = 0
589 packet_loss = ''
590 uptime = ''
591 epoch_seconds = ''
Michael Walsh2a0df682019-09-27 17:19:27 -0500592 elapsed_boot_time = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500593 rest = ''
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600594 redfish = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500595 chassis = ''
596 requested_chassis = ''
597 bmc = ''
598 requested_bmc = ''
George Keishing18f15dd2021-03-09 11:17:35 -0600599 # BootProgress state will get populated when state logic enumerates the
600 # state URI. This is to prevent state dictionary boot_progress value
601 # getting empty when the BootProgress is NOT found, making it optional.
602 boot_progress = 'NA'
Michael Walsh2b269de2017-10-09 11:18:11 -0500603 operating_system = ''
604 host = ''
605 requested_host = ''
606 attempts_left = ''
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600607
Michael Walsh70369fd2016-11-22 11:25:57 -0600608 # Get the component states.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600609 if 'ping' in req_states:
610 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600611 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + openbmc_host,
612 print_output=0, show_err=0,
613 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600614 if rc == 0:
615 ping = 1
616
617 if 'packet_loss' in req_states:
618 # See if the OS pings.
619 cmd_buf = "ping -c 5 -w 5 " + openbmc_host +\
620 " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'"
George Keishing36efbc02018-12-12 10:18:23 -0600621 rc, out_buf = gc.shell_cmd(cmd_buf,
622 print_output=0, show_err=0,
623 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600624 if rc == 0:
625 packet_loss = out_buf.rstrip("\n")
626
Michael Walshe53e47a2017-06-30 17:03:24 -0500627 if 'uptime' in req_states:
Michael Walshfa765932017-10-13 14:07:22 -0500628 # Sometimes reading uptime results in a blank value. Call with
629 # wait_until_keyword_succeeds to ensure a non-blank value is obtained.
630 remote_cmd_buf = "read uptime filler 2>/dev/null < /proc/uptime" +\
631 " && [ ! -z \"${uptime}\" ] && echo ${uptime}"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500632 cmd_buf = ["BMC Execute Command",
Michael Walsh888d85a2019-04-18 11:03:28 -0500633 re.sub('\\$', '\\$', remote_cmd_buf), 'quiet=1',
George Keishing4361d8d2021-03-24 02:59:52 -0500634 'test_mode=0', 'time_out=5']
Michael Walsh2a0df682019-09-27 17:19:27 -0500635 gp.qprint_issuing(cmd_buf, 0)
636 gp.qprint_issuing(remote_cmd_buf, 0)
Michael Walshfa765932017-10-13 14:07:22 -0500637 try:
638 stdout, stderr, rc =\
George Keishing4361d8d2021-03-24 02:59:52 -0500639 BuiltIn().wait_until_keyword_succeeds("10 sec", "5 sec",
Michael Walshfa765932017-10-13 14:07:22 -0500640 *cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500641 if rc == 0 and stderr == "":
642 uptime = stdout
Michael Walshfa765932017-10-13 14:07:22 -0500643 except AssertionError as my_assertion_error:
644 pass
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600645
Michael Walsh2a0df682019-09-27 17:19:27 -0500646 if 'epoch_seconds' in req_states or 'elapsed_boot_time' in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600647 date_cmd_buf = "date -u +%s"
648 if USE_BMC_EPOCH_TIME:
Michael Walshe53e47a2017-06-30 17:03:24 -0500649 cmd_buf = ["BMC Execute Command", date_cmd_buf, 'quiet=${1}']
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600650 if not quiet:
Michael Walshedb5c942019-03-28 12:40:50 -0500651 gp.print_issuing(cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500652 status, ret_values = \
653 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
654 if status == "PASS":
655 stdout, stderr, rc = ret_values
656 if rc == 0 and stderr == "":
657 epoch_seconds = stdout.rstrip("\n")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600658 else:
659 shell_rc, out_buf = gc.cmd_fnc_u(date_cmd_buf,
Michael Walshfa765932017-10-13 14:07:22 -0500660 quiet=quiet,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600661 print_output=0)
662 if shell_rc == 0:
663 epoch_seconds = out_buf.rstrip("\n")
664
Michael Walsh2a0df682019-09-27 17:19:27 -0500665 if 'elapsed_boot_time' in req_states:
666 global start_boot_seconds
667 elapsed_boot_time = int(epoch_seconds) - start_boot_seconds
668
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600669 if not redfish_support_trans_state:
670 master_req_rest = ['rest', 'host', 'requested_host', 'operating_system',
671 'attempts_left', 'boot_progress', 'chassis',
672 'requested_chassis' 'bmc' 'requested_bmc']
Michael Walsh56749222017-09-29 15:26:07 -0500673
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600674 req_rest = [sub_state for sub_state in req_states if sub_state in
675 master_req_rest]
676 need_rest = (len(req_rest) > 0)
677 state = DotDict()
678 if need_rest:
679 cmd_buf = ["Read Properties", SYSTEM_STATE_URI + "enumerate",
680 "quiet=${" + str(quiet) + "}", "timeout=30"]
681 gp.dprint_issuing(cmd_buf)
682 status, ret_values = \
683 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
684 if status == "PASS":
685 state['rest'] = '1'
686 else:
687 state['rest'] = '0'
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600688
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600689 if int(state['rest']):
690 for url_path in ret_values:
George Keishing3adda952021-02-11 13:23:51 -0600691 # Skip conflicting "CurrentHostState" URL from the enum
692 # /xyz/openbmc_project/state/hypervisor0
693 if "hypervisor0" in url_path:
694 continue
695
Konstantin Aladyshev8c9cf9c2021-03-21 23:33:40 +0300696 if platform_arch_type == "x86":
697 # Skip conflicting "CurrentPowerState" URL from the enum
698 # /xyz/openbmc_project/state/chassis_system0
699 if "chassis_system0" in url_path:
700 continue
701
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600702 for attr_name in ret_values[url_path]:
703 # Create a state key value based on the attr_name.
704 try:
705 ret_values[url_path][attr_name] = \
706 re.sub(r'.*\.', "",
707 ret_values[url_path][attr_name])
708 except TypeError:
709 pass
710 # Do some key name manipulations.
711 new_attr_name = re.sub(r'^Current|(State|Transition)$',
712 "", attr_name)
713 new_attr_name = re.sub(r'BMC', r'Bmc', new_attr_name)
714 new_attr_name = re.sub(r'([A-Z][a-z])', r'_\1',
715 new_attr_name)
716 new_attr_name = new_attr_name.lower().lstrip("_")
717 new_attr_name = re.sub(r'power', r'chassis', new_attr_name)
718 if new_attr_name in req_states:
719 state[new_attr_name] = ret_values[url_path][attr_name]
720 else:
721 master_req_rf = ['redfish', 'host', 'requested_host',
722 'attempts_left', 'boot_progress', 'chassis',
723 'requested_chassis' 'bmc' 'requested_bmc']
724
725 req_rf = [sub_state for sub_state in req_states if sub_state in
726 master_req_rf]
727 need_rf = (len(req_rf) > 0)
728 state = DotDict()
729 if need_rf:
730 cmd_buf = ["Redfish Get States"]
731 gp.dprint_issuing(cmd_buf)
George Keishing21e28b42021-05-27 08:10:29 -0500732 try:
733 status, ret_values = \
734 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
735 except Exception as ex:
736 # Robot raised UserKeywordExecutionFailed error exception.
737 gp.dprint_issuing("Retrying Redfish Get States")
738 status, ret_values = \
739 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
740
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600741 gp.dprint_vars(status, ret_values)
742 if status == "PASS":
743 state['redfish'] = '1'
744 else:
745 state['redfish'] = '0'
746
747 if int(state['redfish']):
748 state['chassis'] = ret_values['chassis']
749 state['boot_progress'] = ret_values['boot_progress']
750 state['host'] = ret_values['host']
751 state['bmc'] = ret_values['bmc']
Michael Walshb95eb542017-03-31 09:39:20 -0500752
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600753 for sub_state in req_states:
Michael Walsh56749222017-09-29 15:26:07 -0500754 if sub_state in state:
755 continue
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600756 if sub_state.startswith("os_"):
757 # We pass "os_" requests on to get_os_state.
758 continue
759 cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")"
760 exec(cmd_buf)
761
762 if os_host == "":
763 # The caller has not specified an os_host so as far as we're concerned,
764 # it doesn't exist.
765 return state
766
767 os_req_states = [sub_state for sub_state in req_states
768 if sub_state.startswith('os_')]
769
770 if len(os_req_states) > 0:
771 # The caller has specified an os_host and they have requested
772 # information on os substates.
773
774 # Based on the information gathered on bmc, we'll try to make a
775 # determination of whether the os is even up. We'll pass the result
776 # of that assessment to get_os_state to enhance performance.
777 os_up_match = DotDict()
778 for sub_state in master_os_up_match:
779 if sub_state in req_states:
780 os_up_match[sub_state] = master_os_up_match[sub_state]
Michael Walsh70369fd2016-11-22 11:25:57 -0600781 os_up = compare_states(state, os_up_match)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600782 os_state = get_os_state(os_host=os_host,
783 os_username=os_username,
784 os_password=os_password,
785 req_states=os_req_states,
786 os_up=os_up,
787 quiet=quiet)
788 # Append os_state dictionary to ours.
789 state.update(os_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600790
791 return state
792
Michael Walsh70369fd2016-11-22 11:25:57 -0600793
Michael Walshfd5a8682019-02-01 14:28:42 -0600794exit_wait_early_message = ""
795
796
797def set_exit_wait_early_message(value):
798 r"""
799 Set global exit_wait_early_message to the indicated value.
800
801 This is a mechanism by which the programmer can do an early exit from
802 wait_until_keyword_succeeds() based on some special condition.
803
804 Description of argument(s):
805 value The value to assign to the global
806 exit_wait_early_message.
807 """
808
809 global exit_wait_early_message
810 exit_wait_early_message = value
811
812
Michael Walsh70369fd2016-11-22 11:25:57 -0600813def check_state(match_state,
814 invert=0,
815 print_string="",
816 openbmc_host="",
817 openbmc_username="",
818 openbmc_password="",
819 os_host="",
820 os_username="",
821 os_password="",
822 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600823 r"""
824 Check that the Open BMC machine's composite state matches the specified
825 state. On success, this keyword returns the machine's composite state as a
826 dictionary.
827
Michael Walsh2a0df682019-09-27 17:19:27 -0500828 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600829 match_state A dictionary whose key/value pairs are "state field"/
830 "state value". The state value is interpreted as a
831 regular expression. Example call from robot:
Michael Walsh341c21e2017-01-17 16:25:20 -0600832 ${match_state}= Create Dictionary chassis=^On$
833 ... bmc=^Ready$
Michael Walsh01975fa2017-08-20 20:51:36 -0500834 ... boot_progress=^OSStart$
Michael Walsh70369fd2016-11-22 11:25:57 -0600835 ${state}= Check State &{match_state}
836 invert If this flag is set, this function will succeed if the
837 states do NOT match.
838 print_string This function will print this string to the console prior
839 to getting the state.
840 openbmc_host The DNS name or IP address of the BMC.
841 This defaults to global ${OPENBMC_HOST}.
842 openbmc_username The username to be used to login to the BMC.
843 This defaults to global ${OPENBMC_USERNAME}.
844 openbmc_password The password to be used to login to the BMC.
845 This defaults to global ${OPENBMC_PASSWORD}.
846 os_host The DNS name or IP address of the operating system.
847 This defaults to global ${OS_HOST}.
848 os_username The username to be used to login to the OS.
849 This defaults to global ${OS_USERNAME}.
850 os_password The password to be used to login to the OS.
851 This defaults to global ${OS_PASSWORD}.
852 quiet Indicates whether status details should be written to the
853 console. Defaults to either global value of ${QUIET} or
854 to 1.
855 """
856
Michael Walsh619aa332017-04-12 15:56:51 -0500857 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600858
Michael Walshedb5c942019-03-28 12:40:50 -0500859 gp.gp_print(print_string)
860
861 try:
862 match_state = return_state_constant(match_state)
863 except TypeError:
864 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600865
Michael Walsh2a0df682019-09-27 17:19:27 -0500866 req_states = list(match_state.keys())
867 # Remove special-case match key from req_states.
868 if expressions_key() in req_states:
869 req_states.remove(expressions_key())
Michael Walsh70369fd2016-11-22 11:25:57 -0600870 # Initialize state.
871 state = get_state(openbmc_host=openbmc_host,
872 openbmc_username=openbmc_username,
873 openbmc_password=openbmc_password,
874 os_host=os_host,
875 os_username=os_username,
876 os_password=os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600877 req_states=req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600878 quiet=quiet)
879 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500880 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600881
Michael Walshfd5a8682019-02-01 14:28:42 -0600882 if exit_wait_early_message != "":
883 # The exit_wait_early_message has been set by a signal handler so we
884 # will exit "successfully". It is incumbent upon the calling function
885 # (e.g. wait_state) to check/clear this variable and to fail
886 # appropriately.
887 return state
888
Michael Walsh70369fd2016-11-22 11:25:57 -0600889 match = compare_states(state, match_state)
890
891 if invert and match:
892 fail_msg = "The current state of the machine matches the match" +\
893 " state:\n" + gp.sprint_varx("state", state)
894 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
895 elif not invert and not match:
896 fail_msg = "The current state of the machine does NOT match the" +\
897 " match state:\n" +\
898 gp.sprint_varx("state", state)
899 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
900
901 return state
902
Michael Walsh70369fd2016-11-22 11:25:57 -0600903
Michael Walshf893ba02017-01-10 10:28:05 -0600904def wait_state(match_state=(),
Michael Walsh70369fd2016-11-22 11:25:57 -0600905 wait_time="1 min",
906 interval="1 second",
907 invert=0,
908 openbmc_host="",
909 openbmc_username="",
910 openbmc_password="",
911 os_host="",
912 os_username="",
913 os_password="",
914 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600915 r"""
916 Wait for the Open BMC machine's composite state to match the specified
917 state. On success, this keyword returns the machine's composite state as
918 a dictionary.
919
Michael Walsh2a0df682019-09-27 17:19:27 -0500920 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600921 match_state A dictionary whose key/value pairs are "state field"/
922 "state value". See check_state (above) for details.
Michael Walsh619aa332017-04-12 15:56:51 -0500923 This value may also be any string accepted by
924 return_state_constant (e.g. "standby_match_state").
925 In such a case this function will call
926 return_state_constant to convert it to a proper
927 dictionary as described above.
Michael Walsh70369fd2016-11-22 11:25:57 -0600928 wait_time The total amount of time to wait for the desired state.
929 This value may be expressed in Robot Framework's time
930 format (e.g. 1 minute, 2 min 3 s, 4.5).
931 interval The amount of time between state checks.
932 This value may be expressed in Robot Framework's time
933 format (e.g. 1 minute, 2 min 3 s, 4.5).
934 invert If this flag is set, this function will for the state of
935 the machine to cease to match the match state.
936 openbmc_host The DNS name or IP address of the BMC.
937 This defaults to global ${OPENBMC_HOST}.
938 openbmc_username The username to be used to login to the BMC.
939 This defaults to global ${OPENBMC_USERNAME}.
940 openbmc_password The password to be used to login to the BMC.
941 This defaults to global ${OPENBMC_PASSWORD}.
942 os_host The DNS name or IP address of the operating system.
943 This defaults to global ${OS_HOST}.
944 os_username The username to be used to login to the OS.
945 This defaults to global ${OS_USERNAME}.
946 os_password The password to be used to login to the OS.
947 This defaults to global ${OS_PASSWORD}.
948 quiet Indicates whether status details should be written to the
949 console. Defaults to either global value of ${QUIET} or
950 to 1.
951 """
952
Michael Walsh619aa332017-04-12 15:56:51 -0500953 quiet = int(gp.get_var_value(quiet, 0))
954
George Keishing36efbc02018-12-12 10:18:23 -0600955 try:
Michael Walsh619aa332017-04-12 15:56:51 -0500956 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600957 except TypeError:
958 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600959
960 if not quiet:
961 if invert:
962 alt_text = "cease to "
963 else:
964 alt_text = ""
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500965 gp.print_timen("Checking every " + str(interval) + " for up to "
966 + str(wait_time) + " for the state of the machine to "
967 + alt_text + "match the state shown below.")
Michael Walsh3eb50022017-03-21 11:27:30 -0500968 gp.print_var(match_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600969
Michael Walshf893ba02017-01-10 10:28:05 -0600970 if quiet:
Michael Walsh341c21e2017-01-17 16:25:20 -0600971 print_string = ""
Michael Walshf893ba02017-01-10 10:28:05 -0600972 else:
Michael Walsh341c21e2017-01-17 16:25:20 -0600973 print_string = "#"
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600974
975 debug = int(BuiltIn().get_variable_value("${debug}", "0"))
976 if debug:
977 # In debug we print state so no need to print the "#".
978 print_string = ""
979 check_state_quiet = 1 - debug
Michael Walsh70369fd2016-11-22 11:25:57 -0600980 cmd_buf = ["Check State", match_state, "invert=${" + str(invert) + "}",
Michael Walshf893ba02017-01-10 10:28:05 -0600981 "print_string=" + print_string, "openbmc_host=" + openbmc_host,
Michael Walsh70369fd2016-11-22 11:25:57 -0600982 "openbmc_username=" + openbmc_username,
983 "openbmc_password=" + openbmc_password, "os_host=" + os_host,
984 "os_username=" + os_username, "os_password=" + os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600985 "quiet=${" + str(check_state_quiet) + "}"]
Michael Walshedb5c942019-03-28 12:40:50 -0500986 gp.dprint_issuing(cmd_buf)
Michael Walsh619aa332017-04-12 15:56:51 -0500987 try:
988 state = BuiltIn().wait_until_keyword_succeeds(wait_time, interval,
989 *cmd_buf)
990 except AssertionError as my_assertion_error:
991 gp.printn()
992 message = my_assertion_error.args[0]
993 BuiltIn().fail(message)
994
Michael Walshfd5a8682019-02-01 14:28:42 -0600995 if exit_wait_early_message:
996 # The global exit_wait_early_message was set by a signal handler
997 # indicating that we should fail.
998 message = exit_wait_early_message
999 # Clear the exit_wait_early_message variable for future use.
1000 set_exit_wait_early_message("")
1001 BuiltIn().fail(gp.sprint_error(message))
1002
Michael Walsh70369fd2016-11-22 11:25:57 -06001003 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -05001004 gp.printn()
Michael Walsh70369fd2016-11-22 11:25:57 -06001005 if invert:
Michael Walsh3eb50022017-03-21 11:27:30 -05001006 gp.print_timen("The states no longer match:")
Michael Walsh70369fd2016-11-22 11:25:57 -06001007 else:
Michael Walsh3eb50022017-03-21 11:27:30 -05001008 gp.print_timen("The states match:")
1009 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001010
1011 return state
1012
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001013
Michael Walsh2a0df682019-09-27 17:19:27 -05001014def set_start_boot_seconds(value=0):
1015 global start_boot_seconds
1016 start_boot_seconds = int(value)
1017
1018
1019set_start_boot_seconds(0)
1020
1021
Michael Walsh619aa332017-04-12 15:56:51 -05001022def wait_for_comm_cycle(start_boot_seconds,
1023 quiet=None):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001024 r"""
Michael Walsh2a0df682019-09-27 17:19:27 -05001025 Wait for the BMC uptime to be less than elapsed_boot_time.
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001026
Michael Walsh2a0df682019-09-27 17:19:27 -05001027 This function will tolerate an expected loss of communication to the BMC.
1028 This function is useful when some kind of reboot has been initiated by the
1029 caller.
1030
1031 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001032 start_boot_seconds The time that the boot test started. The format is the
1033 epoch time in seconds, i.e. the number of seconds since
1034 1970-01-01 00:00:00 UTC. This value should be obtained
1035 from the BMC so that it is not dependent on any kind of
1036 synchronization between this machine and the target BMC
1037 This will allow this program to work correctly even in
1038 a simulated environment. This value should be obtained
1039 by the caller prior to initiating a reboot. It can be
1040 obtained as follows:
1041 state = st.get_state(req_states=['epoch_seconds'])
1042 """
1043
Michael Walsh619aa332017-04-12 15:56:51 -05001044 quiet = int(gp.get_var_value(quiet, 0))
1045
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001046 # Validate parms.
Michael Walsh2a0df682019-09-27 17:19:27 -05001047 error_message = gv.valid_integer(start_boot_seconds)
1048 if error_message:
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001049 BuiltIn().fail(gp.sprint_error(error_message))
1050
Michael Walsh2a0df682019-09-27 17:19:27 -05001051 # Wait for uptime to be less than elapsed_boot_time.
1052 set_start_boot_seconds(start_boot_seconds)
1053 expr = 'int(float(state[\'uptime\'])) < int(state[\'elapsed_boot_time\'])'
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001054 match_state = DotDict([('uptime', '^[0-9\\.]+$'),
Michael Walsh2a0df682019-09-27 17:19:27 -05001055 ('elapsed_boot_time', '^[0-9]+$'),
1056 (expressions_key(), [expr])])
David Shawe9192562020-09-28 11:00:45 -05001057 wait_state(match_state, wait_time="12 mins", interval="5 seconds")
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001058
Michael Sheposbdd1dce2020-12-10 11:51:58 -06001059 gp.qprint_timen("Verifying that REST/Redfish API interface is working.")
1060 if not redfish_support_trans_state:
1061 match_state = DotDict([('rest', '^1$')])
1062 else:
1063 match_state = DotDict([('redfish', '^1$')])
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001064 state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")