Merge "Add new ironic driver"
This commit is contained in:
commit
e901c91f62
@ -19,6 +19,9 @@ SUSHY_EMULATOR_AUTH_FILE = None
|
||||
# The OpenStack cloud ID to use. This option enables OpenStack driver.
|
||||
SUSHY_EMULATOR_OS_CLOUD = None
|
||||
|
||||
# The OpenStack cloud ID to use for Ironic. This option enables Ironic driver.
|
||||
SUSHY_EMULATOR_IRONIC_CLOUD = None
|
||||
|
||||
# The libvirt URI to use. This option enables libvirt driver.
|
||||
SUSHY_EMULATOR_LIBVIRT_URI = u'qemu:///system'
|
||||
|
||||
|
@ -303,6 +303,69 @@ And flip its power state via the Redfish call:
|
||||
You can have as many OpenStack instances as you need. The instances can be
|
||||
concurrently managed over Redfish and functionally similar tools.
|
||||
|
||||
Systems resource driver: Ironic
|
||||
++++++++++++++++++++++++++++++++++
|
||||
|
||||
You can use the Ironic driver to manage Ironic baremetal instance to simulated
|
||||
Redfish API. You may want to do this if you require a redfish compatible endpoint
|
||||
but don't have direct access to the BMC (you only have access via Ironic) or
|
||||
the BMC doesn't support redfish.
|
||||
|
||||
Assuming your baremetal cloud is setup you can invoke the Redfish emulator by
|
||||
running
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sushy-emulator --ironic-cloud baremetal-cloud
|
||||
* Running on http://localhost:8000/ (Press CTRL+C to quit)
|
||||
|
||||
By this point you should be able to see your Baremetal instances among the
|
||||
Redfish *Systems*:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl http://localhost:8000/redfish/v1/Systems/
|
||||
{
|
||||
"@odata.type": "#ComputerSystemCollection.ComputerSystemCollection",
|
||||
"Name": "Computer System Collection",
|
||||
"Members@odata.count": 1,
|
||||
"Members": [
|
||||
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/<uuid>"
|
||||
}
|
||||
|
||||
],
|
||||
"@odata.context": "/redfish/v1/$metadata#ComputerSystemCollection.ComputerSystemCollection",
|
||||
"@odata.id": "/redfish/v1/Systems",
|
||||
"@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
|
||||
}
|
||||
|
||||
And flip its power state via the Redfish call:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -d '{"ResetType":"On"}' \
|
||||
-H "Content-Type: application/json" -X POST \
|
||||
http://localhost:8000/redfish/v1/Systems/<uuid>/Actions/ComputerSystem.Reset
|
||||
|
||||
curl -d '{"ResetType":"ForceOff"}' \
|
||||
-H "Content-Type: application/json" -X POST \
|
||||
http://localhost:8000/redfish/v1/Systems/<uuid>/Actions/ComputerSystem.Reset
|
||||
|
||||
Or update their boot device:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -d '{"Boot":{"BootSourceOverrideTarget":"Pxe"}}' \
|
||||
-H "Content-Type: application/json" -X PATCH \
|
||||
http://localhost:8000/redfish/v1/Systems/<uuid>
|
||||
|
||||
curl -d '{"Boot":{"BootSourceOverrideTarget":"Hdd"}}' \
|
||||
-H "Content-Type: application/json" -X PATCH \
|
||||
http://localhost:8000/redfish/v1/Systems/<uuid>
|
||||
|
||||
|
||||
Filtering by allowed instances
|
||||
++++++++++++++++++++++++++++++
|
||||
|
||||
|
@ -5,8 +5,8 @@ Using Redfish emulators
|
||||
The sushy-tools package includes two emulators - static and dynamic.
|
||||
|
||||
Static emulator could be used to serve Redfish mocks in form of static
|
||||
JSON documents. Dynamic emulator relies upon either `libvirt` or `OpenStack`
|
||||
virtualization backend to mimic baremetal nodes behind Redfish BMC.
|
||||
JSON documents. Dynamic emulator relies upon `libvirt`, `OpenStack` or
|
||||
`Ironic` virtualization backend to mimic nodes behind a Redfish BMC.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
9
releasenotes/notes/add-ironic-6f446bf16276b4dd.yaml
Normal file
9
releasenotes/notes/add-ironic-6f446bf16276b4dd.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
|
||||
Adds new ironic driver to provide a limited emulated redfish API
|
||||
to ironic nodes. This would be needed in cases where a redfish
|
||||
compatible endpoint is needed but but don't have direct access
|
||||
to the BMC (you only have access via Ironic) or the BMC doesn't
|
||||
support redfish.
|
@ -34,6 +34,7 @@ from sushy_tools.emulator.resources import indicators as inddriver
|
||||
from sushy_tools.emulator.resources import managers as mgrdriver
|
||||
from sushy_tools.emulator.resources import storage as stgdriver
|
||||
from sushy_tools.emulator.resources.systems import fakedriver
|
||||
from sushy_tools.emulator.resources.systems import ironicdriver
|
||||
from sushy_tools.emulator.resources.systems import libvirtdriver
|
||||
from sushy_tools.emulator.resources.systems import novadriver
|
||||
from sushy_tools.emulator.resources import vmedia as vmddriver
|
||||
@ -102,6 +103,7 @@ class Application(flask.Flask):
|
||||
def systems(self):
|
||||
fake = self.config.get('SUSHY_EMULATOR_FAKE_DRIVER')
|
||||
os_cloud = self.config.get('SUSHY_EMULATOR_OS_CLOUD')
|
||||
ironic_cloud = self.config.get('SUSHY_EMULATOR_IRONIC_CLOUD')
|
||||
|
||||
if fake:
|
||||
result = fakedriver.FakeDriver.initialize(
|
||||
@ -115,6 +117,14 @@ class Application(flask.Flask):
|
||||
result = novadriver.OpenStackDriver.initialize(
|
||||
self.config, self.logger, os_cloud)()
|
||||
|
||||
elif ironic_cloud:
|
||||
if not ironicdriver.is_loaded:
|
||||
self.logger.error('Ironic driver not loaded')
|
||||
sys.exit(1)
|
||||
|
||||
result = ironicdriver.IronicDriver.initialize(
|
||||
self.config, self.logger, ironic_cloud)()
|
||||
|
||||
else:
|
||||
if not libvirtdriver.is_loaded:
|
||||
self.logger.error('libvirt driver not loaded')
|
||||
@ -850,6 +860,11 @@ def parse_args():
|
||||
help='Use the fake driver. Can also be set '
|
||||
'via environmnet variable '
|
||||
'SUSHY_EMULATOR_FAKE_DRIVER.')
|
||||
backend_group.add_argument('--ironic-cloud',
|
||||
type=str,
|
||||
help='Ironic cloud name. Can also be set via '
|
||||
'via config variable '
|
||||
'SUSHY_EMULATOR_IRONIC_CLOUD.')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
@ -868,6 +883,9 @@ def main():
|
||||
if args.libvirt_uri:
|
||||
app.config['SUSHY_EMULATOR_LIBVIRT_URI'] = args.libvirt_uri
|
||||
|
||||
if args.ironic_cloud:
|
||||
app.config['SUSHY_EMULATOR_IRONIC_CLOUD'] = args.ironic_cloud
|
||||
|
||||
if args.fake:
|
||||
app.config['SUSHY_EMULATOR_FAKE_DRIVER'] = True
|
||||
|
||||
|
327
sushy_tools/emulator/resources/systems/ironicdriver.py
Normal file
327
sushy_tools/emulator/resources/systems/ironicdriver.py
Normal file
@ -0,0 +1,327 @@
|
||||
# Copyright 2023 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.
|
||||
|
||||
import math
|
||||
|
||||
from sushy_tools.emulator import memoize
|
||||
from sushy_tools.emulator.resources.systems.base import AbstractSystemsDriver
|
||||
from sushy_tools import error
|
||||
|
||||
try:
|
||||
import openstack
|
||||
|
||||
except ImportError:
|
||||
openstack = None
|
||||
|
||||
|
||||
is_loaded = bool(openstack)
|
||||
|
||||
|
||||
class IronicDriver(AbstractSystemsDriver):
|
||||
"""Ironic driver"""
|
||||
|
||||
IRONIC_POWER_ON = "power on"
|
||||
IRONIC_POWER_OFF = "power off"
|
||||
IRONIC_POWER_OFF_SOFT = "soft power off"
|
||||
IRONIC_POWER_REBOOT = "rebooting"
|
||||
IRONIC_POWER_REBOOT_SOFT = "soft rebooting"
|
||||
|
||||
BOOT_DEVICE_MAP = {
|
||||
'Pxe': 'pxe',
|
||||
'Hdd': 'disk',
|
||||
'Cd': 'cdrom',
|
||||
}
|
||||
|
||||
BOOT_DEVICE_MAP_REV = {v: k for k, v in BOOT_DEVICE_MAP.items()}
|
||||
|
||||
BOOT_MODE_MAP = {
|
||||
'Legacy': 'bios',
|
||||
'UEFI': 'uefi',
|
||||
}
|
||||
|
||||
BOOT_MODE_MAP_REV = {v: k for k, v in BOOT_MODE_MAP.items()}
|
||||
|
||||
PERMANENT_CACHE = {}
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, config, logger, os_cloud, *args, **kwargs):
|
||||
cls._config = config
|
||||
cls._logger = logger
|
||||
cls._os_cloud = os_cloud
|
||||
|
||||
if not hasattr(cls, "_cc"):
|
||||
cls._cc = openstack.connect(cloud=cls._os_cloud)
|
||||
|
||||
return cls
|
||||
|
||||
@memoize.memoize()
|
||||
def _get_node(self, identity):
|
||||
try:
|
||||
node = self._cc.baremetal.get_node(identity)
|
||||
return node
|
||||
except openstack.exceptions.ResourceNotFound:
|
||||
pass
|
||||
|
||||
msg = ('Error finding node by UUID "%(identity)s" at ironic '
|
||||
'cloud %(os_cloud)s"' % {'identity': identity,
|
||||
'os_cloud': self._os_cloud})
|
||||
|
||||
self._logger.debug(msg)
|
||||
|
||||
raise error.NotFound(msg)
|
||||
|
||||
@memoize.memoize(permanent_cache=PERMANENT_CACHE)
|
||||
def _get_properties(self, identity):
|
||||
node = self._get_node(identity)
|
||||
return node.properties
|
||||
|
||||
@memoize.memoize(permanent_cache=PERMANENT_CACHE)
|
||||
def _get_driver_internal_info(self, identity):
|
||||
return self._get_node(identity).driver_internal_info
|
||||
|
||||
@property
|
||||
def driver(self):
|
||||
"""Return human-friendly driver description
|
||||
|
||||
:returns: driver description as `str`
|
||||
"""
|
||||
return '<OpenStack baremetal>'
|
||||
|
||||
@property
|
||||
def systems(self):
|
||||
"""Return available computer systems
|
||||
|
||||
:returns: list of UUIDs representing the systems
|
||||
"""
|
||||
return [node.id for node in self._cc.baremetal.nodes(fields=["uuid"])]
|
||||
|
||||
def uuid(self, identity):
|
||||
"""Get computer system UUID by name
|
||||
|
||||
:param identity: OpenStack node name or ID
|
||||
|
||||
:returns: computer system UUID
|
||||
"""
|
||||
node = self._get_node(identity)
|
||||
return node.id
|
||||
|
||||
def name(self, identity):
|
||||
"""Get computer system name by name
|
||||
|
||||
:param identity: OpenStack node name or ID
|
||||
|
||||
:returns: computer system name
|
||||
"""
|
||||
node = self._get_node(identity)
|
||||
return node.name
|
||||
|
||||
def get_power_state(self, identity):
|
||||
"""Get computer system power state
|
||||
|
||||
:param identity: OpenStack node name or ID
|
||||
|
||||
:returns: *On* or *Off*`str` or `None`
|
||||
if power state can't be determined
|
||||
"""
|
||||
try:
|
||||
node = self._get_node(identity)
|
||||
|
||||
except error.FishyError:
|
||||
return
|
||||
|
||||
if node.power_state == self.IRONIC_POWER_ON:
|
||||
return 'On'
|
||||
|
||||
return 'Off'
|
||||
|
||||
def set_power_state(self, identity, state):
|
||||
"""Set computer system power state
|
||||
|
||||
:param identity: OpenStack node name or ID
|
||||
:param state: string literal requesting power state transition.
|
||||
Valid values are: *On*, *ForceOn*, *ForceOff*, *GracefulShutdown*,
|
||||
*GracefulRestart*, *ForceRestart*, *Nmi*.
|
||||
|
||||
:raises: `error.FishyError` if power state can't be set
|
||||
|
||||
"""
|
||||
node = self._get_node(identity)
|
||||
|
||||
if state in ('On', 'ForceOn'):
|
||||
self._cc.baremetal.set_node_power_state(
|
||||
node.id, self.IRONIC_POWER_ON)
|
||||
|
||||
elif state == 'ForceOff':
|
||||
self._cc.baremetal.set_node_power_state(
|
||||
node.id, self.IRONIC_POWER_OFF)
|
||||
|
||||
elif state == 'GracefulShutdown':
|
||||
self._cc.baremetal.set_node_power_state(
|
||||
node.id, self.IRONIC_POWER_OFF_SOFT)
|
||||
|
||||
elif state == 'GracefulRestart':
|
||||
if node.power_state == self.IRONIC_POWER_ON:
|
||||
self._cc.baremetal.set_node_power_state(
|
||||
node.id, self.IRONIC_POWER_REBOOT_SOFT)
|
||||
|
||||
elif state == 'ForceRestart':
|
||||
if node.power_state == self.IRONIC_POWER_ON:
|
||||
self._cc.baremetal.set_node_power_state(
|
||||
node.id, self.IRONIC_POWER_REBOOT)
|
||||
|
||||
# NOTE(etingof) can't support `state == "Nmi"` as
|
||||
# openstacksdk does not seem to support that
|
||||
else:
|
||||
raise error.BadRequest(
|
||||
'Unknown ResetType "%(state)s"' % {'state': state})
|
||||
|
||||
def get_boot_device(self, identity):
|
||||
"""Get computer system boot device name
|
||||
|
||||
:param identity: OpenStack node name or ID
|
||||
|
||||
:returns: boot device name as `str` or `None` if device name
|
||||
can't be determined. Valid values are: *Pxe*, *Hdd*, *Cd*.
|
||||
"""
|
||||
try:
|
||||
node = self._get_node(identity)
|
||||
|
||||
except error.FishyError:
|
||||
return
|
||||
|
||||
bdevice = node.get_boot_device(self._cc.baremetal).get("boot_device")
|
||||
return self.BOOT_DEVICE_MAP_REV.get(bdevice)
|
||||
|
||||
def set_boot_device(self, identity, boot_source):
|
||||
"""Set computer system boot device name
|
||||
|
||||
:param identity: OpenStack node name or ID
|
||||
:param boot_source: string literal requesting boot device
|
||||
change on the system. Valid values are: *Pxe*, *Hdd*, *Cd*.
|
||||
|
||||
:raises: `error.FishyError` if boot device can't be set
|
||||
"""
|
||||
|
||||
try:
|
||||
target = self.BOOT_DEVICE_MAP[boot_source]
|
||||
|
||||
except KeyError:
|
||||
msg = ('Unknown power state requested: '
|
||||
'%(boot_source)s' % {'boot_source': boot_source})
|
||||
|
||||
raise error.BadRequest(msg)
|
||||
|
||||
self._cc.baremetal.set_node_boot_device(identity, target)
|
||||
|
||||
def get_boot_mode(self, identity):
|
||||
"""Get computer system boot mode.
|
||||
|
||||
:returns: either *UEFI* or *Legacy* as `str` or `None` if
|
||||
current boot mode can't be determined
|
||||
"""
|
||||
|
||||
node = self._get_node(identity)
|
||||
return self.BOOT_MODE_MAP_REV.get(node.boot_mode)
|
||||
|
||||
def set_boot_mode(self, identity, boot_mode):
|
||||
"""Set computer system boot mode.
|
||||
|
||||
:param boot_mode: string literal requesting boot mode
|
||||
change on the system. Valid values are: *UEFI*, *Legacy*.
|
||||
|
||||
:raises: `error.FishyError` if boot mode can't be set
|
||||
"""
|
||||
# just to make sure passed identity exists
|
||||
self._get_node(identity)
|
||||
msg = ('The cloud driver %(driver)s does not allow changing boot '
|
||||
'mode through Redfish' % {'driver': self.driver})
|
||||
raise error.NotSupportedError(msg)
|
||||
|
||||
def get_secure_boot(self, identity):
|
||||
"""Get computer system secure boot state for UEFI boot mode.
|
||||
|
||||
:returns: boolean of the current secure boot state
|
||||
|
||||
:raises: `FishyError` if the state can't be fetched
|
||||
"""
|
||||
node = self._get_node(identity)
|
||||
return node.is_secure_boot or False
|
||||
|
||||
def set_secure_boot(self, identity, secure):
|
||||
"""Set computer system secure boot state for UEFI boot mode.
|
||||
|
||||
:param secure: boolean requesting the secure boot state
|
||||
|
||||
:raises: `FishyError` if the can't be set
|
||||
"""
|
||||
msg = ('The cloud driver %(driver)s does not support changing secure '
|
||||
'boot mode through Redfish' % {'driver': self.driver})
|
||||
raise error.NotSupportedError(msg)
|
||||
|
||||
def get_total_memory(self, identity):
|
||||
"""Get computer system total memory
|
||||
|
||||
:param identity: OpenStack node name or ID
|
||||
|
||||
:returns: available RAM in GiB as `int`
|
||||
"""
|
||||
try:
|
||||
properties = self._get_properties(identity)
|
||||
|
||||
except error.FishyError:
|
||||
return
|
||||
|
||||
memory_mb = properties.get("memory_mb")
|
||||
if memory_mb is None:
|
||||
return None
|
||||
return int(math.ceil(int(memory_mb) / 1024))
|
||||
|
||||
def get_total_cpus(self, identity):
|
||||
"""Get computer system total count of available CPUs
|
||||
|
||||
:param identity: OpenStack node name or ID
|
||||
|
||||
:returns: available CPU count as `int`
|
||||
"""
|
||||
try:
|
||||
properties = self._get_properties(identity)
|
||||
|
||||
except error.FishyError:
|
||||
return
|
||||
|
||||
cpus = properties.get("cpus")
|
||||
if cpus is None:
|
||||
return None
|
||||
return int(cpus)
|
||||
|
||||
def get_nics(self, identity):
|
||||
"""Get node's network interfaces
|
||||
|
||||
Use MAC address as network interface's id
|
||||
|
||||
:param identity: OpenStack node name or ID
|
||||
|
||||
:returns: list of dictionaries with NIC attributes (id and mac)
|
||||
"""
|
||||
|
||||
self._get_node(identity)
|
||||
|
||||
macs = set()
|
||||
for port in self._cc.baremetal.ports(fields=["address", "node_uuid"]):
|
||||
if port["node_uuid"] == identity:
|
||||
macs.add(port["address"])
|
||||
|
||||
return [{'id': mac, 'mac': mac}
|
||||
for mac in macs]
|
199
sushy_tools/tests/unit/emulator/resources/systems/test_ironic.py
Normal file
199
sushy_tools/tests/unit/emulator/resources/systems/test_ironic.py
Normal file
@ -0,0 +1,199 @@
|
||||
# Copyright 2023 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.
|
||||
from unittest import mock
|
||||
|
||||
from oslotest import base
|
||||
|
||||
from sushy_tools.emulator.resources.systems.ironicdriver import IronicDriver
|
||||
from sushy_tools import error
|
||||
|
||||
|
||||
@mock.patch.dict(IronicDriver.PERMANENT_CACHE)
|
||||
class IronicDriverTestCase(base.BaseTestCase):
|
||||
uuid = 'c7a5fdbd-cdaf-9455-926a-d65c16db1809'
|
||||
|
||||
def setUp(self):
|
||||
self.ironic_patcher = mock.patch('openstack.connect', autospec=True)
|
||||
self.ironic_mock = self.ironic_patcher.start()
|
||||
|
||||
self.node_mock = mock.Mock(id=self.uuid)
|
||||
self.ironic_mock.return_value.baremetal.get_node.return_value = \
|
||||
self.node_mock
|
||||
|
||||
# _cc is initialized on the class (not any single object) so it needs
|
||||
# to be cleared between tests
|
||||
if hasattr(IronicDriver, "_cc"):
|
||||
del IronicDriver._cc
|
||||
|
||||
test_driver_class = IronicDriver.initialize(
|
||||
{}, mock.MagicMock(), 'fake-cloud')
|
||||
self.test_driver = test_driver_class()
|
||||
|
||||
super(IronicDriverTestCase, self).setUp()
|
||||
|
||||
def tearDown(self):
|
||||
self.ironic_patcher.stop()
|
||||
super(IronicDriverTestCase, self).tearDown()
|
||||
|
||||
def test_uuid(self):
|
||||
uuid = self.test_driver.uuid(self.uuid)
|
||||
self.assertEqual(self.uuid, uuid)
|
||||
|
||||
def test_systems(self):
|
||||
node0 = mock.Mock(id='host0')
|
||||
node1 = mock.Mock(id='host1')
|
||||
self.ironic_mock.return_value.baremetal.nodes.return_value = [
|
||||
node0, node1]
|
||||
systems = self.test_driver.systems
|
||||
|
||||
self.assertEqual(['host0', 'host1'], systems)
|
||||
|
||||
def test_get_power_state_on(self):
|
||||
self.node_mock.power_state = 'power on'
|
||||
|
||||
power_state = self.test_driver.get_power_state(self.uuid)
|
||||
|
||||
self.assertEqual('On', power_state)
|
||||
|
||||
def test_get_power_state_off(self):
|
||||
self.node_mock.power_state = 'power off'
|
||||
|
||||
power_state = self.test_driver.get_power_state(self.uuid)
|
||||
|
||||
self.assertEqual('Off', power_state)
|
||||
|
||||
def test_set_power_state_on(self):
|
||||
self.node_mock.power_state = 'power off'
|
||||
self.test_driver.set_power_state(self.uuid, 'On')
|
||||
snps = self.ironic_mock.return_value.baremetal.set_node_power_state
|
||||
snps.assert_called_once_with(self.uuid, 'power on')
|
||||
|
||||
def test_set_power_state_forceon(self):
|
||||
self.node_mock.power_state = 'power off'
|
||||
self.test_driver.set_power_state(self.uuid, 'ForceOn')
|
||||
snps = self.ironic_mock.return_value.baremetal.set_node_power_state
|
||||
snps.assert_called_once_with(self.uuid, 'power on')
|
||||
|
||||
def test_set_power_state_forceoff(self):
|
||||
self.node_mock.power_state = 'power on'
|
||||
self.test_driver.set_power_state(self.uuid, 'ForceOff')
|
||||
snps = self.ironic_mock.return_value.baremetal.set_node_power_state
|
||||
snps.assert_called_once_with(self.uuid, 'power off')
|
||||
|
||||
def test_set_power_state_gracefulshutdown(self):
|
||||
self.node_mock.power_state = 'power on'
|
||||
self.test_driver.set_power_state(self.uuid, 'GracefulShutdown')
|
||||
snps = self.ironic_mock.return_value.baremetal.set_node_power_state
|
||||
snps.assert_called_once_with(self.uuid, 'soft power off')
|
||||
|
||||
def test_set_power_state_gracefulrestart(self):
|
||||
self.node_mock.power_state = 'power on'
|
||||
self.test_driver.set_power_state(self.uuid, 'GracefulRestart')
|
||||
snps = self.ironic_mock.return_value.baremetal.set_node_power_state
|
||||
snps.assert_called_once_with(self.uuid, 'soft rebooting')
|
||||
|
||||
def test_set_power_state_forcerestart(self):
|
||||
self.node_mock.power_state = 'power on'
|
||||
self.test_driver.set_power_state(self.uuid, 'ForceRestart')
|
||||
snps = self.ironic_mock.return_value.baremetal.set_node_power_state
|
||||
snps.assert_called_once_with(self.uuid, 'rebooting')
|
||||
|
||||
def test_get_boot_device(self):
|
||||
self.node_mock.get_boot_device.return_value.get.return_value = "pxe"
|
||||
|
||||
boot_device = self.test_driver.get_boot_device(self.uuid)
|
||||
|
||||
self.assertEqual('Pxe', boot_device)
|
||||
|
||||
def test_set_boot_device(self):
|
||||
self.test_driver.set_boot_device(self.uuid, 'Pxe')
|
||||
self.ironic_mock.return_value.baremetal.set_node_boot_device.\
|
||||
assert_called_once_with(self.uuid, "pxe")
|
||||
|
||||
def test_get_boot_mode(self):
|
||||
self.node_mock.boot_mode = 'bios'
|
||||
|
||||
boot_mode = self.test_driver.get_boot_mode(self.uuid)
|
||||
|
||||
self.assertEqual('Legacy', boot_mode)
|
||||
|
||||
def test_set_boot_mode(self):
|
||||
self.assertRaises(
|
||||
error.FishyError, self.test_driver.set_boot_mode,
|
||||
self.uuid, 'Legacy')
|
||||
|
||||
def test_get_total_memory(self):
|
||||
self.node_mock.properties = {'memory_mb': '4096'}
|
||||
|
||||
memory = self.test_driver.get_total_memory(self.uuid)
|
||||
|
||||
self.assertEqual(4, memory)
|
||||
|
||||
def test_get_total_cpus(self):
|
||||
self.node_mock.properties = {'cpus': '2'}
|
||||
|
||||
cpus = self.test_driver.get_total_cpus(self.uuid)
|
||||
|
||||
self.assertEqual(2, cpus)
|
||||
|
||||
def test_get_bios(self):
|
||||
self.assertRaises(
|
||||
error.FishyError, self.test_driver.get_bios, self.uuid)
|
||||
|
||||
def test_set_bios(self):
|
||||
self.assertRaises(
|
||||
error.FishyError,
|
||||
self.test_driver.set_bios,
|
||||
self.uuid,
|
||||
{'attribute 1': 'value 1'})
|
||||
|
||||
def test_reset_bios(self):
|
||||
self.assertRaises(
|
||||
error.FishyError, self.test_driver.reset_bios, self.uuid)
|
||||
|
||||
def test_get_nics(self):
|
||||
self.ironic_mock.return_value.baremetal.ports.return_value = \
|
||||
[{"node_uuid": self.uuid, "address": "fa:16:3e:22:18:31"},
|
||||
{"node_uuid": "dummy", "address": "dummy"}]
|
||||
|
||||
nics = self.test_driver.get_nics(self.uuid)
|
||||
|
||||
self.assertEqual([{'id': 'fa:16:3e:22:18:31',
|
||||
'mac': 'fa:16:3e:22:18:31'}],
|
||||
sorted(nics, key=lambda k: k['id']))
|
||||
|
||||
def test_get_nics_empty(self):
|
||||
self.node_mock.addresses = None
|
||||
self.ironic_mock.return_value.baremetal.ports.return_value = []
|
||||
nics = self.test_driver.get_nics(self.uuid)
|
||||
self.assertEqual([], nics)
|
||||
|
||||
def test_get_simple_storage_collection(self):
|
||||
self.assertRaises(
|
||||
error.FishyError,
|
||||
self.test_driver.get_simple_storage_collection, self.uuid)
|
||||
|
||||
def test_get_secure_boot_off(self):
|
||||
self.node_mock.is_secure_boot = False
|
||||
self.assertFalse(self.test_driver.get_secure_boot(self.uuid))
|
||||
|
||||
def test_get_secure_boot_on(self):
|
||||
self.node_mock.is_secure_boot = True
|
||||
self.assertTrue(self.test_driver.get_secure_boot(self.uuid))
|
||||
|
||||
def test_set_secure_boot(self):
|
||||
self.assertRaises(
|
||||
error.NotSupportedError, self.test_driver.set_secure_boot,
|
||||
self.uuid, True)
|
Loading…
Reference in New Issue
Block a user