summaryrefslogtreecommitdiffstats
path: root/nova/tests
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-02-20 06:16:33 +0000
committerGerrit Code Review <review@openstack.org>2013-02-20 06:16:33 +0000
commit6d0ed8f86fbaa7c52e66a1d335dcf063232de8dd (patch)
tree25d1bbf94abb1f9c35e2578dd128fec764f28847 /nova/tests
parent4e54c59b8888a2c5a8ab41c213d3bff2faba5570 (diff)
parenta9add7d35e27b90f0c420d2b24b1af88b978fd7b (diff)
Merge "Add support for network adapter hotplug."
Diffstat (limited to 'nova/tests')
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_attach_interfaces.py244
-rw-r--r--nova/tests/compute/test_compute.py36
-rw-r--r--nova/tests/fake_policy.py1
-rw-r--r--nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl8
-rw-r--r--nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl3
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-req.json.tpl5
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-req.xml.tpl4
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-resp.json.tpl12
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-resp.xml.tpl12
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-list-resp.json.tpl16
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-list-resp.xml.tpl15
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-show-resp.json.tpl14
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-show-resp.xml.tpl13
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/server-post-req.json.tpl16
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/server-post-req.xml.tpl19
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/server-post-resp.json.tpl16
-rw-r--r--nova/tests/integrated/api_samples/os-attach-interfaces/server-post-resp.xml.tpl6
-rw-r--r--nova/tests/integrated/test_api_samples.py173
-rw-r--r--nova/tests/network/test_quantumv2.py61
19 files changed, 668 insertions, 6 deletions
diff --git a/nova/tests/api/openstack/compute/contrib/test_attach_interfaces.py b/nova/tests/api/openstack/compute/contrib/test_attach_interfaces.py
new file mode 100644
index 000000000..462e4375c
--- /dev/null
+++ b/nova/tests/api/openstack/compute/contrib/test_attach_interfaces.py
@@ -0,0 +1,244 @@
+# Copyright 2012 SINA Inc.
+# 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.
+
+from nova.api.openstack.compute.contrib import attach_interfaces
+from nova.compute import api as compute_api
+from nova import context
+from nova import exception
+from nova.network import api as network_api
+from nova.openstack.common import cfg
+from nova.openstack.common import jsonutils
+from nova import test
+
+import webob
+from webob import exc
+
+
+CONF = cfg.CONF
+
+FAKE_UUID1 = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
+FAKE_UUID2 = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'
+
+FAKE_PORT_ID1 = '11111111-1111-1111-1111-111111111111'
+FAKE_PORT_ID2 = '22222222-2222-2222-2222-222222222222'
+FAKE_PORT_ID3 = '33333333-3333-3333-3333-333333333333'
+
+FAKE_NET_ID1 = '44444444-4444-4444-4444-444444444444'
+FAKE_NET_ID2 = '55555555-5555-5555-5555-555555555555'
+FAKE_NET_ID3 = '66666666-6666-6666-6666-666666666666'
+
+port_data1 = {
+ "id": FAKE_PORT_ID1,
+ "network_id": FAKE_NET_ID1,
+ "admin_state_up": True,
+ "status": "ACTIVE",
+ "mac_address": "aa:aa:aa:aa:aa:aa",
+ "fixed_ips": ["10.0.1.2"],
+ "device_id": FAKE_UUID1,
+}
+
+port_data2 = {
+ "id": FAKE_PORT_ID2,
+ "network_id": FAKE_NET_ID2,
+ "admin_state_up": True,
+ "status": "ACTIVE",
+ "mac_address": "bb:bb:bb:bb:bb:bb",
+ "fixed_ips": ["10.0.2.2"],
+ "device_id": FAKE_UUID1,
+}
+
+port_data3 = {
+ "id": FAKE_PORT_ID3,
+ "network_id": FAKE_NET_ID3,
+ "admin_state_up": True,
+ "status": "ACTIVE",
+ "mac_address": "bb:bb:bb:bb:bb:bb",
+ "fixed_ips": ["10.0.2.2"],
+ "device_id": '',
+}
+
+fake_networks = [FAKE_NET_ID1, FAKE_NET_ID2]
+ports = [port_data1, port_data2, port_data3]
+
+
+def fake_list_ports(self, *args, **kwargs):
+ result = []
+ for port in ports:
+ if port['device_id'] == kwargs['device_id']:
+ result.append(port)
+ return {'ports': result}
+
+
+def fake_show_port(self, context, port_id, **kwargs):
+ for port in ports:
+ if port['id'] == port_id:
+ return {'port': port}
+
+
+def fake_attach_interface(self, context, instance, network_id, port_id,
+ requested_ip='192.168.1.3'):
+ if not network_id:
+ # if no network_id is given when add a port to an instance, use the
+ # first default network.
+ network_id = fake_networks[0]
+ if not port_id:
+ port_id = ports[fake_networks.index(network_id)]['id']
+ network_info = [
+ {'bridge': 'br-100',
+ 'id': network_id,
+ 'cidr': '192.168.1.0/24',
+ 'vlan': '101',
+ 'injected': 'False',
+ 'multi_host': 'False',
+ 'bridge_interface': 'bridge_interface'
+ },
+ {'label': 'fake_network',
+ 'broadcast': '192.168.1.255',
+ 'mac': '11:22:33:11:22:33',
+ 'vif_uuid': port_id,
+ 'rxtx_cap': 0,
+ 'dns': '8.8.8.8',
+ 'dhcp_server': '192.168.1.1',
+ 'ips': {'ip': requested_ip,
+ 'enabled': 1,
+ 'netmask': '255.255.255.0',
+ 'gateway': '192.168.1.254'}
+ }
+ ]
+ return network_info
+
+
+def fake_detach_interface(self, context, instance, port_id):
+ for port in ports:
+ if port['id'] == port_id:
+ return
+ raise exception.PortNotFound(port_id=port_id)
+
+
+def fake_get_instance(self, context, intance_id):
+ return {}
+
+
+class InterfaceAttachTests(test.TestCase):
+ def setUp(self):
+ super(InterfaceAttachTests, self).setUp()
+ self.flags(quantum_auth_strategy=None)
+ self.flags(quantum_url='http://anyhost/')
+ self.flags(quantum_url_timeout=30)
+ self.stubs.Set(network_api.API, 'show_port', fake_show_port)
+ self.stubs.Set(network_api.API, 'list_ports', fake_list_ports)
+ self.stubs.Set(compute_api.API, 'get', fake_get_instance)
+ self.context = context.get_admin_context()
+ self.expected_show = {'interfaceAttachment':
+ {'net_id': FAKE_NET_ID1,
+ 'port_id': FAKE_PORT_ID1,
+ 'mac_addr': port_data1['mac_address'],
+ 'port_state': port_data1['status'],
+ 'fixed_ips': port_data1['fixed_ips'],
+ }}
+
+ def test_show(self):
+ attachments = attach_interfaces.InterfaceAttachmentController()
+ req = webob.Request.blank('/v2/fake/os-interfaces/show')
+ req.method = 'POST'
+ req.body = jsonutils.dumps({})
+ req.headers['content-type'] = 'application/json'
+ req.environ['nova.context'] = self.context
+
+ result = attachments.show(req, FAKE_UUID1, FAKE_PORT_ID1)
+ self.assertEqual(self.expected_show, result)
+
+ def test_show_invalid(self):
+ attachments = attach_interfaces.InterfaceAttachmentController()
+ req = webob.Request.blank('/v2/fake/os-interfaces/show')
+ req.method = 'POST'
+ req.body = jsonutils.dumps({})
+ req.headers['content-type'] = 'application/json'
+ req.environ['nova.context'] = self.context
+
+ self.assertRaises(exc.HTTPNotFound,
+ attachments.show, req, FAKE_UUID2, FAKE_PORT_ID1)
+
+ def test_delete(self):
+ self.stubs.Set(compute_api.API, 'detach_interface',
+ fake_detach_interface)
+ attachments = attach_interfaces.InterfaceAttachmentController()
+ req = webob.Request.blank('/v2/fake/os-interfaces/delete')
+ req.method = 'POST'
+ req.body = jsonutils.dumps({})
+ req.headers['content-type'] = 'application/json'
+ req.environ['nova.context'] = self.context
+
+ result = attachments.delete(req, FAKE_UUID1, FAKE_PORT_ID1)
+ self.assertEqual('202 Accepted', result.status)
+
+ def test_delete_interface_not_found(self):
+ self.stubs.Set(compute_api.API, 'detach_interface',
+ fake_detach_interface)
+ attachments = attach_interfaces.InterfaceAttachmentController()
+ req = webob.Request.blank('/v2/fake/os-interfaces/delete')
+ req.method = 'POST'
+ req.body = jsonutils.dumps({})
+ req.headers['content-type'] = 'application/json'
+ req.environ['nova.context'] = self.context
+
+ self.assertRaises(exc.HTTPNotFound,
+ attachments.delete,
+ req,
+ FAKE_UUID1,
+ 'invaid-port-id')
+
+ def test_attach_interface_without_network_id(self):
+ self.stubs.Set(compute_api.API, 'attach_interface',
+ fake_attach_interface)
+ attachments = attach_interfaces.InterfaceAttachmentController()
+ req = webob.Request.blank('/v2/fake/os-interfaces/attach')
+ req.method = 'POST'
+ body = jsonutils.dumps({'port_id': FAKE_PORT_ID1})
+ req.body = jsonutils.dumps({})
+ req.headers['content-type'] = 'application/json'
+ req.environ['nova.context'] = self.context
+ result = attachments.create(req, FAKE_UUID1, jsonutils.loads(req.body))
+ self.assertEqual(result['interfaceAttachment']['net_id'],
+ FAKE_NET_ID1)
+
+ def test_attach_interface_with_network_id(self):
+ self.stubs.Set(compute_api.API, 'attach_interface',
+ fake_attach_interface)
+ attachments = attach_interfaces.InterfaceAttachmentController()
+ req = webob.Request.blank('/v2/fake/os-interfaces/attach')
+ req.method = 'POST'
+ req.body = jsonutils.dumps({'interfaceAttachment':
+ {'net_id': FAKE_NET_ID2}})
+ req.headers['content-type'] = 'application/json'
+ req.environ['nova.context'] = self.context
+ result = attachments.create(req, FAKE_UUID1, jsonutils.loads(req.body))
+ self.assertEqual(result['interfaceAttachment']['net_id'],
+ FAKE_NET_ID2)
+
+ def test_attach_interface_with_port_and_network_id(self):
+ self.stubs.Set(compute_api.API, 'attach_interface',
+ fake_attach_interface)
+ attachments = attach_interfaces.InterfaceAttachmentController()
+ req = webob.Request.blank('/v2/fake/os-interfaces/attach')
+ req.method = 'POST'
+ req.body = jsonutils.dumps({'interfaceAttachment':
+ {'port_id': FAKE_PORT_ID1,
+ 'net_id': FAKE_NET_ID2}})
+ req.headers['content-type'] = 'application/json'
+ req.environ['nova.context'] = self.context
+ self.assertRaises(exc.HTTPBadRequest,
+ attachments.create, req, FAKE_UUID1,
+ jsonutils.loads(req.body))
diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py
index c372eef02..5a84c5f6e 100644
--- a/nova/tests/compute/test_compute.py
+++ b/nova/tests/compute/test_compute.py
@@ -62,6 +62,7 @@ from nova.tests.compute import fake_resource_tracker
from nova.tests.db import fakes as db_fakes
from nova.tests import fake_instance_actions
from nova.tests import fake_network
+from nova.tests import fake_network_cache_model
from nova.tests.image import fake as fake_image
from nova.tests import matchers
from nova import utils
@@ -5856,6 +5857,41 @@ class ComputeAPITestCase(BaseTestCase):
db.instance_destroy(self.context, instance['uuid'])
+ def test_attach_interface(self):
+ instance = {
+ 'image_ref': 'foo',
+ }
+ self.mox.StubOutWithMock(compute_manager, '_get_image_meta')
+ self.mox.StubOutWithMock(self.compute.network_api,
+ 'allocate_port_for_instance')
+ nwinfo = network_model.NetworkInfo()
+ nwinfo.append(fake_network_cache_model.new_vif())
+ network_id = nwinfo[0]['network']['id']
+ port_id = nwinfo[0]['id']
+ req_ip = '1.2.3.4'
+ self.compute.network_api.allocate_port_for_instance(
+ self.context, instance, port_id, network_id, req_ip,
+ self.compute.conductor_api).AndReturn(nwinfo)
+ compute_manager._get_image_meta(self.context, instance['image_ref'])
+ self.mox.ReplayAll()
+ network, mapping = self.compute.attach_interface(self.context,
+ instance,
+ network_id,
+ port_id,
+ req_ip)
+ self.assertEqual(network['id'], network_id)
+ return nwinfo, port_id
+
+ def test_detach_interface(self):
+ nwinfo, port_id = self.test_attach_interface()
+ self.stubs.Set(self.compute.network_api, 'get_instance_nw_info',
+ lambda *a, **k: nwinfo)
+ self.stubs.Set(self.compute.network_api,
+ 'deallocate_port_for_instance',
+ lambda a, b, c, d: [])
+ self.compute.detach_interface(self.context, {}, port_id)
+ self.assertEqual(self.compute.driver._interfaces, {})
+
def test_attach_volume(self):
# Ensure instance can be soft rebooted.
diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py
index 382667904..80b3a9c85 100644
--- a/nova/tests/fake_policy.py
+++ b/nova/tests/fake_policy.py
@@ -105,6 +105,7 @@ policy_data = """
"compute_extension:admin_actions:migrate": "",
"compute_extension:aggregates": "",
"compute_extension:agents": "",
+ "compute_extension:attach_interfaces": "",
"compute_extension:baremetal_nodes": "",
"compute_extension:cells": "",
"compute_extension:certificates": "",
diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl
index 6aeedbe56..831c93436 100644
--- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl
+++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl
@@ -97,6 +97,14 @@
"updated": "%(timestamp)s"
},
{
+ "alias": "os-attach-interfaces",
+ "description": "Attach interface support.",
+ "links": [],
+ "name": "AttachInterfaces",
+ "namespace": "http://docs.openstack.org/compute/ext/interfaces/api/v1.1",
+ "updated": "2012-07-22T00:00:00+00:00"
+ },
+ {
"alias": "os-availability-zone",
"description": "%(text)s",
"links": [],
diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl
index 421cc2233..c76b55bbb 100644
--- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl
+++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl
@@ -33,6 +33,9 @@
<extension alias="os-aggregates" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/aggregates/api/v1.1" name="Aggregates">
<description>%(text)s</description>
</extension>
+ <extension alias="os-attach-interfaces" updated="2012-07-22T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/interfaces/api/v1.1" name="AttachInterfaces">
+ <description>Attach interface support.</description>
+ </extension>
<extension alias="os-availability-zone" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/availabilityzone/api/v1.1" name="AvailabilityZone">
<description>%(text)s</description>
</extension>
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-req.json.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-req.json.tpl
new file mode 100644
index 000000000..11dcf6437
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-req.json.tpl
@@ -0,0 +1,5 @@
+{
+ "interfaceAttachment": {
+ "port_id": "ce531f90-199f-48c0-816c-13e38010b442"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-req.xml.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-req.xml.tpl
new file mode 100644
index 000000000..75e9b97c8
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-req.xml.tpl
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceAttachment>
+ <port_id>%(port_id)s</port_id>
+</interfaceAttachment>
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-resp.json.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-resp.json.tpl
new file mode 100644
index 000000000..d882cdc61
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-resp.json.tpl
@@ -0,0 +1,12 @@
+{
+ "interfaceAttachment": {
+ "fixed_ips": [{
+ "subnet_id": "%(subnet_id)s",
+ "ip_address": "%(ip_address)s"
+ }],
+ "mac_addr": "fa:16:3e:4c:2c:30",
+ "net_id": "%(net_id)s",
+ "port_id": "%(port_id)s",
+ "port_state": "%(port_state)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-resp.xml.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-resp.xml.tpl
new file mode 100644
index 000000000..b391e5973
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-create-resp.xml.tpl
@@ -0,0 +1,12 @@
+<interfaceAttachment>
+ <net_id>%(net_id)s</net_id>
+ <port_id>%(port_id)s</port_id>
+ <fixed_ips>
+ <fixed_ip>
+ <subnet_id>%(subnet_id)s</subnet_id>
+ <ip_address>%(ip_address)s</ip_address>
+ </fixed_ip>
+ </fixed_ips>
+ <port_state>%(port_state)s</port_state>
+ <mac_addr>%(mac_addr)s</mac_addr>
+</interfaceAttachment>
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-list-resp.json.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-list-resp.json.tpl
new file mode 100644
index 000000000..47dcf2dc6
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-list-resp.json.tpl
@@ -0,0 +1,16 @@
+{
+ "interfaceAttachments": [
+ {
+ "port_state": "%(port_state)s",
+ "fixed_ips": [
+ {
+ "subnet_id": "%(subnet_id)s",
+ "ip_address": "%(ip_address)s"
+ }
+ ],
+ "net_id": "%(net_id)s",
+ "port_id": "%(port_id)s",
+ "mac_addr": "%(mac_addr)s"
+ }
+ ]
+}
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-list-resp.xml.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-list-resp.xml.tpl
new file mode 100644
index 000000000..f3262e948
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-list-resp.xml.tpl
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceAttachments>
+ <interfaceAttachment>
+ <port_state>%(port_state)s</port_state>
+ <fixed_ips>
+ <fixed_ip>
+ <subnet_id>%(subnet_id)s</subnet_id>
+ <ip_address>%(ip_address)s</ip_address>
+ </fixed_ip>
+ </fixed_ips>
+ <port_id>%(port_id)s</port_id>
+ <net_id>%(net_id)s</net_id>
+ <mac_addr>%(mac_addr)s</mac_addr>
+ </interfaceAttachment>
+</interfaceAttachments>
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-show-resp.json.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-show-resp.json.tpl
new file mode 100644
index 000000000..3333bb499
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-show-resp.json.tpl
@@ -0,0 +1,14 @@
+{
+ "interfaceAttachment": {
+ "port_state": "%(port_state)s",
+ "fixed_ips": [
+ {
+ "subnet_id": "%(subnet_id)s",
+ "ip_address": "%(ip_address)s"
+ }
+ ],
+ "net_id": "%(net_id)s",
+ "port_id": "%(port_id)s",
+ "mac_addr": "%(mac_addr)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-show-resp.xml.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-show-resp.xml.tpl
new file mode 100644
index 000000000..a3393448d
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/attach-interfaces-show-resp.xml.tpl
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceAttachment>
+ <port_state>%(port_state)s</port_state>
+ <fixed_ips>
+ <fixed_ip>
+ <subnet_id>%(subnet_id)s</subnet_id>
+ <ip_address>%(ip_address)s</ip_address>
+ </fixed_ip>
+ </fixed_ips>
+ <port_id>%(port_id)s</port_id>
+ <net_id>%(net_id)s</net_id>
+ <mac_addr>%(mac_addr)s</mac_addr>
+</interfaceAttachment>
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-req.json.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-req.json.tpl
new file mode 100644
index 000000000..d3916d1aa
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-req.json.tpl
@@ -0,0 +1,16 @@
+{
+ "server" : {
+ "name" : "new-server-test",
+ "imageRef" : "%(host)s/openstack/images/%(image_id)s",
+ "flavorRef" : "%(host)s/openstack/flavors/1",
+ "metadata" : {
+ "My Server Name" : "Apache1"
+ },
+ "personality" : [
+ {
+ "path" : "/etc/banner.txt",
+ "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
+ }
+ ]
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-req.xml.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-req.xml.tpl
new file mode 100644
index 000000000..f92614984
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-req.xml.tpl
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<server xmlns="http://docs.openstack.org/compute/api/v1.1" imageRef="%(host)s/openstack/images/%(image_id)s" flavorRef="%(host)s/openstack/flavors/1" name="new-server-test">
+ <metadata>
+ <meta key="My Server Name">Apache1</meta>
+ </metadata>
+ <personality>
+ <file path="/etc/banner.txt">
+ ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp
+ dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k
+ IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs
+ c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g
+ QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo
+ ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv
+ dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy
+ c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6
+ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
+ </file>
+ </personality>
+</server>
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-resp.json.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-resp.json.tpl
new file mode 100644
index 000000000..d5f030c87
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-resp.json.tpl
@@ -0,0 +1,16 @@
+{
+ "server": {
+ "adminPass": "%(password)s",
+ "id": "%(id)s",
+ "links": [
+ {
+ "href": "%(host)s/v2/openstack/servers/%(uuid)s",
+ "rel": "self"
+ },
+ {
+ "href": "%(host)s/openstack/servers/%(uuid)s",
+ "rel": "bookmark"
+ }
+ ]
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-resp.xml.tpl b/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-resp.xml.tpl
new file mode 100644
index 000000000..3bb13e69b
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-attach-interfaces/server-post-resp.xml.tpl
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<server xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1" id="%(id)s" adminPass="%(password)s">
+ <metadata/>
+ <atom:link href="%(host)s/v2/openstack/servers/%(uuid)s" rel="self"/>
+ <atom:link href="%(host)s/openstack/servers/%(uuid)s" rel="bookmark"/>
+</server>
diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py
index a61ac3b8d..1b7f10401 100644
--- a/nova/tests/integrated/test_api_samples.py
+++ b/nova/tests/integrated/test_api_samples.py
@@ -31,6 +31,7 @@ from nova.api.openstack.compute.contrib import coverage_ext
from nova.api.openstack.compute.contrib import fping
# Import extensions to pull in osapi_compute_extension CONF option used below.
from nova.cloudpipe import pipelib
+from nova.compute import api as compute_api
from nova import context
from nova import db
from nova.db.sqlalchemy import models
@@ -266,7 +267,6 @@ class ApiSampleTestBase(integrated_helpers._IntegratedTestBase):
sample_data = "{}"
else:
sample_data = None
-
try:
response_result = self._verify_something(subs, expected,
response_data)
@@ -3334,3 +3334,174 @@ class FlavorAccessSampleJsonTests(ApiSampleTestBase):
class FlavorAccessSampleXmlTests(FlavorAccessSampleJsonTests):
ctype = "xml"
+
+
+class AttachInterfacesSampleJsonTest(ServersSampleBase):
+ extension_name = ('nova.api.openstack.compute.contrib.attach_interfaces.'
+ 'Attach_interfaces')
+
+ def setUp(self):
+ super(AttachInterfacesSampleJsonTest, self).setUp()
+
+ def fake_list_ports(self, *args, **kwargs):
+ uuid = kwargs.get('device_id', None)
+ if not uuid:
+ raise InstanceNotFound(instance_id=None)
+ port_data = {
+ "id": "ce531f90-199f-48c0-816c-13e38010b442",
+ "network_id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6",
+ "admin_state_up": True,
+ "status": "ACTIVE",
+ "mac_address": "fa:16:3e:4c:2c:30",
+ "fixed_ips": [
+ {
+ "ip_address": "192.168.1.3",
+ "subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef"
+ }
+ ],
+ "device_id": uuid,
+ }
+ ports = {'ports': [port_data]}
+ return ports
+
+ def fake_show_port(self, context, port_id=None):
+ if not port_id:
+ raise PortNotFound(port_id=None)
+ port_data = {
+ "id": port_id,
+ "network_id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6",
+ "admin_state_up": True,
+ "status": "ACTIVE",
+ "mac_address": "fa:16:3e:4c:2c:30",
+ "fixed_ips": [
+ {
+ "ip_address": "192.168.1.3",
+ "subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef"
+ }
+ ],
+ "device_id": 'bece68a3-2f8b-4e66-9092-244493d6aba7',
+ }
+ port = {'port': port_data}
+ return port
+
+ def fake_attach_interface(self, context, instance,
+ network_id, port_id,
+ requested_ip='192.168.1.3'):
+ if not network_id:
+ network_id = "fake_net_uuid"
+ if not port_id:
+ port_id = "fake_port_uuid"
+ network_info = [
+ {
+ 'bridge': 'br-100',
+ 'id': network_id,
+ 'cidr': '192.168.1.0/24',
+ 'vlan': '101',
+ 'injected': 'False',
+ 'multi_host': 'False',
+ 'bridge_interface': 'bridge_interface'
+ },
+ {
+ "vif_uuid": port_id,
+ "network_id": network_id,
+ "admin_state_up": True,
+ "status": "ACTIVE",
+ "mac_address": "fa:16:3e:4c:2c:30",
+ "fixed_ips": [
+ {
+ "ip_address": requested_ip,
+ "subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef"
+ }
+ ],
+ "device_id": instance['uuid'],
+ }
+ ]
+ return network_info
+
+ def fake_detach_interface(self, context, instance, port_id):
+ pass
+
+ self.stubs.Set(network_api.API, 'list_ports', fake_list_ports)
+ self.stubs.Set(network_api.API, 'show_port', fake_show_port)
+ self.stubs.Set(compute_api.API, 'attach_interface',
+ fake_attach_interface)
+ self.stubs.Set(compute_api.API, 'detach_interface',
+ fake_detach_interface)
+ self.flags(quantum_auth_strategy=None)
+ self.flags(quantum_url='http://anyhost/')
+ self.flags(quantum_url_timeout=30)
+
+ def generalize_subs(self, subs, vanilla_regexes):
+ subs['subnet_id'] = vanilla_regexes['uuid']
+ subs['net_id'] = vanilla_regexes['uuid']
+ subs['port_id'] = vanilla_regexes['uuid']
+ subs['mac_addr'] = '(?:[a-f0-9]{2}:){5}[a-f0-9]{2}'
+ subs['ip_address'] = vanilla_regexes['ip']
+ return subs
+
+ def test_list_interfaces(self):
+ instance_uuid = self._post_server()
+ response = self._do_get('servers/%s/os-interface' % instance_uuid)
+ self.assertEqual(response.status, 200)
+ subs = {
+ 'ip_address': '192.168.1.3',
+ 'subnet_id': 'f8a6e8f8-c2ec-497c-9f23-da9616de54ef',
+ 'mac_addr': 'fa:16:3e:4c:2c:30',
+ 'net_id': '3cb9bc59-5699-4588-a4b1-b87f96708bc6',
+ 'port_id': 'ce531f90-199f-48c0-816c-13e38010b442',
+ 'port_state': 'ACTIVE'
+ }
+ self._verify_response('attach-interfaces-list-resp', subs, response)
+
+ def _stub_show_for_instance(self, instance_uuid, port_id):
+ show_port = network_api.API().show_port(None, port_id)
+ show_port['port']['device_id'] = instance_uuid
+ self.stubs.Set(network_api.API, 'show_port', lambda *a, **k: show_port)
+
+ def test_show_interfaces(self):
+ instance_uuid = self._post_server()
+ port_id = 'ce531f90-199f-48c0-816c-13e38010b442'
+ self._stub_show_for_instance(instance_uuid, port_id)
+ response = self._do_get('servers/%s/os-interface/%s' %
+ (instance_uuid, port_id))
+ self.assertEqual(response.status, 200)
+ subs = {
+ 'ip_address': '192.168.1.3',
+ 'subnet_id': 'f8a6e8f8-c2ec-497c-9f23-da9616de54ef',
+ 'mac_addr': 'fa:16:3e:4c:2c:30',
+ 'net_id': '3cb9bc59-5699-4588-a4b1-b87f96708bc6',
+ 'port_id': port_id,
+ 'port_state': 'ACTIVE'
+ }
+ self._verify_response('attach-interfaces-show-resp', subs, response)
+
+ def test_create_interfaces(self, instance_uuid=None):
+ if instance_uuid is None:
+ instance_uuid = self._post_server()
+ subs = {
+ 'net_id': '3cb9bc59-5699-4588-a4b1-b87f96708bc6',
+ 'port_id': 'ce531f90-199f-48c0-816c-13e38010b442',
+ 'subnet_id': 'f8a6e8f8-c2ec-497c-9f23-da9616de54ef',
+ 'ip_address': '192.168.1.3',
+ 'port_state': 'ACTIVE',
+ 'mac_addr': 'fa:16:3e:4c:2c:30',
+ }
+ self._stub_show_for_instance(instance_uuid, subs['port_id'])
+ response = self._do_post('servers/%s/os-interface' % instance_uuid,
+ 'attach-interfaces-create-req', subs)
+ self.assertEqual(response.status, 200)
+ subs.update(self._get_regexes())
+ self._verify_response('attach-interfaces-create-resp',
+ subs, response)
+
+ def test_delete_interfaces(self):
+ instance_uuid = self._post_server()
+ port_id = 'ce531f90-199f-48c0-816c-13e38010b442'
+ response = self._do_delete('servers/%s/os-interface/%s' %
+ (instance_uuid, port_id))
+ self.assertEqual(response.status, 202)
+ self.assertEqual(response.read(), '')
+
+
+class AttachInterfacesSampleXmlTest(AttachInterfacesSampleJsonTest):
+ ctype = 'xml'
diff --git a/nova/tests/network/test_quantumv2.py b/nova/tests/network/test_quantumv2.py
index 91d609914..4ad56f362 100644
--- a/nova/tests/network/test_quantumv2.py
+++ b/nova/tests/network/test_quantumv2.py
@@ -230,11 +230,10 @@ class TestQuantumv2(test.TestCase):
'router_id': 'router_id1'}
def tearDown(self):
- try:
- self.mox.UnsetStubs()
- self.mox.VerifyAll()
- finally:
- CONF.reset()
+ self.addCleanup(CONF.reset)
+ self.addCleanup(self.mox.VerifyAll)
+ self.addCleanup(self.mox.UnsetStubs)
+ self.addCleanup(self.stubs.UnsetAll)
super(TestQuantumv2, self).tearDown()
def _verify_nw_info(self, nw_inf, index=0):
@@ -640,7 +639,9 @@ class TestQuantumv2(test.TestCase):
{'ports': port_data})
for port in port_data:
self.moxed_client.delete_port(port['id'])
+
self.mox.ReplayAll()
+
api = quantumapi.API()
api.deallocate_for_instance(self.context, self.instance)
@@ -652,6 +653,56 @@ class TestQuantumv2(test.TestCase):
# Test to deallocate in two ports env.
self._deallocate_for_instance(2)
+ def _test_deallocate_port_for_instance(self, number):
+ port_data = number == 1 and self.port_data1 or self.port_data2
+ self.moxed_client.delete_port(port_data[0]['id'])
+
+ nets = [port_data[0]['network_id']]
+ quantumv2.get_client(mox.IgnoreArg(), admin=True).AndReturn(
+ self.moxed_client)
+ self.moxed_client.list_ports(
+ tenant_id=self.instance['project_id'],
+ device_id=self.instance['uuid']).AndReturn(
+ {'ports': port_data[1:]})
+ quantumv2.get_client(mox.IgnoreArg()).MultipleTimes().AndReturn(
+ self.moxed_client)
+ self.moxed_client.list_networks(
+ tenant_id=self.instance['project_id'],
+ shared=False).AndReturn(
+ {'networks': [self.nets2[1]]})
+ self.moxed_client.list_networks(shared=True).AndReturn(
+ {'networks': []})
+ for port in port_data[1:]:
+ self.moxed_client.list_subnets(id=['my_subid2']).AndReturn({})
+
+ self.mox.ReplayAll()
+
+ api = quantumapi.API()
+ nwinfo = api.deallocate_port_for_instance(self.context, self.instance,
+ port_data[0]['id'])
+ self.assertEqual(len(nwinfo), len(port_data[1:]))
+ if len(port_data) > 1:
+ self.assertEqual(nwinfo[0]['network']['id'], 'my_netid2')
+
+ def test_deallocate_port_for_instance_1(self):
+ # Test to deallocate the first and only port
+ self._test_deallocate_port_for_instance(1)
+
+ def test_deallocate_port_for_instance_2(self):
+ # Test to deallocate the first port of two
+ self._test_deallocate_port_for_instance(2)
+
+ def test_list_ports(self):
+ search_opts = {'parm': 'value'}
+ self.moxed_client.list_ports(**search_opts)
+ self.mox.ReplayAll()
+ quantumapi.API().list_ports(self.context, **search_opts)
+
+ def test_show_port(self):
+ self.moxed_client.show_port('foo')
+ self.mox.ReplayAll()
+ quantumapi.API().show_port(self.context, 'foo')
+
def test_validate_networks(self):
requested_networks = [('my_netid1', 'test', None),
('my_netid2', 'test2', None)]