def handle(self, env, values): # find the host, to serve as parent instance_id = values['payload']['instance_id'] host_id = values['payload']['host'] instances_root_id = host_id + '-instances' instances_root = self.inv.get_by_id(env, instances_root_id) if not instances_root: self.log.info('instances root not found, aborting instance add') return EventResult(result=False, retry=True) # scan instance scanner = Scanner() scanner.set_env(env) scanner.scan("ScanInstancesRoot", instances_root, limit_to_child_id=instance_id, limit_to_child_type='instance') scanner.scan_from_queue() # scan host host = self.inv.get_by_id(env, host_id) scanner.scan( 'ScanHost', host, limit_to_child_type=['vconnectors_folder', 'vedges_folder']) scanner.scan_from_queue() scanner.scan_links() scanner.scan_cliques() return EventResult(result=True, related_object=instance_id, display_context=instance_id)
def handle(self, env, notification): network = notification['payload']['network'] network_id = network['id'] network_document = self.inv.get_by_id(env, network_id) if not network_document: self.log.info('Network document not found, aborting network update') return EventResult(result=False, retry=True) # update network document name = network['name'] if name != network_document['name']: network_document['name'] = name network_document['object_name'] = name name_path = network_document['name_path'] network_document['name_path'] = name_path[:name_path.rindex('/') + 1] + name # TBD: fix name_path for descendants self.inv.values_replace({"environment": env, "name_path": {"$regex": r"^" + re.escape(name_path + '/')}}, {"name_path": {"from": name_path, "to": network_document['name_path']}}) network_document['admin_state_up'] = network['admin_state_up'] self.inv.set(network_document) return EventResult(result=True, related_object=network_id, display_context=network_id)
def delete_port(self, env, port_id): port_doc = self.inv.get_by_id(env, port_id) if not port_doc: self.log.info("Port document not found, aborting port deleting.") return EventResult(result=False, retry=False) # if port is binding to a instance, instance document needs to be updated. if 'compute' in port_doc['device_owner']: self.log.info("update instance document to which port is binding.") self.update_instance(env, port_doc) # delete port document self.inv.delete('inventory', {'id': port_id}) # delete vnic and related document vnic_doc = self.inv.get_by_field(env, 'vnic', 'mac_address', port_doc['mac_address'], get_single=True) if not vnic_doc: self.log.info("Vnic document not found, aborting vnic deleting.") return EventResult(result=False, retry=False) result = self.delete_handler(env, vnic_doc['id'], 'vnic') result.related_object = port_id result.display_context = port_doc.get('network_id') self.log.info('Finished port deleting') return result
def handle(self, env, notification): subnet_id = notification['payload']['subnet_id'] network_document = self.inv.get_by_field(env, "network", "subnet_ids", subnet_id, get_single=True) if not network_document: self.log.info( "network document not found, aborting subnet deleting") return EventResult(result=False, retry=False) # remove subnet_id from subnet_ids array network_document["subnet_ids"].remove(subnet_id) # find the subnet in network_document by subnet_id subnet = next( filter(lambda s: s['id'] == subnet_id, network_document['subnets'].values()), None) # remove cidr from cidrs and delete subnet document. if subnet: network_document['cidrs'].remove(subnet['cidr']) del network_document['subnets'][subnet['name']] self.inv.set(network_document) # when network does not have any subnet, delete vservice DHCP, port and vnic documents. if not network_document["subnet_ids"]: vservice_dhcp_id = 'qdhcp-{}'.format(network_document['id']) self.delete_children_documents(env, vservice_dhcp_id) return EventResult(result=True, related_object=subnet['id'], display_context=network_document.get('id'))
def handle_event(self, event_type: str, notification: dict) -> EventResult: self.log.error("Got notification.\nEvent_type: {}\nNotification:\n{}". format(event_type, notification)) try: result = self.handler.handle(event_name=event_type, notification=notification) return result if result else EventResult(result=False, retry=False) except Exception as e: self.inv.log.exception(e) return EventResult(result=False, retry=False)
def handle(self, env, notification): # check for network document. subnet = notification['payload']['subnet'] project_id = subnet['tenant_id'] network_id = subnet['network_id'] if 'id' not in subnet: self.log.info( 'Subnet payload doesn\'t have id, aborting subnet add') return EventResult(result=False, retry=False) network_document = self.inv.get_by_id(env, network_id) if not network_document: self.log.info( 'network document does not exist, aborting subnet add') return EventResult(result=False, retry=True) network_name = network_document['name'] # build subnet document for adding network if subnet['cidr'] not in network_document['cidrs']: network_document['cidrs'].append(subnet['cidr']) if not network_document.get('subnets'): network_document['subnets'] = {} network_document['subnets'][subnet['name']] = subnet if subnet['id'] not in network_document['subnet_ids']: network_document['subnet_ids'].append(subnet['id']) self.inv.set(network_document) # Check DHCP enable, if true, scan network. if subnet['enable_dhcp'] is True: # update network if not ApiAccess.regions: fetcher = ApiFetchRegions() fetcher.set_env(env) fetcher.get(project_id) self.log.info("add new subnet.") host_id = notification["publisher_id"].replace("network.", "", 1) self.add_children_documents(env, project_id, network_id, network_name, host_id) # scan links and cliques self.log.info("scanning for links") FindLinksForPnics().add_links() FindLinksForVserviceVnics().add_links( search={"parent_id": "qdhcp-%s-vnics" % network_id}) scanner = Scanner() scanner.set_env(env) scanner.scan_cliques() self.log.info("Finished subnet added.") return EventResult(result=True, related_object=subnet['id'], display_context=network_id)
def handle(self, env, values): router = values['payload']['router'] host_id = values["publisher_id"].replace("network.", "", 1) project_id = values['_context_project_id'] router_id = encode_router_id(router['id']) host = self.inv.get_by_id(env, host_id) fetcher = CliFetchHostVservice() fetcher.setup(env=env, origin=self.origin) router_doc = fetcher.get_vservice(host_id, router_id) gateway_info = router['external_gateway_info'] if gateway_info: network_id = gateway_info['network_id'] self.add_router_document(env, network_id, router_doc, host) self.add_children_documents(env, project_id, network_id, host, router_doc) else: self.add_router_document(env, None, router_doc, host) # scan links and cliques FindLinksForVserviceVnics().add_links(search={"parent_id": router_id}) scanner = Scanner() scanner.setup(env=env, origin=self.origin) scanner.scan_cliques() self.log.info("Finished router added.") return EventResult(result=True, related_object=router_id, display_context=router_id)
def test_handle_interface_add(self, subnet_add_class_mock, port_add_class_mock, fetcher_class_mock, scanner_class_mock, find_links_class_mock): self.values = EVENT_PAYLOAD_INTERFACE_ADD self.inv.get_by_field.return_value = NETWORK_DOC self.inv.get_by_id.side_effect = self.get_by_id subnet_add_mock = subnet_add_class_mock.return_value subnet_add_mock.add_port_document.return_value = PORT_DOC port_add_mock = port_add_class_mock.return_value port_add_mock.add_vnic_document = \ Mock(return_value=EventResult(result=True)) fetcher_mock = fetcher_class_mock.return_value fetcher_mock.get_vservice.return_value = ROUTER_DOCUMENT fetcher_mock.handle_service.return_value = VNIC_DOCS scanner_mock = scanner_class_mock.return_value find_links_mock = find_links_class_mock.return_value with patch("discover.fetcher.FullLogger"): with TestRegions(EVENT_PAYLOAD_REGION): res = EventInterfaceAdd().handle(self.env, self.values) self.assertTrue(res.result) self.inv.set.assert_called_with(ROUTER_DOCUMENT) self.assertTrue(port_add_mock.add_vnic_document.called) self.assertTrue(scanner_mock.scan_cliques.called) self.assertTrue(find_links_mock.add_links.called)
def delete_handler(self, env, object_id, object_type) -> EventResult: item = self.inv.get_by_id(env, object_id) if not item: self.log.info( '{0} document is not found, aborting {0} delete'.format( object_type)) return EventResult(result=False, retry=False) db_id = ObjectId(item['_id']) id_path = item['id_path'] + '/' # remove related clique clique_finder = CliqueFinder() self.inv.delete('cliques', {'focal_point': db_id}) # keep related links to do rebuild of cliques using them matched_links_source = clique_finder.find_links_by_source(db_id) matched_links_target = clique_finder.find_links_by_target(db_id) links_using_object = [] links_using_object.extend([l['_id'] for l in matched_links_source]) links_using_object.extend([l['_id'] for l in matched_links_target]) # find cliques using these links if links_using_object: matched_cliques = clique_finder.find_cliques_by_link( links_using_object) # find cliques using these links and rebuild them for clique in matched_cliques: clique_finder.rebuild_clique(clique) # remove all related links self.inv.delete('links', {'source': db_id}) self.inv.delete('links', {'target': db_id}) # remove object itself self.inv.delete('inventory', {'_id': db_id}) # remove children regexp = re.compile('^' + id_path) self.inv.delete('inventory', {'id_path': {'$regex': regexp}}) return EventResult(result=True, related_object=object_id, display_context=object_id)
def handle(self, env, values): payload = values['payload'] if 'publisher_id' not in values: self.log.error( "Publisher_id is not in event values. Aborting router delete") return EventResult(result=False, retry=False) if 'router_id' in payload: router_id = payload['router_id'] elif 'id' in payload: router_id = payload['id'] else: router_id = payload.get('router', {}).get('id') if not router_id: self.log.error( "Router id is not in payload. Aborting router delete") return EventResult(result=False, retry=False) router_full_id = encode_router_id(router_id) return self.delete_handler(env, router_full_id, "vservice")
def delete_children_documents(self, env, vservice_id): vnic_parent_id = vservice_id + '-vnics' vnic = self.inv.get_by_field(env, 'vnic', 'parent_id', vnic_parent_id, get_single=True) if not vnic: self.log.info("Vnic document not found.") return EventResult(result=False, retry=False) # delete port and vnic together by mac address. self.inv.delete('inventory', {"mac_address": vnic.get("mac_address")}) return self.delete_handler(env, vservice_id, 'vservice')
def handle(self, env, values): # find the host, to serve as parent payload = values['payload'] instance_id = payload['instance_id'] state = payload['state'] old_state = payload['old_state'] if state == 'building': return EventResult(result=False, retry=False) if state == 'active' and old_state == 'building': return EventInstanceAdd().handle(env, values) if state == 'deleted' and old_state == 'active': return EventInstanceDelete().handle(env, values) name = payload['display_name'] instance = self.inv.get_by_id(env, instance_id) if not instance: self.log.info('instance document not found, aborting instance update') return EventResult(result=False, retry=True) instance['name'] = name instance['object_name'] = name name_path = instance['name_path'] instance['name_path'] = name_path[:name_path.rindex('/') + 1] + name # TBD: fix name_path for descendants if name_path != instance['name_path']: self.inv.values_replace({ "environment": env, "name_path": {"$regex": r"^" + re.escape(name_path + '/')}}, {"name_path": {"from": name_path, "to": instance['name_path']}}) self.inv.set(instance) return EventResult(result=True, related_object=instance_id, display_context=instance_id)
def handle(self, env, notification): network = notification['payload']['network'] network_id = network['id'] network_document = self.inv.get_by_id(env, network_id) if network_document: self.log.info('network already existed, aborting network add') return EventResult(result=False, retry=False) # build network document for adding network project_name = notification['_context_project_name'] project_id = notification['_context_project_id'] parent_id = project_id + '-networks' network_name = network['name'] network['environment'] = env network['type'] = 'network' network['id_path'] = "/%s/%s-projects/%s/%s/%s" \ % (env, env, project_id, parent_id, network_id) network['cidrs'] = [] network['subnet_ids'] = [] network['last_scanned'] = notification['timestamp'] network['name_path'] = "/%s/Projects/%s/Networks/%s" \ % (env, project_name, network_name) network['network'] = network_id network['object_name'] = network_name network['parent_id'] = parent_id network['parent_text'] = "Networks" network['parent_type'] = "networks_folder" network['project'] = project_name network["show_in_tree"] = True network['subnets'] = {} self.inv.set(network) return EventResult(result=True, related_object=network_id, display_context=network_id)
def handle(self, env, notification): # check port document. port = notification['payload']['port'] port_id = port['id'] port_document = self.inv.get_by_id(env, port_id) if not port_document: self.log.info('port document does not exist, aborting port update') return EventResult(result=False, retry=True) # build port document port_document['name'] = port['name'] port_document['admin_state_up'] = port['admin_state_up'] if port_document['admin_state_up']: port_document['status'] = 'ACTIVE' else: port_document['status'] = 'DOWN' port_document['binding:vnic_type'] = port['binding:vnic_type'] # update port document. self.inv.set(port_document) return EventResult(result=True, related_object=port_id, display_context=port_document.get('network_id'))
def test_handle_interface_delete(self, port_delete_class_mock): self.values = EVENT_PAYLOAD_INTERFACE_DELETE self.payload = self.values['payload'] self.interface = self.payload['router_interface'] self.port_id = self.interface['port_id'] self.router_id = encode_router_id(self.interface['id']) port_delete_mock = port_delete_class_mock.return_value port_delete_mock.delete_port.return_value = EventResult(result=True) self.inv.get_by_id.side_effect = self.get_by_id res = EventInterfaceDelete().handle(self.env, self.values) self.assertTrue(res.result) self.assertTrue(port_delete_mock.delete_port.called) self.inv.set.assert_called_with(ROUTER_DOCUMENT)
class TestSubnetDelete(TestEvent): def get_by_field(self, environment, item_type, field_name, field_value, get_single=False): if item_type == "network": return NETWORK_DOC elif item_type == "vnic": return VNIC_DOC else: return None @patch.object(EventDeleteBase, "delete_handler", return_value=EventResult(result=True)) def test_handle_subnet_delete(self, delete_handler_mock): self.values = EVENT_PAYLOAD_SUBNET_DELETE self.subnet_id = self.values['payload']['subnet_id'] self.network_doc = NETWORK_DOC self.network_id = self.network_doc['id'] self.vnic_id = VNIC_DOC['id'] self.vnic_folder_id = 'qdhcp-{}'.format(self.network_id) self.inv.get_by_field.side_effect = self.get_by_field res = EventSubnetDelete().handle(self.env, self.values) self.assertTrue(res.result) delete_handler_mock.assert_called_with(self.env, self.vnic_folder_id, "vservice") updated_network = [ call[0][0] for call in self.inv.set.call_args_list if call[0][0]['type'] == 'network' ] self.assertTrue(updated_network) self.assertTrue( self.subnet_id not in updated_network[0].get('subnet_ids')) self.assertTrue(self.inv.delete.called)
def handle(self, env, values): interface = values['payload']['router_interface'] port_id = interface['port_id'] router_id = encode_router_id(interface['id']) # update router document port_doc = self.inv.get_by_id(env, port_id) if not port_doc: self.log.info("Interface deleting handler: port document not found.") return EventResult(result=False, retry=False) network_id = port_doc['network_id'] router_doc = self.inv.get_by_id(env, router_id) if router_doc and network_id in router_doc.get('network', []): router_doc['network'].remove(network_id) self.inv.set(router_doc) # delete port document result = EventPortDelete().delete_port(env, port_id) result.related_object = interface['id'] result.display_context = network_id return result
def handle(self, env, values): payload = values['payload'] router = payload['router'] project_id = values['_context_project_id'] host_id = values["publisher_id"].replace("network.", "", 1) router_id = payload['id'] if 'id' in payload else router['id'] router_full_id = encode_router_id(router_id) router_doc = self.inv.get_by_id(env, router_full_id) if not router_doc: self.log.info( "Router document not found, aborting router updating") return EventResult(result=False, retry=True) router_doc['admin_state_up'] = router['admin_state_up'] router_doc['name'] = router['name'] gateway_info = router.get('external_gateway_info') if gateway_info is None: # when delete gateway, need to delete the port relate document. port_doc = {} if router_doc.get('gw_port_id'): port_doc = self.inv.get_by_id(env, router_doc['gw_port_id']) EventPortDelete().delete_port(env, router_doc['gw_port_id']) if router_doc.get('network'): if port_doc: router_doc['network'].remove(port_doc['network_id']) router_doc['gw_port_id'] = None # remove related links self.inv.delete('links', {'source_id': router_full_id}) else: if 'network' in router_doc: if gateway_info['network_id'] not in router_doc['network']: router_doc['network'].append(gateway_info['network_id']) else: router_doc['network'] = [gateway_info['network_id']] # update static route router_doc['routes'] = router['routes'] # add gw_port_id info and port document. fetcher = CliFetchHostVservice() fetcher.setup(env=env, origin=self.origin) router_vservice = fetcher.get_vservice(host_id, router_full_id) if router_vservice.get('gw_port_id'): router_doc['gw_port_id'] = router_vservice['gw_port_id'] host = self.inv.get_by_id(env, host_id) EventRouterAdd().add_children_documents(env, project_id, gateway_info['network_id'], host, router_doc) # rescan the vnic links. FindLinksForVserviceVnics().add_links( search={'parent_id': router_full_id + '-vnics'}) self.inv.set(router_doc) # update the cliques. scanner = Scanner() scanner.setup(env=env, origin=self.origin) scanner.scan_cliques() self.log.info("Finished router update.") return EventResult(result=True, related_object=router_full_id, display_context=router_full_id)
def handle(self, env, values): interface = values['payload']['router_interface'] project_id = values['_context_project_id'] project = values['_context_project_name'] host_id = values["publisher_id"].replace("network.", "", 1) port_id = interface['port_id'] subnet_id = interface['subnet_id'] router_id = encode_router_id(interface['id']) network_document = self.inv.get_by_field(env, "network", "subnet_ids", subnet_id, get_single=True) if not network_document: self.log.info( "network document not found, aborting interface adding") return EventResult(result=False, retry=True) network_name = network_document['name'] network_id = network_document['id'] # add router-interface port document. if not ApiAccess.regions: fetcher = ApiFetchRegions() fetcher.setup(env=env, origin=self.origin) fetcher.get(project_id) port_doc = EventSubnetAdd().add_port_document( env, port_id, network_name=network_name) mac_address = port_doc['mac_address'] if port_doc else None # add vnic document host = self.inv.get_by_id(env, host_id) router_doc = self.inv.get_by_id(env, router_id) add_vnic_document = partial(EventPortAdd().add_vnic_document, env=env, host=host, object_id=interface['id'], object_type='router', network_name=network_name, router_name=router_doc['name'], mac_address=mac_address) ret = add_vnic_document() if ret is False: # try it again to fetch vnic document, vnic will be created a little bit late before CLI fetch. time.sleep(self.delay) self.log.info( "Wait {} seconds, and then fetch vnic document again.".format( self.delay)) add_vnic_document() # update the router document: gw_port_id, network. self.update_router(env, project, network_id, network_name, router_doc, host_id) # update vservice-vnic, vnic-network, FindLinksForVserviceVnics().add_links(search={"parent_id": router_id}) scanner = Scanner() scanner.setup(env=env, origin=self.origin) scanner.scan_cliques() self.log.info("Finished router-interface added.") return EventResult(result=True, related_object=interface['id'], display_context=network_id)
def handle(self, env, notification): project = notification['_context_project_name'] project_id = notification['_context_project_id'] payload = notification['payload'] port = payload['port'] network_id = port['network_id'] network_name = self.get_name_by_id(network_id) mac_address = port['mac_address'] # check ports folder document. ports_folder = self.inv.get_by_id(env, network_id + '-ports') if not ports_folder: self.log.info("ports folder not found, add ports folder first.") self.add_ports_folder(env, project_id, network_id, network_name) self.add_port_document(env, project, project_id, network_name, network_id, port) # update the port related documents. if 'compute' in port['device_owner']: # update the instance related document. host_id = port['binding:host_id'] instance_id = port['device_id'] old_instance_doc = self.inv.get_by_id(env, instance_id) instances_root_id = host_id + '-instances' instances_root = self.inv.get_by_id(env, instances_root_id) if not instances_root: self.log.info( 'instance document not found, aborting port adding') return EventResult(result=False, retry=True) # update instance instance_fetcher = ApiFetchHostInstances() instance_fetcher.setup(env=env, origin=self.origin) instance_docs = instance_fetcher.get(host_id + '-') instance = next( filter(lambda i: i['id'] == instance_id, instance_docs), None) if instance: old_instance_doc['network_info'] = instance['network_info'] old_instance_doc['network'] = instance['network'] if old_instance_doc.get('mac_address') is None: old_instance_doc['mac_address'] = mac_address self.inv.set(old_instance_doc) self.log.info("update instance document") # add vnic document. if port['binding:vif_type'] == 'vpp': vnic_fetcher = CliFetchInstanceVnicsVpp() else: # set ovs as default type. vnic_fetcher = CliFetchInstanceVnics() vnic_fetcher.setup(env=env, origin=self.origin) vnic_docs = vnic_fetcher.get(instance_id + '-') vnic = next( filter(lambda vnic: vnic['mac_address'] == mac_address, vnic_docs), None) if vnic: vnic['environment'] = env vnic['type'] = 'vnic' vnic['name_path'] = old_instance_doc[ 'name_path'] + '/vNICs/' + vnic['name'] vnic['id_path'] = '{}/{}/{}'.format( old_instance_doc['id_path'], old_instance_doc['id'], vnic['name']) self.inv.set(vnic) self.log.info( "add instance-vnic document, mac_address: {}".format( mac_address)) self.log.info("scanning for links") fetchers_implementing_add_links = [ FindLinksForInstanceVnics(), FindLinksForVedges() ] for fetcher in fetchers_implementing_add_links: fetcher.add_links() scanner = Scanner() scanner.setup(env=env, origin=self.origin) scanner.scan_cliques() port_document = self.inv.get_by_id(env, port['id']) if not port_document: self.log.error("Port {} failed to add".format(port['id'])) return EventResult(result=False, retry=True) return EventResult(result=True, related_object=port['id'], display_context=network_id)
def handle(self, env, notification): # check for network document. subnet = notification['payload']['subnet'] project_id = notification['_context_project_id'] project = notification['_context_project_name'] host_id = notification['publisher_id'].replace('network.', '', 1) subnet_id = subnet['id'] network_id = subnet['network_id'] network_document = self.inv.get_by_id(env, network_id) if not network_document: self.log.info( 'network document does not exist, aborting subnet update') return EventResult(result=False, retry=True) # update network document. subnets = network_document['subnets'] key = next(filter(lambda k: subnets[k]['id'] == subnet_id, subnets), None) if key: if subnet['enable_dhcp'] and subnets[key]['enable_dhcp'] is False: # scan DHCP namespace to add related document. # add dhcp vservice document. host = self.inv.get_by_id(env, host_id) port_handler = EventPortAdd() port_handler.add_dhcp_document(env, host, network_id, network_document['name']) # make sure that self.regions is not empty. if not ApiAccess.regions: fetcher = ApiFetchRegions() fetcher.setup(env=env, origin=self.origin) fetcher.get(project_id) self.log.info("add port binding to DHCP server.") port_id = DbFetchPort(). \ get_id_by_field(network_id, """device_owner LIKE "%dhcp" """) port = EventSubnetAdd(). \ add_port_document(env, port_id, network_name=network_document['name'], project_name=project) if port: port_handler. \ add_vnic_document(env, host, network_id, network_name=network_document['name'], mac_address=port['mac_address']) # add link for vservice - vnic FindLinksForVserviceVnics().add_links( search={"id": "qdhcp-%s" % network_id}) scanner = Scanner() scanner.setup(env=env, origin=self.origin) scanner.scan_cliques() FindLinksForVserviceVnics(). \ add_links(search={"id": "qdhcp-%s" % network_id}) scanner = Scanner() scanner.setup(env=env, origin=self.origin) scanner.scan_cliques() if subnet['enable_dhcp'] is False and subnets[key]['enable_dhcp']: # delete existed related DHCP documents. self.inv.delete("inventory", {'id': "qdhcp-%s" % subnet['network_id']}) self.log.info("delete DHCP document: qdhcp-%s" % subnet['network_id']) port = self.inv.find_items( { 'network_id': subnet['network_id'], 'device_owner': 'network:dhcp' }, get_single=True) if 'id' in port: EventPortDelete().delete_port(env, port['id']) self.log.info("delete port binding to DHCP server.") if subnet['name'] == subnets[key]['name']: subnets[key] = subnet else: del subnets[key] subnets[subnet['name']] = subnet self.inv.set(network_document) return EventResult(result=True, related_object=subnet['id'], display_context=network_id) else: self.log.info('subnet not in network, aborting subnet update') return EventResult(result=False, retry=False)