blob: ea45dd217ac76a8f43eefa2a68309b913726a689 [file] [log] [blame]
Michael Walsh70369fd2016-11-22 11:25:57 -06001#!/usr/bin/env python
2
3r"""
4This module contains functions having to do with machine state: get_state,
5check_state, wait_state, etc.
6
7The 'State' is a composite of many pieces of data. Therefore, the functions
8in this module define state as an ordered dictionary. Here is an example of
9some test output showing machine state:
10
Michael Walsh341c21e2017-01-17 16:25:20 -060011default_state:
Michael Walsh65b12542017-02-03 15:34:38 -060012 default_state[chassis]: On
Michael Walsh01975fa2017-08-20 20:51:36 -050013 default_state[boot_progress]: OSStart
Michael Walsh56749222017-09-29 15:26:07 -050014 default_state[operating_system]: BootComplete
Michael Walsh65b12542017-02-03 15:34:38 -060015 default_state[host]: Running
Michael Walsh341c21e2017-01-17 16:25:20 -060016 default_state[os_ping]: 1
17 default_state[os_login]: 1
18 default_state[os_run_cmd]: 1
Michael Walsh70369fd2016-11-22 11:25:57 -060019
20Different users may very well have different needs when inquiring about
Michael Walsh8fae6ea2017-02-20 16:14:44 -060021state. Support for new pieces of state information may be added to this
22module as needed.
Michael Walsh70369fd2016-11-22 11:25:57 -060023
24By using the wait_state function, a caller can start a boot and then wait for
25a precisely defined state to indicate that the boot has succeeded. If
26the boot fails, they can see exactly why by looking at the current state as
27compared with the expected state.
28"""
29
30import gen_print as gp
Michael Walsh70369fd2016-11-22 11:25:57 -060031import gen_valid as gv
Michael Walsh16cbb7f2017-02-02 15:54:16 -060032import gen_robot_utils as gru
Michael Walsh8fae6ea2017-02-20 16:14:44 -060033import gen_cmd as gc
Michael Walsh6a9bd142018-07-24 16:10:29 -050034import bmc_ssh_utils as bsu
Michael Walsh70369fd2016-11-22 11:25:57 -060035
Michael Walsh70369fd2016-11-22 11:25:57 -060036from robot.libraries.BuiltIn import BuiltIn
Michael Walsh341c21e2017-01-17 16:25:20 -060037from robot.utils import DotDict
Michael Walsh70369fd2016-11-22 11:25:57 -060038
39import re
Michael Walsh341c21e2017-01-17 16:25:20 -060040import os
Michael Walsh56749222017-09-29 15:26:07 -050041import sys
42import imp
43
Michael Walsh70369fd2016-11-22 11:25:57 -060044
Michael Walsh5a5868a2018-10-31 15:20:04 -050045# NOTE: Avoid importing utils.robot because utils.robot imports state.py
46# (indirectly) which will cause failures.
47gru.my_import_resource("rest_client.robot")
Michael Walsh70369fd2016-11-22 11:25:57 -060048
Michael Walsh56749222017-09-29 15:26:07 -050049base_path = os.path.dirname(os.path.dirname(
50 imp.find_module("gen_robot_print")[1])) + os.sep
51sys.path.append(base_path + "data/")
Michael Walsh56749222017-09-29 15:26:07 -050052
Michael Walsh940d6912017-10-27 12:32:33 -050053# Previously, I had this coded:
54# import variables as var
55# However, we ran into a problem where a robot program did this...
56# Variables ../../lib/ras/variables.py
57# Prior to doing this...
58# Library ../lib/state.py
59
60# This caused the wrong variables.py file to be selected. Attempts to fix this
61# have failed so far. For the moment, we will hard-code the value we need from
62# the file.
63
64SYSTEM_STATE_URI = "/xyz/openbmc_project/state/"
Michael Walsh56749222017-09-29 15:26:07 -050065
Michael Walsh8fae6ea2017-02-20 16:14:44 -060066# The BMC code has recently been changed as far as what states are defined and
67# what the state values can be. This module now has a means of processing both
68# the old style state (i.e. OBMC_STATES_VERSION = 0) and the new style (i.e.
Michael Walsh16cbb7f2017-02-02 15:54:16 -060069# OBMC_STATES_VERSION = 1).
Michael Walsh341c21e2017-01-17 16:25:20 -060070# The caller can set environment variable OBMC_STATES_VERSION to dictate
71# whether we're processing old or new style states. If OBMC_STATES_VERSION is
Michael Walsh8fae6ea2017-02-20 16:14:44 -060072# not set it will default to 1.
Michael Walsh341c21e2017-01-17 16:25:20 -060073
Michael Walsh619aa332017-04-12 15:56:51 -050074# As of the present moment, OBMC_STATES_VERSION of 0 is for cold that is so old
75# that it is no longer worthwhile to maintain. The OBMC_STATES_VERSION 0 code
76# is being removed but the OBMC_STATES_VERSION value will stay for now in the
77# event that it is needed in the future.
78
Michael Walsh8fae6ea2017-02-20 16:14:44 -060079OBMC_STATES_VERSION = int(os.environ.get('OBMC_STATES_VERSION', 1))
Michael Walsh341c21e2017-01-17 16:25:20 -060080
Michael Sheposda40c1d2020-12-01 22:30:00 -060081redfish_support_trans_state = int(os.environ.get('REDFISH_SUPPORT_TRANS_STATE', 0)) or \
82 int(BuiltIn().get_variable_value("${REDFISH_SUPPORT_TRANS_STATE}", default=0))
83
Konstantin Aladyshevc056c2b2021-03-25 11:59:39 +030084platform_arch_type = os.environ.get('PLATFORM_ARCH_TYPE', '') or \
85 BuiltIn().get_variable_value("${PLATFORM_ARCH_TYPE}", default="power")
George Keishing1e2fbee2021-03-19 11:19:29 -050086
Michael Walsh8fae6ea2017-02-20 16:14:44 -060087# valid_os_req_states and default_os_req_states are used by the os_get_state
88# function.
89# valid_os_req_states is a list of state information supported by the
90# get_os_state function.
91valid_os_req_states = ['os_ping',
92 'os_login',
93 'os_run_cmd']
Michael Sheposbdd1dce2020-12-10 11:51:58 -060094
Michael Walsh8fae6ea2017-02-20 16:14:44 -060095# When a user calls get_os_state w/o specifying req_states,
96# default_os_req_states is used as its value.
97default_os_req_states = ['os_ping',
98 'os_login',
99 'os_run_cmd']
100
101# Presently, some BMCs appear to not keep time very well. This environment
102# variable directs the get_state function to use either the BMC's epoch time
103# or the local epoch time.
104USE_BMC_EPOCH_TIME = int(os.environ.get('USE_BMC_EPOCH_TIME', 0))
Michael Walsh341c21e2017-01-17 16:25:20 -0600105
Michael Walsh619aa332017-04-12 15:56:51 -0500106# Useful state constant definition(s).
Michael Sheposda40c1d2020-12-01 22:30:00 -0600107if not redfish_support_trans_state:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600108 # When a user calls get_state w/o specifying req_states, default_req_states
109 # is used as its value.
110 default_req_states = ['rest',
111 'chassis',
112 'bmc',
113 'boot_progress',
114 'operating_system',
115 'host',
116 'os_ping',
117 'os_login',
118 'os_run_cmd']
119
120 # valid_req_states is a list of sub states supported by the get_state function.
121 # valid_req_states, default_req_states and master_os_up_match are used by the
122 # get_state function.
123
124 valid_req_states = ['ping',
125 'packet_loss',
126 'uptime',
127 'epoch_seconds',
128 'elapsed_boot_time',
129 'rest',
130 'chassis',
131 'requested_chassis',
132 'bmc',
133 'requested_bmc',
134 'boot_progress',
135 'operating_system',
136 'host',
137 'requested_host',
138 'attempts_left',
139 'os_ping',
140 'os_login',
141 'os_run_cmd']
142
Michael Sheposda40c1d2020-12-01 22:30:00 -0600143 # default_state is an initial value which may be of use to callers.
144 default_state = DotDict([('rest', '1'),
145 ('chassis', 'On'),
146 ('bmc', 'Ready'),
147 ('boot_progress', 'OSStart'),
148 ('operating_system', 'BootComplete'),
149 ('host', 'Running'),
150 ('os_ping', '1'),
151 ('os_login', '1'),
152 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500153
Michael Sheposda40c1d2020-12-01 22:30:00 -0600154 # A match state for checking that the system is at "standby".
155 standby_match_state = DotDict([('rest', '^1$'),
156 ('chassis', '^Off$'),
157 ('bmc', '^Ready$'),
158 ('boot_progress', '^Off|Unspecified$'),
159 ('operating_system', '^Inactive$'),
160 ('host', '^Off$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500161
Michael Sheposda40c1d2020-12-01 22:30:00 -0600162 # A match state for checking that the system is at "os running".
163 os_running_match_state = DotDict([('chassis', '^On$'),
164 ('bmc', '^Ready$'),
165 ('boot_progress',
166 'FW Progress, Starting OS|OSStart'),
167 ('operating_system', 'BootComplete'),
168 ('host', '^Running$'),
169 ('os_ping', '^1$'),
170 ('os_login', '^1$'),
171 ('os_run_cmd', '^1$')])
172
173 # A master dictionary to determine whether the os may be up.
174 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Walsh7dc885b2018-03-14 17:51:59 -0500175 ('bmc', '^Ready$'),
176 ('boot_progress',
177 'FW Progress, Starting OS|OSStart'),
178 ('operating_system', 'BootComplete'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600179 ('host', '^Running|Quiesced$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500180
Michael Sheposda40c1d2020-12-01 22:30:00 -0600181 invalid_state_match = DotDict([('rest', '^$'),
182 ('chassis', '^$'),
183 ('bmc', '^$'),
184 ('boot_progress', '^$'),
185 ('operating_system', '^$'),
186 ('host', '^$')])
187else:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600188 # When a user calls get_state w/o specifying req_states, default_req_states
189 # is used as its value.
190 default_req_states = ['redfish',
191 'chassis',
192 'bmc',
193 'boot_progress',
194 'host',
195 'os_ping',
196 'os_login',
197 'os_run_cmd']
198
199 # valid_req_states is a list of sub states supported by the get_state function.
200 # valid_req_states, default_req_states and master_os_up_match are used by the
201 # get_state function.
202
203 valid_req_states = ['ping',
204 'packet_loss',
205 'uptime',
206 'epoch_seconds',
207 'elapsed_boot_time',
208 'redfish',
209 'chassis',
210 'requested_chassis',
211 'bmc',
212 'requested_bmc',
213 'boot_progress',
214 'host',
215 'requested_host',
216 'attempts_left',
217 'os_ping',
218 'os_login',
219 'os_run_cmd']
220
Michael Sheposda40c1d2020-12-01 22:30:00 -0600221 # default_state is an initial value which may be of use to callers.
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600222 default_state = DotDict([('redfish', '1'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600223 ('chassis', 'On'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600224 ('bmc', 'Enabled'),
George Keishing89c98372021-08-13 08:15:10 -0500225 ('boot_progress',
226 'SystemHardwareInitializationComplete|OSBootStarted|OSRunning'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600227 ('host', 'Enabled'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600228 ('os_ping', '1'),
229 ('os_login', '1'),
230 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500231
Michael Sheposda40c1d2020-12-01 22:30:00 -0600232 # A match state for checking that the system is at "standby".
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600233 standby_match_state = DotDict([('redfish', '^1$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600234 ('chassis', '^Off$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600235 ('bmc', '^Enabled$'),
236 ('boot_progress', '^None$'),
237 ('host', '^Disabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600238
239 # A match state for checking that the system is at "os running".
240 os_running_match_state = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600241 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600242 ('boot_progress',
George Keishing89c98372021-08-13 08:15:10 -0500243 'SystemHardwareInitializationComplete|OSBootStarted|OSRunning'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600244 ('host', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600245 ('os_ping', '^1$'),
246 ('os_login', '^1$'),
247 ('os_run_cmd', '^1$')])
248
249 # A master dictionary to determine whether the os may be up.
250 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600251 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600252 ('boot_progress',
George Keishing89c98372021-08-13 08:15:10 -0500253 'SystemHardwareInitializationComplete|OSBootStarted|OSRunning'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600254 ('host', '^Enabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600255
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600256 invalid_state_match = DotDict([('redfish', '^$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600257 ('chassis', '^$'),
258 ('bmc', '^$'),
259 ('boot_progress', '^$'),
260 ('host', '^$')])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500261
George Keishing1e2fbee2021-03-19 11:19:29 -0500262# Filter the states based on platform type.
263if platform_arch_type == "x86":
George Keishingb51d1502021-03-25 03:30:33 -0500264
265 if not redfish_support_trans_state:
266 default_req_states.remove("operating_system")
267 valid_req_states.remove("operating_system")
268 del default_state["operating_system"]
269 del standby_match_state["operating_system"]
270 del os_running_match_state["operating_system"]
271 del master_os_up_match["operating_system"]
272 del invalid_state_match["operating_system"]
273
George Keishing1e2fbee2021-03-19 11:19:29 -0500274 default_req_states.remove("boot_progress")
George Keishing1e2fbee2021-03-19 11:19:29 -0500275 valid_req_states.remove("boot_progress")
George Keishing1e2fbee2021-03-19 11:19:29 -0500276 del default_state["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500277 del standby_match_state["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500278 del os_running_match_state["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500279 del master_os_up_match["boot_progress"]
George Keishing1e2fbee2021-03-19 11:19:29 -0500280 del invalid_state_match["boot_progress"]
281
Michael Walsh341c21e2017-01-17 16:25:20 -0600282
George Keishing36efbc02018-12-12 10:18:23 -0600283def return_state_constant(state_name='default_state'):
Michael Walsh619aa332017-04-12 15:56:51 -0500284 r"""
Michael Walsh7dc885b2018-03-14 17:51:59 -0500285 Return the named state dictionary constant.
Michael Walsh619aa332017-04-12 15:56:51 -0500286 """
287
George Keishing36efbc02018-12-12 10:18:23 -0600288 return eval(state_name)
Michael Walsh619aa332017-04-12 15:56:51 -0500289
Michael Walsh619aa332017-04-12 15:56:51 -0500290
Michael Walsh70369fd2016-11-22 11:25:57 -0600291def anchor_state(state):
Michael Walsh70369fd2016-11-22 11:25:57 -0600292 r"""
293 Add regular expression anchors ("^" and "$") to the beginning and end of
294 each item in the state dictionary passed in. Return the resulting
295 dictionary.
296
Michael Walsh2a0df682019-09-27 17:19:27 -0500297 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600298 state A dictionary such as the one returned by the get_state()
299 function.
300 """
301
Michael Walsh2ce067a2017-02-27 14:24:07 -0600302 anchored_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500303 for key in anchored_state.keys():
Michael Walsh70369fd2016-11-22 11:25:57 -0600304 anchored_state[key] = "^" + str(anchored_state[key]) + "$"
305
306 return anchored_state
307
Michael Walsh70369fd2016-11-22 11:25:57 -0600308
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600309def strip_anchor_state(state):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600310 r"""
311 Strip regular expression anchors ("^" and "$") from the beginning and end
312 of each item in the state dictionary passed in. Return the resulting
313 dictionary.
314
Michael Walsh2a0df682019-09-27 17:19:27 -0500315 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600316 state A dictionary such as the one returned by the get_state()
317 function.
318 """
319
Michael Walsh2ce067a2017-02-27 14:24:07 -0600320 stripped_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500321 for key in stripped_state.keys():
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600322 stripped_state[key] = stripped_state[key].strip("^$")
323
324 return stripped_state
325
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600326
Michael Walsh2a0df682019-09-27 17:19:27 -0500327def expressions_key():
328 r"""
329 Return expressions key constant.
330 """
331 return '<expressions>'
332
333
Michael Walsh70369fd2016-11-22 11:25:57 -0600334def compare_states(state,
Michael Walsh45ca6e42017-09-14 17:29:12 -0500335 match_state,
336 match_type='and'):
Michael Walsh70369fd2016-11-22 11:25:57 -0600337 r"""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600338 Compare 2 state dictionaries. Return True if they match and False if they
Michael Walsh70369fd2016-11-22 11:25:57 -0600339 don't. Note that the match_state dictionary does not need to have an entry
340 corresponding to each entry in the state dictionary. But for each entry
341 that it does have, the corresponding state entry will be checked for a
342 match.
343
Michael Walsh2a0df682019-09-27 17:19:27 -0500344 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600345 state A state dictionary such as the one returned by the
346 get_state function.
347 match_state A dictionary whose key/value pairs are "state field"/
348 "state value". The state value is interpreted as a
349 regular expression. Every value in this dictionary is
Michael Walsh45ca6e42017-09-14 17:29:12 -0500350 considered. When match_type is 'and', if each and every
351 comparison matches, the two dictionaries are considered to
352 be matching. If match_type is 'or', if any two of the
353 elements compared match, the two dictionaries are
354 considered to be matching.
Michael Walsh2a0df682019-09-27 17:19:27 -0500355
Michael Walsh7dc885b2018-03-14 17:51:59 -0500356 This value may also be any string accepted by
Michael Walsh2a0df682019-09-27 17:19:27 -0500357 return_state_constant (e.g. "standby_match_state"). In
358 such a case this function will call return_state_constant
359 to convert it to a proper dictionary as described above.
360
361 Finally, one special value is accepted for the key field:
362 expression_key(). If such an entry exists, its value is
363 taken to be a list of expressions to be evaluated. These
364 expressions may reference state dictionary entries by
365 simply coding them in standard python syntax (e.g.
366 state['key1']). What follows is an example expression:
367
368 "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
369
370 In this example, if the state dictionary's 'uptime' entry
371 is less than its 'elapsed_boot_time' entry, it would
372 qualify as a match.
Michael Walsh45ca6e42017-09-14 17:29:12 -0500373 match_type This may be 'and' or 'or'.
Michael Walsh70369fd2016-11-22 11:25:57 -0600374 """
375
Michael Walsh2a0df682019-09-27 17:19:27 -0500376 error_message = gv.valid_value(match_type, valid_values=['and', 'or'])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500377 if error_message != "":
378 BuiltIn().fail(gp.sprint_error(error_message))
379
George Keishing36efbc02018-12-12 10:18:23 -0600380 try:
Michael Walsh7dc885b2018-03-14 17:51:59 -0500381 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600382 except TypeError:
383 pass
Michael Walsh7dc885b2018-03-14 17:51:59 -0500384
Michael Walsh45ca6e42017-09-14 17:29:12 -0500385 default_match = (match_type == 'and')
Michael Walsh70369fd2016-11-22 11:25:57 -0600386 for key, match_state_value in match_state.items():
Michael Walsh97df71c2017-03-27 14:33:24 -0500387 # Blank match_state_value means "don't care".
388 if match_state_value == "":
389 continue
Michael Walsh2a0df682019-09-27 17:19:27 -0500390 if key == expressions_key():
391 for expr in match_state_value:
392 # Use python interpreter to evaluate the expression.
393 match = eval(expr)
394 if match != default_match:
395 return match
396 else:
397 try:
398 match = (re.match(match_state_value, str(state[key])) is not None)
399 except KeyError:
400 match = False
401 if match != default_match:
402 return match
Michael Walsh45ca6e42017-09-14 17:29:12 -0500403
404 return default_match
Michael Walsh70369fd2016-11-22 11:25:57 -0600405
Michael Walsh70369fd2016-11-22 11:25:57 -0600406
Michael Walsh70369fd2016-11-22 11:25:57 -0600407def get_os_state(os_host="",
408 os_username="",
409 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600410 req_states=default_os_req_states,
411 os_up=True,
Michael Walsh70369fd2016-11-22 11:25:57 -0600412 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600413 r"""
414 Get component states for the operating system such as ping, login,
415 etc, put them into a dictionary and return them to the caller.
416
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600417 Note that all substate values are strings.
418
Michael Walsh2a0df682019-09-27 17:19:27 -0500419 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600420 os_host The DNS name or IP address of the operating system.
421 This defaults to global ${OS_HOST}.
422 os_username The username to be used to login to the OS.
423 This defaults to global ${OS_USERNAME}.
424 os_password The password to be used to login to the OS.
425 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600426 req_states This is a list of states whose values are being requested by
427 the caller.
428 os_up If the caller knows that the os can't possibly be up, it can
429 improve performance by passing os_up=False. This function
430 will then simply return default values for all requested os
431 sub states.
Michael Walsh70369fd2016-11-22 11:25:57 -0600432 quiet Indicates whether status details (e.g. curl commands) should
433 be written to the console.
434 Defaults to either global value of ${QUIET} or to 1.
435 """
436
Michael Walsh619aa332017-04-12 15:56:51 -0500437 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600438
439 # Set parm defaults where necessary and validate all parms.
440 if os_host == "":
441 os_host = BuiltIn().get_variable_value("${OS_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500442 error_message = gv.valid_value(os_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600443 if error_message != "":
444 BuiltIn().fail(gp.sprint_error(error_message))
445
446 if os_username == "":
447 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500448 error_message = gv.valid_value(os_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600449 if error_message != "":
450 BuiltIn().fail(gp.sprint_error(error_message))
451
452 if os_password == "":
453 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500454 error_message = gv.valid_value(os_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600455 if error_message != "":
456 BuiltIn().fail(gp.sprint_error(error_message))
457
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600458 invalid_req_states = [sub_state for sub_state in req_states
459 if sub_state not in valid_os_req_states]
460 if len(invalid_req_states) > 0:
461 error_message = "The following req_states are not supported:\n" +\
462 gp.sprint_var(invalid_req_states)
463 BuiltIn().fail(gp.sprint_error(error_message))
Michael Walsh70369fd2016-11-22 11:25:57 -0600464
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600465 # Initialize all substate values supported by this function.
466 os_ping = 0
467 os_login = 0
468 os_run_cmd = 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600469
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600470 if os_up:
471 if 'os_ping' in req_states:
472 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600473 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + os_host,
474 print_output=0, show_err=0,
475 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600476 if rc == 0:
477 os_ping = 1
Michael Walsh70369fd2016-11-22 11:25:57 -0600478
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600479 # Programming note: All attributes which do not require an ssh login
480 # should have been processed by this point.
481 master_req_login = ['os_login', 'os_run_cmd']
482 req_login = [sub_state for sub_state in req_states if sub_state in
483 master_req_login]
Michael Walsh97df71c2017-03-27 14:33:24 -0500484 must_login = (len(req_login) > 0)
Michael Walsh70369fd2016-11-22 11:25:57 -0600485
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600486 if must_login:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500487 output, stderr, rc = bsu.os_execute_command("uptime", quiet=quiet,
Michael Walsh7fc33972018-08-07 14:55:03 -0500488 ignore_err=1,
Michael Shepos3390c852021-02-11 00:12:31 -0600489 time_out=20,
490 os_host=os_host,
491 os_username=os_username,
492 os_password=os_password)
Michael Walsh6a9bd142018-07-24 16:10:29 -0500493 if rc == 0:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600494 os_login = 1
Michael Walsh6a9bd142018-07-24 16:10:29 -0500495 os_run_cmd = 1
Michael Walsh3eb50022017-03-21 11:27:30 -0500496 else:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500497 gp.dprint_vars(output, stderr)
498 gp.dprint_vars(rc, 1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600499
500 os_state = DotDict()
501 for sub_state in req_states:
502 cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")"
503 exec(cmd_buf)
Michael Walsh70369fd2016-11-22 11:25:57 -0600504
505 return os_state
506
Michael Walsh70369fd2016-11-22 11:25:57 -0600507
Michael Walsh70369fd2016-11-22 11:25:57 -0600508def get_state(openbmc_host="",
509 openbmc_username="",
510 openbmc_password="",
511 os_host="",
512 os_username="",
513 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600514 req_states=default_req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600515 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600516 r"""
Michael Walsh619aa332017-04-12 15:56:51 -0500517 Get component states such as chassis state, bmc state, etc, put them into a
Michael Walsh70369fd2016-11-22 11:25:57 -0600518 dictionary and return them to the caller.
519
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600520 Note that all substate values are strings.
521
Michael Walsh2a0df682019-09-27 17:19:27 -0500522 Note: If elapsed_boot_time is included in req_states, it is the caller's
523 duty to call set_start_boot_seconds() in order to set global
524 start_boot_seconds. elapsed_boot_time is the current time minus
525 start_boot_seconds.
526
527 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600528 openbmc_host The DNS name or IP address of the BMC.
529 This defaults to global ${OPENBMC_HOST}.
530 openbmc_username The username to be used to login to the BMC.
531 This defaults to global ${OPENBMC_USERNAME}.
532 openbmc_password The password to be used to login to the BMC.
533 This defaults to global ${OPENBMC_PASSWORD}.
534 os_host The DNS name or IP address of the operating system.
535 This defaults to global ${OS_HOST}.
536 os_username The username to be used to login to the OS.
537 This defaults to global ${OS_USERNAME}.
538 os_password The password to be used to login to the OS.
539 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600540 req_states This is a list of states whose values are being requested
541 by the caller.
Michael Walsh70369fd2016-11-22 11:25:57 -0600542 quiet Indicates whether status details (e.g. curl commands)
543 should be written to the console.
544 Defaults to either global value of ${QUIET} or to 1.
545 """
546
Michael Walsh619aa332017-04-12 15:56:51 -0500547 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600548
549 # Set parm defaults where necessary and validate all parms.
550 if openbmc_host == "":
551 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500552 error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600553 if error_message != "":
554 BuiltIn().fail(gp.sprint_error(error_message))
555
556 if openbmc_username == "":
557 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500558 error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600559 if error_message != "":
560 BuiltIn().fail(gp.sprint_error(error_message))
561
562 if openbmc_password == "":
563 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500564 error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600565 if error_message != "":
566 BuiltIn().fail(gp.sprint_error(error_message))
567
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600568 # NOTE: OS parms are optional.
Michael Walsh70369fd2016-11-22 11:25:57 -0600569 if os_host == "":
570 os_host = BuiltIn().get_variable_value("${OS_HOST}")
571 if os_host is None:
572 os_host = ""
573
574 if os_username is "":
575 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
576 if os_username is None:
577 os_username = ""
578
579 if os_password is "":
580 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
581 if os_password is None:
582 os_password = ""
583
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600584 invalid_req_states = [sub_state for sub_state in req_states
585 if sub_state not in valid_req_states]
586 if len(invalid_req_states) > 0:
587 error_message = "The following req_states are not supported:\n" +\
588 gp.sprint_var(invalid_req_states)
589 BuiltIn().fail(gp.sprint_error(error_message))
590
591 # Initialize all substate values supported by this function.
592 ping = 0
593 packet_loss = ''
594 uptime = ''
595 epoch_seconds = ''
Michael Walsh2a0df682019-09-27 17:19:27 -0500596 elapsed_boot_time = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500597 rest = ''
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600598 redfish = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500599 chassis = ''
600 requested_chassis = ''
601 bmc = ''
602 requested_bmc = ''
George Keishing18f15dd2021-03-09 11:17:35 -0600603 # BootProgress state will get populated when state logic enumerates the
604 # state URI. This is to prevent state dictionary boot_progress value
605 # getting empty when the BootProgress is NOT found, making it optional.
606 boot_progress = 'NA'
Michael Walsh2b269de2017-10-09 11:18:11 -0500607 operating_system = ''
608 host = ''
609 requested_host = ''
610 attempts_left = ''
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600611
Michael Walsh70369fd2016-11-22 11:25:57 -0600612 # Get the component states.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600613 if 'ping' in req_states:
614 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600615 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + openbmc_host,
616 print_output=0, show_err=0,
617 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600618 if rc == 0:
619 ping = 1
620
621 if 'packet_loss' in req_states:
622 # See if the OS pings.
623 cmd_buf = "ping -c 5 -w 5 " + openbmc_host +\
624 " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'"
George Keishing36efbc02018-12-12 10:18:23 -0600625 rc, out_buf = gc.shell_cmd(cmd_buf,
626 print_output=0, show_err=0,
627 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600628 if rc == 0:
629 packet_loss = out_buf.rstrip("\n")
630
Michael Walshe53e47a2017-06-30 17:03:24 -0500631 if 'uptime' in req_states:
Michael Walshfa765932017-10-13 14:07:22 -0500632 # Sometimes reading uptime results in a blank value. Call with
633 # wait_until_keyword_succeeds to ensure a non-blank value is obtained.
634 remote_cmd_buf = "read uptime filler 2>/dev/null < /proc/uptime" +\
635 " && [ ! -z \"${uptime}\" ] && echo ${uptime}"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500636 cmd_buf = ["BMC Execute Command",
Michael Walsh888d85a2019-04-18 11:03:28 -0500637 re.sub('\\$', '\\$', remote_cmd_buf), 'quiet=1',
George Keishing4361d8d2021-03-24 02:59:52 -0500638 'test_mode=0', 'time_out=5']
Michael Walsh2a0df682019-09-27 17:19:27 -0500639 gp.qprint_issuing(cmd_buf, 0)
640 gp.qprint_issuing(remote_cmd_buf, 0)
Michael Walshfa765932017-10-13 14:07:22 -0500641 try:
642 stdout, stderr, rc =\
George Keishing4361d8d2021-03-24 02:59:52 -0500643 BuiltIn().wait_until_keyword_succeeds("10 sec", "5 sec",
Michael Walshfa765932017-10-13 14:07:22 -0500644 *cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500645 if rc == 0 and stderr == "":
646 uptime = stdout
Michael Walshfa765932017-10-13 14:07:22 -0500647 except AssertionError as my_assertion_error:
648 pass
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600649
Michael Walsh2a0df682019-09-27 17:19:27 -0500650 if 'epoch_seconds' in req_states or 'elapsed_boot_time' in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600651 date_cmd_buf = "date -u +%s"
652 if USE_BMC_EPOCH_TIME:
Michael Walshe53e47a2017-06-30 17:03:24 -0500653 cmd_buf = ["BMC Execute Command", date_cmd_buf, 'quiet=${1}']
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600654 if not quiet:
Michael Walshedb5c942019-03-28 12:40:50 -0500655 gp.print_issuing(cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500656 status, ret_values = \
657 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
658 if status == "PASS":
659 stdout, stderr, rc = ret_values
660 if rc == 0 and stderr == "":
661 epoch_seconds = stdout.rstrip("\n")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600662 else:
663 shell_rc, out_buf = gc.cmd_fnc_u(date_cmd_buf,
Michael Walshfa765932017-10-13 14:07:22 -0500664 quiet=quiet,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600665 print_output=0)
666 if shell_rc == 0:
667 epoch_seconds = out_buf.rstrip("\n")
668
Michael Walsh2a0df682019-09-27 17:19:27 -0500669 if 'elapsed_boot_time' in req_states:
670 global start_boot_seconds
671 elapsed_boot_time = int(epoch_seconds) - start_boot_seconds
672
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600673 if not redfish_support_trans_state:
674 master_req_rest = ['rest', 'host', 'requested_host', 'operating_system',
675 'attempts_left', 'boot_progress', 'chassis',
676 'requested_chassis' 'bmc' 'requested_bmc']
Michael Walsh56749222017-09-29 15:26:07 -0500677
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600678 req_rest = [sub_state for sub_state in req_states if sub_state in
679 master_req_rest]
680 need_rest = (len(req_rest) > 0)
681 state = DotDict()
682 if need_rest:
683 cmd_buf = ["Read Properties", SYSTEM_STATE_URI + "enumerate",
684 "quiet=${" + str(quiet) + "}", "timeout=30"]
685 gp.dprint_issuing(cmd_buf)
686 status, ret_values = \
687 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
688 if status == "PASS":
689 state['rest'] = '1'
690 else:
691 state['rest'] = '0'
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600692
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600693 if int(state['rest']):
694 for url_path in ret_values:
George Keishing3adda952021-02-11 13:23:51 -0600695 # Skip conflicting "CurrentHostState" URL from the enum
696 # /xyz/openbmc_project/state/hypervisor0
697 if "hypervisor0" in url_path:
698 continue
699
Konstantin Aladyshev8c9cf9c2021-03-21 23:33:40 +0300700 if platform_arch_type == "x86":
701 # Skip conflicting "CurrentPowerState" URL from the enum
702 # /xyz/openbmc_project/state/chassis_system0
703 if "chassis_system0" in url_path:
704 continue
705
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600706 for attr_name in ret_values[url_path]:
707 # Create a state key value based on the attr_name.
708 try:
709 ret_values[url_path][attr_name] = \
710 re.sub(r'.*\.', "",
711 ret_values[url_path][attr_name])
712 except TypeError:
713 pass
714 # Do some key name manipulations.
715 new_attr_name = re.sub(r'^Current|(State|Transition)$',
716 "", attr_name)
717 new_attr_name = re.sub(r'BMC', r'Bmc', new_attr_name)
718 new_attr_name = re.sub(r'([A-Z][a-z])', r'_\1',
719 new_attr_name)
720 new_attr_name = new_attr_name.lower().lstrip("_")
721 new_attr_name = re.sub(r'power', r'chassis', new_attr_name)
722 if new_attr_name in req_states:
723 state[new_attr_name] = ret_values[url_path][attr_name]
724 else:
725 master_req_rf = ['redfish', 'host', 'requested_host',
726 'attempts_left', 'boot_progress', 'chassis',
727 'requested_chassis' 'bmc' 'requested_bmc']
728
729 req_rf = [sub_state for sub_state in req_states if sub_state in
730 master_req_rf]
731 need_rf = (len(req_rf) > 0)
732 state = DotDict()
733 if need_rf:
734 cmd_buf = ["Redfish Get States"]
735 gp.dprint_issuing(cmd_buf)
George Keishing21e28b42021-05-27 08:10:29 -0500736 try:
737 status, ret_values = \
738 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
739 except Exception as ex:
740 # Robot raised UserKeywordExecutionFailed error exception.
741 gp.dprint_issuing("Retrying Redfish Get States")
742 status, ret_values = \
743 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
744
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600745 gp.dprint_vars(status, ret_values)
746 if status == "PASS":
747 state['redfish'] = '1'
748 else:
749 state['redfish'] = '0'
750
751 if int(state['redfish']):
752 state['chassis'] = ret_values['chassis']
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600753 state['host'] = ret_values['host']
754 state['bmc'] = ret_values['bmc']
George Keishingb51d1502021-03-25 03:30:33 -0500755 if platform_arch_type != "x86":
756 state['boot_progress'] = ret_values['boot_progress']
Michael Walshb95eb542017-03-31 09:39:20 -0500757
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600758 for sub_state in req_states:
Michael Walsh56749222017-09-29 15:26:07 -0500759 if sub_state in state:
760 continue
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600761 if sub_state.startswith("os_"):
762 # We pass "os_" requests on to get_os_state.
763 continue
764 cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")"
765 exec(cmd_buf)
766
767 if os_host == "":
768 # The caller has not specified an os_host so as far as we're concerned,
769 # it doesn't exist.
770 return state
771
772 os_req_states = [sub_state for sub_state in req_states
773 if sub_state.startswith('os_')]
774
775 if len(os_req_states) > 0:
776 # The caller has specified an os_host and they have requested
777 # information on os substates.
778
779 # Based on the information gathered on bmc, we'll try to make a
780 # determination of whether the os is even up. We'll pass the result
781 # of that assessment to get_os_state to enhance performance.
782 os_up_match = DotDict()
783 for sub_state in master_os_up_match:
784 if sub_state in req_states:
785 os_up_match[sub_state] = master_os_up_match[sub_state]
Michael Walsh70369fd2016-11-22 11:25:57 -0600786 os_up = compare_states(state, os_up_match)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600787 os_state = get_os_state(os_host=os_host,
788 os_username=os_username,
789 os_password=os_password,
790 req_states=os_req_states,
791 os_up=os_up,
792 quiet=quiet)
793 # Append os_state dictionary to ours.
794 state.update(os_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600795
796 return state
797
Michael Walsh70369fd2016-11-22 11:25:57 -0600798
Michael Walshfd5a8682019-02-01 14:28:42 -0600799exit_wait_early_message = ""
800
801
802def set_exit_wait_early_message(value):
803 r"""
804 Set global exit_wait_early_message to the indicated value.
805
806 This is a mechanism by which the programmer can do an early exit from
807 wait_until_keyword_succeeds() based on some special condition.
808
809 Description of argument(s):
810 value The value to assign to the global
811 exit_wait_early_message.
812 """
813
814 global exit_wait_early_message
815 exit_wait_early_message = value
816
817
Michael Walsh70369fd2016-11-22 11:25:57 -0600818def check_state(match_state,
819 invert=0,
820 print_string="",
821 openbmc_host="",
822 openbmc_username="",
823 openbmc_password="",
824 os_host="",
825 os_username="",
826 os_password="",
827 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600828 r"""
829 Check that the Open BMC machine's composite state matches the specified
830 state. On success, this keyword returns the machine's composite state as a
831 dictionary.
832
Michael Walsh2a0df682019-09-27 17:19:27 -0500833 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600834 match_state A dictionary whose key/value pairs are "state field"/
835 "state value". The state value is interpreted as a
836 regular expression. Example call from robot:
Michael Walsh341c21e2017-01-17 16:25:20 -0600837 ${match_state}= Create Dictionary chassis=^On$
838 ... bmc=^Ready$
Michael Walsh01975fa2017-08-20 20:51:36 -0500839 ... boot_progress=^OSStart$
Michael Walsh70369fd2016-11-22 11:25:57 -0600840 ${state}= Check State &{match_state}
841 invert If this flag is set, this function will succeed if the
842 states do NOT match.
843 print_string This function will print this string to the console prior
844 to getting the state.
845 openbmc_host The DNS name or IP address of the BMC.
846 This defaults to global ${OPENBMC_HOST}.
847 openbmc_username The username to be used to login to the BMC.
848 This defaults to global ${OPENBMC_USERNAME}.
849 openbmc_password The password to be used to login to the BMC.
850 This defaults to global ${OPENBMC_PASSWORD}.
851 os_host The DNS name or IP address of the operating system.
852 This defaults to global ${OS_HOST}.
853 os_username The username to be used to login to the OS.
854 This defaults to global ${OS_USERNAME}.
855 os_password The password to be used to login to the OS.
856 This defaults to global ${OS_PASSWORD}.
857 quiet Indicates whether status details should be written to the
858 console. Defaults to either global value of ${QUIET} or
859 to 1.
860 """
861
Michael Walsh619aa332017-04-12 15:56:51 -0500862 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600863
Michael Walshedb5c942019-03-28 12:40:50 -0500864 gp.gp_print(print_string)
865
866 try:
867 match_state = return_state_constant(match_state)
868 except TypeError:
869 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600870
Michael Walsh2a0df682019-09-27 17:19:27 -0500871 req_states = list(match_state.keys())
872 # Remove special-case match key from req_states.
873 if expressions_key() in req_states:
874 req_states.remove(expressions_key())
Michael Walsh70369fd2016-11-22 11:25:57 -0600875 # Initialize state.
876 state = get_state(openbmc_host=openbmc_host,
877 openbmc_username=openbmc_username,
878 openbmc_password=openbmc_password,
879 os_host=os_host,
880 os_username=os_username,
881 os_password=os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600882 req_states=req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600883 quiet=quiet)
884 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500885 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600886
Michael Walshfd5a8682019-02-01 14:28:42 -0600887 if exit_wait_early_message != "":
888 # The exit_wait_early_message has been set by a signal handler so we
889 # will exit "successfully". It is incumbent upon the calling function
890 # (e.g. wait_state) to check/clear this variable and to fail
891 # appropriately.
892 return state
893
Michael Walsh70369fd2016-11-22 11:25:57 -0600894 match = compare_states(state, match_state)
895
896 if invert and match:
897 fail_msg = "The current state of the machine matches the match" +\
898 " state:\n" + gp.sprint_varx("state", state)
899 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
900 elif not invert and not match:
901 fail_msg = "The current state of the machine does NOT match the" +\
902 " match state:\n" +\
903 gp.sprint_varx("state", state)
904 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
905
906 return state
907
Michael Walsh70369fd2016-11-22 11:25:57 -0600908
Michael Walshf893ba02017-01-10 10:28:05 -0600909def wait_state(match_state=(),
Michael Walsh70369fd2016-11-22 11:25:57 -0600910 wait_time="1 min",
911 interval="1 second",
912 invert=0,
913 openbmc_host="",
914 openbmc_username="",
915 openbmc_password="",
916 os_host="",
917 os_username="",
918 os_password="",
919 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600920 r"""
921 Wait for the Open BMC machine's composite state to match the specified
922 state. On success, this keyword returns the machine's composite state as
923 a dictionary.
924
Michael Walsh2a0df682019-09-27 17:19:27 -0500925 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600926 match_state A dictionary whose key/value pairs are "state field"/
927 "state value". See check_state (above) for details.
Michael Walsh619aa332017-04-12 15:56:51 -0500928 This value may also be any string accepted by
929 return_state_constant (e.g. "standby_match_state").
930 In such a case this function will call
931 return_state_constant to convert it to a proper
932 dictionary as described above.
Michael Walsh70369fd2016-11-22 11:25:57 -0600933 wait_time The total amount of time to wait for the desired state.
934 This value may be expressed in Robot Framework's time
935 format (e.g. 1 minute, 2 min 3 s, 4.5).
936 interval The amount of time between state checks.
937 This value may be expressed in Robot Framework's time
938 format (e.g. 1 minute, 2 min 3 s, 4.5).
939 invert If this flag is set, this function will for the state of
940 the machine to cease to match the match state.
941 openbmc_host The DNS name or IP address of the BMC.
942 This defaults to global ${OPENBMC_HOST}.
943 openbmc_username The username to be used to login to the BMC.
944 This defaults to global ${OPENBMC_USERNAME}.
945 openbmc_password The password to be used to login to the BMC.
946 This defaults to global ${OPENBMC_PASSWORD}.
947 os_host The DNS name or IP address of the operating system.
948 This defaults to global ${OS_HOST}.
949 os_username The username to be used to login to the OS.
950 This defaults to global ${OS_USERNAME}.
951 os_password The password to be used to login to the OS.
952 This defaults to global ${OS_PASSWORD}.
953 quiet Indicates whether status details should be written to the
954 console. Defaults to either global value of ${QUIET} or
955 to 1.
956 """
957
Michael Walsh619aa332017-04-12 15:56:51 -0500958 quiet = int(gp.get_var_value(quiet, 0))
959
George Keishing36efbc02018-12-12 10:18:23 -0600960 try:
Michael Walsh619aa332017-04-12 15:56:51 -0500961 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600962 except TypeError:
963 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600964
965 if not quiet:
966 if invert:
967 alt_text = "cease to "
968 else:
969 alt_text = ""
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500970 gp.print_timen("Checking every " + str(interval) + " for up to "
971 + str(wait_time) + " for the state of the machine to "
972 + alt_text + "match the state shown below.")
Michael Walsh3eb50022017-03-21 11:27:30 -0500973 gp.print_var(match_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600974
Michael Walshf893ba02017-01-10 10:28:05 -0600975 if quiet:
Michael Walsh341c21e2017-01-17 16:25:20 -0600976 print_string = ""
Michael Walshf893ba02017-01-10 10:28:05 -0600977 else:
Michael Walsh341c21e2017-01-17 16:25:20 -0600978 print_string = "#"
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600979
980 debug = int(BuiltIn().get_variable_value("${debug}", "0"))
981 if debug:
982 # In debug we print state so no need to print the "#".
983 print_string = ""
984 check_state_quiet = 1 - debug
Michael Walsh70369fd2016-11-22 11:25:57 -0600985 cmd_buf = ["Check State", match_state, "invert=${" + str(invert) + "}",
Michael Walshf893ba02017-01-10 10:28:05 -0600986 "print_string=" + print_string, "openbmc_host=" + openbmc_host,
Michael Walsh70369fd2016-11-22 11:25:57 -0600987 "openbmc_username=" + openbmc_username,
988 "openbmc_password=" + openbmc_password, "os_host=" + os_host,
989 "os_username=" + os_username, "os_password=" + os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600990 "quiet=${" + str(check_state_quiet) + "}"]
Michael Walshedb5c942019-03-28 12:40:50 -0500991 gp.dprint_issuing(cmd_buf)
Michael Walsh619aa332017-04-12 15:56:51 -0500992 try:
993 state = BuiltIn().wait_until_keyword_succeeds(wait_time, interval,
994 *cmd_buf)
995 except AssertionError as my_assertion_error:
996 gp.printn()
997 message = my_assertion_error.args[0]
998 BuiltIn().fail(message)
999
Michael Walshfd5a8682019-02-01 14:28:42 -06001000 if exit_wait_early_message:
1001 # The global exit_wait_early_message was set by a signal handler
1002 # indicating that we should fail.
1003 message = exit_wait_early_message
1004 # Clear the exit_wait_early_message variable for future use.
1005 set_exit_wait_early_message("")
1006 BuiltIn().fail(gp.sprint_error(message))
1007
Michael Walsh70369fd2016-11-22 11:25:57 -06001008 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -05001009 gp.printn()
Michael Walsh70369fd2016-11-22 11:25:57 -06001010 if invert:
Michael Walsh3eb50022017-03-21 11:27:30 -05001011 gp.print_timen("The states no longer match:")
Michael Walsh70369fd2016-11-22 11:25:57 -06001012 else:
Michael Walsh3eb50022017-03-21 11:27:30 -05001013 gp.print_timen("The states match:")
1014 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001015
1016 return state
1017
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001018
Michael Walsh2a0df682019-09-27 17:19:27 -05001019def set_start_boot_seconds(value=0):
1020 global start_boot_seconds
1021 start_boot_seconds = int(value)
1022
1023
1024set_start_boot_seconds(0)
1025
1026
Michael Walsh619aa332017-04-12 15:56:51 -05001027def wait_for_comm_cycle(start_boot_seconds,
1028 quiet=None):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001029 r"""
Michael Walsh2a0df682019-09-27 17:19:27 -05001030 Wait for the BMC uptime to be less than elapsed_boot_time.
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001031
Michael Walsh2a0df682019-09-27 17:19:27 -05001032 This function will tolerate an expected loss of communication to the BMC.
1033 This function is useful when some kind of reboot has been initiated by the
1034 caller.
1035
1036 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001037 start_boot_seconds The time that the boot test started. The format is the
1038 epoch time in seconds, i.e. the number of seconds since
1039 1970-01-01 00:00:00 UTC. This value should be obtained
1040 from the BMC so that it is not dependent on any kind of
1041 synchronization between this machine and the target BMC
1042 This will allow this program to work correctly even in
1043 a simulated environment. This value should be obtained
1044 by the caller prior to initiating a reboot. It can be
1045 obtained as follows:
1046 state = st.get_state(req_states=['epoch_seconds'])
1047 """
1048
Michael Walsh619aa332017-04-12 15:56:51 -05001049 quiet = int(gp.get_var_value(quiet, 0))
1050
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001051 # Validate parms.
Michael Walsh2a0df682019-09-27 17:19:27 -05001052 error_message = gv.valid_integer(start_boot_seconds)
1053 if error_message:
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001054 BuiltIn().fail(gp.sprint_error(error_message))
1055
Michael Walsh2a0df682019-09-27 17:19:27 -05001056 # Wait for uptime to be less than elapsed_boot_time.
1057 set_start_boot_seconds(start_boot_seconds)
1058 expr = 'int(float(state[\'uptime\'])) < int(state[\'elapsed_boot_time\'])'
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001059 match_state = DotDict([('uptime', '^[0-9\\.]+$'),
Michael Walsh2a0df682019-09-27 17:19:27 -05001060 ('elapsed_boot_time', '^[0-9]+$'),
1061 (expressions_key(), [expr])])
David Shawe9192562020-09-28 11:00:45 -05001062 wait_state(match_state, wait_time="12 mins", interval="5 seconds")
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001063
Michael Sheposbdd1dce2020-12-10 11:51:58 -06001064 gp.qprint_timen("Verifying that REST/Redfish API interface is working.")
1065 if not redfish_support_trans_state:
1066 match_state = DotDict([('rest', '^1$')])
1067 else:
1068 match_state = DotDict([('redfish', '^1$')])
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001069 state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")