Remove indirection when accessing Image and Network API

Change-Id: I1c6bc900088eb1bc925ef0471f4405208e874348
This commit is contained in:
Dmitry Tantsur 2018-09-06 15:38:32 +02:00
parent 3ce961db8c
commit f57e7547af
7 changed files with 201 additions and 183 deletions

View File

@ -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

View File

@ -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))

View File

@ -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})

View File

@ -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 = {

View File

@ -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()

View File

@ -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')

View File

@ -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)