tricircle/tricircle/tests/unit/api/controllers/test_routing.py

478 lines
20 KiB
Python

# 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 mock
from mock import patch
import unittest
import pecan
from tricircle.api.controllers import pod
from tricircle.api.controllers import routing
from tricircle.common import context
from tricircle.common import policy
from tricircle.db import api as db_api
from tricircle.db import core
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
class FakeResponse(object):
def __new__(cls, code=500):
cls.status = code
cls.status_code = code
return super(FakeResponse, cls).__new__(cls)
class RoutingControllerTest(unittest.TestCase):
def setUp(self):
core.initialize()
core.ModelBase.metadata.create_all(core.get_engine())
self.controller = routing.RoutingController()
self.context = context.get_admin_context()
policy.populate_default_rules()
def _validate_error_code(self, res, code):
self.assertEqual(code, res[res.keys()[0]]['code'])
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(context, 'extract_context_from_environ')
def test_post(self, mock_context):
mock_context.return_value = self.context
# prepare the foreign key: pod_id, project_id
kw_pod = {'pod': {'pod_name': 'pod1', 'az_name': 'az1'}}
pod_id = pod.PodsController().post(**kw_pod)['pod']['pod_id']
kw_binding = {'pod_binding': {'tenant_id': '01', 'pod_id': pod_id}}
project_id = pod.BindingsController().post(**kw_binding)[
'pod_binding']['tenant_id']
kw_routing = {'routing':
{'top_id': '09fd7cc9-d169-4b5a-88e8-436ecf4d0bfe',
'bottom_id': 'dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id,
'project_id': project_id,
'resource_type': 'subnet'
}}
id = self.controller.post(**kw_routing)['routing']['id']
routing = db_api.get_resource_routing(self.context, id)
self.assertEqual(routing['top_id'],
'09fd7cc9-d169-4b5a-88e8-436ecf4d0bfe')
self.assertEqual(routing['bottom_id'],
'dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef')
self.assertEqual(routing['pod_id'], pod_id)
self.assertEqual(routing['project_id'], project_id)
self.assertEqual(routing['resource_type'], 'subnet')
routings = db_api.list_resource_routings(self.context,
[{'key': 'top_id',
'comparator': 'eq',
'value':
'09fd7cc9-d169-4b5a-'
'88e8-436ecf4d0bfe'
},
{'key': 'pod_id',
'comparator': 'eq',
'value': pod_id}
], [])
self.assertEqual(len(routings), 1)
# failure case, only admin can create resource routing
self.context.is_admin = False
res = self.controller.post(**kw_routing)
self._validate_error_code(res, 403)
self.context.is_admin = True
# failure case, request body not found
kw_routing1 = {'route':
{'top_id': '109fd7cc9-d169-4b5a-88e8-436ecf4d0bfe',
'bottom_id': '2dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id,
'project_id': project_id,
'resource_type': 'subnet'
}}
res = self.controller.post(**kw_routing1)
self._validate_error_code(res, 400)
# failure case, top_id is not given
kw_routing2 = {'routing':
{'bottom_id': '2dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id,
'project_id': project_id,
'resource_type': 'subnet'
}}
res = self.controller.post(**kw_routing2)
self._validate_error_code(res, 400)
# failure case, top_id is empty
kw_routing3 = {'routing':
{'top_id': '',
'bottom_id': '2dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id,
'project_id': project_id,
'resource_type': 'subnet'
}}
res = self.controller.post(**kw_routing3)
self._validate_error_code(res, 400)
# failure case, top_id is given value 'None'
kw_routing4 = {'routing':
{'top_id': None,
'bottom_id': '2dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id,
'project_id': project_id,
'resource_type': 'subnet'
}}
res = self.controller.post(**kw_routing4)
self._validate_error_code(res, 400)
# failure case, the pod_id and the project_id should be bound
kw_pod2 = {'pod': {'pod_name': 'pod2', 'az_name': 'az1'}}
pod_id2 = pod.PodsController().post(**kw_pod2)['pod']['pod_id']
# the tenant_id binds with pod_id rather than pod_id2
kw_binding2 = {'pod_binding': {'tenant_id': '02', 'pod_id': pod_id}}
project_id2 = pod.BindingsController().post(**kw_binding2)[
'pod_binding']['tenant_id']
kw_routing5 = {'routing':
{'top_id': '09fd7cc9-d169-4b5a-88e8-436ecf4d0bfe',
'bottom_id': 'dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id2,
'project_id': project_id2,
'resource_type': 'subnet'
}}
res = self.controller.post(**kw_routing5)
self._validate_error_code(res, 400)
# failure case, wrong resource type
kw_routing6 = {'routing':
{'top_id': '09fd7cc9-d169-4b5a-88e8-436ecf4d0b09',
'bottom_id': 'dc80f9de-abb7-4ec6-ab7a-94f8fd1e2031f',
'pod_id': pod_id,
'project_id': project_id,
'resource_type': 'server'
}}
res = self.controller.post(**kw_routing6)
self._validate_error_code(res, 400)
# failure case, the resource routing already exists
res = self.controller.post(**kw_routing)
self._validate_error_code(res, 409)
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(context, 'extract_context_from_environ')
def test_get_one(self, mock_context):
mock_context.return_value = self.context
# prepare the foreign key: pod_id, project_id
kw_pod = {'pod': {'pod_name': 'pod1', 'az_name': 'az1'}}
pod_id = pod.PodsController().post(**kw_pod)['pod']['pod_id']
kw_binding = {'pod_binding': {'tenant_id': '01', 'pod_id': pod_id}}
project_id = pod.BindingsController().post(**kw_binding)[
'pod_binding']['tenant_id']
kw_routing = {'routing':
{'top_id': '09fd7cc9-d169-4b5a-88e8-436ecf4d0bfe',
'bottom_id': 'dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id,
'project_id': project_id,
'resource_type': 'port'
}}
id = self.controller.post(**kw_routing)['routing']['id']
routing = self.controller.get_one(id)
self.assertEqual(routing['routing']['top_id'],
'09fd7cc9-d169-4b5a-88e8-436ecf4d0bfe')
self.assertEqual(routing['routing']['bottom_id'],
'dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef')
self.assertEqual(routing['routing']['pod_id'], pod_id)
self.assertEqual(routing['routing']['project_id'],
project_id)
self.assertEqual(routing['routing']['resource_type'],
'port')
# failure case, only admin can get resource routing
self.context.is_admin = False
res = self.controller.get_one(id)
self._validate_error_code(res, 403)
self.context.is_admin = True
# failure case, resource routing not found
res = self.controller.get_one(-123)
self._validate_error_code(res, 404)
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(context, 'extract_context_from_environ')
def test_get_all(self, mock_context):
mock_context.return_value = self.context
# prepare the foreign key: pod_id, project_id
kw_pod1 = {'pod': {'pod_name': 'pod1', 'az_name': 'az1'}}
pod_id1 = pod.PodsController().post(**kw_pod1)['pod']['pod_id']
kw_binding1 = {'pod_binding': {'tenant_id': '01', 'pod_id': pod_id1}}
project_id1 = pod.BindingsController().post(**kw_binding1)[
'pod_binding']['tenant_id']
kw_routing1 = {'routing':
{'top_id': 'c7f641c9-8462-4007-84b2-3035d8cfb7a3',
'bottom_id': 'dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id1,
'project_id': project_id1,
'resource_type': 'subnet'
}}
# prepare the foreign key: pod_id, project_id
kw_pod2 = {'pod': {'pod_name': 'pod2', 'az_name': 'az1'}}
pod_id2 = pod.PodsController().post(**kw_pod2)['pod']['pod_id']
kw_binding2 = {'pod_binding': {'tenant_id': '02', 'pod_id': pod_id2}}
project_id2 = pod.BindingsController().post(**kw_binding2)[
'pod_binding']['tenant_id']
kw_routing2 = {'routing':
{'top_id': 'b669a2da-ca95-47db-a2a9-ba9e546d82ee',
'bottom_id': 'fd72c010-6e62-4866-b999-6dcb718dd7b4',
'pod_id': pod_id2,
'project_id': project_id2,
'resource_type': 'port'
}}
self.controller.post(**kw_routing1)
self.controller.post(**kw_routing2)
# no filters are applied to the routings, so all of the routings will
# be retrieved.
routings = self.controller.get_all()
actual = [(routing['top_id'], routing['pod_id'])
for routing in routings['routings']]
expect = [('c7f641c9-8462-4007-84b2-3035d8cfb7a3', pod_id1),
('b669a2da-ca95-47db-a2a9-ba9e546d82ee', pod_id2)]
self.assertItemsEqual(expect, actual)
# apply a resource type filter to the retrieved routings.
kw_filter1 = {'resource_type': 'port'}
routings = self.controller.get_all(**kw_filter1)
actual = [(routing['top_id'], routing['pod_id'],
routing['resource_type'])
for routing in routings['routings']]
expect = [('b669a2da-ca95-47db-a2a9-ba9e546d82ee', pod_id2, 'port')]
self.assertItemsEqual(expect, actual)
# apply a filter and if it doesn't match with any of the retrieved
# routings, then all of them will be discarded and the method returns
# with []
kw_filter2 = {'resource_type': 'port2'}
routings = self.controller.get_all(**kw_filter2)
self.assertEqual(routings['routings'], [])
# apply an illegal filter and it won't take effect
kw_filter3 = {'resource': 'port'}
routings = self.controller.get_all(**kw_filter3)
actual = [(routing['top_id'], routing['pod_id'])
for routing in routings['routings']]
expect = [('c7f641c9-8462-4007-84b2-3035d8cfb7a3', pod_id1),
('b669a2da-ca95-47db-a2a9-ba9e546d82ee', pod_id2)]
self.assertItemsEqual(expect, actual)
# failure case, only admin can show all resource routings
self.context.is_admin = False
res = self.controller.get_all()
self._validate_error_code(res, 403)
self.context.is_admin = True
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(pecan, 'response', new=mock.Mock)
@patch.object(context, 'extract_context_from_environ')
def test_delete(self, mock_context):
mock_context.return_value = self.context
# prepare the foreign key: pod_id, project_id
kw_pod = {'pod': {'pod_name': 'pod1', 'az_name': 'az1'}}
pod_id = pod.PodsController().post(**kw_pod)['pod']['pod_id']
kw_binding = {'pod_binding': {'tenant_id': '01', 'pod_id': pod_id}}
project_id = pod.BindingsController().post(**kw_binding)[
'pod_binding']['tenant_id']
kw_routing = {'routing':
{'top_id': '09fd7cc9-d169-4b5a-88e8-436ecf4d0bfe',
'bottom_id': 'dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id,
'project_id': project_id,
'resource_type': 'subnet'
}}
routing = self.controller.post(**kw_routing)
id = routing['routing']['id']
res = self.controller.delete(id)
self.assertEqual(res.status, 200)
routings = db_api.list_resource_routings(self.context,
[{'key': 'top_id',
'comparator': 'eq',
'value': '09fd7cc9-d'
'169-4b5a-88e8-436ecf4d0bfe'
},
{'key': 'pod_id',
'comparator': 'eq',
'value': pod_id
}], [])
self.assertEqual(len(routings), 0)
# failure case, only admin can delete resource routing
self.context.is_admin = False
res = self.controller.delete(id)
self._validate_error_code(res, 403)
self.context.is_admin = True
# failure case, resource routing not found
res = self.controller.delete(-123)
self._validate_error_code(res, 404)
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(pecan, 'response', new=mock.Mock)
@patch.object(context, 'extract_context_from_environ')
def test_put(self, mock_context):
mock_context.return_value = self.context
# prepare the foreign key: pod_id, project_id
kw_pod1 = {'pod': {'pod_name': 'pod1', 'az_name': 'az1'}}
pod_id1 = pod.PodsController().post(**kw_pod1)['pod']['pod_id']
kw_binding1 = {'pod_binding': {'tenant_id': '01', 'pod_id': pod_id1}}
project_id1 = pod.BindingsController().post(**kw_binding1)[
'pod_binding']['tenant_id']
body = {'routing':
{'top_id': 'c7f641c9-8462-4007-84b2-3035d8cfb7a3',
'bottom_id': 'dc80f9de-abb7-4ec6-ab7a-94f8fd1e20ef',
'pod_id': pod_id1,
'project_id': project_id1,
'resource_type': 'router'
}}
# both bottom_id and resource type have been changed
body_update1 = {'routing':
{'bottom_id': 'fd72c010-6e62-4866-b999-6dcb718dd7b4',
'resource_type': 'port'
}}
id = self.controller.post(**body)['routing']['id']
routing = self.controller.put(id, **body_update1)
self.assertEqual('port',
routing['routing']['resource_type'])
self.assertEqual('fd72c010-6e62-4866-b999-6dcb718dd7b4',
routing['routing']['bottom_id'])
self.assertEqual(pod_id1, routing['routing']['pod_id'])
# failure case, only admin can update resource routing
self.context.is_admin = False
res = self.controller.put(id, **body_update1)
self._validate_error_code(res, 403)
self.context.is_admin = True
# failure case, request body not found
body_update2 = {'route':
{'bottom_id': 'fd72c010-6e62-4866-b999-6dcb718dd7b4',
'resource_type': 'port'
}}
res = self.controller.put(id, **body_update2)
self._validate_error_code(res, 400)
# failure case, wrong resource type
body_update3 = {'routing':
{'resource_type': 'volume'}}
res = self.controller.put(id, **body_update3)
self._validate_error_code(res, 400)
# failure case, the value to be updated is empty
body_update4 = {'routing':
{'top_id': ''}}
res = self.controller.put(id, **body_update4)
self._validate_error_code(res, 400)
# failure case, the value to be updated is None
body_update5 = {'routing':
{'top_id': None}}
res = self.controller.put(id, **body_update5)
self._validate_error_code(res, 400)
# failure case, the value to be updated is not appropriate
res = self.controller.put(-123, **body_update1)
self._validate_error_code(res, 404)
# failure case, the pod_id and the project_id should be bound
kw_pod2 = {'pod': {'pod_name': 'pod2', 'az_name': 'az1'}}
pod_id2 = pod.PodsController().post(**kw_pod2)['pod']['pod_id']
# only update pod_id, and the new pod id is pod_id2. pod_id2
# has not been bound to tenant whose tenant_id is project_id1
body_update6 = {'routing':
{'pod_id': pod_id2}}
res = self.controller.put(id, **body_update6)
self._validate_error_code(res, 400)
# failure case, the pod_id and the project_id should be bound
kw_binding2 = {'pod_binding': {'tenant_id': '02', 'pod_id': pod_id2}}
project_id2 = pod.BindingsController().post(**kw_binding2)[
'pod_binding']['tenant_id']
# only update project_id, and the new project id is project_id2,
# this tenant has not been bound to pod whose pod_id is pod_id1
body_update7 = {'routing':
{'project_id': project_id2}}
res = self.controller.put(id, **body_update7)
self._validate_error_code(res, 400)
# failure case, the pod_id and the project_id should be bound
# both the pod_id and project_id are specified, and the
# tenant1 bound to pod1, pod3, tenant2 bound to pod1, pod2
# original routing: pod=pod1, tenant=tenant1
# updated routing: pod=pod3, tenant=tenant2
# this case should be failed
# bind the tenant1 to pod3
kw_pod3 = {'pod': {'pod_name': 'pod3', 'az_name': 'az1'}}
pod_id3 = pod.PodsController().post(**kw_pod3)['pod']['pod_id']
kw_binding3 = {'pod_binding': {'tenant_id': '01', 'pod_id': pod_id3}}
pod_id3 = pod.BindingsController().post(**kw_binding3)[
'pod_binding']['pod_id']
# bind the tenant2 to pod1
kw_binding4 = {'pod_binding': {'tenant_id': '02', 'pod_id': pod_id1}}
project_id2 = pod.BindingsController().post(**kw_binding4)[
'pod_binding']['tenant_id']
body_update8 = {'routing':
{'pod_id': pod_id3,
'project_id': project_id2}}
res = self.controller.put(id, **body_update8)
self._validate_error_code(res, 400)
def tearDown(self):
core.ModelBase.metadata.drop_all(core.get_engine())