| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 1 | #!/usr/bin/env python | 
 | 2 |  | 
 | 3 | r""" | 
 | 4 | This module contains functions having to do with machine state: get_state, | 
 | 5 | check_state, wait_state, etc. | 
 | 6 |  | 
 | 7 | The 'State' is a composite of many pieces of data.  Therefore, the functions | 
 | 8 | in this module define state as an ordered dictionary.  Here is an example of | 
 | 9 | some test output showing machine state: | 
 | 10 |  | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 11 | default_state: | 
| Michael Walsh | 65b1254 | 2017-02-03 15:34:38 -0600 | [diff] [blame] | 12 |   default_state[chassis]:                         On | 
| Michael Walsh | 01975fa | 2017-08-20 20:51:36 -0500 | [diff] [blame] | 13 |   default_state[boot_progress]:                   OSStart | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 14 |   default_state[operating_system]:                BootComplete | 
| Michael Walsh | 65b1254 | 2017-02-03 15:34:38 -0600 | [diff] [blame] | 15 |   default_state[host]:                            Running | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 16 |   default_state[os_ping]:                         1 | 
 | 17 |   default_state[os_login]:                        1 | 
 | 18 |   default_state[os_run_cmd]:                      1 | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 19 |  | 
 | 20 | Different users may very well have different needs when inquiring about | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 21 | state.  Support for new pieces of state information may be added to this | 
 | 22 | module as needed. | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 23 |  | 
 | 24 | By using the wait_state function, a caller can start a boot and then wait for | 
 | 25 | a precisely defined state to indicate that the boot has succeeded.  If | 
 | 26 | the boot fails, they can see exactly why by looking at the current state as | 
 | 27 | compared with the expected state. | 
 | 28 | """ | 
 | 29 |  | 
 | 30 | import gen_print as gp | 
 | 31 | import gen_robot_print as grp | 
 | 32 | import gen_valid as gv | 
| Michael Walsh | 16cbb7f | 2017-02-02 15:54:16 -0600 | [diff] [blame] | 33 | import gen_robot_utils as gru | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 34 | import gen_cmd as gc | 
| Michael Walsh | 6a9bd14 | 2018-07-24 16:10:29 -0500 | [diff] [blame] | 35 | import bmc_ssh_utils as bsu | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 36 |  | 
 | 37 | import commands | 
 | 38 | from robot.libraries.BuiltIn import BuiltIn | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 39 | from robot.utils import DotDict | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 40 |  | 
 | 41 | import re | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 42 | import os | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 43 | import sys | 
 | 44 | import imp | 
 | 45 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 46 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 47 | # We need utils.robot to get keywords like "Get Chassis Power State". | 
| Michael Walsh | 16cbb7f | 2017-02-02 15:54:16 -0600 | [diff] [blame] | 48 | gru.my_import_resource("utils.robot") | 
 | 49 | gru.my_import_resource("state_manager.robot") | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 50 |  | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 51 | base_path = os.path.dirname(os.path.dirname( | 
 | 52 |                             imp.find_module("gen_robot_print")[1])) + os.sep | 
 | 53 | sys.path.append(base_path + "data/") | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 54 |  | 
| Michael Walsh | 940d691 | 2017-10-27 12:32:33 -0500 | [diff] [blame] | 55 | # Previously, I had this coded: | 
 | 56 | # import variables as var | 
 | 57 | # However, we ran into a problem where a robot program did this... | 
 | 58 | # Variables           ../../lib/ras/variables.py | 
 | 59 | # Prior to doing this... | 
 | 60 | # Library            ../lib/state.py | 
 | 61 |  | 
 | 62 | # This caused the wrong variables.py file to be selected.  Attempts to fix this | 
 | 63 | # have failed so far.  For the moment, we will hard-code the value we need from | 
 | 64 | # the file. | 
 | 65 |  | 
 | 66 | SYSTEM_STATE_URI = "/xyz/openbmc_project/state/" | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 67 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 68 | # The BMC code has recently been changed as far as what states are defined and | 
 | 69 | # what the state values can be.  This module now has a means of processing both | 
 | 70 | # the old style state (i.e. OBMC_STATES_VERSION = 0) and the new style (i.e. | 
| Michael Walsh | 16cbb7f | 2017-02-02 15:54:16 -0600 | [diff] [blame] | 71 | # OBMC_STATES_VERSION = 1). | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 72 | # The caller can set environment variable OBMC_STATES_VERSION to dictate | 
 | 73 | # whether we're processing old or new style states.  If OBMC_STATES_VERSION is | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 74 | # not set it will default to 1. | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 75 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 76 | # As of the present moment, OBMC_STATES_VERSION of 0 is for cold that is so old | 
 | 77 | # that it is no longer worthwhile to maintain.  The OBMC_STATES_VERSION 0 code | 
 | 78 | # is being removed but the OBMC_STATES_VERSION value will stay for now in the | 
 | 79 | # event that it is needed in the future. | 
 | 80 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 81 | OBMC_STATES_VERSION = int(os.environ.get('OBMC_STATES_VERSION', 1)) | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 82 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 83 | # When a user calls get_state w/o specifying req_states, default_req_states | 
 | 84 | # is used as its value. | 
 | 85 | default_req_states = ['rest', | 
 | 86 |                       'chassis', | 
 | 87 |                       'bmc', | 
 | 88 |                       'boot_progress', | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 89 |                       'operating_system', | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 90 |                       'host', | 
 | 91 |                       'os_ping', | 
 | 92 |                       'os_login', | 
 | 93 |                       'os_run_cmd'] | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 94 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 95 | # valid_req_states is a list of sub states supported by the get_state function. | 
 | 96 | # valid_req_states, default_req_states and master_os_up_match are used by the | 
 | 97 | # get_state function. | 
 | 98 | valid_req_states = ['ping', | 
 | 99 |                     'packet_loss', | 
 | 100 |                     'uptime', | 
 | 101 |                     'epoch_seconds', | 
 | 102 |                     'rest', | 
 | 103 |                     'chassis', | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 104 |                     'requested_chassis', | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 105 |                     'bmc', | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 106 |                     'requested_bmc', | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 107 |                     'boot_progress', | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 108 |                     'operating_system', | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 109 |                     'host', | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 110 |                     'requested_host', | 
 | 111 |                     'attempts_left', | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 112 |                     'os_ping', | 
 | 113 |                     'os_login', | 
 | 114 |                     'os_run_cmd'] | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 115 |  | 
 | 116 | # valid_os_req_states and default_os_req_states are used by the os_get_state | 
 | 117 | # function. | 
 | 118 | # valid_os_req_states is a list of state information supported by the | 
 | 119 | # get_os_state function. | 
 | 120 | valid_os_req_states = ['os_ping', | 
 | 121 |                        'os_login', | 
 | 122 |                        'os_run_cmd'] | 
 | 123 | # When a user calls get_os_state w/o specifying req_states, | 
 | 124 | # default_os_req_states is used as its value. | 
 | 125 | default_os_req_states = ['os_ping', | 
 | 126 |                          'os_login', | 
 | 127 |                          'os_run_cmd'] | 
 | 128 |  | 
 | 129 | # Presently, some BMCs appear to not keep time very well.  This environment | 
 | 130 | # variable directs the get_state function to use either the BMC's epoch time | 
 | 131 | # or the local epoch time. | 
 | 132 | USE_BMC_EPOCH_TIME = int(os.environ.get('USE_BMC_EPOCH_TIME', 0)) | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 133 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 134 | # Useful state constant definition(s). | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 135 | # default_state is an initial value which may be of use to callers. | 
 | 136 | default_state = DotDict([('rest', '1'), | 
 | 137 |                          ('chassis', 'On'), | 
 | 138 |                          ('bmc', 'Ready'), | 
| Michael Walsh | 01975fa | 2017-08-20 20:51:36 -0500 | [diff] [blame] | 139 |                          ('boot_progress', 'OSStart'), | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 140 |                          ('operating_system', 'BootComplete'), | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 141 |                          ('host', 'Running'), | 
 | 142 |                          ('os_ping', '1'), | 
 | 143 |                          ('os_login', '1'), | 
 | 144 |                          ('os_run_cmd', '1')]) | 
 | 145 |  | 
