blob: e48e8d8e114c1a85924b7f84ec68cc4c6ce680a2 [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
Michael Walsh70369fd2016-11-22 11:25:57 -06002
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
Brian Ma139f1da2024-10-18 13:34:14 +080030import importlib.util
Patrick Williams20f38712022-12-08 06:18:26 -060031import os
32import re
33import sys
George Keishinge635ddc2022-12-08 07:38:02 -060034
Patrick Williams20f38712022-12-08 06:18:26 -060035import bmc_ssh_utils as bsu
36import gen_cmd as gc
37import gen_print as gp
38import gen_robot_utils as gru
39import gen_valid as gv
Michael Walsh70369fd2016-11-22 11:25:57 -060040from robot.libraries.BuiltIn import BuiltIn
Michael Walsh341c21e2017-01-17 16:25:20 -060041from robot.utils import DotDict
Michael Walsh70369fd2016-11-22 11:25:57 -060042
Michael Walsh5a5868a2018-10-31 15:20:04 -050043# NOTE: Avoid importing utils.robot because utils.robot imports state.py
44# (indirectly) which will cause failures.
45gru.my_import_resource("rest_client.robot")
Michael Walsh70369fd2016-11-22 11:25:57 -060046
Patrick Williams20f38712022-12-08 06:18:26 -060047base_path = (
Brian Ma139f1da2024-10-18 13:34:14 +080048 os.path.dirname(
49 os.path.dirname(importlib.util.find_spec("gen_robot_print").origin)
50 )
Patrick Williams20f38712022-12-08 06:18:26 -060051 + os.sep
52)
Michael Walsh56749222017-09-29 15:26:07 -050053sys.path.append(base_path + "data/")
Michael Walsh56749222017-09-29 15:26:07 -050054
Michael Walsh940d6912017-10-27 12:32:33 -050055# Previously, I had this coded:
56# import variables as var
57# However, we ran into a problem where a robot program did this...
58# Variables ../../lib/ras/variables.py
59# Prior to doing this...
60# Library ../lib/state.py
61
62# This caused the wrong variables.py file to be selected. Attempts to fix this
63# have failed so far. For the moment, we will hard-code the value we need from
64# the file.
65
66SYSTEM_STATE_URI = "/xyz/openbmc_project/state/"
Michael Walsh56749222017-09-29 15:26:07 -050067
Michael Walsh8fae6ea2017-02-20 16:14:44 -060068# The BMC code has recently been changed as far as what states are defined and
69# what the state values can be. This module now has a means of processing both
70# the old style state (i.e. OBMC_STATES_VERSION = 0) and the new style (i.e.
Michael Walsh16cbb7f2017-02-02 15:54:16 -060071# OBMC_STATES_VERSION = 1).
Michael Walsh341c21e2017-01-17 16:25:20 -060072# The caller can set environment variable OBMC_STATES_VERSION to dictate
73# whether we're processing old or new style states. If OBMC_STATES_VERSION is
Michael Walsh8fae6ea2017-02-20 16:14:44 -060074# not set it will default to 1.
Michael Walsh341c21e2017-01-17 16:25:20 -060075
Michael Walsh619aa332017-04-12 15:56:51 -050076# As of the present moment, OBMC_STATES_VERSION of 0 is for cold that is so old
77# that it is no longer worthwhile to maintain. The OBMC_STATES_VERSION 0 code
78# is being removed but the OBMC_STATES_VERSION value will stay for now in the
79# event that it is needed in the future.
80
Patrick Williams20f38712022-12-08 06:18:26 -060081OBMC_STATES_VERSION = int(os.environ.get("OBMC_STATES_VERSION", 1))
Michael Walsh341c21e2017-01-17 16:25:20 -060082
Patrick Williams20f38712022-12-08 06:18:26 -060083redfish_support_trans_state = int(
84 os.environ.get("REDFISH_SUPPORT_TRANS_STATE", 0)
85) or int(
86 BuiltIn().get_variable_value("${REDFISH_SUPPORT_TRANS_STATE}", default=0)
87)
Michael Sheposda40c1d2020-12-01 22:30:00 -060088
Patrick Williams20f38712022-12-08 06:18:26 -060089platform_arch_type = os.environ.get(
90 "PLATFORM_ARCH_TYPE", ""
91) or BuiltIn().get_variable_value("${PLATFORM_ARCH_TYPE}", default="power")
George Keishing1e2fbee2021-03-19 11:19:29 -050092
Michael Walsh8fae6ea2017-02-20 16:14:44 -060093# valid_os_req_states and default_os_req_states are used by the os_get_state
94# function.
95# valid_os_req_states is a list of state information supported by the
96# get_os_state function.
Patrick Williams20f38712022-12-08 06:18:26 -060097valid_os_req_states = ["os_ping", "os_login", "os_run_cmd"]
Michael Sheposbdd1dce2020-12-10 11:51:58 -060098
Michael Walsh8fae6ea2017-02-20 16:14:44 -060099# When a user calls get_os_state w/o specifying req_states,
100# default_os_req_states is used as its value.
Patrick Williams20f38712022-12-08 06:18:26 -0600101default_os_req_states = ["os_ping", "os_login", "os_run_cmd"]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600102
103# Presently, some BMCs appear to not keep time very well. This environment
104# variable directs the get_state function to use either the BMC's epoch time
105# or the local epoch time.
Patrick Williams20f38712022-12-08 06:18:26 -0600106USE_BMC_EPOCH_TIME = int(os.environ.get("USE_BMC_EPOCH_TIME", 0))
Michael Walsh341c21e2017-01-17 16:25:20 -0600107
Michael Walsh619aa332017-04-12 15:56:51 -0500108# Useful state constant definition(s).
Michael Sheposda40c1d2020-12-01 22:30:00 -0600109if not redfish_support_trans_state:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600110 # When a user calls get_state w/o specifying req_states, default_req_states
111 # is used as its value.
Patrick Williams20f38712022-12-08 06:18:26 -0600112 default_req_states = [
113 "rest",
114 "chassis",
115 "bmc",
116 "boot_progress",
117 "operating_system",
118 "host",
119 "os_ping",
120 "os_login",
121 "os_run_cmd",
122 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600123
124 # valid_req_states is a list of sub states supported by the get_state function.
125 # valid_req_states, default_req_states and master_os_up_match are used by the
126 # get_state function.
127
Patrick Williams20f38712022-12-08 06:18:26 -0600128 valid_req_states = [
129 "ping",
130 "packet_loss",
131 "uptime",
132 "epoch_seconds",
133 "elapsed_boot_time",
134 "rest",
135 "chassis",
136 "requested_chassis",
137 "bmc",
138 "requested_bmc",
139 "boot_progress",
140 "operating_system",
141 "host",
142 "requested_host",
143 "attempts_left",
144 "os_ping",
145 "os_login",
146 "os_run_cmd",
147 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600148
Michael Sheposda40c1d2020-12-01 22:30:00 -0600149 # default_state is an initial value which may be of use to callers.
Patrick Williams20f38712022-12-08 06:18:26 -0600150 default_state = DotDict(
151 [
152 ("rest", "1"),
153 ("chassis", "On"),
154 ("bmc", "Ready"),
155 ("boot_progress", "OSStart"),
156 ("operating_system", "BootComplete"),
157 ("host", "Running"),
158 ("os_ping", "1"),
159 ("os_login", "1"),
160 ("os_run_cmd", "1"),
161 ]
162 )
Michael Walsh619aa332017-04-12 15:56:51 -0500163
Michael Sheposda40c1d2020-12-01 22:30:00 -0600164 # A match state for checking that the system is at "standby".
Patrick Williams20f38712022-12-08 06:18:26 -0600165 standby_match_state = DotDict(
166 [
167 ("rest", "^1$"),
168 ("chassis", "^Off$"),
169 ("bmc", "^Ready$"),
170 ("boot_progress", "^Off|Unspecified$"),
171 ("operating_system", "^Inactive$"),
172 ("host", "^Off$"),
173 ]
174 )
Michael Walsh7dc885b2018-03-14 17:51:59 -0500175
Michael Sheposda40c1d2020-12-01 22:30:00 -0600176 # A match state for checking that the system is at "os running".
Patrick Williams20f38712022-12-08 06:18:26 -0600177 os_running_match_state = DotDict(
178 [
179 ("chassis", "^On$"),
180 ("bmc", "^Ready$"),
181 ("boot_progress", "FW Progress, Starting OS|OSStart"),
182 ("operating_system", "BootComplete"),
183 ("host", "^Running$"),
184 ("os_ping", "^1$"),
185 ("os_login", "^1$"),
186 ("os_run_cmd", "^1$"),
187 ]
188 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600189
190 # A master dictionary to determine whether the os may be up.
Patrick Williams20f38712022-12-08 06:18:26 -0600191 master_os_up_match = DotDict(
192 [
193 ("chassis", "^On$"),
194 ("bmc", "^Ready$"),
195 ("boot_progress", "FW Progress, Starting OS|OSStart"),
196 ("operating_system", "BootComplete"),
197 ("host", "^Running|Quiesced$"),
198 ]
199 )
Michael Walsh7dc885b2018-03-14 17:51:59 -0500200
Patrick Williams20f38712022-12-08 06:18:26 -0600201 invalid_state_match = DotDict(
202 [
203 ("rest", "^$"),
204 ("chassis", "^$"),
205 ("bmc", "^$"),
206 ("boot_progress", "^$"),
207 ("operating_system", "^$"),
208 ("host", "^$"),
209 ]
210 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600211else:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600212 # When a user calls get_state w/o specifying req_states, default_req_states
213 # is used as its value.
Patrick Williams20f38712022-12-08 06:18:26 -0600214 default_req_states = [
215 "redfish",
216 "chassis",
217 "bmc",
218 "boot_progress",
219 "host",
220 "os_ping",
221 "os_login",
222 "os_run_cmd",
223 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600224
225 # valid_req_states is a list of sub states supported by the get_state function.
226 # valid_req_states, default_req_states and master_os_up_match are used by the
227 # get_state function.
228
Patrick Williams20f38712022-12-08 06:18:26 -0600229 valid_req_states = [
230 "ping",
231 "packet_loss",
232 "uptime",
233 "epoch_seconds",
234 "elapsed_boot_time",
235 "redfish",
236 "chassis",
237 "requested_chassis",
238 "bmc",
239 "requested_bmc",
240 "boot_progress",
241 "host",
242 "requested_host",
243 "attempts_left",
244 "os_ping",
245 "os_login",
246 "os_run_cmd",
247 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600248
Michael Sheposda40c1d2020-12-01 22:30:00 -0600249 # default_state is an initial value which may be of use to callers.
Patrick Williams20f38712022-12-08 06:18:26 -0600250 default_state = DotDict(
251 [
252 ("redfish", "1"),
253 ("chassis", "On"),
254 ("bmc", "Enabled"),
255 (
256 "boot_progress",
257 "SystemHardwareInitializationComplete|OSBootStarted|OSRunning",
258 ),
259 ("host", "Enabled"),
260 ("os_ping", "1"),
261 ("os_login", "1"),
262 ("os_run_cmd", "1"),
263 ]
264 )
Michael Walsh619aa332017-04-12 15:56:51 -0500265
Michael Sheposda40c1d2020-12-01 22:30:00 -0600266 # A match state for checking that the system is at "standby".
Patrick Williams20f38712022-12-08 06:18:26 -0600267 standby_match_state = DotDict(
268 [
269 ("redfish", "^1$"),
270 ("chassis", "^Off$"),
271 ("bmc", "^Enabled$"),
272 ("boot_progress", "^None$"),
273 ("host", "^Disabled$"),
274 ]
275 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600276
277 # A match state for checking that the system is at "os running".
Patrick Williams20f38712022-12-08 06:18:26 -0600278 os_running_match_state = DotDict(
279 [
280 ("chassis", "^On$"),
281 ("bmc", "^Enabled$"),
282 (
283 "boot_progress",
284 "SystemHardwareInitializationComplete|OSBootStarted|OSRunning",
285 ),
286 ("host", "^Enabled$"),
287 ("os_ping", "^1$"),
288 ("os_login", "^1$"),
289 ("os_run_cmd", "^1$"),
290 ]
291 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600292
293 # A master dictionary to determine whether the os may be up.
Patrick Williams20f38712022-12-08 06:18:26 -0600294 master_os_up_match = DotDict(
295 [
296 ("chassis", "^On$"),
297 ("bmc", "^Enabled$"),
298 (
299 "boot_progress",
300 "SystemHardwareInitializationComplete|OSBootStarted|OSRunning",
301 ),
302 ("host", "^Enabled$"),
303 ]
304 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600305
Patrick Williams20f38712022-12-08 06:18:26 -0600306 invalid_state_match = DotDict(
307 [
308 ("redfish", "^$"),
309 ("chassis", "^$"),
310 ("bmc", "^$"),
311 ("boot_progress", "^$"),
312 ("host", "^$"),
313 ]
314 )
Michael Walsh45ca6e42017-09-14 17:29:12 -0500315
George Keishing1e2fbee2021-03-19 11:19:29 -0500316# Filter the states based on platform type.
317if platform_arch_type == "x86":
George Keishingb51d1502021-03-25 03:30:33 -0500318 if not redfish_support_trans_state:
319 default_req_states.remove("operating_system")
320 valid_req_states.remove("operating_system")
321 del default_state["operating_system"]
322 del standby_match_state["operating_system"]
323 del os_running_match_state["operating_system"]
324 del master_os_up_match["operating_system"]
325 del invalid_state_match["operating_system"]
326
George Keishing1e2fbee2021-03-19 11:19:29 -0500327 default_req_states.remove("boot_progress")
George Keishing1e2fbee2021-03-19 11:19:29 -0500328 valid_req_states.remove("boot_progress")
George Keishing1e2fbee2021-03-19 11:19:29 -0500329 del default_state["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500330 del standby_match_state["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500331 del os_running_match_state["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500332 del master_os_up_match["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500333 del invalid_state_match["boot_progress"]
334
Michael Walsh341c21e2017-01-17 16:25:20 -0600335
Patrick Williams20f38712022-12-08 06:18:26 -0600336def return_state_constant(state_name="default_state"):
Michael Walsh619aa332017-04-12 15:56:51 -0500337 r"""
Michael Walsh7dc885b2018-03-14 17:51:59 -0500338 Return the named state dictionary constant.
Michael Walsh619aa332017-04-12 15:56:51 -0500339 """
340
George Keishing36efbc02018-12-12 10:18:23 -0600341 return eval(state_name)
Michael Walsh619aa332017-04-12 15:56:51 -0500342
Michael Walsh619aa332017-04-12 15:56:51 -0500343
Michael Walsh70369fd2016-11-22 11:25:57 -0600344def anchor_state(state):
Michael Walsh70369fd2016-11-22 11:25:57 -0600345 r"""
346 Add regular expression anchors ("^" and "$") to the beginning and end of
347 each item in the state dictionary passed in. Return the resulting
348 dictionary.
349
Michael Walsh2a0df682019-09-27 17:19:27 -0500350 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600351 state A dictionary such as the one returned by the get_state()
352 function.
353 """
354
Michael Walsh2ce067a2017-02-27 14:24:07 -0600355 anchored_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500356 for key in anchored_state.keys():
Michael Walsh70369fd2016-11-22 11:25:57 -0600357 anchored_state[key] = "^" + str(anchored_state[key]) + "$"
358
359 return anchored_state
360
Michael Walsh70369fd2016-11-22 11:25:57 -0600361
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600362def strip_anchor_state(state):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600363 r"""
364 Strip regular expression anchors ("^" and "$") from the beginning and end
365 of each item in the state dictionary passed in. Return the resulting
366 dictionary.
367
Michael Walsh2a0df682019-09-27 17:19:27 -0500368 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600369 state A dictionary such as the one returned by the get_state()
370 function.
371 """
372
Michael Walsh2ce067a2017-02-27 14:24:07 -0600373 stripped_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500374 for key in stripped_state.keys():
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600375 stripped_state[key] = stripped_state[key].strip("^$")
376
377 return stripped_state
378
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600379
Michael Walsh2a0df682019-09-27 17:19:27 -0500380def expressions_key():
381 r"""
382 Return expressions key constant.
383 """
Patrick Williams20f38712022-12-08 06:18:26 -0600384 return "<expressions>"
Michael Walsh2a0df682019-09-27 17:19:27 -0500385
386
Patrick Williams20f38712022-12-08 06:18:26 -0600387def compare_states(state, match_state, match_type="and"):
Michael Walsh70369fd2016-11-22 11:25:57 -0600388 r"""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600389 Compare 2 state dictionaries. Return True if they match and False if they
Michael Walsh70369fd2016-11-22 11:25:57 -0600390 don't. Note that the match_state dictionary does not need to have an entry
391 corresponding to each entry in the state dictionary. But for each entry
392 that it does have, the corresponding state entry will be checked for a
393 match.
394
Michael Walsh2a0df682019-09-27 17:19:27 -0500395 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600396 state A state dictionary such as the one returned by the
397 get_state function.
398 match_state A dictionary whose key/value pairs are "state field"/
399 "state value". The state value is interpreted as a
400 regular expression. Every value in this dictionary is
Michael Walsh45ca6e42017-09-14 17:29:12 -0500401 considered. When match_type is 'and', if each and every
402 comparison matches, the two dictionaries are considered to
403 be matching. If match_type is 'or', if any two of the
404 elements compared match, the two dictionaries are
405 considered to be matching.
Michael Walsh2a0df682019-09-27 17:19:27 -0500406
Michael Walsh7dc885b2018-03-14 17:51:59 -0500407 This value may also be any string accepted by
Michael Walsh2a0df682019-09-27 17:19:27 -0500408 return_state_constant (e.g. "standby_match_state"). In
409 such a case this function will call return_state_constant
410 to convert it to a proper dictionary as described above.
411
412 Finally, one special value is accepted for the key field:
413 expression_key(). If such an entry exists, its value is
414 taken to be a list of expressions to be evaluated. These
415 expressions may reference state dictionary entries by
416 simply coding them in standard python syntax (e.g.
417 state['key1']). What follows is an example expression:
418
419 "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
420
421 In this example, if the state dictionary's 'uptime' entry
422 is less than its 'elapsed_boot_time' entry, it would
423 qualify as a match.
Michael Walsh45ca6e42017-09-14 17:29:12 -0500424 match_type This may be 'and' or 'or'.
Michael Walsh70369fd2016-11-22 11:25:57 -0600425 """
426
Patrick Williams20f38712022-12-08 06:18:26 -0600427 error_message = gv.valid_value(match_type, valid_values=["and", "or"])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500428 if error_message != "":
429 BuiltIn().fail(gp.sprint_error(error_message))
430
George Keishing36efbc02018-12-12 10:18:23 -0600431 try:
Michael Walsh7dc885b2018-03-14 17:51:59 -0500432 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600433 except TypeError:
434 pass
Michael Walsh7dc885b2018-03-14 17:51:59 -0500435
Patrick Williams20f38712022-12-08 06:18:26 -0600436 default_match = match_type == "and"
Michael Walsh70369fd2016-11-22 11:25:57 -0600437 for key, match_state_value in match_state.items():
Michael Walsh97df71c2017-03-27 14:33:24 -0500438 # Blank match_state_value means "don't care".
439 if match_state_value == "":
440 continue
Michael Walsh2a0df682019-09-27 17:19:27 -0500441 if key == expressions_key():
442 for expr in match_state_value:
443 # Use python interpreter to evaluate the expression.
444 match = eval(expr)
445 if match != default_match:
446 return match
447 else:
448 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600449 match = (
450 re.match(match_state_value, str(state[key])) is not None
451 )
Michael Walsh2a0df682019-09-27 17:19:27 -0500452 except KeyError:
453 match = False
454 if match != default_match:
455 return match
Michael Walsh45ca6e42017-09-14 17:29:12 -0500456
457 return default_match
Michael Walsh70369fd2016-11-22 11:25:57 -0600458
Michael Walsh70369fd2016-11-22 11:25:57 -0600459
Patrick Williams20f38712022-12-08 06:18:26 -0600460def get_os_state(
461 os_host="",
462 os_username="",
463 os_password="",
464 req_states=default_os_req_states,
465 os_up=True,
466 quiet=None,
467):
Michael Walsh70369fd2016-11-22 11:25:57 -0600468 r"""
469 Get component states for the operating system such as ping, login,
470 etc, put them into a dictionary and return them to the caller.
471
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600472 Note that all substate values are strings.
473
Michael Walsh2a0df682019-09-27 17:19:27 -0500474 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600475 os_host The DNS name or IP address of the operating system.
476 This defaults to global ${OS_HOST}.
477 os_username The username to be used to login to the OS.
478 This defaults to global ${OS_USERNAME}.
479 os_password The password to be used to login to the OS.
480 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600481 req_states This is a list of states whose values are being requested by
482 the caller.
483 os_up If the caller knows that the os can't possibly be up, it can
484 improve performance by passing os_up=False. This function
485 will then simply return default values for all requested os
486 sub states.
Michael Walsh70369fd2016-11-22 11:25:57 -0600487 quiet Indicates whether status details (e.g. curl commands) should
488 be written to the console.
489 Defaults to either global value of ${QUIET} or to 1.
490 """
491
Michael Walsh619aa332017-04-12 15:56:51 -0500492 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600493
494 # Set parm defaults where necessary and validate all parms.
495 if os_host == "":
496 os_host = BuiltIn().get_variable_value("${OS_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500497 error_message = gv.valid_value(os_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600498 if error_message != "":
499 BuiltIn().fail(gp.sprint_error(error_message))
500
501 if os_username == "":
502 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500503 error_message = gv.valid_value(os_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600504 if error_message != "":
505 BuiltIn().fail(gp.sprint_error(error_message))
506
507 if os_password == "":
508 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500509 error_message = gv.valid_value(os_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600510 if error_message != "":
511 BuiltIn().fail(gp.sprint_error(error_message))
512
Patrick Williams20f38712022-12-08 06:18:26 -0600513 invalid_req_states = [
514 sub_state
515 for sub_state in req_states
516 if sub_state not in valid_os_req_states
517 ]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600518 if len(invalid_req_states) > 0:
Patrick Williams20f38712022-12-08 06:18:26 -0600519 error_message = (
520 "The following req_states are not supported:\n"
521 + gp.sprint_var(invalid_req_states)
522 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600523 BuiltIn().fail(gp.sprint_error(error_message))
Michael Walsh70369fd2016-11-22 11:25:57 -0600524
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600525 # Initialize all substate values supported by this function.
526 os_ping = 0
527 os_login = 0
528 os_run_cmd = 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600529
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600530 if os_up:
Patrick Williams20f38712022-12-08 06:18:26 -0600531 if "os_ping" in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600532 # See if the OS pings.
Patrick Williams20f38712022-12-08 06:18:26 -0600533 rc, out_buf = gc.shell_cmd(
534 "ping -c 1 -w 2 " + os_host,
535 print_output=0,
536 show_err=0,
537 ignore_err=1,
538 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600539 if rc == 0:
540 os_ping = 1
Michael Walsh70369fd2016-11-22 11:25:57 -0600541
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600542 # Programming note: All attributes which do not require an ssh login
543 # should have been processed by this point.
Patrick Williams20f38712022-12-08 06:18:26 -0600544 master_req_login = ["os_login", "os_run_cmd"]
545 req_login = [
546 sub_state
547 for sub_state in req_states
548 if sub_state in master_req_login
549 ]
550 must_login = len(req_login) > 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600551
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600552 if must_login:
Patrick Williams20f38712022-12-08 06:18:26 -0600553 output, stderr, rc = bsu.os_execute_command(
554 "uptime",
555 quiet=quiet,
556 ignore_err=1,
557 time_out=20,
558 os_host=os_host,
559 os_username=os_username,
560 os_password=os_password,
561 )
Michael Walsh6a9bd142018-07-24 16:10:29 -0500562 if rc == 0:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600563 os_login = 1
Michael Walsh6a9bd142018-07-24 16:10:29 -0500564 os_run_cmd = 1
Michael Walsh3eb50022017-03-21 11:27:30 -0500565 else:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500566 gp.dprint_vars(output, stderr)
567 gp.dprint_vars(rc, 1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600568
569 os_state = DotDict()
570 for sub_state in req_states:
571 cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")"
572 exec(cmd_buf)
Michael Walsh70369fd2016-11-22 11:25:57 -0600573
574 return os_state
575
Michael Walsh70369fd2016-11-22 11:25:57 -0600576
Patrick Williams20f38712022-12-08 06:18:26 -0600577def get_state(
578 openbmc_host="",
579 openbmc_username="",
580 openbmc_password="",
581 os_host="",
582 os_username="",
583 os_password="",
584 req_states=default_req_states,
585 quiet=None,
586):
Michael Walsh70369fd2016-11-22 11:25:57 -0600587 r"""
Michael Walsh619aa332017-04-12 15:56:51 -0500588 Get component states such as chassis state, bmc state, etc, put them into a
Michael Walsh70369fd2016-11-22 11:25:57 -0600589 dictionary and return them to the caller.
590
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600591 Note that all substate values are strings.
592
Michael Walsh2a0df682019-09-27 17:19:27 -0500593 Note: If elapsed_boot_time is included in req_states, it is the caller's
594 duty to call set_start_boot_seconds() in order to set global
595 start_boot_seconds. elapsed_boot_time is the current time minus
596 start_boot_seconds.
597
598 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600599 openbmc_host The DNS name or IP address of the BMC.
600 This defaults to global ${OPENBMC_HOST}.
601 openbmc_username The username to be used to login to the BMC.
602 This defaults to global ${OPENBMC_USERNAME}.
603 openbmc_password The password to be used to login to the BMC.
604 This defaults to global ${OPENBMC_PASSWORD}.
605 os_host The DNS name or IP address of the operating system.
606 This defaults to global ${OS_HOST}.
607 os_username The username to be used to login to the OS.
608 This defaults to global ${OS_USERNAME}.
609 os_password The password to be used to login to the OS.
610 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600611 req_states This is a list of states whose values are being requested
612 by the caller.
Michael Walsh70369fd2016-11-22 11:25:57 -0600613 quiet Indicates whether status details (e.g. curl commands)
614 should be written to the console.
615 Defaults to either global value of ${QUIET} or to 1.
616 """
617
Michael Walsh619aa332017-04-12 15:56:51 -0500618 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600619
620 # Set parm defaults where necessary and validate all parms.
621 if openbmc_host == "":
622 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500623 error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600624 if error_message != "":
625 BuiltIn().fail(gp.sprint_error(error_message))
626
627 if openbmc_username == "":
628 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500629 error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600630 if error_message != "":
631 BuiltIn().fail(gp.sprint_error(error_message))
632
633 if openbmc_password == "":
634 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500635 error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600636 if error_message != "":
637 BuiltIn().fail(gp.sprint_error(error_message))
638
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600639 # NOTE: OS parms are optional.
Michael Walsh70369fd2016-11-22 11:25:57 -0600640 if os_host == "":
641 os_host = BuiltIn().get_variable_value("${OS_HOST}")
642 if os_host is None:
643 os_host = ""
644
George Keishinge7e91712021-09-03 11:28:44 -0500645 if os_username == "":
Michael Walsh70369fd2016-11-22 11:25:57 -0600646 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
647 if os_username is None:
648 os_username = ""
649
George Keishinge7e91712021-09-03 11:28:44 -0500650 if os_password == "":
Michael Walsh70369fd2016-11-22 11:25:57 -0600651 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
652 if os_password is None:
653 os_password = ""
654
Patrick Williams20f38712022-12-08 06:18:26 -0600655 invalid_req_states = [
656 sub_state
657 for sub_state in req_states
658 if sub_state not in valid_req_states
659 ]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600660 if len(invalid_req_states) > 0:
Patrick Williams20f38712022-12-08 06:18:26 -0600661 error_message = (
662 "The following req_states are not supported:\n"
663 + gp.sprint_var(invalid_req_states)
664 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600665 BuiltIn().fail(gp.sprint_error(error_message))
666
667 # Initialize all substate values supported by this function.
668 ping = 0
Patrick Williams20f38712022-12-08 06:18:26 -0600669 packet_loss = ""
670 uptime = ""
671 epoch_seconds = ""
672 elapsed_boot_time = ""
673 rest = ""
674 redfish = ""
675 chassis = ""
676 requested_chassis = ""
677 bmc = ""
678 requested_bmc = ""
George Keishing18f15dd2021-03-09 11:17:35 -0600679 # BootProgress state will get populated when state logic enumerates the
680 # state URI. This is to prevent state dictionary boot_progress value
681 # getting empty when the BootProgress is NOT found, making it optional.
Patrick Williams20f38712022-12-08 06:18:26 -0600682 boot_progress = "NA"
683 operating_system = ""
684 host = ""
685 requested_host = ""
686 attempts_left = ""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600687
Michael Walsh70369fd2016-11-22 11:25:57 -0600688 # Get the component states.
Patrick Williams20f38712022-12-08 06:18:26 -0600689 if "ping" in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600690 # See if the OS pings.
Patrick Williams20f38712022-12-08 06:18:26 -0600691 rc, out_buf = gc.shell_cmd(
692 "ping -c 1 -w 2 " + openbmc_host,
693 print_output=0,
694 show_err=0,
695 ignore_err=1,
696 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600697 if rc == 0:
698 ping = 1
699
Patrick Williams20f38712022-12-08 06:18:26 -0600700 if "packet_loss" in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600701 # See if the OS pings.
Patrick Williams20f38712022-12-08 06:18:26 -0600702 cmd_buf = (
703 "ping -c 5 -w 5 "
704 + openbmc_host
705 + " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'"
706 )
707 rc, out_buf = gc.shell_cmd(
708 cmd_buf, print_output=0, show_err=0, ignore_err=1
709 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600710 if rc == 0:
711 packet_loss = out_buf.rstrip("\n")
712
Patrick Williams20f38712022-12-08 06:18:26 -0600713 if "uptime" in req_states:
Michael Walshfa765932017-10-13 14:07:22 -0500714 # Sometimes reading uptime results in a blank value. Call with
715 # wait_until_keyword_succeeds to ensure a non-blank value is obtained.
Patrick Williams20f38712022-12-08 06:18:26 -0600716 remote_cmd_buf = (
717 "bash -c 'read uptime filler 2>/dev/null < /proc/uptime"
718 + ' && [ ! -z "${uptime}" ] && echo ${uptime}\''
719 )
720 cmd_buf = [
721 "BMC Execute Command",
722 re.sub("\\$", "\\$", remote_cmd_buf),
723 "quiet=1",
724 "test_mode=0",
725 "time_out=5",
726 ]
Michael Walsh2a0df682019-09-27 17:19:27 -0500727 gp.qprint_issuing(cmd_buf, 0)
728 gp.qprint_issuing(remote_cmd_buf, 0)
Michael Walshfa765932017-10-13 14:07:22 -0500729 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600730 stdout, stderr, rc = BuiltIn().wait_until_keyword_succeeds(
731 "10 sec", "5 sec", *cmd_buf
732 )
Michael Walsh97df71c2017-03-27 14:33:24 -0500733 if rc == 0 and stderr == "":
734 uptime = stdout
Michael Walshfa765932017-10-13 14:07:22 -0500735 except AssertionError as my_assertion_error:
736 pass
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600737
Patrick Williams20f38712022-12-08 06:18:26 -0600738 if "epoch_seconds" in req_states or "elapsed_boot_time" in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600739 date_cmd_buf = "date -u +%s"
740 if USE_BMC_EPOCH_TIME:
Patrick Williams20f38712022-12-08 06:18:26 -0600741 cmd_buf = ["BMC Execute Command", date_cmd_buf, "quiet=${1}"]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600742 if not quiet:
Michael Walshedb5c942019-03-28 12:40:50 -0500743 gp.print_issuing(cmd_buf)
Patrick Williams20f38712022-12-08 06:18:26 -0600744 status, ret_values = BuiltIn().run_keyword_and_ignore_error(
745 *cmd_buf
746 )
Michael Walsh97df71c2017-03-27 14:33:24 -0500747 if status == "PASS":
748 stdout, stderr, rc = ret_values
749 if rc == 0 and stderr == "":
750 epoch_seconds = stdout.rstrip("\n")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600751 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600752 shell_rc, out_buf = gc.cmd_fnc_u(
753 date_cmd_buf, quiet=quiet, print_output=0
754 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600755 if shell_rc == 0:
756 epoch_seconds = out_buf.rstrip("\n")
757
Patrick Williams20f38712022-12-08 06:18:26 -0600758 if "elapsed_boot_time" in req_states:
Michael Walsh2a0df682019-09-27 17:19:27 -0500759 global start_boot_seconds
760 elapsed_boot_time = int(epoch_seconds) - start_boot_seconds
761
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600762 if not redfish_support_trans_state:
Patrick Williams20f38712022-12-08 06:18:26 -0600763 master_req_rest = [
764 "rest",
765 "host",
766 "requested_host",
767 "operating_system",
768 "attempts_left",
769 "boot_progress",
770 "chassis",
771 "requested_chassisbmcrequested_bmc",
772 ]
Michael Walsh56749222017-09-29 15:26:07 -0500773
Patrick Williams20f38712022-12-08 06:18:26 -0600774 req_rest = [
775 sub_state
776 for sub_state in req_states
777 if sub_state in master_req_rest
778 ]
779 need_rest = len(req_rest) > 0
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600780 state = DotDict()
781 if need_rest:
Patrick Williams20f38712022-12-08 06:18:26 -0600782 cmd_buf = [
783 "Read Properties",
784 SYSTEM_STATE_URI + "enumerate",
785 "quiet=${" + str(quiet) + "}",
786 "timeout=30",
787 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600788 gp.dprint_issuing(cmd_buf)
Patrick Williams20f38712022-12-08 06:18:26 -0600789 status, ret_values = BuiltIn().run_keyword_and_ignore_error(
790 *cmd_buf
791 )
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600792 if status == "PASS":
Patrick Williams20f38712022-12-08 06:18:26 -0600793 state["rest"] = "1"
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600794 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600795 state["rest"] = "0"
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600796
Patrick Williams20f38712022-12-08 06:18:26 -0600797 if int(state["rest"]):
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600798 for url_path in ret_values:
George Keishing3adda952021-02-11 13:23:51 -0600799 # Skip conflicting "CurrentHostState" URL from the enum
800 # /xyz/openbmc_project/state/hypervisor0
801 if "hypervisor0" in url_path:
802 continue
803
Konstantin Aladyshev8c9cf9c2021-03-21 23:33:40 +0300804 if platform_arch_type == "x86":
805 # Skip conflicting "CurrentPowerState" URL from the enum
806 # /xyz/openbmc_project/state/chassis_system0
807 if "chassis_system0" in url_path:
808 continue
809
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600810 for attr_name in ret_values[url_path]:
811 # Create a state key value based on the attr_name.
812 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600813 ret_values[url_path][attr_name] = re.sub(
814 r".*\.", "", ret_values[url_path][attr_name]
815 )
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600816 except TypeError:
817 pass
818 # Do some key name manipulations.
Patrick Williams20f38712022-12-08 06:18:26 -0600819 new_attr_name = re.sub(
820 r"^Current|(State|Transition)$", "", attr_name
821 )
822 new_attr_name = re.sub(r"BMC", r"Bmc", new_attr_name)
823 new_attr_name = re.sub(
824 r"([A-Z][a-z])", r"_\1", new_attr_name
825 )
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600826 new_attr_name = new_attr_name.lower().lstrip("_")
Patrick Williams20f38712022-12-08 06:18:26 -0600827 new_attr_name = re.sub(
828 r"power", r"chassis", new_attr_name
829 )
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600830 if new_attr_name in req_states:
Patrick Williams20f38712022-12-08 06:18:26 -0600831 state[new_attr_name] = ret_values[url_path][
832 attr_name
833 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600834 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600835 master_req_rf = [
836 "redfish",
837 "host",
838 "requested_host",
839 "attempts_left",
840 "boot_progress",
841 "chassis",
842 "requested_chassisbmcrequested_bmc",
843 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600844
Patrick Williams20f38712022-12-08 06:18:26 -0600845 req_rf = [
846 sub_state for sub_state in req_states if sub_state in master_req_rf
847 ]
848 need_rf = len(req_rf) > 0
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600849 state = DotDict()
850 if need_rf:
851 cmd_buf = ["Redfish Get States"]
852 gp.dprint_issuing(cmd_buf)
George Keishing21e28b42021-05-27 08:10:29 -0500853 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600854 status, ret_values = BuiltIn().run_keyword_and_ignore_error(
855 *cmd_buf
856 )
George Keishing21e28b42021-05-27 08:10:29 -0500857 except Exception as ex:
858 # Robot raised UserKeywordExecutionFailed error exception.
859 gp.dprint_issuing("Retrying Redfish Get States")
Patrick Williams20f38712022-12-08 06:18:26 -0600860 status, ret_values = BuiltIn().run_keyword_and_ignore_error(
861 *cmd_buf
862 )
George Keishing21e28b42021-05-27 08:10:29 -0500863
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600864 gp.dprint_vars(status, ret_values)
865 if status == "PASS":
Patrick Williams20f38712022-12-08 06:18:26 -0600866 state["redfish"] = "1"
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600867 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600868 state["redfish"] = "0"
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600869
Patrick Williams20f38712022-12-08 06:18:26 -0600870 if int(state["redfish"]):
871 state["chassis"] = ret_values["chassis"]
872 state["host"] = ret_values["host"]
873 state["bmc"] = ret_values["bmc"]
George Keishingb51d1502021-03-25 03:30:33 -0500874 if platform_arch_type != "x86":
Patrick Williams20f38712022-12-08 06:18:26 -0600875 state["boot_progress"] = ret_values["boot_progress"]
Michael Walshb95eb542017-03-31 09:39:20 -0500876
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600877 for sub_state in req_states:
Michael Walsh56749222017-09-29 15:26:07 -0500878 if sub_state in state:
879 continue
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600880 if sub_state.startswith("os_"):
881 # We pass "os_" requests on to get_os_state.
882 continue
883 cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")"
884 exec(cmd_buf)
885
886 if os_host == "":
887 # The caller has not specified an os_host so as far as we're concerned,
888 # it doesn't exist.
889 return state
890
Patrick Williams20f38712022-12-08 06:18:26 -0600891 os_req_states = [
892 sub_state for sub_state in req_states if sub_state.startswith("os_")
893 ]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600894
895 if len(os_req_states) > 0:
896 # The caller has specified an os_host and they have requested
897 # information on os substates.
898
899 # Based on the information gathered on bmc, we'll try to make a
900 # determination of whether the os is even up. We'll pass the result
901 # of that assessment to get_os_state to enhance performance.
902 os_up_match = DotDict()
903 for sub_state in master_os_up_match:
904 if sub_state in req_states:
905 os_up_match[sub_state] = master_os_up_match[sub_state]
Michael Walsh70369fd2016-11-22 11:25:57 -0600906 os_up = compare_states(state, os_up_match)
Patrick Williams20f38712022-12-08 06:18:26 -0600907 os_state = get_os_state(
908 os_host=os_host,
909 os_username=os_username,
910 os_password=os_password,
911 req_states=os_req_states,
912 os_up=os_up,
913 quiet=quiet,
914 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600915 # Append os_state dictionary to ours.
916 state.update(os_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600917
918 return state
919
Michael Walsh70369fd2016-11-22 11:25:57 -0600920
Michael Walshfd5a8682019-02-01 14:28:42 -0600921exit_wait_early_message = ""
922
923
924def set_exit_wait_early_message(value):
925 r"""
926 Set global exit_wait_early_message to the indicated value.
927
928 This is a mechanism by which the programmer can do an early exit from
929 wait_until_keyword_succeeds() based on some special condition.
930
931 Description of argument(s):
932 value The value to assign to the global
933 exit_wait_early_message.
934 """
935
936 global exit_wait_early_message
937 exit_wait_early_message = value
938
939
Patrick Williams20f38712022-12-08 06:18:26 -0600940def check_state(
941 match_state,
942 invert=0,
943 print_string="",
944 openbmc_host="",
945 openbmc_username="",
946 openbmc_password="",
947 os_host="",
948 os_username="",
949 os_password="",
950 quiet=None,
951):
Michael Walsh70369fd2016-11-22 11:25:57 -0600952 r"""
953 Check that the Open BMC machine's composite state matches the specified
954 state. On success, this keyword returns the machine's composite state as a
955 dictionary.
956
Michael Walsh2a0df682019-09-27 17:19:27 -0500957 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600958 match_state A dictionary whose key/value pairs are "state field"/
959 "state value". The state value is interpreted as a
960 regular expression. Example call from robot:
Michael Walsh341c21e2017-01-17 16:25:20 -0600961 ${match_state}= Create Dictionary chassis=^On$
962 ... bmc=^Ready$
Michael Walsh01975fa2017-08-20 20:51:36 -0500963 ... boot_progress=^OSStart$
Michael Walsh70369fd2016-11-22 11:25:57 -0600964 ${state}= Check State &{match_state}
965 invert If this flag is set, this function will succeed if the
966 states do NOT match.
967 print_string This function will print this string to the console prior
968 to getting the state.
969 openbmc_host The DNS name or IP address of the BMC.
970 This defaults to global ${OPENBMC_HOST}.
971 openbmc_username The username to be used to login to the BMC.
972 This defaults to global ${OPENBMC_USERNAME}.
973 openbmc_password The password to be used to login to the BMC.
974 This defaults to global ${OPENBMC_PASSWORD}.
975 os_host The DNS name or IP address of the operating system.
976 This defaults to global ${OS_HOST}.
977 os_username The username to be used to login to the OS.
978 This defaults to global ${OS_USERNAME}.
979 os_password The password to be used to login to the OS.
980 This defaults to global ${OS_PASSWORD}.
981 quiet Indicates whether status details should be written to the
982 console. Defaults to either global value of ${QUIET} or
983 to 1.
984 """
985
Michael Walsh619aa332017-04-12 15:56:51 -0500986 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600987
Michael Walshedb5c942019-03-28 12:40:50 -0500988 gp.gp_print(print_string)
989
990 try:
991 match_state = return_state_constant(match_state)
992 except TypeError:
993 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600994
Michael Walsh2a0df682019-09-27 17:19:27 -0500995 req_states = list(match_state.keys())
996 # Remove special-case match key from req_states.
997 if expressions_key() in req_states:
998 req_states.remove(expressions_key())
Michael Walsh70369fd2016-11-22 11:25:57 -0600999 # Initialize state.
Patrick Williams20f38712022-12-08 06:18:26 -06001000 state = get_state(
1001 openbmc_host=openbmc_host,
1002 openbmc_username=openbmc_username,
1003 openbmc_password=openbmc_password,
1004 os_host=os_host,
1005 os_username=os_username,
1006 os_password=os_password,
1007 req_states=req_states,
1008 quiet=quiet,
1009 )
Michael Walsh70369fd2016-11-22 11:25:57 -06001010 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -05001011 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001012
Michael Walshfd5a8682019-02-01 14:28:42 -06001013 if exit_wait_early_message != "":
1014 # The exit_wait_early_message has been set by a signal handler so we
1015 # will exit "successfully". It is incumbent upon the calling function
1016 # (e.g. wait_state) to check/clear this variable and to fail
1017 # appropriately.
1018 return state
1019
Michael Walsh70369fd2016-11-22 11:25:57 -06001020 match = compare_states(state, match_state)
1021
1022 if invert and match:
Patrick Williams20f38712022-12-08 06:18:26 -06001023 fail_msg = (
1024 "The current state of the machine matches the match"
1025 + " state:\n"
1026 + gp.sprint_varx("state", state)
1027 )
Michael Walsh70369fd2016-11-22 11:25:57 -06001028 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
1029 elif not invert and not match:
Patrick Williams20f38712022-12-08 06:18:26 -06001030 fail_msg = (
1031 "The current state of the machine does NOT match the"
1032 + " match state:\n"
1033 + gp.sprint_varx("state", state)
1034 )
Michael Walsh70369fd2016-11-22 11:25:57 -06001035 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
1036
1037 return state
1038
Michael Walsh70369fd2016-11-22 11:25:57 -06001039
Patrick Williams20f38712022-12-08 06:18:26 -06001040def wait_state(
1041 match_state=(),
1042 wait_time="1 min",
1043 interval="1 second",
1044 invert=0,
1045 openbmc_host="",
1046 openbmc_username="",
1047 openbmc_password="",
1048 os_host="",
1049 os_username="",
1050 os_password="",
1051 quiet=None,
1052):
Michael Walsh70369fd2016-11-22 11:25:57 -06001053 r"""
1054 Wait for the Open BMC machine's composite state to match the specified
1055 state. On success, this keyword returns the machine's composite state as
1056 a dictionary.
1057
Michael Walsh2a0df682019-09-27 17:19:27 -05001058 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -06001059 match_state A dictionary whose key/value pairs are "state field"/
1060 "state value". See check_state (above) for details.
Michael Walsh619aa332017-04-12 15:56:51 -05001061 This value may also be any string accepted by
1062 return_state_constant (e.g. "standby_match_state").
1063 In such a case this function will call
1064 return_state_constant to convert it to a proper
1065 dictionary as described above.
Michael Walsh70369fd2016-11-22 11:25:57 -06001066 wait_time The total amount of time to wait for the desired state.
1067 This value may be expressed in Robot Framework's time
1068 format (e.g. 1 minute, 2 min 3 s, 4.5).
1069 interval The amount of time between state checks.
1070 This value may be expressed in Robot Framework's time
1071 format (e.g. 1 minute, 2 min 3 s, 4.5).
1072 invert If this flag is set, this function will for the state of
1073 the machine to cease to match the match state.
1074 openbmc_host The DNS name or IP address of the BMC.
1075 This defaults to global ${OPENBMC_HOST}.
1076 openbmc_username The username to be used to login to the BMC.
1077 This defaults to global ${OPENBMC_USERNAME}.
1078 openbmc_password The password to be used to login to the BMC.
1079 This defaults to global ${OPENBMC_PASSWORD}.
1080 os_host The DNS name or IP address of the operating system.
1081 This defaults to global ${OS_HOST}.
1082 os_username The username to be used to login to the OS.
1083 This defaults to global ${OS_USERNAME}.
1084 os_password The password to be used to login to the OS.
1085 This defaults to global ${OS_PASSWORD}.
1086 quiet Indicates whether status details should be written to the
1087 console. Defaults to either global value of ${QUIET} or
1088 to 1.
1089 """
1090
Michael Walsh619aa332017-04-12 15:56:51 -05001091 quiet = int(gp.get_var_value(quiet, 0))
1092
George Keishing36efbc02018-12-12 10:18:23 -06001093 try:
Michael Walsh619aa332017-04-12 15:56:51 -05001094 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -06001095 except TypeError:
1096 pass
Michael Walsh70369fd2016-11-22 11:25:57 -06001097
1098 if not quiet:
1099 if invert:
1100 alt_text = "cease to "
1101 else:
1102 alt_text = ""
Patrick Williams20f38712022-12-08 06:18:26 -06001103 gp.print_timen(
1104 "Checking every "
1105 + str(interval)
1106 + " for up to "
1107 + str(wait_time)
1108 + " for the state of the machine to "
1109 + alt_text
1110 + "match the state shown below."
1111 )
Michael Walsh3eb50022017-03-21 11:27:30 -05001112 gp.print_var(match_state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001113
Michael Walshf893ba02017-01-10 10:28:05 -06001114 if quiet:
Michael Walsh341c21e2017-01-17 16:25:20 -06001115 print_string = ""
Michael Walshf893ba02017-01-10 10:28:05 -06001116 else:
Michael Walsh341c21e2017-01-17 16:25:20 -06001117 print_string = "#"
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001118
1119 debug = int(BuiltIn().get_variable_value("${debug}", "0"))
1120 if debug:
1121 # In debug we print state so no need to print the "#".
1122 print_string = ""
1123 check_state_quiet = 1 - debug
Patrick Williams20f38712022-12-08 06:18:26 -06001124 cmd_buf = [
1125 "Check State",
1126 match_state,
1127 "invert=${" + str(invert) + "}",
1128 "print_string=" + print_string,
1129 "openbmc_host=" + openbmc_host,
1130 "openbmc_username=" + openbmc_username,
1131 "openbmc_password=" + openbmc_password,
1132 "os_host=" + os_host,
1133 "os_username=" + os_username,
1134 "os_password=" + os_password,
1135 "quiet=${" + str(check_state_quiet) + "}",
1136 ]
Michael Walshedb5c942019-03-28 12:40:50 -05001137 gp.dprint_issuing(cmd_buf)
Michael Walsh619aa332017-04-12 15:56:51 -05001138 try:
Patrick Williams20f38712022-12-08 06:18:26 -06001139 state = BuiltIn().wait_until_keyword_succeeds(
1140 wait_time, interval, *cmd_buf
1141 )
Michael Walsh619aa332017-04-12 15:56:51 -05001142 except AssertionError as my_assertion_error:
1143 gp.printn()
1144 message = my_assertion_error.args[0]
1145 BuiltIn().fail(message)
1146
Michael Walshfd5a8682019-02-01 14:28:42 -06001147 if exit_wait_early_message:
1148 # The global exit_wait_early_message was set by a signal handler
1149 # indicating that we should fail.
1150 message = exit_wait_early_message
1151 # Clear the exit_wait_early_message variable for future use.
1152 set_exit_wait_early_message("")
1153 BuiltIn().fail(gp.sprint_error(message))
1154
Michael Walsh70369fd2016-11-22 11:25:57 -06001155 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -05001156 gp.printn()
Michael Walsh70369fd2016-11-22 11:25:57 -06001157 if invert:
Michael Walsh3eb50022017-03-21 11:27:30 -05001158 gp.print_timen("The states no longer match:")
Michael Walsh70369fd2016-11-22 11:25:57 -06001159 else:
Michael Walsh3eb50022017-03-21 11:27:30 -05001160 gp.print_timen("The states match:")
1161 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001162
1163 return state
1164
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001165
Michael Walsh2a0df682019-09-27 17:19:27 -05001166def set_start_boot_seconds(value=0):
1167 global start_boot_seconds
1168 start_boot_seconds = int(value)
1169
1170
1171set_start_boot_seconds(0)
1172
1173
Patrick Williams20f38712022-12-08 06:18:26 -06001174def wait_for_comm_cycle(start_boot_seconds, quiet=None):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001175 r"""
Michael Walsh2a0df682019-09-27 17:19:27 -05001176 Wait for the BMC uptime to be less than elapsed_boot_time.
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001177
Michael Walsh2a0df682019-09-27 17:19:27 -05001178 This function will tolerate an expected loss of communication to the BMC.
1179 This function is useful when some kind of reboot has been initiated by the
1180 caller.
1181
1182 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001183 start_boot_seconds The time that the boot test started. The format is the
1184 epoch time in seconds, i.e. the number of seconds since
1185 1970-01-01 00:00:00 UTC. This value should be obtained
1186 from the BMC so that it is not dependent on any kind of
1187 synchronization between this machine and the target BMC
1188 This will allow this program to work correctly even in
1189 a simulated environment. This value should be obtained
1190 by the caller prior to initiating a reboot. It can be
1191 obtained as follows:
1192 state = st.get_state(req_states=['epoch_seconds'])
1193 """
1194
Michael Walsh619aa332017-04-12 15:56:51 -05001195 quiet = int(gp.get_var_value(quiet, 0))
1196
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001197 # Validate parms.
Michael Walsh2a0df682019-09-27 17:19:27 -05001198 error_message = gv.valid_integer(start_boot_seconds)
1199 if error_message:
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001200 BuiltIn().fail(gp.sprint_error(error_message))
1201
Michael Walsh2a0df682019-09-27 17:19:27 -05001202 # Wait for uptime to be less than elapsed_boot_time.
1203 set_start_boot_seconds(start_boot_seconds)
Patrick Williams20f38712022-12-08 06:18:26 -06001204 expr = "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
1205 match_state = DotDict(
1206 [
1207 ("uptime", "^[0-9\\.]+$"),
1208 ("elapsed_boot_time", "^[0-9]+$"),
1209 (expressions_key(), [expr]),
1210 ]
1211 )
David Shawe9192562020-09-28 11:00:45 -05001212 wait_state(match_state, wait_time="12 mins", interval="5 seconds")
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001213
Michael Sheposbdd1dce2020-12-10 11:51:58 -06001214 gp.qprint_timen("Verifying that REST/Redfish API interface is working.")
1215 if not redfish_support_trans_state:
Patrick Williams20f38712022-12-08 06:18:26 -06001216 match_state = DotDict([("rest", "^1$")])
Michael Sheposbdd1dce2020-12-10 11:51:58 -06001217 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001218 match_state = DotDict([("redfish", "^1$")])
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001219 state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")