Files
trio2o/tricircle/tests/unit/nova_apigw/controllers/test_server.py
zhiyuan_cai 8c0e8276c3 Implement security group - Nova API gateway part
Nova API gateway needs to finish two tasks for security group
functionality. One is to create security group in bottom pod if
it does not exist. Only when the user boot a server can we know
which pod to create the security group. The other one is to
handle default security group. We decide to replace the default
"remote group" rules with several "remote ip prefix" rules.
Each rule points to a CIDR of one of the project's subnets.
Since the collection of subnets may change, we need to update
bottom security group rules.

Currently these two tasks both run in synchronous way. For
better response time the second task can be implemented in
asynchronous way later.

Neutron plugin part for security group functionality will be
covered in next patch.

Change-Id: I1822ff16e3dfc2a89c34f7833adcc1ad1d952462
2016-03-14 09:47:50 +08:00

866 lines
35 KiB
Python

# Copyright 2015 Huawei Technologies Co., Ltd.
# 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 datetime
import mock
from mock import patch
import pecan
import unittest
import neutronclient.common.exceptions as q_exceptions
from oslo_utils import uuidutils
from tricircle.common import context
from tricircle.common import lock_handle
from tricircle.db import api
from tricircle.db import core
from tricircle.db import models
from tricircle.nova_apigw.controllers import server
TOP_NETS = []
TOP_SUBNETS = []
TOP_PORTS = []
TOP_SGS = []
BOTTOM1_NETS = []
BOTTOM1_SUBNETS = []
BOTTOM1_PORTS = []
BOTTOM1_SGS = []
BOTTOM2_NETS = []
BOTTOM2_SUBNETS = []
BOTTOM2_PORTS = []
BOTTOM2_SGS = []
BOTTOM_NETS = BOTTOM1_NETS
BOTTOM_SUBNETS = BOTTOM1_SUBNETS
BOTTOM_PORTS = BOTTOM1_PORTS
BOTTOM_SGS = BOTTOM1_SGS
RES_LIST = [TOP_NETS, TOP_SUBNETS, TOP_PORTS, TOP_SGS,
BOTTOM1_NETS, BOTTOM1_SUBNETS, BOTTOM1_PORTS, BOTTOM1_SGS,
BOTTOM2_NETS, BOTTOM2_SUBNETS, BOTTOM2_PORTS, BOTTOM2_SGS]
class FakeException(Exception):
pass
class FakeServerController(server.ServerController):
def __init__(self, project_id):
self.clients = {'t_region': FakeClient('t_region')}
self.project_id = project_id
def _get_client(self, pod_name=None):
if not pod_name:
return self.clients['t_region']
else:
if pod_name not in self.clients:
self.clients[pod_name] = FakeClient(pod_name)
return self.clients[pod_name]
class FakeClient(object):
_res_map = {'top': {'network': TOP_NETS,
'subnet': TOP_SUBNETS,
'port': TOP_PORTS,
'security_group': TOP_SGS},
'bottom': {'network': BOTTOM_NETS,
'subnet': BOTTOM_SUBNETS,
'port': BOTTOM_PORTS,
'security_group': BOTTOM_SGS},
'bottom2': {'network': BOTTOM2_NETS,
'subnet': BOTTOM2_SUBNETS,
'port': BOTTOM2_PORTS,
'security_group': BOTTOM2_SGS}}
def __init__(self, pod_name):
self.pod_name = pod_name
self.ip_suffix_gen = self._get_ip_suffix()
def _get_res_list(self, _type):
if self.pod_name == 'b_region_2':
pod = 'bottom2'
elif self.pod_name == 't_region':
pod = 'top'
else:
pod = 'bottom'
return self._res_map[pod][_type]
def _check_port_ip_conflict(self, subnet_id, ip):
port_list = self._get_res_list('port')
for port in port_list:
if 'fixed_ips' in port:
if port['fixed_ips'][0]['ip_address'] == ip and (
port['fixed_ips'][0]['subnet_id'] == subnet_id
):
raise FakeException()
def create_resources(self, _type, ctx, body):
if 'id' not in body[_type]:
body[_type]['id'] = uuidutils.generate_uuid()
if _type == 'port' and 'fixed_ips' in body[_type]:
ip_dict = body[_type]['fixed_ips'][0]
self._check_port_ip_conflict(ip_dict['subnet_id'],
ip_dict['ip_address'])
if _type == 'security_group':
body[_type]['security_group_rules'] = [
{'remote_group_id': None,
'direction': 'egress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4',
'id': uuidutils.generate_uuid()},
{'remote_group_id': None,
'direction': 'egress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv6',
'id': uuidutils.generate_uuid()},
]
res_list = self._get_res_list(_type)
res = dict(body[_type])
res_list.append(res)
return res
def list_resources(self, _type, ctx, filters):
res_list = self._get_res_list(_type)
ret_list = []
for res in res_list:
match = True
for filter in filters:
if filter['key'] not in res:
match = False
break
if res[filter['key']] != filter['value']:
match = False
break
if match:
ret_list.append(res)
return ret_list
@staticmethod
def _get_ip_suffix():
# three elements should be enough
suffix_list = ['3', '4', '5']
index = 0
while True:
yield suffix_list[index]
index += 1
index %= 3
def create_ports(self, ctx, body):
if 'fixed_ips' in body['port']:
return self.create_resources('port', ctx, body)
net_id = body['port']['network_id']
subnets = self._get_res_list('subnet')
fixed_ip_list = []
for subnet in subnets:
if subnet['network_id'] == net_id:
cidr = subnet['cidr']
ip_prefix = cidr[:cidr.rindex('.') + 1]
mac_prefix = 'fa:16:3e:96:41:0'
if 'device_owner' in body['port']:
ip = ip_prefix + '2'
body['port']['mac_address'] = mac_prefix + '2'
else:
suffix = self.ip_suffix_gen.next()
ip = ip_prefix + suffix
body['port']['mac_address'] = mac_prefix + suffix
fixed_ip_list.append({'ip_address': ip,
'subnet_id': subnet['id']})
body['port']['fixed_ips'] = fixed_ip_list
return self.create_resources('port', ctx, body)
def list_ports(self, ctx, filters):
return self.list_resources('port', ctx, filters)
def list_security_groups(self, ctx, filters):
return self.list_resources('security_group', ctx, filters)
def delete_ports(self, ctx, port_id):
port_list = self._get_res_list('port')
for i, port in enumerate(port_list):
if port['id'] == port_id:
break
port_list.pop(i)
def get_networks(self, ctx, network_id):
return self.list_resources(
'network', ctx,
[{'key': 'id', 'comparator': 'eq', 'value': network_id}])[0]
def list_subnets(self, ctx, filters):
return self.list_resources('subnet', ctx, filters)
def get_subnets(self, ctx, subnet_id):
return self.list_resources(
'subnet', ctx,
[{'key': 'id', 'comparator': 'eq', 'value': subnet_id}])[0]
def create_servers(self, ctx, body):
# do nothing here since it will be mocked
pass
def get_security_groups(self, ctx, sg_id):
sg = self.list_resources(
'security_group', ctx,
[{'key': 'id', 'comparator': 'eq', 'value': sg_id}])[0]
# need to do a deep copy because we will traverse the security group's
# 'security_group_rules' field and make change to the group
ret_sg = copy.deepcopy(sg)
return ret_sg
def create_security_group_rules(self, ctx, body):
for _rule in body['security_group_rules']:
sg_id = _rule['security_group_id']
sg = self.list_resources(
'security_group', ctx,
[{'key': 'id', 'comparator': 'eq', 'value': sg_id}])[0]
new_rule = copy.copy(_rule)
match_found = False
for rule in sg['security_group_rules']:
old_rule = copy.copy(rule)
if new_rule == old_rule:
match_found = True
break
if match_found:
raise q_exceptions.Conflict()
sg['security_group_rules'].append(new_rule)
def delete_security_group_rules(self, ctx, rule_id):
res_list = self._get_res_list('security_group')
for sg in res_list:
for rule in sg['security_group_rules']:
if rule['id'] == rule_id:
sg['security_group_rules'].remove(rule)
return
class ServerTest(unittest.TestCase):
def setUp(self):
core.initialize()
core.ModelBase.metadata.create_all(core.get_engine())
self.context = context.Context()
self.project_id = 'test_project'
self.controller = FakeServerController(self.project_id)
def _prepare_pod(self, bottom_pod_num=1):
t_pod = {'pod_id': 't_pod_uuid', 'pod_name': 't_region',
'az_name': ''}
api.create_pod(self.context, t_pod)
if bottom_pod_num == 1:
b_pod = {'pod_id': 'b_pod_uuid', 'pod_name': 'b_region',
'az_name': 'b_az'}
api.create_pod(self.context, b_pod)
return t_pod, b_pod
b_pods = []
for i in xrange(1, bottom_pod_num + 1):
b_pod = {'pod_id': 'b_pod_%d_uuid' % i,
'pod_name': 'b_region_%d' % i,
'az_name': 'b_az_%d' % i}
api.create_pod(self.context, b_pod)
b_pods.append(b_pod)
return t_pod, b_pods
def test_get_or_create_route(self):
t_pod, b_pod = self._prepare_pod()
route, is_own = self.controller._get_or_create_route(
self.context, b_pod, 'test_top_id', 'port')
self.assertTrue(is_own)
self.assertEqual('test_top_id', route['top_id'])
self.assertIsNone(route['bottom_id'])
self.assertEqual('port', route['resource_type'])
self.assertEqual(self.project_id, route['project_id'])
def test_get_or_create_route_conflict(self):
t_pod, b_pod = self._prepare_pod()
self.controller._get_or_create_route(
self.context, b_pod, 'test_top_id', 'port')
route, status = self.controller._get_or_create_route(
self.context, b_pod, 'test_top_id', 'port')
self.assertEqual(lock_handle.NONE_DONE, status)
self.assertIsNone(route)
def test_get_or_create_route_conflict_expire(self):
t_pod, b_pod = self._prepare_pod()
route, is_own = self.controller._get_or_create_route(
self.context, b_pod, 'test_top_id', 'port')
# manually set update time to expire the routing entry
with self.context.session.begin():
update_time = route['created_at'] - datetime.timedelta(0, 60)
core.update_resource(self.context, models.ResourceRouting,
route['id'], {'updated_at': update_time})
new_route, is_own = self.controller._get_or_create_route(
self.context, b_pod, 'test_top_id', 'port')
self.assertTrue(is_own)
self.assertEqual('test_top_id', new_route['top_id'])
self.assertIsNone(new_route['bottom_id'])
self.assertEqual('port', new_route['resource_type'])
self.assertEqual(self.project_id, new_route['project_id'])
def test_get_or_create_route_conflict_expire_has_bottom_res(self):
t_pod, b_pod = self._prepare_pod()
route, is_own = self.controller._get_or_create_route(
self.context, b_pod, 'test_top_id', 'port')
# manually set update time to expire the routing entry
with self.context.session.begin():
update_time = route['created_at'] - datetime.timedelta(0, 60)
core.update_resource(self.context, models.ResourceRouting,
route['id'], {'updated_at': update_time})
# insert a fake bottom port
BOTTOM_PORTS.append({'id': 'test_bottom_id', 'name': 'test_top_id'})
new_route, status = self.controller._get_or_create_route(
self.context, b_pod, 'test_top_id', 'port')
self.assertEqual(lock_handle.RES_DONE, status)
self.assertEqual('test_top_id', new_route['top_id'])
self.assertEqual('test_bottom_id', new_route['bottom_id'])
self.assertEqual('port', new_route['resource_type'])
self.assertEqual(self.project_id, new_route['project_id'])
def test_prepare_neutron_element(self):
t_pod, b_pod = self._prepare_pod()
port = {'id': 'top_port_id'}
body = {'port': {'name': 'top_port_id'}}
_, bottom_port_id = self.controller._prepare_neutron_element(
self.context, b_pod, port, 'port', body)
mappings = api.get_bottom_mappings_by_top_id(self.context,
'top_port_id', 'port')
self.assertEqual(bottom_port_id, mappings[0][1])
@patch.object(FakeClient, 'create_resources')
def test_prepare_neutron_element_create_res_exception(self, mock_method):
mock_method.side_effect = FakeException()
t_pod, b_pod = self._prepare_pod()
port = {'id': 'top_port_id'}
body = {'port': {'name': 'top_port_id'}}
self.assertRaises(FakeException,
self.controller._prepare_neutron_element,
self.context, b_pod, port, 'port', body)
mappings = api.get_bottom_mappings_by_top_id(self.context,
'top_port_id', 'port')
self.assertEqual(0, len(mappings))
def _check_routes(self):
for res in (TOP_NETS, TOP_SUBNETS, BOTTOM_NETS, BOTTOM_SUBNETS):
self.assertEqual(1, len(res))
self.assertEqual(2, len(TOP_PORTS))
self.assertEqual(2, len(BOTTOM_PORTS))
with self.context.session.begin():
routes = core.query_resource(self.context,
models.ResourceRouting, [], [])
self.assertEqual(4, len(routes))
actual = [[], [], [], []]
for region in ('t_region', 'b_region'):
actual[0].append(self.controller._get_client(
region).list_resources('network', self.context, [])[0]['id'])
actual[1].append(self.controller._get_client(
region).list_resources('subnet', self.context, [])[0]['id'])
t_ports = self.controller._get_client(
region).list_resources('port', self.context, [])
if 'device_id' in t_ports[0]:
actual[2].append(t_ports[0]['id'])
actual[3].append(t_ports[1]['id'])
else:
actual[2].append(t_ports[1]['id'])
actual[3].append(t_ports[0]['id'])
expect = [[route['top_id'], route['bottom_id']] for route in routes]
self.assertItemsEqual(expect, actual)
def test_handle_network(self):
t_pod, b_pod = self._prepare_pod()
net = {'id': 'top_net_id'}
subnet = {'id': 'top_subnet_id',
'network_id': 'top_net_id',
'ip_version': 4,
'cidr': '10.0.0.0/24',
'gateway_ip': '10.0.0.1',
'allocation_pools': {'start': '10.0.0.2',
'end': '10.0.0.254'},
'enable_dhcp': True}
TOP_NETS.append(net)
TOP_SUBNETS.append(subnet)
self.controller._handle_network(self.context, b_pod, net, [subnet])
self._check_routes()
def test_handle_port(self):
t_pod, b_pod = self._prepare_pod()
net = {'id': 'top_net_id'}
subnet = {'id': 'top_subnet_id',
'network_id': 'top_net_id',
'ip_version': 4,
'cidr': '10.0.0.0/24',
'gateway_ip': '10.0.0.1',
'allocation_pools': {'start': '10.0.0.2',
'end': '10.0.0.254'},
'enable_dhcp': True}
port = {
'id': 'top_port_id',
'network_id': 'top_net_id',
'mac_address': 'fa:16:3e:96:41:03',
'fixed_ips': [{'subnet_id': 'top_subnet_id',
'ip_address': '10.0.0.3'}]
}
TOP_NETS.append(net)
TOP_SUBNETS.append(subnet)
TOP_PORTS.append(port)
self.controller._handle_port(self.context, b_pod, port)
self._check_routes()
def _test_handle_network_dhcp_port(self, dhcp_ip):
t_pod, b_pod = self._prepare_pod()
top_net_id = 'top_net_id'
bottom_net_id = 'bottom_net_id'
top_subnet_id = 'top_subnet_id'
bottom_subnet_id = 'bottom_subnet_id'
t_net = {'id': top_net_id}
b_net = {'id': bottom_net_id}
t_subnet = {'id': top_subnet_id,
'network_id': top_net_id,
'ip_version': 4,
'cidr': '10.0.0.0/24',
'gateway_ip': '10.0.0.1',
'allocation_pools': {'start': '10.0.0.2',
'end': '10.0.0.254'},
'enable_dhcp': True}
b_subnet = {'id': bottom_subnet_id,
'network_id': bottom_net_id,
'ip_version': 4,
'cidr': '10.0.0.0/24',
'gateway_ip': '10.0.0.1',
'allocation_pools': {'start': '10.0.0.2',
'end': '10.0.0.254'},
'enable_dhcp': True}
b_dhcp_port = {'id': 'bottom_dhcp_port_id',
'network_id': bottom_net_id,
'fixed_ips': [
{'subnet_id': bottom_subnet_id,
'ip_address': dhcp_ip}
],
'mac_address': 'fa:16:3e:96:41:0a',
'binding:profile': {},
'device_id': 'reserved_dhcp_port',
'device_owner': 'network:dhcp'}
TOP_NETS.append(t_net)
TOP_SUBNETS.append(t_subnet)
BOTTOM_NETS.append(b_net)
BOTTOM_SUBNETS.append(b_subnet)
BOTTOM_PORTS.append(b_dhcp_port)
with self.context.session.begin():
core.create_resource(
self.context, models.ResourceRouting,
{'top_id': top_net_id, 'bottom_id': bottom_net_id,
'pod_id': b_pod['pod_id'], 'project_id': self.project_id,
'resource_type': 'network'})
core.create_resource(
self.context, models.ResourceRouting,
{'top_id': top_subnet_id, 'bottom_id': bottom_subnet_id,
'pod_id': b_pod['pod_id'], 'project_id': self.project_id,
'resource_type': 'subnet'})
self.controller._handle_network(self.context,
b_pod, t_net, [t_subnet])
self._check_routes()
def test_handle_network_dhcp_port_same_ip(self):
self._test_handle_network_dhcp_port('10.0.0.2')
def test_handle_network_dhcp_port_exist_diff_ip(self):
self._test_handle_network_dhcp_port('10.0.0.4')
@patch.object(pecan, 'abort')
@patch.object(FakeClient, 'create_servers')
@patch.object(context, 'extract_context_from_environ')
def test_post_with_network_az(self, mock_ctx, mock_create, mock_abort):
t_pod, b_pod = self._prepare_pod()
top_net_id = 'top_net_id'
top_subnet_id = 'top_subnet_id'
top_sg_id = 'top_sg_id'
t_net = {'id': top_net_id}
t_subnet = {'id': top_subnet_id,
'network_id': top_net_id,
'ip_version': 4,
'cidr': '10.0.0.0/24',
'gateway_ip': '10.0.0.1',
'allocation_pools': {'start': '10.0.0.2',
'end': '10.0.0.254'},
'enable_dhcp': True}
t_sg = {'id': top_sg_id, 'name': 'default', 'description': '',
'tenant_id': self.project_id,
'security_group_rules': [
{'remote_group_id': top_sg_id,
'direction': 'ingress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
{'remote_group_id': None,
'direction': 'egress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
]}
TOP_NETS.append(t_net)
TOP_SUBNETS.append(t_subnet)
TOP_SGS.append(t_sg)
server_name = 'test_server'
image_id = 'image_id'
flavor_id = 1
body = {
'server': {
'name': server_name,
'imageRef': image_id,
'flavorRef': flavor_id,
'availability_zone': b_pod['az_name'],
'networks': [{'uuid': top_net_id}]
}
}
mock_create.return_value = {'id': 'bottom_server_id'}
mock_ctx.return_value = self.context
# update top net for test purpose, correct az
TOP_NETS[0]['availability_zone_hints'] = ['b_az']
self.controller.post(**body)
# update top net for test purpose, wrong az
TOP_NETS[0]['availability_zone_hints'] = ['fake_az']
self.controller.post(**body)
# update top net for test purpose, correct az and wrong az
TOP_NETS[0]['availability_zone_hints'] = ['b_az', 'fake_az']
self.controller.post(**body)
msg = 'Network and server not in the same availability zone'
# abort two times
calls = [mock.call(400, msg), mock.call(400, msg)]
mock_abort.assert_has_calls(calls)
@patch.object(FakeClient, 'create_servers')
@patch.object(context, 'extract_context_from_environ')
def test_post(self, mock_ctx, mock_create):
t_pod, b_pod = self._prepare_pod()
top_net_id = 'top_net_id'
top_subnet_id = 'top_subnet_id'
top_sg_id = 'top_sg_id'
t_net = {'id': top_net_id}
t_subnet = {'id': top_subnet_id,
'network_id': top_net_id,
'ip_version': 4,
'cidr': '10.0.0.0/24',
'gateway_ip': '10.0.0.1',
'allocation_pools': {'start': '10.0.0.2',
'end': '10.0.0.254'},
'enable_dhcp': True}
t_sg = {'id': top_sg_id, 'name': 'default', 'description': '',
'tenant_id': self.project_id,
'security_group_rules': [
{'remote_group_id': top_sg_id,
'direction': 'ingress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
{'remote_group_id': None,
'direction': 'egress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
]}
TOP_NETS.append(t_net)
TOP_SUBNETS.append(t_subnet)
TOP_SGS.append(t_sg)
server_name = 'test_server'
image_id = 'image_id'
flavor_id = 1
body = {
'server': {
'name': server_name,
'imageRef': image_id,
'flavorRef': flavor_id,
'availability_zone': b_pod['az_name'],
'networks': [{'uuid': top_net_id}]
}
}
mock_create.return_value = {'id': 'bottom_server_id'}
mock_ctx.return_value = self.context
server_dict = self.controller.post(**body)['server']
for port in BOTTOM_PORTS:
if 'device_id' not in port:
bottom_port_id = port['id']
for sg in BOTTOM_SGS:
if sg['name'] == top_sg_id:
bottom_sg = sg
mock_create.assert_called_with(self.context, name=server_name,
image=image_id, flavor=flavor_id,
nics=[{'port-id': bottom_port_id}],
security_groups=[bottom_sg['id']])
# make sure remote group is extended to ip addresses
for rule in bottom_sg['security_group_rules']:
if rule['ethertype'] == 'IPv4' and rule['direction'] == 'ingress':
self.assertIsNone(rule['remote_group_id'])
self.assertEqual('10.0.0.0/24', rule['remote_ip_prefix'])
with self.context.session.begin():
routes = core.query_resource(self.context, models.ResourceRouting,
[{'key': 'resource_type',
'comparator': 'eq',
'value': 'server'}], [])
self.assertEqual(1, len(routes))
self.assertEqual(server_dict['id'], routes[0]['top_id'])
self.assertEqual(server_dict['id'], routes[0]['bottom_id'])
self.assertEqual(b_pod['pod_id'], routes[0]['pod_id'])
self.assertEqual(self.project_id, routes[0]['project_id'])
# make sure security group mapping is built
routes = core.query_resource(self.context, models.ResourceRouting,
[{'key': 'resource_type',
'comparator': 'eq',
'value': 'security_group'}], [])
self.assertEqual(1, len(routes))
self.assertEqual(top_sg_id, routes[0]['top_id'])
self.assertEqual(bottom_sg['id'], routes[0]['bottom_id'])
self.assertEqual(b_pod['pod_id'], routes[0]['pod_id'])
self.assertEqual(self.project_id, routes[0]['project_id'])
@patch.object(FakeClient, 'create_servers')
@patch.object(context, 'extract_context_from_environ')
def test_post_exception_retry(self, mock_ctx, mock_server):
t_pod, b_pod = self._prepare_pod()
top_net_id = 'top_net_id'
top_subnet_id = 'top_subnet_id'
top_sg_id = 'top_sg_id'
t_net = {'id': top_net_id}
t_subnet = {'id': top_subnet_id,
'network_id': top_net_id,
'ip_version': 4,
'cidr': '10.0.0.0/24',
'gateway_ip': '10.0.0.1',
'allocation_pools': {'start': '10.0.0.2',
'end': '10.0.0.254'},
'enable_dhcp': True}
t_sg = {'id': top_sg_id, 'name': 'test_sg', 'description': '',
'tenant_id': self.project_id,
'security_group_rules': [
{'remote_group_id': None,
'direction': 'ingress',
'remote_ip_prefix': '10.0.1.0/24',
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
{'remote_group_id': None,
'direction': 'egress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
]}
TOP_NETS.append(t_net)
TOP_SUBNETS.append(t_subnet)
TOP_SGS.append(t_sg)
server_name = 'test_server'
image_id = 'image_id'
flavor_id = 1
body = {
'server': {
'name': server_name,
'imageRef': image_id,
'flavorRef': flavor_id,
'availability_zone': b_pod['az_name'],
'networks': [{'uuid': top_net_id}],
'security_groups': [{'name': 'test_sg'}]
}
}
mock_server.return_value = {'id': 'bottom_server_id'}
mock_ctx.return_value = self.context
create_security_group_rules = FakeClient.create_security_group_rules
FakeClient.create_security_group_rules = mock.Mock()
FakeClient.create_security_group_rules.side_effect = \
q_exceptions.ConnectionFailed
self.assertRaises(q_exceptions.ConnectionFailed, self.controller.post,
**body)
with self.context.session.begin():
routes = core.query_resource(
self.context, models.ResourceRouting,
[{'key': 'top_sg_id', 'comparator': 'eq',
'value': t_sg['id']},
{'key': 'pod_id', 'comparator': 'eq',
'value': 'b_pod_uuid'}], [])
self.assertIsNone(routes[0]['bottom_id'])
# test we can redo after exception
FakeClient.create_security_group_rules = create_security_group_rules
self.controller.post(**body)
for port in BOTTOM_PORTS:
if 'device_id' not in port:
bottom_port_id = port['id']
for sg in BOTTOM_SGS:
if sg['name'] == top_sg_id:
bottom_sg = sg
mock_server.assert_called_with(self.context, name=server_name,
image=image_id, flavor=flavor_id,
nics=[{'port-id': bottom_port_id}],
security_groups=[bottom_sg['id']])
@patch.object(FakeClient, 'create_servers')
@patch.object(context, 'extract_context_from_environ')
def test_post_across_pods(self, mock_ctx, mock_create):
t_pod, b_pods = self._prepare_pod(2)
b_pod1, b_pod2 = b_pods
top_net1_id = 'top_net1_id'
top_subnet1_id = 'top_subnet1_id'
top_net2_id = 'top_net2_id'
top_subnet2_id = 'top_subnet2_id'
top_sg_id = 'top_sg_id'
t_net1 = {'id': top_net1_id}
t_subnet1 = {'id': top_subnet1_id,
'tenant_id': self.project_id,
'network_id': top_net1_id,
'ip_version': 4,
'cidr': '10.0.1.0/24',
'gateway_ip': '10.0.1.1',
'allocation_pools': {'start': '10.0.1.2',
'end': '10.0.1.254'},
'enable_dhcp': True}
t_net2 = {'id': top_net2_id}
t_subnet2 = {'id': top_subnet2_id,
'tenant_id': self.project_id,
'network_id': top_net2_id,
'ip_version': 4,
'cidr': '10.0.2.0/24',
'gateway_ip': '10.0.2.1',
'allocation_pools': {'start': '10.0.2.2',
'end': '10.0.2.254'},
'enable_dhcp': True}
t_sg = {'id': top_sg_id, 'name': 'default', 'description': '',
'tenant_id': self.project_id,
'security_group_rules': [
{'remote_group_id': top_sg_id,
'direction': 'ingress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
{'remote_group_id': None,
'direction': 'egress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
]}
TOP_NETS.append(t_net1)
TOP_SUBNETS.append(t_subnet1)
TOP_NETS.append(t_net2)
TOP_SUBNETS.append(t_subnet2)
TOP_SGS.append(t_sg)
image_id = 'image_id'
flavor_id = 1
mock_ctx.return_value = self.context
body = {
'server': {
'name': 'test_server1',
'imageRef': image_id,
'flavorRef': flavor_id,
'availability_zone': b_pod1['az_name'],
'networks': [{'uuid': top_net1_id}]
}
}
mock_create.return_value = {'id': 'bottom_server1_id'}
self.controller.post(**body)['server']
body = {
'server': {
'name': 'test_server2',
'imageRef': image_id,
'flavorRef': flavor_id,
'availability_zone': b_pod2['az_name'],
'networks': [{'uuid': top_net2_id}]
}
}
mock_create.return_value = {'id': 'bottom_server2_id'}
self.controller.post(**body)['server']
for port in BOTTOM1_PORTS:
if 'device_id' not in port:
bottom_port1_id = port['id']
for port in BOTTOM2_PORTS:
if 'device_id' not in port:
bottom_port2_id = port['id']
for sg in BOTTOM1_SGS:
if sg['name'] == top_sg_id:
bottom_sg1 = sg
for sg in BOTTOM2_SGS:
if sg['name'] == top_sg_id:
bottom_sg2 = sg
calls = [mock.call(self.context, name='test_server1', image=image_id,
flavor=flavor_id,
nics=[{'port-id': bottom_port1_id}],
security_groups=[bottom_sg1['id']]),
mock.call(self.context, name='test_server2', image=image_id,
flavor=flavor_id,
nics=[{'port-id': bottom_port2_id}],
security_groups=[bottom_sg2['id']])]
mock_create.assert_has_calls(calls)
# make sure remote group is extended to ip addresses
expected_ips = ['10.0.1.0/24', '10.0.2.0/24']
ips = []
for rule in bottom_sg1['security_group_rules']:
if rule['ethertype'] == 'IPv4' and rule['direction'] == 'ingress':
self.assertIsNone(rule['remote_group_id'])
ips.append(rule['remote_ip_prefix'])
self.assertEqual(expected_ips, ips)
ips = []
for rule in bottom_sg2['security_group_rules']:
if rule['ethertype'] == 'IPv4' and rule['direction'] == 'ingress':
self.assertIsNone(rule['remote_group_id'])
ips.append(rule['remote_ip_prefix'])
self.assertEqual(expected_ips, ips)
def tearDown(self):
core.ModelBase.metadata.drop_all(core.get_engine())
for res in RES_LIST:
del res[:]