| Michael Walsh | 7dc885b | 2018-03-14 17:51:59 -0500 | [diff] [blame] | 146 | # A match state for checking that the system is at "standby". | 
 | 147 | standby_match_state = DotDict([('rest', '^1$'), | 
 | 148 |                                ('chassis', '^Off$'), | 
 | 149 |                                ('bmc', '^Ready$'), | 
 | 150 |                                ('boot_progress', '^$'), | 
 | 151 |                                ('operating_system', '^$'), | 
 | 152 |                                ('host', '^$')]) | 
 | 153 |  | 
 | 154 | # A match state for checking that the system is at "os running". | 
 | 155 | os_running_match_state = DotDict([('chassis', '^On$'), | 
 | 156 |                                   ('bmc', '^Ready$'), | 
 | 157 |                                   ('boot_progress', | 
 | 158 |                                    'FW Progress, Starting OS|OSStart'), | 
 | 159 |                                   ('operating_system', 'BootComplete'), | 
 | 160 |                                   ('host', '^Running$'), | 
 | 161 |                                   ('os_ping', '^1$'), | 
 | 162 |                                   ('os_login', '^1$'), | 
 | 163 |                                   ('os_run_cmd', '^1$')]) | 
 | 164 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 165 | # A master dictionary to determine whether the os may be up. | 
 | 166 | master_os_up_match = DotDict([('chassis', '^On$'), | 
 | 167 |                               ('bmc', '^Ready$'), | 
 | 168 |                               ('boot_progress', | 
| Michael Walsh | 01975fa | 2017-08-20 20:51:36 -0500 | [diff] [blame] | 169 |                                'FW Progress, Starting OS|OSStart'), | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 170 |                               ('operating_system', 'BootComplete'), | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 171 |                               ('host', '^Running$')]) | 
 | 172 |  | 
| Michael Walsh | 45ca6e4 | 2017-09-14 17:29:12 -0500 | [diff] [blame] | 173 | invalid_state_match = DotDict([('rest', '^$'), | 
 | 174 |                                ('chassis', '^$'), | 
 | 175 |                                ('bmc', '^$'), | 
 | 176 |                                ('boot_progress', '^$'), | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 177 |                                ('operating_system', '^$'), | 
| Michael Walsh | 45ca6e4 | 2017-09-14 17:29:12 -0500 | [diff] [blame] | 178 |                                ('host', '^$')]) | 
 | 179 |  | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 180 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 181 | def return_state_constant(state_name='default'): | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 182 |     r""" | 
| Michael Walsh | 7dc885b | 2018-03-14 17:51:59 -0500 | [diff] [blame] | 183 |     Return the named state dictionary constant. | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 184 |     """ | 
 | 185 |  | 
| Michael Walsh | 7dc885b | 2018-03-14 17:51:59 -0500 | [diff] [blame] | 186 |     cmd_buf = "state = " + state_name | 
 | 187 |     exec(cmd_buf) | 
 | 188 |     return state | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 189 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 190 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 191 | def anchor_state(state): | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 192 |     r""" | 
 | 193 |     Add regular expression anchors ("^" and "$") to the beginning and end of | 
 | 194 |     each item in the state dictionary passed in.  Return the resulting | 
 | 195 |     dictionary. | 
 | 196 |  | 
 | 197 |     Description of Arguments: | 
 | 198 |     state    A dictionary such as the one returned by the get_state() | 
 | 199 |              function. | 
 | 200 |     """ | 
 | 201 |  | 
| Michael Walsh | 2ce067a | 2017-02-27 14:24:07 -0600 | [diff] [blame] | 202 |     anchored_state = state.copy() | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 203 |     for key, match_state_value in anchored_state.items(): | 
 | 204 |         anchored_state[key] = "^" + str(anchored_state[key]) + "$" | 
 | 205 |  | 
 | 206 |     return anchored_state | 
 | 207 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 208 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 209 | def strip_anchor_state(state): | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 210 |     r""" | 
 | 211 |     Strip regular expression anchors ("^" and "$") from the beginning and end | 
 | 212 |     of each item in the state dictionary passed in.  Return the resulting | 
 | 213 |     dictionary. | 
 | 214 |  | 
 | 215 |     Description of Arguments: | 
 | 216 |     state    A dictionary such as the one returned by the get_state() | 
 | 217 |              function. | 
 | 218 |     """ | 
 | 219 |  | 
