Last active
August 29, 2015 14:24
-
-
Save mgagne/08679164845921f7107f to your computer and use it in GitHub Desktop.
Add attach_interface and detach_interface support for Nova cells (Icehouse)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From: =?UTF-8?q?Mathieu=20Gagn=C3=A9?= <[email protected]> | |
Date: Fri, 15 May 2015 14:11:10 -0400 | |
Subject: Add attach_interface and detach_interface support with cells | |
Make sure the API cell is updated when networks are allocated. | |
Change-Id: Iff24c44f6d8f6e03cfa499795dccd42e5f49f70d | |
--- | |
.../openstack/compute/contrib/attach_interfaces.py | 7 ++++ | |
.../compute/plugins/v3/attach_interfaces.py | 6 ++++ | |
nova/compute/api.py | 36 +++++++++++++++++--- | |
nova/compute/cells_api.py | 14 ++++++++ | |
nova/network/neutronv2/api.py | 18 ++++++---- | |
nova/tests/compute/test_compute_api.py | 30 ++++++++++++++++ | |
nova/tests/fake_policy.py | 3 ++ | |
nova/tests/network/test_neutronv2.py | 3 +- | |
8 files changed, 105 insertions(+), 12 deletions(-) | |
diff --git a/nova/api/openstack/compute/contrib/attach_interfaces.py b/nova/api/openstack/compute/contrib/attach_interfaces.py | |
index c33d941..9d67532 100644 | |
--- a/nova/api/openstack/compute/contrib/attach_interfaces.py | |
+++ b/nova/api/openstack/compute/contrib/attach_interfaces.py | |
@@ -18,6 +18,7 @@ | |
import webob | |
from webob import exc | |
+from nova.api.openstack import common | |
from nova.api.openstack import extensions | |
from nova import compute | |
from nova import exception | |
@@ -120,6 +121,9 @@ class InterfaceAttachmentController(object): | |
LOG.exception(e) | |
msg = _("Failed to attach interface") | |
raise webob.exc.HTTPInternalServerError(explanation=msg) | |
+ except exception.InstanceInvalidState as state_error: | |
+ common.raise_http_conflict_for_instance_invalid_state(state_error, | |
+ 'attach_interface') | |
return self.show(req, server_id, vif['id']) | |
@@ -149,6 +153,9 @@ class InterfaceAttachmentController(object): | |
except NotImplementedError: | |
msg = _("Network driver does not support this function.") | |
raise webob.exc.HTTPNotImplemented(explanation=msg) | |
+ except exception.InstanceInvalidState as state_error: | |
+ common.raise_http_conflict_for_instance_invalid_state(state_error, | |
+ 'detach_interface') | |
return webob.Response(status_int=202) | |
diff --git a/nova/api/openstack/compute/plugins/v3/attach_interfaces.py b/nova/api/openstack/compute/plugins/v3/attach_interfaces.py | |
index d87aa18..3665c4d 100644 | |
--- a/nova/api/openstack/compute/plugins/v3/attach_interfaces.py | |
+++ b/nova/api/openstack/compute/plugins/v3/attach_interfaces.py | |
@@ -117,6 +117,9 @@ class InterfaceAttachmentController(object): | |
LOG.exception(e) | |
raise webob.exc.HTTPInternalServerError( | |
explanation=e.format_message()) | |
+ except exception.InstanceInvalidState as state_error: | |
+ common.raise_http_conflict_for_instance_invalid_state(state_error, | |
+ 'attach_interface') | |
return self.show(req, server_id, vif['id']) | |
@@ -141,6 +144,9 @@ class InterfaceAttachmentController(object): | |
raise exc.HTTPNotFound() | |
except NotImplementedError as e: | |
raise webob.exc.HTTPNotImplemented(explanation=e.format_message()) | |
+ except exception.InstanceInvalidState as state_error: | |
+ common.raise_http_conflict_for_instance_invalid_state(state_error, | |
+ 'detach_interface') | |
return webob.Response(status_int=202) | |
diff --git a/nova/compute/api.py b/nova/compute/api.py | |
index 0ea3251..d4361e9 100644 | |
--- a/nova/compute/api.py | |
+++ b/nova/compute/api.py | |
@@ -2855,21 +2855,47 @@ class API(base.Base): | |
self.volume_api.roll_detaching(context, old_volume['id']) | |
self.volume_api.unreserve_volume(context, new_volume['id']) | |
- @wrap_check_policy | |
- def attach_interface(self, context, instance, network_id, port_id, | |
+ def _attach_interface(self, context, instance, network_id, port_id, | |
requested_ip): | |
- """Use hotplug to add an network adapter to an instance.""" | |
+ """Attach an interface to an existing instance. | |
+ | |
+ This method is separated to make it possible for cells version | |
+ to override it. | |
+ """ | |
return self.compute_rpcapi.attach_interface(context, | |
instance=instance, network_id=network_id, port_id=port_id, | |
requested_ip=requested_ip) | |
@wrap_check_policy | |
- def detach_interface(self, context, instance, port_id): | |
- """Detach an network adapter from an instance.""" | |
+ @check_instance_lock | |
+ @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED, | |
+ vm_states.STOPPED], | |
+ task_state=[None]) | |
+ def attach_interface(self, context, instance, network_id, port_id, | |
+ requested_ip): | |
+ """Use hotplug to add an network adapter to an instance.""" | |
+ return self._attach_interface(context, instance=instance, | |
+ network_id=network_id, port_id=port_id, requested_ip=requested_ip) | |
+ | |
+ def _detach_interface(self, context, instance, port_id): | |
+ """Detach interface from instance. | |
+ | |
+ This method is separated to make it easier for cells version | |
+ to override. | |
+ """ | |
self.compute_rpcapi.detach_interface(context, instance=instance, | |
port_id=port_id) | |
@wrap_check_policy | |
+ @check_instance_lock | |
+ @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED, | |
+ vm_states.STOPPED], | |
+ task_state=[None]) | |
+ def detach_interface(self, context, instance, port_id): | |
+ """Detach an network adapter from an instance.""" | |
+ self._detach_interface(context, instance=instance, port_id=port_id) | |
+ | |
+ @wrap_check_policy | |
def get_instance_metadata(self, context, instance): | |
"""Get all metadata associated with an instance.""" | |
rv = self.db.instance_metadata_get(context, instance['uuid']) | |
diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py | |
index 1cf9f39..2e8da09 100644 | |
--- a/nova/compute/cells_api.py | |
+++ b/nova/compute/cells_api.py | |
@@ -425,6 +425,20 @@ class ComputeCellsAPI(compute_api.API): | |
self._cast_to_cells(context, instance, 'detach_volume', | |
volume) | |
+ @check_instance_cell | |
+ def _attach_interface(self, context, instance, network_id, port_id, | |
+ requested_ip): | |
+ """Use hotplug to add an network adapter to an instance.""" | |
+ return self._call_to_cells(context, instance, 'attach_interface', | |
+ network_id=network_id, port_id=port_id, | |
+ requested_ip=requested_ip) | |
+ | |
+ @check_instance_cell | |
+ def _detach_interface(self, context, instance, port_id): | |
+ """Detach an network adapter from an instance.""" | |
+ return self._call_to_cells(context, instance, 'detach_interface', | |
+ port_id=port_id) | |
+ | |
@wrap_check_policy | |
@check_instance_cell | |
def associate_floating_ip(self, context, instance, address): | |
diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py | |
index 3eb4cbf..cbe39da 100644 | |
--- a/nova/network/neutronv2/api.py | |
+++ b/nova/network/neutronv2/api.py | |
@@ -33,6 +33,7 @@ from nova.network.security_group import openstack_driver | |
from nova.openstack.common import excutils | |
from nova.openstack.common.gettextutils import _ | |
from nova.openstack.common import jsonutils | |
+from nova.openstack.common import lockutils | |
from nova.openstack.common import log as logging | |
from nova.openstack.common import uuidutils | |
@@ -361,7 +362,8 @@ class API(base.Base): | |
LOG.exception(msg, port_id) | |
nw_info = self.get_instance_nw_info(context, instance, networks=nets, | |
- port_ids=ports_in_requested_order) | |
+ port_ids=ports_in_requested_order, | |
+ update_cells=True) | |
# NOTE(danms): Only return info about ports we created in this run. | |
# In the initial allocation case, this will be everything we created, | |
# and in later runs will only be what was created that time. Thus, | |
@@ -450,7 +452,7 @@ class API(base.Base): | |
LOG.exception(_("Failed to delete neutron port %s") % | |
port_id) | |
- return self.get_instance_nw_info(context, instance) | |
+ return self.get_instance_nw_info(context, instance, update_cells=True) | |
def list_ports(self, context, **search_opts): | |
"""List ports for the client based on search options.""" | |
@@ -460,17 +462,21 @@ class API(base.Base): | |
"""Return the port for the client given the port id.""" | |
return neutronv2.get_client(context).show_port(port_id) | |
- @refresh_cache | |
def get_instance_nw_info(self, context, instance, networks=None, | |
- port_ids=None, use_slave=False): | |
+ port_ids=None, use_slave=False, | |
+ update_cells=False): | |
"""Return network information for specified instance | |
and update cache. | |
""" | |
# NOTE(geekinutah): It would be nice if use_slave had us call | |
# special APIs that pummeled slaves instead of | |
# the master. For now we just ignore this arg. | |
- result = self._get_instance_nw_info(context, instance, networks, | |
- port_ids) | |
+ with lockutils.lock('refresh_cache-%s' % instance['uuid']): | |
+ result = self._get_instance_nw_info(context, instance, networks, | |
+ port_ids) | |
+ update_instance_info_cache( | |
+ self, context, instance, nw_info=result, | |
+ update_cells=update_cells) | |
return result | |
def _get_instance_nw_info(self, context, instance, networks=None, | |
diff --git a/nova/tests/compute/test_compute_api.py b/nova/tests/compute/test_compute_api.py | |
index 361a402..fa0373a 100644 | |
--- a/nova/tests/compute/test_compute_api.py | |
+++ b/nova/tests/compute/test_compute_api.py | |
@@ -1872,6 +1872,36 @@ class _ComputeAPIUnitTestMixIn(object): | |
self.context, instance, | |
volume_id, new_volume_id) | |
+ def _test_detach_interface_invalid_state(self, state): | |
+ instance = self._create_instance_obj( | |
+ params={'vm_state': state}) | |
+ self.assertRaises(exception.InstanceInvalidState, | |
+ self.compute_api.detach_interface, | |
+ self.context, instance, '') | |
+ | |
+ def test_detach_interface_invalid_state(self): | |
+ for state in [vm_states.BUILDING, vm_states.DELETED, | |
+ vm_states.ERROR, vm_states.RESCUED, | |
+ vm_states.RESIZED, vm_states.SOFT_DELETED, | |
+ vm_states.SUSPENDED, vm_states.SHELVED, | |
+ vm_states.SHELVED_OFFLOADED]: | |
+ self._test_detach_interface_invalid_state(state) | |
+ | |
+ def _test_attach_interface_invalid_state(self, state): | |
+ instance = self._create_instance_obj( | |
+ params={'vm_state': state}) | |
+ self.assertRaises(exception.InstanceInvalidState, | |
+ self.compute_api.attach_interface, | |
+ self.context, instance, '', '', []) | |
+ | |
+ def test_attach_interface_invalid_state(self): | |
+ for state in [vm_states.BUILDING, vm_states.DELETED, | |
+ vm_states.ERROR, vm_states.RESCUED, | |
+ vm_states.RESIZED, vm_states.SOFT_DELETED, | |
+ vm_states.SUSPENDED, vm_states.SHELVED, | |
+ vm_states.SHELVED_OFFLOADED]: | |
+ self._test_attach_interface_invalid_state(state) | |
+ | |
class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase): | |
def setUp(self): | |
diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py | |
index ad7953f..c6c52fd 100644 | |
--- a/nova/tests/fake_policy.py | |
+++ b/nova/tests/fake_policy.py | |
@@ -58,6 +58,9 @@ policy_data = """ | |
"compute:attach_volume": "", | |
"compute:detach_volume": "", | |
+ "compute:attach_interface": "", | |
+ "compute:detach_interface": "", | |
+ | |
"compute:set_admin_password": "", | |
"compute:rescue": "", | |
diff --git a/nova/tests/network/test_neutronv2.py b/nova/tests/network/test_neutronv2.py | |
index 025e15a..de35f77 100644 | |
--- a/nova/tests/network/test_neutronv2.py | |
+++ b/nova/tests/network/test_neutronv2.py | |
@@ -408,7 +408,8 @@ class TestNeutronv2Base(test.TestCase): | |
api.get_instance_nw_info(mox.IgnoreArg(), | |
self.instance, | |
networks=nets, | |
- port_ids=ports_in_requested_net_order | |
+ port_ids=ports_in_requested_net_order, | |
+ update_cells=True | |
).AndReturn(self._returned_nw_info) | |
self.mox.ReplayAll() | |
return api |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment