613 lines
20 KiB
Python
613 lines
20 KiB
Python
|
# Copyright 2017 OVH SAS
|
||
|
# 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 copy
|
||
|
|
||
|
import testtools
|
||
|
|
||
|
from openstack.cloud import exc
|
||
|
from openstack.network.v2 import port as _port
|
||
|
from openstack.network.v2 import router as _router
|
||
|
from openstack.tests.unit import base
|
||
|
|
||
|
|
||
|
class TestRouter(base.TestCase):
|
||
|
router_name = 'goofy'
|
||
|
router_id = '57076620-dcfb-42ed-8ad6-79ccb4a79ed2'
|
||
|
subnet_id = '1f1696eb-7f47-47f6-835c-4889bff88604'
|
||
|
|
||
|
mock_router_rep = {
|
||
|
'admin_state_up': True,
|
||
|
'availability_zone_hints': [],
|
||
|
'availability_zones': [],
|
||
|
'description': u'',
|
||
|
'distributed': False,
|
||
|
'external_gateway_info': None,
|
||
|
'flavor_id': None,
|
||
|
'ha': False,
|
||
|
'id': router_id,
|
||
|
'name': router_name,
|
||
|
'project_id': u'861808a93da0484ea1767967c4df8a23',
|
||
|
'routes': [{"destination": "179.24.1.0/24", "nexthop": "172.24.3.99"}],
|
||
|
'status': u'ACTIVE',
|
||
|
'tenant_id': u'861808a93da0484ea1767967c4df8a23',
|
||
|
}
|
||
|
|
||
|
mock_router_interface_rep = {
|
||
|
'network_id': '53aee281-b06d-47fc-9e1a-37f045182b8e',
|
||
|
'subnet_id': '1f1696eb-7f47-47f6-835c-4889bff88604',
|
||
|
'tenant_id': '861808a93da0484ea1767967c4df8a23',
|
||
|
'subnet_ids': [subnet_id],
|
||
|
'port_id': '23999891-78b3-4a6b-818d-d1b713f67848',
|
||
|
'id': '57076620-dcfb-42ed-8ad6-79ccb4a79ed2',
|
||
|
'request_ids': ['req-f1b0b1b4-ae51-4ef9-b371-0cc3c3402cf7'],
|
||
|
}
|
||
|
|
||
|
router_availability_zone_extension = {
|
||
|
"alias": "router_availability_zone",
|
||
|
"updated": "2015-01-01T10:00:00-00:00",
|
||
|
"description": "Availability zone support for router.",
|
||
|
"links": [],
|
||
|
"name": "Router Availability Zone",
|
||
|
}
|
||
|
|
||
|
router_extraroute_extension = {
|
||
|
"alias": "extraroute",
|
||
|
"updated": "2015-01-01T10:00:00-00:00",
|
||
|
"description": "extra routes extension for router.",
|
||
|
"links": [],
|
||
|
"name": "Extra Routes",
|
||
|
}
|
||
|
|
||
|
enabled_neutron_extensions = [
|
||
|
router_availability_zone_extension,
|
||
|
router_extraroute_extension,
|
||
|
]
|
||
|
|
||
|
def _compare_routers(self, exp, real):
|
||
|
self.assertDictEqual(
|
||
|
_router.Router(**exp).to_dict(computed=False),
|
||
|
real.to_dict(computed=False),
|
||
|
)
|
||
|
|
||
|
def test_get_router(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers', self.router_name],
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers'],
|
||
|
qs_elements=['name=%s' % self.router_name],
|
||
|
),
|
||
|
json={'routers': [self.mock_router_rep]},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
r = self.cloud.get_router(self.router_name)
|
||
|
self.assertIsNotNone(r)
|
||
|
self._compare_routers(self.mock_router_rep, r)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_get_router_not_found(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers', 'mickey'],
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers'],
|
||
|
qs_elements=['name=mickey'],
|
||
|
),
|
||
|
json={'routers': []},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
r = self.cloud.get_router('mickey')
|
||
|
self.assertIsNone(r)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_router(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'network', 'public', append=['v2.0', 'routers']
|
||
|
),
|
||
|
json={'router': self.mock_router_rep},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'router': {
|
||
|
'name': self.router_name,
|
||
|
'admin_state_up': True,
|
||
|
}
|
||
|
}
|
||
|
),
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
new_router = self.cloud.create_router(
|
||
|
name=self.router_name, admin_state_up=True
|
||
|
)
|
||
|
|
||
|
self._compare_routers(self.mock_router_rep, new_router)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_router_specific_tenant(self):
|
||
|
new_router_tenant_id = "project_id_value"
|
||
|
mock_router_rep = copy.copy(self.mock_router_rep)
|
||
|
mock_router_rep['tenant_id'] = new_router_tenant_id
|
||
|
mock_router_rep['project_id'] = new_router_tenant_id
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'network', 'public', append=['v2.0', 'routers']
|
||
|
),
|
||
|
json={'router': mock_router_rep},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'router': {
|
||
|
'name': self.router_name,
|
||
|
'admin_state_up': True,
|
||
|
'project_id': new_router_tenant_id,
|
||
|
}
|
||
|
}
|
||
|
),
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.cloud.create_router(
|
||
|
self.router_name, project_id=new_router_tenant_id
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_router_with_availability_zone_hints(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network', 'public', append=['v2.0', 'extensions']
|
||
|
),
|
||
|
json={'extensions': self.enabled_neutron_extensions},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'network', 'public', append=['v2.0', 'routers']
|
||
|
),
|
||
|
json={'router': self.mock_router_rep},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'router': {
|
||
|
'name': self.router_name,
|
||
|
'admin_state_up': True,
|
||
|
'availability_zone_hints': ['nova'],
|
||
|
}
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
self.cloud.create_router(
|
||
|
name=self.router_name,
|
||
|
admin_state_up=True,
|
||
|
availability_zone_hints=['nova'],
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_router_without_enable_snat(self):
|
||
|
"""Do not send enable_snat when not given."""
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'network', 'public', append=['v2.0', 'routers']
|
||
|
),
|
||
|
json={'router': self.mock_router_rep},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'router': {
|
||
|
'name': self.router_name,
|
||
|
'admin_state_up': True,
|
||
|
}
|
||
|
}
|
||
|
),
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
self.cloud.create_router(name=self.router_name, admin_state_up=True)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_router_with_enable_snat_True(self):
|
||
|
"""Send enable_snat when it is True."""
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'network', 'public', append=['v2.0', 'routers']
|
||
|
),
|
||
|
json={'router': self.mock_router_rep},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'router': {
|
||
|
'name': self.router_name,
|
||
|
'admin_state_up': True,
|
||
|
'external_gateway_info': {'enable_snat': True},
|
||
|
}
|
||
|
}
|
||
|
),
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
self.cloud.create_router(
|
||
|
name=self.router_name, admin_state_up=True, enable_snat=True
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_router_with_enable_snat_False(self):
|
||
|
"""Send enable_snat when it is False."""
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'network', 'public', append=['v2.0', 'routers']
|
||
|
),
|
||
|
json={'router': self.mock_router_rep},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'router': {
|
||
|
'name': self.router_name,
|
||
|
'external_gateway_info': {
|
||
|
'enable_snat': False
|
||
|
},
|
||
|
'admin_state_up': True,
|
||
|
}
|
||
|
}
|
||
|
),
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
self.cloud.create_router(
|
||
|
name=self.router_name, admin_state_up=True, enable_snat=False
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_router_wrong_availability_zone_hints_type(self):
|
||
|
azh_opts = "invalid"
|
||
|
with testtools.ExpectedException(
|
||
|
exc.OpenStackCloudException,
|
||
|
"Parameter 'availability_zone_hints' must be a list",
|
||
|
):
|
||
|
self.cloud.create_router(
|
||
|
name=self.router_name,
|
||
|
admin_state_up=True,
|
||
|
availability_zone_hints=azh_opts,
|
||
|
)
|
||
|
|
||
|
def test_add_router_interface(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=[
|
||
|
'v2.0',
|
||
|
'routers',
|
||
|
self.router_id,
|
||
|
'add_router_interface',
|
||
|
],
|
||
|
),
|
||
|
json={'port': self.mock_router_interface_rep},
|
||
|
validate=dict(json={'subnet_id': self.subnet_id}),
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
self.cloud.add_router_interface(
|
||
|
{'id': self.router_id}, subnet_id=self.subnet_id
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_remove_router_interface(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=[
|
||
|
'v2.0',
|
||
|
'routers',
|
||
|
self.router_id,
|
||
|
'remove_router_interface',
|
||
|
],
|
||
|
),
|
||
|
json={'port': self.mock_router_interface_rep},
|
||
|
validate=dict(json={'subnet_id': self.subnet_id}),
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
self.cloud.remove_router_interface(
|
||
|
{'id': self.router_id}, subnet_id=self.subnet_id
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_remove_router_interface_missing_argument(self):
|
||
|
self.assertRaises(
|
||
|
ValueError, self.cloud.remove_router_interface, {'id': '123'}
|
||
|
)
|
||
|
|
||
|
def test_update_router(self):
|
||
|
new_router_name = "mickey"
|
||
|
new_routes = []
|
||
|
expected_router_rep = copy.copy(self.mock_router_rep)
|
||
|
expected_router_rep['name'] = new_router_name
|
||
|
expected_router_rep['routes'] = new_routes
|
||
|
# validate_calls() asserts that these requests are done in order,
|
||
|
# but the extensions call is only called if a non-None value is
|
||
|
# passed in 'routes'
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network', 'public', append=['v2.0', 'extensions']
|
||
|
),
|
||
|
json={'extensions': self.enabled_neutron_extensions},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers', self.router_id],
|
||
|
),
|
||
|
json=self.mock_router_rep,
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers', self.router_id],
|
||
|
),
|
||
|
json={'router': expected_router_rep},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'router': {
|
||
|
'name': new_router_name,
|
||
|
'routes': new_routes,
|
||
|
}
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
new_router = self.cloud.update_router(
|
||
|
self.router_id, name=new_router_name, routes=new_routes
|
||
|
)
|
||
|
|
||
|
self._compare_routers(expected_router_rep, new_router)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_delete_router(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers', self.router_name],
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers'],
|
||
|
qs_elements=['name=%s' % self.router_name],
|
||
|
),
|
||
|
json={'routers': [self.mock_router_rep]},
|
||
|
),
|
||
|
dict(
|
||
|
method='DELETE',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers', self.router_id],
|
||
|
),
|
||
|
json={},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
self.assertTrue(self.cloud.delete_router(self.router_name))
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_delete_router_not_found(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers', self.router_name],
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers'],
|
||
|
qs_elements=['name=%s' % self.router_name],
|
||
|
),
|
||
|
json={'routers': []},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
self.assertFalse(self.cloud.delete_router(self.router_name))
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_delete_router_multiple_found(self):
|
||
|
router1 = dict(id='123', name='mickey')
|
||
|
router2 = dict(id='456', name='mickey')
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers', 'mickey'],
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'routers'],
|
||
|
qs_elements=['name=mickey'],
|
||
|
),
|
||
|
json={'routers': [router1, router2]},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
self.assertRaises(
|
||
|
exc.OpenStackCloudException, self.cloud.delete_router, 'mickey'
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def _test_list_router_interfaces(
|
||
|
self, router, interface_type, expected_result=None
|
||
|
):
|
||
|
internal_ports = [
|
||
|
{
|
||
|
'id': 'internal_port_id',
|
||
|
'fixed_ips': [
|
||
|
{
|
||
|
'subnet_id': 'internal_subnet_id',
|
||
|
'ip_address': "10.0.0.1",
|
||
|
}
|
||
|
],
|
||
|
'device_id': self.router_id,
|
||
|
'device_owner': device_owner,
|
||
|
}
|
||
|
for device_owner in [
|
||
|
'network:router_interface',
|
||
|
'network:ha_router_replicated_interface',
|
||
|
'network:router_interface_distributed',
|
||
|
]
|
||
|
]
|
||
|
|
||
|
external_ports = [
|
||
|
{
|
||
|
'id': 'external_port_id',
|
||
|
'fixed_ips': [
|
||
|
{
|
||
|
'subnet_id': 'external_subnet_id',
|
||
|
'ip_address': "1.2.3.4",
|
||
|
}
|
||
|
],
|
||
|
'device_id': self.router_id,
|
||
|
'device_owner': 'network:router_gateway',
|
||
|
}
|
||
|
]
|
||
|
|
||
|
if expected_result is None:
|
||
|
if interface_type == "internal":
|
||
|
expected_result = internal_ports
|
||
|
elif interface_type == "external":
|
||
|
expected_result = external_ports
|
||
|
else:
|
||
|
expected_result = internal_ports + external_ports
|
||
|
|
||
|
mock_uri = dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'network',
|
||
|
'public',
|
||
|
append=['v2.0', 'ports'],
|
||
|
qs_elements=["device_id=%s" % self.router_id],
|
||
|
),
|
||
|
json={'ports': (internal_ports + external_ports)},
|
||
|
)
|
||
|
|
||
|
self.register_uris([mock_uri])
|
||
|
ret = self.cloud.list_router_interfaces(router, interface_type)
|
||
|
self.assertEqual(
|
||
|
[_port.Port(**i).to_dict(computed=False) for i in expected_result],
|
||
|
[i.to_dict(computed=False) for i in ret],
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
router = {
|
||
|
'id': router_id,
|
||
|
'external_gateway_info': {
|
||
|
'external_fixed_ips': [
|
||
|
{'subnet_id': 'external_subnet_id', 'ip_address': '1.2.3.4'}
|
||
|
]
|
||
|
},
|
||
|
}
|
||
|
|
||
|
def test_list_router_interfaces_all(self):
|
||
|
self._test_list_router_interfaces(self.router, interface_type=None)
|
||
|
|
||
|
def test_list_router_interfaces_internal(self):
|
||
|
self._test_list_router_interfaces(
|
||
|
self.router, interface_type="internal"
|
||
|
)
|
||
|
|
||
|
def test_list_router_interfaces_external(self):
|
||
|
self._test_list_router_interfaces(
|
||
|
self.router, interface_type="external"
|
||
|
)
|