diff --git a/src/config/svc-monitor/SConscript b/src/config/svc-monitor/SConscript index 1a314cbc08d..537c7c2f60b 100644 --- a/src/config/svc-monitor/SConscript +++ b/src/config/svc-monitor/SConscript @@ -37,6 +37,7 @@ sources = [ 'svc_monitor/tests/test_vrouter_instance_manager.py', 'svc_monitor/tests/test_virtual_machine_manager.py', 'svc_monitor/tests/test_snat.py', + 'svc_monitor/tests/test_common_utils.py', 'svc_monitor/tests/scheduler/__init__.py', 'svc_monitor/tests/scheduler/test_vrouter_schedulers.py', ] diff --git a/src/config/svc-monitor/svc_monitor/config_db.py b/src/config/svc-monitor/svc_monitor/config_db.py index efc539455b3..46ea5f40948 100644 --- a/src/config/svc-monitor/svc_monitor/config_db.py +++ b/src/config/svc-monitor/svc_monitor/config_db.py @@ -226,6 +226,8 @@ class VirtualRouterSM(DBBase): def __init__(self, uuid, obj_dict=None): self.uuid = uuid + self.agent_state = False + self.agent_down_count = 0 self.virtual_machines = set() self.update(obj_dict) # end __init__ @@ -246,6 +248,19 @@ def delete(cls, uuid): obj.update_multiple_refs('virtual_machine', {}) del cls._dict[uuid] # end delete + + def set_agent_state(self, up): + if up: + self.agent_down_count = 0 + self.agent_state = True + else: + self.agent_down_count += 1 + if not (self.agent_down_count % 3): + self.agent_state = False + + def set_netns_version(self, netns_version): + self.netns_version = netns_version + # end VirtualRouterSM diff --git a/src/config/svc-monitor/svc_monitor/instance_manager.py b/src/config/svc-monitor/svc_monitor/instance_manager.py index 00a75a0fb34..697986d8bac 100644 --- a/src/config/svc-monitor/svc_monitor/instance_manager.py +++ b/src/config/svc-monitor/svc_monitor/instance_manager.py @@ -542,13 +542,12 @@ def _create_svc_vm_port(self, nic, instance_name, si, st, def _associate_vrouter(self, si, vm): vrouter_name = None if not vm.virtual_router: - chosen_vr_fq_name = None - chosen_vr_fq_name = self.vrouter_scheduler.schedule( - si.uuid, vm.uuid) - if chosen_vr_fq_name: - vrouter_name = chosen_vr_fq_name[-1] - self.logger.log_info("VRouter %s updated with VM %s" % - (':'.join(chosen_vr_fq_name), vm.name)) + chosen_vr = self.vrouter_scheduler.schedule(si, vm) + if chosen_vr: + vr = VirtualRouterSM.get(chosen_vr) + vrouter_name = vr.name + self.logger.log_notice("vrouter %s updated with vm %s" % + (':'.join(vr.fq_name), vm.name)) vm.update() else: vr = VirtualRouterSM.get(vm.virtual_router) @@ -607,7 +606,7 @@ def check_service(self, si): service_up = False else: vr = VirtualRouterSM.get(vm.virtual_router) - if self.vrouter_scheduler.vrouter_running(vr.name): + if vr.agent_state: continue self._vnc_lib.ref_update('virtual-router', vr.uuid, 'virtual-machine', vm.uuid, None, 'DELETE') diff --git a/src/config/svc-monitor/svc_monitor/scheduler/vrouter_scheduler.py b/src/config/svc-monitor/svc_monitor/scheduler/vrouter_scheduler.py index a4013f3ad69..236ac13abe2 100644 --- a/src/config/svc-monitor/svc_monitor/scheduler/vrouter_scheduler.py +++ b/src/config/svc-monitor/svc_monitor/scheduler/vrouter_scheduler.py @@ -28,18 +28,20 @@ from sandesh_common.vns import constants from vnc_api.vnc_api import NoIdError +from svc_monitor.config_db import * + +from sandesh_common.vns.constants import \ + ANALYTICS_API_SERVER_DISCOVERY_SERVICE_NAME as analytics_svc_name + @six.add_metaclass(abc.ABCMeta) class VRouterScheduler(object): - def __init__(self, vnc_lib, nova_client, args): + def __init__(self, vnc_lib, nova_client, disc, logger, args): self._vnc_lib = vnc_lib self._args = args self._nc = nova_client - - # initialize analytics client - endpoint = "http://%s:%s" % (self._args.analytics_server_ip, - self._args.analytics_server_port) - self._analytics = analytics_client.Client(endpoint) + self._disc = disc + self._logger = logger @abc.abstractmethod def schedule(self, plugin, context, router_id, candidates=None): @@ -51,130 +53,124 @@ def schedule(self, plugin, context, router_id, candidates=None): """ pass - def _get_candidates(self, si_uuid, vm_uuid): - """Return vrouter agents where a service instance virtual machine - could be scheduled. - - If a VM of a same service instance is already scheduled on the vrouter, - that vrouter is exclude from the candidates list. - If the VM is already scheduled on a running vrouter, only that vrouter - is return in the candidates list. - """ - - # if availability zone configured then use it - vr_list = [] - if self._args.netns_availability_zone: - az_list = self._nc.oper('availability_zones', 'list', - 'admin', detailed=True) - for az in az_list: - if self._args.netns_availability_zone in str(az): - for host in az.hosts: - vr_list.append({'fq_name': - ['default-global-system-config', host]}) - else: - vr_list = self._vnc_lib.virtual_routers_list()['virtual-routers'] - - # check if vrouters are functional and support service instance - vrs_fq_name = [vr['fq_name'] for vr in vr_list - if self.vrouter_running(vr['fq_name'][-1])] - vrs_fq_name = [vr_fq_name for vr_fq_name in vrs_fq_name - if self.vrouter_check_version( - vr_fq_name[-1], - svc_info._VROUTER_NETNS_SUPPORTED_VERSION)] - - for vr_fq_name in vrs_fq_name: - try: - vr_obj = self._vnc_lib.virtual_router_read(fq_name=vr_fq_name) - except NoIdError: - vrs_fq_name.remove(vr_fq_name) + def _get_az_vrouter_list(self): + if not self._args.netns_availability_zone: + return None + az_list = self._nc.oper('availability_zones', 'list', + self._args.admin_tenant_name, + detailed=True) + az_vr_list = [] + for az in az_list: + if self._args.netns_availability_zone not in str(az): continue - for vm_ref in vr_obj.get_virtual_machine_refs() or []: - if vm_uuid == vm_ref['uuid']: - return [vr_fq_name] - try: - vm_obj = self._vnc_lib.virtual_machine_read( - id=vm_ref['uuid']) - except NoIdError: - continue - if si_uuid in [si['uuid'] for si in - vm_obj.get_service_instance_refs()]: - vrs_fq_name.remove(vr_fq_name) - continue - return vrs_fq_name - - def vrouter_running(self, vrouter_name): - """Check if a vrouter agent is up and running.""" + az_vr_list.extend(az.hosts.keys()) + + return az_vr_list + + def get_analytics_client(self): + try: + sub_obj = self._disc.subscribe(analytics_svc_name, 0) + slist = sub_obj.info + except Exception as ex: + self._logger.log_error('Failed to get analytics api from discovery') + return None + else: + if not slist: + self._logger.log_error('No analytics api client in discovery') + return None + analytics_api = random.choice(slist) + endpoint = "http://%s:%s" % (analytics_api['ip-address'], + str(analytics_api['port'])) + return analytics_client.Client(endpoint) + + def query_uve(self, filter_string): path = "/analytics/uves/vrouter/" - - fqdn_uuid = "%s?cfilt=VrouterAgent" % vrouter_name - try: - vrouter_agent = self._analytics.request(path, fqdn_uuid) - except analytics_client.OpenContrailAPIFailed: - return False - - if 'VrouterAgent' not in vrouter_agent or \ - ('mode' in vrouter_agent['VrouterAgent'] and \ - vrouter_agent['VrouterAgent']['mode'] != \ - constants.VrouterAgentTypeMap[ - constants.VrouterAgentType.VROUTER_AGENT_EMBEDDED]): - return False - - fqdn_uuid = "%s?cfilt=NodeStatus" % vrouter_name + response_dict = {} try: - node_status = self._analytics.request(path, fqdn_uuid) + response = self._analytics.request(path, filter_string) + for values in response['value']: + response_dict[values['name']] = values['value'] except analytics_client.OpenContrailAPIFailed: - return False + pass + return response_dict - if not node_status or 'NodeStatus' not in node_status or \ - 'process_status' not in node_status['NodeStatus']: - return False + def vrouters_running(self): + # get az host list + az_vrs = self._get_az_vrouter_list() - for process in node_status['NodeStatus']['process_status']: - if (process['module_id'] == constants.MODULE_VROUTER_AGENT_NAME and - int(process['instance_id']) == 0 and - process['state'] == 'Functional'): - return True - return False + # read all vrouter information + self._analytics = self.get_analytics_client() + if not self._analytics: + return + agents_status = self.query_uve("*?cfilt=NodeStatus:process_status") + vrouters_mode = self.query_uve("*?cfilt=VrouterAgent:mode") - def vrouter_check_version(self, vrouter_name, version): - """Check the vrouter version is upper or equal to a desired version.""" - path = "/analytics/uves/vrouter/" - fqdn_uuid = "%s?cfilt=VrouterAgent" % vrouter_name + for vr in VirtualRouterSM.values(): + if az_vrs and vr.name not in az_vrs: + vr.set_agent_state(False) + continue - try: - vrouter_agent = self._analytics.request(path, fqdn_uuid) - except analytics_client.OpenContrailAPIFailed: - return False + if vr.name not in vrouters_mode or vr.name not in agents_status: + vr.set_agent_state(False) + continue - if not vrouter_agent: - return False + try: + vr_mode = vrouters_mode[vr.name]['VrouterAgent'] + if (vr_mode['mode'] != constants.VrouterAgentTypeMap[ + constants.VrouterAgentType.VROUTER_AGENT_EMBEDDED]): + vr.set_agent_state(False) + continue + except Exception as e: + vr.set_agent_state(False) + continue - try: - build_info = ast.literal_eval( - vrouter_agent['VrouterAgent']['build_info']) - vrouter_version = V(build_info['build-info'][0]['build-version']) - requested_version = V(version) - except KeyError, ValueError: - return False + try: + state_up = False + for vr_status in agents_status[vr.name]['NodeStatus']['process_status'] or []: + if (vr_status['module_id'] != constants.MODULE_VROUTER_AGENT_NAME): + continue + if (int(vr_status['instance_id']) == 0 and + vr_status['state'] == 'Functional'): + vr.set_agent_state(True) + state_up = True + break + if not state_up: + vr.set_agent_state(False) + except Exception as e: + vr.set_agent_state(False) + continue - return vrouter_version >= requested_version + def _get_candidates(self, si, vm): + if vm.virtual_router: + return [vm.virtual_router] - def _bind_vrouter(self, vm_uuid, vr_fq_name): - """Bind the virtual machine to the vrouter which has been chosen.""" - vm_obj = self._vnc_lib.virtual_machine_read(id=vm_uuid) - vr_obj = self._vnc_lib.virtual_router_read(fq_name=vr_fq_name) - vr_obj.add_virtual_machine(vm_obj) - self._vnc_lib.virtual_router_update(vr_obj) + vr_list = VirtualRouterSM._dict.keys() + for vm_id in si.virtual_machines: + if vm_id == vm.uuid: + continue + anti_affinity_vm = VirtualMachineSM.get(vm_id) + if anti_affinity_vm: + try: + vr_list.remove(anti_affinity_vm.virtual_router) + except ValueError: + pass + for vr in VirtualRouterSM.values(): + if not vr.agent_state: + try: + vr_list.remove(vr.uuid) + except ValueError: + pass + return vr_list class RandomScheduler(VRouterScheduler): """Randomly allocate a vrouter agent for virtual machine of a service instance.""" - - def schedule(self, si_uuid, vm_uuid): - candidates = self._get_candidates(si_uuid, vm_uuid) + def schedule(self, si, vm): + candidates = self._get_candidates(si, vm) if not candidates: - return + return None chosen_vrouter = random.choice(candidates) - self._bind_vrouter(vm_uuid, chosen_vrouter) + self._vnc_lib.ref_update('virtual-router', chosen_vrouter, + 'virtual-machine', vm.uuid, None, 'ADD') return chosen_vrouter diff --git a/src/config/svc-monitor/svc_monitor/svc_monitor.py b/src/config/svc-monitor/svc_monitor/svc_monitor.py index ac66db8cc41..7fa2e2a786f 100644 --- a/src/config/svc-monitor/svc_monitor/svc_monitor.py +++ b/src/config/svc-monitor/svc_monitor/svc_monitor.py @@ -325,7 +325,7 @@ def post_init(self, vnc_lib, args=None): self.vrouter_scheduler = importutils.import_object( self._args.si_netns_scheduler_driver, self._vnc_lib, self._nova_client, - self._args) + self._disc, self.logger, self._args) # load virtual machine instance manager self.vm_manager = importutils.import_object( @@ -383,6 +383,7 @@ def post_init(self, vnc_lib, args=None): self.upgrade() # check services + self.vrouter_scheduler.vrouters_running() self.launch_services() self._db_resync_done.set() @@ -915,6 +916,8 @@ def timer_callback(monitor): for vm in vm_delete_list: monitor._delete_service_instance(vm) + monitor.vrouter_scheduler.vrouters_running() + # check status of service si_list = list(ServiceInstanceSM.values()) for si in si_list: diff --git a/src/config/svc-monitor/svc_monitor/tests/scheduler/test_vrouter_schedulers.py b/src/config/svc-monitor/svc_monitor/tests/scheduler/test_vrouter_schedulers.py index b1365dcec7c..1856bd0e229 100644 --- a/src/config/svc-monitor/svc_monitor/tests/scheduler/test_vrouter_schedulers.py +++ b/src/config/svc-monitor/svc_monitor/tests/scheduler/test_vrouter_schedulers.py @@ -26,258 +26,23 @@ import svc_monitor.scheduler.vrouter_scheduler as scheduler from vnc_api.vnc_api import VirtualRouter, VirtualMachine - -RUNNING_VROUTER_UVES_STATUS = { - "NodeStatus": { - "process_status": [ - { - "instance_id": "0", - "module_id": constants.MODULE_VROUTER_AGENT_NAME, - "state": "Functional", - "connection_infos": [ - { - "server_addrs": [ - "10.0.1.2:0" - ], - "status": "Up", - "type": "XMPP", - "name": "control-node:10.0.1.2", - "description": "OpenSent" - }, - { - "server_addrs": [ - "127.0.0.1:8086" - ], - "status": "Up", - "type": "Collector", - "name": "null", - "description": "Established" - } - ] - } - ], - "description": "null" - }, - "VrouterAgent": { - "build_info": "{\"build-info\":[{\"build-time\":\"2015-01-12 11:13:42.160435\",\"build-hostname\":\"vrouter1\",\"build-git-ver\":\"bdcb043\",\"build-user\":\"root\",\"build-version\":\"2.0\",\"build-id\":\"unknown\",\"build-number\":\"unknown\"}]}" - } -} - -NON_RUNNING_VROUTER_UVES_STATUS_1 = { - "NodeStatus": { - "process_status": [ - { - "instance_id": "2", - "module_id": constants.MODULE_VROUTER_AGENT_NAME, - "state": "Functional", - "connection_infos": [ - { - "server_addrs": [ - "10.0.1.2:0" - ], - "status": "Up", - "type": "XMPP", - "name": "control-node:10.0.1.2", - "description": "OpenSent" - }, - { - "server_addrs": [ - "127.0.0.1:8086" - ], - "status": "Up", - "type": "Collector", - "name": "null", - "description": "Established" - } - ] - } - ], - "description": "null" - }, - "VrouterAgent": { - "build_info": "{\"build-info\":[{\"build-time\":\"2015-01-12 11:13:42.160435\",\"build-hostname\":\"vrouter1\",\"build-git-ver\":\"bdcb043\",\"build-user\":\"root\",\"build-version\":\"1.06\",\"build-id\":\"unknown\",\"build-number\":\"unknown\"}]}" - } -} - -NON_RUNNING_VROUTER_UVES_STATUS_2 = { - "NodeStatus": { - "process_status": [ - { - "instance_id": "0", - "module_id": "FakeModuleID", - "state": "Functional", - "connection_infos": [ - { - "server_addrs": [ - "10.0.1.2:0" - ], - "status": "Up", - "type": "XMPP", - "name": "control-node:10.0.1.2", - "description": "OpenSent" - }, - { - "server_addrs": [ - "127.0.0.1:8086" - ], - "status": "Up", - "type": "Collector", - "name": "null", - "description": "Established" - } - ] - } - ], - "description": "null" - }, - "VrouterAgent": { - "build_info": "{\"build-info\":[{\"build-time\":\"2015-01-12 11:13:42.160435\",\"build-hostname\":\"vrouter1\",\"build-git-ver\":\"bdcb043\",\"build-user\":\"root\",\"build-version\":\"1.10\",\"build-id\":\"unknown\",\"build-number\":\"unknown\"}]}" - } -} - -NON_RUNNING_VROUTER_UVES_STATUS_3 = { - "NodeStatus": { - "process_status": [ - { - "instance_id": "0", - "module_id": constants.MODULE_VROUTER_AGENT_NAME, - "state": "Non-functional", - "connection_infos": [ - { - "server_addrs": [ - "10.0.1.2:0" - ], - "status": "Up", - "type": "XMPP", - "name": "control-node:10.0.1.2", - "description": "OpenSent" - }, - { - "server_addrs": [ - "127.0.0.1:8086" - ], - "status": "Up", - "type": "Collector", - "name": "null", - "description": "Established" - } - ] - } - ], - "description": "null" - }, - "VrouterAgent": { - "build_info": "{\"build-info\":[{\"build-time\":\"2015-01-12 11:13:42.160435\",\"build-hostname\":\"vrouter1\",\"build-git-ver\":\"bdcb043\",\"build-user\":\"root\",\"build-version\":\"2.0\",\"build-id\":\"unknown\",\"build-number\":\"unknown\"}]}" - } -} - -NON_RUNNING_VROUTER_UVES_STATUS_4 = {} - -NON_RUNNING_VROUTER_UVES_STATUS_5 = { - "NodeStatus": { - "process_status": [ - { - "instance_id": "0", - "module_id": constants.MODULE_VROUTER_AGENT_NAME, - "state": "Functional", - "connection_infos": [ - { - "server_addrs": [ - "10.0.1.2:0" - ], - "status": "Up", - "type": "XMPP", - "name": "control-node:10.0.1.2", - "description": "OpenSent" - }, - { - "server_addrs": [ - "127.0.0.1:8086" - ], - "status": "Up", - "type": "Collector", - "name": "null", - "description": "Established" - } - ] - } - ], - "description": "null" - }, - "VrouterAgent": { - "mode": "TOR", - "build_info": "{\"build-info\":[{\"build-time\":\"2015-01-12 11:13:42.160435\",\"build-hostname\":\"vrouter1\",\"build-git-ver\":\"bdcb043\",\"build-user\":\"root\",\"build-version\":\"2.0\",\"build-id\":\"unknown\",\"build-number\":\"unknown\"}]}" - } -} - -NON_RUNNING_VROUTER_UVES_STATUS_6 = { - "NodeStatus": { - "process_status": [ - { - "instance_id": "0", - "module_id": constants.MODULE_VROUTER_AGENT_NAME, - "state": "Functional", - "connection_infos": [ - { - "server_addrs": [ - "10.0.1.2:0" - ], - "status": "Up", - "type": "XMPP", - "name": "control-node:10.0.1.2", - "description": "OpenSent" - }, - { - "server_addrs": [ - "127.0.0.1:8086" - ], - "status": "Up", - "type": "Collector", - "name": "null", - "description": "Established" - } - ] - } - ], - "description": "null" - }, - "VrouterAgent": { - "mode": "TSN", - "build_info": "{\"build-info\":[{\"build-time\":\"2015-01-12 11:13:42.160435\",\"build-hostname\":\"vrouter1\",\"build-git-ver\":\"bdcb043\",\"build-user\":\"root\",\"build-version\":\"2.0\",\"build-id\":\"unknown\",\"build-number\":\"unknown\"}]}" - } +from svc_monitor.config_db import ServiceInstanceSM, VirtualRouterSM +import svc_monitor.tests.test_common_utils as test_utils + +AGENTS_STATUS = \ +{ + u'vrouter1': {u'NodeStatus': {u'process_status': + [{u'module_id': u'contrail-vrouter-agent', u'state': u'Functional'}]}}, + u'vrouter2': {u'NodeStatus': {u'process_status': + [{u'module_id': u'contrail-vrouter-agent', u'state': u'Functional'}]}} } - -VROUTER_LIST = { - "virtual-routers": [ - { - "href": "http://127.0.0.1:8082/virtual-router/uuid1", - "fq_name": [ - "default-global-system-config", - "vrouter1" - ], - "uuid": "uuid1" - }, - { - "href": "http://127.0.0.1:8082/virtual-router/uuid2", - "fq_name": [ - "default-global-system-config", - "vrouter2" - ], - "uuid": "uuid2" - }, - { - "href": "http://127.0.0.1:8082/virtual-router/uuid3", - "fq_name": [ - "default-global-system-config", - "vrouter3" - ], - "uuid": "uuid3" - } - ] +VROUTERS_MODE = \ +{ + u'vrouter1': {u'VrouterAgent': {u'mode': u'VROUTER'}}, + u'vrouter2': {u'VrouterAgent': {u'mode': u'VROUTER'}} } - class TestRandomScheduler(unittest.TestCase): def setUp(self): @@ -292,107 +57,74 @@ def setUp(self): self.scheduler = \ scheduler.RandomScheduler(self.vnc_mock, mock.MagicMock(), + mock.MagicMock(), mock.MagicMock(), mock.MagicMock(netns_availability_zone=False)) def tearDown(self): self.analytics_patch.stop() + VirtualRouterSM.reset() + ServiceInstanceSM.reset() self.vnc_patch.stop() super(TestRandomScheduler, self).tearDown() def test_get_candidates(self): - self.vnc_mock.virtual_routers_list.return_value = VROUTER_LIST - - self.analytics_mock.side_effect = [RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - NON_RUNNING_VROUTER_UVES_STATUS_3, - NON_RUNNING_VROUTER_UVES_STATUS_3, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS] - - vr_obj = VirtualRouter() - vr_obj.get_virtual_machine_refs = mock.MagicMock( - return_value=[{'uuid': 'fake_vm_uuid1'}]) - self.vnc_mock.virtual_router_read.return_value = vr_obj - - vm_obj_1 = VirtualMachine() - vm_obj_1.get_service_instance_refs = mock.MagicMock( - return_value=[{'uuid': 'fake_si_uuid1'}]) - vm_obj_2 = VirtualMachine() - vm_obj_2.get_service_instance_refs = mock.MagicMock( - return_value=[{'uuid': 'fake_si_uuid2'}]) - self.vnc_mock.virtual_machine_read.side_effect = [vm_obj_1, vm_obj_2] - - # Test the vrouters seected does not already have a VM of the same SI - # schedule on it. - expected_result = [["default-global-system-config", "vrouter3"]] - self.assertEqual(self.scheduler._get_candidates('fake_si_uuid1', - 'fake_vm_uuid2'), - expected_result) - - self.analytics_mock.side_effect = [RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - NON_RUNNING_VROUTER_UVES_STATUS_3, - NON_RUNNING_VROUTER_UVES_STATUS_3, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS] - self.vnc_mock.virtual_machine_read.side_effect = [vm_obj_1, vm_obj_2] - - # Test the same vrouter is return if the VM is already scheduled on - # a running vrouter - expected_result = [["default-global-system-config", "vrouter1"]] - self.assertEqual(self.scheduler._get_candidates('fake_si_uuid1', - 'fake_vm_uuid1'), - expected_result) + test_utils.create_test_project('fake-domain:fake-project') + test_utils.create_test_virtual_network('fake-domain:fake-project:vn1') + test_utils.create_test_virtual_network('fake-domain:fake-project:vn2') + test_utils.create_test_security_group('fake-domain:fake-project:default') + st = test_utils.create_test_st(name='test-template', + virt_type='network-namespace', + intf_list=[['right', True], ['left', True]]) + si = test_utils.create_test_si(name='test-instance', count=2, + intf_list=['vn1', 'vn2']) + + # test anti-affinity + vr1 = test_utils.create_test_virtual_router('vr-candidate1') + vr2 = test_utils.create_test_virtual_router('vr-candidate2') + vm1 = test_utils.create_test_virtual_machine('vm1') + vm2 = test_utils.create_test_virtual_machine('vm2') + si.virtual_machines.add(vm1.uuid) + si.virtual_machines.add(vm2.uuid) + vm1.virtual_router = vr1.uuid + candidates = self.scheduler._get_candidates(si, vm2) + self.assertEqual(candidates, [vr2.uuid]) + + # test same vrouter returned if already scheduled + candidates = self.scheduler._get_candidates(si, vm1) + self.assertEqual(len(candidates), 1) + self.assertEqual(candidates, [vr1.uuid]) + + # test all candidates returned + vm1.virtual_router = None + candidates = self.scheduler._get_candidates(si, vm1) + self.assertEqual(len(candidates), 2) + + # test non running candidates returned + vr1.agent_state = False + candidates = self.scheduler._get_candidates(si, vm1) + self.assertEqual(len(candidates), 1) + self.assertEqual(candidates, [vr2.uuid]) + + # test no candidates + vr1.agent_state = False + vr2.agent_state = False + candidates = self.scheduler._get_candidates(si, vm1) + self.assertEqual(len(candidates), 0) def test_vrouter_running(self): - self.analytics_mock.side_effect = [analytics.OpenContrailAPIFailed, - NON_RUNNING_VROUTER_UVES_STATUS_1, - NON_RUNNING_VROUTER_UVES_STATUS_1, - NON_RUNNING_VROUTER_UVES_STATUS_2, - NON_RUNNING_VROUTER_UVES_STATUS_2, - NON_RUNNING_VROUTER_UVES_STATUS_3, - NON_RUNNING_VROUTER_UVES_STATUS_3, - NON_RUNNING_VROUTER_UVES_STATUS_4, - NON_RUNNING_VROUTER_UVES_STATUS_5, - NON_RUNNING_VROUTER_UVES_STATUS_6, - RUNNING_VROUTER_UVES_STATUS, - RUNNING_VROUTER_UVES_STATUS] - self.assertFalse(self.scheduler.vrouter_running('fake_vrouter_name')) - self.assertFalse(self.scheduler.vrouter_running('fake_vrouter_name')) - self.assertFalse(self.scheduler.vrouter_running('fake_vrouter_name')) - self.assertFalse(self.scheduler.vrouter_running('fake_vrouter_name')) - self.assertFalse(self.scheduler.vrouter_running('fake_vrouter_name')) - self.assertFalse(self.scheduler.vrouter_running('fake_vrouter_name')) - self.assertFalse(self.scheduler.vrouter_running('fake_vrouter_name')) - self.assertTrue(self.scheduler.vrouter_running('fake_vrouter_name')) - - def test_vrouter_check_version(self): - self.analytics_mock.side_effect = [analytics.OpenContrailAPIFailed, - NON_RUNNING_VROUTER_UVES_STATUS_1, - NON_RUNNING_VROUTER_UVES_STATUS_2, - NON_RUNNING_VROUTER_UVES_STATUS_3, - NON_RUNNING_VROUTER_UVES_STATUS_4, - RUNNING_VROUTER_UVES_STATUS] - self.assertFalse(self.scheduler.vrouter_check_version( - 'fake_vrouter_name', svc_info._VROUTER_NETNS_SUPPORTED_VERSION)) - self.assertFalse(self.scheduler.vrouter_check_version( - 'fake_vrouter_name', svc_info._VROUTER_NETNS_SUPPORTED_VERSION)) - self.assertTrue(self.scheduler.vrouter_check_version( - 'fake_vrouter_name', svc_info._VROUTER_NETNS_SUPPORTED_VERSION)) - self.assertTrue(self.scheduler.vrouter_check_version( - 'fake_vrouter_name', svc_info._VROUTER_NETNS_SUPPORTED_VERSION)) - self.assertFalse(self.scheduler.vrouter_check_version( - 'fake_vrouter_name', svc_info._VROUTER_NETNS_SUPPORTED_VERSION)) - self.assertTrue(self.scheduler.vrouter_check_version( - 'fake_vrouter_name', svc_info._VROUTER_NETNS_SUPPORTED_VERSION)) + vr1 = test_utils.create_test_virtual_router('vrouter1') + vr2 = test_utils.create_test_virtual_router('vrouter2') + self.scheduler.get_analytics_client = mock.MagicMock() + def query_uve_side_effect(query_str): + if 'NodeStatus' in query_str: + return AGENTS_STATUS + elif 'VrouterAgent' in query_str: + return VROUTERS_MODE + else: + return {} + self.scheduler.query_uve = query_uve_side_effect + self.assertTrue(VirtualRouterSM.get('vrouter1').agent_state) + self.assertTrue(VirtualRouterSM.get('vrouter2').agent_state) def test_random_scheduling(self): random_patch = mock.patch('random.choice') @@ -402,11 +134,14 @@ def side_effect(seq): return seq[0] random_mock.side_effect = side_effect + si = test_utils.create_test_si(name='test-instance', count=2, + intf_list=['vn1', 'vn2']) + vm = test_utils.create_test_virtual_machine('vm') + with mock.patch.object(scheduler.RandomScheduler, '_get_candidates', - return_value=[["default-global-system-config", "vrouter1"], - ["default-global-system-config", "vrouter2"]]): - self.assertEqual(self.scheduler.schedule('fake_uuid', 'fake_uuid'), - ["default-global-system-config", "vrouter1"]) + return_value=['vrouter1', 'vrouter2']): + chosen_vrouter = self.scheduler.schedule(si, vm) self.assertEqual(random_mock.call_count, 1) + self.assertEqual(chosen_vrouter, 'vrouter1') random_patch.stop() diff --git a/src/config/svc-monitor/svc_monitor/tests/test_common_utils.py b/src/config/svc-monitor/svc_monitor/tests/test_common_utils.py new file mode 100644 index 00000000000..5dc57d37e75 --- /dev/null +++ b/src/config/svc-monitor/svc_monitor/tests/test_common_utils.py @@ -0,0 +1,249 @@ +from svc_monitor.config_db import * +from vnc_api.vnc_api import * + +class VMObjMatcher(object): + """ + Object for assert_called_with to check if vm object is created properly + """ + def __init__(self, index, check_delete=False): + self.check_delete = check_delete + self.index = index + + def _has_field(self, index, ob): + if self.check_delete: + if index == ob.fq_name[0]: + return True + else: + if str(index) == ob.display_name.split('__')[-2]: + return True + return False + + def __eq__(self, other): + if not(self._has_field(self.index, other)): + return False + return True + +class VRObjMatcher(object): + """ + Object for assert_called_with to check if vr object is created properly + """ + def __init__(self, vm): + self.vm = vm + + def _has_field(self, vm, ob): + if vm == ob.get_virtual_machine_refs()[0]['to']: + return True + return False + + def __eq__(self, other): + if not(self._has_field(self.vm, other)): + return False + return True + +class FakeNovaServer(object): + def __init__(self, uuid, name): + self.id = uuid + self.name = name + + def get(self): + if self.id: + return True + return False + + def delete(self): + self.id = None + self.name = None + return + +class AnyStringWith(str): + def __eq__(self, other): + return self in other + +def create_test_st(name='fake-template', virt_type='virtual-machine', intf_list=[]): + st_obj = {} + st_obj['fq_name'] = ['fake-domain', name] + st_obj['uuid'] = name + st_obj['id_perms'] = 'fake-id-perms' + st_props = {} + st_props['flavor'] = 'm1.medium' + st_props['image_name'] = 'nat-image' + st_props['service_virtualization_type'] = virt_type + if virt_type == 'vrouter-instance': + st_props['vrouter_instance_type'] = 'docker' + st_props['service_type'] = 'firewall' + st_props['service_mode'] = 'in-network' + st_props['ordered_interfaces'] = True + st_props['service_scaling'] = True + st_props['interface_type'] = [] + for intf in intf_list: + try: + static_route_enable = intf[2] + except IndexError: + static_route_enable = False + st_props['interface_type'].append({'service_interface_type': intf[0], + 'shared_ip': intf[1], 'static_route_enable': static_route_enable}) + st_obj['service_template_properties'] = st_props + st = ServiceTemplateSM.locate(st_obj['uuid'], st_obj) + return st + +def create_test_si(name='fake-instance', count=1, vr_id=None, intf_list=None): + si_obj = {} + si_obj['fq_name'] = ['fake-domain', 'fake-project', name] + si_obj['uuid'] = name + si_obj['id_perms'] = 'fake-id-perms' + si_props = {} + si_props['scale_out'] = {'max_instances': count} + si_props['interface_list'] = [] + for vn_name in intf_list: + if not vn_name: + vn_fq_name = vn_name + else: + vn_fq_name = ':'.join(si_obj['fq_name'][0:-1]) + ':' + vn_name + si_props['interface_list'].append({'virtual_network': vn_fq_name}) + si_props['virtual_router_id'] = 'fake-vr-uuid' + si_obj['service_instance_properties'] = si_props + si_obj['parent_type'] = 'project' + si = ServiceInstanceSM.locate(si_obj['uuid'], si_obj) + return si + +def create_test_project(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(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' + vn_obj['parent_type'] = 'project' + VirtualNetworkSM.locate(vn_obj['uuid'], vn_obj) + +def create_test_virtual_machine(fq_name_str): + vm_obj = {} + vm_obj['fq_name'] = fq_name_str.split(':') + vm_obj['uuid'] = fq_name_str + vm_obj['display_name'] = fq_name_str + vm = VirtualMachineSM.locate(vm_obj['uuid'], vm_obj) + vm.proj_fq_name = ['fake-domain', 'fake-project'] + return vm + +def create_test_virtual_router(fq_name_str): + vr_obj = {} + vr_obj['fq_name'] = fq_name_str.split(':') + vr_obj['name'] = fq_name_str.split(':')[0] + vr_obj['uuid'] = fq_name_str + vr_obj['display_name'] = fq_name_str + vr = VirtualRouterSM.locate(vr_obj['uuid'], vr_obj) + vr.agent_state = True + return vr + +def create_test_security_group(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' + sg_obj['parent_type'] = 'project' + SecurityGroupSM.locate(sg_obj['uuid'], sg_obj) + +def get_vn_id_for_fq_name(obj_type, fq_name): + if obj_type != 'virtual-network': + return + for vn in VirtualNetworkSM.values(): + if vn.fq_name == fq_name: + return vn.uuid + raise NoIdError(fq_name) + +def vn_create(vn_obj): + vn_obj.uuid = (':').join(vn_obj.fq_name) + vn = {} + vn['uuid'] = vn_obj.uuid + vn['fq_name'] = vn_obj.fq_name + vn['parent_type'] = 'project' + VirtualNetworkSM.locate(vn_obj.uuid, vn) + return vn_obj.uuid + +def vmi_create(vmi_obj): + vmi_obj.uuid = 'fake-vmi-uuid' + return vmi_obj.uuid + +def iip_create(iip_obj): + iip_obj.uuid = 'fake-iip-uuid' + return iip_obj.uuid + +def vm_create(vm_obj): + vm_obj.uuid = (':').join(vm_obj.fq_name) + vm = {} + vm['uuid'] = vm_obj.uuid + vm['fq_name'] = vm_obj.fq_name + vm['display_name'] = vm_obj.display_name + VirtualMachineSM.locate(vm_obj.uuid, vm) + si = ServiceInstanceSM.get(vm_obj.get_service_instance_refs()[0]['uuid']) + if si: + si.virtual_machines.add(vm_obj.uuid) + vm_db = VirtualMachineSM.locate(vm_obj.uuid) + vm_db.index = int(vm_obj.display_name.split('__')[-2]) - 1 + return vm_obj.uuid + +def st_create(st_obj): + st_obj.uuid = st_obj.name + st = {} + st['uuid'] = st_obj.uuid + st['fq_name'] = st_obj.fq_name + ServiceTemplateSM.locate(st_obj.uuid, st) + return st_obj.uuid + +def test_get_instance_name(si, inst_count): + name = si.name + '__' + str(inst_count + 1) + instance_name = "__".join(si.fq_name[:-1] + [name]) + return instance_name + +def vm_db_read(obj_type, vm_id): + class SI(object): + def __init__(self, name, fq_name): + self.name = name + self.fq_name = fq_name + + vm_obj = {} + vm_obj['uuid'] = 'fake-vm-uuid' + vm_obj['fq_name'] = ['fake-vm-uuid'] + fq_name = ['fake-domain', 'fake-project', 'fake-instance'] + name = 'fake-instance' + si = SI(name, fq_name) + instance_name = test_get_instance_name(si, 0) + vm_obj['display_name'] = instance_name + '__' + 'vrouter-instance' + return True, [vm_obj] + +def vr_db_read(obj_type, vr_id): + vr_obj = {} + vr_obj['uuid'] = 'fake-vr-uuid' + vr_obj['fq_name'] = ['fake-vr-uuid'] + return True, [vr_obj] + +def vmi_db_read(obj_type, vmi_id): + vmi_obj = {} + vmi_obj['uuid'] = 'fake-vmi-uuid' + vmi_obj['fq_name'] = ['fake-vmi-uuid'] + vmi_obj['parent_type'] = 'project' + vmi_obj['parent_uuid'] = 'fake-project' + return True, [vmi_obj] + +def iip_db_read(obj_type, iip_id): + iip_obj = {} + iip_obj['uuid'] = 'fake-iip-uuid' + iip_obj['fq_name'] = ['fake-iip-uuid'] + return True, [iip_obj] + +def vn_db_read(obj_type, vn_id): + vn_obj = {} + vn_obj['uuid'] = 'fake-vn-uuid' + vn_obj['fq_name'] = ['fake-domain', 'fake-project', 'fake-vn-uuid'] + return True, [vn_obj] + +def irt_db_read(obj_type, irt_id): + irt_obj = {} + irt_obj['uuid'] = 'fake-irt-uuid' + irt_obj['fq_name'] = ['fake-domain', 'fake-project', 'fake-irt-uuid'] + return True, [irt_obj] 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 8142d11b3e7..dd9f7fdd2fa 100644 --- a/src/config/svc-monitor/svc_monitor/tests/test_snat.py +++ b/src/config/svc-monitor/svc_monitor/tests/test_snat.py @@ -118,9 +118,12 @@ def iip_create(iip_obj): self.mocked_args = mock.MagicMock() self.mocked_args.availability_zone = None + self.mocked_scheduler = mock.MagicMock() + self.mocked_scheduler.schedule = mock.Mock(return_value=('fake-virtual-router')) + self.netns_manager = NetworkNamespaceManager( db=self.mocked_db, logger=mock.MagicMock(), - vnc_lib=self.mocked_vnc, vrouter_scheduler=mock.MagicMock(), + vnc_lib=self.mocked_vnc, vrouter_scheduler=self.mocked_scheduler, nova_client=self.nova_mock, args=self.mocked_args) def tearDown(self): @@ -167,10 +170,21 @@ def create_test_virtual_machine(self, fq_name_str): vm.proj_fq_name = ['fake-domain', 'fake-project'] return vm + def create_test_virtual_router(self, fq_name_str): + vr_obj = {} + vr_obj['fq_name'] = fq_name_str.split(':') + vr_obj['name'] = fq_name_str.split(':')[0] + vr_obj['uuid'] = fq_name_str + vr_obj['display_name'] = fq_name_str + vr = VirtualRouterSM.locate(vr_obj['uuid'], vr_obj) + vr.agent_state = True + return vr + 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_virtual_router('fake-virtual-router') st_obj = {} st_obj['fq_name'] = ['fake-domain', 'fake-snat-template'] diff --git a/src/discovery/client.py b/src/discovery/client.py index 4928186ca13..fe30c6c13af 100644 --- a/src/discovery/client.py +++ b/src/discovery/client.py @@ -149,12 +149,6 @@ def _query(self): infostr = json.dumps(info) sig = hashlib.md5(infostr).hexdigest() - # convert to strings - for obj in info: - if type(obj) is dict: - for k, v in obj.items(): - obj[k] = v.encode('utf-8') - self.ttl = response['ttl'] self.change = False if sig != self.sig: