merge tests and WIP branches
This commit is contained in:
parent
f9004a6786
commit
d543e7ef39
|
@ -40,6 +40,9 @@ def update_config(host, port, config_dict):
|
||||||
|
|
||||||
if response.status_code != requests.codes.ok:
|
if response.status_code != requests.codes.ok:
|
||||||
raise Exception('Config update failed: %s' % response.text)
|
raise Exception('Config update failed: %s' % response.text)
|
||||||
|
else:
|
||||||
|
return response.json
|
||||||
|
|
||||||
|
|
||||||
def read_labels(host, port):
|
def read_labels(host, port):
|
||||||
path = AKANDA_BASE_PATH + 'firewall/labels'
|
path = AKANDA_BASE_PATH + 'firewall/labels'
|
|
@ -35,16 +35,15 @@ def generate(client, router, interfaces):
|
||||||
|
|
||||||
def load_provider_rules(path):
|
def load_provider_rules(path):
|
||||||
try:
|
try:
|
||||||
return jsonutils.load(file(path))
|
return jsonutils.load(open(path))
|
||||||
except:
|
except: #pragma nocove
|
||||||
LOG.exception('unable to open provider rules: %s' % path)
|
LOG.exception('unable to open provider rules: %s' % path)
|
||||||
|
|
||||||
|
|
||||||
def generate_network_config(client, router, interfaces):
|
def generate_network_config(client, router, interfaces):
|
||||||
iface_map = dict([(i['lladdr'], i['ifname']) for i in interfaces])
|
iface_map = dict([(i['lladdr'], i['ifname']) for i in interfaces])
|
||||||
|
|
||||||
|
retval = [
|
||||||
retval= [
|
|
||||||
_network_config(
|
_network_config(
|
||||||
client,
|
client,
|
||||||
router.external_port,
|
router.external_port,
|
||||||
|
@ -72,12 +71,13 @@ def _management_network_config(port, ifname, interfaces):
|
||||||
for iface in interfaces:
|
for iface in interfaces:
|
||||||
if iface['ifname'] == ifname:
|
if iface['ifname'] == ifname:
|
||||||
interface = iface
|
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=[]):
|
def _network_config(client, port, ifname, network_type, network_ports=[]):
|
||||||
subnets = client.get_network_subnets(port.network_id)
|
subnets = client.get_network_subnets(port.network_id)
|
||||||
return _network_config_dict(
|
return _make_network_config_dict(
|
||||||
_interface_config(ifname, port, subnets),
|
_interface_config(ifname, port, subnets),
|
||||||
network_type,
|
network_type,
|
||||||
port.network_id,
|
port.network_id,
|
||||||
|
@ -85,10 +85,9 @@ def _network_config(client, port, ifname, network_type, network_ports=[]):
|
||||||
network_ports=network_ports)
|
network_ports=network_ports)
|
||||||
|
|
||||||
|
|
||||||
def _network_config_dict(interface, network_type, network_id,
|
def _make_network_config_dict(interface, network_type, network_id,
|
||||||
v4_conf=SERVICE_STATIC, v6_conf=SERVICE_STATIC,
|
v4_conf=SERVICE_STATIC, v6_conf=SERVICE_STATIC,
|
||||||
subnets=[], network_ports=[]):
|
subnets=[], network_ports=[]):
|
||||||
|
|
||||||
return {'interface': interface,
|
return {'interface': interface,
|
||||||
'network_id': network_id,
|
'network_id': network_id,
|
||||||
'v4_conf_service': v4_conf,
|
'v4_conf_service': v4_conf,
|
||||||
|
@ -106,7 +105,7 @@ def _interface_config(ifname, port, subnets):
|
||||||
subnet_lookup[fixed.subnet_id].cidr.prefixlen)
|
subnet_lookup[fixed.subnet_id].cidr.prefixlen)
|
||||||
|
|
||||||
return {'ifname': ifname,
|
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):
|
def _subnet_config(subnet):
|
||||||
|
@ -114,7 +113,8 @@ def _subnet_config(subnet):
|
||||||
'cidr': str(subnet.cidr),
|
'cidr': str(subnet.cidr),
|
||||||
'dhcp_enabled': subnet.enable_dhcp,
|
'dhcp_enabled': subnet.enable_dhcp,
|
||||||
'dns_nameservers': subnet.dns_nameservers,
|
'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):
|
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)
|
||||||
rules = [_format_port_forward_rule(to_ip, pf)
|
for pf in client.get_portforwards(router.tenant_id)]
|
||||||
for pf in client.get_portforwards(router.tenant_id)]
|
|
||||||
else:
|
|
||||||
rules = []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'name': 'tenant_v4_portforwards',
|
'name': 'tenant_v4_portforwards',
|
||||||
|
@ -164,7 +161,7 @@ def generate_tenant_port_forward_anchor(client, router):
|
||||||
|
|
||||||
|
|
||||||
def _format_port_forward_rule(to_ip, pf):
|
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:
|
if not redirect_ip:
|
||||||
return
|
return
|
||||||
|
@ -188,16 +185,12 @@ def generate_tenant_filter_rule_anchor(client, router):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _format_filter_rules(rule):
|
def _format_filter_rule(rule):
|
||||||
return {
|
return {
|
||||||
'action': rule.action,
|
'action': rule.action,
|
||||||
'protocol': rule.protocol,
|
'protocol': rule.protocol,
|
||||||
'source': rule.source.name if rule.source else None,
|
'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': 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])
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ class Nova(object):
|
||||||
flavor=self.conf.router_instance_flavor,
|
flavor=self.conf.router_instance_flavor,
|
||||||
nics=nics)
|
nics=nics)
|
||||||
|
|
||||||
def get_instance_id(self, router):
|
def get_instance(self, router):
|
||||||
instances = self.client.servers.list(
|
instances = self.client.servers.list(
|
||||||
search_opts=dict(name='ak-' + router.id))
|
search_opts=dict(name='ak-' + router.id))
|
||||||
|
|
||||||
|
@ -34,22 +34,22 @@ class Nova(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_router_instance_status(self, router):
|
def get_router_instance_status(self, router):
|
||||||
instances = self.client.servers.list(
|
instance = self.get_instance(router)
|
||||||
search_opts=dict(name='ak-' + router.id))
|
if instance:
|
||||||
|
return instance.status
|
||||||
if instances:
|
|
||||||
return instances[0].status
|
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def destory_router_instance(self, router):
|
def destroy_router_instance(self, router):
|
||||||
instance_id = self.get_instance_id(router)
|
instance = self.get_instance(router)
|
||||||
if instance_id:
|
if instance:
|
||||||
self.client.servers.destroy(instance_id)
|
self.client.servers.destroy(instance.id)
|
||||||
|
|
||||||
def reboot_router_instance(self, router):
|
def reboot_router_instance(self, router):
|
||||||
instance_id = self.get_instance_id(router)
|
instance = self.get_instance(router)
|
||||||
if instance_id:
|
if instance:
|
||||||
self.client.servers.reboot(instance_id)
|
if instance.status != 'REBOOT':
|
||||||
|
# no need to reboot twice
|
||||||
|
self.client.servers.reboot(instance.id)
|
||||||
else:
|
else:
|
||||||
self.create_router_instance(router)
|
self.create_router_instance(router)
|
||||||
|
|
|
@ -69,7 +69,7 @@ class Subnet(object):
|
||||||
self.network_id = network_id
|
self.network_id = network_id
|
||||||
self.ip_version = ip_version
|
self.ip_version = ip_version
|
||||||
self.cidr = netaddr.IPNetwork(cidr)
|
self.cidr = netaddr.IPNetwork(cidr)
|
||||||
self.gateway_ip = gateway_ip
|
self.gateway_ip = netaddr.IPAddress(gateway_ip)
|
||||||
self.enable_dhcp = enable_dhcp
|
self.enable_dhcp = enable_dhcp
|
||||||
self.dns_nameservers = dns_nameservers
|
self.dns_nameservers = dns_nameservers
|
||||||
self.host_routes = host_routes
|
self.host_routes = host_routes
|
||||||
|
@ -157,7 +157,7 @@ class FilterRule(object):
|
||||||
self.source = source
|
self.source = source
|
||||||
self.source_port = source_port
|
self.source_port = source_port
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
self.desination_port = destination_port
|
self.destination_port = destination_port
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, d):
|
def from_dict(cls, d):
|
||||||
|
@ -183,7 +183,7 @@ class FilterRule(object):
|
||||||
|
|
||||||
class PortForward(object):
|
class PortForward(object):
|
||||||
def __init__(self, id_, name, protocol, public_port, private_port, port):
|
def __init__(self, id_, name, protocol, public_port, private_port, port):
|
||||||
self.id_ = id
|
self.id = id_
|
||||||
self.name = name
|
self.name = name
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
self.public_port = public_port
|
self.public_port = public_port
|
||||||
|
@ -201,7 +201,7 @@ class PortForward(object):
|
||||||
Port.from_dict(d['port']))
|
Port.from_dict(d['port']))
|
||||||
|
|
||||||
|
|
||||||
class AkandaClientWrapper(client.Client):
|
class AkandaExtClientWrapper(client.Client):
|
||||||
"""Add client support for Akanda Extensions. """
|
"""Add client support for Akanda Extensions. """
|
||||||
addressgroup_path = '/dhaddressgroup'
|
addressgroup_path = '/dhaddressgroup'
|
||||||
addressentry_path = '/dhaddressentry'
|
addressentry_path = '/dhaddressentry'
|
||||||
|
@ -209,7 +209,6 @@ class AkandaClientWrapper(client.Client):
|
||||||
portalias_path = '/dhportalias'
|
portalias_path = '/dhportalias'
|
||||||
portforward_path = '/dhportforward'
|
portforward_path = '/dhportforward'
|
||||||
|
|
||||||
|
|
||||||
# portalias crud
|
# portalias crud
|
||||||
@client.APIParamsCall
|
@client.APIParamsCall
|
||||||
def list_portalias(self, **params):
|
def list_portalias(self, **params):
|
||||||
|
@ -266,7 +265,7 @@ class AkandaClientWrapper(client.Client):
|
||||||
class Quantum(object):
|
class Quantum(object):
|
||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
self.client = AkandaClientWrapper(
|
self.client = AkandaExtClientWrapper(
|
||||||
username=conf.admin_user,
|
username=conf.admin_user,
|
||||||
password=conf.admin_password,
|
password=conf.admin_password,
|
||||||
tenant_name=conf.admin_tenant_name,
|
tenant_name=conf.admin_tenant_name,
|
||||||
|
@ -376,15 +375,3 @@ class Quantum(object):
|
||||||
driver.init_l3(driver.get_device_name(port), [rug_ip])
|
driver.init_l3(driver.get_device_name(port), [rug_ip])
|
||||||
|
|
||||||
return port
|
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)
|
|
||||||
|
|
||||||
|
|
|
@ -3,25 +3,20 @@ import weakref
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RouterCache(object):
|
class RouterCache(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
self.router_by_tenant = weakref.WeakValueDictionary()
|
self.router_by_tenant = weakref.WeakValueDictionary()
|
||||||
self.router_by_tenant_network = 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):
|
def put(self, router):
|
||||||
self.cache[router.id] = router
|
self.cache[router.id] = router
|
||||||
self.router_by_tenant[router.tenant_id] = router
|
self.router_by_tenant[router.tenant_id] = router
|
||||||
|
|
||||||
for port in router.internal_ports:
|
for port in router.internal_ports:
|
||||||
self.router_by_port[port.id] = router
|
|
||||||
self.router_by_tenant_network[port.network_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):
|
def remove(self, router_id):
|
||||||
try:
|
try:
|
||||||
del self.cache[router_id]
|
del self.cache[router_id]
|
||||||
|
|
|
@ -9,7 +9,6 @@ _HANDLER_ATTR = '_notification_handle_event_type'
|
||||||
|
|
||||||
|
|
||||||
class NotificationMixin(object):
|
class NotificationMixin(object):
|
||||||
|
|
||||||
def create_notification_listener(self, topic, exchange_name=None):
|
def create_notification_listener(self, topic, exchange_name=None):
|
||||||
self._notification_handlers = {}
|
self._notification_handlers = {}
|
||||||
for method in inspect.getmembers(self, inspect.ismethod):
|
for method in inspect.getmembers(self, inspect.ismethod):
|
||||||
|
@ -24,7 +23,6 @@ class NotificationMixin(object):
|
||||||
exchange_name=exchange_name)
|
exchange_name=exchange_name)
|
||||||
self.notification_connection.consume_in_thread()
|
self.notification_connection.consume_in_thread()
|
||||||
|
|
||||||
|
|
||||||
def _notification_mixin_dispatcher(self, msg):
|
def _notification_mixin_dispatcher(self, msg):
|
||||||
try:
|
try:
|
||||||
handlers = self._notification_handlers.get(msg['event_type'], [])
|
handlers = self._notification_handlers.get(msg['event_type'], [])
|
||||||
|
@ -34,7 +32,7 @@ class NotificationMixin(object):
|
||||||
else:
|
else:
|
||||||
if hasattr(self, 'default_notification_handler'):
|
if hasattr(self, 'default_notification_handler'):
|
||||||
self.default_notification_handler(
|
self.default_notification_handler(
|
||||||
event_type,
|
msg['event_type'],
|
||||||
msg['_context_tenant_id'],
|
msg['_context_tenant_id'],
|
||||||
msg['payload'])
|
msg['payload'])
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,14 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Task(object):
|
class Task(object):
|
||||||
def __init__(self, method, data, max_attempts=9):
|
def __init__(self, method, data, max_attempts=3):
|
||||||
self.method = method
|
self.method = method
|
||||||
self.data = data
|
self.data = data
|
||||||
self.current = 0
|
self.current = 0
|
||||||
self.max_attempts = max_attempts
|
self.max_attempts = max_attempts
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
self.current +=1
|
self.current += 1
|
||||||
self.method(self.data)
|
self.method(self.data)
|
||||||
|
|
||||||
def should_retry(self):
|
def should_retry(self):
|
||||||
|
@ -53,6 +53,8 @@ class TaskManager(object):
|
||||||
|
|
||||||
if task.should_retry():
|
if task.should_retry():
|
||||||
self.delay_queue.put(task)
|
self.delay_queue.put(task)
|
||||||
|
else:
|
||||||
|
LOG.error('Task Error: %s' % task)
|
||||||
|
|
||||||
def _requeue_failed(self):
|
def _requeue_failed(self):
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -10,7 +10,7 @@ from akanda.rug.common import task
|
||||||
from akanda.rug.api import configuration
|
from akanda.rug.api import configuration
|
||||||
from akanda.rug.api import nova
|
from akanda.rug.api import nova
|
||||||
from akanda.rug.api import quantum
|
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 cfg
|
||||||
from akanda.rug.openstack.common import context
|
from akanda.rug.openstack.common import context
|
||||||
from akanda.rug.openstack.common import periodic_task
|
from akanda.rug.openstack.common import periodic_task
|
||||||
|
@ -44,14 +44,14 @@ OPTIONS = [
|
||||||
|
|
||||||
# listen for Quantum notification events
|
# listen for Quantum notification events
|
||||||
cfg.StrOpt('notification_topic',
|
cfg.StrOpt('notification_topic',
|
||||||
default='notifications.info',
|
default='notifications.info',
|
||||||
help='Quantum notification topic name'),
|
help='Quantum notification topic name'),
|
||||||
cfg.StrOpt('quantum_control_exchange',
|
cfg.StrOpt('quantum_control_exchange',
|
||||||
default='openstack',
|
default='openstack',
|
||||||
help='Quantum control exchange name'),
|
help='Quantum control exchange name'),
|
||||||
cfg.StrOpt('control_exchange',
|
cfg.StrOpt('control_exchange',
|
||||||
default='akanda',
|
default='akanda',
|
||||||
help='Akanda control exchange name')
|
help='Akanda control exchange name')
|
||||||
]
|
]
|
||||||
|
|
||||||
cfg.CONF.register_opts(OPTIONS)
|
cfg.CONF.register_opts(OPTIONS)
|
||||||
|
@ -95,8 +95,8 @@ class AkandaL3Manager(notification.NotificationMixin,
|
||||||
@periodic_task.periodic_task(ticks_between_runs=15)
|
@periodic_task.periodic_task(ticks_between_runs=15)
|
||||||
def refresh_configs(self):
|
def refresh_configs(self):
|
||||||
LOG.debug('resync configuration state')
|
LOG.debug('resync configuration state')
|
||||||
for rtr in self.cache.keys():
|
for rtr_id in self.cache.keys():
|
||||||
self.task_mgr.put(self.update_config, rtr.id)
|
self.task_mgr.put(self.update_config, rtr_id)
|
||||||
|
|
||||||
# notification handlers
|
# notification handlers
|
||||||
def default_notifcation_handler(self, event_type, tenant_id, payload):
|
def default_notifcation_handler(self, event_type, tenant_id, payload):
|
||||||
|
@ -117,7 +117,6 @@ class AkandaL3Manager(notification.NotificationMixin,
|
||||||
if rtr:
|
if rtr:
|
||||||
self.task_mgr.put(self.update_router, rtr.id)
|
self.task_mgr.put(self.update_router, rtr.id)
|
||||||
|
|
||||||
|
|
||||||
@notification.handles('router.create.end')
|
@notification.handles('router.create.end')
|
||||||
def handle_router_create_notification(self, tenant_id, payload):
|
def handle_router_create_notification(self, tenant_id, payload):
|
||||||
self.task_mgr.put(self._spawn_router, payload['router']['id'])
|
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)
|
self.task_mgr.put(self.update_router, rtr.id)
|
||||||
|
|
||||||
for rtr_id in (known_routers - active_routers):
|
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):
|
def update_router(self, router_id):
|
||||||
LOG.debug('Updating router: %s' % router_id)
|
LOG.debug('Updating router: %s' % router_id)
|
||||||
|
@ -159,7 +158,7 @@ class AkandaL3Manager(notification.NotificationMixin,
|
||||||
LOG.debug('Destroying router: %s' % router_id)
|
LOG.debug('Destroying router: %s' % router_id)
|
||||||
rtr = self.cache.get(router_id)
|
rtr = self.cache.get(router_id)
|
||||||
if rtr:
|
if rtr:
|
||||||
self.nova.destory_router_instance(rtr)
|
self.nova.destroy_router_instance(rtr)
|
||||||
self.cache.remove(rtr)
|
self.cache.remove(rtr)
|
||||||
|
|
||||||
def reboot_router(self, router):
|
def reboot_router(self, router):
|
||||||
|
@ -174,26 +173,28 @@ class AkandaL3Manager(notification.NotificationMixin,
|
||||||
|
|
||||||
def update_config(self, router):
|
def update_config(self, router):
|
||||||
LOG.debug('Updating router %s config' % router.id)
|
LOG.debug('Updating router %s config' % router.id)
|
||||||
interfaces = rest.get_interfaces(_get_management_address(router),
|
interfaces = router_api.get_interfaces(
|
||||||
cfg.CONF.akanda_mgt_service_port)
|
_get_management_address(router),
|
||||||
|
cfg.CONF.akanda_mgt_service_port)
|
||||||
|
|
||||||
config = configuration.generate(self.quantum, router, interfaces)
|
config = configuration.generate(self.quantum, router, interfaces)
|
||||||
import pprint
|
import pprint
|
||||||
pprint.pprint(config)
|
pprint.pprint(config)
|
||||||
|
|
||||||
rest.update_config(_get_management_address(router),
|
router_api.update_config(_get_management_address(router),
|
||||||
cfg.CONF.akanda_mgt_service_port,
|
cfg.CONF.akanda_mgt_service_port,
|
||||||
config)
|
config)
|
||||||
LOG.debug('Router %s config updated.' % router.id)
|
LOG.debug('Router %s config updated.' % router.id)
|
||||||
|
|
||||||
def router_is_alive(self, router):
|
def router_is_alive(self, router):
|
||||||
return rest.is_alive(_get_management_address(router),
|
return router_api.is_alive(_get_management_address(router),
|
||||||
cfg.CONF.akanda_mgt_service_port)
|
cfg.CONF.akanda_mgt_service_port)
|
||||||
|
|
||||||
def verify_router_interfaces(self, router):
|
def verify_router_interfaces(self, router):
|
||||||
try:
|
try:
|
||||||
interfaces = rest.get_interfaces(_get_management_address(router),
|
interfaces = router_api.get_interfaces(
|
||||||
cfg.CONF.akanda_mgt_service_port)
|
_get_management_address(router),
|
||||||
|
cfg.CONF.akanda_mgt_service_port)
|
||||||
|
|
||||||
router_macs = set([iface['lladdr'] for iface in interfaces])
|
router_macs = set([iface['lladdr'] for iface in interfaces])
|
||||||
|
|
||||||
|
@ -207,8 +208,10 @@ class AkandaL3Manager(notification.NotificationMixin,
|
||||||
|
|
||||||
def report_bandwidth(self, router):
|
def report_bandwidth(self, router):
|
||||||
try:
|
try:
|
||||||
bandwidth = rest.read_labels(_get_management_address(router),
|
bandwidth = router_api.read_labels(
|
||||||
cfg.CONF.akanda_mgt_service_port)
|
_get_management_address(router),
|
||||||
|
cfg.CONF.akanda_mgt_service_port)
|
||||||
|
|
||||||
if bandwidth:
|
if bandwidth:
|
||||||
message = {
|
message = {
|
||||||
'tenant_id': router.tenant_id,
|
'tenant_id': router.tenant_id,
|
||||||
|
@ -239,6 +242,7 @@ class AkandaL3Manager(notification.NotificationMixin,
|
||||||
|
|
||||||
return router
|
return router
|
||||||
|
|
||||||
|
|
||||||
def _get_management_address(router):
|
def _get_management_address(router):
|
||||||
prefix, prefix_len = cfg.CONF.management_prefix.split('/', 1)
|
prefix, prefix_len = cfg.CONF.management_prefix.split('/', 1)
|
||||||
eui = netaddr.EUI(router.management_port.mac_address)
|
eui = netaddr.EUI(router.management_port.mac_address)
|
||||||
|
|
|
@ -9,7 +9,7 @@ from akanda.rug.openstack.common import service
|
||||||
|
|
||||||
cfg.CONF.register_opts([
|
cfg.CONF.register_opts([
|
||||||
cfg.IntOpt('periodic_interval',
|
cfg.IntOpt('periodic_interval',
|
||||||
default=10,
|
default=60,
|
||||||
help='seconds between running periodic tasks (ie health check)')
|
help='seconds between running periodic tasks (ie health check)')
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{"labels": {"ext": ["192.168.57.0/24"]},
|
||||||
|
"prerules": [],
|
||||||
|
"postrules": []
|
||||||
|
}
|
|
@ -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
|
3
setup.py
3
setup.py
|
@ -11,7 +11,8 @@ setup(
|
||||||
license='BSD',
|
license='BSD',
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'netaddr>=0.7.7',
|
'netaddr>=0.7.7',
|
||||||
'requests>=0.14.1'
|
'requests>=0.14.1',
|
||||||
|
'python-quantumclient>=2.1'
|
||||||
],
|
],
|
||||||
namespace_packages=['akanda'],
|
namespace_packages=['akanda'],
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
|
|
|
@ -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'])
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -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])
|
|
@ -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)])
|
|
@ -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)
|
Loading…
Reference in New Issue