Merge "api: Add response body schemas for port interface APIs"
This commit is contained in:
@@ -20,7 +20,7 @@ from webob import exc
|
||||
|
||||
from nova.api.openstack import api_version_request
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack.compute.schemas import attach_interfaces
|
||||
from nova.api.openstack.compute.schemas import attach_interfaces as schema
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api import validation
|
||||
from nova.compute import api as compute
|
||||
@@ -64,7 +64,9 @@ class InterfaceAttachmentController(wsgi.Controller):
|
||||
self.network_api = neutron.API()
|
||||
|
||||
@wsgi.expected_errors((404, 501))
|
||||
@validation.query_schema(attach_interfaces.index_query)
|
||||
@validation.query_schema(schema.index_query)
|
||||
@validation.response_body_schema(schema.index_response, '2.1', '2.69')
|
||||
@validation.response_body_schema(schema.index_response_v270, '2.70')
|
||||
def index(self, req, server_id):
|
||||
"""Returns the list of interface attachments for a given instance."""
|
||||
context = req.environ['nova.context']
|
||||
@@ -107,7 +109,9 @@ class InterfaceAttachmentController(wsgi.Controller):
|
||||
return {'interfaceAttachments': results}
|
||||
|
||||
@wsgi.expected_errors((403, 404))
|
||||
@validation.query_schema(attach_interfaces.show_query)
|
||||
@validation.query_schema(schema.show_query)
|
||||
@validation.response_body_schema(schema.show_response, '2.1', '2.69')
|
||||
@validation.response_body_schema(schema.show_response_v270, '2.70')
|
||||
def show(self, req, server_id, id):
|
||||
"""Return data about the given interface attachment."""
|
||||
context = req.environ['nova.context']
|
||||
@@ -135,8 +139,10 @@ class InterfaceAttachmentController(wsgi.Controller):
|
||||
show_tag=api_version_request.is_supported(req, '2.70'))}
|
||||
|
||||
@wsgi.expected_errors((400, 403, 404, 409, 500, 501))
|
||||
@validation.schema(attach_interfaces.create, '2.0', '2.48')
|
||||
@validation.schema(attach_interfaces.create_v249, '2.49')
|
||||
@validation.schema(schema.create, '2.0', '2.48')
|
||||
@validation.schema(schema.create_v249, '2.49')
|
||||
@validation.response_body_schema(schema.create_response, '2.1', '2.69')
|
||||
@validation.response_body_schema(schema.create_response_v270, '2.70')
|
||||
def create(self, req, server_id, body):
|
||||
"""Attach an interface to an instance."""
|
||||
context = req.environ['nova.context']
|
||||
@@ -206,6 +212,7 @@ class InterfaceAttachmentController(wsgi.Controller):
|
||||
|
||||
@wsgi.response(202)
|
||||
@wsgi.expected_errors((400, 404, 409, 501))
|
||||
@validation.response_body_schema(schema.delete_response)
|
||||
def delete(self, req, server_id, id):
|
||||
"""Detach an interface from an instance."""
|
||||
context = req.environ['nova.context']
|
||||
|
||||
@@ -48,8 +48,7 @@ create = {
|
||||
}
|
||||
|
||||
create_v249 = copy.deepcopy(create)
|
||||
create_v249['properties']['interfaceAttachment'][
|
||||
'properties']['tag'] = parameter_types.tag
|
||||
create_v249['properties']['interfaceAttachment']['properties']['tag'] = parameter_types.tag # noqa: E501
|
||||
|
||||
# TODO(stephenfin): Remove additionalProperties in a future API version
|
||||
index_query = {
|
||||
@@ -64,3 +63,78 @@ show_query = {
|
||||
'properties': {},
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
_interface_attachment = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'fixed_ips': {
|
||||
'type': ['null', 'array'],
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'ip_address': {
|
||||
'type': 'string',
|
||||
'anyOf': [
|
||||
{'format': 'ipv4'},
|
||||
{'format': 'ipv6'},
|
||||
],
|
||||
},
|
||||
'subnet_id': {'type': 'string', 'format': 'uuid'},
|
||||
},
|
||||
'required': ['ip_address', 'subnet_id'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'mac_addr': {'type': 'string', 'format': 'mac-address'},
|
||||
'net_id': {'type': 'string', 'format': 'uuid'},
|
||||
'port_id': {'type': 'string', 'format': 'uuid'},
|
||||
'port_state': {'type': 'string'},
|
||||
},
|
||||
'required': ['fixed_ips', 'mac_addr', 'net_id', 'port_id', 'port_state'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
_interface_attachment_v270 = copy.deepcopy(_interface_attachment)
|
||||
_interface_attachment_v270['properties']['tag'] = {
|
||||
'type': ['null', 'string'],
|
||||
}
|
||||
_interface_attachment_v270['required'].append('tag')
|
||||
|
||||
index_response = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'interfaceAttachments': {
|
||||
'type': 'array',
|
||||
'items': copy.deepcopy(_interface_attachment),
|
||||
},
|
||||
},
|
||||
'required': ['interfaceAttachments'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
index_response_v270 = copy.deepcopy(index_response)
|
||||
index_response_v270['properties']['interfaceAttachments']['items'] = copy.deepcopy( # noqa: E501
|
||||
_interface_attachment_v270
|
||||
)
|
||||
|
||||
show_response = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'interfaceAttachment': copy.deepcopy(_interface_attachment),
|
||||
},
|
||||
'required': ['interfaceAttachment'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
show_response_v270 = copy.deepcopy(show_response)
|
||||
show_response_v270['properties']['interfaceAttachment'] = copy.deepcopy(
|
||||
_interface_attachment_v270
|
||||
)
|
||||
|
||||
# create responses are identical to show, including microversions
|
||||
create_response = copy.deepcopy(show_response)
|
||||
create_response_v270 = copy.deepcopy(show_response_v270)
|
||||
|
||||
delete_response = {
|
||||
'type': 'null',
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ port_data1 = {
|
||||
"admin_state_up": True,
|
||||
"status": "ACTIVE",
|
||||
"mac_address": "aa:aa:aa:aa:aa:aa",
|
||||
"fixed_ips": ["10.0.1.2"],
|
||||
"fixed_ips": [
|
||||
{"ip_address": "10.0.1.2", "subnet_id": FAKE_UUID2},
|
||||
],
|
||||
"device_id": FAKE_UUID1,
|
||||
}
|
||||
|
||||
@@ -58,7 +60,9 @@ port_data2 = {
|
||||
"admin_state_up": True,
|
||||
"status": "ACTIVE",
|
||||
"mac_address": "bb:bb:bb:bb:bb:bb",
|
||||
"fixed_ips": ["10.0.2.2"],
|
||||
"fixed_ips": [
|
||||
{"ip_address": "10.0.2.2", "subnet_id": FAKE_UUID2},
|
||||
],
|
||||
"device_id": FAKE_UUID1,
|
||||
}
|
||||
|
||||
@@ -68,7 +72,9 @@ port_data3 = {
|
||||
"admin_state_up": True,
|
||||
"status": "ACTIVE",
|
||||
"mac_address": "bb:bb:bb:bb:bb:bb",
|
||||
"fixed_ips": ["10.0.2.2"],
|
||||
"fixed_ips": [
|
||||
{"ip_address": "10.0.3.2", "subnet_id": FAKE_UUID2},
|
||||
],
|
||||
"device_id": '',
|
||||
}
|
||||
|
||||
@@ -529,6 +535,13 @@ class InterfaceAttachTestsV249(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(InterfaceAttachTestsV249, self).setUp()
|
||||
self.attachments = self.controller_cls()
|
||||
show_port_patch = mock.patch.object(self.attachments.network_api,
|
||||
'show_port', fake_show_port)
|
||||
show_port_patch.start()
|
||||
self.addCleanup(show_port_patch.stop)
|
||||
self.stub_out('nova.compute.api.API.attach_interface',
|
||||
fake_attach_interface)
|
||||
|
||||
self.req = fakes.HTTPRequest.blank('', version='2.49')
|
||||
|
||||
def test_tagged_interface_attach_invalid_tag_comma(self):
|
||||
@@ -550,12 +563,12 @@ class InterfaceAttachTestsV249(test.NoDBTestCase):
|
||||
self.assertRaises(exception.ValidationError, self.attachments.create,
|
||||
self.req, FAKE_UUID1, body=body)
|
||||
|
||||
@mock.patch('nova.compute.api.API.attach_interface')
|
||||
@mock.patch('nova.compute.api.API.get', fake_get_instance)
|
||||
def test_tagged_interface_attach_valid_tag(self, _):
|
||||
body = {'interfaceAttachment': {'net_id': FAKE_NET_ID2,
|
||||
def test_tagged_interface_attach_valid_tag(self):
|
||||
body = {'interfaceAttachment': {'net_id': FAKE_NET_ID1,
|
||||
'tag': 'foo'}}
|
||||
with mock.patch.object(self.attachments, 'show'):
|
||||
with mock.patch.object(self.attachments.network_api, 'show_port',
|
||||
fake_show_port):
|
||||
self.attachments.create(self.req, FAKE_UUID1, body=body)
|
||||
|
||||
|
||||
|
||||
@@ -80,7 +80,12 @@ class AttachInterfacesPolicyTest(base.BasePolicyTest):
|
||||
"admin_state_up": True,
|
||||
"status": "ACTIVE",
|
||||
"mac_address": "bb:bb:bb:bb:bb:bb",
|
||||
"fixed_ips": ["10.0.2.2"],
|
||||
"fixed_ips": [
|
||||
{
|
||||
"ip_address": "10.0.2.2",
|
||||
"subnet_id": uuids.subnet_id,
|
||||
},
|
||||
],
|
||||
"device_id": server_id,
|
||||
}}
|
||||
self.common_policy_auth(self.project_reader_authorized_contexts,
|
||||
@@ -89,15 +94,29 @@ class AttachInterfacesPolicyTest(base.BasePolicyTest):
|
||||
self.req, server_id, port_id)
|
||||
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
@mock.patch('nova.api.openstack.compute.attach_interfaces'
|
||||
'.InterfaceAttachmentController.show')
|
||||
@mock.patch('nova.network.neutron.API.show_port')
|
||||
@mock.patch('nova.compute.api.API.attach_interface')
|
||||
def test_attach_interface(self, mock_interface, mock_port, mock_get):
|
||||
rule_name = "os_compute_api:os-attach-interfaces:create"
|
||||
server_id = uuids.fake_id
|
||||
mock_port.return_value = {'port': {
|
||||
"id": uuids.port_id,
|
||||
"network_id": uuids.fake_id,
|
||||
"admin_state_up": True,
|
||||
"status": "ACTIVE",
|
||||
"mac_address": "bb:bb:bb:bb:bb:bb",
|
||||
"fixed_ips": [
|
||||
{
|
||||
"ip_address": "10.0.2.2",
|
||||
"subnet_id": uuids.subnet_id,
|
||||
},
|
||||
],
|
||||
"device_id": server_id,
|
||||
}}
|
||||
body = {'interfaceAttachment': {'net_id': uuids.fake_id}}
|
||||
self.common_policy_auth(self.project_member_authorized_contexts,
|
||||
rule_name, self.controller.create,
|
||||
self.req, uuids.fake_id, body=body)
|
||||
self.req, server_id, body=body)
|
||||
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
@mock.patch('nova.compute.api.API.detach_interface')
|
||||
|
||||
Reference in New Issue
Block a user