File: chassis.py

package info (click to toggle)
python-ipmi 0.5.7-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,132 kB
  • sloc: python: 12,645; makefile: 2
file content (269 lines) | stat: -rw-r--r-- 9,977 bytes parent folder | download
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')