| Michael Walsh | 2ce067a | 2017-02-27 14:24:07 -0600 | [diff] [blame] | 220 |     stripped_state = state.copy() | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 221 |     for key, match_state_value in stripped_state.items(): | 
 | 222 |         stripped_state[key] = stripped_state[key].strip("^$") | 
 | 223 |  | 
 | 224 |     return stripped_state | 
 | 225 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 226 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 227 | def compare_states(state, | 
| Michael Walsh | 45ca6e4 | 2017-09-14 17:29:12 -0500 | [diff] [blame] | 228 |                    match_state, | 
 | 229 |                    match_type='and'): | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 230 |     r""" | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 231 |     Compare 2 state dictionaries.  Return True if they match and False if they | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 232 |     don't.  Note that the match_state dictionary does not need to have an entry | 
 | 233 |     corresponding to each entry in the state dictionary.  But for each entry | 
 | 234 |     that it does have, the corresponding state entry will be checked for a | 
 | 235 |     match. | 
 | 236 |  | 
 | 237 |     Description of arguments: | 
 | 238 |     state           A state dictionary such as the one returned by the | 
 | 239 |                     get_state function. | 
 | 240 |     match_state     A dictionary whose key/value pairs are "state field"/ | 
 | 241 |                     "state value".  The state value is interpreted as a | 
 | 242 |                     regular expression.  Every value in this dictionary is | 
| Michael Walsh | 45ca6e4 | 2017-09-14 17:29:12 -0500 | [diff] [blame] | 243 |                     considered.  When match_type is 'and', if each and every | 
 | 244 |                     comparison matches, the two dictionaries are considered to | 
 | 245 |                     be matching.  If match_type is 'or', if any two of the | 
 | 246 |                     elements compared match, the two dictionaries are | 
 | 247 |                     considered to be matching. | 
| Michael Walsh | 7dc885b | 2018-03-14 17:51:59 -0500 | [diff] [blame] | 248 |                     This value may also be any string accepted by | 
 | 249 |                     return_state_constant (e.g. "standby_match_state"). | 
 | 250 |                     In such a case this function will call | 
 | 251 |                     return_state_constant to convert it to a proper | 
 | 252 |                     dictionary as described above. | 
| Michael Walsh | 45ca6e4 | 2017-09-14 17:29:12 -0500 | [diff] [blame] | 253 |     match_type      This may be 'and' or 'or'. | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 254 |     """ | 
 | 255 |  | 
| Michael Walsh | 45ca6e4 | 2017-09-14 17:29:12 -0500 | [diff] [blame] | 256 |     error_message = gv.svalid_value(match_type, var_name="match_type", | 
 | 257 |                                     valid_values=['and', 'or']) | 
 | 258 |     if error_message != "": | 
 | 259 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
 | 260 |  | 
| Michael Walsh | 7dc885b | 2018-03-14 17:51:59 -0500 | [diff] [blame] | 261 |     if type(match_state) in (str, unicode): | 
 | 262 |         match_state = return_state_constant(match_state) | 
 | 263 |  | 
| Michael Walsh | 45ca6e4 | 2017-09-14 17:29:12 -0500 | [diff] [blame] | 264 |     default_match = (match_type == 'and') | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 265 |     for key, match_state_value in match_state.items(): | 
| Michael Walsh | 97df71c | 2017-03-27 14:33:24 -0500 | [diff] [blame] | 266 |         # Blank match_state_value means "don't care". | 
 | 267 |         if match_state_value == "": | 
 | 268 |             continue | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 269 |         try: | 
| Michael Walsh | 45ca6e4 | 2017-09-14 17:29:12 -0500 | [diff] [blame] | 270 |             match = (re.match(match_state_value, str(state[key])) is not None) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 271 |         except KeyError: | 
 | 272 |             match = False | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 273 |  | 
| Michael Walsh | 45ca6e4 | 2017-09-14 17:29:12 -0500 | [diff] [blame] | 274 |         if match != default_match: | 
 | 275 |             return match | 
 | 276 |  | 
 | 277 |     return default_match | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 278 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 279 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 280 | def get_os_state(os_host="", | 
 | 281 |                  os_username="", | 
 | 282 |                  os_password="", | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 283 |                  req_states=default_os_req_states, | 
 | 284 |                  os_up=True, | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 285 |                  quiet=None): | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 286 |     r""" | 
 | 287 |     Get component states for the operating system such as ping, login, | 
 | 288 |     etc, put them into a dictionary and return them to the caller. | 
 | 289 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 290 |     Note that all substate values are strings. | 
 | 291 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 292 |     Description of arguments: | 
 | 293 |     os_host      The DNS name or IP address of the operating system. | 
 | 294 |                  This defaults to global ${OS_HOST}. | 
 | 295 |     os_username  The username to be used to login to the OS. | 
 | 296 |                  This defaults to global ${OS_USERNAME}. | 
 | 297 |     os_password  The password to be used to login to the OS. | 
 | 298 |                  This defaults to global ${OS_PASSWORD}. | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 299 |     req_states   This is a list of states whose values are being requested by | 
 | 300 |                  the caller. | 
 | 301 |     os_up        If the caller knows that the os can't possibly be up, it can | 
 | 302 |                  improve performance by passing os_up=False.  This function | 
 | 303 |                  will then simply return default values for all requested os | 
 | 304 |                  sub states. | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 305 |     quiet        Indicates whether status details (e.g. curl commands) should | 
 | 306 |                  be written to the console. | 
 | 307 |                  Defaults to either global value of ${QUIET} or to 1. | 
 | 308 |     """ | 
 | 309 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 310 |     quiet = int(gp.get_var_value(quiet, 0)) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 311 |  | 
 | 312 |     # Set parm defaults where necessary and validate all parms. | 
 | 313 |     if os_host == "": | 
 | 314 |         os_host = BuiltIn().get_variable_value("${OS_HOST}") | 
 | 315 |     error_message = gv.svalid_value(os_host, var_name="os_host", | 
 | 316 |                                     invalid_values=[None, ""]) | 
 | 317 |     if error_message != "": | 
 | 318 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
 | 319 |  | 
 | 320 |     if os_username == "": | 
 | 321 |         os_username = BuiltIn().get_variable_value("${OS_USERNAME}") | 
 | 322 |     error_message = gv.svalid_value(os_username, var_name="os_username", | 
 | 323 |                                     invalid_values=[None, ""]) | 
 | 324 |     if error_message != "": | 
 | 325 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
 | 326 |  | 
 | 327 |     if os_password == "": | 
 | 328 |         os_password = BuiltIn().get_variable_value("${OS_PASSWORD}") | 
 | 329 |     error_message = gv.svalid_value(os_password, var_name="os_password", | 
 | 330 |                                     invalid_values=[None, ""]) | 
 | 331 |     if error_message != "": | 
 | 332 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
 | 333 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 334 |     invalid_req_states = [sub_state for sub_state in req_states | 
 | 335 |                           if sub_state not in valid_os_req_states] | 
 | 336 |     if len(invalid_req_states) > 0: | 
 | 337 |         error_message = "The following req_states are not supported:\n" +\ | 
 | 338 |             gp.sprint_var(invalid_req_states) | 
 | 339 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 340 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 341 |     # Initialize all substate values supported by this function. | 
 | 342 |     os_ping = 0 | 
 | 343 |     os_login = 0 | 
 | 344 |     os_run_cmd = 0 | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 345 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 346 |     if os_up: | 
 | 347 |         if 'os_ping' in req_states: | 
 | 348 |             # See if the OS pings. | 
 | 349 |             cmd_buf = "ping -c 1 -w 2 " + os_host | 
 | 350 |             if not quiet: | 
