Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mgagne/08679164845921f7107f to your computer and use it in GitHub Desktop.
Save mgagne/08679164845921f7107f to your computer and use it in GitHub Desktop.
Add attach_interface and detach_interface support for Nova cells (Icehouse)
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