diff --git a/metalsmith/_instance.py b/metalsmith/_instance.py index 477ce5d..3b76baa 100644 --- a/metalsmith/_instance.py +++ b/metalsmith/_instance.py @@ -77,8 +77,9 @@ class Instance(object): result = [] vifs = self._api.list_node_attached_ports(self.node) for vif in vifs: - port = self._api.get_port(vif.id) - port.network = self._api.get_network(port.network_id) + port = self._api.connection.network.get_port(vif.id) + port.network = self._api.connection.network.get_network( + port.network_id) result.append(port) return result diff --git a/metalsmith/_os_api.py b/metalsmith/_os_api.py index 933771c..b646516 100644 --- a/metalsmith/_os_api.py +++ b/metalsmith/_os_api.py @@ -17,7 +17,6 @@ import contextlib import logging from ironicclient import client as ir_client -from openstack import connection import six from metalsmith import _utils @@ -60,24 +59,11 @@ class API(object): _node_list = None - def __init__(self, session=None, cloud_region=None): - if cloud_region is None: - if session is None: - raise TypeError('Either session or cloud_region must ' - 'be provided') - self.session = session - self.connection = connection.Connection(session=session) - elif session is not None: - raise TypeError('Either session or cloud_region must be provided, ' - 'but not both') - else: - self.session = cloud_region.get_session() - self.connection = connection.Connection(config=cloud_region) - - LOG.debug('Creating service clients') + def __init__(self, session, connection): self.ironic = ir_client.get_client( - self.IRONIC_VERSION, session=self.session, + self.IRONIC_VERSION, session=session, os_ironic_api_version=self.IRONIC_MICRO_VERSION) + self.connection = connection def _nodes_for_lookup(self): return self.list_nodes(maintenance=None, @@ -95,15 +81,6 @@ class API(object): yield self._node_list self._node_list = None - def create_port(self, network_id, **kwargs): - return self.connection.network.create_port(network_id=network_id, - admin_state_up=True, - **kwargs) - - def delete_port(self, port_id): - self.connection.network.delete_port(port_id, - ignore_missing=False) - def detach_port_from_node(self, node, port_id): self.ironic.node.vif_detach(_node_id(node), port_id) @@ -123,14 +100,6 @@ class API(object): # Fetch the complete node record return self.get_node(existing[0].uuid, accept_hostname=False) - def get_image(self, image_id): - return self.connection.image.find_image(image_id, - ignore_missing=False) - - def get_network(self, network_id): - return self.connection.network.find_network(network_id, - ignore_missing=False) - def get_node(self, node, refresh=False, accept_hostname=False): if isinstance(node, six.string_types): if accept_hostname and _utils.is_hostname_safe(node): @@ -150,10 +119,6 @@ class API(object): else: return node - def get_port(self, port_id): - return self.connection.network.find_port(port_id, - ignore_missing=False) - def list_node_attached_ports(self, node): return self.ironic.node.vif_list(_node_id(node)) diff --git a/metalsmith/_provisioner.py b/metalsmith/_provisioner.py index 2514e1d..d9668e8 100644 --- a/metalsmith/_provisioner.py +++ b/metalsmith/_provisioner.py @@ -18,6 +18,7 @@ import random import sys import time +from openstack import connection import six from metalsmith import _config @@ -44,10 +45,25 @@ class Provisioner(object): to use when making API requests. Mutually exclusive with **session**. :param dry_run: boolean value, set to ``True`` to prevent any API calls from being actually made. + + :ivar connection: `openstacksdk` `Connection` object used for accessing + OpenStack API during provisioning. """ def __init__(self, session=None, cloud_region=None, dry_run=False): - self._api = _os_api.API(session=session, cloud_region=cloud_region) + if cloud_region is None: + if session is None: + raise TypeError('Either session or cloud_region must ' + 'be provided') + self.connection = connection.Connection(session=session) + elif session is not None: + raise TypeError('Either session or cloud_region must be provided, ' + 'but not both') + else: + session = cloud_region.get_session() + self.connection = connection.Connection(config=cloud_region) + + self._api = _os_api.API(session, self.connection) self._dry_run = dry_run def reserve_node(self, resource_class=None, conductor_group=None, @@ -228,7 +244,7 @@ class Provisioner(object): hostname = self._check_hostname(node, hostname) root_disk_size = _utils.get_root_disk(root_disk_size, node) - image._validate(self._api) + image._validate(self.connection) nics = self._get_nics(nics or []) @@ -250,7 +266,7 @@ class Provisioner(object): '/extra/%s' % _CREATED_PORTS: created_ports, '/extra/%s' % _ATTACHED_PORTS: attached_ports, '/instance_info/%s' % _os_api.HOSTNAME_FIELD: hostname} - updates.update(image._node_updates(self._api)) + updates.update(image._node_updates(self.connection)) LOG.debug('Updating node %(node)s with %(updates)s', {'node': _utils.log_node(node), 'updates': updates}) @@ -299,7 +315,8 @@ class Provisioner(object): nic_type, nic_id = next(iter(nic.items())) if nic_type == 'network': try: - network = self._api.get_network(nic_id) + network = self.connection.network.find_network( + nic_id, ignore_missing=False) except Exception as exc: raise exceptions.InvalidNIC( 'Cannot find network %(net)s: %(error)s' % @@ -308,7 +325,8 @@ class Provisioner(object): result.append((nic_type, network)) else: try: - port = self._api.get_port(nic_id) + port = self.connection.network.find_port( + nic_id, ignore_missing=False) except Exception as exc: raise exceptions.InvalidNIC( 'Cannot find port %(port)s: %(error)s' % @@ -323,7 +341,7 @@ class Provisioner(object): """Create and attach ports on given networks.""" for nic_type, nic in nics: if nic_type == 'network': - port = self._api.create_port(network_id=nic.id) + port = self.connection.network.create_port(network_id=nic.id) created_ports.append(port.id) LOG.info('Created port %(port)s for node %(node)s on ' 'network %(net)s', @@ -359,7 +377,8 @@ class Provisioner(object): for port_id in created_ports: LOG.debug('Deleting port %s', port_id) try: - self._api.delete_port(port_id) + self.connection.network.delete_port(port_id, + ignore_missing=False) except Exception as exc: LOG.warning('Failed to delete neutron port %(port)s: %(exc)s', {'port': port_id, 'exc': exc}) diff --git a/metalsmith/sources.py b/metalsmith/sources.py index 18a71e8..1cc39bf 100644 --- a/metalsmith/sources.py +++ b/metalsmith/sources.py @@ -18,6 +18,7 @@ import abc import logging +import openstack.exceptions import six from metalsmith import exceptions @@ -29,11 +30,11 @@ LOG = logging.getLogger(__name__) @six.add_metaclass(abc.ABCMeta) class _Source(object): - def _validate(self, api): + def _validate(self, connection): """Validate the source.""" @abc.abstractmethod - def _node_updates(self, api): + def _node_updates(self, connection): """Updates required for a node to use this source.""" @@ -48,18 +49,19 @@ class Glance(_Source): self._image_id = image self._image_obj = None - def _validate(self, api): + def _validate(self, connection): if self._image_obj is not None: return try: - self._image_obj = api.get_image(self._image_id) - except Exception as exc: + self._image_obj = connection.image.find_image(self._image_id, + ignore_missing=False) + except openstack.exceptions.SDKException as exc: raise exceptions.InvalidImage( 'Cannot find image %(image)s: %(error)s' % {'image': self._image_id, 'error': exc}) - def _node_updates(self, api): - self._validate(api) + def _node_updates(self, connection): + self._validate(connection) LOG.debug('Image: %s', self._image_obj) updates = { diff --git a/metalsmith/test/test_instance.py b/metalsmith/test/test_instance.py index c1f0a89..f53046e 100644 --- a/metalsmith/test/test_instance.py +++ b/metalsmith/test/test_instance.py @@ -31,13 +31,13 @@ class TestInstanceIPAddresses(test_provisioner.Base): network_id=n, fixed_ips=[{'ip_address': ip}]) for n, ip in [('0', '192.168.0.1'), ('1', '10.0.0.2')] ] - self.api.get_port.side_effect = self.ports + self.conn.network.get_port.side_effect = self.ports self.nets = [ mock.Mock(spec=['id', 'name'], id=str(i)) for i in range(2) ] for n in self.nets: n.name = 'name-%s' % n.id - self.api.get_network.side_effect = self.nets + self.conn.network.get_network.side_effect = self.nets def test_ip_addresses(self): ips = self.instance.ip_addresses() diff --git a/metalsmith/test/test_os_api.py b/metalsmith/test/test_os_api.py index 0fac915..49cb13e 100644 --- a/metalsmith/test/test_os_api.py +++ b/metalsmith/test/test_os_api.py @@ -21,27 +21,6 @@ from metalsmith import _instance from metalsmith import _os_api -class TestInit(testtools.TestCase): - def test_missing_auth(self): - self.assertRaisesRegex(TypeError, 'must be provided', _os_api.API) - - def test_both_provided(self): - self.assertRaisesRegex(TypeError, 'not both', _os_api.API, - session=mock.Mock(), cloud_region=mock.Mock()) - - def test_session_only(self): - session = mock.Mock() - api = _os_api.API(session=session) - self.assertIs(api.session, session) - - @mock.patch.object(_os_api.connection, 'Connection', autospec=True) - def test_cloud_region_only(self, mock_conn): - region = mock.Mock() - api = _os_api.API(cloud_region=region) - self.assertIs(api.session, region.get_session.return_value) - mock_conn.assert_called_once_with(config=region) - - class TestNodes(testtools.TestCase): def setUp(self): super(TestNodes, self).setUp() @@ -50,7 +29,7 @@ class TestNodes(testtools.TestCase): fixtures.MockPatchObject(_os_api.ir_client, 'get_client', autospec=True)) self.cli = self.ironic_fixture.mock.return_value - self.api = _os_api.API(session=self.session) + self.api = _os_api.API(session=self.session, connection=mock.Mock()) def test_get_node_by_uuid(self): res = self.api.get_node('uuid1') diff --git a/metalsmith/test/test_provisioner.py b/metalsmith/test/test_provisioner.py index b4216cf..4b17900 100644 --- a/metalsmith/test/test_provisioner.py +++ b/metalsmith/test/test_provisioner.py @@ -15,6 +15,7 @@ import fixtures import mock +from openstack import exceptions as os_exc import testtools from metalsmith import _config @@ -30,6 +31,28 @@ NODE_FIELDS = ['name', 'uuid', 'instance_info', 'instance_uuid', 'maintenance', 'last_error'] +class TestInit(testtools.TestCase): + def test_missing_auth(self): + self.assertRaisesRegex(TypeError, 'must be provided', + _provisioner.Provisioner) + + def test_both_provided(self): + self.assertRaisesRegex(TypeError, 'not both', _provisioner.Provisioner, + session=mock.Mock(), cloud_region=mock.Mock()) + + @mock.patch.object(_provisioner.connection, 'Connection', autospec=True) + def test_session_only(self, mock_conn): + session = mock.Mock() + _provisioner.Provisioner(session=session) + mock_conn.assert_called_once_with(session=session) + + @mock.patch.object(_provisioner.connection, 'Connection', autospec=True) + def test_cloud_region_only(self, mock_conn): + region = mock.Mock() + _provisioner.Provisioner(cloud_region=region) + mock_conn.assert_called_once_with(config=region) + + class Base(testtools.TestCase): def setUp(self): @@ -58,6 +81,10 @@ class Base(testtools.TestCase): self.api.cache_node_list_for_lookup = mock.MagicMock() self.pr._api = self.api + self.conn = mock.Mock(spec=['image', 'network', 'baremetal']) + self.pr.connection = self.conn + self.api.connection = self.conn + class TestReserveNode(Base): @@ -210,19 +237,19 @@ class TestProvisionNode(Base): def setUp(self): super(TestProvisionNode, self).setUp() - image = self.api.get_image.return_value + self.image = self.conn.image.find_image.return_value self.node.instance_uuid = self.node.uuid self.updates = { - '/instance_info/ramdisk': image.ramdisk_id, - '/instance_info/kernel': image.kernel_id, - '/instance_info/image_source': image.id, + '/instance_info/ramdisk': self.image.ramdisk_id, + '/instance_info/kernel': self.image.kernel_id, + '/instance_info/image_source': self.image.id, '/instance_info/root_gb': 99, # 100 - 1 '/instance_info/capabilities': {'boot_option': 'local'}, '/extra/metalsmith_created_ports': [ - self.api.create_port.return_value.id + self.conn.network.create_port.return_value.id ], '/extra/metalsmith_attached_ports': [ - self.api.create_port.return_value.id + self.conn.network.create_port.return_value.id ], '/instance_info/%s' % _os_api.HOSTNAME_FIELD: 'control-0' } @@ -239,10 +266,10 @@ class TestProvisionNode(Base): self.assertEqual(inst.uuid, self.node.uuid) self.assertEqual(inst.node, self.node) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -250,7 +277,7 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_ok_with_source(self): inst = self.pr.provision_node(self.node, sources.Glance('image'), @@ -259,10 +286,10 @@ class TestProvisionNode(Base): self.assertEqual(inst.uuid, self.node.uuid) self.assertEqual(inst.node, self.node) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -270,7 +297,7 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_with_config(self): config = mock.MagicMock(spec=_config.InstanceConfig) @@ -283,10 +310,10 @@ class TestProvisionNode(Base): config.build_configdrive_directory.assert_called_once_with( self.node, self.node.name) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -294,7 +321,7 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_with_hostname(self): hostname = 'control-0.example.com' @@ -306,10 +333,10 @@ class TestProvisionNode(Base): self.assertEqual(inst.uuid, self.node.uuid) self.assertEqual(inst.node, self.node) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -317,7 +344,7 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_name_not_valid_hostname(self): self.node.name = 'node_1' @@ -328,10 +355,10 @@ class TestProvisionNode(Base): self.assertEqual(inst.uuid, self.node.uuid) self.assertEqual(inst.node, self.node) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -339,7 +366,7 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_unreserved(self): self.node.instance_uuid = None @@ -348,10 +375,10 @@ class TestProvisionNode(Base): self.api.reserve_node.assert_called_once_with( self.node, instance_uuid=self.node.uuid) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -359,23 +386,24 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_with_ports(self): self.updates['/extra/metalsmith_created_ports'] = [] self.updates['/extra/metalsmith_attached_ports'] = [ - self.api.get_port.return_value.id + self.conn.network.find_port.return_value.id ] * 2 self.pr.provision_node(self.node, 'image', [{'port': 'port1'}, {'port': 'port2'}]) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.api.attach_port_to_node.assert_called_with( - self.node.uuid, self.api.get_port.return_value.id) + self.node.uuid, self.conn.network.find_port.return_value.id) self.assertEqual(2, self.api.attach_port_to_node.call_count) - self.assertEqual([mock.call('port1'), mock.call('port2')], - self.api.get_port.call_args_list) + self.assertEqual([mock.call('port1', ignore_missing=False), + mock.call('port2', ignore_missing=False)], + self.conn.network.find_port.call_args_list) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -383,21 +411,20 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_whole_disk(self): - image = self.api.get_image.return_value - image.kernel_id = None - image.ramdisk_id = None + self.image.kernel_id = None + self.image.ramdisk_id = None del self.updates['/instance_info/kernel'] del self.updates['/instance_info/ramdisk'] self.pr.provision_node(self.node, 'image', [{'network': 'network'}]) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -405,7 +432,7 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_with_root_disk_size(self): self.updates['/instance_info/root_gb'] = 50 @@ -413,10 +440,10 @@ class TestProvisionNode(Base): self.pr.provision_node(self.node, 'image', [{'network': 'network'}], root_disk_size=50) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -424,7 +451,7 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_with_capabilities(self): inst = self.pr.provision_node(self.node, 'image', @@ -436,10 +463,10 @@ class TestProvisionNode(Base): self.assertEqual(inst.uuid, self.node.uuid) self.assertEqual(inst.node, self.node) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -447,7 +474,7 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_with_existing_capabilities(self): self.node.instance_info['capabilities'] = {'answer': '42'} @@ -459,10 +486,10 @@ class TestProvisionNode(Base): self.assertEqual(inst.uuid, self.node.uuid) self.assertEqual(inst.node, self.node) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -470,7 +497,7 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_override_existing_capabilities(self): self.node.instance_info['capabilities'] = {'answer': '1', @@ -484,10 +511,10 @@ class TestProvisionNode(Base): self.assertEqual(inst.uuid, self.node.uuid) self.assertEqual(inst.node, self.node) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -495,20 +522,20 @@ class TestProvisionNode(Base): configdrive=mock.ANY) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_with_wait(self): - self.api.get_port.return_value = mock.Mock( + self.conn.network.find_port.return_value = mock.Mock( spec=['fixed_ips'], fixed_ips=[{'ip_address': '192.168.1.5'}, {}] ) self.pr.provision_node(self.node, 'image', [{'network': 'network'}], wait=3600) - self.api.create_port.assert_called_once_with( - network_id=self.api.get_network.return_value.id) + self.conn.network.create_port.assert_called_once_with( + network_id=self.conn.network.find_network.return_value.id) self.api.attach_port_to_node.assert_called_once_with( - self.node.uuid, self.api.create_port.return_value.id) + self.node.uuid, self.conn.network.create_port.return_value.id) self.api.update_node.assert_called_once_with(self.node, self.updates) self.api.validate_node.assert_called_once_with(self.node, validate_deploy=True) @@ -520,19 +547,19 @@ class TestProvisionNode(Base): delay=15, timeout=3600) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_dry_run(self): self.pr._dry_run = True self.pr.provision_node(self.node, 'image', [{'network': 'network'}]) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.attach_port_to_node.called) self.assertFalse(self.api.update_node.called) self.assertFalse(self.api.node_action.called) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_unreserve_dry_run(self): self.pr._dry_run = True @@ -541,13 +568,13 @@ class TestProvisionNode(Base): self.pr.provision_node(self.node, 'image', [{'network': 'network'}]) self.assertFalse(self.api.reserve_node.called) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.attach_port_to_node.called) self.assertFalse(self.api.update_node.called) self.assertFalse(self.api.node_action.called) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) def test_deploy_failure(self): self.api.node_action.side_effect = RuntimeError('boom') @@ -559,16 +586,18 @@ class TestProvisionNode(Base): self.api.update_node.assert_any_call(self.node, CLEAN_UP) self.assertFalse(self.wait_mock.called) self.api.release_node.assert_called_once_with(self.node) - self.api.delete_port.assert_called_once_with( - self.api.create_port.return_value.id) + self.conn.network.delete_port.assert_called_once_with( + self.conn.network.create_port.return_value.id, + ignore_missing=False) calls = [ - mock.call(self.node, self.api.create_port.return_value.id), - mock.call(self.node, self.api.get_port.return_value.id) + mock.call(self.node, + self.conn.network.create_port.return_value.id), + mock.call(self.node, self.conn.network.find_port.return_value.id) ] self.api.detach_port_from_node.assert_has_calls(calls, any_order=True) def test_port_creation_failure(self): - self.api.create_port.side_effect = RuntimeError('boom') + self.conn.network.create_port.side_effect = RuntimeError('boom') self.assertRaisesRegex(RuntimeError, 'boom', self.pr.provision_node, self.node, 'image', [{'network': 'network'}], wait=3600) @@ -576,7 +605,7 @@ class TestProvisionNode(Base): self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_once_with(self.node) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) self.assertFalse(self.api.detach_port_from_node.called) def test_port_attach_failure(self): @@ -588,15 +617,32 @@ class TestProvisionNode(Base): self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_once_with(self.node) - self.api.delete_port.assert_called_once_with( - self.api.create_port.return_value.id) + self.conn.network.delete_port.assert_called_once_with( + self.conn.network.create_port.return_value.id, + ignore_missing=False) self.api.detach_port_from_node.assert_called_once_with( - self.node, self.api.create_port.return_value.id) + self.node, self.conn.network.create_port.return_value.id) + + def test_failure_during_port_deletion(self): + self.conn.network.delete_port.side_effect = AssertionError() + self.api.node_action.side_effect = RuntimeError('boom') + self.assertRaisesRegex(RuntimeError, 'boom', + self.pr.provision_node, self.node, + 'image', [{'network': 'network'}], + wait=3600) + + self.assertFalse(self.wait_mock.called) + self.api.release_node.assert_called_once_with(self.node) + self.conn.network.delete_port.assert_called_once_with( + self.conn.network.create_port.return_value.id, + ignore_missing=False) + self.api.detach_port_from_node.assert_called_once_with( + self.node, self.conn.network.create_port.return_value.id) @mock.patch.object(_provisioner.LOG, 'exception', autospec=True) def test_failure_during_deploy_failure(self, mock_log_exc): for failed_call in ['detach_port_from_node', - 'delete_port', 'release_node']: + 'release_node']: self._reset_api_mock() getattr(self.api, failed_call).side_effect = AssertionError() self.api.node_action.side_effect = RuntimeError('boom') @@ -607,10 +653,11 @@ class TestProvisionNode(Base): self.assertFalse(self.wait_mock.called) self.api.release_node.assert_called_once_with(self.node) - self.api.delete_port.assert_called_once_with( - self.api.create_port.return_value.id) + self.conn.network.delete_port.assert_called_once_with( + self.conn.network.create_port.return_value.id, + ignore_missing=False) self.api.detach_port_from_node.assert_called_once_with( - self.node, self.api.create_port.return_value.id) + self.node, self.conn.network.create_port.return_value.id) self.assertEqual(mock_log_exc.called, failed_call == 'release_node') @@ -624,10 +671,11 @@ class TestProvisionNode(Base): self.assertFalse(self.wait_mock.called) self.api.release_node.assert_called_once_with(self.node) - self.api.delete_port.assert_called_once_with( - self.api.create_port.return_value.id) + self.conn.network.delete_port.assert_called_once_with( + self.conn.network.create_port.return_value.id, + ignore_missing=False) self.api.detach_port_from_node.assert_called_once_with( - self.node, self.api.create_port.return_value.id) + self.node, self.conn.network.create_port.return_value.id) def test_wait_failure(self): self.wait_mock.side_effect = RuntimeError('boom') @@ -639,11 +687,12 @@ class TestProvisionNode(Base): self.api.node_action.assert_called_once_with(self.node, 'active', configdrive=mock.ANY) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) self.assertFalse(self.api.detach_port_from_node.called) def test_missing_image(self): - self.api.get_image.side_effect = RuntimeError('Not found') + self.conn.image.find_image.side_effect = os_exc.ResourceNotFound( + 'Not found') self.assertRaisesRegex(exceptions.InvalidImage, 'Not found', self.pr.provision_node, self.node, 'image', [{'network': 'network'}]) @@ -652,22 +701,22 @@ class TestProvisionNode(Base): self.api.release_node.assert_called_once_with(self.node) def test_invalid_network(self): - self.api.get_network.side_effect = RuntimeError('Not found') + self.conn.network.find_network.side_effect = RuntimeError('Not found') self.assertRaisesRegex(exceptions.InvalidNIC, 'Not found', self.pr.provision_node, self.node, 'image', [{'network': 'network'}]) self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_once_with(self.node) def test_invalid_port(self): - self.api.get_port.side_effect = RuntimeError('Not found') + self.conn.network.find_port.side_effect = RuntimeError('Not found') self.assertRaisesRegex(exceptions.InvalidNIC, 'Not found', self.pr.provision_node, self.node, 'image', [{'port': 'port1'}]) self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_once_with(self.node) @@ -676,7 +725,7 @@ class TestProvisionNode(Base): self.assertRaises(exceptions.UnknownRootDiskSize, self.pr.provision_node, self.node, 'image', [{'network': 'network'}]) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_once_with(self.node) @@ -686,7 +735,7 @@ class TestProvisionNode(Base): self.assertRaises(exceptions.UnknownRootDiskSize, self.pr.provision_node, self.node, 'image', [{'network': 'network'}]) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_with(self.node) @@ -699,7 +748,7 @@ class TestProvisionNode(Base): self.pr.provision_node, self.node, 'image', [{'network': 'network'}], root_disk_size=0) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_with(self.node) @@ -707,7 +756,7 @@ class TestProvisionNode(Base): self.assertRaisesRegex(TypeError, 'must be a list', self.pr.provision_node, self.node, 'image', 42) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.attach_port_to_node.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_once_with(self.node) @@ -717,7 +766,7 @@ class TestProvisionNode(Base): self.assertRaisesRegex(TypeError, 'must be a dict', self.pr.provision_node, self.node, 'image', item) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.attach_port_to_node.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_with(self.node) @@ -726,7 +775,7 @@ class TestProvisionNode(Base): self.assertRaisesRegex(ValueError, r'Unexpected NIC type\(s\) foo', self.pr.provision_node, self.node, 'image', [{'foo': 'bar'}]) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.attach_port_to_node.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_once_with(self.node) @@ -737,7 +786,7 @@ class TestProvisionNode(Base): self.node, 'image', [{'port': 'port1'}], hostname='n_1') self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_once_with(self.node) @@ -749,7 +798,7 @@ class TestProvisionNode(Base): self.node, 'image', [{'port': 'port1'}], hostname='host') self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.node_action.called) self.api.release_node.assert_called_once_with(self.node) @@ -758,7 +807,7 @@ class TestProvisionNode(Base): self.assertRaisesRegex(exceptions.InvalidNode, 'not found', self.pr.provision_node, self.node, 'image', [{'network': 'network'}]) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.update_node.called) self.assertFalse(self.api.node_action.called) self.assertFalse(self.api.release_node.called) @@ -769,7 +818,7 @@ class TestProvisionNode(Base): 'reserved by instance nova', self.pr.provision_node, self.node, 'image', [{'network': 'network'}]) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.update_node.called) self.assertFalse(self.api.node_action.called) self.assertFalse(self.api.release_node.called) @@ -781,7 +830,7 @@ class TestProvisionNode(Base): 'in maintenance mode .* power failure', self.pr.provision_node, self.node, 'image', [{'network': 'network'}]) - self.assertFalse(self.api.create_port.called) + self.assertFalse(self.conn.network.create_port.called) self.assertFalse(self.api.update_node.called) self.assertFalse(self.api.node_action.called) self.assertFalse(self.api.release_node.called) @@ -801,7 +850,8 @@ class TestUnprovisionNode(Base): result = self.pr.unprovision_node(self.node) self.assertIs(result, self.node) - self.api.delete_port.assert_called_once_with('port1') + self.conn.network.delete_port.assert_called_once_with( + 'port1', ignore_missing=False) self.api.detach_port_from_node.assert_called_once_with(self.node, 'port1') self.api.node_action.assert_called_once_with(self.node, 'deleted') @@ -814,7 +864,8 @@ class TestUnprovisionNode(Base): self.node.extra['metalsmith_attached_ports'] = ['port1', 'port2'] self.pr.unprovision_node(self.node) - self.api.delete_port.assert_called_once_with('port1') + self.conn.network.delete_port.assert_called_once_with( + 'port1', ignore_missing=False) calls = [mock.call(self.node, 'port1'), mock.call(self.node, 'port2')] self.api.detach_port_from_node.assert_has_calls(calls, any_order=True) self.api.node_action.assert_called_once_with(self.node, 'deleted') @@ -827,7 +878,8 @@ class TestUnprovisionNode(Base): result = self.pr.unprovision_node(self.node, wait=3600) self.assertIs(result, self.node) - self.api.delete_port.assert_called_once_with('port1') + self.conn.network.delete_port.assert_called_once_with( + 'port1', ignore_missing=False) self.api.detach_port_from_node.assert_called_once_with(self.node, 'port1') self.api.node_action.assert_called_once_with(self.node, 'deleted') @@ -844,7 +896,7 @@ class TestUnprovisionNode(Base): self.assertFalse(self.api.node_action.called) self.assertFalse(self.api.release_node.called) - self.assertFalse(self.api.delete_port.called) + self.assertFalse(self.conn.network.delete_port.called) self.assertFalse(self.api.detach_port_from_node.called) self.assertFalse(self.wait_mock.called) self.assertFalse(self.api.update_node.called)