| Michael Walsh | 3eb5002 | 2017-03-21 11:27:30 -0500 | [diff] [blame] | 351 |                 gp.pissuing(cmd_buf) | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 352 |             rc, out_buf = commands.getstatusoutput(cmd_buf) | 
 | 353 |             if rc == 0: | 
 | 354 |                 os_ping = 1 | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 355 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 356 |         # Programming note: All attributes which do not require an ssh login | 
 | 357 |         # should have been processed by this point. | 
 | 358 |         master_req_login = ['os_login', 'os_run_cmd'] | 
 | 359 |         req_login = [sub_state for sub_state in req_states if sub_state in | 
 | 360 |                      master_req_login] | 
| Michael Walsh | 97df71c | 2017-03-27 14:33:24 -0500 | [diff] [blame] | 361 |         must_login = (len(req_login) > 0) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 362 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 363 |         if must_login: | 
| Michael Walsh | 6a9bd14 | 2018-07-24 16:10:29 -0500 | [diff] [blame] | 364 |             output, stderr, rc = bsu.os_execute_command("uptime", quiet=quiet, | 
 | 365 |                                                         ignore_err=1) | 
 | 366 |             if rc == 0: | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 367 |                 os_login = 1 | 
| Michael Walsh | 6a9bd14 | 2018-07-24 16:10:29 -0500 | [diff] [blame] | 368 |                 os_run_cmd = 1 | 
| Michael Walsh | 3eb5002 | 2017-03-21 11:27:30 -0500 | [diff] [blame] | 369 |             else: | 
| Michael Walsh | 6a9bd14 | 2018-07-24 16:10:29 -0500 | [diff] [blame] | 370 |                 gp.dprint_vars(output, stderr) | 
 | 371 |                 gp.dprint_vars(rc, 1) | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 372 |  | 
 | 373 |     os_state = DotDict() | 
 | 374 |     for sub_state in req_states: | 
 | 375 |         cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")" | 
 | 376 |         exec(cmd_buf) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 377 |  | 
 | 378 |     return os_state | 
 | 379 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 380 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 381 | def get_state(openbmc_host="", | 
 | 382 |               openbmc_username="", | 
 | 383 |               openbmc_password="", | 
 | 384 |               os_host="", | 
 | 385 |               os_username="", | 
 | 386 |               os_password="", | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 387 |               req_states=default_req_states, | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 388 |               quiet=None): | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 389 |     r""" | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 390 |     Get component states such as chassis state, bmc state, etc, put them into a | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 391 |     dictionary and return them to the caller. | 
 | 392 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 393 |     Note that all substate values are strings. | 
 | 394 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 395 |     Description of arguments: | 
 | 396 |     openbmc_host      The DNS name or IP address of the BMC. | 
 | 397 |                       This defaults to global ${OPENBMC_HOST}. | 
 | 398 |     openbmc_username  The username to be used to login to the BMC. | 
 | 399 |                       This defaults to global ${OPENBMC_USERNAME}. | 
 | 400 |     openbmc_password  The password to be used to login to the BMC. | 
 | 401 |                       This defaults to global ${OPENBMC_PASSWORD}. | 
 | 402 |     os_host           The DNS name or IP address of the operating system. | 
 | 403 |                       This defaults to global ${OS_HOST}. | 
 | 404 |     os_username       The username to be used to login to the OS. | 
 | 405 |                       This defaults to global ${OS_USERNAME}. | 
 | 406 |     os_password       The password to be used to login to the OS. | 
 | 407 |                       This defaults to global ${OS_PASSWORD}. | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 408 |     req_states        This is a list of states whose values are being requested | 
 | 409 |                       by the caller. | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 410 |     quiet             Indicates whether status details (e.g. curl commands) | 
 | 411 |                       should be written to the console. | 
 | 412 |                       Defaults to either global value of ${QUIET} or to 1. | 
 | 413 |     """ | 
 | 414 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 415 |     quiet = int(gp.get_var_value(quiet, 0)) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 416 |  | 
 | 417 |     # Set parm defaults where necessary and validate all parms. | 
 | 418 |     if openbmc_host == "": | 
 | 419 |         openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}") | 
 | 420 |     error_message = gv.svalid_value(openbmc_host, | 
 | 421 |                                     var_name="openbmc_host", | 
 | 422 |                                     invalid_values=[None, ""]) | 
 | 423 |     if error_message != "": | 
 | 424 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
 | 425 |  | 
 | 426 |     if openbmc_username == "": | 
 | 427 |         openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}") | 
 | 428 |     error_message = gv.svalid_value(openbmc_username, | 
 | 429 |                                     var_name="openbmc_username", | 
 | 430 |                                     invalid_values=[None, ""]) | 
 | 431 |     if error_message != "": | 
 | 432 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
 | 433 |  | 
 | 434 |     if openbmc_password == "": | 
 | 435 |         openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}") | 
 | 436 |     error_message = gv.svalid_value(openbmc_password, | 
 | 437 |                                     var_name="openbmc_password", | 
 | 438 |                                     invalid_values=[None, ""]) | 
 | 439 |     if error_message != "": | 
 | 440 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
 | 441 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 442 |     # NOTE: OS parms are optional. | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 443 |     if os_host == "": | 
 | 444 |         os_host = BuiltIn().get_variable_value("${OS_HOST}") | 
 | 445 |         if os_host is None: | 
 | 446 |             os_host = "" | 
 | 447 |  | 
 | 448 |     if os_username is "": | 
 | 449 |         os_username = BuiltIn().get_variable_value("${OS_USERNAME}") | 
 | 450 |         if os_username is None: | 
 | 451 |             os_username = "" | 
 | 452 |  | 
 | 453 |     if os_password is "": | 
 | 454 |         os_password = BuiltIn().get_variable_value("${OS_PASSWORD}") | 
 | 455 |         if os_password is None: | 
 | 456 |             os_password = "" | 
 | 457 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 458 |     invalid_req_states = [sub_state for sub_state in req_states | 
 | 459 |                           if sub_state not in valid_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)) | 
 | 464 |  | 
 | 465 |     # Initialize all substate values supported by this function. | 
 | 466 |     ping = 0 | 
 | 467 |     packet_loss = '' | 
 | 468 |     uptime = '' | 
 | 469 |     epoch_seconds = '' | 
| Michael Walsh | 2b269de | 2017-10-09 11:18:11 -0500 | [diff] [blame] | 470 |     rest = '' | 
 | 471 |     chassis = '' | 
 | 472 |     requested_chassis = '' | 
 | 473 |     bmc = '' | 
 | 474 |     requested_bmc = '' | 
 | 475 |     boot_progress = '' | 
 | 476 |     operating_system = '' | 
 | 477 |     host = '' | 
 | 478 |     requested_host = '' | 
 | 479 |     attempts_left = '' | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 480 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 481 |     # Get the component states. | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 482 |     if 'ping' in req_states: | 
 | 483 |         # See if the OS pings. | 
 | 484 |         cmd_buf = "ping -c 1 -w 2 " + openbmc_host | 
 | 485 |         if not quiet: | 
| Michael Walsh | 3eb5002 | 2017-03-21 11:27:30 -0500 | [diff] [blame] | 486 |             gp.pissuing(cmd_buf) | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 487 |         rc, out_buf = commands.getstatusoutput(cmd_buf) | 
 | 488 |         if rc == 0: | 
 | 489 |             ping = 1 | 
 | 490 |  | 
 | 491 |     if 'packet_loss' in req_states: | 
 | 492 |         # See if the OS pings. | 
 | 493 |         cmd_buf = "ping -c 5 -w 5 " + openbmc_host +\ | 
 | 494 |             " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'" | 
 | 495 |         if not quiet: | 
| Michael Walsh | 3eb5002 | 2017-03-21 11:27:30 -0500 | [diff] [blame] | 496 |             gp.pissuing(cmd_buf) | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 497 |         rc, out_buf = commands.getstatusoutput(cmd_buf) | 
 | 498 |         if rc == 0: | 
 | 499 |             packet_loss = out_buf.rstrip("\n") | 
 | 500 |  | 
| Michael Walsh | e53e47a | 2017-06-30 17:03:24 -0500 | [diff] [blame] | 501 |     if 'uptime' in req_states: | 
| Michael Walsh | fa76593 | 2017-10-13 14:07:22 -0500 | [diff] [blame] | 502 |         # Sometimes reading uptime results in a blank value. Call with | 
 | 503 |         # wait_until_keyword_succeeds to ensure a non-blank value is obtained. | 
 | 504 |         remote_cmd_buf = "read uptime filler 2>/dev/null < /proc/uptime" +\ | 
 | 505 |             " && [ ! -z \"${uptime}\" ] && echo ${uptime}" | 
| Joy Onyerikwu | 004ad3c | 2018-06-11 16:29:56 -0500 | [diff] [blame] | 506 |         cmd_buf = ["BMC Execute Command", | 
| Michael Walsh | 8c34eb7 | 2018-06-14 11:26:16 -0500 | [diff] [blame] | 507 |                    re.sub('\\$', '\\$', remote_cmd_buf), 'quiet=1'] | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 508 |         if not quiet: | 
 | 509 |             grp.rpissuing_keyword(cmd_buf) | 
| Michael Walsh | fa76593 | 2017-10-13 14:07:22 -0500 | [diff] [blame] | 510 |             grp.rpissuing(remote_cmd_buf) | 
 | 511 |         try: | 
 | 512 |             stdout, stderr, rc =\ | 
| Michael Walsh | caccd85 | 2017-11-01 17:58:41 -0500 | [diff] [blame] | 513 |                 BuiltIn().wait_until_keyword_succeeds("10 sec", "0 sec", | 
| Michael Walsh | fa76593 | 2017-10-13 14:07:22 -0500 | [diff] [blame] | 514 |                                                       *cmd_buf) | 
| Michael Walsh | 97df71c | 2017-03-27 14:33:24 -0500 | [diff] [blame] | 515 |             if rc == 0 and stderr == "": | 
 | 516 |                 uptime = stdout | 
| Michael Walsh | fa76593 | 2017-10-13 14:07:22 -0500 | [diff] [blame] | 517 |         except AssertionError as my_assertion_error: | 
 | 518 |             pass | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 519 |  | 
| Michael Walsh | e53e47a | 2017-06-30 17:03:24 -0500 | [diff] [blame] | 520 |     if 'epoch_seconds' in req_states: | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 521 |         date_cmd_buf = "date -u +%s" | 
 | 522 |         if USE_BMC_EPOCH_TIME: | 
| Michael Walsh | e53e47a | 2017-06-30 17:03:24 -0500 | [diff] [blame] | 523 |             cmd_buf = ["BMC Execute Command", date_cmd_buf, 'quiet=${1}'] | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 524 |             if not quiet: | 
 | 525 |                 grp.rpissuing_keyword(cmd_buf) | 
| Michael Walsh | 97df71c | 2017-03-27 14:33:24 -0500 | [diff] [blame] | 526 |             status, ret_values = \ | 
 | 527 |                 BuiltIn().run_keyword_and_ignore_error(*cmd_buf) | 
 | 528 |             if status == "PASS": | 
 | 529 |                 stdout, stderr, rc = ret_values | 
 | 530 |                 if rc == 0 and stderr == "": | 
 | 531 |                     epoch_seconds = stdout.rstrip("\n") | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 532 |         else: | 
 | 533 |             shell_rc, out_buf = gc.cmd_fnc_u(date_cmd_buf, | 
| Michael Walsh | fa76593 | 2017-10-13 14:07:22 -0500 | [diff] [blame] | 534 |                                              quiet=quiet, | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 535 |                                              print_output=0) | 
 | 536 |             if shell_rc == 0: | 
 | 537 |                 epoch_seconds = out_buf.rstrip("\n") | 
 | 538 |  | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 539 |     master_req_rest = ['rest', 'host', 'requested_host', 'operating_system', | 
 | 540 |                        'attempts_left', 'boot_progress', 'chassis', | 
 | 541 |                        'requested_chassis' 'bmc' 'requested_bmc'] | 
 | 542 |  | 
| Michael Walsh | b95eb54 | 2017-03-31 09:39:20 -0500 | [diff] [blame] | 543 |     req_rest = [sub_state for sub_state in req_states if sub_state in | 
 | 544 |                 master_req_rest] | 
 | 545 |     need_rest = (len(req_rest) > 0) | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 546 |     state = DotDict() | 
 | 547 |     if need_rest: | 
| Michael Walsh | 940d691 | 2017-10-27 12:32:33 -0500 | [diff] [blame] | 548 |         cmd_buf = ["Read Properties", SYSTEM_STATE_URI + "enumerate", | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 549 |                    "quiet=${" + str(quiet) + "}"] | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 550 |         grp.rdpissuing_keyword(cmd_buf) | 
| Michael Walsh | 97df71c | 2017-03-27 14:33:24 -0500 | [diff] [blame] | 551 |         status, ret_values = \ | 
 | 552 |             BuiltIn().run_keyword_and_ignore_error(*cmd_buf) | 
 | 553 |         if status == "PASS": | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 554 |             state['rest'] = '1' | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 555 |         else: | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 556 |             state['rest'] = '0' | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 557 |  | 
