summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Tantsur <divius.inside@gmail.com>2018-09-06 15:38:32 +0200
committerDmitry Tantsur <divius.inside@gmail.com>2018-09-06 18:57:47 +0200
commitf57e7547af51986250a8e1244eddcb7ce2934fb3 (patch)
tree07e7cf10b4c04cb6f5abac328bbe61ff0e7f30dd
parent3ce961db8cf9a2f1e2b06133279f40db1c7657bf (diff)
Remove indirection when accessing Image and Network API
Notes
Notes (review): Code-Review+2: Dmitry Tantsur <divius.inside@gmail.com> Workflow+1: Dmitry Tantsur <divius.inside@gmail.com> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Fri, 07 Sep 2018 08:43:04 +0000 Reviewed-on: https://review.openstack.org/600448 Project: openstack/metalsmith Branch: refs/heads/master
-rw-r--r--metalsmith/_instance.py5
-rw-r--r--metalsmith/_os_api.py41
-rw-r--r--metalsmith/_provisioner.py33
-rw-r--r--metalsmith/sources.py16
-rw-r--r--metalsmith/test/test_instance.py4
-rw-r--r--metalsmith/test/test_os_api.py23
-rw-r--r--metalsmith/test/test_provisioner.py262
7 files changed, 201 insertions, 183 deletions
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):
77 result = [] 77 result = []
78 vifs = self._api.list_node_attached_ports(self.node) 78 vifs = self._api.list_node_attached_ports(self.node)
79 for vif in vifs: 79 for vif in vifs:
80 port = self._api.get_port(vif.id) 80 port = self._api.connection.network.get_port(vif.id)
81 port.network = self._api.get_network(port.network_id) 81 port.network = self._api.connection.network.get_network(
82 port.network_id)
82 result.append(port) 83 result.append(port)
83 return result 84 return result
84 85
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
17import logging 17import logging
18 18
19from ironicclient import client as ir_client 19from ironicclient import client as ir_client
20from openstack import connection
21import six 20import six
22 21
23from metalsmith import _utils 22from metalsmith import _utils
@@ -60,24 +59,11 @@ class API(object):
60 59
61 _node_list = None 60 _node_list = None
62 61
63 def __init__(self, session=None, cloud_region=None): 62 def __init__(self, session, connection):
64 if cloud_region is None:
65 if session is None:
66 raise TypeError('Either session or cloud_region must '
67 'be provided')
68 self.session = session
69 self.connection = connection.Connection(session=session)
70 elif session is not None:
71 raise TypeError('Either session or cloud_region must be provided, '
72 'but not both')
73 else:
74 self.session = cloud_region.get_session()
75 self.connection = connection.Connection(config=cloud_region)
76
77 LOG.debug('Creating service clients')
78 self.ironic = ir_client.get_client( 63 self.ironic = ir_client.get_client(
79 self.IRONIC_VERSION, session=self.session, 64 self.IRONIC_VERSION, session=session,
80 os_ironic_api_version=self.IRONIC_MICRO_VERSION) 65 os_ironic_api_version=self.IRONIC_MICRO_VERSION)
66 self.connection = connection
81 67
82 def _nodes_for_lookup(self): 68 def _nodes_for_lookup(self):
83 return self.list_nodes(maintenance=None, 69 return self.list_nodes(maintenance=None,
@@ -95,15 +81,6 @@ class API(object):
95 yield self._node_list 81 yield self._node_list
96 self._node_list = None 82 self._node_list = None
97 83
98 def create_port(self, network_id, **kwargs):
99 return self.connection.network.create_port(network_id=network_id,
100 admin_state_up=True,
101 **kwargs)
102
103 def delete_port(self, port_id):
104 self.connection.network.delete_port(port_id,
105 ignore_missing=False)
106
107 def detach_port_from_node(self, node, port_id): 84 def detach_port_from_node(self, node, port_id):
108 self.ironic.node.vif_detach(_node_id(node), port_id) 85 self.ironic.node.vif_detach(_node_id(node), port_id)
109 86
@@ -123,14 +100,6 @@ class API(object):
123 # Fetch the complete node record 100 # Fetch the complete node record
124 return self.get_node(existing[0].uuid, accept_hostname=False) 101 return self.get_node(existing[0].uuid, accept_hostname=False)
125 102
126 def get_image(self, image_id):
127 return self.connection.image.find_image(image_id,
128 ignore_missing=False)
129
130 def get_network(self, network_id):
131 return self.connection.network.find_network(network_id,
132 ignore_missing=False)
133
134 def get_node(self, node, refresh=False, accept_hostname=False): 103 def get_node(self, node, refresh=False, accept_hostname=False):
135 if isinstance(node, six.string_types): 104 if isinstance(node, six.string_types):
136 if accept_hostname and _utils.is_hostname_safe(node): 105 if accept_hostname and _utils.is_hostname_safe(node):
@@ -150,10 +119,6 @@ class API(object):
150 else: 119 else:
151 return node 120 return node
152 121
153 def get_port(self, port_id):
154 return self.connection.network.find_port(port_id,
155 ignore_missing=False)
156
157 def list_node_attached_ports(self, node): 122 def list_node_attached_ports(self, node):
158 return self.ironic.node.vif_list(_node_id(node)) 123 return self.ironic.node.vif_list(_node_id(node))
159 124
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
18import sys 18import sys
19import time 19import time
20 20
21from openstack import connection
21import six 22import six
22 23
23from metalsmith import _config 24from metalsmith import _config
@@ -44,10 +45,25 @@ class Provisioner(object):
44 to use when making API requests. Mutually exclusive with **session**. 45 to use when making API requests. Mutually exclusive with **session**.
45 :param dry_run: boolean value, set to ``True`` to prevent any API calls 46 :param dry_run: boolean value, set to ``True`` to prevent any API calls
46 from being actually made. 47 from being actually made.
48
49 :ivar connection: `openstacksdk` `Connection` object used for accessing
50 OpenStack API during provisioning.
47 """ 51 """
48 52
49 def __init__(self, session=None, cloud_region=None, dry_run=False): 53 def __init__(self, session=None, cloud_region=None, dry_run=False):
50 self._api = _os_api.API(session=session, cloud_region=cloud_region) 54 if cloud_region is None:
55 if session is None:
56 raise TypeError('Either session or cloud_region must '
57 'be provided')
58 self.connection = connection.Connection(session=session)
59 elif session is not None:
60 raise TypeError('Either session or cloud_region must be provided, '
61 'but not both')
62 else:
63 session = cloud_region.get_session()
64 self.connection = connection.Connection(config=cloud_region)
65
66 self._api = _os_api.API(session, self.connection)
51 self._dry_run = dry_run 67 self._dry_run = dry_run
52 68
53 def reserve_node(self, resource_class=None, conductor_group=None, 69 def reserve_node(self, resource_class=None, conductor_group=None,
@@ -228,7 +244,7 @@ class Provisioner(object):
228 hostname = self._check_hostname(node, hostname) 244 hostname = self._check_hostname(node, hostname)
229 root_disk_size = _utils.get_root_disk(root_disk_size, node) 245 root_disk_size = _utils.get_root_disk(root_disk_size, node)
230 246
231 image._validate(self._api) 247 image._validate(self.connection)
232 248
233 nics = self._get_nics(nics or []) 249 nics = self._get_nics(nics or [])
234 250
@@ -250,7 +266,7 @@ class Provisioner(object):
250 '/extra/%s' % _CREATED_PORTS: created_ports, 266 '/extra/%s' % _CREATED_PORTS: created_ports,
251 '/extra/%s' % _ATTACHED_PORTS: attached_ports, 267 '/extra/%s' % _ATTACHED_PORTS: attached_ports,
252 '/instance_info/%s' % _os_api.HOSTNAME_FIELD: hostname} 268 '/instance_info/%s' % _os_api.HOSTNAME_FIELD: hostname}
253 updates.update(image._node_updates(self._api)) 269 updates.update(image._node_updates(self.connection))
254 270
255 LOG.debug('Updating node %(node)s with %(updates)s', 271 LOG.debug('Updating node %(node)s with %(updates)s',
256 {'node': _utils.log_node(node), 'updates': updates}) 272 {'node': _utils.log_node(node), 'updates': updates})
@@ -299,7 +315,8 @@ class Provisioner(object):
299 nic_type, nic_id = next(iter(nic.items())) 315 nic_type, nic_id = next(iter(nic.items()))
300 if nic_type == 'network': 316 if nic_type == 'network':
301 try: 317 try:
302 network = self._api.get_network(nic_id) 318 network = self.connection.network.find_network(
319 nic_id, ignore_missing=False)
303 except Exception as exc: 320 except Exception as exc:
304 raise exceptions.InvalidNIC( 321 raise exceptions.InvalidNIC(
305 'Cannot find network %(net)s: %(error)s' % 322 'Cannot find network %(net)s: %(error)s' %
@@ -308,7 +325,8 @@ class Provisioner(object):
308 result.append((nic_type, network)) 325 result.append((nic_type, network))
309 else: 326 else:
310 try: 327 try:
311 port = self._api.get_port(nic_id) 328 port = self.connection.network.find_port(
329 nic_id, ignore_missing=False)
312 except Exception as exc: 330 except Exception as exc:
313 raise exceptions.InvalidNIC( 331 raise exceptions.InvalidNIC(
314 'Cannot find port %(port)s: %(error)s' % 332 'Cannot find port %(port)s: %(error)s' %
@@ -323,7 +341,7 @@ class Provisioner(object):
323 """Create and attach ports on given networks.""" 341 """Create and attach ports on given networks."""
324 for nic_type, nic in nics: 342 for nic_type, nic in nics:
325 if nic_type == 'network': 343 if nic_type == 'network':
326 port = self._api.create_port(network_id=nic.id) 344 port = self.connection.network.create_port(network_id=nic.id)
327 created_ports.append(port.id) 345 created_ports.append(port.id)
328 LOG.info('Created port %(port)s for node %(node)s on ' 346 LOG.info('Created port %(port)s for node %(node)s on '
329 'network %(net)s', 347 'network %(net)s',
@@ -359,7 +377,8 @@ class Provisioner(object):
359 for port_id in created_ports: 377 for port_id in created_ports:
360 LOG.debug('Deleting port %s', port_id) 378 LOG.debug('Deleting port %s', port_id)
361 try: 379 try:
362 self._api.delete_port(port_id) 380 self.connection.network.delete_port(port_id,
381 ignore_missing=False)
363 except Exception as exc: 382 except Exception as exc:
364 LOG.warning('Failed to delete neutron port %(port)s: %(exc)s', 383 LOG.warning('Failed to delete neutron port %(port)s: %(exc)s',
365 {'port': port_id, 'exc': exc}) 384 {'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 @@
18import abc 18import abc
19import logging 19import logging
20 20
21import openstack.exceptions
21import six 22import six
22 23
23from metalsmith import exceptions 24from metalsmith import exceptions
@@ -29,11 +30,11 @@ LOG = logging.getLogger(__name__)
29@six.add_metaclass(abc.ABCMeta) 30@six.add_metaclass(abc.ABCMeta)
30class _Source(object): 31class _Source(object):
31 32
32 def _validate(self, api): 33 def _validate(self, connection):
33 """Validate the source.""" 34 """Validate the source."""
34 35
35 @abc.abstractmethod 36 @abc.abstractmethod
36 def _node_updates(self, api): 37 def _node_updates(self, connection):
37 """Updates required for a node to use this source.""" 38 """Updates required for a node to use this source."""
38 39
39 40
@@ -48,18 +49,19 @@ class Glance(_Source):
48 self._image_id = image 49 self._image_id = image
49 self._image_obj = None 50 self._image_obj = None
50 51
51 def _validate(self, api): 52 def _validate(self, connection):
52 if self._image_obj is not None: 53 if self._image_obj is not None:
53 return 54 return
54 try: 55 try:
55 self._image_obj = api.get_image(self._image_id) 56 self._image_obj = connection.image.find_image(self._image_id,
56 except Exception as exc: 57 ignore_missing=False)
58 except openstack.exceptions.SDKException as exc:
57 raise exceptions.InvalidImage( 59 raise exceptions.InvalidImage(
58 'Cannot find image %(image)s: %(error)s' % 60 'Cannot find image %(image)s: %(error)s' %
59 {'image': self._image_id, 'error': exc}) 61 {'image': self._image_id, 'error': exc})
60 62
61 def _node_updates(self, api): 63 def _node_updates(self, connection):
62 self._validate(api) 64 self._validate(connection)
63 LOG.debug('Image: %s', self._image_obj) 65 LOG.debug('Image: %s', self._image_obj)
64 66
65 updates = { 67 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):
31 network_id=n, fixed_ips=[{'ip_address': ip}]) 31 network_id=n, fixed_ips=[{'ip_address': ip}])
32 for n, ip in [('0', '192.168.0.1'), ('1', '10.0.0.2')] 32 for n, ip in [('0', '192.168.0.1'), ('1', '10.0.0.2')]
33 ] 33 ]
34 self.api.get_port.side_effect = self.ports 34 self.conn.network.get_port.side_effect = self.ports
35 self.nets = [ 35 self.nets = [
36 mock.Mock(spec=['id', 'name'], id=str(i)) for i in range(2) 36 mock.Mock(spec=['id', 'name'], id=str(i)) for i in range(2)
37 ] 37 ]
38 for n in self.nets: 38 for n in self.nets:
39 n.name = 'name-%s' % n.id 39 n.name = 'name-%s' % n.id
40 self.api.get_network.side_effect = self.nets 40 self.conn.network.get_network.side_effect = self.nets
41 41
42 def test_ip_addresses(self): 42 def test_ip_addresses(self):
43 ips = self.instance.ip_addresses() 43 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
21from metalsmith import _os_api 21from metalsmith import _os_api
22 22
23 23
24class TestInit(testtools.TestCase):
25 def test_missing_auth(self):
26 self.assertRaisesRegex(TypeError, 'must be provided', _os_api.API)
27
28 def test_both_provided(self):
29 self.assertRaisesRegex(TypeError, 'not both', _os_api.API,
30 session=mock.Mock(), cloud_region=mock.Mock())
31
32 def test_session_only(self):
33 session = mock.Mock()
34 api = _os_api.API(session=session)
35 self.assertIs(api.session, session)
36
37 @mock.patch.object(_os_api.connection, 'Connection', autospec=True)
38 def test_cloud_region_only(self, mock_conn):
39 region = mock.Mock()
40 api = _os_api.API(cloud_region=region)
41 self.assertIs(api.session, region.get_session.return_value)
42 mock_conn.assert_called_once_with(config=region)
43
44
45class TestNodes(testtools.TestCase): 24class TestNodes(testtools.TestCase):
46 def setUp(self): 25 def setUp(self):
47 super(TestNodes, self).setUp() 26 super(TestNodes, self).setUp()
@@ -50,7 +29,7 @@ class TestNodes(testtools.TestCase):
50 fixtures.MockPatchObject(_os_api.ir_client, 'get_client', 29 fixtures.MockPatchObject(_os_api.ir_client, 'get_client',
51 autospec=True)) 30 autospec=True))
52 self.cli = self.ironic_fixture.mock.return_value 31 self.cli = self.ironic_fixture.mock.return_value
53 self.api = _os_api.API(session=self.session) 32 self.api = _os_api.API(session=self.session, connection=mock.Mock())
54 33
55 def test_get_node_by_uuid(self): 34 def test_get_node_by_uuid(self):
56 res = self.api.get_node('uuid1') 35 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 @@
15 15
16import fixtures 16import fixtures
17import mock 17import mock
18from openstack import exceptions as os_exc
18import testtools 19import testtools
19 20
20from metalsmith import _config 21from metalsmith import _config
@@ -30,6 +31,28 @@ NODE_FIELDS = ['name', 'uuid', 'instance_info', 'instance_uuid', 'maintenance',
30 'last_error'] 31 'last_error']
31 32
32 33
34class TestInit(testtools.TestCase):
35 def test_missing_auth(self):
36 self.assertRaisesRegex(TypeError, 'must be provided',
37 _provisioner.Provisioner)
38
39 def test_both_provided(self):
40 self.assertRaisesRegex(TypeError, 'not both', _provisioner.Provisioner,
41 session=mock.Mock(), cloud_region=mock.Mock())
42
43 @mock.patch.object(_provisioner.connection, 'Connection', autospec=True)
44 def test_session_only(self, mock_conn):
45 session = mock.Mock()
46 _provisioner.Provisioner(session=session)
47 mock_conn.assert_called_once_with(session=session)
48
49 @mock.patch.object(_provisioner.connection, 'Connection', autospec=True)
50 def test_cloud_region_only(self, mock_conn):
51 region = mock.Mock()
52 _provisioner.Provisioner(cloud_region=region)
53 mock_conn.assert_called_once_with(config=region)
54
55
33class Base(testtools.TestCase): 56class Base(testtools.TestCase):
34 57
35 def setUp(self): 58 def setUp(self):
@@ -58,6 +81,10 @@ class Base(testtools.TestCase):
58 self.api.cache_node_list_for_lookup = mock.MagicMock() 81 self.api.cache_node_list_for_lookup = mock.MagicMock()
59 self.pr._api = self.api 82 self.pr._api = self.api
60 83
84 self.conn = mock.Mock(spec=['image', 'network', 'baremetal'])
85 self.pr.connection = self.conn
86 self.api.connection = self.conn
87
61 88
62class TestReserveNode(Base): 89class TestReserveNode(Base):
63 90
@@ -210,19 +237,19 @@ class TestProvisionNode(Base):
210 237
211 def setUp(self): 238 def setUp(self):
212 super(TestProvisionNode, self).setUp() 239 super(TestProvisionNode, self).setUp()
213 image = self.api.get_image.return_value 240 self.image = self.conn.image.find_image.return_value
214 self.node.instance_uuid = self.node.uuid 241 self.node.instance_uuid = self.node.uuid
215 self.updates = { 242 self.updates = {
216 '/instance_info/ramdisk': image.ramdisk_id, 243 '/instance_info/ramdisk': self.image.ramdisk_id,
217 '/instance_info/kernel': image.kernel_id, 244 '/instance_info/kernel': self.image.kernel_id,
218 '/instance_info/image_source': image.id, 245 '/instance_info/image_source': self.image.id,
219 '/instance_info/root_gb': 99, # 100 - 1 246 '/instance_info/root_gb': 99, # 100 - 1
220 '/instance_info/capabilities': {'boot_option': 'local'}, 247 '/instance_info/capabilities': {'boot_option': 'local'},
221 '/extra/metalsmith_created_ports': [ 248 '/extra/metalsmith_created_ports': [
222 self.api.create_port.return_value.id 249 self.conn.network.create_port.return_value.id
223 ], 250 ],
224 '/extra/metalsmith_attached_ports': [ 251 '/extra/metalsmith_attached_ports': [
225 self.api.create_port.return_value.id 252 self.conn.network.create_port.return_value.id
226 ], 253 ],
227 '/instance_info/%s' % _os_api.HOSTNAME_FIELD: 'control-0' 254 '/instance_info/%s' % _os_api.HOSTNAME_FIELD: 'control-0'
228 } 255 }
@@ -239,10 +266,10 @@ class TestProvisionNode(Base):
239 self.assertEqual(inst.uuid, self.node.uuid) 266 self.assertEqual(inst.uuid, self.node.uuid)
240 self.assertEqual(inst.node, self.node) 267 self.assertEqual(inst.node, self.node)
241 268
242 self.api.create_port.assert_called_once_with( 269 self.conn.network.create_port.assert_called_once_with(
243 network_id=self.api.get_network.return_value.id) 270 network_id=self.conn.network.find_network.return_value.id)
244 self.api.attach_port_to_node.assert_called_once_with( 271 self.api.attach_port_to_node.assert_called_once_with(
245 self.node.uuid, self.api.create_port.return_value.id) 272 self.node.uuid, self.conn.network.create_port.return_value.id)
246 self.api.update_node.assert_called_once_with(self.node, self.updates) 273 self.api.update_node.assert_called_once_with(self.node, self.updates)
247 self.api.validate_node.assert_called_once_with(self.node, 274 self.api.validate_node.assert_called_once_with(self.node,
248 validate_deploy=True) 275 validate_deploy=True)
@@ -250,7 +277,7 @@ class TestProvisionNode(Base):
250 configdrive=mock.ANY) 277 configdrive=mock.ANY)
251 self.assertFalse(self.wait_mock.called) 278 self.assertFalse(self.wait_mock.called)
252 self.assertFalse(self.api.release_node.called) 279 self.assertFalse(self.api.release_node.called)
253 self.assertFalse(self.api.delete_port.called) 280 self.assertFalse(self.conn.network.delete_port.called)
254 281
255 def test_ok_with_source(self): 282 def test_ok_with_source(self):
256 inst = self.pr.provision_node(self.node, sources.Glance('image'), 283 inst = self.pr.provision_node(self.node, sources.Glance('image'),
@@ -259,10 +286,10 @@ class TestProvisionNode(Base):
259 self.assertEqual(inst.uuid, self.node.uuid) 286 self.assertEqual(inst.uuid, self.node.uuid)
260 self.assertEqual(inst.node, self.node) 287 self.assertEqual(inst.node, self.node)
261 288
262 self.api.create_port.assert_called_once_with( 289 self.conn.network.create_port.assert_called_once_with(
263 network_id=self.api.get_network.return_value.id) 290 network_id=self.conn.network.find_network.return_value.id)
264 self.api.attach_port_to_node.assert_called_once_with( 291 self.api.attach_port_to_node.assert_called_once_with(
265 self.node.uuid, self.api.create_port.return_value.id) 292 self.node.uuid, self.conn.network.create_port.return_value.id)
266 self.api.update_node.assert_called_once_with(self.node, self.updates) 293 self.api.update_node.assert_called_once_with(self.node, self.updates)
267 self.api.validate_node.assert_called_once_with(self.node, 294 self.api.validate_node.assert_called_once_with(self.node,
268 validate_deploy=True) 295 validate_deploy=True)
@@ -270,7 +297,7 @@ class TestProvisionNode(Base):
270 configdrive=mock.ANY) 297 configdrive=mock.ANY)
271 self.assertFalse(self.wait_mock.called) 298 self.assertFalse(self.wait_mock.called)
272 self.assertFalse(self.api.release_node.called) 299 self.assertFalse(self.api.release_node.called)
273 self.assertFalse(self.api.delete_port.called) 300 self.assertFalse(self.conn.network.delete_port.called)
274 301
275 def test_with_config(self): 302 def test_with_config(self):
276 config = mock.MagicMock(spec=_config.InstanceConfig) 303 config = mock.MagicMock(spec=_config.InstanceConfig)
@@ -283,10 +310,10 @@ class TestProvisionNode(Base):
283 310
284 config.build_configdrive_directory.assert_called_once_with( 311 config.build_configdrive_directory.assert_called_once_with(
285 self.node, self.node.name) 312 self.node, self.node.name)
286 self.api.create_port.assert_called_once_with( 313 self.conn.network.create_port.assert_called_once_with(
287 network_id=self.api.get_network.return_value.id) 314 network_id=self.conn.network.find_network.return_value.id)
288 self.api.attach_port_to_node.assert_called_once_with( 315 self.api.attach_port_to_node.assert_called_once_with(
289 self.node.uuid, self.api.create_port.return_value.id) 316 self.node.uuid, self.conn.network.create_port.return_value.id)
290 self.api.update_node.assert_called_once_with(self.node, self.updates) 317 self.api.update_node.assert_called_once_with(self.node, self.updates)
291 self.api.validate_node.assert_called_once_with(self.node, 318 self.api.validate_node.assert_called_once_with(self.node,
292 validate_deploy=True) 319 validate_deploy=True)
@@ -294,7 +321,7 @@ class TestProvisionNode(Base):
294 configdrive=mock.ANY) 321 configdrive=mock.ANY)
295 self.assertFalse(self.wait_mock.called) 322 self.assertFalse(self.wait_mock.called)
296 self.assertFalse(self.api.release_node.called) 323 self.assertFalse(self.api.release_node.called)
297 self.assertFalse(self.api.delete_port.called) 324 self.assertFalse(self.conn.network.delete_port.called)
298 325
299 def test_with_hostname(self): 326 def test_with_hostname(self):
300 hostname = 'control-0.example.com' 327 hostname = 'control-0.example.com'
@@ -306,10 +333,10 @@ class TestProvisionNode(Base):
306 self.assertEqual(inst.uuid, self.node.uuid) 333 self.assertEqual(inst.uuid, self.node.uuid)
307 self.assertEqual(inst.node, self.node) 334 self.assertEqual(inst.node, self.node)
308 335
309 self.api.create_port.assert_called_once_with( 336 self.conn.network.create_port.assert_called_once_with(
310 network_id=self.api.get_network.return_value.id) 337 network_id=self.conn.network.find_network.return_value.id)
311 self.api.attach_port_to_node.assert_called_once_with( 338 self.api.attach_port_to_node.assert_called_once_with(
312 self.node.uuid, self.api.create_port.return_value.id) 339 self.node.uuid, self.conn.network.create_port.return_value.id)
313 self.api.update_node.assert_called_once_with(self.node, self.updates) 340 self.api.update_node.assert_called_once_with(self.node, self.updates)
314 self.api.validate_node.assert_called_once_with(self.node, 341 self.api.validate_node.assert_called_once_with(self.node,
315 validate_deploy=True) 342 validate_deploy=True)
@@ -317,7 +344,7 @@ class TestProvisionNode(Base):
317 configdrive=mock.ANY) 344 configdrive=mock.ANY)
318 self.assertFalse(self.wait_mock.called) 345 self.assertFalse(self.wait_mock.called)
319 self.assertFalse(self.api.release_node.called) 346 self.assertFalse(self.api.release_node.called)
320 self.assertFalse(self.api.delete_port.called) 347 self.assertFalse(self.conn.network.delete_port.called)
321 348
322 def test_name_not_valid_hostname(self): 349 def test_name_not_valid_hostname(self):
323 self.node.name = 'node_1' 350 self.node.name = 'node_1'
@@ -328,10 +355,10 @@ class TestProvisionNode(Base):
328 self.assertEqual(inst.uuid, self.node.uuid) 355 self.assertEqual(inst.uuid, self.node.uuid)
329 self.assertEqual(inst.node, self.node) 356 self.assertEqual(inst.node, self.node)
330 357
331 self.api.create_port.assert_called_once_with( 358 self.conn.network.create_port.assert_called_once_with(
332 network_id=self.api.get_network.return_value.id) 359 network_id=self.conn.network.find_network.return_value.id)
333 self.api.attach_port_to_node.assert_called_once_with( 360 self.api.attach_port_to_node.assert_called_once_with(
334 self.node.uuid, self.api.create_port.return_value.id) 361 self.node.uuid, self.conn.network.create_port.return_value.id)
335 self.api.update_node.assert_called_once_with(self.node, self.updates) 362 self.api.update_node.assert_called_once_with(self.node, self.updates)
336 self.api.validate_node.assert_called_once_with(self.node, 363 self.api.validate_node.assert_called_once_with(self.node,
337 validate_deploy=True) 364 validate_deploy=True)
@@ -339,7 +366,7 @@ class TestProvisionNode(Base):
339 configdrive=mock.ANY) 366 configdrive=mock.ANY)
340 self.assertFalse(self.wait_mock.called) 367 self.assertFalse(self.wait_mock.called)
341 self.assertFalse(self.api.release_node.called) 368 self.assertFalse(self.api.release_node.called)
342 self.assertFalse(self.api.delete_port.called) 369 self.assertFalse(self.conn.network.delete_port.called)
343 370
344 def test_unreserved(self): 371 def test_unreserved(self):
345 self.node.instance_uuid = None 372 self.node.instance_uuid = None
@@ -348,10 +375,10 @@ class TestProvisionNode(Base):
348 375
349 self.api.reserve_node.assert_called_once_with( 376 self.api.reserve_node.assert_called_once_with(
350 self.node, instance_uuid=self.node.uuid) 377 self.node, instance_uuid=self.node.uuid)
351 self.api.create_port.assert_called_once_with( 378 self.conn.network.create_port.assert_called_once_with(
352 network_id=self.api.get_network.return_value.id) 379 network_id=self.conn.network.find_network.return_value.id)
353 self.api.attach_port_to_node.assert_called_once_with( 380 self.api.attach_port_to_node.assert_called_once_with(
354 self.node.uuid, self.api.create_port.return_value.id) 381 self.node.uuid, self.conn.network.create_port.return_value.id)
355 self.api.update_node.assert_called_once_with(self.node, self.updates) 382 self.api.update_node.assert_called_once_with(self.node, self.updates)
356 self.api.validate_node.assert_called_once_with(self.node, 383 self.api.validate_node.assert_called_once_with(self.node,
357 validate_deploy=True) 384 validate_deploy=True)
@@ -359,23 +386,24 @@ class TestProvisionNode(Base):
359 configdrive=mock.ANY) 386 configdrive=mock.ANY)
360 self.assertFalse(self.wait_mock.called) 387 self.assertFalse(self.wait_mock.called)
361 self.assertFalse(self.api.release_node.called) 388 self.assertFalse(self.api.release_node.called)
362 self.assertFalse(self.api.delete_port.called) 389 self.assertFalse(self.conn.network.delete_port.called)
363 390
364 def test_with_ports(self): 391 def test_with_ports(self):
365 self.updates['/extra/metalsmith_created_ports'] = [] 392 self.updates['/extra/metalsmith_created_ports'] = []
366 self.updates['/extra/metalsmith_attached_ports'] = [ 393 self.updates['/extra/metalsmith_attached_ports'] = [
367 self.api.get_port.return_value.id 394 self.conn.network.find_port.return_value.id
368 ] * 2 395 ] * 2
369 396
370 self.pr.provision_node(self.node, 'image', 397 self.pr.provision_node(self.node, 'image',
371 [{'port': 'port1'}, {'port': 'port2'}]) 398 [{'port': 'port1'}, {'port': 'port2'}])
372 399
373 self.assertFalse(self.api.create_port.called) 400 self.assertFalse(self.conn.network.create_port.called)
374 self.api.attach_port_to_node.assert_called_with( 401 self.api.attach_port_to_node.assert_called_with(
375 self.node.uuid, self.api.get_port.return_value.id) 402 self.node.uuid, self.conn.network.find_port.return_value.id)
376 self.assertEqual(2, self.api.attach_port_to_node.call_count) 403 self.assertEqual(2, self.api.attach_port_to_node.call_count)
377 self.assertEqual([mock.call('port1'), mock.call('port2')], 404 self.assertEqual([mock.call('port1', ignore_missing=False),
378 self.api.get_port.call_args_list) 405 mock.call('port2', ignore_missing=False)],
406 self.conn.network.find_port.call_args_list)
379 self.api.update_node.assert_called_once_with(self.node, self.updates) 407 self.api.update_node.assert_called_once_with(self.node, self.updates)
380 self.api.validate_node.assert_called_once_with(self.node, 408 self.api.validate_node.assert_called_once_with(self.node,
381 validate_deploy=True) 409 validate_deploy=True)
@@ -383,21 +411,20 @@ class TestProvisionNode(Base):
383 configdrive=mock.ANY) 411 configdrive=mock.ANY)
384 self.assertFalse(self.wait_mock.called) 412 self.assertFalse(self.wait_mock.called)
385 self.assertFalse(self.api.release_node.called) 413 self.assertFalse(self.api.release_node.called)
386 self.assertFalse(self.api.delete_port.called) 414 self.assertFalse(self.conn.network.delete_port.called)
387 415
388 def test_whole_disk(self): 416 def test_whole_disk(self):
389 image = self.api.get_image.return_value 417 self.image.kernel_id = None
390 image.kernel_id = None 418 self.image.ramdisk_id = None
391 image.ramdisk_id = None
392 del self.updates['/instance_info/kernel'] 419 del self.updates['/instance_info/kernel']
393 del self.updates['/instance_info/ramdisk'] 420 del self.updates['/instance_info/ramdisk']
394 421
395 self.pr.provision_node(self.node, 'image', [{'network': 'network'}]) 422 self.pr.provision_node(self.node, 'image', [{'network': 'network'}])
396 423
397 self.api.create_port.assert_called_once_with( 424 self.conn.network.create_port.assert_called_once_with(
398 network_id=self.api.get_network.return_value.id) 425 network_id=self.conn.network.find_network.return_value.id)
399 self.api.attach_port_to_node.assert_called_once_with( 426 self.api.attach_port_to_node.assert_called_once_with(
400 self.node.uuid, self.api.create_port.return_value.id) 427 self.node.uuid, self.conn.network.create_port.return_value.id)
401 self.api.update_node.assert_called_once_with(self.node, self.updates) 428 self.api.update_node.assert_called_once_with(self.node, self.updates)
402 self.api.validate_node.assert_called_once_with(self.node, 429 self.api.validate_node.assert_called_once_with(self.node,
403 validate_deploy=True) 430 validate_deploy=True)
@@ -405,7 +432,7 @@ class TestProvisionNode(Base):
405 configdrive=mock.ANY) 432 configdrive=mock.ANY)
406 self.assertFalse(self.wait_mock.called) 433 self.assertFalse(self.wait_mock.called)
407 self.assertFalse(self.api.release_node.called) 434 self.assertFalse(self.api.release_node.called)
408 self.assertFalse(self.api.delete_port.called) 435 self.assertFalse(self.conn.network.delete_port.called)
409 436
410 def test_with_root_disk_size(self): 437 def test_with_root_disk_size(self):
411 self.updates['/instance_info/root_gb'] = 50 438 self.updates['/instance_info/root_gb'] = 50
@@ -413,10 +440,10 @@ class TestProvisionNode(Base):
413 self.pr.provision_node(self.node, 'image', [{'network': 'network'}], 440 self.pr.provision_node(self.node, 'image', [{'network': 'network'}],
414 root_disk_size=50) 441 root_disk_size=50)
415 442
416 self.api.create_port.assert_called_once_with( 443 self.conn.network.create_port.assert_called_once_with(
417 network_id=self.api.get_network.return_value.id) 444 network_id=self.conn.network.find_network.return_value.id)
418 self.api.attach_port_to_node.assert_called_once_with( 445 self.api.attach_port_to_node.assert_called_once_with(
419 self.node.uuid, self.api.create_port.return_value.id) 446 self.node.uuid, self.conn.network.create_port.return_value.id)
420 self.api.update_node.assert_called_once_with(self.node, self.updates) 447 self.api.update_node.assert_called_once_with(self.node, self.updates)
421 self.api.validate_node.assert_called_once_with(self.node, 448 self.api.validate_node.assert_called_once_with(self.node,
422 validate_deploy=True) 449 validate_deploy=True)
@@ -424,7 +451,7 @@ class TestProvisionNode(Base):
424 configdrive=mock.ANY) 451 configdrive=mock.ANY)
425 self.assertFalse(self.wait_mock.called) 452 self.assertFalse(self.wait_mock.called)
426 self.assertFalse(self.api.release_node.called) 453 self.assertFalse(self.api.release_node.called)
427 self.assertFalse(self.api.delete_port.called) 454 self.assertFalse(self.conn.network.delete_port.called)
428 455
429 def test_with_capabilities(self): 456 def test_with_capabilities(self):
430 inst = self.pr.provision_node(self.node, 'image', 457 inst = self.pr.provision_node(self.node, 'image',
@@ -436,10 +463,10 @@ class TestProvisionNode(Base):
436 self.assertEqual(inst.uuid, self.node.uuid) 463 self.assertEqual(inst.uuid, self.node.uuid)
437 self.assertEqual(inst.node, self.node) 464 self.assertEqual(inst.node, self.node)
438 465
439 self.api.create_port.assert_called_once_with( 466 self.conn.network.create_port.assert_called_once_with(
440 network_id=self.api.get_network.return_value.id) 467 network_id=self.conn.network.find_network.return_value.id)
441 self.api.attach_port_to_node.assert_called_once_with( 468 self.api.attach_port_to_node.assert_called_once_with(
442 self.node.uuid, self.api.create_port.return_value.id) 469 self.node.uuid, self.conn.network.create_port.return_value.id)
443 self.api.update_node.assert_called_once_with(self.node, self.updates) 470 self.api.update_node.assert_called_once_with(self.node, self.updates)
444 self.api.validate_node.assert_called_once_with(self.node, 471 self.api.validate_node.assert_called_once_with(self.node,
445 validate_deploy=True) 472 validate_deploy=True)
@@ -447,7 +474,7 @@ class TestProvisionNode(Base):
447 configdrive=mock.ANY) 474 configdrive=mock.ANY)
448 self.assertFalse(self.wait_mock.called) 475 self.assertFalse(self.wait_mock.called)
449 self.assertFalse(self.api.release_node.called) 476 self.assertFalse(self.api.release_node.called)
450 self.assertFalse(self.api.delete_port.called) 477 self.assertFalse(self.conn.network.delete_port.called)
451 478
452 def test_with_existing_capabilities(self): 479 def test_with_existing_capabilities(self):
453 self.node.instance_info['capabilities'] = {'answer': '42'} 480 self.node.instance_info['capabilities'] = {'answer': '42'}
@@ -459,10 +486,10 @@ class TestProvisionNode(Base):
459 self.assertEqual(inst.uuid, self.node.uuid) 486 self.assertEqual(inst.uuid, self.node.uuid)
460 self.assertEqual(inst.node, self.node) 487 self.assertEqual(inst.node, self.node)
461 488
462 self.api.create_port.assert_called_once_with( 489 self.conn.network.create_port.assert_called_once_with(
463 network_id=self.api.get_network.return_value.id) 490 network_id=self.conn.network.find_network.return_value.id)
464 self.api.attach_port_to_node.assert_called_once_with( 491 self.api.attach_port_to_node.assert_called_once_with(
465 self.node.uuid, self.api.create_port.return_value.id) 492 self.node.uuid, self.conn.network.create_port.return_value.id)
466 self.api.update_node.assert_called_once_with(self.node, self.updates) 493 self.api.update_node.assert_called_once_with(self.node, self.updates)
467 self.api.validate_node.assert_called_once_with(self.node, 494 self.api.validate_node.assert_called_once_with(self.node,
468 validate_deploy=True) 495 validate_deploy=True)
@@ -470,7 +497,7 @@ class TestProvisionNode(Base):
470 configdrive=mock.ANY) 497 configdrive=mock.ANY)
471 self.assertFalse(self.wait_mock.called) 498 self.assertFalse(self.wait_mock.called)
472 self.assertFalse(self.api.release_node.called) 499 self.assertFalse(self.api.release_node.called)
473 self.assertFalse(self.api.delete_port.called) 500 self.assertFalse(self.conn.network.delete_port.called)
474 501
475 def test_override_existing_capabilities(self): 502 def test_override_existing_capabilities(self):
476 self.node.instance_info['capabilities'] = {'answer': '1', 503 self.node.instance_info['capabilities'] = {'answer': '1',
@@ -484,10 +511,10 @@ class TestProvisionNode(Base):
484 self.assertEqual(inst.uuid, self.node.uuid) 511 self.assertEqual(inst.uuid, self.node.uuid)
485 self.assertEqual(inst.node, self.node) 512 self.assertEqual(inst.node, self.node)
486 513
487 self.api.create_port.assert_called_once_with( 514 self.conn.network.create_port.assert_called_once_with(
488 network_id=self.api.get_network.return_value.id) 515 network_id=self.conn.network.find_network.return_value.id)
489 self.api.attach_port_to_node.assert_called_once_with( 516 self.api.attach_port_to_node.assert_called_once_with(
490 self.node.uuid, self.api.create_port.return_value.id) 517 self.node.uuid, self.conn.network.create_port.return_value.id)
491 self.api.update_node.assert_called_once_with(self.node, self.updates) 518 self.api.update_node.assert_called_once_with(self.node, self.updates)
492 self.api.validate_node.assert_called_once_with(self.node, 519 self.api.validate_node.assert_called_once_with(self.node,
493 validate_deploy=True) 520 validate_deploy=True)
@@ -495,20 +522,20 @@ class TestProvisionNode(Base):
495 configdrive=mock.ANY) 522 configdrive=mock.ANY)
496 self.assertFalse(self.wait_mock.called) 523 self.assertFalse(self.wait_mock.called)
497 self.assertFalse(self.api.release_node.called) 524 self.assertFalse(self.api.release_node.called)
498 self.assertFalse(self.api.delete_port.called) 525 self.assertFalse(self.conn.network.delete_port.called)
499 526
500 def test_with_wait(self): 527 def test_with_wait(self):
501 self.api.get_port.return_value = mock.Mock( 528 self.conn.network.find_port.return_value = mock.Mock(
502 spec=['fixed_ips'], 529 spec=['fixed_ips'],
503 fixed_ips=[{'ip_address': '192.168.1.5'}, {}] 530 fixed_ips=[{'ip_address': '192.168.1.5'}, {}]
504 ) 531 )
505 self.pr.provision_node(self.node, 'image', [{'network': 'network'}], 532 self.pr.provision_node(self.node, 'image', [{'network': 'network'}],
506 wait=3600) 533 wait=3600)
507 534
508 self.api.create_port.assert_called_once_with( 535 self.conn.network.create_port.assert_called_once_with(
509 network_id=self.api.get_network.return_value.id) 536 network_id=self.conn.network.find_network.return_value.id)
510 self.api.attach_port_to_node.assert_called_once_with( 537 self.api.attach_port_to_node.assert_called_once_with(
511 self.node.uuid, self.api.create_port.return_value.id) 538 self.node.uuid, self.conn.network.create_port.return_value.id)
512 self.api.update_node.assert_called_once_with(self.node, self.updates) 539 self.api.update_node.assert_called_once_with(self.node, self.updates)
513 self.api.validate_node.assert_called_once_with(self.node, 540 self.api.validate_node.assert_called_once_with(self.node,
514 validate_deploy=True) 541 validate_deploy=True)
@@ -520,19 +547,19 @@ class TestProvisionNode(Base):
520 delay=15, 547 delay=15,
521 timeout=3600) 548 timeout=3600)
522 self.assertFalse(self.api.release_node.called) 549 self.assertFalse(self.api.release_node.called)
523 self.assertFalse(self.api.delete_port.called) 550 self.assertFalse(self.conn.network.delete_port.called)
524 551
525 def test_dry_run(self): 552 def test_dry_run(self):
526 self.pr._dry_run = True 553 self.pr._dry_run = True
527 self.pr.provision_node(self.node, 'image', [{'network': 'network'}]) 554 self.pr.provision_node(self.node, 'image', [{'network': 'network'}])
528 555
529 self.assertFalse(self.api.create_port.called) 556 self.assertFalse(self.conn.network.create_port.called)
530 self.assertFalse(self.api.attach_port_to_node.called) 557 self.assertFalse(self.api.attach_port_to_node.called)
531 self.assertFalse(self.api.update_node.called) 558 self.assertFalse(self.api.update_node.called)
532 self.assertFalse(self.api.node_action.called) 559 self.assertFalse(self.api.node_action.called)
533 self.assertFalse(self.wait_mock.called) 560 self.assertFalse(self.wait_mock.called)
534 self.assertFalse(self.api.release_node.called) 561 self.assertFalse(self.api.release_node.called)
535 self.assertFalse(self.api.delete_port.called) 562 self.assertFalse(self.conn.network.delete_port.called)
536 563
537 def test_unreserve_dry_run(self): 564 def test_unreserve_dry_run(self):
538 self.pr._dry_run = True 565 self.pr._dry_run = True
@@ -541,13 +568,13 @@ class TestProvisionNode(Base):
541 self.pr.provision_node(self.node, 'image', [{'network': 'network'}]) 568 self.pr.provision_node(self.node, 'image', [{'network': 'network'}])
542 569
543 self.assertFalse(self.api.reserve_node.called) 570 self.assertFalse(self.api.reserve_node.called)
544 self.assertFalse(self.api.create_port.called) 571 self.assertFalse(self.conn.network.create_port.called)
545 self.assertFalse(self.api.attach_port_to_node.called) 572 self.assertFalse(self.api.attach_port_to_node.called)
546 self.assertFalse(self.api.update_node.called) 573 self.assertFalse(self.api.update_node.called)
547 self.assertFalse(self.api.node_action.called) 574 self.assertFalse(self.api.node_action.called)
548 self.assertFalse(self.wait_mock.called) 575 self.assertFalse(self.wait_mock.called)
549 self.assertFalse(self.api.release_node.called) 576 self.assertFalse(self.api.release_node.called)
550 self.assertFalse(self.api.delete_port.called) 577 self.assertFalse(self.conn.network.delete_port.called)
551 578
552 def test_deploy_failure(self): 579 def test_deploy_failure(self):
553 self.api.node_action.side_effect = RuntimeError('boom') 580 self.api.node_action.side_effect = RuntimeError('boom')
@@ -559,16 +586,18 @@ class TestProvisionNode(Base):
559 self.api.update_node.assert_any_call(self.node, CLEAN_UP) 586 self.api.update_node.assert_any_call(self.node, CLEAN_UP)
560 self.assertFalse(self.wait_mock.called) 587 self.assertFalse(self.wait_mock.called)
561 self.api.release_node.assert_called_once_with(self.node) 588 self.api.release_node.assert_called_once_with(self.node)
562 self.api.delete_port.assert_called_once_with( 589 self.conn.network.delete_port.assert_called_once_with(
563 self.api.create_port.return_value.id) 590 self.conn.network.create_port.return_value.id,
591 ignore_missing=False)
564 calls = [ 592 calls = [
565 mock.call(self.node, self.api.create_port.return_value.id), 593 mock.call(self.node,
566 mock.call(self.node, self.api.get_port.return_value.id) 594 self.conn.network.create_port.return_value.id),
595 mock.call(self.node, self.conn.network.find_port.return_value.id)
567 ] 596 ]
568 self.api.detach_port_from_node.assert_has_calls(calls, any_order=True) 597 self.api.detach_port_from_node.assert_has_calls(calls, any_order=True)
569 598
570 def test_port_creation_failure(self): 599 def test_port_creation_failure(self):
571 self.api.create_port.side_effect = RuntimeError('boom') 600 self.conn.network.create_port.side_effect = RuntimeError('boom')
572 self.assertRaisesRegex(RuntimeError, 'boom', 601 self.assertRaisesRegex(RuntimeError, 'boom',
573 self.pr.provision_node, self.node, 602 self.pr.provision_node, self.node,
574 'image', [{'network': 'network'}], wait=3600) 603 'image', [{'network': 'network'}], wait=3600)
@@ -576,7 +605,7 @@ class TestProvisionNode(Base):
576 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) 605 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP)
577 self.assertFalse(self.api.node_action.called) 606 self.assertFalse(self.api.node_action.called)
578 self.api.release_node.assert_called_once_with(self.node) 607 self.api.release_node.assert_called_once_with(self.node)
579 self.assertFalse(self.api.delete_port.called) 608 self.assertFalse(self.conn.network.delete_port.called)
580 self.assertFalse(self.api.detach_port_from_node.called) 609 self.assertFalse(self.api.detach_port_from_node.called)
581 610
582 def test_port_attach_failure(self): 611 def test_port_attach_failure(self):
@@ -588,15 +617,32 @@ class TestProvisionNode(Base):
588 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) 617 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP)
589 self.assertFalse(self.api.node_action.called) 618 self.assertFalse(self.api.node_action.called)
590 self.api.release_node.assert_called_once_with(self.node) 619 self.api.release_node.assert_called_once_with(self.node)
591 self.api.delete_port.assert_called_once_with( 620 self.conn.network.delete_port.assert_called_once_with(
592 self.api.create_port.return_value.id) 621 self.conn.network.create_port.return_value.id,
622 ignore_missing=False)
623 self.api.detach_port_from_node.assert_called_once_with(
624 self.node, self.conn.network.create_port.return_value.id)
625
626 def test_failure_during_port_deletion(self):
627 self.conn.network.delete_port.side_effect = AssertionError()
628 self.api.node_action.side_effect = RuntimeError('boom')
629 self.assertRaisesRegex(RuntimeError, 'boom',
630 self.pr.provision_node, self.node,
631 'image', [{'network': 'network'}],
632 wait=3600)
633
634 self.assertFalse(self.wait_mock.called)
635 self.api.release_node.assert_called_once_with(self.node)
636 self.conn.network.delete_port.assert_called_once_with(
637 self.conn.network.create_port.return_value.id,
638 ignore_missing=False)
593 self.api.detach_port_from_node.assert_called_once_with( 639 self.api.detach_port_from_node.assert_called_once_with(
594 self.node, self.api.create_port.return_value.id) 640 self.node, self.conn.network.create_port.return_value.id)
595 641
596 @mock.patch.object(_provisioner.LOG, 'exception', autospec=True) 642 @mock.patch.object(_provisioner.LOG, 'exception', autospec=True)
597 def test_failure_during_deploy_failure(self, mock_log_exc): 643 def test_failure_during_deploy_failure(self, mock_log_exc):
598 for failed_call in ['detach_port_from_node', 644 for failed_call in ['detach_port_from_node',
599 'delete_port', 'release_node']: 645 'release_node']:
600 self._reset_api_mock() 646 self._reset_api_mock()
601 getattr(self.api, failed_call).side_effect = AssertionError() 647 getattr(self.api, failed_call).side_effect = AssertionError()
602 self.api.node_action.side_effect = RuntimeError('boom') 648 self.api.node_action.side_effect = RuntimeError('boom')
@@ -607,10 +653,11 @@ class TestProvisionNode(Base):
607 653
608 self.assertFalse(self.wait_mock.called) 654 self.assertFalse(self.wait_mock.called)
609 self.api.release_node.assert_called_once_with(self.node) 655 self.api.release_node.assert_called_once_with(self.node)
610 self.api.delete_port.assert_called_once_with( 656 self.conn.network.delete_port.assert_called_once_with(
611 self.api.create_port.return_value.id) 657 self.conn.network.create_port.return_value.id,
658 ignore_missing=False)
612 self.api.detach_port_from_node.assert_called_once_with( 659 self.api.detach_port_from_node.assert_called_once_with(
613 self.node, self.api.create_port.return_value.id) 660 self.node, self.conn.network.create_port.return_value.id)
614 self.assertEqual(mock_log_exc.called, 661 self.assertEqual(mock_log_exc.called,
615 failed_call == 'release_node') 662 failed_call == 'release_node')
616 663
@@ -624,10 +671,11 @@ class TestProvisionNode(Base):
624 671
625 self.assertFalse(self.wait_mock.called) 672 self.assertFalse(self.wait_mock.called)
626 self.api.release_node.assert_called_once_with(self.node) 673 self.api.release_node.assert_called_once_with(self.node)
627 self.api.delete_port.assert_called_once_with( 674 self.conn.network.delete_port.assert_called_once_with(
628 self.api.create_port.return_value.id) 675 self.conn.network.create_port.return_value.id,
676 ignore_missing=False)
629 self.api.detach_port_from_node.assert_called_once_with( 677 self.api.detach_port_from_node.assert_called_once_with(
630 self.node, self.api.create_port.return_value.id) 678 self.node, self.conn.network.create_port.return_value.id)
631 679
632 def test_wait_failure(self): 680 def test_wait_failure(self):
633 self.wait_mock.side_effect = RuntimeError('boom') 681 self.wait_mock.side_effect = RuntimeError('boom')
@@ -639,11 +687,12 @@ class TestProvisionNode(Base):
639 self.api.node_action.assert_called_once_with(self.node, 'active', 687 self.api.node_action.assert_called_once_with(self.node, 'active',
640 configdrive=mock.ANY) 688 configdrive=mock.ANY)
641 self.assertFalse(self.api.release_node.called) 689 self.assertFalse(self.api.release_node.called)
642 self.assertFalse(self.api.delete_port.called) 690 self.assertFalse(self.conn.network.delete_port.called)
643 self.assertFalse(self.api.detach_port_from_node.called) 691 self.assertFalse(self.api.detach_port_from_node.called)
644 692
645 def test_missing_image(self): 693 def test_missing_image(self):
646 self.api.get_image.side_effect = RuntimeError('Not found') 694 self.conn.image.find_image.side_effect = os_exc.ResourceNotFound(
695 'Not found')
647 self.assertRaisesRegex(exceptions.InvalidImage, 'Not found', 696 self.assertRaisesRegex(exceptions.InvalidImage, 'Not found',
648 self.pr.provision_node, 697 self.pr.provision_node,
649 self.node, 'image', [{'network': 'network'}]) 698 self.node, 'image', [{'network': 'network'}])
@@ -652,22 +701,22 @@ class TestProvisionNode(Base):
652 self.api.release_node.assert_called_once_with(self.node) 701 self.api.release_node.assert_called_once_with(self.node)
653 702
654 def test_invalid_network(self): 703 def test_invalid_network(self):
655 self.api.get_network.side_effect = RuntimeError('Not found') 704 self.conn.network.find_network.side_effect = RuntimeError('Not found')
656 self.assertRaisesRegex(exceptions.InvalidNIC, 'Not found', 705 self.assertRaisesRegex(exceptions.InvalidNIC, 'Not found',
657 self.pr.provision_node, 706 self.pr.provision_node,
658 self.node, 'image', [{'network': 'network'}]) 707 self.node, 'image', [{'network': 'network'}])
659 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) 708 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP)
660 self.assertFalse(self.api.create_port.called) 709 self.assertFalse(self.conn.network.create_port.called)
661 self.assertFalse(self.api.node_action.called) 710 self.assertFalse(self.api.node_action.called)
662 self.api.release_node.assert_called_once_with(self.node) 711 self.api.release_node.assert_called_once_with(self.node)
663 712
664 def test_invalid_port(self): 713 def test_invalid_port(self):
665 self.api.get_port.side_effect = RuntimeError('Not found') 714 self.conn.network.find_port.side_effect = RuntimeError('Not found')
666 self.assertRaisesRegex(exceptions.InvalidNIC, 'Not found', 715 self.assertRaisesRegex(exceptions.InvalidNIC, 'Not found',
667 self.pr.provision_node, 716 self.pr.provision_node,
668 self.node, 'image', [{'port': 'port1'}]) 717 self.node, 'image', [{'port': 'port1'}])
669 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) 718 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP)
670 self.assertFalse(self.api.create_port.called) 719 self.assertFalse(self.conn.network.create_port.called)
671 self.assertFalse(self.api.node_action.called) 720 self.assertFalse(self.api.node_action.called)
672 self.api.release_node.assert_called_once_with(self.node) 721 self.api.release_node.assert_called_once_with(self.node)
673 722
@@ -676,7 +725,7 @@ class TestProvisionNode(Base):
676 self.assertRaises(exceptions.UnknownRootDiskSize, 725 self.assertRaises(exceptions.UnknownRootDiskSize,
677 self.pr.provision_node, 726 self.pr.provision_node,
678 self.node, 'image', [{'network': 'network'}]) 727 self.node, 'image', [{'network': 'network'}])
679 self.assertFalse(self.api.create_port.called) 728 self.assertFalse(self.conn.network.create_port.called)
680 self.assertFalse(self.api.node_action.called) 729 self.assertFalse(self.api.node_action.called)
681 self.api.release_node.assert_called_once_with(self.node) 730 self.api.release_node.assert_called_once_with(self.node)
682 731
@@ -686,7 +735,7 @@ class TestProvisionNode(Base):
686 self.assertRaises(exceptions.UnknownRootDiskSize, 735 self.assertRaises(exceptions.UnknownRootDiskSize,
687 self.pr.provision_node, 736 self.pr.provision_node,
688 self.node, 'image', [{'network': 'network'}]) 737 self.node, 'image', [{'network': 'network'}])
689 self.assertFalse(self.api.create_port.called) 738 self.assertFalse(self.conn.network.create_port.called)
690 self.assertFalse(self.api.node_action.called) 739 self.assertFalse(self.api.node_action.called)
691 self.api.release_node.assert_called_with(self.node) 740 self.api.release_node.assert_called_with(self.node)
692 741
@@ -699,7 +748,7 @@ class TestProvisionNode(Base):
699 self.pr.provision_node, 748 self.pr.provision_node,
700 self.node, 'image', [{'network': 'network'}], 749 self.node, 'image', [{'network': 'network'}],
701 root_disk_size=0) 750 root_disk_size=0)
702 self.assertFalse(self.api.create_port.called) 751 self.assertFalse(self.conn.network.create_port.called)
703 self.assertFalse(self.api.node_action.called) 752 self.assertFalse(self.api.node_action.called)
704 self.api.release_node.assert_called_with(self.node) 753 self.api.release_node.assert_called_with(self.node)
705 754
@@ -707,7 +756,7 @@ class TestProvisionNode(Base):
707 self.assertRaisesRegex(TypeError, 'must be a list', 756 self.assertRaisesRegex(TypeError, 'must be a list',
708 self.pr.provision_node, 757 self.pr.provision_node,
709 self.node, 'image', 42) 758 self.node, 'image', 42)
710 self.assertFalse(self.api.create_port.called) 759 self.assertFalse(self.conn.network.create_port.called)
711 self.assertFalse(self.api.attach_port_to_node.called) 760 self.assertFalse(self.api.attach_port_to_node.called)
712 self.assertFalse(self.api.node_action.called) 761 self.assertFalse(self.api.node_action.called)
713 self.api.release_node.assert_called_once_with(self.node) 762 self.api.release_node.assert_called_once_with(self.node)
@@ -717,7 +766,7 @@ class TestProvisionNode(Base):
717 self.assertRaisesRegex(TypeError, 'must be a dict', 766 self.assertRaisesRegex(TypeError, 'must be a dict',
718 self.pr.provision_node, 767 self.pr.provision_node,
719 self.node, 'image', item) 768 self.node, 'image', item)
720 self.assertFalse(self.api.create_port.called) 769 self.assertFalse(self.conn.network.create_port.called)
721 self.assertFalse(self.api.attach_port_to_node.called) 770 self.assertFalse(self.api.attach_port_to_node.called)
722 self.assertFalse(self.api.node_action.called) 771 self.assertFalse(self.api.node_action.called)
723 self.api.release_node.assert_called_with(self.node) 772 self.api.release_node.assert_called_with(self.node)
@@ -726,7 +775,7 @@ class TestProvisionNode(Base):
726 self.assertRaisesRegex(ValueError, r'Unexpected NIC type\(s\) foo', 775 self.assertRaisesRegex(ValueError, r'Unexpected NIC type\(s\) foo',
727 self.pr.provision_node, 776 self.pr.provision_node,
728 self.node, 'image', [{'foo': 'bar'}]) 777 self.node, 'image', [{'foo': 'bar'}])
729 self.assertFalse(self.api.create_port.called) 778 self.assertFalse(self.conn.network.create_port.called)
730 self.assertFalse(self.api.attach_port_to_node.called) 779 self.assertFalse(self.api.attach_port_to_node.called)
731 self.assertFalse(self.api.node_action.called) 780 self.assertFalse(self.api.node_action.called)
732 self.api.release_node.assert_called_once_with(self.node) 781 self.api.release_node.assert_called_once_with(self.node)
@@ -737,7 +786,7 @@ class TestProvisionNode(Base):
737 self.node, 'image', [{'port': 'port1'}], 786 self.node, 'image', [{'port': 'port1'}],
738 hostname='n_1') 787 hostname='n_1')
739 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) 788 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP)
740 self.assertFalse(self.api.create_port.called) 789 self.assertFalse(self.conn.network.create_port.called)
741 self.assertFalse(self.api.node_action.called) 790 self.assertFalse(self.api.node_action.called)
742 self.api.release_node.assert_called_once_with(self.node) 791 self.api.release_node.assert_called_once_with(self.node)
743 792
@@ -749,7 +798,7 @@ class TestProvisionNode(Base):
749 self.node, 'image', [{'port': 'port1'}], 798 self.node, 'image', [{'port': 'port1'}],
750 hostname='host') 799 hostname='host')
751 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP) 800 self.api.update_node.assert_called_once_with(self.node, CLEAN_UP)
752 self.assertFalse(self.api.create_port.called) 801 self.assertFalse(self.conn.network.create_port.called)
753 self.assertFalse(self.api.node_action.called) 802 self.assertFalse(self.api.node_action.called)
754 self.api.release_node.assert_called_once_with(self.node) 803 self.api.release_node.assert_called_once_with(self.node)
755 804
@@ -758,7 +807,7 @@ class TestProvisionNode(Base):
758 self.assertRaisesRegex(exceptions.InvalidNode, 'not found', 807 self.assertRaisesRegex(exceptions.InvalidNode, 'not found',
759 self.pr.provision_node, 808 self.pr.provision_node,
760 self.node, 'image', [{'network': 'network'}]) 809 self.node, 'image', [{'network': 'network'}])
761 self.assertFalse(self.api.create_port.called) 810 self.assertFalse(self.conn.network.create_port.called)
762 self.assertFalse(self.api.update_node.called) 811 self.assertFalse(self.api.update_node.called)
763 self.assertFalse(self.api.node_action.called) 812 self.assertFalse(self.api.node_action.called)
764 self.assertFalse(self.api.release_node.called) 813 self.assertFalse(self.api.release_node.called)
@@ -769,7 +818,7 @@ class TestProvisionNode(Base):
769 'reserved by instance nova', 818 'reserved by instance nova',
770 self.pr.provision_node, 819 self.pr.provision_node,
771 self.node, 'image', [{'network': 'network'}]) 820 self.node, 'image', [{'network': 'network'}])
772 self.assertFalse(self.api.create_port.called) 821 self.assertFalse(self.conn.network.create_port.called)
773 self.assertFalse(self.api.update_node.called) 822 self.assertFalse(self.api.update_node.called)
774 self.assertFalse(self.api.node_action.called) 823 self.assertFalse(self.api.node_action.called)
775 self.assertFalse(self.api.release_node.called) 824 self.assertFalse(self.api.release_node.called)
@@ -781,7 +830,7 @@ class TestProvisionNode(Base):
781 'in maintenance mode .* power failure', 830 'in maintenance mode .* power failure',
782 self.pr.provision_node, 831 self.pr.provision_node,
783 self.node, 'image', [{'network': 'network'}]) 832 self.node, 'image', [{'network': 'network'}])
784 self.assertFalse(self.api.create_port.called) 833 self.assertFalse(self.conn.network.create_port.called)
785 self.assertFalse(self.api.update_node.called) 834 self.assertFalse(self.api.update_node.called)
786 self.assertFalse(self.api.node_action.called) 835 self.assertFalse(self.api.node_action.called)
787 self.assertFalse(self.api.release_node.called) 836 self.assertFalse(self.api.release_node.called)
@@ -801,7 +850,8 @@ class TestUnprovisionNode(Base):
801 result = self.pr.unprovision_node(self.node) 850 result = self.pr.unprovision_node(self.node)
802 self.assertIs(result, self.node) 851 self.assertIs(result, self.node)
803 852
804 self.api.delete_port.assert_called_once_with('port1') 853 self.conn.network.delete_port.assert_called_once_with(
854 'port1', ignore_missing=False)
805 self.api.detach_port_from_node.assert_called_once_with(self.node, 855 self.api.detach_port_from_node.assert_called_once_with(self.node,
806 'port1') 856 'port1')
807 self.api.node_action.assert_called_once_with(self.node, 'deleted') 857 self.api.node_action.assert_called_once_with(self.node, 'deleted')
@@ -814,7 +864,8 @@ class TestUnprovisionNode(Base):
814 self.node.extra['metalsmith_attached_ports'] = ['port1', 'port2'] 864 self.node.extra['metalsmith_attached_ports'] = ['port1', 'port2']
815 self.pr.unprovision_node(self.node) 865 self.pr.unprovision_node(self.node)
816 866
817 self.api.delete_port.assert_called_once_with('port1') 867 self.conn.network.delete_port.assert_called_once_with(
868 'port1', ignore_missing=False)
818 calls = [mock.call(self.node, 'port1'), mock.call(self.node, 'port2')] 869 calls = [mock.call(self.node, 'port1'), mock.call(self.node, 'port2')]
819 self.api.detach_port_from_node.assert_has_calls(calls, any_order=True) 870 self.api.detach_port_from_node.assert_has_calls(calls, any_order=True)
820 self.api.node_action.assert_called_once_with(self.node, 'deleted') 871 self.api.node_action.assert_called_once_with(self.node, 'deleted')
@@ -827,7 +878,8 @@ class TestUnprovisionNode(Base):
827 result = self.pr.unprovision_node(self.node, wait=3600) 878 result = self.pr.unprovision_node(self.node, wait=3600)
828 self.assertIs(result, self.node) 879 self.assertIs(result, self.node)
829 880
830 self.api.delete_port.assert_called_once_with('port1') 881 self.conn.network.delete_port.assert_called_once_with(
882 'port1', ignore_missing=False)
831 self.api.detach_port_from_node.assert_called_once_with(self.node, 883 self.api.detach_port_from_node.assert_called_once_with(self.node,
832 'port1') 884 'port1')
833 self.api.node_action.assert_called_once_with(self.node, 'deleted') 885 self.api.node_action.assert_called_once_with(self.node, 'deleted')
@@ -844,7 +896,7 @@ class TestUnprovisionNode(Base):
844 896
845 self.assertFalse(self.api.node_action.called) 897 self.assertFalse(self.api.node_action.called)
846 self.assertFalse(self.api.release_node.called) 898 self.assertFalse(self.api.release_node.called)
847 self.assertFalse(self.api.delete_port.called) 899 self.assertFalse(self.conn.network.delete_port.called)
848 self.assertFalse(self.api.detach_port_from_node.called) 900 self.assertFalse(self.api.detach_port_from_node.called)
849 self.assertFalse(self.wait_mock.called) 901 self.assertFalse(self.wait_mock.called)
850 self.assertFalse(self.api.update_node.called) 902 self.assertFalse(self.api.update_node.called)