1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
|
# Copyright (c) 2014 Kontron Europe GmbH
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from __future__ import absolute_import
from enum import Enum
from .msgs import create_request_by_name
from .utils import check_completion_code, check_rsp_completion_code, ByteBuffer
from .state import State
from .msgs.chassis import \
CONTROL_POWER_DOWN, CONTROL_POWER_UP, CONTROL_POWER_CYCLE, \
CONTROL_HARD_RESET, CONTROL_DIAGNOSTIC_INTERRUPT, \
CONTROL_SOFT_SHUTDOWN
BOOT_PARAMETER_SET_IN_PROGRESS = 0
BOOT_PARAMETER_SERVICE_PARTITION_SELECTOR = 1
BOOT_PARAMETER_SERVICE_PARTITION_SCAN = 2
BOOT_PARAMETER_BMC_BOOT_FLAG_VALID_BIT_CLEARING = 3
BOOT_PARAMETER_BOOT_INFO_ACKNOWLEDGE = 4
BOOT_PARAMETER_BOOT_FLAGS = 5
BOOT_PARAMETER_BOOT_INITIATOR_INFO = 6
BOOT_PARAMETER_BOOT_INITIATOR_MAILBOX = 7
class BootDevice(str, Enum):
NO_OVERRIDE = "no override",
PXE = "pxe",
DEFAULT_HDD = "default hard drive",
DEFAULT_HDD_SAFE = "default hard drive safe mode",
DIAGNOSTIC = "diagnostic partition",
CD = "cd",
BIOS = "bios setup",
REMOTE_USB = "remote removable media",
PRIMARY_REMOTE = "primary remote media",
REMOTE_CD = "remote cd",
REMOTE_HDD = "remote hard drive",
PRIMARY_USB = "primary removable media (usb)"
CONVERT_RAW_TO_BOOT_DEVICE = {
0: BootDevice.NO_OVERRIDE,
1: BootDevice.PXE,
2: BootDevice.DEFAULT_HDD,
3: BootDevice.DEFAULT_HDD_SAFE,
4: BootDevice.DIAGNOSTIC,
5: BootDevice.CD,
6: BootDevice.BIOS,
7: BootDevice.REMOTE_USB,
8: BootDevice.PRIMARY_REMOTE,
9: BootDevice.REMOTE_CD,
11: BootDevice.REMOTE_HDD,
15: BootDevice.PRIMARY_USB
}
CONVERT_BOOT_DEVICE_TO_RAW = {
BootDevice.NO_OVERRIDE: 0b0000,
BootDevice.PXE: 0b0001,
BootDevice.DEFAULT_HDD: 0b0010,
BootDevice.DEFAULT_HDD_SAFE: 0b0011,
BootDevice.DIAGNOSTIC: 0b0100,
BootDevice.CD: 0b0101,
BootDevice.BIOS: 0b0110,
BootDevice.REMOTE_USB: 0b0111,
BootDevice.PRIMARY_REMOTE: 0b1001,
BootDevice.REMOTE_CD: 0b1000,
BootDevice.REMOTE_HDD: 0b1011,
BootDevice.PRIMARY_USB: 0b1111
}
def data_to_boot_mode(data):
"""
Convert a `GetSystemBootOptions(BOOT_PARAMETER_BOOT_FLAGS)` response data
into the string representation of the encoded boot mode.
"""
boot_mode_raw = (data[0] >> 5) & 1
boot_mode = "legacy" if boot_mode_raw == 0 else "efi"
return boot_mode
def data_to_boot_persistency(data):
"""
Convert a `GetSystemBootOptions(BOOT_PARAMETER_BOOT_FLAGS)` response data
into the boolean representation of the encoded boot persistency.
"""
boot_persistent_raw = (data[0] >> 6) & 1
return boot_persistent_raw == 1
def data_to_boot_device(data):
"""
Convert a `GetSystemBootOptions(BOOT_PARAMETER_BOOT_FLAGS)` response data
into the string representation of the encoded boot device.
"""
boot_device_raw = (data[1] >> 2) & 0b1111
return CONVERT_RAW_TO_BOOT_DEVICE[boot_device_raw]
def boot_options_to_data(boot_device, boot_mode, boot_persistency):
"""
Convert a boot mode (string), boot device (string) and boot persistency (bool)
into a `SetSystemBootOptions(BOOT_PARAMETER_BOOT_FLAGS)` request data.
"""
if not isinstance(boot_persistency, bool):
raise TypeError(f"Wrong type for boot_persistency argument: {type(boot_persistency)}, expected bool.")
# Construct the boot mode byte
if boot_mode == "efi":
boot_mode_raw = 0b100000
elif boot_mode == "legacy":
boot_mode_raw = 0
else:
raise ValueError(f"Unknown value for boot_mode argument: {boot_mode}. Possible values are : legacy, efi.")
# Construct the boot persistency + boot flags valid bits
if boot_persistency:
boot_persistent_raw = 0b11000000
else:
boot_persistent_raw = 0b10000000
# Construct the boot device byte
device_raw = CONVERT_BOOT_DEVICE_TO_RAW.get(boot_device, None)
if device_raw is None:
raise ValueError(f"Unknown value for boot_device argument: {boot_device}")
# Construct the final data bytearray
data = ByteBuffer([boot_mode_raw | boot_persistent_raw, device_raw << 2, 0, 0, 0])
return data
class Chassis(object):
def get_chassis_status(self):
return ChassisStatus(self.send_message_with_name('GetChassisStatus'))
def chassis_control(self, option):
req = create_request_by_name('ChassisControl')
req.control.option = option
rsp = self.send_message(req)
check_completion_code(rsp.completion_code)
def chassis_control_power_down(self):
self.chassis_control(CONTROL_POWER_DOWN)
def chassis_control_power_up(self):
self.chassis_control(CONTROL_POWER_UP)
def chassis_control_power_cycle(self):
self.chassis_control(CONTROL_POWER_CYCLE)
def chassis_control_hard_reset(self):
self.chassis_control(CONTROL_HARD_RESET)
def chassis_control_diagnostic_interrupt(self):
self.chassis_control(CONTROL_DIAGNOSTIC_INTERRUPT)
def chassis_control_soft_shutdown(self):
self.chassis_control(CONTROL_SOFT_SHUTDOWN)
def get_system_boot_options(self, parameter_selector=0,
set_selector=0, block_selector=0):
req = create_request_by_name('GetSystemBootOptions')
req.parameter_selector.boot_option_parameter_selector = parameter_selector
req.set_selector = set_selector
req.block_selector = block_selector
rsp = self.send_message(req)
check_rsp_completion_code(rsp)
return rsp.data
def set_system_boot_options(self, parameter_selector, data,
mark_parameter_invalid=0):
req = create_request_by_name('SetSystemBootOptions')
req.parameter_selector.parameter_validity = mark_parameter_invalid
req.parameter_selector.boot_option_parameter_selector = parameter_selector
req.data = data
rsp = self.send_message(req)
check_rsp_completion_code(rsp)
def get_boot_mode(self):
"""
Return a string corresponding to the device boot mode.
Possible values are: legacy, efi.
"""
rsp = self.get_system_boot_options(BOOT_PARAMETER_BOOT_FLAGS)
return data_to_boot_mode(rsp)
def get_boot_persistency(self):
"""
Return True if the boot configuration is to be applied to every future
boot, Fale if it only will applied to the next boot.
"""
rsp = self.get_system_boot_options(BOOT_PARAMETER_BOOT_FLAGS)
return data_to_boot_persistency(rsp)
def get_boot_device(self):
"""
Return a string corresponding to the target boot device.
Possible values are listed in the `BootDevice` class.
"""
rsp = self.get_system_boot_options(BOOT_PARAMETER_BOOT_FLAGS)
return data_to_boot_device(rsp)
def set_boot_options(self, boot_device, boot_mode, boot_persistency):
data = boot_options_to_data(boot_device, boot_mode, boot_persistency)
self.set_system_boot_options(BOOT_PARAMETER_BOOT_FLAGS, data)
class ChassisStatus(State):
power_on = None
overload = None
interlock = None
fault = None
control_fault = None
restore_policy = None
id_cmd_state_info_support = None
chassis_id_state = None
front_panel_button_capabilities = None
last_event = []
chassis_state = []
def _from_response(self, rsp):
self.power_on = bool(rsp.current_power_state.power_on)
self.overload = bool(rsp.current_power_state.power_overload)
self.interlock = bool(rsp.current_power_state.interlock)
self.fault = bool(rsp.current_power_state.power_fault)
self.control_fault = bool(rsp.current_power_state.power_control_fault)
self.restore_policy = rsp.current_power_state.power_restore_policy
self.id_cmd_state_info_support = \
bool(rsp.misc_chassis_state.id_cmd_state_info_support) # noqa:E127
self.chassis_id_state = rsp.misc_chassis_state.chassis_id_state
if rsp.front_panel_button_capabilities is not None:
self.front_panel_button_capabilities = \
rsp.front_panel_button_capabilities
if rsp.last_power_event.ac_failed:
self.last_event.append('ac_failed')
if rsp.last_power_event.power_overload:
self.last_event.append('overload')
if rsp.last_power_event.power_interlock:
self.last_event.append('interlock')
if rsp.last_power_event.power_fault:
self.last_event.append('fault')
if rsp.last_power_event.power_is_on_via_ipmi_command:
self.last_event.append('power_on_via_ipmi')
if rsp.misc_chassis_state.chassis_intrusion_active:
self.chassis_state.append('intrusion')
if rsp.misc_chassis_state.front_panel_lockout_active:
self.chassis_state.append('front_panel_lockout')
if rsp.misc_chassis_state.drive_fault:
self.chassis_state.append('drive_fault')
if rsp.misc_chassis_state.cooling_fault_detected:
self.chassis_state.append('cooling_fault')
|