| Michael Walsh | 2b269de | 2017-10-09 11:18:11 -0500 | [diff] [blame] | 558 |         if int(state['rest']): | 
 | 559 |             for url_path in ret_values: | 
 | 560 |                 for attr_name in ret_values[url_path]: | 
 | 561 |                     # Create a state key value based on the attr_name. | 
| Joy Onyerikwu | 004ad3c | 2018-06-11 16:29:56 -0500 | [diff] [blame] | 562 |                     if isinstance(ret_values[url_path][attr_name], unicode): | 
| Michael Walsh | 2b269de | 2017-10-09 11:18:11 -0500 | [diff] [blame] | 563 |                         ret_values[url_path][attr_name] = \ | 
 | 564 |                             re.sub(r'.*\.', "", | 
 | 565 |                                    ret_values[url_path][attr_name]) | 
 | 566 |                     # Do some key name manipulations. | 
 | 567 |                     new_attr_name = re.sub(r'^Current|(State|Transition)$', | 
 | 568 |                                            "", attr_name) | 
 | 569 |                     new_attr_name = re.sub(r'BMC', r'Bmc', new_attr_name) | 
 | 570 |                     new_attr_name = re.sub(r'([A-Z][a-z])', r'_\1', | 
 | 571 |                                            new_attr_name) | 
 | 572 |                     new_attr_name = new_attr_name.lower().lstrip("_") | 
 | 573 |                     new_attr_name = re.sub(r'power', r'chassis', new_attr_name) | 
 | 574 |                     if new_attr_name in req_states: | 
 | 575 |                         state[new_attr_name] = ret_values[url_path][attr_name] | 
| Michael Walsh | b95eb54 | 2017-03-31 09:39:20 -0500 | [diff] [blame] | 576 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 577 |     for sub_state in req_states: | 
| Michael Walsh | 5674922 | 2017-09-29 15:26:07 -0500 | [diff] [blame] | 578 |         if sub_state in state: | 
 | 579 |             continue | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 580 |         if sub_state.startswith("os_"): | 
 | 581 |             # We pass "os_" requests on to get_os_state. | 
 | 582 |             continue | 
 | 583 |         cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")" | 
 | 584 |         exec(cmd_buf) | 
 | 585 |  | 
 | 586 |     if os_host == "": | 
 | 587 |         # The caller has not specified an os_host so as far as we're concerned, | 
 | 588 |         # it doesn't exist. | 
 | 589 |         return state | 
 | 590 |  | 
 | 591 |     os_req_states = [sub_state for sub_state in req_states | 
 | 592 |                      if sub_state.startswith('os_')] | 
 | 593 |  | 
 | 594 |     if len(os_req_states) > 0: | 
 | 595 |         # The caller has specified an os_host and they have requested | 
 | 596 |         # information on os substates. | 
 | 597 |  | 
 | 598 |         # Based on the information gathered on bmc, we'll try to make a | 
 | 599 |         # determination of whether the os is even up.  We'll pass the result | 
 | 600 |         # of that assessment to get_os_state to enhance performance. | 
 | 601 |         os_up_match = DotDict() | 
 | 602 |         for sub_state in master_os_up_match: | 
 | 603 |             if sub_state in req_states: | 
 | 604 |                 os_up_match[sub_state] = master_os_up_match[sub_state] | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 605 |         os_up = compare_states(state, os_up_match) | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 606 |         os_state = get_os_state(os_host=os_host, | 
 | 607 |                                 os_username=os_username, | 
 | 608 |                                 os_password=os_password, | 
 | 609 |                                 req_states=os_req_states, | 
 | 610 |                                 os_up=os_up, | 
 | 611 |                                 quiet=quiet) | 
 | 612 |         # Append os_state dictionary to ours. | 
 | 613 |         state.update(os_state) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 614 |  | 
 | 615 |     return state | 
 | 616 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 617 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 618 | def check_state(match_state, | 
 | 619 |                 invert=0, | 
 | 620 |                 print_string="", | 
 | 621 |                 openbmc_host="", | 
 | 622 |                 openbmc_username="", | 
 | 623 |                 openbmc_password="", | 
 | 624 |                 os_host="", | 
 | 625 |                 os_username="", | 
 | 626 |                 os_password="", | 
 | 627 |                 quiet=None): | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 628 |     r""" | 
 | 629 |     Check that the Open BMC machine's composite state matches the specified | 
 | 630 |     state.  On success, this keyword returns the machine's composite state as a | 
 | 631 |     dictionary. | 
 | 632 |  | 
 | 633 |     Description of arguments: | 
 | 634 |     match_state       A dictionary whose key/value pairs are "state field"/ | 
 | 635 |                       "state value".  The state value is interpreted as a | 
 | 636 |                       regular expression.  Example call from robot: | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 637 |                       ${match_state}=  Create Dictionary  chassis=^On$ | 
 | 638 |                       ...  bmc=^Ready$ | 
| Michael Walsh | 01975fa | 2017-08-20 20:51:36 -0500 | [diff] [blame] | 639 |                       ...  boot_progress=^OSStart$ | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 640 |                       ${state}=  Check State  &{match_state} | 
 | 641 |     invert            If this flag is set, this function will succeed if the | 
 | 642 |                       states do NOT match. | 
 | 643 |     print_string      This function will print this string to the console prior | 
 | 644 |                       to getting the state. | 
 | 645 |     openbmc_host      The DNS name or IP address of the BMC. | 
 | 646 |                       This defaults to global ${OPENBMC_HOST}. | 
 | 647 |     openbmc_username  The username to be used to login to the BMC. | 
 | 648 |                       This defaults to global ${OPENBMC_USERNAME}. | 
 | 649 |     openbmc_password  The password to be used to login to the BMC. | 
 | 650 |                       This defaults to global ${OPENBMC_PASSWORD}. | 
 | 651 |     os_host           The DNS name or IP address of the operating system. | 
 | 652 |                       This defaults to global ${OS_HOST}. | 
 | 653 |     os_username       The username to be used to login to the OS. | 
 | 654 |                       This defaults to global ${OS_USERNAME}. | 
 | 655 |     os_password       The password to be used to login to the OS. | 
 | 656 |                       This defaults to global ${OS_PASSWORD}. | 
 | 657 |     quiet             Indicates whether status details should be written to the | 
 | 658 |                       console.  Defaults to either global value of ${QUIET} or | 
 | 659 |                       to 1. | 
 | 660 |     """ | 
 | 661 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 662 |     quiet = int(gp.get_var_value(quiet, 0)) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 663 |  | 
 | 664 |     grp.rprint(print_string) | 
 | 665 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 666 |     req_states = match_state.keys() | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 667 |     # Initialize state. | 
 | 668 |     state = get_state(openbmc_host=openbmc_host, | 
 | 669 |                       openbmc_username=openbmc_username, | 
 | 670 |                       openbmc_password=openbmc_password, | 
 | 671 |                       os_host=os_host, | 
 | 672 |                       os_username=os_username, | 
 | 673 |                       os_password=os_password, | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 674 |                       req_states=req_states, | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 675 |                       quiet=quiet) | 
 | 676 |     if not quiet: | 
