summaryrefslogtreecommitdiff
path: root/ironic_tempest_plugin
diff options
context:
space:
mode:
authorSam Betts <sam@code-smash.net>2016-11-30 18:43:35 +0000
committerVladyslav Drok <vdrok@mirantis.com>2017-01-11 15:08:26 +0200
commit4789d3b41ad045f201351ea40097dfd895bfc8a9 (patch)
tree98398d8f36ea671a472af37faa9d622655d6e872 /ironic_tempest_plugin
parentd18ebbc3e20b38eaecf681e698690101b0bd516d (diff)
downloadironic-4789d3b41ad045f201351ea40097dfd895bfc8a9.tar.gz
Add Virtual Network Interface REST APIs
This patch adds the REST APIs for the virtual network interface API in order to abstract the task of assigning logical network interfaces to physical network interfaces. Since Newton Ironic provides an interface for pluggable network implementations. Different network implementations may want to handle how logical to physical network interface assignment happens. To do this the new API calls into new functions on the network implementation loaded for the specified node. This is part 3 of 3, and adds the node vif subcontroller to expose the /nodes/<ident>/vifs REST API endpoint. API version is bumped to 1.28. Co-Authored-By: Vasyl Saienko (vsaienko@mirantis.com) Change-Id: I70f1166a15a26f392734e21d6bc30a03da4e5486 Partial-Bug: #1582188
Diffstat (limited to 'ironic_tempest_plugin')
-rw-r--r--ironic_tempest_plugin/services/baremetal/base.py27
-rw-r--r--ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py40
-rw-r--r--ironic_tempest_plugin/tests/api/admin/test_nodes.py31
-rw-r--r--ironic_tempest_plugin/tests/scenario/baremetal_manager.py5
-rw-r--r--ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py10
5 files changed, 108 insertions, 5 deletions
diff --git a/ironic_tempest_plugin/services/baremetal/base.py b/ironic_tempest_plugin/services/baremetal/base.py
index 67e4d08cd..30589e13c 100644
--- a/ironic_tempest_plugin/services/baremetal/base.py
+++ b/ironic_tempest_plugin/services/baremetal/base.py
@@ -115,10 +115,13 @@ class BaremetalClient(rest_client.RestClient):
return patch
- def _list_request(self, resource, permanent=False, **kwargs):
+ def _list_request(self, resource, permanent=False, headers=None,
+ extra_headers=False, **kwargs):
"""Get the list of objects of the specified type.
:param resource: The name of the REST resource, e.g., 'nodes'.
+ :param headers: List of headers to use in request.
+ :param extra_headers: Specify whether to use headers.
:param **kwargs: Parameters for the request.
:returns: A tuple with the server response and deserialized JSON list
of objects
@@ -128,7 +131,8 @@ class BaremetalClient(rest_client.RestClient):
if kwargs:
uri += "?%s" % urllib.urlencode(kwargs)
- resp, body = self.get(uri)
+ resp, body = self.get(uri, headers=headers,
+ extra_headers=extra_headers)
self.expected_success(200, resp.status)
return resp, self.deserialize(body)
@@ -167,6 +171,25 @@ class BaremetalClient(rest_client.RestClient):
return resp, self.deserialize(body)
+ def _create_request_no_response_body(self, resource, object_dict):
+ """Create an object of the specified type.
+
+ Do not expect any body in the response.
+
+ :param resource: The name of the REST resource, e.g., 'nodes'.
+ :param object_dict: A Python dict that represents an object of the
+ specified type.
+ :returns: The server response.
+ """
+
+ body = self.serialize(object_dict)
+ uri = self._get_uri(resource)
+
+ resp, body = self.post(uri, body=body)
+ self.expected_success(204, resp.status)
+
+ return resp
+
def _delete_request(self, resource, uuid):
"""Delete specified object.
diff --git a/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py b/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
index 49049d273..cf1abad9c 100644
--- a/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
+++ b/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
@@ -372,3 +372,43 @@ class BaremetalClient(base.BaremetalClient):
enabled)
self.expected_success(202, resp.status)
return resp, body
+
+ @base.handle_errors
+ def vif_list(self, node_uuid, api_version=None):
+ """Get list of attached VIFs.
+
+ :param node_uuid: Unique identifier of the node in UUID format.
+ :param api_version: Ironic API version to use.
+ """
+ extra_headers = False
+ headers = None
+ if api_version is not None:
+ extra_headers = True
+ headers = {'x-openstack-ironic-api-version': api_version}
+ return self._list_request('nodes/%s/vifs' % node_uuid,
+ headers=headers,
+ extra_headers=extra_headers)
+
+ @base.handle_errors
+ def vif_attach(self, node_uuid, vif_id):
+ """Attach a VIF to a node
+
+ :param node_uuid: Unique identifier of the node in UUID format.
+ :param vif_id: An ID representing the VIF
+ """
+ vif = {'id': vif_id}
+ resp = self._create_request_no_response_body(
+ 'nodes/%s/vifs' % node_uuid, vif)
+
+ return resp
+
+ @base.handle_errors
+ def vif_detach(self, node_uuid, vif_id):
+ """Detach a VIF from a node
+
+ :param node_uuid: Unique identifier of the node in UUID format.
+ :param vif_id: An ID representing the VIF
+ """
+ resp, body = self._delete_request('nodes/%s/vifs' % node_uuid, vif_id)
+ self.expected_success(204, resp.status)
+ return resp, body
diff --git a/ironic_tempest_plugin/tests/api/admin/test_nodes.py b/ironic_tempest_plugin/tests/api/admin/test_nodes.py
index fe95c0257..e7cfa8b74 100644
--- a/ironic_tempest_plugin/tests/api/admin/test_nodes.py
+++ b/ironic_tempest_plugin/tests/api/admin/test_nodes.py
@@ -16,6 +16,7 @@ from tempest.lib import exceptions as lib_exc
from tempest import test
from ironic_tempest_plugin.common import waiters
+from ironic_tempest_plugin.tests.api.admin import api_microversion_fixture
from ironic_tempest_plugin.tests.api.admin import base
@@ -166,3 +167,33 @@ class TestNodes(base.BaseBaremetalTest):
_, body = self.client.show_node_by_instance_uuid(instance_uuid)
self.assertEqual(1, len(body['nodes']))
self.assertIn(self.node['uuid'], [n['uuid'] for n in body['nodes']])
+
+ @test.idempotent_id('a3d319d0-cacb-4e55-a3dc-3fa8b74880f1')
+ def test_vifs(self):
+ self.useFixture(
+ api_microversion_fixture.APIMicroversionFixture('1.28'))
+ _, self.port = self.create_port(self.node['uuid'],
+ data_utils.rand_mac_address())
+ self.client.vif_attach(self.node['uuid'], 'test-vif')
+ _, body = self.client.vif_list(self.node['uuid'])
+ self.assertEqual(body, {'vifs': [{'id': 'test-vif'}]})
+ self.client.vif_detach(self.node['uuid'], 'test-vif')
+
+ @test.idempotent_id('a3d319d0-cacb-4e55-a3dc-3fa8b74880f2')
+ def test_vif_already_set_on_extra(self):
+ self.useFixture(
+ api_microversion_fixture.APIMicroversionFixture('1.28'))
+ _, self.port = self.create_port(self.node['uuid'],
+ data_utils.rand_mac_address())
+ patch = [{'path': '/extra/vif_port_id',
+ 'op': 'add',
+ 'value': 'test-vif'}]
+ self.client.update_port(self.port['uuid'], patch)
+
+ _, body = self.client.vif_list(self.node['uuid'])
+ self.assertEqual(body, {'vifs': [{'id': 'test-vif'}]})
+
+ self.assertRaises(lib_exc.Conflict, self.client.vif_attach,
+ self.node['uuid'], 'test-vif')
+
+ self.client.vif_detach(self.node['uuid'], 'test-vif')
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
index 286750ded..5c805a957 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
@@ -134,6 +134,11 @@ class BaremetalScenarioTest(manager.ScenarioTest):
ports.append(p)
return ports
+ def get_node_vifs(self, node_uuid, api_version='1.28'):
+ _, body = self.baremetal_client.vif_list(node_uuid,
+ api_version=api_version)
+ return body['vifs']
+
def add_keypair(self):
self.keypair = self.create_keypair()
diff --git a/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py b/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py
index 6d07399d9..1a918790b 100644
--- a/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py
+++ b/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py
@@ -97,12 +97,16 @@ class BaremetalBasicOps(baremetal_manager.BaremetalScenarioTest):
return int(ephemeral)
def validate_ports(self):
- for port in self.get_ports(self.node['uuid']):
- n_port_id = port['extra']['vif_port_id']
+ node_uuid = self.node['uuid']
+ vifs = self.get_node_vifs(node_uuid)
+ ir_ports = self.get_ports(node_uuid)
+ ir_ports_addresses = [x['address'] for x in ir_ports]
+ for vif in vifs:
+ n_port_id = vif['id']
body = self.ports_client.show_port(n_port_id)
n_port = body['port']
self.assertEqual(n_port['device_id'], self.instance['id'])
- self.assertEqual(n_port['mac_address'], port['address'])
+ self.assertIn(n_port['mac_address'], ir_ports_addresses)
@test.idempotent_id('549173a5-38ec-42bb-b0e2-c8b9f4a08943')
@test.services('compute', 'image', 'network')