From 0914c953f45ee19139ac6dcd022a6e14b026fa5b Mon Sep 17 00:00:00 2001 From: Anonymous Date: Wed, 6 May 2015 09:09:06 +0000 Subject: [PATCH] Move remaining SNAT code from VNC to SVC Since this commit 4baf65c347ce13e34c500205f1d8c43b95d9da9 the SNAT code has moved from the VNC to SVC. however there were some remaining code related to the route table/interfaces association. This patch moves these remaining codes. This patch also introduces an agent manager which handles and forward some evens during the service instance creation. The goal of this refactoring is to move the specific pieces of code that are present in the instance_manager closer to their related agent. Unit-tests have been added for the snat_agent. Closes-Bug: #1460540 Change-Id: I19502bfa77e4567946e9062be27f97e8766c5c84 --- src/config/svc-monitor/SConscript | 9 +- src/config/svc-monitor/svc_monitor/agent.py | 40 ++ .../svc-monitor/svc_monitor/agent_manager.py | 36 ++ .../svc-monitor/svc_monitor/config_db.py | 1 + .../svc_monitor/instance_manager.py | 73 +-- .../svc_monitor/loadbalancer_agent.py | 40 +- .../svc-monitor/svc_monitor/snat_agent.py | 212 +++++--- .../svc-monitor/svc_monitor/svc_monitor.py | 29 +- .../svc_monitor/tests/test_snat.py | 24 +- .../svc_monitor/tests/test_snat_agent.py | 480 ++++++++++++++++++ src/config/svc-monitor/test-requirements.txt | 1 + .../vnc_openstack/neutron_plugin_db.py | 53 +- 12 files changed, 799 insertions(+), 199 deletions(-) create mode 100644 src/config/svc-monitor/svc_monitor/agent.py create mode 100644 src/config/svc-monitor/svc_monitor/agent_manager.py create mode 100644 src/config/svc-monitor/svc_monitor/tests/test_snat_agent.py diff --git a/src/config/svc-monitor/SConscript b/src/config/svc-monitor/SConscript index 01f88a3deca..df77146c7e8 100644 --- a/src/config/svc-monitor/SConscript +++ b/src/config/svc-monitor/SConscript @@ -2,7 +2,7 @@ # # Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. -# +# import os Import('CfgmEnv') @@ -17,6 +17,8 @@ sources = [ '.coveragerc', 'MANIFEST.in', 'svc_monitor/__init__.py', + 'svc_monitor/agent.py', + 'svc_monitor/agent_manager.py', 'svc_monitor/svc_monitor.py', 'svc_monitor/instance_manager.py', 'svc_monitor/virtual_machine_manager.py', @@ -42,6 +44,7 @@ sources = [ 'svc_monitor/tests/test_virtual_machine_manager.py', 'svc_monitor/tests/test_dep_track.py', 'svc_monitor/tests/test_snat.py', + 'svc_monitor/tests/test_snat_agent.py', 'svc_monitor/tests/scheduler/__init__.py', 'svc_monitor/tests/scheduler/test_vrouter_schedulers.py', ] @@ -81,8 +84,8 @@ if 'install' in BUILD_TARGETS: env.Alias('install', env.Install(env['INSTALL_CONF'], 'contrail-svc-monitor.conf')) env.Alias('install', env.InstallAs( env['INSTALL_INITD'] + '/contrail-svc-monitor', 'contrail-svc-monitor.initd.supervisord')) -env.Alias('install', env.Install(env['INSTALL_CONF']+ - '/supervisord_config_files', 'contrail-svc-monitor.ini')) +env.Alias('install', env.Install(env['INSTALL_CONF']+ + '/supervisord_config_files', 'contrail-svc-monitor.ini')) buildspace_link = os.environ.get('CONTRAIL_REPO') if buildspace_link: diff --git a/src/config/svc-monitor/svc_monitor/agent.py b/src/config/svc-monitor/svc_monitor/agent.py new file mode 100644 index 00000000000..fc27eb41cb7 --- /dev/null +++ b/src/config/svc-monitor/svc_monitor/agent.py @@ -0,0 +1,40 @@ +# Copyright (c) 2015 Redhat +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Sylvain Afchain + +import abc + + +class Agent(object): + __metaclass__ = abc.ABCMeta + + def __init__(self, svc_mon, vnc_lib, cassandra, config_section): + self._vnc_lib = vnc_lib + self._svc_mon = svc_mon + self._cassandra = cassandra + self._args = config_section + + @abc.abstractmethod + def handle_service_type(self): + pass + + # method called just before creation of vm and vmi + def pre_create_service_vm(self, instance_index, si, st, vm): + pass + + # method called just after creation of vm and vmi + def post_create_service_vm(self, instance_index, si, st, vm): + pass diff --git a/src/config/svc-monitor/svc_monitor/agent_manager.py b/src/config/svc-monitor/svc_monitor/agent_manager.py new file mode 100644 index 00000000000..5736b7b1e2c --- /dev/null +++ b/src/config/svc-monitor/svc_monitor/agent_manager.py @@ -0,0 +1,36 @@ +# Copyright (c) 2015 Redhat +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Sylvain Afchain + + +class AgentManager(object): + + def __init__(self): + self._agents = {} + + def register_agent(self, agent): + type = agent.handle_service_type() + self._agents[type] = agent + + def pre_create_service_vm(self, instance_index, si, st, vm): + agent = self._agents.get(st.params.get('service_type')) + if agent: + agent.pre_create_service_vm(instance_index, si, st, vm) + + def post_create_service_vm(self, instance_index, si, st, vm): + agent = self._agents.get(st.params.get('service_type')) + if agent: + agent.post_create_service_vm(instance_index, si, st, vm) diff --git a/src/config/svc-monitor/svc_monitor/config_db.py b/src/config/svc-monitor/svc_monitor/config_db.py index 35b80dd9571..78ca3fffdda 100644 --- a/src/config/svc-monitor/svc_monitor/config_db.py +++ b/src/config/svc-monitor/svc_monitor/config_db.py @@ -828,6 +828,7 @@ def __init__(self, uuid, obj_dict=None): self.service_instance = None self.virtual_network = None self.virtual_machine_interfaces = set() + self.last_virtual_machine_interfaces = set() self.update(obj_dict) # end __init__ diff --git a/src/config/svc-monitor/svc_monitor/instance_manager.py b/src/config/svc-monitor/svc_monitor/instance_manager.py index 0e89b12592b..b9a5010c4ee 100644 --- a/src/config/svc-monitor/svc_monitor/instance_manager.py +++ b/src/config/svc-monitor/svc_monitor/instance_manager.py @@ -30,11 +30,12 @@ class InstanceManager(object): def __init__(self, vnc_lib, db, logger, vrouter_scheduler, - nova_client, args=None): + nova_client, agent_manager, args=None): self.logger = logger self._vnc_lib = vnc_lib self._args = args self._nc = nova_client + self._agent_manager = agent_manager self.vrouter_scheduler = vrouter_scheduler @abc.abstractmethod @@ -87,7 +88,7 @@ def _get_project_obj(self, proj_fq_name): break if not proj_obj: self.logger.log_error("%s project not found" % - (proj_fq_name.join(':'))) + (':'.join(proj_fq_name))) return proj_obj def _allocate_iip(self, vn_obj, iip_name): @@ -239,18 +240,7 @@ def validate_network_config(self, st, si): user_visible = True itf_type = st_if.get('service_interface_type') vn_fq_str = si_if.get('virtual_network', None) - if (itf_type == svc_info.get_left_if_str() and - (st.params.get('service_type') == - svc_info.get_snat_service_type())): - vn_id = self._create_snat_vn(si, vn_fq_str, index) - user_visible = False - elif (itf_type == svc_info.get_right_if_str() and - (st.params.get('service_type') == - svc_info.get_lb_service_type())): - iip_id, vn_id = self._get_vip_vmi_iip(si) - nic['iip-id'] = iip_id - user_visible = False - elif not vn_fq_str or vn_fq_str == '': + if not vn_fq_str: vn_id = self._check_create_service_vn(itf_type, si) else: try: @@ -259,7 +249,7 @@ def validate_network_config(self, st, si): except NoIdError: config_complete = False - nic['type'] = st_if.get('service_interface_type') + nic['type'] = itf_type nic['index'] = str(index + 1) nic['net-id'] = vn_id nic['shared-ip'] = st_if.get('shared_ip') @@ -298,6 +288,9 @@ def cleanup_svc_vm_ports(self, vmi_list, port_delete=True): pass def _check_create_netns_vm(self, instance_index, si, st, vm): + # notify all the agents + self._agent_manager.pre_create_service_vm(instance_index, si, st, vm) + instance_name = self._get_instance_name(si, instance_index) vm_obj = VirtualMachine(instance_name) vm_obj.set_display_name(instance_name + '__' + st.virtualization_type) @@ -319,6 +312,9 @@ def _check_create_netns_vm(self, instance_index, si, st, vm): local_preference=si.local_preference[instance_index], vm_obj=vm_obj) + # notify all the agents + self._agent_manager.post_create_service_vm(instance_index, si, st, vm) + return vm def _create_svc_vm_port(self, nic, instance_name, si, st, @@ -343,7 +339,7 @@ def _create_svc_vm_port(self, nic, instance_name, si, st, vmi_create = False vmi_updated = False if_properties = None - + port_name = ('__').join([instance_name, nic['type'], nic['index']]) port_fq_name = proj_obj.fq_name + [port_name] vmi_obj = VirtualMachineInterface(parent_obj=proj_obj, name=port_name) @@ -595,51 +591,6 @@ def create_service(self, st, si): status='CREATE', vms=instances, st_name=(':').join(st.fq_name)) - def _create_snat_vn(self, si, vn_fq_str, index): - vn_name = '%s_%s' % (svc_info.get_snat_left_vn_prefix(), - si.name) - vn_fq_name = si.fq_name[:-1] + [vn_name] - try: - vn_id = self._vnc_lib.fq_name_to_id( - 'virtual-network', vn_fq_name) - except NoIdError: - snat_subnet = svc_info.get_snat_left_subnet() - vn_id = self.create_service_vn(vn_name, snat_subnet, - si.fq_name[:-1], user_visible=False) - - if vn_fq_str != ':'.join(vn_fq_name): - si_obj = ServiceInstance() - si_obj.uuid = si.uuid - si_obj.fq_name = si.fq_name - si_props = ServiceInstanceType(**si.params) - left_if = ServiceInstanceInterfaceType( - virtual_network=':'.join(vn_fq_name)) - si_props.insert_interface_list(index, left_if) - si_obj.set_service_instance_properties(si_props) - self._vnc_lib.service_instance_update(si_obj) - self.logger.log_info("SI %s updated with left vn %s" % - (si_obj.get_fq_name_str(), vn_fq_str)) - - return vn_id - - def _get_vip_vmi_iip(self, si): - if not si.loadbalancer_pool: - return None, None - - pool = LoadbalancerPoolSM.get(si.loadbalancer_pool) - if not pool.virtual_ip: - return None, None - - vip = VirtualIpSM.get(pool.virtual_ip) - if not vip.virtual_machine_interface: - return None, None - - vmi = VirtualMachineInterfaceSM.get(vip.virtual_machine_interface) - if not vmi.instance_ip or not vmi.virtual_network: - return None, None - - return vmi.instance_ip, vmi.virtual_network - def add_fip_to_vip_vmi(self, vmi, fip): iip = InstanceIpSM.get(vmi.instance_ip) if not iip: diff --git a/src/config/svc-monitor/svc_monitor/loadbalancer_agent.py b/src/config/svc-monitor/svc_monitor/loadbalancer_agent.py index a61ec2a684e..7dacf04a3c4 100644 --- a/src/config/svc-monitor/svc_monitor/loadbalancer_agent.py +++ b/src/config/svc-monitor/svc_monitor/loadbalancer_agent.py @@ -2,14 +2,20 @@ from cfgm_common import importutils from cfgm_common import exceptions as vnc_exc +from cfgm_common import svc_info + +from agent import Agent from config_db import ServiceApplianceSM, ServiceApplianceSetSM, \ - LoadbalancerPoolSM, InstanceIpSM, VirtualMachineInterfaceSM + LoadbalancerPoolSM, InstanceIpSM, VirtualMachineInterfaceSM, \ + VirtualIpSM -class LoadbalancerAgent(object): +class LoadbalancerAgent(Agent): - def __init__(self, svc_mon, vnc_lib, config_section): + def __init__(self, svc_mon, vnc_lib, cassandra, config_section): # Loadbalancer + super(LoadbalancerAgent, self).__init__(svc_mon, vnc_lib, + cassandra, config_section) self._vnc_lib = vnc_lib self._svc_mon = svc_mon self._cassandra = self._svc_mon._cassandra @@ -24,6 +30,34 @@ def __init__(self, svc_mon, vnc_lib, config_section): self._default_provider = "opencontrail" # end __init__ + def handle_service_type(self): + return svc_info.get_lb_service_type() + + def pre_create_service_vm(self, instance_index, si, st, vm): + for nic in si.vn_info: + if nic['type'] == svc_info.get_right_if_str(): + iip_id, vn_id = self._get_vip_vmi_iip(si) + nic['iip-id'] = iip_id + nic['user-visible'] = False + + def _get_vip_vmi_iip(self, si): + if not si.loadbalancer_pool: + return None, None + + pool = LoadbalancerPoolSM.get(si.loadbalancer_pool) + if not pool.virtual_ip: + return None, None + + vip = VirtualIpSM.get(pool.virtual_ip) + if not vip.virtual_machine_interface: + return None, None + + vmi = VirtualMachineInterfaceSM.get(vip.virtual_machine_interface) + if not vmi.instance_ip or not vmi.virtual_network: + return None, None + + return vmi.instance_ip, vmi.virtual_network + # create default loadbalancer driver def _create_default_service_appliance_set(self, sa_set_name, driver_name): default_gsc_name = "default-global-system-config" diff --git a/src/config/svc-monitor/svc_monitor/snat_agent.py b/src/config/svc-monitor/svc_monitor/snat_agent.py index e3985bbd088..a922f4e2cb7 100644 --- a/src/config/svc-monitor/svc_monitor/snat_agent.py +++ b/src/config/svc-monitor/svc_monitor/snat_agent.py @@ -1,16 +1,19 @@ +import copy + from vnc_api.vnc_api import * +from agent import Agent from cfgm_common import exceptions as vnc_exc -from config_db import VirtualNetworkSM, LogicalRouterSM, VirtualMachineInterfaceSM, ServiceInstanceSM, ServiceTemplateSM +from cfgm_common import svc_info +from config_db import VirtualNetworkSM, LogicalRouterSM, \ + VirtualMachineInterfaceSM, ServiceInstanceSM, ServiceTemplateSM, \ + ProjectSM + SNAT_SERVICE_TEMPLATE_FQ_NAME = ['default-domain', 'netns-snat-template'] -class SNATAgent(object): - def __init__(self, svc_mon, vnc_lib): - self._vnc_lib = vnc_lib - self._svc_mon = svc_mon - # end __init__ +class SNATAgent(Agent): def audit_snat_instances(self): for lr in LogicalRouterSM.values(): @@ -26,16 +29,124 @@ def audit_snat_instances(self): self.cleanup_snat_instance(lr_uuid, si.uuid) # end audit_snat_instances + def handle_service_type(self): + return svc_info.get_snat_service_type() + + def pre_create_service_vm(self, instance_index, si, st, vm): + for nic in si.vn_info: + if nic['type'] == svc_info.get_left_if_str(): + nic['user-visible'] = False + + def _create_snat_vn(self, si_obj, vn_name): + snat_subnet = svc_info.get_snat_left_subnet() + self._svc_mon.netns_manager.create_service_vn( + vn_name, snat_subnet, si_obj.fq_name[:-1], + user_visible=False) + + def _get_snat_vn(self, project_obj, si_obj): + vn_name = '%s_%s' % (svc_info.get_snat_left_vn_prefix(), + si_obj.name) + vn_fq_name = si_obj.fq_name[:-1] + [vn_name] + try: + self._cassandra.fq_name_to_uuid('virtual-network', vn_fq_name) + except NoIdError: + self._create_snat_vn(si_obj, vn_name) + + return ':'.join(vn_fq_name) + def update_snat_instance(self, router_obj): - if router_obj.virtual_network: + if (router_obj.virtual_network and + router_obj.virtual_machine_interfaces): if router_obj.service_instance is None: - self.add_snat_instance(router_obj) + self._add_snat_instance(router_obj) + else: + self._update_snat_instance(router_obj) else: if router_obj.service_instance: - self.delete_snat_instance(router_obj) + self._delete_snat_instance(router_obj) + + router_obj.last_virtual_machine_interfaces = copy.copy( + router_obj.virtual_machine_interfaces) + return router_obj # end update_snat_instance - - def add_snat_instance(self, router_obj): + + def _diff_virtual_interfaces(self, router_obj): + uuids = set([uuid for uuid in + router_obj.virtual_machine_interfaces]) + + to_del = router_obj.last_virtual_machine_interfaces - uuids + to_add = uuids - router_obj.last_virtual_machine_interfaces + + return to_del, to_add + + def _get_net_uuids(self, vmi_uuids): + return [ + VirtualMachineInterfaceSM.get(uuid).virtual_network + for uuid in vmi_uuids] + + def _virtual_network_read(self, net_uuid): + (ok, result) = self._cassandra._cassandra_virtual_network_read( + [net_uuid]) + if not ok: + return + return VirtualNetwork.from_dict(**result[0]) + + def _add_route_table(self, net_uuid, rt_obj): + net_obj = self._virtual_network_read(net_uuid) + if not net_obj: + return + net_obj.set_route_table(rt_obj) + self._vnc_lib.virtual_network_update(net_obj) + + def _add_route_tables(self, net_uuids, rt_obj): + for net_uuid in net_uuids: + self._add_route_table(net_uuid, rt_obj) + + def _del_route_table(self, net_uuid, rt_obj): + net_obj = self._virtual_network_read(net_uuid) + if not net_obj: + return + net_obj.del_route_table(rt_obj) + self._vnc_lib.virtual_network_update(net_obj) + + def _del_route_tables(self, net_uuids, rt_obj): + for net_uuid in net_uuids: + self._del_route_table(net_uuid, rt_obj) + + def _update_snat_instance(self, router_obj): + to_del, to_add = self._diff_virtual_interfaces(router_obj) + if to_del or to_add: + project_obj = ProjectSM.get(router_obj.parent_uuid) + + rt_obj = self._get_route_table(router_obj, project_obj) + if not rt_obj: + return + + if to_add: + net_uuids = self._get_net_uuids(to_add) + self._add_route_tables(net_uuids, rt_obj) + + if to_del: + net_uuids = self._get_net_uuids(to_del) + self._del_route_tables(net_uuids, rt_obj) + + def _get_route_table(self, router_obj, project_obj): + rt_name = 'rt_' + router_obj.uuid + rt_fq_name = project_obj.fq_name + [rt_name] + try: + rt_uuid = self._cassandra.fq_name_to_uuid('route-table', + rt_fq_name) + except NoIdError: + return + + (ok, result) = self._cassandra._cassandra_route_table_read( + [rt_uuid]) + if not ok: + return + + return RouteTable.from_dict(**result[0]) + + def _add_snat_instance(self, router_obj): try: vnc_rtr_obj = self._vnc_lib.logical_router_read(id=router_obj.uuid) except vnc_exc.NoIdError: @@ -61,18 +172,11 @@ def add_snat_instance(self, router_obj): si_fq_name = project_obj.fq_name + [si_name] try: si_obj = self._vnc_lib.service_instance_read(fq_name=si_fq_name) - si_uuid = si_obj.uuid except vnc_exc.NoIdError: si_obj = None # Get route table for default route it it exists - rt_name = 'rt_' + router_obj.uuid - rt_fq_name = project_obj.fq_name + [rt_name] - try: - rt_obj = self._vnc_lib.route_table_read(fq_name=rt_fq_name) - rt_uuid = rt_obj.uuid - except vnc_exc.NoIdError: - rt_obj = None + rt_obj = self._get_route_table(router_obj, project_obj) # Set the service instance si_created = False @@ -85,7 +189,8 @@ def add_snat_instance(self, router_obj): auto_policy=True) # set right interface in order of [right, left] to match template - left_if = ServiceInstanceInterfaceType() + vn_left_fq_name = self._get_snat_vn(project_obj, si_obj) + left_if = ServiceInstanceInterfaceType(virtual_network=vn_left_fq_name) virtual_network = router_obj.virtual_network vn_obj = VirtualNetworkSM.get(virtual_network) right_if = ServiceInstanceInterfaceType( @@ -95,8 +200,9 @@ def add_snat_instance(self, router_obj): si_obj.set_service_instance_properties(si_prop_obj) si_obj.set_service_template(st_obj) + if si_created: - si_uuid = self._vnc_lib.service_instance_create(si_obj) + self._vnc_lib.service_instance_create(si_obj) else: self._vnc_lib.service_instance_update(si_obj) @@ -105,32 +211,26 @@ def add_snat_instance(self, router_obj): next_hop=si_obj.get_fq_name_str()) rt_created = False if not rt_obj: + rt_name = 'rt_' + router_obj.uuid rt_obj = RouteTable(name=rt_name, parent_obj=project_obj) rt_created = True rt_obj.set_routes(RouteTableType.factory([route_obj])) if rt_created: - rt_uuid = self._vnc_lib.route_table_create(rt_obj) + self._vnc_lib.route_table_create(rt_obj) else: self._vnc_lib.route_table_update(rt_obj) # Associate route table to all private networks connected onto # that router - for intf in router_obj.virtual_machine_interfaces or []: - vmi_obj = VirtualMachineInterfaceSM.locate(intf) - net_id = vmi_obj.virtual_network - try: - net_obj = self._vnc_lib.virtual_network_read(id=net_id) - except vnc_exc.NoIdError: - continue - net_obj.set_route_table(rt_obj) - self._vnc_lib.virtual_network_update(net_obj) + net_uuids = self._get_net_uuids(router_obj.virtual_machine_interfaces) + self._add_route_tables(net_uuids, rt_obj) # Add logical gateway virtual network vnc_rtr_obj.set_service_instance(si_obj) self._vnc_lib.logical_router_update(vnc_rtr_obj) # end add_snat_instance - def delete_snat_instance(self, router_obj): + def _delete_snat_instance(self, router_obj): try: vnc_rtr_obj = self._vnc_lib.logical_router_read(id=router_obj.uuid) except vnc_exc.NoIdError: @@ -152,26 +252,17 @@ def delete_snat_instance(self, router_obj): si_obj = None # Get route table for default route it it exists - rt_name = 'rt_' + router_obj.uuid - rt_fq_name = project_obj.get_fq_name() + [rt_name] - try: - rt_obj = self._vnc_lib.route_table_read(fq_name=rt_fq_name) - rt_uuid = rt_obj.uuid - except vnc_exc.NoIdError: - rt_obj = None + rt_obj = self._get_route_table(router_obj, project_obj) # Delete route table if rt_obj: - # Disassociate route table to all private networks connected - # onto that router - for net_ref in rt_obj.get_virtual_network_back_refs() or []: - try: - net_obj = self._vnc_lib.virtual_network_read( - id=net_ref['uuid']) - except vnc_exc.NoIdError: - continue - net_obj.del_route_table(rt_obj) - self._vnc_lib.virtual_network_update(net_obj) + if (hasattr(rt_obj, 'virtual_network_back_refs') and + rt_obj.virtual_network_back_refs): + # Disassociate route table to all private networks connected + # onto that router + uuids = [ref['uuid'] for ref in + rt_obj.virtual_network_back_refs] + self._del_route_tables(uuids, rt_obj) self._vnc_lib.route_table_delete(id=rt_obj.uuid) if vnc_rtr_obj: @@ -188,11 +279,6 @@ def delete_snat_instance(self, router_obj): # end delete_snat_instance def cleanup_snat_instance(self, lr_id, si_id): - try: - vnc_rtr_obj = self._vnc_lib.logical_router_read(id=lr_id) - except vnc_exc.NoIdError: - vnc_rtr_obj = None - # Get the service instance if it exists try: si_obj = self._vnc_lib.service_instance_read(id=si_id) @@ -204,7 +290,6 @@ def cleanup_snat_instance(self, lr_id, si_id): rt_fq_name = si_obj.get_fq_name()[0:2] + [rt_name] try: rt_obj = self._vnc_lib.route_table_read(fq_name=rt_fq_name) - rt_uuid = rt_obj.uuid except vnc_exc.NoIdError: rt_obj = None @@ -212,18 +297,11 @@ def cleanup_snat_instance(self, lr_id, si_id): if rt_obj: # Disassociate route table to all private networks connected # onto that router - for net_ref in rt_obj.get_virtual_network_back_refs() or []: - try: - net_obj = self._vnc_lib.virtual_network_read( - id=net_ref['uuid']) - except vnc_exc.NoIdError: - continue - net_obj.del_route_table(rt_obj) - self._vnc_lib.virtual_network_update(net_obj) + uuids = [ref['uuid'] for ref in + rt_obj.virtual_network_back_refs] + self._del_route_tables(uuids, rt_obj) self._vnc_lib.route_table_delete(id=rt_obj.uuid) + # Delete service instance - if si_obj: - self._vnc_lib.service_instance_delete(id=si_id) + self._vnc_lib.service_instance_delete(id=si_id) # end cleanup_snat_instance - - diff --git a/src/config/svc-monitor/svc_monitor/svc_monitor.py b/src/config/svc-monitor/svc_monitor/svc_monitor.py index b6c50704b7b..f71f080011d 100644 --- a/src/config/svc-monitor/svc_monitor/svc_monitor.py +++ b/src/config/svc-monitor/svc_monitor/svc_monitor.py @@ -44,6 +44,7 @@ import discoveryclient.client as client +from agent_manager import AgentManager from db import ServiceMonitorDB from logger import ServiceMonitorLogger from loadbalancer_agent import LoadbalancerAgent @@ -130,6 +131,9 @@ class SvcMonitor(object): "project": { 'self': [], }, + "logical_router": { + 'self': [], + }, } def __init__(self, args=None): @@ -295,6 +299,9 @@ def post_init(self, vnc_lib, args=None): 'svc_monitor.nova_client.ServiceMonitorNovaClient', self._args, self.logger) + # agent manager + self._agent_manager = AgentManager() + # load vrouter scheduler self.vrouter_scheduler = importutils.import_object( self._args.si_netns_scheduler_driver, @@ -305,23 +312,32 @@ def post_init(self, vnc_lib, args=None): self.vm_manager = importutils.import_object( 'svc_monitor.virtual_machine_manager.VirtualMachineManager', self._vnc_lib, self._cassandra, self.logger, - self.vrouter_scheduler, self._nova_client, self._args) + self.vrouter_scheduler, self._nova_client, self._agent_manager, + self._args) # load network namespace instance manager self.netns_manager = importutils.import_object( 'svc_monitor.instance_manager.NetworkNamespaceManager', self._vnc_lib, self._cassandra, self.logger, - self.vrouter_scheduler, self._nova_client, self._args) + self.vrouter_scheduler, self._nova_client, self._agent_manager, + self._args) # load a vrouter instance manager self.vrouter_manager = importutils.import_object( 'svc_monitor.vrouter_instance_manager.VRouterInstanceManager', self._vnc_lib, self._cassandra, self.logger, - self.vrouter_scheduler, self._nova_client, self._args) + self.vrouter_scheduler, self._nova_client, + self._agent_manager, self._args) # load a loadbalancer agent - self.loadbalancer_agent = LoadbalancerAgent(self, self._vnc_lib, self._args) - self.snat_agent = SNATAgent(self, self._vnc_lib) + self.loadbalancer_agent = LoadbalancerAgent(self, self._vnc_lib, + self._cassandra, self._args) + self._agent_manager.register_agent(self.loadbalancer_agent) + + # load a snat agent + self.snat_agent = SNATAgent(self, self._vnc_lib, + self._cassandra, self._args) + self._agent_manager.register_agent(self.snat_agent) # Read the cassandra and populate the entry in ServiceMonitor DB self.sync_sm() @@ -567,13 +583,12 @@ def sync_sm(self): continue self.check_link_si_to_vm(vm, vmi) - ok, lr_list = self._cassandra._cassandra_logical_router_list() if not ok: pass else: for fq_name, uuid in lr_list: - lr = LogicalRouterSM.locate(uuid) + LogicalRouterSM.locate(uuid) # Load the loadbalancer driver self.loadbalancer_agent.load_drivers() diff --git a/src/config/svc-monitor/svc_monitor/tests/test_snat.py b/src/config/svc-monitor/svc_monitor/tests/test_snat.py index d8c342b1dfd..95ccb869c1c 100644 --- a/src/config/svc-monitor/svc_monitor/tests/test_snat.py +++ b/src/config/svc-monitor/svc_monitor/tests/test_snat.py @@ -98,38 +98,41 @@ def vr_read(vm_id): self.mocked_args = mock.MagicMock() self.mocked_args.availability_zone = None + self.mocked_manager = mock.MagicMock() + self.netns_manager = NetworkNamespaceManager( db=self.mocked_db, logger=mock.MagicMock(), vnc_lib=self.mocked_vnc, vrouter_scheduler=mock.MagicMock(), - nova_client=self.nova_mock, args=self.mocked_args) + nova_client=self.nova_mock, agent_manager=self.mocked_manager, + args=self.mocked_args) def tearDown(self): ServiceTemplateSM.delete('fake-st-uuid') ServiceInstanceSM.delete('fake-si-uuid') pass - def create_test_project(self, fq_name_str): + def _create_test_project(self, fq_name_str): proj_obj = {} proj_obj['fq_name'] = fq_name_str.split(':') proj_obj['uuid'] = fq_name_str proj_obj['id_perms'] = 'fake-id-perms' ProjectSM.locate(proj_obj['uuid'], proj_obj) - def create_test_virtual_network(self, fq_name_str): + def _create_test_virtual_network(self, fq_name_str): vn_obj = {} vn_obj['fq_name'] = fq_name_str.split(':') vn_obj['uuid'] = fq_name_str vn_obj['id_perms'] = 'fake-id-perms' VirtualNetworkSM.locate(vn_obj['uuid'], vn_obj) - def create_test_security_group(self, fq_name_str): + def _create_test_security_group(self, fq_name_str): sg_obj = {} sg_obj['fq_name'] = fq_name_str.split(':') sg_obj['uuid'] = fq_name_str sg_obj['id_perms'] = 'fake-id-perms' SecurityGroupSM.locate(sg_obj['uuid'], sg_obj) - def create_test_virtual_machine(self, fq_name_str): + def _create_test_virtual_machine(self, fq_name_str): vm_obj = {} vm_obj['fq_name'] = fq_name_str.split(':') vm_obj['uuid'] = fq_name_str @@ -139,9 +142,10 @@ def create_test_virtual_machine(self, fq_name_str): return vm def test_snat_instance_create(self): - self.create_test_project('fake-domain:fake-project') - self.create_test_virtual_network('fake-domain:fake-project:public-vn') - self.create_test_security_group('fake-domain:fake-project:default') + self._create_test_project('fake-domain:fake-project') + self._create_test_virtual_network('fake-domain:fake-project:public-vn') + self._create_test_virtual_network('fake-domain:fake-project:fake-vn-uuid') + self._create_test_security_group('fake-domain:fake-project:default') st_obj = {} st_obj['fq_name'] = ['fake-domain', 'fake-snat-template'] @@ -164,7 +168,7 @@ def test_snat_instance_create(self): si_props = {} si_props['scale_out'] = {'max_instances': 2} si_props['interface_list'] = [{'virtual_network': 'fake-domain:fake-project:public-vn'}, - {'virtual_network': ''}] + {'virtual_network': 'fake-domain:fake-project:fake-vn-uuid'}] si_obj['service_instance_properties'] = si_props st = ServiceTemplateSM.locate('fake-st-uuid', st_obj) @@ -173,7 +177,7 @@ def test_snat_instance_create(self): self.netns_manager.create_service(st, si) self.mocked_vnc.virtual_machine_create.assert_any_call(VMObjMatcher(1)) self.mocked_vnc.virtual_machine_create.assert_any_call(VMObjMatcher(2)) - self.assertEqual(si.vn_info[1]['net-id'], 'fake-vn-uuid') + self.assertEqual(si.vn_info[1]['net-id'], 'fake-domain:fake-project:fake-vn-uuid') def test_snat_instance_delete(self): def create_fake_virtual_machine(fq_name_str): diff --git a/src/config/svc-monitor/svc_monitor/tests/test_snat_agent.py b/src/config/svc-monitor/svc_monitor/tests/test_snat_agent.py new file mode 100644 index 00000000000..8b09d15e9f5 --- /dev/null +++ b/src/config/svc-monitor/svc_monitor/tests/test_snat_agent.py @@ -0,0 +1,480 @@ +import copy +import json +import mock +import unittest + +from cfgm_common.vnc_db import DBBase +from svc_monitor import config_db +from svc_monitor import instance_manager +from svc_monitor import snat_agent +from vnc_api.vnc_api import * + + +ROUTER_1 = { + 'fq_name': ['default-domain', 'demo', 'router1'], + 'parent_uuid': 'a8d55cfc-c66a-4eeb-82f8-d144fe74c46b', + 'uuid': '8e9b4859-d4c2-4ed5-9468-4809b1a926f3', + 'virtual_machine_interface_refs': [ + {'attr': None, + 'to': ['default-domain', + 'demo', + 'ccc56e1c-a749-4147-9f2f-a04b32bd658d'], + 'uuid': 'ccc56e1c-a749-4147-9f2f-a04b32bd658d'}], + 'virtual_network_refs': [ + {'attr': None, + 'to': ['default-domain', 'demo', 'public'], + 'uuid': '4f60e029-6029-4039-ad3a-4e4eb0e89407'}] +} + + +class RouteTableMatcher(object): + + def __init__(self, prefix): + self._prefix = prefix + + def __eq__(self, rt_obj): + prefix = rt_obj.get_routes().route[0].prefix + return self._prefix == prefix + + +class VirtualNetworkRouteTableMatcher(object): + + def __init__(self, net_name, rt_name): + self._net_name = net_name + self._rt_name = rt_name + + def __eq__(self, net_obj): + if self._rt_name: + return (net_obj.fq_name[-1] == self._net_name and + net_obj.get_route_table_refs()[0]['to'][-1] == + self._rt_name) + else: + return (net_obj.fq_name[-1] == self._net_name and + not net_obj.get_route_table_refs()) + + +class VirtualNetworkMatcher(object): + + def __init__(self, name, user_visible): + self._name = name + self._user_visible = user_visible + + def __eq__(self, net_obj): + user_visible = net_obj.get_id_perms().get_user_visible() + return (net_obj.get_fq_name_str() == self._name and + user_visible == self._user_visible) + + +class ServiceInstanceMatcher(object): + + def __init__(self, name, left, right, mode): + self._name = name + self._left = left + self._right = right + self._mode = mode + + def __eq__(self, si_obj): + si_props = si_obj.get_service_instance_properties() + mode = si_props.get_ha_mode() + right = si_props.get_interface_list()[0].get_virtual_network() + left = si_props.get_interface_list()[1].get_virtual_network() + + return (si_obj.fq_name[-1] == self._name and + mode == self._mode and + left == self._left and + right == self._right) + + +class LogicalRouterMatcher(object): + + def __init__(self, si_name): + self._si_name = si_name + + def __eq__(self, rtr_obj): + if self._si_name: + return (rtr_obj.get_service_instance_refs()[0]['to'][-1] == + self._si_name) + else: + return not rtr_obj.get_service_instance_refs() + + +class SnatAgentTest(unittest.TestCase): + + def setUp(self): + self.vnc_lib = mock.Mock() + + self.cassandra = mock.Mock() + self.logger = mock.Mock() + + self.svc = mock.Mock() + self.svc.netns_manager = instance_manager.NetworkNamespaceManager( + self.vnc_lib, self.cassandra, self.logger, None, None, None) + + self.snat_agent = snat_agent.SNATAgent(self.svc, self.vnc_lib, + self.cassandra, None) + DBBase.init(self, None, self.cassandra) + + # register the project + proj_fq_name = ['default-domain', 'demo'] + config_db.ProjectSM.locate( + ROUTER_1['parent_uuid'], + {'fq_name': proj_fq_name}) + + project = Project(name=proj_fq_name[-1]) + self.vnc_lib.project_read = mock.Mock( + return_value=project) + + # register the public network + config_db.VirtualNetworkSM.locate( + ROUTER_1['virtual_network_refs'][0]['uuid'], + {'fq_name': ROUTER_1['virtual_network_refs'][0]['to']}) + + # register interfaces + config_db.VirtualMachineInterfaceSM.locate( + ROUTER_1['virtual_machine_interface_refs'][0]['uuid'], + {'fq_name': ROUTER_1['virtual_machine_interface_refs'][0]['to'], + 'virtual_network_refs': [{'uuid': 'private1-uuid'}]}) + + def tearDown(self): + config_db.LogicalRouterSM.reset() + config_db.VirtualNetworkSM.reset() + config_db.VirtualMachineInterfaceSM.reset() + + def obj_to_dict(self, obj): + def to_json(obj): + if hasattr(obj, 'serialize_to_json'): + return obj.serialize_to_json(obj.get_pending_updates()) + else: + return dict((k, v) for k, v in obj.__dict__.iteritems()) + + return json.loads(json.dumps(obj, default=to_json)) + + def test_gateway_set(self): + router_obj = LogicalRouter('rtr1-name') + self.vnc_lib.logical_router_read = mock.Mock(return_value=router_obj) + + # will return the private virtual network, and will return + # an error when trying to read the service snat VN + def vn_read_side_effect(uuids): + if 'private1-uuid' in uuids: + return (True, [{'fq_name': ['default-domain', + 'demo', + 'private1-name'], + 'uuid': 'private1-uuid'}]) + elif 'snat-vn-uuid' in uuids: + return (True, [{'fq_name': ['default-domain', + 'demo', + 'snat-si-left_si_' + ROUTER_1['uuid']], + 'uuid': 'snat-vn-uuid'}]) + return (False, None) + + self.cassandra._cassandra_virtual_network_read = mock.Mock( + side_effect=vn_read_side_effect) + + def vn_create_side_effect(vn_obj): + if vn_obj.name == ('snat-si-left_si_' + ROUTER_1['uuid']): + vn_obj.uuid = 'snat-vn-uuid' + + self.vnc_lib.virtual_network_create = mock.Mock( + side_effect=vn_create_side_effect) + + self.vnc_lib.service_instance_read = mock.Mock(return_value=None) + self.vnc_lib.route_table_read = mock.Mock(return_value=None) + + def no_id_side_effect(type, fq_name): + if type == 'network-ipam': + return 'fake-uuid' + + raise NoIdError("xxx") + + self.cassandra.fq_name_to_uuid = mock.Mock(side_effect=no_id_side_effect) + + self.vnc_lib.route_table_create = mock.Mock() + self.vnc_lib.virtual_network_update = mock.Mock() + + router = config_db.LogicalRouterSM.locate(ROUTER_1['uuid'], + ROUTER_1) + self.snat_agent.update_snat_instance(router) + + # check that the correct private network is read + self.cassandra._cassandra_virtual_network_read.assert_called_with( + ['private1-uuid']) + + # check that the snat service network is created + left = ('default-domain:demo:snat-si-left_si_' + + ROUTER_1['uuid']) + self.vnc_lib.virtual_network_create.assert_called_with( + VirtualNetworkMatcher(left, False)) + + # route table that is going to be set for each interface + self.vnc_lib.route_table_create.assert_called_with( + RouteTableMatcher('0.0.0.0/0')) + + # check that the route table is applied to the network + rt_name = 'rt_' + ROUTER_1['uuid'] + self.vnc_lib.virtual_network_update.assert_called_with( + VirtualNetworkRouteTableMatcher('private1-name', rt_name)) + + # check that the SI is correctly set with the right interfaces + right = ':'.join(ROUTER_1['virtual_network_refs'][0]['to']) + self.vnc_lib.service_instance_create.assert_called_with( + ServiceInstanceMatcher('si_' + ROUTER_1['uuid'], + left, right, 'active-standby')) + + # check that the SI created is set to the logical router + self.vnc_lib.logical_router_update.assert_called_with( + LogicalRouterMatcher('si_' + ROUTER_1['uuid'])) + + def _test_snat_delete(self, router_dict): + self.test_gateway_set() + + # reset all calls + self.vnc_lib.virtual_network_update.reset_mock() + self.vnc_lib.service_instance_read.reset_mock() + + # now we have a route table + def no_id_side_effect(type, fq_name): + if type == 'route-table': + return 'fake-uuid' + + raise NoIdError("xxx") + + self.cassandra.fq_name_to_uuid = mock.Mock( + side_effect=no_id_side_effect) + + si_obj = ServiceInstance('si_' + ROUTER_1['uuid']) + si_obj.set_uuid('si-uuid') + self.vnc_lib.service_instance_read.return_value = si_obj + + # get and use the route table previously created + rt_obj = self.vnc_lib.route_table_create.mock_calls[0][1][0] + rt_dict = self.obj_to_dict(rt_obj) + rt_dict['virtual_network_back_refs'] = [{'uuid': 'private1-uuid'}] + self.cassandra._cassandra_route_table_read = mock.Mock( + return_value=(True, [rt_dict])) + + router = config_db.LogicalRouterSM.locate(ROUTER_1['uuid']) + router.update(router_dict) + self.snat_agent.update_snat_instance(router) + + self.vnc_lib.service_instance_read.assert_called_with( + fq_name=router_dict['service_instance_refs'][0]['to']) + + # check that the route table is removed from the networks + self.vnc_lib.virtual_network_update.assert_called_with( + VirtualNetworkRouteTableMatcher('private1-name', None)) + + # check that the route table is deleted + self.vnc_lib.route_table_delete.assert_called_with(id=rt_obj.uuid) + + # check that the SI is removed from the logical router + self.vnc_lib.logical_router_update.assert_called_with( + LogicalRouterMatcher(None)) + + # check that the SI is beeing to be deleted + self.vnc_lib.service_instance_delete.assert_called_with( + id=si_obj.get_uuid()) + + # Test cleanup of snat SI created without internat interfaces + def test_snat_delete_without_rt_net_back_refs(self): + self.test_gateway_set() + + # reset all calls + self.vnc_lib.virtual_network_update.reset_mock() + self.vnc_lib.service_instance_read.reset_mock() + + # now we have a route table + def no_id_side_effect(type, fq_name): + if type == 'route-table': + return 'fake-uuid' + + raise NoIdError("xxx") + + self.cassandra.fq_name_to_uuid = mock.Mock( + side_effect=no_id_side_effect) + + si_obj = ServiceInstance('si_' + ROUTER_1['uuid']) + si_obj.set_uuid('si-uuid') + self.vnc_lib.service_instance_read.return_value = si_obj + + # get and use the route table previously created, but without + # any back_refs to virtual networks + rt_obj = self.vnc_lib.route_table_create.mock_calls[0][1][0] + rt_dict = self.obj_to_dict(rt_obj) + self.cassandra._cassandra_route_table_read = mock.Mock( + return_value=(True, [rt_dict])) + + router_dict = copy.deepcopy(ROUTER_1) + router_dict['virtual_machine_interface_refs'] = [] + router_dict['service_instance_refs'] = [ + {'to': ['default-domain', + 'demo', + 'si_' + ROUTER_1['uuid']], + 'uuid': 'si-uuid'}] + + router = config_db.LogicalRouterSM.locate(ROUTER_1['uuid']) + router.update(router_dict) + self.snat_agent.update_snat_instance(router) + + self.vnc_lib.service_instance_read.assert_called_with( + fq_name=router_dict['service_instance_refs'][0]['to']) + + # check that the route table is deleted + self.vnc_lib.route_table_delete.assert_called_with(id=rt_obj.uuid) + + # check that the SI is removed from the logical router + self.vnc_lib.logical_router_update.assert_called_with( + LogicalRouterMatcher(None)) + + # check that the SI is beeing to be deleted + self.vnc_lib.service_instance_delete.assert_called_with( + id=si_obj.get_uuid()) + + def test_gateway_clear(self): + + # update the previous router by removing the ext net + router_dict = copy.deepcopy(ROUTER_1) + router_dict['virtual_network_refs'] = [] + router_dict['service_instance_refs'] = [ + {'to': ['default-domain', + 'demo', + 'si_' + ROUTER_1['uuid']], + 'uuid': 'si-uuid'}] + + self._test_snat_delete(router_dict) + + def test_no_more_interface(self): + + # update the previous router by removing all the interfaces + router_dict = copy.deepcopy(ROUTER_1) + router_dict['virtual_machine_interface_refs'] = [] + router_dict['service_instance_refs'] = [ + {'to': ['default-domain', + 'demo', + 'si_' + ROUTER_1['uuid']], + 'uuid': 'si-uuid'}] + + self._test_snat_delete(router_dict) + + def test_add_interface(self): + self.test_gateway_set() + + # update the previous router by removing all the interfaces + router_dict = copy.deepcopy(ROUTER_1) + router_dict['virtual_machine_interface_refs'].append( + {'attr': None, + 'to': ['default-domain', + 'demo', + 'd0022578-5b16-4da8-bd4d-5760faf134dc'], + 'uuid': 'd0022578-5b16-4da8-bd4d-5760faf134dc'}) + router_dict['service_instance_refs'] = [ + {'to': ['default-domain', + 'demo', + 'si_' + ROUTER_1['uuid']], + 'uuid': 'si-uuid'}] + + config_db.VirtualMachineInterfaceSM.locate( + router_dict['virtual_machine_interface_refs'][1]['uuid'], + {'fq_name': router_dict['virtual_machine_interface_refs'][1]['to'], + 'virtual_network_refs': [{'uuid': 'private2-uuid'}]}) + + # reset all calls + self.vnc_lib.virtual_network_update.reset_mock() + self.vnc_lib.service_instance_read.reset_mock() + self.vnc_lib.virtual_network_read.reset_mock() + + # now we have a route table + def no_id_side_effect(type, fq_name): + if type == 'route-table': + return 'fake-uuid' + + raise NoIdError("xxx") + + self.cassandra.fq_name_to_uuid = mock.Mock( + side_effect=no_id_side_effect) + + # get and use the route table previously created + rt_obj = self.vnc_lib.route_table_create.mock_calls[0][1][0] + rt_dict = self.obj_to_dict(rt_obj) + rt_dict['virtual_network_back_refs'] = [{'uuid': 'private1-uuid'}] + self.cassandra._cassandra_route_table_read = mock.Mock( + return_value=(True, [rt_dict])) + + def vn_read_side_effect(uuids): + if 'private2-uuid' in uuids: + return (True, [{'fq_name': ['default-domain', + 'demo', + 'private2-name'], + 'uuid': 'private2-uuid'}]) + return (False, None) + + self.cassandra._cassandra_virtual_network_read = mock.Mock( + side_effect=vn_read_side_effect) + + # generate an update on the router to add the interface + router = config_db.LogicalRouterSM.locate(ROUTER_1['uuid']) + router.update(router_dict) + self.snat_agent.update_snat_instance(router) + + # check that the correct private network is read + self.cassandra._cassandra_virtual_network_read.assert_called_with( + ['private2-uuid']) + + # check that the route table is applied to the network + rt_name = 'rt_' + ROUTER_1['uuid'] + self.vnc_lib.virtual_network_update.assert_called_with( + VirtualNetworkRouteTableMatcher('private2-name', rt_name)) + + def test_del_interface(self): + self.test_add_interface() + + # update the previous router by removing all the interfaces + router_dict = copy.deepcopy(ROUTER_1) + router_dict['virtual_machine_interface_refs'] = [ + {'attr': None, + 'to': ['default-domain', + 'demo', + 'd0022578-5b16-4da8-bd4d-5760faf134dc'], + 'uuid': 'd0022578-5b16-4da8-bd4d-5760faf134dc'}] + router_dict['service_instance_refs'] = [ + {'to': ['default-domain', + 'demo', + 'si_' + ROUTER_1['uuid']], + 'uuid': 'si-uuid'}] + + # reset all calls + self.vnc_lib.virtual_network_update.reset_mock() + self.vnc_lib.service_instance_read.reset_mock() + self.vnc_lib.virtual_network_read.reset_mock() + + self.vnc_lib.virtual_network_read = mock.Mock( + return_value=VirtualNetwork(name='private1-name')) + + # get and use the route table previously created + rt_obj = self.vnc_lib.route_table_create.mock_calls[0][1][0] + self.cassandra._cassandra_route_table_read = mock.Mock( + return_value=(True, [self.obj_to_dict(rt_obj)])) + + def vn_read_side_effect(uuids): + if 'private1-uuid' in uuids: + return (True, [{'fq_name': ['default-domain', + 'demo', + 'private1-name'], + 'uuid': 'private1-uuid'}]) + return (False, None) + + self.cassandra._cassandra_virtual_network_read = mock.Mock( + side_effect=vn_read_side_effect) + + # generate an update on the router to remove the interface + router = config_db.LogicalRouterSM.locate(ROUTER_1['uuid']) + router.update(router_dict) + self.snat_agent.update_snat_instance(router) + + # check that the correct private network is read + self.cassandra._cassandra_virtual_network_read.assert_called_with( + ['private1-uuid']) + + # check that the route table is applied to the network + self.vnc_lib.virtual_network_update.assert_called_with( + VirtualNetworkRouteTableMatcher('private1-name', None)) diff --git a/src/config/svc-monitor/test-requirements.txt b/src/config/svc-monitor/test-requirements.txt index 3452a1d8527..1b32034192e 100644 --- a/src/config/svc-monitor/test-requirements.txt +++ b/src/config/svc-monitor/test-requirements.txt @@ -8,6 +8,7 @@ geventhttpclient psutil bottle distribute>=0.7.3 +subunit ../../../debug/config/common/dist/cfgm_common-0.1dev.tar.gz ../../../debug/api-lib/dist/vnc_api-0.1dev.tar.gz ../../../debug/discovery/client/dist/discoveryclient-0.1dev.tar.gz diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index d7a1d425e31..79cd0f2f2ad 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -33,7 +33,6 @@ DELETE = 4 # SNAT defines -SNAT_SERVICE_TEMPLATE_FQ_NAME = ['default-domain', 'netns-snat-template'] _IFACE_ROUTE_TABLE_NAME_PREFIX = 'NEUTRON_IFACE_RT' class DBInterface(object): @@ -1891,14 +1890,14 @@ def _gw_port_vnc_to_neutron(self, port_obj, port_req_memo): vm_obj = self._vnc_lib.virtual_machine_read(id=vm_uuid) except NoIdError: return None - port_req_memo['virtual-machines'][vm_uuid] = vm_obj + port_req_memo['virtual-machines'][vm_uuid] = vm_obj si_refs = vm_obj.get_service_instance_refs() if not si_refs: return None try: - si_obj = self._vnc_lib.service_instance_read(id=si_refs[0]['uuid'], + si_obj = self._vnc_lib.service_instance_read(id=si_refs[0]['uuid'], fields=["logical_router_back_refs"]) except NoIdError: return None @@ -2889,7 +2888,7 @@ def policy_count(self, filters=None): return len(policy_info) #end policy_count - def _router_add_gateway(self, router_q, rtr_obj): + def _router_update_gateway(self, router_q, rtr_obj): ext_gateway = router_q.get('external_gateway_info', None) old_ext_gateway = self._get_external_gateway_info(rtr_obj) if ext_gateway or old_ext_gateway: @@ -2923,46 +2922,6 @@ def _router_clear_external_gateway(self, router_obj): router_obj.set_virtual_network_list([]) self._vnc_lib.logical_router_update(router_obj) - def _set_snat_routing_table(self, router_obj, network_id): - project_obj = self._project_read(proj_id=router_obj.parent_uuid) - rt_name = 'rt_' + router_obj.uuid - rt_fq_name = project_obj.get_fq_name() + [rt_name] - - try: - rt_obj = self._vnc_lib.route_table_read(fq_name=rt_fq_name) - rt_uuid = rt_obj.uuid - except NoIdError: - # No route table set with that router ID, the gateway is not set - return - - try: - net_obj = self._vnc_lib.virtual_network_read(id=network_id) - except NoIdError: - self._raise_contrail_exception( - 'NetworkNotFound', net_id=network_id) - net_obj.set_route_table(rt_obj) - self._vnc_lib.virtual_network_update(net_obj) - - def _clear_snat_routing_table(self, router_obj, network_id): - project_obj = self._project_read(proj_id=router_obj.parent_uuid) - rt_name = 'rt_' + router_obj.uuid - rt_fq_name = project_obj.get_fq_name() + [rt_name] - - try: - rt_obj = self._vnc_lib.route_table_read(fq_name=rt_fq_name) - rt_uuid = rt_obj.uuid - except NoIdError: - # No route table set with that router ID, the gateway is not set - return - - try: - net_obj = self._vnc_lib.virtual_network_read(id=network_id) - except NoIdError: - self._raise_contrail_exception( - 'NetworkNotFound', net_id=network_id) - net_obj.del_route_table(rt_obj) - self._vnc_lib.virtual_network_update(net_obj) - # router api handlers def router_create(self, router_q): #self._ensure_project_exists(router_q['tenant_id']) @@ -2971,7 +2930,7 @@ def router_create(self, router_q): rtr_uuid = self._resource_create('logical_router', rtr_obj) # read it back to update id perms rtr_obj = self._logical_router_read(rtr_uuid) - self._router_add_gateway(router_q, rtr_obj) + self._router_update_gateway(router_q, rtr_obj) ret_router_q = self._router_vnc_to_neutron(rtr_obj, rtr_repr='SHOW') return ret_router_q @@ -2996,7 +2955,7 @@ def router_update(self, rtr_id, router_q): router_q['id'] = rtr_id rtr_obj = self._router_neutron_to_vnc(router_q, UPDATE) self._logical_router_update(rtr_obj) - self._router_add_gateway(router_q, rtr_obj) + self._router_update_gateway(router_q, rtr_obj) ret_router_q = self._router_vnc_to_neutron(rtr_obj, rtr_repr='SHOW') return ret_router_q @@ -3188,7 +3147,6 @@ def add_router_interface(self, context, router_id, port_id=None, subnet_id=None) 'BadRequest', resource='router', msg='Either port or subnet must be specified') - self._set_snat_routing_table(router_obj, subnet['network_id']) vmi_obj = self._vnc_lib.virtual_machine_interface_read(id=port_id) vmi_obj.set_virtual_machine_interface_device_owner( constants.DEVICE_OWNER_ROUTER_INTF) @@ -3235,7 +3193,6 @@ def remove_router_interface(self, router_id, port_id=None, subnet_id=None): self._raise_contrail_exception('BadRequest', resource='router', msg=msg) - self._clear_snat_routing_table(router_obj, subnet['network_id']) port_obj = self._virtual_machine_interface_read(port_id) router_obj.del_virtual_machine_interface(port_obj) self._vnc_lib.logical_router_update(router_obj)