| Michael Walsh | 3eb5002 | 2017-03-21 11:27:30 -0500 | [diff] [blame] | 677 |         gp.print_var(state) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 678 |  | 
 | 679 |     match = compare_states(state, match_state) | 
 | 680 |  | 
 | 681 |     if invert and match: | 
 | 682 |         fail_msg = "The current state of the machine matches the match" +\ | 
 | 683 |                    " state:\n" + gp.sprint_varx("state", state) | 
 | 684 |         BuiltIn().fail("\n" + gp.sprint_error(fail_msg)) | 
 | 685 |     elif not invert and not match: | 
 | 686 |         fail_msg = "The current state of the machine does NOT match the" +\ | 
 | 687 |                    " match state:\n" +\ | 
 | 688 |                    gp.sprint_varx("state", state) | 
 | 689 |         BuiltIn().fail("\n" + gp.sprint_error(fail_msg)) | 
 | 690 |  | 
 | 691 |     return state | 
 | 692 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 693 |  | 
| Michael Walsh | f893ba0 | 2017-01-10 10:28:05 -0600 | [diff] [blame] | 694 | def wait_state(match_state=(), | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 695 |                wait_time="1 min", | 
 | 696 |                interval="1 second", | 
 | 697 |                invert=0, | 
 | 698 |                openbmc_host="", | 
 | 699 |                openbmc_username="", | 
 | 700 |                openbmc_password="", | 
 | 701 |                os_host="", | 
 | 702 |                os_username="", | 
 | 703 |                os_password="", | 
 | 704 |                quiet=None): | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 705 |     r""" | 
 | 706 |     Wait for the Open BMC machine's composite state to match the specified | 
 | 707 |     state.  On success, this keyword returns the machine's composite state as | 
 | 708 |     a dictionary. | 
 | 709 |  | 
 | 710 |     Description of arguments: | 
 | 711 |     match_state       A dictionary whose key/value pairs are "state field"/ | 
 | 712 |                       "state value".  See check_state (above) for details. | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 713 |                       This value may also be any string accepted by | 
 | 714 |                       return_state_constant (e.g. "standby_match_state"). | 
 | 715 |                       In such a case this function will call | 
 | 716 |                       return_state_constant to convert it to a proper | 
 | 717 |                       dictionary as described above. | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 718 |     wait_time         The total amount of time to wait for the desired state. | 
 | 719 |                       This value may be expressed in Robot Framework's time | 
 | 720 |                       format (e.g. 1 minute, 2 min 3 s, 4.5). | 
 | 721 |     interval          The amount of time between state checks. | 
 | 722 |                       This value may be expressed in Robot Framework's time | 
 | 723 |                       format (e.g. 1 minute, 2 min 3 s, 4.5). | 
 | 724 |     invert            If this flag is set, this function will for the state of | 
 | 725 |                       the machine to cease to match the match state. | 
 | 726 |     openbmc_host      The DNS name or IP address of the BMC. | 
 | 727 |                       This defaults to global ${OPENBMC_HOST}. | 
 | 728 |     openbmc_username  The username to be used to login to the BMC. | 
 | 729 |                       This defaults to global ${OPENBMC_USERNAME}. | 
 | 730 |     openbmc_password  The password to be used to login to the BMC. | 
 | 731 |                       This defaults to global ${OPENBMC_PASSWORD}. | 
 | 732 |     os_host           The DNS name or IP address of the operating system. | 
 | 733 |                       This defaults to global ${OS_HOST}. | 
 | 734 |     os_username       The username to be used to login to the OS. | 
 | 735 |                       This defaults to global ${OS_USERNAME}. | 
 | 736 |     os_password       The password to be used to login to the OS. | 
 | 737 |                       This defaults to global ${OS_PASSWORD}. | 
 | 738 |     quiet             Indicates whether status details should be written to the | 
 | 739 |                       console.  Defaults to either global value of ${QUIET} or | 
 | 740 |                       to 1. | 
 | 741 |     """ | 
 | 742 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 743 |     quiet = int(gp.get_var_value(quiet, 0)) | 
 | 744 |  | 
 | 745 |     if type(match_state) in (str, unicode): | 
 | 746 |         match_state = return_state_constant(match_state) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 747 |  | 
 | 748 |     if not quiet: | 
 | 749 |         if invert: | 
 | 750 |             alt_text = "cease to " | 
 | 751 |         else: | 
 | 752 |             alt_text = "" | 
| Joy Onyerikwu | 004ad3c | 2018-06-11 16:29:56 -0500 | [diff] [blame] | 753 |         gp.print_timen("Checking every " + str(interval) + " for up to " | 
 | 754 |                        + str(wait_time) + " for the state of the machine to " | 
 | 755 |                        + alt_text + "match the state shown below.") | 
| Michael Walsh | 3eb5002 | 2017-03-21 11:27:30 -0500 | [diff] [blame] | 756 |         gp.print_var(match_state) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 757 |  | 
| Michael Walsh | f893ba0 | 2017-01-10 10:28:05 -0600 | [diff] [blame] | 758 |     if quiet: | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 759 |         print_string = "" | 
| Michael Walsh | f893ba0 | 2017-01-10 10:28:05 -0600 | [diff] [blame] | 760 |     else: | 
| Michael Walsh | 341c21e | 2017-01-17 16:25:20 -0600 | [diff] [blame] | 761 |         print_string = "#" | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 762 |  | 
 | 763 |     debug = int(BuiltIn().get_variable_value("${debug}", "0")) | 
 | 764 |     if debug: | 
 | 765 |         # In debug we print state so no need to print the "#". | 
 | 766 |         print_string = "" | 
 | 767 |     check_state_quiet = 1 - debug | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 768 |     cmd_buf = ["Check State", match_state, "invert=${" + str(invert) + "}", | 
| Michael Walsh | f893ba0 | 2017-01-10 10:28:05 -0600 | [diff] [blame] | 769 |                "print_string=" + print_string, "openbmc_host=" + openbmc_host, | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 770 |                "openbmc_username=" + openbmc_username, | 
 | 771 |                "openbmc_password=" + openbmc_password, "os_host=" + os_host, | 
 | 772 |                "os_username=" + os_username, "os_password=" + os_password, | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 773 |                "quiet=${" + str(check_state_quiet) + "}"] | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 774 |     grp.rdpissuing_keyword(cmd_buf) | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 775 |     try: | 
 | 776 |         state = BuiltIn().wait_until_keyword_succeeds(wait_time, interval, | 
 | 777 |                                                       *cmd_buf) | 
 | 778 |     except AssertionError as my_assertion_error: | 
 | 779 |         gp.printn() | 
 | 780 |         message = my_assertion_error.args[0] | 
 | 781 |         BuiltIn().fail(message) | 
 | 782 |  | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 783 |     if not quiet: | 
