Add indicator management harness to ManagementInterface
Adds ``get_indicator_state``, ``set_indicator_state`` and ``get_supported_indicators`` methods to driver management interface. Drivers can override these methods implementing indicators (e.g. LEDs) management calls against the BMC of the baremetal nodes being managed. The spec: https://review.opendev.org/#/c/655685/7/specs/approved/expose-hardware-indicators.rst Change-Id: I952de78312f09897e6b743e11c9cc5dac977dc36 Story: 2005342 Task: 30311
This commit is contained in:
parent
a96f926c24
commit
d3e4fe50b5
33
ironic/common/components.py
Normal file
33
ironic/common/components.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Mapping of common hardware components of a computer system.
|
||||
"""
|
||||
|
||||
CHASSIS = 'chassis'
|
||||
"""Chassis enclosing one or more hardware components"""
|
||||
|
||||
SYSTEM = 'system'
|
||||
"""Computing system"""
|
||||
|
||||
DISK = 'disk'
|
||||
"""Storage drive"""
|
||||
|
||||
POWER = 'power'
|
||||
"""Power supply unit"""
|
||||
|
||||
NIC = 'nic'
|
||||
"""Network interface"""
|
30
ironic/common/indicator_states.py
Normal file
30
ironic/common/indicator_states.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Mapping of the indicator LED states.
|
||||
"""
|
||||
|
||||
OFF = 'off'
|
||||
"""LED is off"""
|
||||
|
||||
ON = 'on'
|
||||
"""LED is on"""
|
||||
|
||||
BLINKING = 'blinking'
|
||||
"""LED is blinking"""
|
||||
|
||||
UNKNOWN = 'unknown'
|
||||
"""LED state is not known"""
|
@ -1000,6 +1000,89 @@ class ManagementInterface(BaseInterface):
|
||||
raise exception.UnsupportedDriverExtension(
|
||||
driver=task.node.driver, extension='inject_nmi')
|
||||
|
||||
def get_supported_indicators(self, task, component=None):
|
||||
"""Get a map of the supported indicators (e.g. LEDs).
|
||||
|
||||
:param task: A task from TaskManager.
|
||||
:param component: If not `None`, return indicator information
|
||||
for just this component, otherwise return indicators for
|
||||
all existing components.
|
||||
:returns: A dictionary of hardware components
|
||||
(:mod:`ironic.common.components`) as keys with values
|
||||
being dictionaries having indicator IDs as keys and indicator
|
||||
properties as values.
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
'chassis': {
|
||||
'enclosure-0': {
|
||||
"readonly": true,
|
||||
"states": [
|
||||
"off",
|
||||
"on"
|
||||
]
|
||||
}
|
||||
},
|
||||
'system':
|
||||
'blade-A': {
|
||||
"readonly": true,
|
||||
"states": [
|
||||
"pff",
|
||||
"on"
|
||||
]
|
||||
}
|
||||
},
|
||||
'drive':
|
||||
'ssd0': {
|
||||
"readonly": true,
|
||||
"states": [
|
||||
"off",
|
||||
"on"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
raise exception.UnsupportedDriverExtension(
|
||||
driver=task.node.driver, extension='get_supported_indicators')
|
||||
|
||||
def set_indicator_state(self, task, component, indicator, state):
|
||||
"""Set indicator on the hardware component to the desired state.
|
||||
|
||||
:param task: A task from TaskManager.
|
||||
:param component: The hardware component, one of
|
||||
:mod:`ironic.common.components`.
|
||||
:param indicator: Indicator ID (as reported by
|
||||
`get_supported_indicators`).
|
||||
:state: Desired state of the indicator, one of
|
||||
:mod:`ironic.common.indicator_states`.
|
||||
:raises: InvalidParameterValue if an invalid component, indicator
|
||||
or state is specified.
|
||||
:raises: MissingParameterValue if a required parameter is missing
|
||||
"""
|
||||
raise exception.UnsupportedDriverExtension(
|
||||
driver=task.node.driver, extension='set_indicator_state')
|
||||
|
||||
def get_indicator_state(self, task, component, indicator):
|
||||
"""Get current state of the indicator of the hardware component.
|
||||
|
||||
:param task: A task from TaskManager.
|
||||
:param component: The hardware component, one of
|
||||
:mod:`ironic.common.components`.
|
||||
:param indicator: Indicator ID (as reported by
|
||||
`get_supported_indicators`).
|
||||
:raises: InvalidParameterValue if an invalid component or indicator
|
||||
is specified.
|
||||
:raises: MissingParameterValue if a required parameter is missing
|
||||
:returns: Current state of the indicator, one of
|
||||
:mod:`ironic.common.indicator_states`.
|
||||
|
||||
"""
|
||||
raise exception.UnsupportedDriverExtension(
|
||||
driver=task.node.driver, extension='get_indicator_state')
|
||||
|
||||
|
||||
class InspectInterface(BaseInterface):
|
||||
"""Interface for inspection-related actions."""
|
||||
|
@ -27,8 +27,10 @@ on separate vendor_passthru methods.
|
||||
from oslo_log import log
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import components
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common import indicator_states
|
||||
from ironic.common import states
|
||||
from ironic.drivers import base
|
||||
from ironic import objects
|
||||
@ -219,6 +221,44 @@ class FakeManagement(base.ManagementInterface):
|
||||
def get_sensors_data(self, task):
|
||||
return {}
|
||||
|
||||
def get_supported_indicators(self, task, component=None):
|
||||
indicators = {
|
||||
components.CHASSIS: {
|
||||
'led-0': {
|
||||
"readonly": True,
|
||||
"states": [
|
||||
indicator_states.OFF,
|
||||
indicator_states.ON
|
||||
]
|
||||
}
|
||||
},
|
||||
components.SYSTEM: {
|
||||
'led': {
|
||||
"readonly": False,
|
||||
"states": [
|
||||
indicator_states.BLINKING,
|
||||
indicator_states.OFF,
|
||||
indicator_states.ON
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {c: indicators[c] for c in indicators
|
||||
if not component or component == c}
|
||||
|
||||
def get_indicator_state(self, task, component, indicator):
|
||||
indicators = self.get_supported_indicators(task)
|
||||
if component not in indicators:
|
||||
raise exception.InvalidParameterValue(_(
|
||||
"Invalid component %s specified.") % component)
|
||||
|
||||
if indicator not in indicators[component]:
|
||||
raise exception.InvalidParameterValue(_(
|
||||
"Invalid indicator %s specified.") % indicator)
|
||||
|
||||
return indicator_states.ON
|
||||
|
||||
|
||||
class FakeInspect(base.InspectInterface):
|
||||
|
||||
|
@ -17,7 +17,9 @@ import json
|
||||
|
||||
import mock
|
||||
|
||||
from ironic.common import components
|
||||
from ironic.common import exception
|
||||
from ironic.common import indicator_states
|
||||
from ironic.common import raid
|
||||
from ironic.common import states
|
||||
from ironic.drivers import base as driver_base
|
||||
@ -783,6 +785,53 @@ class TestManagementInterface(base.TestCase):
|
||||
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||
management.get_boot_mode, task_mock)
|
||||
|
||||
def test_get_supported_indicators_default_impl(self):
|
||||
management = fake.FakeManagement()
|
||||
task_mock = mock.MagicMock(spec_set=['node'])
|
||||
|
||||
expected = {
|
||||
components.CHASSIS: {
|
||||
'led-0': {
|
||||
"readonly": True,
|
||||
"states": [
|
||||
indicator_states.OFF,
|
||||
indicator_states.ON
|
||||
]
|
||||
}
|
||||
},
|
||||
components.SYSTEM: {
|
||||
'led': {
|
||||
"readonly": False,
|
||||
"states": [
|
||||
indicator_states.BLINKING,
|
||||
indicator_states.OFF,
|
||||
indicator_states.ON
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
expected, management.get_supported_indicators(task_mock))
|
||||
|
||||
def test_set_indicator_state_default_impl(self):
|
||||
management = fake.FakeManagement()
|
||||
task_mock = mock.MagicMock(spec_set=['node'])
|
||||
|
||||
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||
management.set_indicator_state, task_mock,
|
||||
components.CHASSIS, 'led-0', indicator_states.ON)
|
||||
|
||||
def test_get_indicator_state_default_impl(self):
|
||||
management = fake.FakeManagement()
|
||||
task_mock = mock.MagicMock(spec_set=['node'])
|
||||
|
||||
expected = indicator_states.ON
|
||||
|
||||
self.assertEqual(
|
||||
expected, management.get_indicator_state(
|
||||
task_mock, components.CHASSIS, 'led-0'))
|
||||
|
||||
|
||||
class TestBareDriver(base.TestCase):
|
||||
|
||||
|
@ -20,7 +20,9 @@
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import boot_modes
|
||||
from ironic.common import components
|
||||
from ironic.common import exception
|
||||
from ironic.common import indicator_states
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import base as driver_base
|
||||
@ -119,6 +121,45 @@ class FakeHardwareTestCase(db_base.DbTestCase):
|
||||
self.task, boot_modes.LEGACY_BIOS
|
||||
)
|
||||
|
||||
def test_management_interface_get_supported_indicators(self):
|
||||
expected = {
|
||||
components.CHASSIS: {
|
||||
'led-0': {
|
||||
"readonly": True,
|
||||
"states": [
|
||||
indicator_states.OFF,
|
||||
indicator_states.ON
|
||||
]
|
||||
}
|
||||
},
|
||||
components.SYSTEM: {
|
||||
'led': {
|
||||
"readonly": False,
|
||||
"states": [
|
||||
indicator_states.BLINKING,
|
||||
indicator_states.OFF,
|
||||
indicator_states.ON
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
expected,
|
||||
self.driver.management.get_supported_indicators(self.task))
|
||||
|
||||
def test_management_interface_get_indicator_state(self):
|
||||
expected = indicator_states.ON
|
||||
self.assertEqual(
|
||||
expected, self.driver.management.get_indicator_state(
|
||||
self.task, components.CHASSIS, 'led-0'))
|
||||
|
||||
def test_management_interface_set_indicator_state_good(self):
|
||||
self.assertRaises(
|
||||
exception.UnsupportedDriverExtension,
|
||||
self.driver.management.set_indicator_state,
|
||||
self.task, components.CHASSIS, 'led-0', indicator_states.ON)
|
||||
|
||||
def test_inspect_interface(self):
|
||||
self.assertEqual({}, self.driver.inspect.get_properties())
|
||||
self.driver.inspect.validate(self.task)
|
||||
|
Loading…
Reference in New Issue
Block a user