blob: 00fa12490d77f0c6d262f6ed6e9291a2955f3f5c [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
Patrick Williams20f38712022-12-08 06:18:26 -060030import imp
31import 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 = (
48 os.path.dirname(os.path.dirname(imp.find_module("gen_robot_print")[1]))
49 + os.sep
50)
Michael Walsh56749222017-09-29 15:26:07 -050051sys.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
Patrick Williams20f38712022-12-08 06:18:26 -060079OBMC_STATES_VERSION = int(os.environ.get("OBMC_STATES_VERSION", 1))
Michael Walsh341c21e2017-01-17 16:25:20 -060080
Patrick Williams20f38712022-12-08 06:18:26 -060081redfish_support_trans_state = int(
82 os.environ.get("REDFISH_SUPPORT_TRANS_STATE", 0)
83) or int(
84 BuiltIn().get_variable_value("${REDFISH_SUPPORT_TRANS_STATE}", default=0)
85)
Michael Sheposda40c1d2020-12-01 22:30:00 -060086
Patrick Williams20f38712022-12-08 06:18:26 -060087platform_arch_type = os.environ.get(
88 "PLATFORM_ARCH_TYPE", ""
89) or BuiltIn().get_variable_value("${PLATFORM_ARCH_TYPE}", default="power")
George Keishing1e2fbee2021-03-19 11:19:29 -050090
Michael Walsh8fae6ea2017-02-20 16:14:44 -060091# valid_os_req_states and default_os_req_states are used by the os_get_state
92# function.
93# valid_os_req_states is a list of state information supported by the
94# get_os_state function.
Patrick Williams20f38712022-12-08 06:18:26 -060095valid_os_req_states = ["os_ping", "os_login", "os_run_cmd"]
Michael Sheposbdd1dce2020-12-10 11:51:58 -060096
Michael Walsh8fae6ea2017-02-20 16:14:44 -060097# When a user calls get_os_state w/o specifying req_states,
98# default_os_req_states is used as its value.
Patrick Williams20f38712022-12-08 06:18:26 -060099default_os_req_states = ["os_ping", "os_login", "os_run_cmd"]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600100
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.
Patrick Williams20f38712022-12-08 06:18:26 -0600104USE_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.
Patrick Williams20f38712022-12-08 06:18:26 -0600110 default_req_states = [
111 "rest",
112 "chassis",
113 "bmc",
114 "boot_progress",
115 "operating_system",
116 "host",
117 "os_ping",
118 "os_login",
119 "os_run_cmd",
120 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600121
122 # valid_req_states is a list of sub states supported by the get_state function.
123 # valid_req_states, default_req_states and master_os_up_match are used by the
124 # get_state function.
125
Patrick Williams20f38712022-12-08 06:18:26 -0600126 valid_req_states = [
127 "ping",
128 "packet_loss",
129 "uptime",
130 "epoch_seconds",
131 "elapsed_boot_time",
132 "rest",
133 "chassis",
134 "requested_chassis",
135 "bmc",
136 "requested_bmc",
137 "boot_progress",
138 "operating_system",
139 "host",
140 "requested_host",
141 "attempts_left",
142 "os_ping",
143 "os_login",
144 "os_run_cmd",
145 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600146
Michael Sheposda40c1d2020-12-01 22:30:00 -0600147 # default_state is an initial value which may be of use to callers.
Patrick Williams20f38712022-12-08 06:18:26 -0600148 default_state = DotDict(
149 [
150 ("rest", "1"),
151 ("chassis", "On"),
152 ("bmc", "Ready"),
153 ("boot_progress", "OSStart"),
154 ("operating_system", "BootComplete"),
155 ("host", "Running"),
156 ("os_ping", "1"),
157 ("os_login", "1"),
158 ("os_run_cmd", "1"),
159 ]
160 )
Michael Walsh619aa332017-04-12 15:56:51 -0500161
Michael Sheposda40c1d2020-12-01 22:30:00 -0600162 # A match state for checking that the system is at "standby".
Patrick Williams20f38712022-12-08 06:18:26 -0600163 standby_match_state = DotDict(
164 [
165 ("rest", "^1$"),
166 ("chassis", "^Off$"),
167 ("bmc", "^Ready$"),
168 ("boot_progress", "^Off|Unspecified$"),
169 ("operating_system", "^Inactive$"),
170 ("host", "^Off$"),
171 ]
172 )
Michael Walsh7dc885b2018-03-14 17:51:59 -0500173
Michael Sheposda40c1d2020-12-01 22:30:00 -0600174 # A match state for checking that the system is at "os running".
Patrick Williams20f38712022-12-08 06:18:26 -0600175 os_running_match_state = DotDict(
176 [
177 ("chassis", "^On$"),
178 ("bmc", "^Ready$"),
179 ("boot_progress", "FW Progress, Starting OS|OSStart"),
180 ("operating_system", "BootComplete"),
181 ("host", "^Running$"),
182 ("os_ping", "^1$"),
183 ("os_login", "^1$"),
184 ("os_run_cmd", "^1$"),
185 ]
186 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600187
188 # A master dictionary to determine whether the os may be up.
Patrick Williams20f38712022-12-08 06:18:26 -0600189 master_os_up_match = DotDict(
190 [
191 ("chassis", "^On$"),
192 ("bmc", "^Ready$"),
193 ("boot_progress", "FW Progress, Starting OS|OSStart"),
194 ("operating_system", "BootComplete"),
195 ("host", "^Running|Quiesced$"),
196 ]
197 )
Michael Walsh7dc885b2018-03-14 17:51:59 -0500198
Patrick Williams20f38712022-12-08 06:18:26 -0600199 invalid_state_match = DotDict(
200 [
201 ("rest", "^$"),
202 ("chassis", "^$"),
203 ("bmc", "^$"),
204 ("boot_progress", "^$"),
205 ("operating_system", "^$"),
206 ("host", "^$"),
207 ]
208 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600209else:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600210 # When a user calls get_state w/o specifying req_states, default_req_states
211 # is used as its value.
Patrick Williams20f38712022-12-08 06:18:26 -0600212 default_req_states = [
213 "redfish",
214 "chassis",
215 "bmc",
216 "boot_progress",
217 "host",
218 "os_ping",
219 "os_login",
220 "os_run_cmd",
221 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600222
223 # valid_req_states is a list of sub states supported by the get_state function.
224 # valid_req_states, default_req_states and master_os_up_match are used by the
225 # get_state function.
226
Patrick Williams20f38712022-12-08 06:18:26 -0600227 valid_req_states = [
228 "ping",
229 "packet_loss",
230 "uptime",
231 "epoch_seconds",
232 "elapsed_boot_time",
233 "redfish",
234 "chassis",
235 "requested_chassis",
236 "bmc",
237 "requested_bmc",
238 "boot_progress",
239 "host",
240 "requested_host",
241 "attempts_left",
242 "os_ping",
243 "os_login",
244 "os_run_cmd",
245 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600246
Michael Sheposda40c1d2020-12-01 22:30:00 -0600247 # default_state is an initial value which may be of use to callers.
Patrick Williams20f38712022-12-08 06:18:26 -0600248 default_state = DotDict(
249 [
250 ("redfish", "1"),
251 ("chassis", "On"),
252 ("bmc", "Enabled"),
253 (
254 "boot_progress",
255 "SystemHardwareInitializationComplete|OSBootStarted|OSRunning",
256 ),
257 ("host", "Enabled"),
258 ("os_ping", "1"),
259 ("os_login", "1"),
260 ("os_run_cmd", "1"),
261 ]
262 )
Michael Walsh619aa332017-04-12 15:56:51 -0500263
Michael Sheposda40c1d2020-12-01 22:30:00 -0600264 # A match state for checking that the system is at "standby".
Patrick Williams20f38712022-12-08 06:18:26 -0600265 standby_match_state = DotDict(
266 [
267 ("redfish", "^1$"),
268 ("chassis", "^Off$"),
269 ("bmc", "^Enabled$"),
270 ("boot_progress", "^None$"),
271 ("host", "^Disabled$"),
272 ]
273 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600274
275 # A match state for checking that the system is at "os running".
Patrick Williams20f38712022-12-08 06:18:26 -0600276 os_running_match_state = DotDict(
277 [
278 ("chassis", "^On$"),
279 ("bmc", "^Enabled$"),
280 (
281 "boot_progress",
282 "SystemHardwareInitializationComplete|OSBootStarted|OSRunning",
283 ),
284 ("host", "^Enabled$"),
285 ("os_ping", "^1$"),
286 ("os_login", "^1$"),
287 ("os_run_cmd", "^1$"),
288 ]
289 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600290
291 # A master dictionary to determine whether the os may be up.
Patrick Williams20f38712022-12-08 06:18:26 -0600292 master_os_up_match = DotDict(
293 [
294 ("chassis", "^On$"),
295 ("bmc", "^Enabled$"),
296 (
297 "boot_progress",
298 "SystemHardwareInitializationComplete|OSBootStarted|OSRunning",
299 ),
300 ("host", "^Enabled$"),
301 ]
302 )
Michael Sheposda40c1d2020-12-01 22:30:00 -0600303
Patrick Williams20f38712022-12-08 06:18:26 -0600304 invalid_state_match = DotDict(
305 [
306 ("redfish", "^$"),
307 ("chassis", "^$"),
308 ("bmc", "^$"),
309 ("boot_progress", "^$"),
310 ("host", "^$"),
311 ]
312 )
Michael Walsh45ca6e42017-09-14 17:29:12 -0500313
George Keishing1e2fbee2021-03-19 11:19:29 -0500314# Filter the states based on platform type.
315if platform_arch_type == "x86":
George Keishingb51d1502021-03-25 03:30:33 -0500316 if not redfish_support_trans_state:
317 default_req_states.remove("operating_system")
318 valid_req_states.remove("operating_system")
319 del default_state["operating_system"]
320 del standby_match_state["operating_system"]
321 del os_running_match_state["operating_system"]
322 del master_os_up_match["operating_system"]
323 del invalid_state_match["operating_system"]
324
George Keishing1e2fbee2021-03-19 11:19:29 -0500325 default_req_states.remove("boot_progress")
George Keishing1e2fbee2021-03-19 11:19:29 -0500326 valid_req_states.remove("boot_progress")
George Keishing1e2fbee2021-03-19 11:19:29 -0500327 del default_state["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500328 del standby_match_state["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500329 del os_running_match_state["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500330 del master_os_up_match["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500331 del invalid_state_match["boot_progress"]
332
Michael Walsh341c21e2017-01-17 16:25:20 -0600333
Patrick Williams20f38712022-12-08 06:18:26 -0600334def return_state_constant(state_name="default_state"):
Michael Walsh619aa332017-04-12 15:56:51 -0500335 r"""
Michael Walsh7dc885b2018-03-14 17:51:59 -0500336 Return the named state dictionary constant.
Michael Walsh619aa332017-04-12 15:56:51 -0500337 """
338
George Keishing36efbc02018-12-12 10:18:23 -0600339 return eval(state_name)
Michael Walsh619aa332017-04-12 15:56:51 -0500340
Michael Walsh619aa332017-04-12 15:56:51 -0500341
Michael Walsh70369fd2016-11-22 11:25:57 -0600342def anchor_state(state):
Michael Walsh70369fd2016-11-22 11:25:57 -0600343 r"""
344 Add regular expression anchors ("^" and "$") to the beginning and end of
345 each item in the state dictionary passed in. Return the resulting
346 dictionary.
347
Michael Walsh2a0df682019-09-27 17:19:27 -0500348 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600349 state A dictionary such as the one returned by the get_state()
350 function.
351 """
352
Michael Walsh2ce067a2017-02-27 14:24:07 -0600353 anchored_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500354 for key in anchored_state.keys():
Michael Walsh70369fd2016-11-22 11:25:57 -0600355 anchored_state[key] = "^" + str(anchored_state[key]) + "$"
356
357 return anchored_state
358
Michael Walsh70369fd2016-11-22 11:25:57 -0600359
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600360def strip_anchor_state(state):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600361 r"""
362 Strip regular expression anchors ("^" and "$") from the beginning and end
363 of each item in the state dictionary passed in. Return the resulting
364 dictionary.
365
Michael Walsh2a0df682019-09-27 17:19:27 -0500366 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600367 state A dictionary such as the one returned by the get_state()
368 function.
369 """
370
Michael Walsh2ce067a2017-02-27 14:24:07 -0600371 stripped_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500372 for key in stripped_state.keys():
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600373 stripped_state[key] = stripped_state[key].strip("^$")
374
375 return stripped_state
376
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600377
Michael Walsh2a0df682019-09-27 17:19:27 -0500378def expressions_key():
379 r"""
380 Return expressions key constant.
381 """
Patrick Williams20f38712022-12-08 06:18:26 -0600382 return "<expressions>"
Michael Walsh2a0df682019-09-27 17:19:27 -0500383
384
Patrick Williams20f38712022-12-08 06:18:26 -0600385def compare_states(state, match_state, match_type="and"):
Michael Walsh70369fd2016-11-22 11:25:57 -0600386 r"""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600387 Compare 2 state dictionaries. Return True if they match and False if they
Michael Walsh70369fd2016-11-22 11:25:57 -0600388 don't. Note that the match_state dictionary does not need to have an entry
389 corresponding to each entry in the state dictionary. But for each entry
390 that it does have, the corresponding state entry will be checked for a
391 match.
392
Michael Walsh2a0df682019-09-27 17:19:27 -0500393 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600394 state A state dictionary such as the one returned by the
395 get_state function.
396 match_state A dictionary whose key/value pairs are "state field"/
397 "state value". The state value is interpreted as a
398 regular expression. Every value in this dictionary is
Michael Walsh45ca6e42017-09-14 17:29:12 -0500399 considered. When match_type is 'and', if each and every
400 comparison matches, the two dictionaries are considered to
401 be matching. If match_type is 'or', if any two of the
402 elements compared match, the two dictionaries are
403 considered to be matching.
Michael Walsh2a0df682019-09-27 17:19:27 -0500404
Michael Walsh7dc885b2018-03-14 17:51:59 -0500405 This value may also be any string accepted by
Michael Walsh2a0df682019-09-27 17:19:27 -0500406 return_state_constant (e.g. "standby_match_state"). In
407 such a case this function will call return_state_constant
408 to convert it to a proper dictionary as described above.
409
410 Finally, one special value is accepted for the key field:
411 expression_key(). If such an entry exists, its value is
412 taken to be a list of expressions to be evaluated. These
413 expressions may reference state dictionary entries by
414 simply coding them in standard python syntax (e.g.
415 state['key1']). What follows is an example expression:
416
417 "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
418
419 In this example, if the state dictionary's 'uptime' entry
420 is less than its 'elapsed_boot_time' entry, it would
421 qualify as a match.
Michael Walsh45ca6e42017-09-14 17:29:12 -0500422 match_type This may be 'and' or 'or'.
Michael Walsh70369fd2016-11-22 11:25:57 -0600423 """
424
Patrick Williams20f38712022-12-08 06:18:26 -0600425 error_message = gv.valid_value(match_type, valid_values=["and", "or"])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500426 if error_message != "":
427 BuiltIn().fail(gp.sprint_error(error_message))
428
George Keishing36efbc02018-12-12 10:18:23 -0600429 try:
Michael Walsh7dc885b2018-03-14 17:51:59 -0500430 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600431 except TypeError:
432 pass
Michael Walsh7dc885b2018-03-14 17:51:59 -0500433
Patrick Williams20f38712022-12-08 06:18:26 -0600434 default_match = match_type == "and"
Michael Walsh70369fd2016-11-22 11:25:57 -0600435 for key, match_state_value in match_state.items():
Michael Walsh97df71c2017-03-27 14:33:24 -0500436 # Blank match_state_value means "don't care".
437 if match_state_value == "":
438 continue
Michael Walsh2a0df682019-09-27 17:19:27 -0500439 if key == expressions_key():
440 for expr in match_state_value:
441 # Use python interpreter to evaluate the expression.
442 match = eval(expr)
443 if match != default_match:
444 return match
445 else:
446 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600447 match = (
448 re.match(match_state_value, str(state[key])) is not None
449 )
Michael Walsh2a0df682019-09-27 17:19:27 -0500450 except KeyError:
451 match = False
452 if match != default_match:
453 return match
Michael Walsh45ca6e42017-09-14 17:29:12 -0500454
455 return default_match
Michael Walsh70369fd2016-11-22 11:25:57 -0600456
Michael Walsh70369fd2016-11-22 11:25:57 -0600457
Patrick Williams20f38712022-12-08 06:18:26 -0600458def get_os_state(
459 os_host="",
460 os_username="",
461 os_password="",
462 req_states=default_os_req_states,
463 os_up=True,
464 quiet=None,
465):
Michael Walsh70369fd2016-11-22 11:25:57 -0600466 r"""
467 Get component states for the operating system such as ping, login,
468 etc, put them into a dictionary and return them to the caller.
469
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600470 Note that all substate values are strings.
471
Michael Walsh2a0df682019-09-27 17:19:27 -0500472 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600473 os_host The DNS name or IP address of the operating system.
474 This defaults to global ${OS_HOST}.
475 os_username The username to be used to login to the OS.
476 This defaults to global ${OS_USERNAME}.
477 os_password The password to be used to login to the OS.
478 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600479 req_states This is a list of states whose values are being requested by
480 the caller.
481 os_up If the caller knows that the os can't possibly be up, it can
482 improve performance by passing os_up=False. This function
483 will then simply return default values for all requested os
484 sub states.
Michael Walsh70369fd2016-11-22 11:25:57 -0600485 quiet Indicates whether status details (e.g. curl commands) should
486 be written to the console.
487 Defaults to either global value of ${QUIET} or to 1.
488 """
489
Michael Walsh619aa332017-04-12 15:56:51 -0500490 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600491
492 # Set parm defaults where necessary and validate all parms.
493 if os_host == "":
494 os_host = BuiltIn().get_variable_value("${OS_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500495 error_message = gv.valid_value(os_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600496 if error_message != "":
497 BuiltIn().fail(gp.sprint_error(error_message))
498
499 if os_username == "":
500 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500501 error_message = gv.valid_value(os_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600502 if error_message != "":
503 BuiltIn().fail(gp.sprint_error(error_message))
504
505 if os_password == "":
506 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500507 error_message = gv.valid_value(os_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600508 if error_message != "":
509 BuiltIn().fail(gp.sprint_error(error_message))
510
Patrick Williams20f38712022-12-08 06:18:26 -0600511 invalid_req_states = [
512 sub_state
513 for sub_state in req_states
514 if sub_state not in valid_os_req_states
515 ]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600516 if len(invalid_req_states) > 0:
Patrick Williams20f38712022-12-08 06:18:26 -0600517 error_message = (
518 "The following req_states are not supported:\n"
519 + gp.sprint_var(invalid_req_states)
520 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600521 BuiltIn().fail(gp.sprint_error(error_message))
Michael Walsh70369fd2016-11-22 11:25:57 -0600522
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600523 # Initialize all substate values supported by this function.
524 os_ping = 0
525 os_login = 0
526 os_run_cmd = 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600527
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600528 if os_up:
Patrick Williams20f38712022-12-08 06:18:26 -0600529 if "os_ping" in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600530 # See if the OS pings.
Patrick Williams20f38712022-12-08 06:18:26 -0600531 rc, out_buf = gc.shell_cmd(
532 "ping -c 1 -w 2 " + os_host,
533 print_output=0,
534 show_err=0,
535 ignore_err=1,
536 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600537 if rc == 0:
538 os_ping = 1
Michael Walsh70369fd2016-11-22 11:25:57 -0600539
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600540 # Programming note: All attributes which do not require an ssh login
541 # should have been processed by this point.
Patrick Williams20f38712022-12-08 06:18:26 -0600542 master_req_login = ["os_login", "os_run_cmd"]
543 req_login = [
544 sub_state
545 for sub_state in req_states
546 if sub_state in master_req_login
547 ]
548 must_login = len(req_login) > 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600549
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600550 if must_login:
Patrick Williams20f38712022-12-08 06:18:26 -0600551 output, stderr, rc = bsu.os_execute_command(
552 "uptime",
553 quiet=quiet,
554 ignore_err=1,
555 time_out=20,
556 os_host=os_host,
557 os_username=os_username,
558 os_password=os_password,
559 )
Michael Walsh6a9bd142018-07-24 16:10:29 -0500560 if rc == 0:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600561 os_login = 1
Michael Walsh6a9bd142018-07-24 16:10:29 -0500562 os_run_cmd = 1
Michael Walsh3eb50022017-03-21 11:27:30 -0500563 else:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500564 gp.dprint_vars(output, stderr)
565 gp.dprint_vars(rc, 1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600566
567 os_state = DotDict()
568 for sub_state in req_states:
569 cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")"
570 exec(cmd_buf)
Michael Walsh70369fd2016-11-22 11:25:57 -0600571
572 return os_state
573
Michael Walsh70369fd2016-11-22 11:25:57 -0600574
Patrick Williams20f38712022-12-08 06:18:26 -0600575def get_state(
576 openbmc_host="",
577 openbmc_username="",
578 openbmc_password="",
579 os_host="",
580 os_username="",
581 os_password="",
582 req_states=default_req_states,
583 quiet=None,
584):
Michael Walsh70369fd2016-11-22 11:25:57 -0600585 r"""
Michael Walsh619aa332017-04-12 15:56:51 -0500586 Get component states such as chassis state, bmc state, etc, put them into a
Michael Walsh70369fd2016-11-22 11:25:57 -0600587 dictionary and return them to the caller.
588
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600589 Note that all substate values are strings.
590
Michael Walsh2a0df682019-09-27 17:19:27 -0500591 Note: If elapsed_boot_time is included in req_states, it is the caller's
592 duty to call set_start_boot_seconds() in order to set global
593 start_boot_seconds. elapsed_boot_time is the current time minus
594 start_boot_seconds.
595
596 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600597 openbmc_host The DNS name or IP address of the BMC.
598 This defaults to global ${OPENBMC_HOST}.
599 openbmc_username The username to be used to login to the BMC.
600 This defaults to global ${OPENBMC_USERNAME}.
601 openbmc_password The password to be used to login to the BMC.
602 This defaults to global ${OPENBMC_PASSWORD}.
603 os_host The DNS name or IP address of the operating system.
604 This defaults to global ${OS_HOST}.
605 os_username The username to be used to login to the OS.
606 This defaults to global ${OS_USERNAME}.
607 os_password The password to be used to login to the OS.
608 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600609 req_states This is a list of states whose values are being requested
610 by the caller.
Michael Walsh70369fd2016-11-22 11:25:57 -0600611 quiet Indicates whether status details (e.g. curl commands)
612 should be written to the console.
613 Defaults to either global value of ${QUIET} or to 1.
614 """
615
Michael Walsh619aa332017-04-12 15:56:51 -0500616 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600617
618 # Set parm defaults where necessary and validate all parms.
619 if openbmc_host == "":
620 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500621 error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600622 if error_message != "":
623 BuiltIn().fail(gp.sprint_error(error_message))
624
625 if openbmc_username == "":
626 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500627 error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600628 if error_message != "":
629 BuiltIn().fail(gp.sprint_error(error_message))
630
631 if openbmc_password == "":
632 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500633 error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600634 if error_message != "":
635 BuiltIn().fail(gp.sprint_error(error_message))
636
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600637 # NOTE: OS parms are optional.
Michael Walsh70369fd2016-11-22 11:25:57 -0600638 if os_host == "":
639 os_host = BuiltIn().get_variable_value("${OS_HOST}")
640 if os_host is None:
641 os_host = ""
642
George Keishinge7e91712021-09-03 11:28:44 -0500643 if os_username == "":
Michael Walsh70369fd2016-11-22 11:25:57 -0600644 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
645 if os_username is None:
646 os_username = ""
647
George Keishinge7e91712021-09-03 11:28:44 -0500648 if os_password == "":
Michael Walsh70369fd2016-11-22 11:25:57 -0600649 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
650 if os_password is None:
651 os_password = ""
652
Patrick Williams20f38712022-12-08 06:18:26 -0600653 invalid_req_states = [
654 sub_state
655 for sub_state in req_states
656 if sub_state not in valid_req_states
657 ]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600658 if len(invalid_req_states) > 0:
Patrick Williams20f38712022-12-08 06:18:26 -0600659 error_message = (
660 "The following req_states are not supported:\n"
661 + gp.sprint_var(invalid_req_states)
662 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600663 BuiltIn().fail(gp.sprint_error(error_message))
664
665 # Initialize all substate values supported by this function.
666 ping = 0
Patrick Williams20f38712022-12-08 06:18:26 -0600667 packet_loss = ""
668 uptime = ""
669 epoch_seconds = ""
670 elapsed_boot_time = ""
671 rest = ""
672 redfish = ""
673 chassis = ""
674 requested_chassis = ""
675 bmc = ""
676 requested_bmc = ""
George Keishing18f15dd2021-03-09 11:17:35 -0600677 # BootProgress state will get populated when state logic enumerates the
678 # state URI. This is to prevent state dictionary boot_progress value
679 # getting empty when the BootProgress is NOT found, making it optional.
Patrick Williams20f38712022-12-08 06:18:26 -0600680 boot_progress = "NA"
681 operating_system = ""
682 host = ""
683 requested_host = ""
684 attempts_left = ""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600685
Michael Walsh70369fd2016-11-22 11:25:57 -0600686 # Get the component states.
Patrick Williams20f38712022-12-08 06:18:26 -0600687 if "ping" in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600688 # See if the OS pings.
Patrick Williams20f38712022-12-08 06:18:26 -0600689 rc, out_buf = gc.shell_cmd(
690 "ping -c 1 -w 2 " + openbmc_host,
691 print_output=0,
692 show_err=0,
693 ignore_err=1,
694 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600695 if rc == 0:
696 ping = 1
697
Patrick Williams20f38712022-12-08 06:18:26 -0600698 if "packet_loss" in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600699 # See if the OS pings.
Patrick Williams20f38712022-12-08 06:18:26 -0600700 cmd_buf = (
701 "ping -c 5 -w 5 "
702 + openbmc_host
703 + " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'"
704 )
705 rc, out_buf = gc.shell_cmd(
706 cmd_buf, print_output=0, show_err=0, ignore_err=1
707 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600708 if rc == 0:
709 packet_loss = out_buf.rstrip("\n")
710
Patrick Williams20f38712022-12-08 06:18:26 -0600711 if "uptime" in req_states:
Michael Walshfa765932017-10-13 14:07:22 -0500712 # Sometimes reading uptime results in a blank value. Call with
713 # wait_until_keyword_succeeds to ensure a non-blank value is obtained.
Patrick Williams20f38712022-12-08 06:18:26 -0600714 remote_cmd_buf = (
715 "bash -c 'read uptime filler 2>/dev/null < /proc/uptime"
716 + ' && [ ! -z "${uptime}" ] && echo ${uptime}\''
717 )
718 cmd_buf = [
719 "BMC Execute Command",
720 re.sub("\\$", "\\$", remote_cmd_buf),
721 "quiet=1",
722 "test_mode=0",
723 "time_out=5",
724 ]
Michael Walsh2a0df682019-09-27 17:19:27 -0500725 gp.qprint_issuing(cmd_buf, 0)
726 gp.qprint_issuing(remote_cmd_buf, 0)
Michael Walshfa765932017-10-13 14:07:22 -0500727 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600728 stdout, stderr, rc = BuiltIn().wait_until_keyword_succeeds(
729 "10 sec", "5 sec", *cmd_buf
730 )
Michael Walsh97df71c2017-03-27 14:33:24 -0500731 if rc == 0 and stderr == "":
732 uptime = stdout
Michael Walshfa765932017-10-13 14:07:22 -0500733 except AssertionError as my_assertion_error:
734 pass
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600735
Patrick Williams20f38712022-12-08 06:18:26 -0600736 if "epoch_seconds" in req_states or "elapsed_boot_time" in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600737 date_cmd_buf = "date -u +%s"
738 if USE_BMC_EPOCH_TIME:
Patrick Williams20f38712022-12-08 06:18:26 -0600739 cmd_buf = ["BMC Execute Command", date_cmd_buf, "quiet=${1}"]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600740 if not quiet:
Michael Walshedb5c942019-03-28 12:40:50 -0500741 gp.print_issuing(cmd_buf)
Patrick Williams20f38712022-12-08 06:18:26 -0600742 status, ret_values = BuiltIn().run_keyword_and_ignore_error(
743 *cmd_buf
744 )
Michael Walsh97df71c2017-03-27 14:33:24 -0500745 if status == "PASS":
746 stdout, stderr, rc = ret_values
747 if rc == 0 and stderr == "":
748 epoch_seconds = stdout.rstrip("\n")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600749 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600750 shell_rc, out_buf = gc.cmd_fnc_u(
751 date_cmd_buf, quiet=quiet, print_output=0
752 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600753 if shell_rc == 0:
754 epoch_seconds = out_buf.rstrip("\n")
755
Patrick Williams20f38712022-12-08 06:18:26 -0600756 if "elapsed_boot_time" in req_states:
Michael Walsh2a0df682019-09-27 17:19:27 -0500757 global start_boot_seconds
758 elapsed_boot_time = int(epoch_seconds) - start_boot_seconds
759
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600760 if not redfish_support_trans_state:
Patrick Williams20f38712022-12-08 06:18:26 -0600761 master_req_rest = [
762 "rest",
763 "host",
764 "requested_host",
765 "operating_system",
766 "attempts_left",
767 "boot_progress",
768 "chassis",
769 "requested_chassisbmcrequested_bmc",
770 ]
Michael Walsh56749222017-09-29 15:26:07 -0500771
Patrick Williams20f38712022-12-08 06:18:26 -0600772 req_rest = [
773 sub_state
774 for sub_state in req_states
775 if sub_state in master_req_rest
776 ]
777 need_rest = len(req_rest) > 0
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600778 state = DotDict()
779 if need_rest:
Patrick Williams20f38712022-12-08 06:18:26 -0600780 cmd_buf = [
781 "Read Properties",
782 SYSTEM_STATE_URI + "enumerate",
783 "quiet=${" + str(quiet) + "}",
784 "timeout=30",
785 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600786 gp.dprint_issuing(cmd_buf)
Patrick Williams20f38712022-12-08 06:18:26 -0600787 status, ret_values = BuiltIn().run_keyword_and_ignore_error(
788 *cmd_buf
789 )
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600790 if status == "PASS":
Patrick Williams20f38712022-12-08 06:18:26 -0600791 state["rest"] = "1"
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600792 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600793 state["rest"] = "0"
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600794
Patrick Williams20f38712022-12-08 06:18:26 -0600795 if int(state["rest"]):
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600796 for url_path in ret_values:
George Keishing3adda952021-02-11 13:23:51 -0600797 # Skip conflicting "CurrentHostState" URL from the enum
798 # /xyz/openbmc_project/state/hypervisor0
799 if "hypervisor0" in url_path:
800 continue
801
Konstantin Aladyshev8c9cf9c2021-03-21 23:33:40 +0300802 if platform_arch_type == "x86":
803 # Skip conflicting "CurrentPowerState" URL from the enum
804 # /xyz/openbmc_project/state/chassis_system0
805 if "chassis_system0" in url_path:
806 continue
807
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600808 for attr_name in ret_values[url_path]:
809 # Create a state key value based on the attr_name.
810 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600811 ret_values[url_path][attr_name] = re.sub(
812 r".*\.", "", ret_values[url_path][attr_name]
813 )
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600814 except TypeError:
815 pass
816 # Do some key name manipulations.
Patrick Williams20f38712022-12-08 06:18:26 -0600817 new_attr_name = re.sub(
818 r"^Current|(State|Transition)$", "", attr_name
819 )
820 new_attr_name = re.sub(r"BMC", r"Bmc", new_attr_name)
821 new_attr_name = re.sub(
822 r"([A-Z][a-z])", r"_\1", new_attr_name
823 )
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600824 new_attr_name = new_attr_name.lower().lstrip("_")
Patrick Williams20f38712022-12-08 06:18:26 -0600825 new_attr_name = re.sub(
826 r"power", r"chassis", new_attr_name
827 )
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600828 if new_attr_name in req_states:
Patrick Williams20f38712022-12-08 06:18:26 -0600829 state[new_attr_name] = ret_values[url_path][
830 attr_name
831 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600832 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600833 master_req_rf = [
834 "redfish",
835 "host",
836 "requested_host",
837 "attempts_left",
838 "boot_progress",
839 "chassis",
840 "requested_chassisbmcrequested_bmc",
841 ]
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600842
Patrick Williams20f38712022-12-08 06:18:26 -0600843 req_rf = [
844 sub_state for sub_state in req_states if sub_state in master_req_rf
845 ]
846 need_rf = len(req_rf) > 0
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600847 state = DotDict()
848 if need_rf:
849 cmd_buf = ["Redfish Get States"]
850 gp.dprint_issuing(cmd_buf)
George Keishing21e28b42021-05-27 08:10:29 -0500851 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600852 status, ret_values = BuiltIn().run_keyword_and_ignore_error(
853 *cmd_buf
854 )
George Keishing21e28b42021-05-27 08:10:29 -0500855 except Exception as ex:
856 # Robot raised UserKeywordExecutionFailed error exception.
857 gp.dprint_issuing("Retrying Redfish Get States")
Patrick Williams20f38712022-12-08 06:18:26 -0600858 status, ret_values = BuiltIn().run_keyword_and_ignore_error(
859 *cmd_buf
860 )
George Keishing21e28b42021-05-27 08:10:29 -0500861
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600862 gp.dprint_vars(status, ret_values)
863 if status == "PASS":
Patrick Williams20f38712022-12-08 06:18:26 -0600864 state["redfish"] = "1"
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600865 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600866 state["redfish"] = "0"
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600867
Patrick Williams20f38712022-12-08 06:18:26 -0600868 if int(state["redfish"]):
869 state["chassis"] = ret_values["chassis"]
870 state["host"] = ret_values["host"]
871 state["bmc"] = ret_values["bmc"]
George Keishingb51d1502021-03-25 03:30:33 -0500872 if platform_arch_type != "x86":
Patrick Williams20f38712022-12-08 06:18:26 -0600873 state["boot_progress"] = ret_values["boot_progress"]
Michael Walshb95eb542017-03-31 09:39:20 -0500874
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600875 for sub_state in req_states:
Michael Walsh56749222017-09-29 15:26:07 -0500876 if sub_state in state:
877 continue
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600878 if sub_state.startswith("os_"):
879 # We pass "os_" requests on to get_os_state.
880 continue
881 cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")"
882 exec(cmd_buf)
883
884 if os_host == "":
885 # The caller has not specified an os_host so as far as we're concerned,
886 # it doesn't exist.
887 return state
888
Patrick Williams20f38712022-12-08 06:18:26 -0600889 os_req_states = [
890 sub_state for sub_state in req_states if sub_state.startswith("os_")
891 ]
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600892
893 if len(os_req_states) > 0:
894 # The caller has specified an os_host and they have requested
895 # information on os substates.
896
897 # Based on the information gathered on bmc, we'll try to make a
898 # determination of whether the os is even up. We'll pass the result
899 # of that assessment to get_os_state to enhance performance.
900 os_up_match = DotDict()
901 for sub_state in master_os_up_match:
902 if sub_state in req_states:
903 os_up_match[sub_state] = master_os_up_match[sub_state]
Michael Walsh70369fd2016-11-22 11:25:57 -0600904 os_up = compare_states(state, os_up_match)
Patrick Williams20f38712022-12-08 06:18:26 -0600905 os_state = get_os_state(
906 os_host=os_host,
907 os_username=os_username,
908 os_password=os_password,
909 req_states=os_req_states,
910 os_up=os_up,
911 quiet=quiet,
912 )
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600913 # Append os_state dictionary to ours.
914 state.update(os_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600915
916 return state
917
Michael Walsh70369fd2016-11-22 11:25:57 -0600918
Michael Walshfd5a8682019-02-01 14:28:42 -0600919exit_wait_early_message = ""
920
921
922def set_exit_wait_early_message(value):
923 r"""
924 Set global exit_wait_early_message to the indicated value.
925
926 This is a mechanism by which the programmer can do an early exit from
927 wait_until_keyword_succeeds() based on some special condition.
928
929 Description of argument(s):
930 value The value to assign to the global
931 exit_wait_early_message.
932 """
933
934 global exit_wait_early_message
935 exit_wait_early_message = value
936
937
Patrick Williams20f38712022-12-08 06:18:26 -0600938def check_state(
939 match_state,
940 invert=0,
941 print_string="",
942 openbmc_host="",
943 openbmc_username="",
944 openbmc_password="",
945 os_host="",
946 os_username="",
947 os_password="",
948 quiet=None,
949):
Michael Walsh70369fd2016-11-22 11:25:57 -0600950 r"""
951 Check that the Open BMC machine's composite state matches the specified
952 state. On success, this keyword returns the machine's composite state as a
953 dictionary.
954
Michael Walsh2a0df682019-09-27 17:19:27 -0500955 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600956 match_state A dictionary whose key/value pairs are "state field"/
957 "state value". The state value is interpreted as a
958 regular expression. Example call from robot:
Michael Walsh341c21e2017-01-17 16:25:20 -0600959 ${match_state}= Create Dictionary chassis=^On$
960 ... bmc=^Ready$
Michael Walsh01975fa2017-08-20 20:51:36 -0500961 ... boot_progress=^OSStart$
Michael Walsh70369fd2016-11-22 11:25:57 -0600962 ${state}= Check State &{match_state}
963 invert If this flag is set, this function will succeed if the
964 states do NOT match.
965 print_string This function will print this string to the console prior
966 to getting the state.
967 openbmc_host The DNS name or IP address of the BMC.
968 This defaults to global ${OPENBMC_HOST}.
969 openbmc_username The username to be used to login to the BMC.
970 This defaults to global ${OPENBMC_USERNAME}.
971 openbmc_password The password to be used to login to the BMC.
972 This defaults to global ${OPENBMC_PASSWORD}.
973 os_host The DNS name or IP address of the operating system.
974 This defaults to global ${OS_HOST}.
975 os_username The username to be used to login to the OS.
976 This defaults to global ${OS_USERNAME}.
977 os_password The password to be used to login to the OS.
978 This defaults to global ${OS_PASSWORD}.
979 quiet Indicates whether status details should be written to the
980 console. Defaults to either global value of ${QUIET} or
981 to 1.
982 """
983
Michael Walsh619aa332017-04-12 15:56:51 -0500984 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600985
Michael Walshedb5c942019-03-28 12:40:50 -0500986 gp.gp_print(print_string)
987
988 try:
989 match_state = return_state_constant(match_state)
990 except TypeError:
991 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600992
Michael Walsh2a0df682019-09-27 17:19:27 -0500993 req_states = list(match_state.keys())
994 # Remove special-case match key from req_states.
995 if expressions_key() in req_states:
996 req_states.remove(expressions_key())
Michael Walsh70369fd2016-11-22 11:25:57 -0600997 # Initialize state.
Patrick Williams20f38712022-12-08 06:18:26 -0600998 state = get_state(
999 openbmc_host=openbmc_host,
1000 openbmc_username=openbmc_username,
1001 openbmc_password=openbmc_password,
1002 os_host=os_host,
1003 os_username=os_username,
1004 os_password=os_password,
1005 req_states=req_states,
1006 quiet=quiet,
1007 )
Michael Walsh70369fd2016-11-22 11:25:57 -06001008 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -05001009 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001010
Michael Walshfd5a8682019-02-01 14:28:42 -06001011 if exit_wait_early_message != "":
1012 # The exit_wait_early_message has been set by a signal handler so we
1013 # will exit "successfully". It is incumbent upon the calling function
1014 # (e.g. wait_state) to check/clear this variable and to fail
1015 # appropriately.
1016 return state
1017
Michael Walsh70369fd2016-11-22 11:25:57 -06001018 match = compare_states(state, match_state)
1019
1020 if invert and match:
Patrick Williams20f38712022-12-08 06:18:26 -06001021 fail_msg = (
1022 "The current state of the machine matches the match"
1023 + " state:\n"
1024 + gp.sprint_varx("state", state)
1025 )
Michael Walsh70369fd2016-11-22 11:25:57 -06001026 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
1027 elif not invert and not match:
Patrick Williams20f38712022-12-08 06:18:26 -06001028 fail_msg = (
1029 "The current state of the machine does NOT match the"
1030 + " match state:\n"
1031 + gp.sprint_varx("state", state)
1032 )
Michael Walsh70369fd2016-11-22 11:25:57 -06001033 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
1034
1035 return state
1036
Michael Walsh70369fd2016-11-22 11:25:57 -06001037
Patrick Williams20f38712022-12-08 06:18:26 -06001038def wait_state(
1039 match_state=(),
1040 wait_time="1 min",
1041 interval="1 second",
1042 invert=0,
1043 openbmc_host="",
1044 openbmc_username="",
1045 openbmc_password="",
1046 os_host="",
1047 os_username="",
1048 os_password="",
1049 quiet=None,
1050):
Michael Walsh70369fd2016-11-22 11:25:57 -06001051 r"""
1052 Wait for the Open BMC machine's composite state to match the specified
1053 state. On success, this keyword returns the machine's composite state as
1054 a dictionary.
1055
Michael Walsh2a0df682019-09-27 17:19:27 -05001056 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -06001057 match_state A dictionary whose key/value pairs are "state field"/
1058 "state value". See check_state (above) for details.
Michael Walsh619aa332017-04-12 15:56:51 -05001059 This value may also be any string accepted by
1060 return_state_constant (e.g. "standby_match_state").
1061 In such a case this function will call
1062 return_state_constant to convert it to a proper
1063 dictionary as described above.
Michael Walsh70369fd2016-11-22 11:25:57 -06001064 wait_time The total amount of time to wait for the desired state.
1065 This value may be expressed in Robot Framework's time
1066 format (e.g. 1 minute, 2 min 3 s, 4.5).
1067 interval The amount of time between state checks.
1068 This value may be expressed in Robot Framework's time
1069 format (e.g. 1 minute, 2 min 3 s, 4.5).
1070 invert If this flag is set, this function will for the state of
1071 the machine to cease to match the match state.
1072 openbmc_host The DNS name or IP address of the BMC.
1073 This defaults to global ${OPENBMC_HOST}.
1074 openbmc_username The username to be used to login to the BMC.
1075 This defaults to global ${OPENBMC_USERNAME}.
1076 openbmc_password The password to be used to login to the BMC.
1077 This defaults to global ${OPENBMC_PASSWORD}.
1078 os_host The DNS name or IP address of the operating system.
1079 This defaults to global ${OS_HOST}.
1080 os_username The username to be used to login to the OS.
1081 This defaults to global ${OS_USERNAME}.
1082 os_password The password to be used to login to the OS.
1083 This defaults to global ${OS_PASSWORD}.
1084 quiet Indicates whether status details should be written to the
1085 console. Defaults to either global value of ${QUIET} or
1086 to 1.
1087 """
1088
Michael Walsh619aa332017-04-12 15:56:51 -05001089 quiet = int(gp.get_var_value(quiet, 0))
1090
George Keishing36efbc02018-12-12 10:18:23 -06001091 try:
Michael Walsh619aa332017-04-12 15:56:51 -05001092 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -06001093 except TypeError:
1094 pass
Michael Walsh70369fd2016-11-22 11:25:57 -06001095
1096 if not quiet:
1097 if invert:
1098 alt_text = "cease to "
1099 else:
1100 alt_text = ""
Patrick Williams20f38712022-12-08 06:18:26 -06001101 gp.print_timen(
1102 "Checking every "
1103 + str(interval)
1104 + " for up to "
1105 + str(wait_time)
1106 + " for the state of the machine to "
1107 + alt_text
1108 + "match the state shown below."
1109 )
Michael Walsh3eb50022017-03-21 11:27:30 -05001110 gp.print_var(match_state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001111
Michael Walshf893ba02017-01-10 10:28:05 -06001112 if quiet:
Michael Walsh341c21e2017-01-17 16:25:20 -06001113 print_string = ""
Michael Walshf893ba02017-01-10 10:28:05 -06001114 else:
Michael Walsh341c21e2017-01-17 16:25:20 -06001115 print_string = "#"
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001116
1117 debug = int(BuiltIn().get_variable_value("${debug}", "0"))
1118 if debug:
1119 # In debug we print state so no need to print the "#".
1120 print_string = ""
1121 check_state_quiet = 1 - debug
Patrick Williams20f38712022-12-08 06:18:26 -06001122 cmd_buf = [
1123 "Check State",
1124 match_state,
1125 "invert=${" + str(invert) + "}",
1126 "print_string=" + print_string,
1127 "openbmc_host=" + openbmc_host,
1128 "openbmc_username=" + openbmc_username,
1129 "openbmc_password=" + openbmc_password,
1130 "os_host=" + os_host,
1131 "os_username=" + os_username,
1132 "os_password=" + os_password,
1133 "quiet=${" + str(check_state_quiet) + "}",
1134 ]
Michael Walshedb5c942019-03-28 12:40:50 -05001135 gp.dprint_issuing(cmd_buf)
Michael Walsh619aa332017-04-12 15:56:51 -05001136 try:
Patrick Williams20f38712022-12-08 06:18:26 -06001137 state = BuiltIn().wait_until_keyword_succeeds(
1138 wait_time, interval, *cmd_buf
1139 )
Michael Walsh619aa332017-04-12 15:56:51 -05001140 except AssertionError as my_assertion_error:
1141 gp.printn()
1142 message = my_assertion_error.args[0]
1143 BuiltIn().fail(message)
1144
Michael Walshfd5a8682019-02-01 14:28:42 -06001145 if exit_wait_early_message:
1146 # The global exit_wait_early_message was set by a signal handler
1147 # indicating that we should fail.
1148 message = exit_wait_early_message
1149 # Clear the exit_wait_early_message variable for future use.
1150 set_exit_wait_early_message("")
1151 BuiltIn().fail(gp.sprint_error(message))
1152
Michael Walsh70369fd2016-11-22 11:25:57 -06001153 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -05001154 gp.printn()
Michael Walsh70369fd2016-11-22 11:25:57 -06001155 if invert:
Michael Walsh3eb50022017-03-21 11:27:30 -05001156 gp.print_timen("The states no longer match:")
Michael Walsh70369fd2016-11-22 11:25:57 -06001157 else:
Michael Walsh3eb50022017-03-21 11:27:30 -05001158 gp.print_timen("The states match:")
1159 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001160
1161 return state
1162
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001163
Michael Walsh2a0df682019-09-27 17:19:27 -05001164def set_start_boot_seconds(value=0):
1165 global start_boot_seconds
1166 start_boot_seconds = int(value)
1167
1168
1169set_start_boot_seconds(0)
1170
1171
Patrick Williams20f38712022-12-08 06:18:26 -06001172def wait_for_comm_cycle(start_boot_seconds, quiet=None):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001173 r"""
Michael Walsh2a0df682019-09-27 17:19:27 -05001174 Wait for the BMC uptime to be less than elapsed_boot_time.
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001175
Michael Walsh2a0df682019-09-27 17:19:27 -05001176 This function will tolerate an expected loss of communication to the BMC.
1177 This function is useful when some kind of reboot has been initiated by the
1178 caller.
1179
1180 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001181 start_boot_seconds The time that the boot test started. The format is the
1182 epoch time in seconds, i.e. the number of seconds since
1183 1970-01-01 00:00:00 UTC. This value should be obtained
1184 from the BMC so that it is not dependent on any kind of
1185 synchronization between this machine and the target BMC
1186 This will allow this program to work correctly even in
1187 a simulated environment. This value should be obtained
1188 by the caller prior to initiating a reboot. It can be
1189 obtained as follows:
1190 state = st.get_state(req_states=['epoch_seconds'])
1191 """
1192
Michael Walsh619aa332017-04-12 15:56:51 -05001193 quiet = int(gp.get_var_value(quiet, 0))
1194
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001195 # Validate parms.
Michael Walsh2a0df682019-09-27 17:19:27 -05001196 error_message = gv.valid_integer(start_boot_seconds)
1197 if error_message:
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001198 BuiltIn().fail(gp.sprint_error(error_message))
1199
Michael Walsh2a0df682019-09-27 17:19:27 -05001200 # Wait for uptime to be less than elapsed_boot_time.
1201 set_start_boot_seconds(start_boot_seconds)
Patrick Williams20f38712022-12-08 06:18:26 -06001202 expr = "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
1203 match_state = DotDict(
1204 [
1205 ("uptime", "^[0-9\\.]+$"),
1206 ("elapsed_boot_time", "^[0-9]+$"),
1207 (expressions_key(), [expr]),
1208 ]
1209 )
David Shawe9192562020-09-28 11:00:45 -05001210 wait_state(match_state, wait_time="12 mins", interval="5 seconds")
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001211
Michael Sheposbdd1dce2020-12-10 11:51:58 -06001212 gp.qprint_timen("Verifying that REST/Redfish API interface is working.")
1213 if not redfish_support_trans_state:
Patrick Williams20f38712022-12-08 06:18:26 -06001214 match_state = DotDict([("rest", "^1$")])
Michael Sheposbdd1dce2020-12-10 11:51:58 -06001215 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001216 match_state = DotDict([("redfish", "^1$")])
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001217 state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")