| Michael Walsh | 3eb5002 | 2017-03-21 11:27:30 -0500 | [diff] [blame] | 784 |         gp.printn() | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 785 |         if invert: | 
| Michael Walsh | 3eb5002 | 2017-03-21 11:27:30 -0500 | [diff] [blame] | 786 |             gp.print_timen("The states no longer match:") | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 787 |         else: | 
| Michael Walsh | 3eb5002 | 2017-03-21 11:27:30 -0500 | [diff] [blame] | 788 |             gp.print_timen("The states match:") | 
 | 789 |         gp.print_var(state) | 
| Michael Walsh | 70369fd | 2016-11-22 11:25:57 -0600 | [diff] [blame] | 790 |  | 
 | 791 |     return state | 
 | 792 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 793 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 794 | def wait_for_comm_cycle(start_boot_seconds, | 
 | 795 |                         quiet=None): | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 796 |     r""" | 
 | 797 |     Wait for communications to the BMC to stop working and then resume working. | 
 | 798 |     This function is useful when you have initiated some kind of reboot. | 
 | 799 |  | 
 | 800 |     Description of arguments: | 
 | 801 |     start_boot_seconds  The time that the boot test started.  The format is the | 
 | 802 |                         epoch time in seconds, i.e. the number of seconds since | 
 | 803 |                         1970-01-01 00:00:00 UTC.  This value should be obtained | 
 | 804 |                         from the BMC so that it is not dependent on any kind of | 
 | 805 |                         synchronization between this machine and the target BMC | 
 | 806 |                         This will allow this program to work correctly even in | 
 | 807 |                         a simulated environment.  This value should be obtained | 
 | 808 |                         by the caller prior to initiating a reboot.  It can be | 
 | 809 |                         obtained as follows: | 
 | 810 |                         state = st.get_state(req_states=['epoch_seconds']) | 
 | 811 |     """ | 
 | 812 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 813 |     quiet = int(gp.get_var_value(quiet, 0)) | 
 | 814 |  | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 815 |     # Validate parms. | 
 | 816 |     error_message = gv.svalid_integer(start_boot_seconds, | 
 | 817 |                                       var_name="start_boot_seconds") | 
 | 818 |     if error_message != "": | 
 | 819 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
 | 820 |  | 
 | 821 |     match_state = anchor_state(DotDict([('packet_loss', '100')])) | 
 | 822 |     # Wait for 100% packet loss trying to ping machine. | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 823 |     wait_state(match_state, wait_time="8 mins", interval="0 seconds") | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 824 |  | 
 | 825 |     match_state['packet_loss'] = '^0$' | 
 | 826 |     # Wait for 0% packet loss trying to ping machine. | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 827 |     wait_state(match_state, wait_time="8 mins", interval="0 seconds") | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 828 |  | 
 | 829 |     # Get the uptime and epoch seconds for comparisons.  We want to be sure | 
 | 830 |     # that the uptime is less than the elapsed boot time.  Further proof that | 
 | 831 |     # a reboot has indeed occurred (vs random network instability giving a | 
| Michael Walsh | c4c05d3 | 2018-05-29 11:39:39 -0500 | [diff] [blame] | 832 |     # false positive.  We also use wait_state because the BMC may take a short | 
 | 833 |     # while to be ready to process SSH requests. | 
| Joy Onyerikwu | 004ad3c | 2018-06-11 16:29:56 -0500 | [diff] [blame] | 834 |     match_state = DotDict([('uptime', '^[0-9\\.]+$'), | 
| Michael Walsh | c4c05d3 | 2018-05-29 11:39:39 -0500 | [diff] [blame] | 835 |                            ('epoch_seconds', '^[0-9]+$')]) | 
 | 836 |     state = wait_state(match_state, wait_time="2 mins", interval="1 second") | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 837 |  | 
 | 838 |     elapsed_boot_time = int(state['epoch_seconds']) - start_boot_seconds | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 839 |     gp.qprint_var(elapsed_boot_time) | 
| Michael Walsh | e77585a | 2017-12-14 11:02:28 -0600 | [diff] [blame] | 840 |     if state['uptime'] == "": | 
 | 841 |         error_message = "Unable to obtain uptime from the BMC. BMC is not" +\ | 
 | 842 |             " communicating." | 
 | 843 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 844 |     if int(float(state['uptime'])) < elapsed_boot_time: | 
 | 845 |         uptime = state['uptime'] | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 846 |         gp.qprint_var(uptime) | 
| Joy Onyerikwu | 004ad3c | 2018-06-11 16:29:56 -0500 | [diff] [blame] | 847 |         gp.qprint_timen("The uptime is less than the elapsed boot time," | 
 | 848 |                         + " as expected.") | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 849 |     else: | 
 | 850 |         error_message = "The uptime is greater than the elapsed boot time," +\ | 
 | 851 |                         " which is unexpected:\n" +\ | 
 | 852 |                         gp.sprint_var(start_boot_seconds) +\ | 
 | 853 |                         gp.sprint_var(state) | 
 | 854 |         BuiltIn().fail(gp.sprint_error(error_message)) | 
 | 855 |  | 
| Michael Walsh | 619aa33 | 2017-04-12 15:56:51 -0500 | [diff] [blame] | 856 |     gp.qprint_timen("Verifying that REST API interface is working.") | 
| Michael Walsh | b95eb54 | 2017-03-31 09:39:20 -0500 | [diff] [blame] | 857 |     match_state = DotDict([('rest', '^1$')]) | 
| Michael Walsh | 8fae6ea | 2017-02-20 16:14:44 -0600 | [diff] [blame] | 858 |     state = wait_state(match_state, wait_time="5 mins", interval="2 seconds") |