merge tests and WIP branches

This commit is contained in:
Mark McClain 2012-10-10 17:02:30 -04:00
parent f9004a6786
commit d543e7ef39
21 changed files with 1230 additions and 93 deletions

View File

@ -40,6 +40,9 @@ def update_config(host, port, config_dict):
if response.status_code != requests.codes.ok:
raise Exception('Config update failed: %s' % response.text)
else:
return response.json
def read_labels(host, port):
path = AKANDA_BASE_PATH + 'firewall/labels'

View File

@ -35,16 +35,15 @@ def generate(client, router, interfaces):
def load_provider_rules(path):
try:
return jsonutils.load(file(path))
except:
return jsonutils.load(open(path))
except: #pragma nocove
LOG.exception('unable to open provider rules: %s' % path)
def generate_network_config(client, router, interfaces):
iface_map = dict([(i['lladdr'], i['ifname']) for i in interfaces])
retval= [
retval = [
_network_config(
client,
router.external_port,
@ -72,12 +71,13 @@ def _management_network_config(port, ifname, interfaces):
for iface in interfaces:
if iface['ifname'] == ifname:
interface = iface
return _network_config_dict(iface, MANAGEMENT_NET, port.network_id)
return _make_network_config_dict(
iface, MANAGEMENT_NET, port.network_id)
def _network_config(client, port, ifname, network_type, network_ports=[]):
subnets = client.get_network_subnets(port.network_id)
return _network_config_dict(
return _make_network_config_dict(
_interface_config(ifname, port, subnets),
network_type,
port.network_id,
@ -85,10 +85,9 @@ def _network_config(client, port, ifname, network_type, network_ports=[]):
network_ports=network_ports)
def _network_config_dict(interface, network_type, network_id,
v4_conf=SERVICE_STATIC, v6_conf=SERVICE_STATIC,
subnets=[], network_ports=[]):
def _make_network_config_dict(interface, network_type, network_id,
v4_conf=SERVICE_STATIC, v6_conf=SERVICE_STATIC,
subnets=[], network_ports=[]):
return {'interface': interface,
'network_id': network_id,
'v4_conf_service': v4_conf,
@ -106,7 +105,7 @@ def _interface_config(ifname, port, subnets):
subnet_lookup[fixed.subnet_id].cidr.prefixlen)
return {'ifname': ifname,
'addresses': [fmt(fixed) for fixed in port.fixed_ips]}
'addresses': [fmt(fixed) for fixed in port.fixed_ips]}
def _subnet_config(subnet):
@ -114,7 +113,8 @@ def _subnet_config(subnet):
'cidr': str(subnet.cidr),
'dhcp_enabled': subnet.enable_dhcp,
'dns_nameservers': subnet.dns_nameservers,
'host_routes': subnet.host_routes
'host_routes': subnet.host_routes,
'gateway_ip': str(subnet.gateway_ip)
}
@ -149,13 +149,10 @@ def generate_anchor_config(client, provider_rules, router):
def generate_tenant_port_forward_anchor(client, router):
to_ip = router.external_port.first_v4
to_ip = router.external_port.first_v4 or INVALID_IP
if not to_ip:
rules = [_format_port_forward_rule(to_ip, pf)
for pf in client.get_portforwards(router.tenant_id)]
else:
rules = []
rules = [_format_port_forward_rule(to_ip, pf)
for pf in client.get_portforwards(router.tenant_id)]
return {
'name': 'tenant_v4_portforwards',
@ -164,7 +161,7 @@ def generate_tenant_port_forward_anchor(client, router):
def _format_port_forward_rule(to_ip, pf):
redirect_ip = pf.port.first_v4
redirect_ip = pf.port.first_v4 or INVALID_IP
if not redirect_ip:
return
@ -188,16 +185,12 @@ def generate_tenant_filter_rule_anchor(client, router):
}
def _format_filter_rules(rule):
def _format_filter_rule(rule):
return {
'action': rule.action,
'protocol': rule.protocol,
'source': rule.source.name if rule.source else None,
'source_port': source_port,
'source_port': rule.source_port,
'destination': rule.destination.name if rule.destination else None,
'destination_port': destination_port,
'destination_port': rule.destination_port,
}
def generate_label_config():
return dict([l.split('=', 1) for l in cfg.CONF.destination_labels])

View File

@ -24,7 +24,7 @@ class Nova(object):
flavor=self.conf.router_instance_flavor,
nics=nics)
def get_instance_id(self, router):
def get_instance(self, router):
instances = self.client.servers.list(
search_opts=dict(name='ak-' + router.id))
@ -34,22 +34,22 @@ class Nova(object):
return None
def get_router_instance_status(self, router):
instances = self.client.servers.list(
search_opts=dict(name='ak-' + router.id))
if instances:
return instances[0].status
instance = self.get_instance(router)
if instance:
return instance.status
else:
return None
def destory_router_instance(self, router):
instance_id = self.get_instance_id(router)
if instance_id:
self.client.servers.destroy(instance_id)
def destroy_router_instance(self, router):
instance = self.get_instance(router)
if instance:
self.client.servers.destroy(instance.id)
def reboot_router_instance(self, router):
instance_id = self.get_instance_id(router)
if instance_id:
self.client.servers.reboot(instance_id)
instance = self.get_instance(router)
if instance:
if instance.status != 'REBOOT':
# no need to reboot twice
self.client.servers.reboot(instance.id)
else:
self.create_router_instance(router)

View File

@ -69,7 +69,7 @@ class Subnet(object):
self.network_id = network_id
self.ip_version = ip_version
self.cidr = netaddr.IPNetwork(cidr)
self.gateway_ip = gateway_ip
self.gateway_ip = netaddr.IPAddress(gateway_ip)
self.enable_dhcp = enable_dhcp
self.dns_nameservers = dns_nameservers
self.host_routes = host_routes
@ -157,7 +157,7 @@ class FilterRule(object):
self.source = source
self.source_port = source_port
self.destination = destination
self.desination_port = destination_port
self.destination_port = destination_port
@classmethod
def from_dict(cls, d):
@ -183,7 +183,7 @@ class FilterRule(object):
class PortForward(object):
def __init__(self, id_, name, protocol, public_port, private_port, port):
self.id_ = id
self.id = id_
self.name = name
self.protocol = protocol
self.public_port = public_port
@ -201,7 +201,7 @@ class PortForward(object):
Port.from_dict(d['port']))
class AkandaClientWrapper(client.Client):
class AkandaExtClientWrapper(client.Client):
"""Add client support for Akanda Extensions. """
addressgroup_path = '/dhaddressgroup'
addressentry_path = '/dhaddressentry'
@ -209,7 +209,6 @@ class AkandaClientWrapper(client.Client):
portalias_path = '/dhportalias'
portforward_path = '/dhportforward'
# portalias crud
@client.APIParamsCall
def list_portalias(self, **params):
@ -266,7 +265,7 @@ class AkandaClientWrapper(client.Client):
class Quantum(object):
def __init__(self, conf):
self.conf = conf
self.client = AkandaClientWrapper(
self.client = AkandaExtClientWrapper(
username=conf.admin_user,
password=conf.admin_password,
tenant_name=conf.admin_tenant_name,
@ -376,15 +375,3 @@ class Quantum(object):
driver.init_l3(driver.get_device_name(port), [rug_ip])
return port
class DummyConf:
admin_user='demo'
admin_password='secret'
admin_tenant_name='demo'
auth_url='http://192.168.57.100:5000/v2.0/'
auth_strategy='keystone'
auth_region='RegionOne'
q= Quantum(DummyConf)

View File

@ -3,25 +3,20 @@ import weakref
LOG = logging.getLogger(__name__)
class RouterCache(object):
def __init__(self):
self.cache = {}
self.router_by_tenant = weakref.WeakValueDictionary()
self.router_by_tenant_network = weakref.WeakValueDictionary()
self.router_by_tenant_subnet = weakref.WeakValueDictionary()
self.router_by_port = weakref.WeakValueDictionary()
def put(self, router):
self.cache[router.id] = router
self.router_by_tenant[router.tenant_id] = router
for port in router.internal_ports:
self.router_by_port[port.id] = router
self.router_by_tenant_network[port.network_id] = router
for fixed in port.fixed_ips:
self.router_by_tenant_subnet[fixed.subnet_id] = router
def remove(self, router_id):
try:
del self.cache[router_id]

View File

@ -9,7 +9,6 @@ _HANDLER_ATTR = '_notification_handle_event_type'
class NotificationMixin(object):
def create_notification_listener(self, topic, exchange_name=None):
self._notification_handlers = {}
for method in inspect.getmembers(self, inspect.ismethod):
@ -24,7 +23,6 @@ class NotificationMixin(object):
exchange_name=exchange_name)
self.notification_connection.consume_in_thread()
def _notification_mixin_dispatcher(self, msg):
try:
handlers = self._notification_handlers.get(msg['event_type'], [])
@ -34,7 +32,7 @@ class NotificationMixin(object):
else:
if hasattr(self, 'default_notification_handler'):
self.default_notification_handler(
event_type,
msg['event_type'],
msg['_context_tenant_id'],
msg['payload'])

View File

@ -6,14 +6,14 @@ LOG = logging.getLogger(__name__)
class Task(object):
def __init__(self, method, data, max_attempts=9):
def __init__(self, method, data, max_attempts=3):
self.method = method
self.data = data
self.current = 0
self.max_attempts = max_attempts
def __call__(self):
self.current +=1
self.current += 1
self.method(self.data)
def should_retry(self):
@ -53,6 +53,8 @@ class TaskManager(object):
if task.should_retry():
self.delay_queue.put(task)
else:
LOG.error('Task Error: %s' % task)
def _requeue_failed(self):
while True:

View File

@ -10,7 +10,7 @@ from akanda.rug.common import task
from akanda.rug.api import configuration
from akanda.rug.api import nova
from akanda.rug.api import quantum
from akanda.rug.api import rest
from akanda.rug.api import akanda_client as router_api
from akanda.rug.openstack.common import cfg
from akanda.rug.openstack.common import context
from akanda.rug.openstack.common import periodic_task
@ -44,14 +44,14 @@ OPTIONS = [
# listen for Quantum notification events
cfg.StrOpt('notification_topic',
default='notifications.info',
help='Quantum notification topic name'),
default='notifications.info',
help='Quantum notification topic name'),
cfg.StrOpt('quantum_control_exchange',
default='openstack',
help='Quantum control exchange name'),
default='openstack',
help='Quantum control exchange name'),
cfg.StrOpt('control_exchange',
default='akanda',
help='Akanda control exchange name')
default='akanda',
help='Akanda control exchange name')
]
cfg.CONF.register_opts(OPTIONS)
@ -95,8 +95,8 @@ class AkandaL3Manager(notification.NotificationMixin,
@periodic_task.periodic_task(ticks_between_runs=15)
def refresh_configs(self):
LOG.debug('resync configuration state')
for rtr in self.cache.keys():
self.task_mgr.put(self.update_config, rtr.id)
for rtr_id in self.cache.keys():
self.task_mgr.put(self.update_config, rtr_id)
# notification handlers
def default_notifcation_handler(self, event_type, tenant_id, payload):
@ -117,7 +117,6 @@ class AkandaL3Manager(notification.NotificationMixin,
if rtr:
self.task_mgr.put(self.update_router, rtr.id)
@notification.handles('router.create.end')
def handle_router_create_notification(self, tenant_id, payload):
self.task_mgr.put(self._spawn_router, payload['router']['id'])
@ -140,7 +139,7 @@ class AkandaL3Manager(notification.NotificationMixin,
self.task_mgr.put(self.update_router, rtr.id)
for rtr_id in (known_routers - active_routers):
self.task_mgr.put(self.destory_router, rtr.id)
self.task_mgr.put(self.destroy_router, rtr.id)
def update_router(self, router_id):
LOG.debug('Updating router: %s' % router_id)
@ -159,7 +158,7 @@ class AkandaL3Manager(notification.NotificationMixin,
LOG.debug('Destroying router: %s' % router_id)
rtr = self.cache.get(router_id)
if rtr:
self.nova.destory_router_instance(rtr)
self.nova.destroy_router_instance(rtr)
self.cache.remove(rtr)
def reboot_router(self, router):
@ -174,26 +173,28 @@ class AkandaL3Manager(notification.NotificationMixin,
def update_config(self, router):
LOG.debug('Updating router %s config' % router.id)
interfaces = rest.get_interfaces(_get_management_address(router),
cfg.CONF.akanda_mgt_service_port)
interfaces = router_api.get_interfaces(
_get_management_address(router),
cfg.CONF.akanda_mgt_service_port)
config = configuration.generate(self.quantum, router, interfaces)
import pprint
pprint.pprint(config)
rest.update_config(_get_management_address(router),
cfg.CONF.akanda_mgt_service_port,
config)
router_api.update_config(_get_management_address(router),
cfg.CONF.akanda_mgt_service_port,
config)
LOG.debug('Router %s config updated.' % router.id)
def router_is_alive(self, router):
return rest.is_alive(_get_management_address(router),
cfg.CONF.akanda_mgt_service_port)
return router_api.is_alive(_get_management_address(router),
cfg.CONF.akanda_mgt_service_port)
def verify_router_interfaces(self, router):
try:
interfaces = rest.get_interfaces(_get_management_address(router),
cfg.CONF.akanda_mgt_service_port)
interfaces = router_api.get_interfaces(
_get_management_address(router),
cfg.CONF.akanda_mgt_service_port)
router_macs = set([iface['lladdr'] for iface in interfaces])
@ -207,8 +208,10 @@ class AkandaL3Manager(notification.NotificationMixin,
def report_bandwidth(self, router):
try:
bandwidth = rest.read_labels(_get_management_address(router),
cfg.CONF.akanda_mgt_service_port)
bandwidth = router_api.read_labels(
_get_management_address(router),
cfg.CONF.akanda_mgt_service_port)
if bandwidth:
message = {
'tenant_id': router.tenant_id,
@ -239,6 +242,7 @@ class AkandaL3Manager(notification.NotificationMixin,
return router
def _get_management_address(router):
prefix, prefix_len = cfg.CONF.management_prefix.split('/', 1)
eui = netaddr.EUI(router.management_port.mac_address)

View File

@ -9,7 +9,7 @@ from akanda.rug.openstack.common import service
cfg.CONF.register_opts([
cfg.IntOpt('periodic_interval',
default=10,
default=60,
help='seconds between running periodic tasks (ie health check)')
])

4
etc/provider_rules.json Normal file
View File

@ -0,0 +1,4 @@
{"labels": {"ext": ["192.168.57.0/24"]},
"prerules": [],
"postrules": []
}

27
etc/rug.ini Normal file
View File

@ -0,0 +1,27 @@
[DEFAULT]
admin_user=quantum
admin_password=password
admin_tenant_name=service
auth_url=http://192.168.57.100:35357/v2.0/
auth_strategy=keystone
auth_region=RegionOne
management_prefix=fdca:3ba5:a17a:acda::/64
akanda_mgt_service_port=5000
management_network_id=7860030c-fc43-45d1-88d1-5863a04e9ebf
management_subnet_id=25f3586d-fa0a-434a-8001-d4849ab514fd
external_network_id=3a9a71ce-8bea-4b6f-82ca-d74ee672895c
router_image_uuid=27334edd-49b8-4a2d-8dae-2f37af5126c7
router_instance_flavor=1
# to plug in rug interface
root_helper=sudo
interface_driver=quantum.agent.linux.interface.OVSInterfaceDriver
ovs_integration_bridge=br-int
rabbit_password = yetanothersecret
rabbit_host = 192.168.57.100
provider_rules_path=/opt/stack/akanda-rug/akanda/rug/provider_rules.json

View File

@ -11,7 +11,8 @@ setup(
license='BSD',
install_requires=[
'netaddr>=0.7.7',
'requests>=0.14.1'
'requests>=0.14.1',
'python-quantumclient>=2.1'
],
namespace_packages=['akanda'],
packages=find_packages(),

View File

View File

@ -0,0 +1,103 @@
import mock
import unittest2 as unittest
from akanda.rug.api import akanda_client
class TestAkandaClient(unittest.TestCase):
def test_mgt_url(self):
self.assertEqual('http://[fe80::2]:5000/',
akanda_client._mgt_url('fe80::2', 5000, '/'))
self.assertEqual('http://192.168.1.1:5000/',
akanda_client._mgt_url('192.168.1.1', 5000, '/'))
def test_is_alive_success(self):
with mock.patch('requests.get') as request_get:
response = mock.Mock()
response.status_code = 200
request_get.return_value = response
self.assertTrue(akanda_client.is_alive('fe80::2', 5000))
request_get.assert_called_once_with(
'http://[fe80::2]:5000/v1/firewall/labels',
timeout=1.0)
def test_is_alive_bad_status(self):
with mock.patch('requests.get') as request_get:
response = mock.Mock()
response.status_code = 500
request_get.return_value = response
self.assertFalse(akanda_client.is_alive('fe80::2', 5000))
request_get.assert_called_once_with(
'http://[fe80::2]:5000/v1/firewall/labels',
timeout=1.0)
def test_is_alive_exception(self):
with mock.patch('requests.get') as request_get:
request_get.side_effect = Exception
self.assertFalse(akanda_client.is_alive('fe80::2', 5000))
request_get.assert_called_once_with(
'http://[fe80::2]:5000/v1/firewall/labels',
timeout=1.0)
def test_get_interfaces(self):
with mock.patch('requests.get') as request_get:
response = mock.Mock()
response.status_code = 200
response.json = {'interfaces': 'the_interfaces'}
request_get.return_value = response
self.assertEqual(akanda_client.get_interfaces('fe80::2', 5000),
'the_interfaces')
request_get.assert_called_once_with(
'http://[fe80::2]:5000/v1/system/interfaces')
def test_update_config(self):
config = {'foo': 'bar'}
with mock.patch('requests.put') as request_put:
mock_response = mock.Mock()
mock_response.status_code = 200
mock_response.json = config
request_put.return_value = mock_response
resp = akanda_client.update_config('fe80::2', 5000, config)
request_put.assert_called_once_with(
'http://[fe80::2]:5000/v1/system/config',
data='{"foo": "bar"}',
headers={'Content-type': 'application/json'})
self.assertEqual(resp, config)
def test_update_config_failure(self):
config = {'foo': 'bar'}
with mock.patch('requests.put') as request_put:
mock_response = mock.Mock()
mock_response.status_code = 500
mock_response.text = 'error text'
request_put.return_value = mock_response
with self.assertRaises(Exception):
akanda_client.update_config('fe80::2', 5000, config)
request_put.assert_called_once_with(
'http://[fe80::2]:5000/v1/system/config',
data='{"foo": "bar"}',
headers={'Content-type': 'application/json'})
def test_read_labels(self):
with mock.patch('requests.post') as request_post:
mock_response = mock.Mock()
mock_response.status_code = 200
mock_response.json = {'labels': ['label1', 'label2']}
request_post.return_value = mock_response
resp = akanda_client.read_labels('fe80::2', 5000)
request_post.assert_called_once_with(
'http://[fe80::2]:5000/v1/firewall/labels')
self.assertEqual(resp, ['label1', 'label2'])

View File

@ -0,0 +1,352 @@
import mock
import netaddr
import unittest2 as unittest
from akanda.rug.api import configuration as conf_mod
from akanda.rug.openstack.common import cfg
class FakeModel(object):
def __init__(self, id_, **kwargs):
self.id = id_
self.__dict__.update(kwargs)
fake_ext_port = FakeModel(
'1',
mac_address='aa:bb:cc:dd:ee:ff',
network_id='ext-net',
fixed_ips=[FakeModel('', ip_address='9.9.9.9', subnet_id='s2')],
first_v4='9.9.9.9')
fake_mgt_port = FakeModel(
'2',
mac_address='aa:bb:cc:cc:bb:aa',
network_id='mgt-net')
fake_int_port = FakeModel(
'3',
mac_address='aa:aa:aa:aa:aa:aa',
network_id='int-net',
fixed_ips=[FakeModel('', ip_address='192.168.1.1', subnet_id='s1')])
fake_vm_port = FakeModel(
'4',
mac_address='aa:aa:aa:aa:aa:bb',
network_id='int-net',
fixed_ips=[FakeModel('', ip_address='192.168.1.2', subnet_id='s1')],
first_v4='192.168.1.2')
fake_subnet = FakeModel(
's1',
cidr=netaddr.IPNetwork('192.168.1.0/24'),
gateway_ip='192.168.1.1',
enable_dhcp=True,
dns_nameservers=['8.8.8.8'],
host_routes={})
fake_router = FakeModel(
'router_id',
tenant_id='tenant_id',
external_port=fake_ext_port,
management_port=fake_mgt_port,
internal_ports=[fake_int_port])
class TestAkandaClient(unittest.TestCase):
def setUp(self):
cfg.CONF.set_override('provider_rules_path', '/the/path')
def tearDown(self):
cfg.CONF.reset()
def test_generate(self):
methods = {
'load_provider_rules': mock.DEFAULT,
'generate_network_config': mock.DEFAULT,
'generate_address_book_config': mock.DEFAULT,
'generate_anchor_config': mock.DEFAULT
}
mock_client = mock.Mock()
ifaces = []
provider_rules = {'labels': {'ext': ['192.168.1.1']}}
with mock.patch.multiple(conf_mod, **methods) as mocks:
mocks['load_provider_rules'].return_value = provider_rules
mocks['generate_network_config'].return_value = 'network_config'
mocks['generate_address_book_config'].return_value = 'ab_config'
mocks['generate_anchor_config'].return_value = 'anchor_config'
config = conf_mod.generate(mock_client, fake_router, ifaces)
expected = {
'networks': 'network_config',
'address_book': 'ab_config',
'anchors': 'anchor_config',
'labels': {'ext': ['192.168.1.1']}}
self.assertEqual(config, expected)
mocks['load_provider_rules'].assert_called_once_with('/the/path')
mocks['generate_network_config'].assert_called_once_with(
mock_client, fake_router, ifaces)
mocks['generate_address_book_config'].assert_called_once_with(
mock_client, fake_router)
mocks['generate_anchor_config'].assert_called_once_with(
mock_client, provider_rules, fake_router)
def test_load_provider_rules(self):
rules_dict = {'labels': {}, 'preanchors': [], 'postanchors': []}
with mock.patch('akanda.rug.openstack.common.jsonutils.load') as load:
load.return_value = rules_dict
with mock.patch('__builtin__.open') as mock_open:
r = conf_mod.load_provider_rules('/the/path')
mock_open.assert_called_once_with('/the/path')
load.assert_called_once_with(mock_open.return_value)
self.assertEqual(r, rules_dict)
def test_generate_network_config(self):
methods = {
'_network_config': mock.DEFAULT,
'_management_network_config': mock.DEFAULT,
}
mock_client = mock.Mock()
ifaces = [
{'ifname': 'ge0', 'lladdr': fake_mgt_port.mac_address},
{'ifname': 'ge1', 'lladdr': fake_ext_port.mac_address},
{'ifname': 'ge2', 'lladdr': fake_int_port.mac_address}
]
with mock.patch.multiple(conf_mod, **methods) as mocks:
mocks['_network_config'].return_value = 'configured_network'
mocks['_management_network_config'].return_value = 'mgt_net'
result = conf_mod.generate_network_config(
mock_client, fake_router, ifaces)
expected = [
'configured_network',
'mgt_net',
'configured_network'
]
self.assertEqual(result, expected)
mocks['_network_config'].assert_has_calls([
mock.call(
mock_client,
fake_router.external_port,
'ge1',
'external'),
mock.call(
mock_client,
fake_int_port,
'ge2',
'internal',
mock.ANY)])
mocks['_management_network_config'].assert_called_once_with(
fake_router.management_port, 'ge0', ifaces)
def test_managment_network_config(self):
with mock.patch.object(conf_mod, '_make_network_config_dict') as nc:
interface = {
'ifname': 'ge0',
}
ifaces = [interface]
conf_mod._management_network_config(fake_mgt_port, 'ge0', ifaces)
nc.assert_called_once_with(interface, 'management', 'mgt-net')
def test_network_config(self):
subnets = [fake_subnet]
mock_client = mock.Mock()
mock_client.get_network_subnets.return_value = subnets
with mock.patch.object(conf_mod, '_make_network_config_dict') as nc:
with mock.patch.object(conf_mod, '_interface_config') as ic:
mock_interface = mock.Mock()
ic.return_value = mock_interface
conf_mod._network_config(
mock_client,
fake_int_port,
'ge1',
'internal',
[])
ic.assert_called_once_with('ge1', fake_int_port, subnets)
nc.assert_called_once_with(
mock_interface,
'internal',
'int-net',
subnets=subnets,
network_ports=[])
def test_make_network_config(self):
interface = {'ifname': 'ge2'}
result = conf_mod._make_network_config_dict(
interface,
'internal',
fake_int_port.network_id,
'dhcp',
'ra',
subnets=[fake_subnet],
network_ports=[fake_vm_port])
expected = {
'interface': interface,
'network_id': fake_int_port.network_id,
'v4_conf_service': 'dhcp',
'v6_conf_service': 'ra',
'network_type': 'internal',
'subnets': [{'cidr': '192.168.1.0/24',
'dhcp_enabled': True,
'dns_nameservers': ['8.8.8.8'],
'gateway_ip': '192.168.1.1',
'host_routes': {}}],
'allocations': [('aa:aa:aa:aa:aa:bb',
'192-168-1-2.local',
'192.168.1.2')]
}
self.assertEqual(result, expected)
def test_interface_config(self):
expected = {'addresses': ['192.168.1.1/24'], 'ifname': 'ge1'}
self.assertEqual(
expected,
conf_mod._interface_config('ge1', fake_int_port, [fake_subnet]))
def test_subnet_config(self):
expected = {
'cidr': '192.168.1.0/24',
'dhcp_enabled': True,
'dns_nameservers': ['8.8.8.8'],
'gateway_ip': '192.168.1.1',
'host_routes': {}
}
self.assertEqual(conf_mod._subnet_config(fake_subnet), expected)
def test_allocation_config(self):
expected = [('aa:aa:aa:aa:aa:bb', '192-168-1-2.local', '192.168.1.2')]
self.assertEqual(conf_mod._allocation_config([fake_vm_port]), expected)
def test_generate_address_book_config(self):
fake_address_group = FakeModel(
'g1',
name='local_net',
entries=[netaddr.IPNetwork('10.0.0.0/8')])
mock_client = mock.Mock()
mock_client.get_addressgroups.return_value = [fake_address_group]
result = conf_mod.generate_address_book_config(mock_client,
fake_router)
expected = {'local_net': ['10.0.0.0/8']}
self.assertEqual(result, expected)
def test_generate_anchor_config(self):
mock_client = mock.Mock()
provider_rules = {
'preanchors': ['pre'],
'postanchors': ['post']
}
methods = {
'generate_tenant_port_forward_anchor': mock.DEFAULT,
'generate_tenant_filter_rule_anchor': mock.DEFAULT
}
with mock.patch.multiple(conf_mod, **methods) as mocks:
mocks['generate_tenant_port_forward_anchor'].return_value = 'fwd'
mocks['generate_tenant_filter_rule_anchor'].return_value = 'filter'
result = conf_mod.generate_anchor_config(
mock_client, provider_rules, fake_router)
expected = ['pre', 'fwd', 'filter', 'post']
self.assertEqual(result, expected)
def test_generate_port_forward_anchor(self):
port_forward = FakeModel(
'pf1',
protocol='tcp',
public_port=8080,
private_port=80,
port=fake_vm_port)
mock_client = mock.Mock()
mock_client.get_portforwards.return_value = [port_forward]
result = conf_mod.generate_tenant_port_forward_anchor(
mock_client, fake_router)
expected = {
'name': 'tenant_v4_portforwards',
'rules': [
{'action': 'pass',
'family': 'inet',
'protocol': 'tcp',
'redirect': '192.168.1.2',
'redirect_port': 80,
'to': '9.9.9.9/32',
'to_port': 8080}
]
}
self.assertEqual(result, expected)
def test_generate_filter_rule_anchor(self):
dest_rule = FakeModel(
'fr1',
action='pass',
protocol='tcp',
source=None,
source_port=None,
destination=FakeModel('d1', name='webservers'),
destination_port=80)
source_rule = FakeModel(
'fr1',
action='pass',
protocol='tcp',
source=FakeModel('s1', name='home'),
source_port=None,
destination=None,
destination_port=22)
mock_client = mock.Mock()
mock_client.get_filterrules.return_value = [dest_rule, source_rule]
result = conf_mod.generate_tenant_filter_rule_anchor(
mock_client, fake_router)
expected = {
'name': 'tenant_filterrules',
'rules': [{'action': 'pass',
'destination': 'webservers',
'destination_port': 80,
'protocol': 'tcp',
'source': None,
'source_port': None},
{'action': 'pass',
'destination': None,
'destination_port': 22,
'protocol': 'tcp',
'source': 'home',
'source_port': None}
]
}
self.assertEqual(result, expected)

View File

@ -0,0 +1,165 @@
import mock
import unittest2 as unittest
from akanda.rug.api import nova
class FakeModel(object):
def __init__(self, id_, **kwargs):
self.id = id_
self.__dict__.update(kwargs)
fake_ext_port = FakeModel(
'1',
mac_address='aa:bb:cc:dd:ee:ff',
network_id='ext-net',
fixed_ips=[FakeModel('', ip_address='9.9.9.9', subnet_id='s2')])
fake_mgt_port = FakeModel(
'2',
mac_address='aa:bb:cc:cc:bb:aa',
network_id='mgt-net')
fake_int_port = FakeModel(
'3',
mac_address='aa:aa:aa:aa:aa:aa',
network_id='int-net',
fixed_ips=[FakeModel('', ip_address='192.168.1.1', subnet_id='s1')])
fake_router = FakeModel(
'router_id',
tenant_id='tenant_id',
external_port=fake_ext_port,
management_port=fake_mgt_port,
internal_ports=[fake_int_port])
class FakeConf:
admin_user='admin'
admin_password='password'
admin_tenant_name='admin'
auth_url='http://127.0.0.1/'
auth_strategy='keystone'
auth_region='RegionOne'
router_image_uuid='akanda-image'
router_instance_flavor=1
class TestNovaWrapper(unittest.TestCase):
def setUp(self):
self.addCleanup(mock.patch.stopall)
patch = mock.patch('novaclient.v1_1.client.Client')
self.client = mock.Mock()
self.client_cls = patch.start()
self.client_cls.return_value = self.client
self.nova = nova.Nova(FakeConf)
def test_create_router_instance(self):
expected = [
mock.call.servers.create(
'ak-router_id',
nics=[{'port-id': '2',
'net-id': 'mgt-net',
'v4-fixed-ip': ''},
{'port-id': '1',
'net-id': 'ext-net',
'v4-fixed-ip': ''},
{'port-id': '3',
'net-id': 'int-net',
'v4-fixed-ip': ''}],
flavor=1,
image='akanda-image'
)
]
self.nova.create_router_instance(fake_router)
self.client.assert_has_calls(expected)
def test_get_instance(self):
instance = mock.Mock()
self.client.servers.list.return_value = [instance]
expected = [
mock.call.servers.list(search_opts={'name': 'ak-router_id'})
]
result = self.nova.get_instance(fake_router)
self.client.assert_has_calls(expected)
self.assertEqual(result, instance)
def test_get_instance_not_found(self):
self.client.servers.list.return_value = []
expected = [
mock.call.servers.list(search_opts={'name': 'ak-router_id'})
]
result = self.nova.get_instance(fake_router)
self.client.assert_has_calls(expected)
self.assertIsNone(result)
def test_get_router_instance_status(self):
instance = mock.Mock()
instance.status = 'ACTIVE'
self.client.servers.list.return_value = [instance]
expected = [
mock.call.servers.list(search_opts={'name': 'ak-router_id'})
]
result = self.nova.get_router_instance_status(fake_router)
self.client.assert_has_calls(expected)
self.assertEqual(result, 'ACTIVE')
def test_get_router_instance_status_not_found(self):
self.client.servers.list.return_value = []
expected = [
mock.call.servers.list(search_opts={'name': 'ak-router_id'})
]
result = self.nova.get_router_instance_status(fake_router)
self.client.assert_has_calls(expected)
self.assertIsNone(result)
def test_destory_router_instance(self):
with mock.patch.object(self.nova, 'get_instance') as get_instance:
get_instance.return_value.id='instance_id'
expected = [
mock.call.servers.destroy('instance_id')
]
self.nova.destroy_router_instance(fake_router)
self.client.assert_has_calls(expected)
def test_reboot_router_instance_exists(self):
with mock.patch.object(self.nova, 'get_instance') as get_instance:
get_instance.return_value.id='instance_id'
get_instance.return_value.status='ACTIVE'
expected = [
mock.call.servers.reboot('instance_id')
]
self.nova.reboot_router_instance(fake_router)
self.client.assert_has_calls(expected)
def test_reboot_router_instance_rebooting(self):
with mock.patch.object(self.nova, 'get_instance') as get_instance:
get_instance.return_value.id='instance_id'
get_instance.return_value.status='REBOOT'
self.nova.reboot_router_instance(fake_router)
self.assertEqual(self.client.mock_calls, [])
def test_reboot_router_instance_missing(self):
with mock.patch.object(self.nova, 'get_instance') as get_instance:
with mock.patch.object(self.nova, 'create_router_instance') as cr:
get_instance.return_value = None
self.nova.reboot_router_instance(fake_router)
self.assertEqual(self.client.mock_calls, [])
cr.assert_called_once_with(fake_router)

View File

@ -0,0 +1,188 @@
import mock
import netaddr
import unittest2 as unittest
from akanda.rug.api import quantum
class TestQuantumModels(unittest.TestCase):
def test_router(self):
r = quantum.Router(
'1', 'tenant_id', 'name', True, 'ext', ['int'], 'mgt')
self.assertEqual(r.id, '1')
self.assertEqual(r.tenant_id, 'tenant_id')
self.assertEqual(r.name, 'name')
self.assertTrue(r.admin_state_up)
self.assertEqual(r.external_port, 'ext')
self.assertEqual(r.management_port, 'mgt')
self.assertEqual(r.internal_ports, ['int'])
def test_router_from_dict(self):
d = {
'id': '1',
'tenant_id': 'tenant_id',
'name': 'name',
'admin_state_up': True,
'external_port': 'ext',
'ports' : []
}
r = quantum.Router.from_dict(d)
self.assertEqual(r.id, '1')
self.assertEqual(r.tenant_id, 'tenant_id')
self.assertEqual(r.name, 'name')
self.assertTrue(r.admin_state_up)
self.assertEqual(r.external_port, 'ext')
def test_router_eq(self):
r1 = quantum.Router(
'1', 'tenant_id', 'name', True, 'ext', ['int'], 'mgt')
r2 = quantum.Router(
'1', 'tenant_id', 'name', True, 'ext', ['int'], 'mgt')
self.assertEqual(r1, r2)
def test_router_ne(self):
r1 = quantum.Router(
'1', 'tenant_id', 'name', True, 'ext', ['int'], 'mgt')
r2 = quantum.Router(
'2', 'tenant_id', 'name', True, 'ext', ['int'], 'mgt')
self.assertNotEqual(r1, r2)
def test_subnet_model(self):
d = {
'id': '1',
'tenant_id': 'tenant_id',
'name': 'name',
'network_id': 'network_id',
'ip_version': 6,
'cidr': 'fe80::/64',
'gateway_ip': 'fe80::1',
'enable_dhcp': True,
'dns_nameservers': ['8.8.8.8', '8.8.4.4'],
'host_routes': []
}
s = quantum.Subnet.from_dict(d)
self.assertEqual(s.id, '1')
self.assertEqual(s.tenant_id, 'tenant_id')
self.assertEqual(s.name, 'name')
self.assertEqual(s.network_id, 'network_id')
self.assertEqual(s.ip_version, 6)
self.assertEqual(s.cidr, netaddr.IPNetwork('fe80::/64'))
self.assertEqual(s.gateway_ip, netaddr.IPAddress('fe80::1'))
self.assertTrue(s.enable_dhcp, True)
self.assertEqual(s.dns_nameservers, ['8.8.8.8', '8.8.4.4'])
self.assertEqual(s.host_routes, [])
def test_port_model(self):
d = {
'id': '1',
'device_id': 'device_id',
'fixed_ips': [{'ip_address': '192.168.1.1', 'subnet_id': 'sub1'}],
'mac_address': 'aa:bb:cc:dd:ee:ff',
'network_id': 'net_id',
'device_owner': 'test'
}
p = quantum.Port.from_dict(d)
self.assertEqual(p.id, '1')
self.assertEqual(p.device_id, 'device_id')
self.assertEqual(p.mac_address, 'aa:bb:cc:dd:ee:ff')
self.assertEqual(p.device_owner, 'test')
self.assertEqual(len(p.fixed_ips), 1)
def test_fixed_ip_model(self):
d = {
'subnet_id': 'sub1',
'ip_address': '192.168.1.1'
}
fip = quantum.FixedIp.from_dict(d)
self.assertEqual(fip.subnet_id, 'sub1')
self.assertEqual(fip.ip_address, netaddr.IPAddress('192.168.1.1'))
def test_addressgroup_model(self):
d = {
'id': '1',
'name': 'group1',
'entries': [{'cidr': '192.168.1.1/24'}]
}
g = quantum.AddressGroup.from_dict(d)
self.assertEqual(g.id, '1')
self.assertEqual(g.name, 'group1')
self.assertEqual(g.entries, [netaddr.IPNetwork('192.168.1.1/24')])
def test_filterrule_model(self):
d = {
'id': '1',
'action': 'pass',
'protocol': 'tcp',
'source': {'id': '1',
'name': 'group',
'entries': [{'cidr': '192.168.1.1/24'}]},
'source_port': None,
'destination': None,
'destination_port': 80
}
r = quantum.FilterRule.from_dict(d)
self.assertEqual(r.id, '1')
self.assertEqual(r.action, 'pass')
self.assertEqual(r.protocol, 'tcp')
self.assertEqual(r.source.name, 'group')
self.assertIsNone(r.source_port)
self.assertIsNone(r.destination)
self.assertEqual(r.destination_port, 80)
def test_portforward_model(self):
p = {
'id': '1',
'device_id': 'device_id',
'fixed_ips': [{'ip_address': '192.168.1.1', 'subnet_id': 'sub1'}],
'mac_address': 'aa:bb:cc:dd:ee:ff',
'network_id': 'net_id',
'device_owner': 'test'
}
d = {
'id': '1',
'name': 'name',
'protocol': 'tcp',
'public_port': 8022,
'private_port': 22,
'port': p
}
fw = quantum.PortForward.from_dict(d)
self.assertEqual(fw.id, '1')
self.assertEqual(fw.name, 'name')
self.assertEqual(fw.protocol, 'tcp')
self.assertEqual(fw.public_port, 8022)
self.assertEqual(fw.private_port, 22)
self.assertEqual(fw.port.device_id, 'device_id')
class FakeConf:
admin_user='admin'
admin_password='password'
admin_tenant_name='admin'
auth_url='http://127.0.0.1/'
auth_strategy='keystone'
auth_region='RegionOne'
class TestAkandaClientWrapper(unittest.TestCase):
pass

View File

View File

@ -0,0 +1,98 @@
import unittest2 as unittest
from akanda.rug.common import cache
class FakeModel:
def __init__(self, id_, **kwargs):
self.id = id_
self.__dict__.update(kwargs)
def __str__(self):
return str(self.__dict__)
class TestCache(unittest.TestCase):
def test_init(self):
c = cache.RouterCache()
def test_put(self):
fake_router = FakeModel(
'the_id',
tenant_id='tenant_id',
internal_ports=[FakeModel('port_id', network_id='net1')])
c = cache.RouterCache()
c.put(fake_router)
self.assertEqual(c.cache, {'the_id': fake_router})
self.assertEqual(c.router_by_tenant.keys(), ['tenant_id'])
self.assertEqual(c.router_by_tenant_network.keys(), ['net1'])
def test_remove(self):
fake_router = FakeModel(
'the_id',
tenant_id='tenant_id',
internal_ports=[FakeModel('port_id', network_id='net1')])
c = cache.RouterCache()
c.put(fake_router)
c.remove(fake_router.id)
del fake_router
self.assertEqual(len(c.cache), 0)
self.assertEqual(len(c.router_by_tenant), 0)
self.assertEqual(len(c.router_by_tenant_network), 0)
def test_remove_nonexistent(self):
c = cache.RouterCache()
c.remove('bad_id')
self.assertEqual(len(c.cache), 0)
self.assertEqual(len(c.router_by_tenant), 0)
self.assertEqual(len(c.router_by_tenant_network), 0)
def test_get(self):
fake_router = FakeModel(
'the_id',
tenant_id='tenant_id',
internal_ports=[FakeModel('port_id', network_id='net1')])
c = cache.RouterCache()
c.put(fake_router)
self.assertEqual(c.get('the_id'), fake_router)
self.assertEqual(c.get_by_tenant_id('tenant_id'), fake_router)
def test_keys(self):
fake_router1 = FakeModel(
'the_id',
tenant_id='tenant_id',
internal_ports=[FakeModel('port_id', network_id='net1')])
fake_router2 = FakeModel(
'the_2nd',
tenant_id='tenant_id2',
internal_ports=[FakeModel('port_id', network_id='net2')])
c = cache.RouterCache()
c.put(fake_router1)
c.put(fake_router2)
self.assertItemsEqual(c.keys(), ['the_id', 'the_2nd'])
def test_routers(self):
fake_router1 = FakeModel(
'the_id',
tenant_id='tenant_id',
internal_ports=[FakeModel('port_id', network_id='net1')])
fake_router2 = FakeModel(
'the_2nd',
tenant_id='tenant_id2',
internal_ports=[FakeModel('port_id', network_id='net2')])
c = cache.RouterCache()
c.put(fake_router1)
c.put(fake_router2)
self.assertItemsEqual(c.routers(), [fake_router1, fake_router2])

View File

@ -0,0 +1,89 @@
import mock
import unittest2 as unittest
from akanda.rug.common import notification
class NotificationTest(notification.NotificationMixin):
@notification.handles('foo')
def handle_foo(self, tenant_id, payload):
pass
@notification.handles('bar')
@notification.handles('baz')
def handle_multi(self, tenant_id, payload):
pass
def default_notification_handler(self):
pass
class TestNotificationMixin(unittest.TestCase):
def test_init(self):
n = NotificationTest()
def test_create_listener(self):
with mock.patch.object(notification, 'rpc') as rpc:
n = NotificationTest()
n.create_notification_listener('the_topic', 'the_exch')
expected = [
mock.call.create_connection(new=True),
mock.call.create_connection().declare_topic_consumer(
topic='the_topic',
callback=n._notification_mixin_dispatcher,
exchange_name='the_exch'),
mock.call.create_connection().consume_in_thread()]
rpc.assert_has_calls(expected)
self.assertEqual(n._notification_handlers,
{'foo': [n.handle_foo],
'bar': [n.handle_multi],
'baz': [n.handle_multi]})
def test_dispatcher_known_event_type(self):
test_message = {
'event_type': 'foo',
'_context_tenant_id': 'tenant_id',
'payload': 'the_payload'
}
mock_handler = mock.Mock()
n = NotificationTest()
n._notification_handlers = {'foo': [mock_handler]}
n._notification_mixin_dispatcher(test_message)
mock_handler.assert_called_once_with('tenant_id', 'the_payload')
def test_dispatcher_unknown_event_type(self):
test_message = {
'event_type': 'mystery',
'_context_tenant_id': 'tenant_id',
'payload': 'the_payload'
}
n = NotificationTest()
n._notification_handlers = {}
with mock.patch.object(n, 'default_notification_handler') as dh:
n._notification_mixin_dispatcher(test_message)
dh.assert_called_once_with('mystery', 'tenant_id', 'the_payload')
def test_exception_during_dispatcher(self):
test_message = {
'event_type': 'foo',
'_context_tenant_id': 'tenant_id',
'payload': 'the_payload'
}
mock_handler = mock.Mock()
mock_handler.side_effect = Exception
n = NotificationTest()
n._notification_handlers = {'foo': [mock_handler]}
with mock.patch.object(notification, 'LOG') as log:
n._notification_mixin_dispatcher(test_message)
mock_handler.assert_called_once_with('tenant_id', 'the_payload')
log.assert_has_calls([mock.call.exception(mock.ANY)])

View File

@ -0,0 +1,128 @@
import mock
import unittest2 as unittest
from akanda.rug.common import task
class TestTask(unittest.TestCase):
def test_init(self):
t = task.Task('the_method', 'data')
self.assertEqual(t.method, 'the_method')
self.assertEqual(t.data, 'data')
self.assertEqual(t.current, 0)
self.assertEqual(t.max_attempts, 3)
def test_call(self):
method = mock.Mock()
t = task.Task(method, 'data', 3)
t()
self.assertEqual(t.current, 1)
method.assert_called_once_with('data')
def test_should_retry(self):
method = mock.Mock()
t = task.Task(method, 'data', 1)
self.assertTrue(t.should_retry())
t()
self.assertFalse(t.should_retry())
method.assert_called_once_with('data')
def test_repr(self):
method = mock.Mock()
method.__name__ = 'method'
t = task.Task(method, 'data', 1)
self.assertEqual(
repr(t),
'<Task method: method data: data attempt: 0/1 >')
class TestTaskManager(unittest.TestCase):
def test_init(self):
tm = task.TaskManager(10)
def test_put(self):
tm = task.TaskManager(10)
tm.put('method', 'data')
self.assertEqual(tm.task_queue.qsize(), 1)
qt = tm.task_queue.get()
self.assertEqual(qt.method, 'method')
self.assertEqual(qt.data, 'data')
self.assertEqual(qt.max_attempts, 3)
def test_start(self):
with mock.patch('eventlet.spawn') as spawn:
tm = task.TaskManager(10)
tm.start()
spawn.assert_has_calls([
mock.call.spawn(tm._serialized_task_runner),
mock.call.spawn(tm._requeue_failed)])
def test_task_runner(self):
tm = task.TaskManager(10)
with mock.patch.object(tm, 'task_queue') as q:
with mock.patch.object(task.LOG, 'info') as info:
info.side_effect = [None, IOError]
try:
tm._serialized_task_runner()
except IOError:
pass
q.assert_has_calls([mock.call.get(), mock.call.get()()])
def test_task_runner_exception_during_task(self):
tm = task.TaskManager(10)
mock_task = mock.Mock()
mock_task.should_retry.return_value = True
mock_task.side_effect = Exception
with mock.patch.object(tm, 'task_queue') as q:
q.get.return_value = mock_task
with mock.patch.object(task.LOG, 'info') as info:
info.side_effect = [None, IOError]
try:
tm._serialized_task_runner()
except IOError:
pass
q.assert_has_calls([mock.call.get(), mock.call.get()()])
self.assertEqual(tm.delay_queue.qsize(), 1)
def test_task_runner_exception_during_task_out_of_retries(self):
tm = task.TaskManager(10)
mock_task = mock.Mock()
mock_task.should_retry.return_value = False
mock_task.side_effect = Exception
with mock.patch.object(tm, 'task_queue') as q:
q.get.return_value = mock_task
with mock.patch.object(task.LOG, 'info') as info:
info.side_effect = [None, IOError]
with mock.patch.object(task.LOG, 'error') as error:
try:
tm._serialized_task_runner()
except IOError:
pass
q.assert_has_calls([mock.call.get(), mock.call.get()()])
self.assertEqual(tm.delay_queue.qsize(), 0)
self.assertEqual(len(error.mock_calls), 2)
def test_requeue_failed(self):
tm = task.TaskManager(10)
with mock.patch('eventlet.sleep') as sleep:
sleep.side_effect = [None, IOError]
tm.delay_queue.put(mock.Mock())
try:
tm._requeue_failed()
except IOError:
pass
self.assertEqual(tm.task_queue.qsize(), 1)
self.assertEqual(tm.delay_queue.qsize(), 0)