From 499536afcb9a7cdf598cf707cacda8eb7b9735ca Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Wed, 31 Oct 2018 11:06:36 +0100 Subject: [PATCH] Redirect to UUID URLs Redfish ComputerSystem is addressed by Redfish resource `Id`, while the resource can also carry the `name` and/or `uuid` properties. Technically, Redfish emulator could respond to queries performed against `uuid` and `name`. This change makes Redfish emulator HTTP redirecting all queries performed against `name` to the same REST endpoint addressing the same ComputerSystem by its `uuid` as a more canonical. Also, when listing ComputerSystems, `uuid` is now universally used. Prior to this change the libvirt driver advertised ComputerSystems names while nova driver advertised UUIDs. Story: 2004306 Task: 27867 Change-Id: Ic5dab0a5642d65d832f66092b7bf178ec58a3655 --- sushy_tools/emulator/drivers/base.py | 17 ++- sushy_tools/emulator/drivers/libvirtdriver.py | 55 ++++--- sushy_tools/emulator/drivers/novadriver.py | 53 ++++--- sushy_tools/emulator/main.py | 11 +- sushy_tools/emulator/templates/system.json | 2 +- sushy_tools/error.py | 4 + .../tests/unit/emulator/test_libvirt.py | 142 +++++++++--------- sushy_tools/tests/unit/emulator/test_main.py | 43 +++--- sushy_tools/tests/unit/emulator/test_nova.py | 116 ++++++++------ 9 files changed, 252 insertions(+), 191 deletions(-) diff --git a/sushy_tools/emulator/drivers/base.py b/sushy_tools/emulator/drivers/base.py index 58091eb5..e7f9ea4d 100644 --- a/sushy_tools/emulator/drivers/base.py +++ b/sushy_tools/emulator/drivers/base.py @@ -32,7 +32,7 @@ class AbstractDriver(object): def systems(self): """Return available computer systems - :returns: list of computer systems names. + :returns: list of UUIDs representing the systems """ @abc.abstractmethod @@ -43,11 +43,24 @@ class AbstractDriver(object): in place of system name if there are duplicates. If virtualization backend does not support non-unique system identity, - this property may just return the `identity`. + this method may just return the `identity`. :returns: computer system UUID """ + @abc.abstractmethod + def name(self, identity): + """Get computer system name by UUID + + The universal unique identifier (UUID) for this system. Can be used + in place of system name if there are duplicates. + + If virtualization backend does not support system names + this method may just return the `identity`. + + :returns: computer system name + """ + @abc.abstractmethod def get_power_state(self, identity): """Get computer system power state diff --git a/sushy_tools/emulator/drivers/libvirtdriver.py b/sushy_tools/emulator/drivers/libvirtdriver.py index 880fbf2c..5a3fd3b7 100644 --- a/sushy_tools/emulator/drivers/libvirtdriver.py +++ b/sushy_tools/emulator/drivers/libvirtdriver.py @@ -19,7 +19,7 @@ import xml.etree.ElementTree as ET from collections import namedtuple from sushy_tools.emulator.drivers.base import AbstractDriver -from sushy_tools.error import FishyError +from sushy_tools import error try: import libvirt @@ -57,7 +57,7 @@ class libvirt_open(object): except libvirt.libvirtError as e: msg = ('Error when connecting to the libvirt URI "%(uri)s": ' '%(error)s' % {'uri': self._uri, 'error': e}) - raise FishyError(msg) + raise error.FishyError(msg) def __exit__(self, type, value, traceback): self._conn.close() @@ -108,15 +108,15 @@ class LibvirtDriver(AbstractDriver): def _get_domain(self, identity, readonly=False): with libvirt_open(self._uri, readonly=readonly) as conn: try: - return conn.lookupByName(identity) + uu_identity = uuid.UUID(identity) - except libvirt.libvirtError as ex: + return conn.lookupByUUID(uu_identity.bytes) + + except (ValueError, libvirt.libvirtError): try: - uu_identity = uuid.UUID(identity) + domain = conn.lookupByName(identity) - return conn.lookupByUUID(uu_identity.bytes) - - except (ValueError, libvirt.libvirtError): + except libvirt.libvirtError as ex: msg = ('Error finding domain by name/UUID "%(identity)s" ' 'at libvirt URI %(uri)s": %(err)s' % {'identity': identity, @@ -124,7 +124,9 @@ class LibvirtDriver(AbstractDriver): logger.debug(msg) - raise FishyError(msg) + raise error.FishyError(msg) + + raise error.AliasAccessError(domain.UUIDString()) @property def driver(self): @@ -138,10 +140,11 @@ class LibvirtDriver(AbstractDriver): def systems(self): """Return available computer systems - :returns: list of computer systems names. + :returns: list of UUIDs representing the systems """ with libvirt_open(self._uri, readonly=True) as conn: - return conn.listDefinedDomains() + return [domain.UUIDString() + for domain in conn.listDefinedDomains()] def uuid(self, identity): """Get computer system UUID @@ -156,6 +159,16 @@ class LibvirtDriver(AbstractDriver): domain = self._get_domain(identity, readonly=True) return domain.UUIDString() + def name(self, identity): + """Get computer system name by name + + :param identity: libvirt domain name or UUID + + :returns: computer system name + """ + domain = self._get_domain(identity, readonly=True) + return domain.name() + def get_power_state(self, identity): """Get computer system power state @@ -176,7 +189,7 @@ class LibvirtDriver(AbstractDriver): Valid values are: *On*, *ForceOn*, *ForceOff*, *GracefulShutdown*, *GracefulRestart*, *ForceRestart*, *Nmi*. - :raises: `FishyError` if power state can't be set + :raises: `error.FishyError` if power state can't be set """ domain = self._get_domain(identity) @@ -204,7 +217,7 @@ class LibvirtDriver(AbstractDriver): msg = ('Error changing power state at libvirt URI "%(uri)s": ' '%(error)s' % {'uri': self._uri, 'error': e}) - raise FishyError(msg) + raise error.FishyError(msg) def get_boot_device(self, identity): """Get computer system boot device name @@ -234,7 +247,7 @@ class LibvirtDriver(AbstractDriver): change on the system. If not specified, current boot device is returned. Valid values are: *Pxe*, *Hdd*, *Cd*. - :raises: `FishyError` if boot device can't be set + :raises: `error.FishyError` if boot device can't be set """ domain = self._get_domain(identity) @@ -248,7 +261,7 @@ class LibvirtDriver(AbstractDriver): msg = ('Unknown power state requested: ' '%(boot_source)s' % {'boot_source': boot_source}) - raise FishyError(msg) + raise error.FishyError(msg) for os_element in tree.findall('os'): # Remove all "boot" elements @@ -267,7 +280,7 @@ class LibvirtDriver(AbstractDriver): msg = ('Error changing boot device at libvirt URI "%(uri)s": ' '%(error)s' % {'uri': self._uri, 'error': e}) - raise FishyError(msg) + raise error.FishyError(msg) def get_boot_mode(self, identity): """Get computer system boot mode. @@ -296,7 +309,7 @@ class LibvirtDriver(AbstractDriver): change on the system. If not specified, current boot mode is returned. Valid values are: *Uefi*, *Legacy*. - :raises: `FishyError` if boot mode can't be set + :raises: `error.FishyError` if boot mode can't be set """ domain = self._get_domain(identity, readonly=True) @@ -310,7 +323,7 @@ class LibvirtDriver(AbstractDriver): msg = ('Unknown boot mode requested: ' '%(boot_mode)s' % {'boot_mode': boot_mode}) - raise FishyError(msg) + raise error.FishyError(msg) for os_element in tree.findall('os'): type_element = os_element.find('type') @@ -346,7 +359,7 @@ class LibvirtDriver(AbstractDriver): '"%(uri)s": %(error)s' % {'uri': self._uri, 'error': e}) - raise FishyError(msg) + raise error.FishyError(msg) def get_total_memory(self, identity): """Get computer system total memory @@ -464,7 +477,7 @@ class LibvirtDriver(AbstractDriver): :returns: New or existing dict of BIOS attributes - :raises: `FishyError` if BIOS attributes cannot be saved + :raises: `error.FishyError` if BIOS attributes cannot be saved """ domain = self._get_domain(identity) @@ -481,7 +494,7 @@ class LibvirtDriver(AbstractDriver): msg = ('Error updating BIOS attributes' ' at libvirt URI "%(uri)s": ' '%(error)s' % {'uri': self._uri, 'error': e}) - raise FishyError(msg) + raise error.FishyError(msg) return result.bios_attributes diff --git a/sushy_tools/emulator/drivers/novadriver.py b/sushy_tools/emulator/drivers/novadriver.py index 93f515f2..3ead145b 100644 --- a/sushy_tools/emulator/drivers/novadriver.py +++ b/sushy_tools/emulator/drivers/novadriver.py @@ -17,7 +17,7 @@ import logging import math from sushy_tools.emulator.drivers.base import AbstractDriver -from sushy_tools.error import FishyError +from sushy_tools import error try: import openstack @@ -56,9 +56,12 @@ class OpenStackDriver(AbstractDriver): self._os_cloud = os_cloud def _get_instance(self, identity): - server = self._cc.get_server(identity) - if server: - return server + instance = self._cc.get_server(identity) + if instance: + if identity != instance.id: + raise error.AliasAccessError(instance.id) + + return instance msg = ('Error finding instance by UUID "%(identity)s" at OS ' 'cloud %(os_cloud)s"' % {'identity': identity, @@ -66,7 +69,7 @@ class OpenStackDriver(AbstractDriver): logger.debug(msg) - raise FishyError(msg) + raise error.FishyError(msg) def _get_flavor(self, identity): instance = self._get_instance(identity) @@ -85,7 +88,7 @@ class OpenStackDriver(AbstractDriver): def systems(self): """Return available computer systems - :returns: list of computer systems names. + :returns: list of UUIDs representing the systems """ return [server.id for server in self._cc.list_servers()] @@ -99,6 +102,16 @@ class OpenStackDriver(AbstractDriver): instance = self._get_instance(identity) return instance.id + def name(self, identity): + """Get computer system name by name + + :param identity: OpenStack instance name or ID + + :returns: computer system name + """ + instance = self._get_instance(identity) + return instance.name + def get_power_state(self, identity): """Get computer system power state @@ -110,7 +123,7 @@ class OpenStackDriver(AbstractDriver): try: instance = self._get_instance(identity) - except FishyError: + except error.FishyError: return if instance.power_state == self.NOVA_POWER_STATE_ON: @@ -127,7 +140,7 @@ class OpenStackDriver(AbstractDriver): returned. Valid values are: *On*, *ForceOn*, *ForceOff*, *GracefulShutdown*, *GracefulRestart*, *ForceRestart*, *Nmi*. - :raises: `FishyError` if power state can't be set + :raises: `error.FishyError` if power state can't be set """ instance = self._get_instance(identity) @@ -159,8 +172,8 @@ class OpenStackDriver(AbstractDriver): # NOTE(etingof) can't support `state == "Nmi"` as # openstacksdk does not seem to support that else: - raise FishyError('Unknown ResetType ' - '"%(state)s"' % {'state': state}) + raise error.FishyError( + 'Unknown ResetType "%(state)s"' % {'state': state}) def get_boot_device(self, identity): """Get computer system boot device name @@ -173,7 +186,7 @@ class OpenStackDriver(AbstractDriver): try: instance = self._get_instance(identity) - except FishyError: + except error.FishyError: return metadata = self._cc.compute.get_server_metadata(instance.id).to_dict() @@ -195,7 +208,7 @@ class OpenStackDriver(AbstractDriver): change on the system. If not specified, current boot device is returned. Valid values are: *Pxe*, *Hdd*, *Cd*. - :raises: `FishyError` if boot device can't be set + :raises: `error.FishyError` if boot device can't be set """ instance = self._get_instance(identity) @@ -206,7 +219,7 @@ class OpenStackDriver(AbstractDriver): msg = ('Unknown power state requested: ' '%(boot_source)s' % {'boot_source': boot_source}) - raise FishyError(msg) + raise error.FishyError(msg) # NOTE(etingof): the following probably only works with # libvirt-backed compute nodes @@ -237,7 +250,7 @@ class OpenStackDriver(AbstractDriver): change on the system. If not specified, current boot mode is returned. Valid values are: *Uefi*, *Legacy*. - :raises: `FishyError` if boot mode can't be set + :raises: `error.FishyError` if boot mode can't be set """ # just to make sure passed identity exists self._get_instance(identity) @@ -245,7 +258,7 @@ class OpenStackDriver(AbstractDriver): msg = ('The cloud driver %(driver)s does not allow changing boot ' 'mode through Redfish' % {'driver': self.driver}) - raise FishyError(msg) + raise error.FishyError(msg) def get_total_memory(self, identity): """Get computer system total memory @@ -258,7 +271,7 @@ class OpenStackDriver(AbstractDriver): try: flavor = self._get_flavor(identity) - except FishyError: + except error.FishyError: return return int(math.ceil(flavor.ram / 1024.)) @@ -274,24 +287,24 @@ class OpenStackDriver(AbstractDriver): try: flavor = self._get_flavor(identity) - except FishyError: + except error.FishyError: return return flavor.vcpus def get_bios(self, identity): """Not supported as Openstack SDK does not expose API for BIOS""" - raise FishyError( + raise error.FishyError( 'Operation not supported by the virtualization driver') def set_bios(self, identity, attributes): """Not supported as Openstack SDK does not expose API for BIOS""" - raise FishyError( + raise error.FishyError( 'Operation not supported by the virtualization driver') def reset_bios(self, identity): """Not supported as Openstack SDK does not expose API for BIOS""" - raise FishyError( + raise error.FishyError( 'Operation not supported by the virtualization driver') def get_nics(self, identity): diff --git a/sushy_tools/emulator/main.py b/sushy_tools/emulator/main.py index f5ad3db2..58cf497d 100755 --- a/sushy_tools/emulator/main.py +++ b/sushy_tools/emulator/main.py @@ -22,6 +22,7 @@ import sys from sushy_tools.emulator.drivers import libvirtdriver from sushy_tools.emulator.drivers import novadriver +from sushy_tools import error import flask @@ -85,6 +86,9 @@ def returns_json(decorated_func): @app.errorhandler(Exception) @returns_json def all_exception_handler(message): + if isinstance(message, error.AliasAccessError): + url = flask.url_for(flask.request.endpoint, identity=message.args[0]) + return flask.redirect(url, Response=flask.Response) return flask.render_template('error.json', message=message), 500 @@ -104,8 +108,7 @@ def system_collection_resource(): app.logger.debug('Serving systems list') return flask.render_template( - 'system_collection.json', system_count=len(systems), - systems=systems) + 'system_collection.json', system_count=len(systems), systems=systems) @app.route('/redfish/v1/Systems/', methods=['GET', 'PATCH']) @@ -117,7 +120,9 @@ def system_resource(identity): app.logger.debug('Serving resources for system "%s"', identity) return flask.render_template( - 'system.json', identity=identity, + 'system.json', + identity=identity, + name=driver.name(identity), uuid=driver.uuid(identity), power_state=driver.get_power_state(identity), total_memory_gb=driver.get_total_memory(identity), diff --git a/sushy_tools/emulator/templates/system.json b/sushy_tools/emulator/templates/system.json index 8b2bc516..99c0d44e 100644 --- a/sushy_tools/emulator/templates/system.json +++ b/sushy_tools/emulator/templates/system.json @@ -1,7 +1,7 @@ { "@odata.type": "#ComputerSystem.v1_1_0.ComputerSystem", "Id": {{ identity|string|tojson }}, - "Name": {{ identity|string|tojson }}, + "Name": {{ name|string|tojson }}, "UUID": {{ uuid|string|tojson }}, "Status": { "State": "Enabled", diff --git a/sushy_tools/error.py b/sushy_tools/error.py index 23a4a354..913517ac 100644 --- a/sushy_tools/error.py +++ b/sushy_tools/error.py @@ -16,3 +16,7 @@ class FishyError(Exception): """Create generic sushy-tools exception object""" + + +class AliasAccessError(FishyError): + """Node access attempted via an alias, not UUID""" diff --git a/sushy_tools/tests/unit/emulator/test_libvirt.py b/sushy_tools/tests/unit/emulator/test_libvirt.py index 8c3f4578..7cc86899 100644 --- a/sushy_tools/tests/unit/emulator/test_libvirt.py +++ b/sushy_tools/tests/unit/emulator/test_libvirt.py @@ -9,159 +9,151 @@ # 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 libvirt import uuid +import libvirt from oslotest import base from six.moves import mock from sushy_tools.emulator.drivers.libvirtdriver import LibvirtDriver -from sushy_tools.error import FishyError +from sushy_tools import error class LibvirtDriverTestCase(base.BaseTestCase): + name = 'QEmu-fedora-i686' + uuid = 'c7a5fdbd-cdaf-9455-926a-d65c16db1809' + def setUp(self): self.test_driver = LibvirtDriver() super(LibvirtDriverTestCase, self).setUp() @mock.patch('libvirt.open', autospec=True) def test__get_domain_by_name(self, libvirt_mock): - domain_id = 'name' - domain_info = 'domain' - conn_mock = libvirt_mock.return_value - lookupByName_mock = conn_mock.lookupByName - lookupByName_mock.return_value = domain_info lookupByUUID_mock = conn_mock.lookupByUUID - - domain = self.test_driver._get_domain(domain_id) - - self.assertEqual(domain_info, domain) - lookupByName_mock.assert_called_once_with(domain_id) - self.assertFalse(lookupByUUID_mock.called) + domain_mock = lookupByUUID_mock.return_value + domain_mock.UUIDString.return_value = self.uuid + self.assertRaises( + error.AliasAccessError, self.test_driver._get_domain, self.name) @mock.patch('libvirt.open', autospec=True) def test__get_domain_by_uuid(self, libvirt_mock): - domain_id = uuid.UUID('b9fbc4f5-2c81-4c80-97ea-272621fb7360') - domain_info = 'domain' + domain_id = uuid.UUID(self.uuid) conn_mock = libvirt_mock.return_value - lookupByName_mock = conn_mock.lookupByName - lookupByName_mock.side_effect = libvirt.libvirtError( - 'domain not found') lookupByUUID_mock = conn_mock.lookupByUUID - lookupByUUID_mock.return_value = domain_info - - domain = self.test_driver._get_domain(str(domain_id)) - - self.assertEqual(domain_info, domain) - lookupByName_mock.assert_called_once_with(str(domain_id)) + self.test_driver._get_domain(str(domain_id)) lookupByUUID_mock.assert_called_once_with(domain_id.bytes) @mock.patch('libvirt.openReadOnly', autospec=True) def test_uuid(self, libvirt_mock): conn_mock = libvirt_mock.return_value domain_mock = conn_mock.lookupByName.return_value - domain_mock.UUIDString.return_value = 'zzzz-yyyy-xxxx' - uuid = self.test_driver.uuid('name') - self.assertEqual('zzzz-yyyy-xxxx', uuid) + domain_mock.UUIDString.return_value = self.uuid + self.assertRaises(error.AliasAccessError, + self.test_driver.uuid, 'name') @mock.patch('libvirt.openReadOnly', autospec=True) def test_systems(self, libvirt_mock): conn_mock = libvirt_mock.return_value - conn_mock.listDefinedDomains.return_value = ['host0', 'host1'] + domain = mock.MagicMock() + domain.UUIDString.return_value = self.uuid + conn_mock.listDefinedDomains.return_value = [domain] systems = self.test_driver.systems - self.assertEqual(['host0', 'host1'], systems) + self.assertEqual([self.uuid], systems) @mock.patch('libvirt.openReadOnly', autospec=True) def test_get_power_state_on(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value - domain_mock.isActive.return_value = True + domain_mock = conn_mock.lookupByUUID.return_value + domain_mock.UUIDString.return_value = self.uuid - power_state = self.test_driver.get_power_state('zzzz-yyyy-xxxx') + domain_mock.isActive.return_value = True + domain_mock.maxMemory.return_value = 1024 * 1024 + domain_mock.maxVcpus.return_value = 2 + + power_state = self.test_driver.get_power_state(self.uuid) self.assertEqual('On', power_state) @mock.patch('libvirt.openReadOnly', autospec=True) def test_get_power_state_off(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.isActive.return_value = False - power_state = self.test_driver.get_power_state('zzzz-yyyy-xxxx') + power_state = self.test_driver.get_power_state(self.uuid) self.assertEqual('Off', power_state) @mock.patch('libvirt.open', autospec=True) def test_set_power_state_on(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.isActive.return_value = False - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'On') + self.test_driver.set_power_state(self.uuid, 'On') domain_mock.create.assert_called_once_with() @mock.patch('libvirt.open', autospec=True) def test_set_power_state_forceon(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.isActive.return_value = False - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'ForceOn') + self.test_driver.set_power_state(self.uuid, 'ForceOn') domain_mock.create.assert_called_once_with() @mock.patch('libvirt.open', autospec=True) def test_set_power_state_forceoff(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.isActive.return_value = True - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'ForceOff') + self.test_driver.set_power_state(self.uuid, 'ForceOff') domain_mock.destroy.assert_called_once_with() @mock.patch('libvirt.open', autospec=True) def test_set_power_state_gracefulshutdown(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.isActive.return_value = True - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'GracefulShutdown') + self.test_driver.set_power_state(self.uuid, 'GracefulShutdown') domain_mock.shutdown.assert_called_once_with() @mock.patch('libvirt.open', autospec=True) def test_set_power_state_gracefulrestart(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.isActive.return_value = True - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'GracefulRestart') + self.test_driver.set_power_state(self.uuid, 'GracefulRestart') domain_mock.reboot.assert_called_once_with() @mock.patch('libvirt.open', autospec=True) def test_set_power_state_forcerestart(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.isActive.return_value = True - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'ForceRestart') + self.test_driver.set_power_state(self.uuid, 'ForceRestart') domain_mock.reset.assert_called_once_with() @mock.patch('libvirt.open', autospec=True) def test_set_power_state_nmi(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.isActive.return_value = True - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'Nmi') + self.test_driver.set_power_state(self.uuid, 'Nmi') domain_mock.injectNMI.assert_called_once_with() @@ -171,10 +163,10 @@ class LibvirtDriverTestCase(base.BaseTestCase): data = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = data - boot_device = self.test_driver.get_boot_device('zzzz-yyyy-xxxx') + boot_device = self.test_driver.get_boot_device(self.uuid) self.assertEqual('Cd', boot_device) @@ -184,10 +176,10 @@ class LibvirtDriverTestCase(base.BaseTestCase): data = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = data - self.test_driver.set_boot_device('zzzz-yyyy-xxxx', 'Hdd') + self.test_driver.set_boot_device(self.uuid, 'Hdd') conn_mock.defineXML.assert_called_once_with(mock.ANY) @@ -197,10 +189,10 @@ class LibvirtDriverTestCase(base.BaseTestCase): data = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = data - boot_mode = self.test_driver.get_boot_mode('zzzz-yyyy-xxxx') + boot_mode = self.test_driver.get_boot_mode(self.uuid) self.assertEqual('Legacy', boot_mode) @@ -211,10 +203,10 @@ class LibvirtDriverTestCase(base.BaseTestCase): data = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = data - self.test_driver.set_boot_mode('zzzz-yyyy-xxxx', 'Uefi') + self.test_driver.set_boot_mode(self.uuid, 'Uefi') conn_mock = libvirt_rw_mock.return_value conn_mock.defineXML.assert_called_once_with(mock.ANY) @@ -222,22 +214,22 @@ class LibvirtDriverTestCase(base.BaseTestCase): @mock.patch('libvirt.openReadOnly', autospec=True) def test_get_total_memory(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.maxMemory.return_value = 1024 * 1024 - memory = self.test_driver.get_total_memory('zzzz-yyyy-xxxx') + memory = self.test_driver.get_total_memory(self.uuid) self.assertEqual(1, memory) @mock.patch('libvirt.openReadOnly', autospec=True) def test_get_total_cpus(self, libvirt_mock): conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.isActive.return_value = True domain_mock.XMLDesc.return_value = b'' domain_mock.maxVcpus.return_value = 2 - cpus = self.test_driver.get_total_cpus('zzzz-yyyy-xxxx') + cpus = self.test_driver.get_total_cpus(self.uuid) self.assertEqual(2, cpus) @@ -247,10 +239,10 @@ class LibvirtDriverTestCase(base.BaseTestCase): domain_xml = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = domain_xml - bios_attributes = self.test_driver.get_bios('xxx-yyy-zzz') + bios_attributes = self.test_driver.get_bios(self.uuid) self.assertEqual(LibvirtDriver.DEFAULT_BIOS_ATTRIBUTES, bios_attributes) conn_mock.defineXML.assert_called_once_with(mock.ANY) @@ -261,10 +253,10 @@ class LibvirtDriverTestCase(base.BaseTestCase): domain_xml = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = domain_xml - bios_attributes = self.test_driver.get_bios('xxx-yyy-zzz') + bios_attributes = self.test_driver.get_bios(self.uuid) self.assertEqual({"BootMode": "Bios", "EmbeddedSata": "Raid", "NicBoot1": "NetworkBoot", @@ -278,10 +270,10 @@ class LibvirtDriverTestCase(base.BaseTestCase): domain_xml = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = domain_xml - self.test_driver.set_bios('xxx-yyy-zzz', + self.test_driver.set_bios(self.uuid, {"BootMode": "Uefi", "ProcTurboMode": "Enabled"}) conn_mock.defineXML.assert_called_once_with(mock.ANY) @@ -292,10 +284,10 @@ class LibvirtDriverTestCase(base.BaseTestCase): domain_xml = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = domain_xml - self.test_driver.reset_bios('xxx-yyy-zzz') + self.test_driver.reset_bios(self.uuid) conn_mock.defineXML.assert_called_once_with(mock.ANY) def test__process_bios_attributes_get_default(self): @@ -358,12 +350,12 @@ class LibvirtDriverTestCase(base.BaseTestCase): domain_xml = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = domain_xml conn_mock.defineXML.side_effect = libvirt.libvirtError( 'because I can') - self.assertRaises(FishyError, + self.assertRaises(error.FishyError, self.test_driver._process_bios, 'xxx-yyy-zzz', {"BootMode": "Uefi", @@ -375,10 +367,10 @@ class LibvirtDriverTestCase(base.BaseTestCase): domain_xml = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = domain_xml - nics = self.test_driver.get_nics('xxx-yyy-zzz') + nics = self.test_driver.get_nics(self.uuid) self.assertEqual([{'id': '00:11:22:33:44:55', 'mac': '00:11:22:33:44:55'}, {'id': '52:54:00:4e:5d:37', @@ -391,8 +383,8 @@ class LibvirtDriverTestCase(base.BaseTestCase): domain_xml = f.read() conn_mock = libvirt_mock.return_value - domain_mock = conn_mock.lookupByName.return_value + domain_mock = conn_mock.lookupByUUID.return_value domain_mock.XMLDesc.return_value = domain_xml - nics = self.test_driver.get_nics('xxx-yyy-zzz') + nics = self.test_driver.get_nics(self.uuid) self.assertEqual([], nics) diff --git a/sushy_tools/tests/unit/emulator/test_main.py b/sushy_tools/tests/unit/emulator/test_main.py index d59cb316..0ed11031 100644 --- a/sushy_tools/tests/unit/emulator/test_main.py +++ b/sushy_tools/tests/unit/emulator/test_main.py @@ -19,6 +19,9 @@ from sushy_tools.emulator import main @mock.patch('sushy_tools.emulator.main.driver', autospec=True) class EmulatorTestCase(base.BaseTestCase): + name = 'QEmu-fedora-i686' + uuid = 'c7a5fdbd-cdaf-9455-926a-d65c16db1809' + def setUp(self): main.driver = None self.app = main.app.test_client() @@ -27,7 +30,7 @@ class EmulatorTestCase(base.BaseTestCase): def test_error(self, driver_mock): driver_mock.get_power_state.side_effect = Exception('Fish is dead') - response = self.app.get('/redfish/v1/Systems/xxxx-yyyy-zzzz') + response = self.app.get('/redfish/v1/Systems/' + self.uuid) self.assertEqual('500 INTERNAL SERVER ERROR', response.status) @@ -145,7 +148,7 @@ class EmulatorTestCase(base.BaseTestCase): def test_get_bios(self, driver_mock): driver_mock.get_bios.return_value = {"attribute 1": "value 1", "attribute 2": "value 2"} - response = self.app.get('/redfish/v1/Systems/xxxx-yyyy-zzzz/BIOS') + response = self.app.get('/redfish/v1/Systems/' + self.uuid + '/BIOS') self.assertEqual('200 OK', response.status) self.assertEqual('BIOS', response.json['Id']) @@ -157,7 +160,7 @@ class EmulatorTestCase(base.BaseTestCase): driver_mock.get_bios.return_value = {"attribute 1": "value 1", "attribute 2": "value 2"} response = self.app.get( - '/redfish/v1/Systems/xxxx-yyyy-zzzz/BIOS/Settings') + '/redfish/v1/Systems/' + self.uuid + '/BIOS/Settings') self.assertEqual(200, response.status_code) self.assertEqual('Settings', response.json['Id']) @@ -189,34 +192,34 @@ class EmulatorTestCase(base.BaseTestCase): def test_reset_bios(self, driver_mock): self.app.driver = driver_mock - response = self.app.post('/redfish/v1/Systems/xxxx-yyyy-zzzz/' - 'BIOS/Actions/Bios.ResetBios') + response = self.app.post('/redfish/v1/Systems/' + self.uuid + + '/BIOS/Actions/Bios.ResetBios') self.assertEqual(204, response.status_code) - driver_mock.reset_bios.assert_called_once_with('xxxx-yyyy-zzzz') + driver_mock.reset_bios.assert_called_once_with(self.uuid) def test_ethernet_interfaces_collection(self, driver_mock): driver_mock.get_nics.return_value = [{'id': 'nic1', 'mac': '52:54:00:4e:5d:37'}, {'id': 'nic2', 'mac': '00:11:22:33:44:55'}] - response = self.app.get('redfish/v1/Systems/xxx-yyy-zzz/' - 'EthernetInterfaces') + response = self.app.get('redfish/v1/Systems/' + self.uuid + + '/EthernetInterfaces') self.assertEqual(200, response.status_code) self.assertEqual('Ethernet Interface Collection', response.json['Name']) self.assertEqual(2, response.json['Members@odata.count']) - self.assertEqual(['/redfish/v1/Systems/xxx-yyy-zzz/' - 'EthernetInterfaces/nic1', - '/redfish/v1/Systems/xxx-yyy-zzz/' - 'EthernetInterfaces/nic2'], + self.assertEqual(['/redfish/v1/Systems/' + self.uuid + + '/EthernetInterfaces/nic1', + '/redfish/v1/Systems/' + self.uuid + + '/EthernetInterfaces/nic2'], [m['@odata.id'] for m in response.json['Members']]) def test_ethernet_interfaces_collection_empty(self, driver_mock): driver_mock.get_nics.return_value = [] - response = self.app.get('redfish/v1/Systems/xxx-yyy-zzz/' - 'EthernetInterfaces') + response = self.app.get('redfish/v1/Systems/' + self.uuid + + '/EthernetInterfaces') self.assertEqual(200, response.status_code) self.assertEqual('Ethernet Interface Collection', @@ -229,8 +232,8 @@ class EmulatorTestCase(base.BaseTestCase): 'mac': '52:54:00:4e:5d:37'}, {'id': 'nic2', 'mac': '00:11:22:33:44:55'}] - response = self.app.get('/redfish/v1/Systems/xxx-yyy-zzz/' - 'EthernetInterfaces/nic2') + response = self.app.get('/redfish/v1/Systems/' + self.uuid + + '/EthernetInterfaces/nic2') self.assertEqual(200, response.status_code) self.assertEqual('nic2', response.json['Id']) @@ -239,8 +242,8 @@ class EmulatorTestCase(base.BaseTestCase): response.json['PermanentMACAddress']) self.assertEqual('00:11:22:33:44:55', response.json['MACAddress']) - self.assertEqual('/redfish/v1/Systems/xxx-yyy-zzz/' - 'EthernetInterfaces/nic2', + self.assertEqual('/redfish/v1/Systems/' + self.uuid + + '/EthernetInterfaces/nic2', response.json['@odata.id']) def test_ethernet_interface_not_found(self, driver_mock): @@ -248,7 +251,7 @@ class EmulatorTestCase(base.BaseTestCase): 'mac': '52:54:00:4e:5d:37'}, {'id': 'nic2', 'mac': '00:11:22:33:44:55'}] - response = self.app.get('/redfish/v1/Systems/xxx-yyy-zzz/' - 'EthernetInterfaces/nic3') + response = self.app.get('/redfish/v1/Systems/' + self.uuid + + '/EthernetInterfaces/nic3') self.assertEqual(404, response.status_code) diff --git a/sushy_tools/tests/unit/emulator/test_nova.py b/sushy_tools/tests/unit/emulator/test_nova.py index 94d8cfe8..c92a8f43 100644 --- a/sushy_tools/tests/unit/emulator/test_nova.py +++ b/sushy_tools/tests/unit/emulator/test_nova.py @@ -23,6 +23,9 @@ from sushy_tools import error class NovaDriverTestCase(base.BaseTestCase): + name = 'QEmu-fedora-i686' + uuid = 'c7a5fdbd-cdaf-9455-926a-d65c16db1809' + def setUp(self): self.nova_patcher = mock.patch('openstack.connect', autospec=True) self.nova_mock = self.nova_patcher.start() @@ -36,89 +39,88 @@ class NovaDriverTestCase(base.BaseTestCase): super(NovaDriverTestCase, self).tearDown() def test_uuid(self): - server = mock.Mock(id='zzzz-yyyy-xxxx') + server = mock.Mock(id=self.uuid) self.nova_mock.return_value.get_server.return_value = server - uuid = self.test_driver.uuid('zzzz-yyyy-xxxx') - self.assertEqual('zzzz-yyyy-xxxx', uuid) + uuid = self.test_driver.uuid(self.uuid) + self.assertEqual(self.uuid, uuid) def test_systems(self): server0 = mock.Mock(id='host0') server1 = mock.Mock(id='host1') self.nova_mock.return_value.list_servers.return_value = [ server0, server1] - systems = self.test_driver.systems self.assertEqual(['host0', 'host1'], systems) def test_get_power_state_on(self,): - server = mock.Mock(id='zzzz-yyyy-xxxx', + server = mock.Mock(id=self.uuid, power_state=1) self.nova_mock.return_value.get_server.return_value = server - power_state = self.test_driver.get_power_state('zzzz-yyyy-xxxx') + power_state = self.test_driver.get_power_state(self.uuid) self.assertEqual('On', power_state) def test_get_power_state_off(self): - server = mock.Mock(id='zzzz-yyyy-xxxx', + server = mock.Mock(id=self.uuid, power_state=0) self.nova_mock.return_value.get_server.return_value = server - power_state = self.test_driver.get_power_state('zzzz-yyyy-xxxx') + power_state = self.test_driver.get_power_state(self.uuid) self.assertEqual('Off', power_state) def test_set_power_state_on(self): - server = mock.Mock(id='zzzz-yyyy-xxxx', power_state=0) + server = mock.Mock(id=self.uuid, power_state=0) self.nova_mock.return_value.get_server.return_value = server - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'On') + self.test_driver.set_power_state(self.uuid, 'On') compute = self.nova_mock.return_value.compute - compute.start_server.assert_called_once_with('zzzz-yyyy-xxxx') + compute.start_server.assert_called_once_with(self.uuid) def test_set_power_state_forceon(self): - server = mock.Mock(id='zzzz-yyyy-xxxx', power_state=0) + server = mock.Mock(id=self.uuid, power_state=0) self.nova_mock.return_value.get_server.return_value = server - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'ForceOn') + self.test_driver.set_power_state(self.uuid, 'ForceOn') compute = self.nova_mock.return_value.compute - compute.start_server.assert_called_once_with('zzzz-yyyy-xxxx') + compute.start_server.assert_called_once_with(self.uuid) def test_set_power_state_forceoff(self): - server = mock.Mock(id='zzzz-yyyy-xxxx', power_state=1) + server = mock.Mock(id=self.uuid, power_state=1) self.nova_mock.return_value.get_server.return_value = server - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'ForceOff') + self.test_driver.set_power_state(self.uuid, 'ForceOff') compute = self.nova_mock.return_value.compute - compute.stop_server.assert_called_once_with('zzzz-yyyy-xxxx') + compute.stop_server.assert_called_once_with(self.uuid) def test_set_power_state_gracefulshutdown(self): - server = mock.Mock(id='zzzz-yyyy-xxxx', power_state=1) + server = mock.Mock(id=self.uuid, power_state=1) self.nova_mock.return_value.get_server.return_value = server - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'GracefulShutdown') + self.test_driver.set_power_state(self.uuid, 'GracefulShutdown') compute = self.nova_mock.return_value.compute - compute.stop_server.assert_called_once_with('zzzz-yyyy-xxxx') + compute.stop_server.assert_called_once_with(self.uuid) def test_set_power_state_gracefulrestart(self): - server = mock.Mock(id='zzzz-yyyy-xxxx', power_state=1) + server = mock.Mock(id=self.uuid, power_state=1) self.nova_mock.return_value.get_server.return_value = server - self.test_driver.set_power_state('zzzz-yyyy-xxxx', 'GracefulRestart') + self.test_driver.set_power_state(self.uuid, 'GracefulRestart') compute = self.nova_mock.return_value.compute compute.reboot_server.assert_called_once_with( - 'zzzz-yyyy-xxxx', reboot_type='SOFT') + self.uuid, reboot_type='SOFT') def test_set_power_state_forcerestart(self): - server = mock.Mock(id='zzzz-yyyy-xxxx', power_state=1) + server = mock.Mock(id=self.uuid, power_state=1) self.nova_mock.return_value.get_server.return_value = server self.test_driver.set_power_state( - 'zzzz-yyyy-xxxx', 'ForceRestart') + self.uuid, 'ForceRestart') compute = self.nova_mock.return_value.compute compute.reboot_server.assert_called_once_with( - 'zzzz-yyyy-xxxx', reboot_type='HARD') + self.uuid, reboot_type='HARD') def test_get_boot_device(self): - server = mock.Mock(id='zzzz-yyyy-xxxx') + server = mock.Mock(id=self.uuid) self.nova_mock.return_value.get_server.return_value = server - boot_device = self.test_driver.get_boot_device('zzzz-yyyy-xxxx') + boot_device = self.test_driver.get_boot_device(self.uuid) self.assertEqual('Pxe', boot_device) get_server_metadata = ( @@ -126,58 +128,70 @@ class NovaDriverTestCase(base.BaseTestCase): get_server_metadata.assert_called_once_with(server.id) def test_set_boot_device(self): - server = mock.Mock(id='zzzz-yyyy-xxxx') + server = mock.Mock(id=self.uuid) self.nova_mock.return_value.get_server.return_value = server compute = self.nova_mock.return_value.compute set_server_metadata = compute.set_server_metadata - self.test_driver.set_boot_device('zzzz-yyyy-xxxx', 'Pxe') + self.test_driver.set_boot_device(self.uuid, 'Pxe') set_server_metadata.assert_called_once_with( - 'zzzz-yyyy-xxxx', **{'libvirt:pxe-first': '1'} + self.uuid, **{'libvirt:pxe-first': '1'} ) def test_get_boot_mode(self): + server = mock.Mock(id=self.uuid, image=dict(id=self.uuid)) + self.nova_mock.return_value.get_server.return_value = server + image = mock.Mock(hw_firmware_type='bios') self.nova_mock.return_value.image.find_image.return_value = image - boot_mode = self.test_driver.get_boot_mode('zzzz-yyyy-xxxx') + 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, - 'zzzz-yyyy-xxxx', 'Legacy') + self.uuid, 'Legacy') def test_get_total_memory(self): + server = mock.Mock(id=self.uuid) + self.nova_mock.return_value.get_server.return_value = server + flavor = mock.Mock(ram=1024) self.nova_mock.return_value.get_flavor.return_value = flavor - memory = self.test_driver.get_total_memory('zzzz-yyyy-xxxx') + memory = self.test_driver.get_total_memory(self.uuid) self.assertEqual(1, memory) def test_get_total_cpus(self): + server = mock.Mock(id=self.uuid) + self.nova_mock.return_value.get_server.return_value = server + flavor = mock.Mock(vcpus=2) self.nova_mock.return_value.get_flavor.return_value = flavor - cpus = self.test_driver.get_total_cpus('zzzz-yyyy-xxxx') + 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, - 'xxx-yyy-zzz') + 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, - 'xxx-yyy-zzz', {'attribute 1': 'value 1'}) + 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, - 'xxx-yyy-zzz') + self.assertRaises( + error.FishyError, self.test_driver.reset_bios, self.uuid) def test_get_nics(self): addresses = Munch( @@ -199,10 +213,13 @@ class NovaDriverTestCase(base.BaseTestCase): u'version': 4, u'addr': u'10.0.0.10', u'OS-EXT-IPS:type': u'fixed'})]}) - server = mock.Mock(addresses=addresses) + + server = mock.Mock(id=self.uuid, addresses=addresses) self.nova_mock.return_value.get_server.return_value = server - nics = self.test_driver.get_nics('xxxx-yyyy-zzzz') + test_driver = OpenStackDriver('fake-cloud') + nics = test_driver.get_nics(self.uuid) + self.assertEqual([{'id': 'fa:16:3e:22:18:31', 'mac': 'fa:16:3e:22:18:31'}, {'id': 'fa:16:3e:46:e3:ac', @@ -210,9 +227,10 @@ class NovaDriverTestCase(base.BaseTestCase): sorted(nics, key=lambda k: k['id'])) def test_get_nics_empty(self): - server = mock.Mock(addresses=None) + server = mock.Mock(id=self.uuid, addresses=None) self.nova_mock.return_value.get_server.return_value = server - nics = self.test_driver.get_nics('xxxx-yyyy-zzzz') + test_driver = OpenStackDriver('fake-cloud') + nics = test_driver.get_nics(self.uuid) self.assertEqual(set(), nics) def test_get_nics_error(self): @@ -231,9 +249,9 @@ class NovaDriverTestCase(base.BaseTestCase): u'version': 4, u'addr': u'10.0.0.10', u'OS-EXT-IPS:type': u'fixed'})]}) - server = mock.Mock(addresses=addresses) + + server = mock.Mock(id=self.uuid, addresses=addresses) self.nova_mock.return_value.get_server.return_value = server - nics = self.test_driver.get_nics('xxxx-yyyy-zzzz') + nics = self.test_driver.get_nics(self.uuid) self.assertEqual([{'id': 'fa:16:3e:22:18:31', - 'mac': 'fa:16:3e:22:18:31'}], - nics) + 'mac': 'fa:16:3e:22:18:31'}], nics)