summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyu Ishimoto <ryu@midokura.jp>2011-08-19 08:54:49 +0000
committerTarmac <>2011-08-19 08:54:49 +0000
commitba079e5ccfa6b5f9fdfa036842f3e5ba51df4b75 (patch)
tree98d274d18ff5f6a684f83fbe2b230e7be8bb720d
parent965f82ac122173f942806cd8a39890a1678a641e (diff)
parent93bf9b46a8ca28063752bc9e6c14ed59e91c50a9 (diff)
downloadnova-ba079e5ccfa6b5f9fdfa036842f3e5ba51df4b75.tar.gz
nova-ba079e5ccfa6b5f9fdfa036842f3e5ba51df4b75.tar.xz
nova-ba079e5ccfa6b5f9fdfa036842f3e5ba51df4b75.zip
Added uuid column in virtual_interfaces table, and an OpenStack extension API for virtual interfaces to expose these IDs. Also set this UUID as one of the external IDs in the OVS vif driver.
-rw-r--r--nova/api/openstack/contrib/virtual_interfaces.py108
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/038_add_uuid_to_virtual_interfaces.py44
-rw-r--r--nova/db/sqlalchemy/models.py2
-rw-r--r--nova/network/manager.py4
-rw-r--r--nova/tests/api/openstack/contrib/test_virtual_interfaces.py55
-rw-r--r--nova/tests/api/openstack/test_extensions.py3
-rw-r--r--nova/tests/test_network.py5
-rw-r--r--nova/virt/libvirt/vif.py13
-rw-r--r--nova/virt/xenapi/vif.py4
9 files changed, 228 insertions, 10 deletions
diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py
new file mode 100644
index 000000000..dab61efc8
--- /dev/null
+++ b/nova/api/openstack/contrib/virtual_interfaces.py
@@ -0,0 +1,108 @@
+# Copyright (C) 2011 Midokura KK
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""The virtual interfaces extension."""
+
+from webob import exc
+import webob
+
+from nova import compute
+from nova import exception
+from nova import log as logging
+from nova.api.openstack import common
+from nova.api.openstack import extensions
+from nova.api.openstack import faults
+from nova.api.openstack import wsgi
+
+
+LOG = logging.getLogger("nova.api.virtual_interfaces")
+
+
+def _translate_vif_summary_view(_context, vif):
+ """Maps keys for VIF summary view."""
+ d = {}
+ d['id'] = vif['uuid']
+ d['mac_address'] = vif['address']
+ return d
+
+
+def _get_metadata():
+ metadata = {
+ "attributes": {
+ 'virtual_interface': ["id", "mac_address"]}}
+ return metadata
+
+
+class ServerVirtualInterfaceController(object):
+ """The instance VIF API controller for the Openstack API.
+ """
+
+ def __init__(self):
+ self.compute_api = compute.API()
+ super(ServerVirtualInterfaceController, self).__init__()
+
+ def _items(self, req, server_id, entity_maker):
+ """Returns a list of VIFs, transformed through entity_maker."""
+ context = req.environ['nova.context']
+
+ try:
+ instance = self.compute_api.get(context, server_id)
+ except exception.NotFound:
+ return faults.Fault(exc.HTTPNotFound())
+
+ vifs = instance['virtual_interfaces']
+ limited_list = common.limited(vifs, req)
+ res = [entity_maker(context, vif) for vif in limited_list]
+ return {'virtual_interfaces': res}
+
+ def index(self, req, server_id):
+ """Returns the list of VIFs for a given instance."""
+ return self._items(req, server_id,
+ entity_maker=_translate_vif_summary_view)
+
+
+class Virtual_interfaces(extensions.ExtensionDescriptor):
+
+ def get_name(self):
+ return "VirtualInterfaces"
+
+ def get_alias(self):
+ return "virtual_interfaces"
+
+ def get_description(self):
+ return "Virtual interface support"
+
+ def get_namespace(self):
+ return "http://docs.openstack.org/ext/virtual_interfaces/api/v1.1"
+
+ def get_updated(self):
+ return "2011-08-17T00:00:00+00:00"
+
+ def get_resources(self):
+ resources = []
+
+ metadata = _get_metadata()
+ body_serializers = {
+ 'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
+ xmlns=wsgi.XMLNS_V11)}
+ serializer = wsgi.ResponseSerializer(body_serializers, None)
+ res = extensions.ResourceExtension(
+ 'os-virtual-interfaces',
+ controller=ServerVirtualInterfaceController(),
+ parent=dict(member_name='server', collection_name='servers'),
+ serializer=serializer)
+ resources.append(res)
+
+ return resources
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/038_add_uuid_to_virtual_interfaces.py b/nova/db/sqlalchemy/migrate_repo/versions/038_add_uuid_to_virtual_interfaces.py
new file mode 100644
index 000000000..0f542cbec
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/038_add_uuid_to_virtual_interfaces.py
@@ -0,0 +1,44 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (C) 2011 Midokura KK
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from sqlalchemy import Column, Integer, MetaData, String, Table
+
+from nova import utils
+
+
+meta = MetaData()
+
+virtual_interfaces = Table("virtual_interfaces", meta,
+ Column("id", Integer(), primary_key=True,
+ nullable=False))
+uuid_column = Column("uuid", String(36))
+
+
+def upgrade(migrate_engine):
+ meta.bind = migrate_engine
+ virtual_interfaces.create_column(uuid_column)
+
+ rows = migrate_engine.execute(virtual_interfaces.select())
+ for row in rows:
+ vif_uuid = str(utils.gen_uuid())
+ migrate_engine.execute(virtual_interfaces.update()\
+ .where(virtual_interfaces.c.id == row[0])\
+ .values(uuid=vif_uuid))
+
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+ virtual_interfaces.drop_column(uuid_column)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index a8e9c36db..0e2bace83 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -570,6 +570,8 @@ class VirtualInterface(BASE, NovaBase):
instance_id = Column(Integer, ForeignKey('instances.id'), nullable=False)
instance = relationship(Instance, backref=backref('virtual_interfaces'))
+ uuid = Column(String(36))
+
@property
def fixed_ipv6(self):
cidr_v6 = self.network.cidr_v6
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 08439b004..921c27e45 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -500,6 +500,7 @@ class NetworkManager(manager.SchedulerDependentManager):
'dhcp_server': dhcp_server,
'broadcast': network['broadcast'],
'mac': vif['address'],
+ 'vif_uuid': vif['uuid'],
'rxtx_cap': flavor['rxtx_cap'],
'dns': [],
'ips': [ip_dict(ip) for ip in network_IPs],
@@ -524,7 +525,8 @@ class NetworkManager(manager.SchedulerDependentManager):
for network in networks:
vif = {'address': self.generate_mac_address(),
'instance_id': instance_id,
- 'network_id': network['id']}
+ 'network_id': network['id'],
+ 'uuid': str(utils.gen_uuid())}
# try FLAG times to create a vif record with a unique mac_address
for i in range(FLAGS.create_unique_mac_address_attempts):
try:
diff --git a/nova/tests/api/openstack/contrib/test_virtual_interfaces.py b/nova/tests/api/openstack/contrib/test_virtual_interfaces.py
new file mode 100644
index 000000000..d541a9e95
--- /dev/null
+++ b/nova/tests/api/openstack/contrib/test_virtual_interfaces.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2011 Midokura KK
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+import stubout
+import webob
+
+from nova import test
+from nova import compute
+from nova.tests.api.openstack import fakes
+from nova.api.openstack.contrib.virtual_interfaces import \
+ ServerVirtualInterfaceController
+
+
+def compute_api_get(self, context, server_id):
+ return {'virtual_interfaces': [
+ {'uuid': '00000000-0000-0000-0000-00000000000000000',
+ 'address': '00-00-00-00-00-00'},
+ {'uuid': '11111111-1111-1111-1111-11111111111111111',
+ 'address': '11-11-11-11-11-11'}]}
+
+
+class ServerVirtualInterfaceTest(test.TestCase):
+
+ def setUp(self):
+ super(ServerVirtualInterfaceTest, self).setUp()
+ self.controller = ServerVirtualInterfaceController()
+ self.stubs.Set(compute.api.API, "get", compute_api_get)
+
+ def tearDown(self):
+ super(ServerVirtualInterfaceTest, self).tearDown()
+
+ def test_get_virtual_interfaces_list(self):
+ req = webob.Request.blank('/v1.1/servers/1/os-virtual-interfaces')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ res_dict = json.loads(res.body)
+ response = {'virtual_interfaces': [
+ {'id': '00000000-0000-0000-0000-00000000000000000',
+ 'mac_address': '00-00-00-00-00-00'},
+ {'id': '11111111-1111-1111-1111-11111111111111111',
+ 'mac_address': '11-11-11-11-11-11'}]}
+ self.assertEqual(res_dict, response)
diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py
index 34a4b3f89..d89cb28d6 100644
--- a/nova/tests/api/openstack/test_extensions.py
+++ b/nova/tests/api/openstack/test_extensions.py
@@ -92,9 +92,10 @@ class ExtensionControllerTest(test.TestCase):
"Keypairs",
"Multinic",
"Quotas",
+ "Rescue",
"SecurityGroups",
+ "VirtualInterfaces",
"Volumes",
- "Rescue",
]
self.ext_list.sort()
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index 0ead680ee..e5c80b6f6 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -108,11 +108,14 @@ floating_ip_fields = {'id': 0,
vifs = [{'id': 0,
'address': 'DE:AD:BE:EF:00:00',
+ 'uuid': '00000000-0000-0000-0000-0000000000000000',
'network_id': 0,
'network': FakeModel(**networks[0]),
'instance_id': 0},
{'id': 1,
'address': 'DE:AD:BE:EF:00:01',
+ 'uuid': '00000000-0000-0000-0000-0000000000000001',
+ 'network_id': 0,
'network_id': 1,
'network': FakeModel(**networks[1]),
'instance_id': 0}]
@@ -163,6 +166,8 @@ class FlatNetworkTestCase(test.TestCase):
'ips': 'DONTCARE',
'label': 'test%s' % i,
'mac': 'DE:AD:BE:EF:00:0%s' % i,
+ 'vif_uuid': ('00000000-0000-0000-0000-000000000000000%s' %
+ i),
'rxtx_cap': 'DONTCARE',
'should_create_vlan': False,
'should_create_bridge': False}
diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py
index 5a91a4e28..0b7438011 100644
--- a/nova/virt/libvirt/vif.py
+++ b/nova/virt/libvirt/vif.py
@@ -100,10 +100,12 @@ class LibvirtBridgeDriver(VIFDriver):
class LibvirtOpenVswitchDriver(VIFDriver):
"""VIF driver for Open vSwitch."""
+ def get_dev_name(_self, iface_id):
+ return "tap-" + iface_id[0:15]
+
def plug(self, instance, network, mapping):
- vif_id = str(instance['id']) + "-" + str(network['id'])
- dev = "tap-%s" % vif_id
- iface_id = "nova-" + vif_id
+ iface_id = mapping['vif_uuid']
+ dev = self.get_dev_name(iface_id)
if not linux_net._device_exists(dev):
utils.execute('ip', 'tuntap', 'add', dev, 'mode', 'tap',
run_as_root=True)
@@ -127,11 +129,10 @@ class LibvirtOpenVswitchDriver(VIFDriver):
def unplug(self, instance, network, mapping):
"""Unplug the VIF from the network by deleting the port from
the bridge."""
- vif_id = str(instance['id']) + "-" + str(network['id'])
- dev = "tap-%s" % vif_id
+ dev = self.get_dev_name(mapping['vif_uuid'])
try:
utils.execute('ovs-vsctl', 'del-port',
- network['bridge'], dev, run_as_root=True)
+ FLAGS.libvirt_ovs_bridge, dev, run_as_root=True)
utils.execute('ip', 'link', 'delete', dev, run_as_root=True)
except exception.ProcessExecutionError:
LOG.warning(_("Failed while unplugging vif of instance '%s'"),
diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py
index 527602243..2f25efeb2 100644
--- a/nova/virt/xenapi/vif.py
+++ b/nova/virt/xenapi/vif.py
@@ -128,12 +128,12 @@ class XenAPIOpenVswitchDriver(VIFDriver):
vif_rec['VM'] = vm_ref
vif_rec['MAC'] = network_mapping['mac']
vif_rec['MTU'] = '1500'
- vif_id = "nova-" + str(instance['id']) + "-" + str(network['id'])
vif_rec['qos_algorithm_type'] = ""
vif_rec['qos_algorithm_params'] = {}
# OVS on the hypervisor monitors this key and uses it to
# set the iface-id attribute
- vif_rec['other_config'] = {"nicira-iface-id": vif_id}
+ vif_rec['other_config'] = \
+ {"nicira-iface-id": network_mapping['vif_uuid']}
return vif_rec
def unplug(self, instance, network, mapping):