From 6cc4359925a6f062afb31626a0dd5996698ae626 Mon Sep 17 00:00:00 2001 From: Sachin Bansal Date: Wed, 22 Mar 2017 16:58:57 -0700 Subject: [PATCH] Add support for diff calculation in dependency tracker It is possible that processing an object can take a long time in schema transformer. During this time, many updates to the same objects could be received from rabbitmq. All those changes would be processed with the one update only. We should have a way to ignore the updates where no change is detected. With this change, we are adding support for specifying ref/prop fields for each object type and on update, we compare these fields from previously cached values. If no change is detected, we terminate the dependency tracker process. Closes-Bug: 1674514 Change-Id: I0b1244e5473ded2f085874f2f6bcdade74a5fe63 --- src/api-lib/vnc_api.py | 7 - src/config/common/utils.py | 6 + src/config/common/vnc_amqp.py | 10 +- src/config/common/vnc_db.py | 65 +- src/config/schema-transformer/config_db.py | 707 +++++++++--------- .../test/test_route_table.py | 2 +- 6 files changed, 422 insertions(+), 375 deletions(-) diff --git a/src/api-lib/vnc_api.py b/src/api-lib/vnc_api.py index 3deff9309b6..174d2765aa3 100644 --- a/src/api-lib/vnc_api.py +++ b/src/api-lib/vnc_api.py @@ -42,13 +42,6 @@ def wrapper(self, *args, **kwargs): return func(self, *args, **kwargs) return wrapper -def compare_refs(old_refs, new_refs): - # compare refs in an object - old_ref_dict = dict((':'.join(ref['to']), ref['attr']) for ref in old_refs or []) - new_ref_dict = dict((':'.join(ref['to']), ref['attr']) for ref in new_refs or []) - return old_ref_dict == new_ref_dict -# end compare_refs - def get_object_class(res_type): cls_name = '%s' %(utils.CamelCase(res_type)) return utils.str_to_class(cls_name, __name__) diff --git a/src/config/common/utils.py b/src/config/common/utils.py index 78ee5daaa49..457e3e1dd77 100644 --- a/src/config/common/utils.py +++ b/src/config/common/utils.py @@ -178,3 +178,9 @@ def shareinfo_from_perms2(field): return x # end +def compare_refs(old_refs, new_refs): + # compare refs in an object + old_ref_dict = dict((':'.join(ref['to']), ref.get('attr')) for ref in old_refs or []) + new_ref_dict = dict((':'.join(ref['to']), ref.get('attr')) for ref in new_refs or []) + return old_ref_dict == new_ref_dict +# end compare_refs diff --git a/src/config/common/vnc_amqp.py b/src/config/common/vnc_amqp.py index bd6f2385a80..4f9481d3d78 100644 --- a/src/config/common/vnc_amqp.py +++ b/src/config/common/vnc_amqp.py @@ -38,8 +38,8 @@ def msgbus_store_err_msg(self, msg): self.msg_tracer.error = msg def msgbus_trace_msg(self): - self.msg_tracer.trace_msg(name='MessageBusNotifyTraceBuf', - sandesh=self.logger._sandesh) + self.msg_tracer.trace_msg(name='MessageBusNotifyTraceBuf', + sandesh=self.logger._sandesh) def _vnc_subscribe_callback(self, oper_info): self._db_resync_done.wait() @@ -137,7 +137,11 @@ def handle_update(self): return try: - self.obj.update() + if self.obj.update() == False: + # If update returns a False it indicates nothing has changed. + # If it returns True or None, then some change was detected. + # If no change, then terminate dependency tracker + return except NoIdError: obj_id = self.oper_info['uuid'] self.logger.warning('%s uuid %s update caused NoIdError' % diff --git a/src/config/common/vnc_db.py b/src/config/common/vnc_db.py index bd0aca26f7a..997f57c08a3 100644 --- a/src/config/common/vnc_db.py +++ b/src/config/common/vnc_db.py @@ -7,7 +7,8 @@ """ from exceptions import NoIdError from vnc_api.gen.resource_client import * -from utils import obj_type_to_vnc_class +from utils import obj_type_to_vnc_class, compare_refs + class DBBase(object): # This is the base class for all DB objects. All derived objects must @@ -191,6 +192,7 @@ def get_single_ref_attr(self, ref_type, obj): return None # end get_single_ref_attr + # Update a single ref. Return True if any update was made def update_single_ref(self, ref_type, obj): if isinstance(obj, dict): refs = obj.get(ref_type+'_refs') or obj.get(ref_type+'_back_refs') @@ -204,14 +206,17 @@ def update_single_ref(self, ref_type, obj): new_key = None old_key = getattr(self, ref_type, None) if old_key == new_key: - return - ref_obj = self.get_obj_type_map()[ref_type].get(old_key) - if ref_obj is not None: - ref_obj.delete_ref(self.obj_type, self.get_key()) - ref_obj = self.get_obj_type_map()[ref_type].get(new_key) - if ref_obj is not None: - ref_obj.add_ref(self.obj_type, self.get_key()) + return False + ref_cls = self.get_obj_type_map().get(ref_type) + if ref_cls: + ref_obj = ref_cls.get(old_key) + if ref_obj is not None: + ref_obj.delete_ref(self.obj_type, self.get_key()) + ref_obj = ref_cls.get(new_key) + if ref_obj is not None: + ref_obj.add_ref(self.obj_type, self.get_key()) setattr(self, ref_type, new_key) + return True # end update_single_ref def set_children(self, ref_type, obj): @@ -226,6 +231,7 @@ def set_children(self, ref_type, obj): setattr(self, ref_type+'s', new_refs) # end set_children + # Update a multiple refs. Return True if any update was made def update_multiple_refs(self, ref_type, obj): if isinstance(obj, dict): refs = obj.get(ref_type+'_refs') or obj.get(ref_type+'_back_refs') @@ -238,6 +244,8 @@ def update_multiple_refs(self, ref_type, obj): new_key = self._get_ref_key(ref, ref_type) new_refs.add(new_key) old_refs = getattr(self, ref_type+'s') + if old_refs == new_refs: + return False for ref_key in old_refs - new_refs: ref_obj = self.get_obj_type_map()[ref_type].get(ref_key) if ref_obj is not None: @@ -247,8 +255,15 @@ def update_multiple_refs(self, ref_type, obj): if ref_obj is not None: ref_obj.add_ref(self.obj_type, self.get_key()) setattr(self, ref_type+'s', new_refs) + return True # end update_multiple_refs + def update_refs(self, ref_type, obj): + if hasattr(self, ref_type): + return self.update_single_ref(ref_type, obj) + elif isinstance(getattr(self, ref_type+'s', None), set): + return self.update_multiple_refs(ref_type, obj) + def update_multiple_refs_with_attr(self, ref_type, obj): if isinstance(obj, dict): refs = obj.get(ref_type+'_refs') or obj.get(ref_type+'_back_refs') @@ -261,17 +276,21 @@ def update_multiple_refs_with_attr(self, ref_type, obj): new_key = self._get_ref_key(ref, ref_type) new_refs[new_key] = ref.get('attr') old_refs = getattr(self, ref_type+'s') + update = False for ref_key in set(old_refs.keys()) - set(new_refs.keys()): + update = True ref_obj = self.get_obj_type_map()[ref_type].get(ref_key) if ref_obj is not None: ref_obj.delete_ref(self.obj_type, self.get_key()) for ref_key in new_refs: if ref_key in old_refs and new_refs[ref_key] == old_refs[ref_key]: continue + update = True ref_obj = self.get_obj_type_map()[ref_type].get(ref_key) if ref_obj is not None: ref_obj.add_ref(self.obj_type, self.get_key(), new_refs[ref_key]) setattr(self, ref_type+'s', new_refs) + return update # end update_multiple_refs @classmethod @@ -305,6 +324,36 @@ def read_vnc_obj(cls, uuid=None, fq_name=None, obj_type=None, fields=None): return obj # end read_vnc_obj + def update_vnc_obj(self, obj=None): + if obj: + old_obj = None + self.obj = obj + else: + old_obj = getattr(self, 'obj', None) + uuid = getattr(self, 'uuid', None) + if uuid: + self.obj = self.read_vnc_obj(uuid=uuid) + else: + self.obj = self.read_vnc_obj(fq_name=self.name) + + changed = [] + for field in self.ref_fields or []: + old_field = getattr(old_obj, field+'_refs', None) + new_field = getattr(self.obj, field+'_refs', None) + if compare_refs(old_field, new_field): + continue + self.update_refs(field, self.obj) + changed.append(field) + for field in self.prop_fields or []: + old_field = getattr(old_obj, field, None) + new_field = getattr(self.obj, field, None) + if old_field == new_field: + continue + if hasattr(self, field): + setattr(self, field, new_field) + changed.append(field) + return changed + @classmethod def list_obj(cls, obj_type=None, fields=None): obj_type = obj_type or cls.obj_type diff --git a/src/config/schema-transformer/config_db.py b/src/config/schema-transformer/config_db.py index 163da28f04d..8a30b2d7f26 100644 --- a/src/config/schema-transformer/config_db.py +++ b/src/config/schema-transformer/config_db.py @@ -1,7 +1,6 @@ # # Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. # - """ This file contains config data model for schema transformer """ @@ -19,7 +18,8 @@ import itertools import cfgm_common as common from netaddr import IPNetwork, IPAddress -from cfgm_common.exceptions import * +from cfgm_common.exceptions import NoIdError, RefsExistError, BadRequest +from cfgm_common.exceptions import HttpError from cfgm_common import svc_info from cfgm_common.vnc_db import DBBase from vnc_api.vnc_api import * @@ -57,8 +57,8 @@ def _access_control_list_update(acl_obj, name, obj, entries): return acl_obj except (NoIdError, BadRequest) as e: DBBaseST._logger.error( - "Error while creating acl %s for %s: %s"%( - name, obj.get_fq_name_str(), str(e))) + "Error while creating acl %s for %s: %s" % + (name, obj.get_fq_name_str(), str(e))) return None else: if entries is None: @@ -80,11 +80,11 @@ def _access_control_list_update(acl_obj, name, obj, entries): DBBaseST._vnc_lib.access_control_list_update(acl_obj) except HttpError as he: DBBaseST._logger.error( - "HTTP error while updating acl %s for %s: %d, %s"%( - name, obj.get_fq_name_str(), he.status_code, he.content)) + "HTTP error while updating acl %s for %s: %d, %s" % + (name, obj.get_fq_name_str(), he.status_code, he.content)) except NoIdError: - DBBaseST._logger.error("NoIdError while updating acl %s for %s"%( - name, obj.get_fq_name_str())) + DBBaseST._logger.error("NoIdError while updating acl %s for %s" % + (name, obj.get_fq_name_str())) return acl_obj # end _access_control_list_update @@ -92,6 +92,15 @@ def _access_control_list_update(acl_obj, name, obj, entries): class DBBaseST(DBBase): obj_type = __name__ _indexed_by_name = True + ref_fields = [] + prop_fields = [] + + def update(self, obj=None): + return self.update_vnc_obj(obj) + + def delete_obj(self): + for ref_field in self.ref_fields: + self.update_refs(ref_field, {}) def evaluate(self): # Implement in the derived class @@ -114,6 +123,11 @@ def handle_st_object_req(self): st_obj.object_uuid = self.obj.uuid except AttributeError: pass + st_obj.obj_refs = [self._get_sandesh_ref_list(field) + for field in self.ref_fields] + st_obj.properties = [sandesh.PropList(field, str(getattr(self, field))) + for field in self.prop_fields + if hasattr(self, field)] return st_obj def _get_sandesh_ref_list(self, ref_type): @@ -121,7 +135,7 @@ def _get_sandesh_ref_list(self, ref_type): ref = getattr(self, ref_type) refs = [ref] if ref else [] except AttributeError: - refs = getattr(self, ref_type+'s') + refs = getattr(self, ref_type + 's') if isinstance(refs, dict): refs = refs.keys() return sandesh.RefList(ref_type, refs) @@ -152,8 +166,9 @@ def __init__(self, name, obj): def update(self, obj=None): self.obj = obj or self.read_vnc_obj(uuid=self.uuid) - self.update_autonomous_system(self.obj.autonomous_system) - self.update_ibgp_auto_mesh(self.obj.ibgp_auto_mesh) + ret = self.update_autonomous_system(self.obj.autonomous_system) + ret = self.update_ibgp_auto_mesh(self.obj.ibgp_auto_mesh) or ret + return ret # end update @classmethod @@ -169,12 +184,12 @@ def get_ibgp_auto_mesh(cls): @classmethod def update_autonomous_system(cls, new_asn): if int(new_asn) == cls._autonomous_system: - return + return False # From the global route target list, pick ones with the # changed ASN, and update the routing instances' referred # by the route target for route_tgt in RouteTargetST.values(): - _,asn,target = route_tgt.obj.get_fq_name()[0].split(':') + _, asn, target = route_tgt.obj.get_fq_name()[0].split(':') if int(asn) != cls.get_autonomous_system(): continue if int(target) < common.BGP_RTGT_MIN_ID: @@ -214,6 +229,7 @@ def update_autonomous_system(cls, new_asn): RouteTargetST.delete_vnc_obj(old_rtgt_obj.get_fq_name()[0]) cls._autonomous_system = int(new_asn) + return True # end update_autonomous_system def evaluate(self): @@ -230,7 +246,10 @@ def evaluate(self): def update_ibgp_auto_mesh(cls, value): if value is None: value = True + if cls._ibgp_auto_mesh == value: + return False cls._ibgp_auto_mesh = value + return True # end update_ibgp_auto_mesh # end GlobalSystemConfigST @@ -243,14 +262,16 @@ def update_ibgp_auto_mesh(cls, value): class VirtualNetworkST(DBBaseST): _dict = {} obj_type = 'virtual_network' + ref_fields = ['network_policy', 'virtual_machine_interface', 'route_table', + 'bgpvpn', 'network_ipam'] + prop_fields = ['virtual_network_properties', 'route_target_list', + 'multi_policy_service_chains_enabled'] def me(self, name): return name in (self.name, 'any') def __init__(self, name, obj=None, acl_dict=None): - self.obj = obj or self.read_vnc_obj(fq_name=name) self.name = name - self.uuid = self.obj.uuid self.network_policys = OrderedDict() self.virtual_machine_interfaces = set() self.connections = set() @@ -260,13 +281,15 @@ def __init__(self, name, obj=None, acl_dict=None): self.dynamic_acl = None self.acl_rule_count = 0 self.multi_policy_service_chains_enabled = None + self.update_vnc_obj(obj) + self.uuid = self.obj.uuid for acl in self.obj.get_access_control_lists() or []: if acl_dict: acl_obj = acl_dict[acl['uuid']] else: - acl_obj = self.read_vnc_obj(acl['uuid'], - obj_type='access_control_list', - fields=['access_control_list_hash']) + acl_obj = self.read_vnc_obj( + acl['uuid'], obj_type='access_control_list', + fields=['access_control_list_hash']) if acl_obj.name == self.obj.name: self.acl = acl_obj elif acl_obj.name == 'dynamic': @@ -316,7 +339,7 @@ def get_routes(self): # end def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(uuid=self.uuid) + ret = self.update_vnc_obj(obj) old_policies = set(self.network_policys.keys()) self.network_policys = OrderedDict() @@ -327,31 +350,30 @@ def update(self, obj=None): self.add_policy(policy_name) for policy_name in old_policies - set(self.network_policys.keys()): policy = NetworkPolicyST.get(policy_name) - if not policy: - continue - policy.virtual_networks.discard(self.name) + if policy: + policy.virtual_networks.discard(self.name) for policy_name in set(self.network_policys.keys()) - old_policies: policy = NetworkPolicyST.get(policy_name) - if not policy: - continue - policy.virtual_networks.add(self.name) + if policy: + policy.virtual_networks.add(self.name) - self.update_multiple_refs('route_table', self.obj) - self.update_multiple_refs('bgpvpn', self.obj) + old_ipams = self.ipams self.ipams = {} for ipam_ref in self.obj.get_network_ipam_refs() or []: subnet = ipam_ref['attr'] - self.ipams[':'.join(ipam_ref['to'])] = subnet - self.update_ipams() + ipam_fq_name = ':'.join(ipam_ref['to']) + if self.ipams.get(ipam_fq_name) != subnet: + self.ipams[ipam_fq_name] = subnet + if self.ipams != old_ipams: + self.update_ipams() prop = self.obj.get_virtual_network_properties( ) or VirtualNetworkType() self.set_properties(prop) self.set_route_target_list(self.obj) - mpsce = self.obj.get_multi_policy_service_chains_enabled() - if mpsce != self.multi_policy_service_chains_enabled: - self.multi_policy_service_chains_enabled = mpsce - self.multi_policy_service_chains_status_changed = True + self.multi_policy_service_chains_status_changed = ( + 'multi_policy_service_chains_enabled' in ret) + return ret # end update def _update_primary_ri_to_service_ri_connection(self, sc, si_name, @@ -422,7 +444,8 @@ def delete_inactive_service_chains(self, old_scs, new_scs=None): if remote_vn is None: remote_service_chain_list = [] else: - remote_service_chain_list = remote_vn.service_chains.get(self.name) + remote_service_chain_list = remote_vn.service_chains.get( + self.name) # Get a list of this VN's service chains which has a # remote VN name as one of its service endpoints. @@ -438,8 +461,8 @@ def delete_inactive_service_chains(self, old_scs, new_scs=None): # VNs that are referring to this SC. service_chain_list = old_scs[remote_vn_name] for service_chain in service_chain_list or []: - if new_scs and service_chain in (new_scs.get(remote_vn_name) - or []): + if new_scs and service_chain in (new_scs.get(remote_vn_name) or + []): continue if service_chain in (remote_service_chain_list or []): service_chain.destroy() @@ -486,12 +509,15 @@ def add_policy(self, policy_name, attrib=None): self._logger.error("Cannot assign policy %s to %s: sequence " "number is not available" % (policy_name, self.name)) - return + return False + if self.network_policys.get(policy_name) == attrib: + return False self.network_policys[policy_name] = attrib self.network_policys = OrderedDict(sorted(self.network_policys.items(), key=lambda t:(t[1].sequence.major, t[1].sequence.minor))) + return True # end add_policy def get_primary_routing_instance(self): @@ -540,7 +566,7 @@ def add_service_chain(self, svn, dvn, proto, prule): return {} if self.name == remote_vn: self._logger.error("Service chain source and dest vn are same: %s" - % self.name) + % self.name) return None if remote_vn == 'any': remote_vns = self.get_vns_in_project() @@ -639,11 +665,11 @@ def get_route_target(self): # end get_route_target def set_route_target(self, rtgt_name): - _,asn,target = rtgt_name.split(':') + _, asn, target = rtgt_name.split(':') if int(asn) != GlobalSystemConfigST.get_autonomous_system(): return self._route_target = int(target) - #end set_route_target + # end set_route_target @staticmethod def _ri_needs_external_rt(vn_name, ri_name): @@ -680,8 +706,9 @@ def expand_connections(self): def set_properties(self, properties): if self.allow_transit == properties.allow_transit: # If allow_transit didn't change, then we have nothing to do - return + return False self.allow_transit = properties.allow_transit + ret = False for sc_list in self.service_chains.values(): for service_chain in sc_list: if not service_chain.created: @@ -705,6 +732,8 @@ def set_properties(self, properties): # if the network is not a transit network any more, then we # need to delete the route target from service RIs ri.update_route_target_list(rt_del=[self.get_route_target()]) + ret = True + return ret # end set_properties def get_route_target_lists(self, obj): @@ -771,7 +800,7 @@ def set_route_target_list(self, obj): self.bgpvpn_export_rt_list) ) if not (rt_add or rt_add_export or rt_add_import or rt_del): - return + return False for rt in itertools.chain(rt_add, rt_add_export, rt_add_import): RouteTargetST.locate(rt) @@ -789,7 +818,7 @@ def set_route_target_list(self, obj): for route in self.get_routes(): prefix = route.prefix nexthop = route.next_hop - (left_ri, si) = self._get_routing_instance_from_route(nexthop) + (left_ri, _) = self._get_routing_instance_from_route(nexthop) if left_ri is None: continue left_ri.update_route_target_list(rt_add_import=rt_add, @@ -821,6 +850,7 @@ def set_route_target_list(self, obj): # if other routing instances are referring to this target, # it will be deleted when those instances are deleted pass + return True # end set_route_target_list # next-hop in a route contains fq-name of a service instance, which must @@ -838,7 +868,7 @@ def _get_routing_instance_from_route(self, next_hop): left_vn = VirtualNetworkST.get(si.left_vn_str) if left_vn is None: self._logger.error("Virtual network %s not present" - % si.left_vn_str) + % si.left_vn_str) return (None, None) return (left_vn.get_primary_routing_instance(), si) # end _get_routing_instance_from_route @@ -878,13 +908,14 @@ def uve_send(self, deleted=False): # one way connection vn_trace.partially_connected_networks.append(rhs) # end for - vn_msg = UveVirtualNetworkConfigTrace(data=vn_trace, sandesh=self._sandesh) + vn_msg = UveVirtualNetworkConfigTrace(data=vn_trace, + sandesh=self._sandesh) vn_msg.send(sandesh=self._sandesh) # end uve_send def get_service_name(self, sc_name, si_name): name = "service-%s-%s" % (sc_name, si_name) - return "%s:%s" %(self.obj.get_fq_name_str(), name.replace(':', '_')) + return "%s:%s" % (self.obj.get_fq_name_str(), name.replace(':', '_')) # end get_service_name @staticmethod @@ -1074,19 +1105,19 @@ def policy_to_acl_rule(self, prule, dynamic): if self.me(sa.virtual_network): service_ri = service_ris.get(da.virtual_network, [None])[0] elif self.me(da.virtual_network): - service_ri = service_ris.get(sa.virtual_network, [None, None])[1] + service_ri = service_ris.get(sa.virtual_network, + [None, None])[1] acl = self.add_acl_rule( - sa, sp, da, dp, arule_proto, rule_uuid, - prule.action_list, prule.direction, - service_ri) + sa, sp, da, dp, arule_proto, rule_uuid, + prule.action_list, prule.direction, service_ri) result_acl_rule_list.append(acl) acl_direction_comp = self._manager._args.acl_direction_comp - if ((prule.direction == "<>") and (sa != da or sp != dp) - and (not acl_direction_comp)): + if ((prule.direction == "<>") and + (sa != da or sp != dp) and + (not acl_direction_comp)): acl = self.add_acl_rule( - da, dp, sa, sp, arule_proto, rule_uuid, - prule.action_list, prule.direction, - service_ri) + da, dp, sa, sp, arule_proto, rule_uuid, + prule.action_list, prule.direction, service_ri) result_acl_rule_list.append(acl) # end for sp, dp @@ -1222,18 +1253,21 @@ def evaluate(self): dst_address.subnet = None dst_address.subnet_list = [] - acl = self.add_acl_rule(src_address, PortType(), dst_address, - PortType(), "any", rule.get_rule_uuid(), - ActionListType("deny"), rule.direction) + acl = self.add_acl_rule( + src_address, PortType(), dst_address, PortType(), + "any", rule.get_rule_uuid(), ActionListType("deny"), + rule.direction) acl_list.append(acl) acl_direction_comp = self._manager._args.acl_direction_comp - if ((rule.direction == "<>") and (src_address != dst_address) - and (not acl_direction_comp)): - acl = self.add_acl_rule(src_address, PortType(), dst_address, - PortType(), "any", rule.get_rule_uuid(), - ActionListType("deny"), rule.direction) + if ((rule.direction == "<>") and + (src_address != dst_address) and + (not acl_direction_comp)): + acl = self.add_acl_rule( + src_address, PortType(), dst_address, PortType(), + "any", rule.get_rule_uuid(), + ActionListType("deny"), rule.direction) acl_list.append(acl) # end for rule @@ -1345,21 +1379,15 @@ def get_prefixes(self, ip_version): def handle_st_object_req(self): resp = super(VirtualNetworkST, self).handle_st_object_req() - resp.obj_refs = [ + resp.obj_refs.extend([ self._get_sandesh_ref_list('routing_instance'), - self._get_sandesh_ref_list('network_policy'), - self._get_sandesh_ref_list('virtual_machine_interface'), sandesh.RefList('virtual_network', self.connections), - self._get_sandesh_ref_list('route_table'), - self._get_sandesh_ref_list('service_chain'), - self._get_sandesh_ref_list('bgpvpn'), - ] - resp.properties = [ + self._get_sandesh_ref_list('service_chain') + ]) + resp.properties.extend([ sandesh.PropList('route_target', self.get_route_target()), sandesh.PropList('network_id', str(self.obj.get_virtual_network_network_id())), - sandesh.PropList('multi_service_chains', - str(self.multi_policy_service_chains_enabled)), sandesh.PropList('rt_list', ', '.join(self.rt_list)), sandesh.PropList('import_rt_list', ', '.join(self.import_rt_list)), @@ -1370,7 +1398,7 @@ def handle_st_object_req(self): ', '.join(self.bgpvpn_import_rt_list)), sandesh.PropList('bgpvpn_export_rt_list', ', '.join(self.bgpvpn_export_rt_list)), - ] + ]) return resp # end handle_st_object_req @@ -1407,8 +1435,7 @@ def reinit(cls): for ri, val in cls._object_db._rt_cf.get_range(): rt = val['rtgt_num'] asn = GlobalSystemConfigST.get_autonomous_system() - rt_key = "target:%s:%s" % ( - GlobalSystemConfigST.get_autonomous_system(), rt) + rt_key = "target:%s:%s" % (asn, rt) if rt_key not in cls: cls._object_db.free_route_target(ri) # end reinit @@ -1423,7 +1450,7 @@ def __init__(self, rt_key, obj=None): # end __init__ def update(self, obj=None): - pass + return False @classmethod def delete_vnc_obj(cls, key): @@ -1442,6 +1469,7 @@ def delete_vnc_obj(cls, key): class NetworkPolicyST(DBBaseST): _dict = {} obj_type = 'network_policy' + prop_fields = ['network_policy_entries'] _internal_policies = set() _service_instances = {} _network_policys = {} @@ -1489,14 +1517,20 @@ def get_by_service_instance(cls, si_name): return cls._service_instances.get(si_name, set()) def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) - self.add_rules(self.obj.get_network_policy_entries()) + ret = self.update_vnc_obj(obj) + if ret: + self.add_rules(self.obj.get_network_policy_entries()) + return ret # end update def add_rules(self, entries): if entries is None: + if not self.rules: + return False self.rules = [] else: + if self.rules == entries.policy_rule: + return False self.rules = entries.policy_rule np_set = set() si_set = set() @@ -1531,6 +1565,7 @@ def add_rules(self, entries): policy.network_policys = policy_set self.referred_policies = np_set self.update_service_instances(si_set) + return True # end add_rules def update_service_instances(self, si_set): @@ -1586,6 +1621,7 @@ def handle_st_object_req(self): class RouteTableST(DBBaseST): _dict = {} obj_type = 'route_table' + prop_fields = ['routes'] _service_instances = {} @@ -1597,20 +1633,20 @@ def __init__(self, name, obj=None): self.routes = [] self.update(obj) self.update_multiple_refs('virtual_network', self.obj) + self.update_multiple_refs('logical_router', self.obj) # end __init__ def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) - self.routes = [] - routes = self.obj.get_routes() - if routes: - self.routes = routes.get_route() or [] - si_set = set() - for route in self.routes: - if route.next_hop_type != 'ip-address': - si_set.add(route.next_hop) - self.update_service_instances(si_set) - self.update_multiple_refs('logical_router', self.obj) + changed = self.update_vnc_obj(obj) + if 'routes' in changed: + if self.routes is None: + self.routes = [] + else: + self.routes = self.routes.get_route() or [] + si_set = set([route.next_hop for route in self.routes + if route.next_hop_type != 'ip-address']) + self.update_service_instances(si_set) + return changed # end update def update_service_instances(self, si_set): @@ -1665,6 +1701,7 @@ def handle_st_object_req(self): class SecurityGroupST(DBBaseST): _dict = {} obj_type = 'security_group' + prop_fields = ['security_group_entries', 'configured_security_group_id'] _sg_dict = {} def __init__(self, name, obj=None, acl_dict=None): @@ -1676,11 +1713,12 @@ def _get_acl(uuid): self.name = name self.obj = obj or self.read_vnc_obj(fq_name=name) self.uuid = self.obj.uuid - self.config_sgid = None + self.configured_security_group_id = None self.sg_id = None self.ingress_acl = None self.egress_acl = None self.referred_sgs = set() + self.security_group_entries = None acls = self.obj.get_access_control_lists() for acl in acls or []: if acl['to'][-1] == 'egress-access-control-list': @@ -1694,20 +1732,21 @@ def _get_acl(uuid): # end __init__ def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(uuid=self.uuid) - self.rule_entries = self.obj.get_security_group_entries() + changed = self.update_vnc_obj(obj) # TODO(ethuleau): We keep the virtual network and security group ID # allocation in schema and in the vnc API for one # release overlap to prevent any upgrade issue. So the # following code need to be remove in release (3.2 + 1) - config_id = self.obj.get_configured_security_group_id() or 0 - self.set_configured_security_group_id(config_id) - self.process_referred_sgs() + if 'configured_security_group_id' in changed: + self.set_configured_security_group_id() + if changed: + self.process_referred_sgs() + return changed # end update def process_referred_sgs(self): - if self.rule_entries: - prules = self.rule_entries.get_policy_rule() or [] + if self.security_group_entries: + prules = self.security_group_entries.get_policy_rule() or [] else: prules = [] @@ -1743,11 +1782,9 @@ def process_referred_sgs(self): # allocation in schema and in the vnc API for one # release overlap to prevent any upgrade issue. So the # following code need to be remove in release (3.2 + 1) - def set_configured_security_group_id(self, config_id): - if self.config_sgid == config_id: - return - self.config_sgid = config_id + def set_configured_security_group_id(self): sg_id = self.obj.get_security_group_id() + config_id = self.configured_security_group_id or 0 if sg_id is not None: sg_id = int(sg_id) if config_id: @@ -1774,6 +1811,7 @@ def set_configured_security_group_id(self, config_id): if sg_id != int(self.obj.get_security_group_id()): self._vnc_lib.security_group_update(self.obj) self.sg_id = self.obj.get_security_group_id() + return True # end set_configured_security_group_id def delete_obj(self): @@ -1786,12 +1824,12 @@ def delete_obj(self): # release overlap to prevent any upgrade issue. So the # following code need to be remove in release (3.2 + 1) sg_id = self.obj.get_security_group_id() - if sg_id is not None and not self.config_sgid: + if sg_id is not None and not self.configured_security_group_id: if sg_id < SGID_MIN_ALLOC: self._object_db.free_sg_id(sg_id) else: self._object_db.free_sg_id(sg_id-SGID_MIN_ALLOC) - self.rule_entries = None + self.security_group_entries = None self.process_referred_sgs() # end delete_obj @@ -1802,8 +1840,8 @@ def update_policy_entries(self): ingress_acl_entries = AclEntriesType() egress_acl_entries = AclEntriesType() - if self.rule_entries: - prules = self.rule_entries.get_policy_rule() or [] + if self.security_group_entries: + prules = self.security_group_entries.get_policy_rule() or [] else: prules = [] @@ -1898,11 +1936,10 @@ def handle_st_object_req(self): self._get_sandesh_ref_list('security_group'), sandesh.RefList('referred_security_group', self.referred_sgs) ] - resp.properties = [ + resp.properties.extend([ sandesh.PropList('sg_id', str(self.sg_id)), - sandesh.PropList('configured_id', str(self.config_sgid)) ] + [sandesh.PropList('rule', str(rule)) - for rule in self.rule_entries.get_policy_rule() or []] + for rule in self.security_group_entries.get_policy_rule() or []]) return resp # end handle_st_object_req # end class SecurityGroupST @@ -1973,7 +2010,7 @@ def __init__(self, name, obj=None): def update(self, obj=None): # Nothing to do - pass + return False @classmethod def create(cls, fq_name, vn_obj, has_pnf=False): @@ -2306,7 +2343,8 @@ def update_static_routes(self): for route_table_name in route_tables: route_table = RouteTableST.get(route_table_name) if route_table is None or not route_table.routes: - self._logger.debug("route table/routes None for: " + route_table_name) + self._logger.debug("route table/routes None for: " + + route_table_name) continue route_targets = set() for lr_name in route_table.logical_routers: @@ -2331,8 +2369,9 @@ def update_static_routes(self): all_route_targets |= route_targets if old_route_target_list != all_route_targets: - self.update_route_target_list(rt_add_import=all_route_targets - old_route_target_list, - rt_del=old_route_target_list - all_route_targets) + self.update_route_target_list( + rt_add_import=all_route_targets - old_route_target_list, + rt_del=old_route_target_list - all_route_targets) #update static ip routes vn_obj = VirtualNetworkST.get(self.virtual_network) @@ -2572,7 +2611,7 @@ def update_ipams(self, vn2_name): def log_error(self, msg): self.error_msg = msg - self._logger.error('service chain %s: %s' %(self.name, msg)) + self._logger.error('service chain %s: %s' % (self.name, msg)) # end log_error def _get_vm_pt_info(self, vm_pt, mode): @@ -2947,7 +2986,7 @@ def _port_is_subset(lhs, rhs): @staticmethod def _address_is_subset(lhs, rhs): if not(rhs.subnet or lhs.subnet or lhs.subnet_list or rhs.subnet_list): - return rhs.virtual_network in [lhs.virtual_network, 'any'] + return rhs.virtual_network in [lhs.virtual_network, 'any'] l_subnets = lhs.subnet_list or [] if lhs.subnet: l_subnets.append(lhs.subnet) @@ -2997,18 +3036,25 @@ def update_acl_entries(self, acl_entries): class BgpRouterST(DBBaseST): _dict = {} obj_type = 'bgp_router' + prop_fields = ['bgp_router_parameters'] + ref_fields = ['bgp_as_a_service'] def __init__(self, name, obj=None): self.name = name self.asn = None self.bgp_as_a_service = None + self.vendor = None + self.identifier = None + self.router_type = None + self.source_port = None self.update(obj) # end __init__ def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) - self.set_params(self.obj.get_bgp_router_parameters()) - self.update_single_ref('bgp_as_a_service', self.obj) + changed = self.update_vnc_obj(obj) + if 'bgp_router_parameters' in changed: + self.set_params(self.obj.get_bgp_router_parameters()) + return changed # end update def delete_obj(self): @@ -3046,9 +3092,10 @@ def update_global_asn(self, asn): def update_autonomous_system(self, asn): if self.asn == int(asn): - return + return False self.asn = int(asn) self.update_peering() + return True # end update_autonomous_system def evaluate(self): @@ -3094,17 +3141,17 @@ def update_bgpaas_client(self, bgpaas): return -1 update = False params = self.obj.get_bgp_router_parameters() - if params.autonomous_system != int(bgpaas.asn): - params.autonomous_system = int(bgpaas.asn) + if params.autonomous_system != int(bgpaas.autonomous_system): + params.autonomous_system = int(bgpaas.autonomous_system) update = True - ip = bgpaas.ip_address or vmi.get_primary_instance_ip_address() + ip = bgpaas.bgpaas_ip_address or vmi.get_primary_instance_ip_address() if params.address != ip: params.address = ip update = True if params.identifier != ip: params.identifier = ip update = True - if bgpaas.obj.bgpaas_suppress_route_advertisement: + if bgpaas.bgpaas_suppress_route_advertisement: if params.gateway_address: params.gateway_address = None update = True @@ -3180,7 +3227,6 @@ def update_peering(self): def handle_st_object_req(self): resp = super(BgpRouterST, self).handle_st_object_req() - resp.obj_refs = [] resp.properties = [ sandesh.PropList('asn', str(self.asn)), sandesh.PropList('vendor', self.vendor), @@ -3194,14 +3240,24 @@ def handle_st_object_req(self): class BgpAsAServiceST(DBBaseST): _dict = {} obj_type = 'bgp_as_a_service' + prop_fields = ['bgpaas_ip_address', 'autonomous_system', + 'bgpaas_session_attributes', + 'bgpaas_suppress_route_advertisement', + 'bgpaas_ipv4_mapped_ipv6_nexthop'] + ref_fields = ['virtual_machine_interface', 'bgp_router'] def __init__(self, name, obj=None): self.name = name - self.obj = obj or self.read_vnc_obj(fq_name=name) self.virtual_machine_interfaces = set() self.bgp_routers = set() self.bgpaas_clients = {} - self.update(self.obj) + self.bgpaas_ip_address = None + self.autonomous_system = None + self.bgpaas_session_attributes = None + self.bgpaas_suppress_route_advertisement = None + self.bgpaas_ipv4_mapped_ipv6_nexthop = None + self.peering_attribs = None + self.update(obj) self.set_bgpaas_clients() # end __init__ @@ -3215,23 +3271,17 @@ def set_bgpaas_clients(self): # end set_bgp_clients def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) - self.ip_address = self.obj.get_bgpaas_ip_address() - self.asn = self.obj.get_autonomous_system() - session_attrib = self.obj.get_bgpaas_session_attributes() - bgp_session = BgpSession() - if session_attrib: - bgp_session.attributes=[session_attrib] - self.peering_attribs = BgpPeeringAttributes(session=[bgp_session]) - self.update_multiple_refs('virtual_machine_interface', self.obj) - self.update_multiple_refs('bgp_router', self.obj) + changed = self.update_vnc_obj(obj) + if 'bgpaas_session_attributes' in changed: + session_attrib = self.bgpaas_session_attributes + bgp_session = BgpSession() + if session_attrib: + bgp_session.attributes=[session_attrib] + peering_attribs = BgpPeeringAttributes(session=[bgp_session]) + self.peering_attribs = BgpPeeringAttributes(session=[bgp_session]) + return changed # end update - def delete_obj(self): - self.update_multiple_refs('virtual_machine_interface', {}) - self.update_multiple_refs('bgp_router', {}) - # end delete_obj - def evaluate(self): for name in (self.virtual_machine_interfaces - set(self.bgpaas_clients.keys())): @@ -3276,9 +3326,9 @@ def create_bgp_router(self, name): else: bgp_router = self._vnc_lib.bgp_router_read(id=bgpr.obj.uuid) src_port = bgpr.source_port - ip = self.ip_address or vmi.get_primary_instance_ip_address() + ip = self.bgpaas_ip_address or vmi.get_primary_instance_ip_address() params = BgpRouterParams( - autonomous_system=int(self.asn) if self.asn else None, + autonomous_system=int(self.autonomous_system) if self.autonomous_system else None, address=ip, identifier=ip, source_port=src_port, @@ -3299,23 +3349,14 @@ def create_bgp_router(self, name): self.bgpaas_clients[name] = router_fq_name # end create_bgp_router - def handle_st_object_req(self): - resp = super(BgpAsAServiceST, self).handle_st_object_req() - resp.obj_refs = [ - self._get_sandesh_ref_list('virtual_machine_interface'), - self._get_sandesh_ref_list('bgp_router') - ] - resp.properties = [ - sandesh.PropList('ip_address', self.ip_address), - sandesh.PropList('asn', str(self.asn)) - ] - return resp - # end handle_st_object_req # end class BgpAsAServiceST class VirtualMachineInterfaceST(DBBaseST): _dict = {} obj_type = 'virtual_machine_interface' + ref_fields = ['virtual_network', 'virtual_machine', 'port_tuple', + 'logical_router', 'bgp_as_a_service', 'routing_instance'] + prop_fields = ['virtual_machine_interface_properties'] def __init__(self, name, obj=None): self.name = name @@ -3331,28 +3372,21 @@ def __init__(self, name, obj=None): self.floating_ips = set() self.alias_ips = set() self.routing_instances = {} - self.obj = obj or self.read_vnc_obj(fq_name=name) + self.update(obj) self.uuid = self.obj.uuid self.update_multiple_refs('instance_ip', self.obj) self.update_multiple_refs('floating_ip', self.obj) self.update_multiple_refs('alias_ip', self.obj) self.vrf_table = jsonpickle.encode(self.obj.get_vrf_assign_table()) - self.update(self.obj) # end __init__ def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(uuid=self.obj.uuid) - self.update_single_ref('virtual_network', self.obj) - if self.obj.parent_type == 'virtual-machine': - self.add_to_parent(self.obj) - self.virtual_machine = self.obj.get_parent_fq_name_str() - else: - self.update_single_ref('virtual_machine', self.obj) - self.update_multiple_refs('port_tuple', self.obj) - self.update_single_ref('logical_router', self.obj) - self.update_single_ref('bgp_as_a_service', self.obj) - self.set_properties() - self.update_routing_instances(self.obj.get_routing_instance_refs()) + changed = self.update_vnc_obj(obj) + if 'virtual_machine_interface_properties' in changed: + self.set_properties() + if 'routing_instance' in changed: + self.update_routing_instances(self.obj.get_routing_instance_refs()) + return changed # end update def delete_obj(self): @@ -3377,31 +3411,39 @@ def evaluate(self): def get_any_instance_ip_address(self, ip_version=0): for ip_name in self.instance_ips: ip = InstanceIpST.get(ip_name) - if ip is None or ip.address is None: + if ip is None or ip.instance_ip_address is None: continue if not ip.service_instance_ip: continue if not ip_version or ip.ip_version == ip_version: - return ip.address + return ip.instance_ip_address return None # end get_any_instance_ip_address def get_primary_instance_ip_address(self, ip_version=4): for ip_name in self.instance_ips: ip = InstanceIpST.get(ip_name) - if ip.is_primary() and ip.address and ip_version == ip.ip_version: - return ip.address + if ip.is_primary() and ip.instance_ip_address and ip_version == ip.ip_version: + return ip.instance_ip_address return None # end get_primary_instance_ip_address def set_properties(self): props = self.obj.get_virtual_machine_interface_properties() if props: - self.service_interface_type = props.service_interface_type - self.interface_mirror = props.interface_mirror + service_interface_type = props.service_interface_type + interface_mirror = props.interface_mirror else: - self.service_interface_type = None - self.interface_mirror = None + service_interface_type = None + interface_mirror = None + ret = False + if service_interface_type != self.service_interface_type: + self.service_interface_type = service_interface_type + ret = True + if interface_mirror != self.interface_mirror: + self.interface_mirror = interface_mirror + ret = True + return ret # end set_properties def update_routing_instances(self, ri_refs): @@ -3543,21 +3585,21 @@ def recreate_vrf_assign_table(self): ip_list = [] for ip_name in self.instance_ips: ip = InstanceIpST.get(ip_name) - if ip and ip.address: - ip_list.append(ip) + if ip and ip.instance_ip_address: + ip_list.append((ip.ip_version, ip.instance_ip_address)) for ip_name in self.floating_ips: ip = FloatingIpST.get(ip_name) - if ip and ip.address: - ip_list.append(ip) + if ip and ip.floating_ip_address: + ip_list.append((ip.ip_version, ip.floating_ip_address)) for ip_name in self.alias_ips: ip = AliasIpST.get(ip_name) - if ip and ip.address: - ip_list.append(ip) - for ip in ip_list: - if ip.ip_version == 6: - address = AddressType(subnet=SubnetType(ip.address, 128)) + if ip and ip.alias_ip_address: + ip_list.append((ip.ip_version, ip.alias_ip_address)) + for (ip_version, ip_address) in ip_list: + if ip_version == 6: + address = AddressType(subnet=SubnetType(ip_address, 128)) else: - address = AddressType(subnet=SubnetType(ip.address, 32)) + address = AddressType(subnet=SubnetType(ip_address, 32)) mc = MatchConditionType(src_address=address, protocol='any', @@ -3598,15 +3640,15 @@ def recreate_vrf_assign_table(self): ignore_acl=True) vrf_table.add_vrf_assign_rule(vrf_rule) policy_rule_count += 1 - #end for dp - #end for sp + # end for dp + # end for sp # end for service_chain # end for service_chain_list if policy_rule_count == 0: vrf_table = None self._set_vrf_assign_table(vrf_table) - #end for vm_pt_list + # end for vm_pt_list # end recreate_vrf_assign_table def _set_vrf_assign_table(self, vrf_table): @@ -3623,15 +3665,11 @@ def _set_vrf_assign_table(self, vrf_table): def handle_st_object_req(self): resp = super(VirtualMachineInterfaceST, self).handle_st_object_req() - resp.obj_refs = [ + resp.obj_refs.extend([ self._get_sandesh_ref_list('instance_ip'), self._get_sandesh_ref_list('floating_ip'), self._get_sandesh_ref_list('alias_ip'), - self._get_sandesh_ref_list('virtual_network'), - self._get_sandesh_ref_list('virtual_machine'), - self._get_sandesh_ref_list('port_tuple'), - self._get_sandesh_ref_list('logical_router'), - ] + ]) resp.properties = [ sandesh.PropList('service_interface_type', self.service_interface_type), @@ -3673,139 +3711,90 @@ def get_ipv4_mapped_ipv6_gateway(self): class InstanceIpST(DBBaseST): _dict = {} obj_type = 'instance_ip' + prop_fields = ['instance_ip_address', 'service_instance_ip', + 'instance_ip_secondary'] + ref_fields = ['virtual_machine_interface'] def __init__(self, name, obj=None): self.name = name self.is_secondary = False self.virtual_machine_interfaces = set() self.ip_version = None + self.instance_ip_address = None + self.service_instance_ip = None + self.instance_ip_secondary = False self.update(obj) # end __init def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) - self.address = self.obj.get_instance_ip_address() - if self.address: - self.ip_version = IPAddress(self.address).version - self.service_instance_ip = self.obj.get_service_instance_ip() - self.is_secondary = self.obj.get_instance_ip_secondary() or False - self.update_multiple_refs('virtual_machine_interface', self.obj) + changed = self.update_vnc_obj(obj) + if 'instance_ip_address' in changed and self.instance_ip_address: + self.ip_version = IPAddress(self.instance_ip_address).version + return changed # end update def is_primary(self): - return not self.is_secondary - #end - - def delete_obj(self): - self.update_multiple_refs('virtual_machine_interface', {}) - - def handle_st_object_req(self): - resp = super(InstanceIpST, self).handle_st_object_req() - resp.obj_refs = [ - self._get_sandesh_ref_list('virtual_machine_interface'), - ] - resp.properties = [ - sandesh.PropList('address', self.address), - sandesh.PropList('service_instance_ip', - str(self.service_instance_ip)), - ] - return resp - # end handle_st_object_req + return not self.instance_ip_secondary # end InstanceIpST class FloatingIpST(DBBaseST): _dict = {} obj_type = 'floating_ip' - + prop_fields = ['floating_ip_address'] + ref_fields = ['virtual_machine_interface'] def __init__(self, name, obj=None): self.name = name self.virtual_machine_interface = None self.ip_version = None + self.floating_ip_address = None self.update(obj) # end __init def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) - self.address = self.obj.get_floating_ip_address() - if self.address: - self.ip_version = IPAddress(self.address).version - self.update_single_ref('virtual_machine_interface', self.obj) + changed = self.update_vnc_obj(obj) + if 'floating_ip_address' in changed and self.floating_ip_address: + self.ip_version = IPAddress(self.floating_ip_address).version + return changed # end update - - def delete_obj(self): - self.update_single_ref('virtual_machine_interface', {}) - - def handle_st_object_req(self): - resp = super(FloatingIpST, self).handle_st_object_req() - resp.obj_refs = [ - self._get_sandesh_ref_list('virtual_machine_interface'), - ] - resp.properties = [ - sandesh.PropList('address', self.address), - ] - return resp - # end handle_st_object_req # end FloatingIpST class AliasIpST(DBBaseST): _dict = {} obj_type = 'alias_ip' + prop_fields = ['floating_ip_address'] + ref_fields = ['virtual_machine_interface'] def __init__(self, name, obj=None): self.name = name self.virtual_machine_interface = None + self.alias_ip_address = None self.ip_version = None self.update(obj) # end __init def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) - self.address = self.obj.get_alias_ip_address() - if self.address: - self.ip_version = IPAddress(self.address).version - self.update_single_ref('virtual_machine_interface', self.obj) + changed = self.update_vnc_obj(obj) + if 'alias_ip_address' in changed and self.alias_ip_address: + self.ip_version = IPAddress(self.alias_ip_address).version + return changed # end update - - def delete_obj(self): - self.update_single_ref('virtual_machine_interface', {}) - - def handle_st_object_req(self): - resp = super(AliasIpST, self).handle_st_object_req() - resp.obj_refs = [ - self._get_sandesh_ref_list('virtual_machine_interface'), - ] - resp.properties = [ - sandesh.PropList('address', self.address), - ] - return resp - # end handle_st_object_req # end AliasIpST class VirtualMachineST(DBBaseST): _dict = {} obj_type = 'virtual_machine' - + ref_fields = ['service_instance'] def __init__(self, name, obj=None): self.name = name self.virtual_machine_interfaces = set() self.service_instance = None self.update(obj) self.uuid = self.obj.uuid - # end __init__ - - def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) self.update_multiple_refs('virtual_machine_interface', self.obj) - self.update_single_ref('service_instance', self.obj) - # end update - - def delete_obj(self): - self.update_multiple_refs('virtual_machine_interface', {}) - self.update_single_ref('service_instance', {}) - # end delete_obj + # end __init__ def get_vmi_by_service_type(self, service_type): for vmi_name in self.virtual_machine_interfaces: @@ -3827,13 +3816,12 @@ def get_service_mode(self): def handle_st_object_req(self): resp = super(VirtualMachineST, self).handle_st_object_req() - resp.obj_refs = [ + resp.obj_refs.extend([ self._get_sandesh_ref_list('virtual_machine_interface'), - self._get_sandesh_ref_list('service_instance'), - ] - resp.properties = [ + ]) + resp.properties.extend([ sandesh.PropList('service_mode', self.get_service_mode()), - ] + ]) return resp # end handle_st_object_req # end VirtualMachineST @@ -3842,18 +3830,20 @@ def handle_st_object_req(self): class LogicalRouterST(DBBaseST): _dict = {} obj_type = 'logical_router' - + ref_fields = ['virtual_machine_interface', 'route_table', 'bgpvpn'] + prop_fields = ['configured_route_target_list'] def __init__(self, name, obj=None): self.name = name self.virtual_machine_interfaces = set() self.virtual_networks = set() self.route_tables = set() self.rt_list = set() + self.configured_route_target_list = None self.bgpvpns = set() self.bgpvpn_rt_list = set() self.bgpvpn_import_rt_list = set() self.bgpvpn_export_rt_list = set() - self.obj = obj or self.read_vnc_obj(fq_name=name) + self.update_vnc_obj() rt_ref = self.obj.get_route_target_refs() old_rt_key = None @@ -3874,17 +3864,8 @@ def __init__(self, name, obj=None): if old_rt_key: RouteTargetST.delete_vnc_obj(old_rt_key) self.route_target = rt_key - - self.update(self.obj) # end __init__ - def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(uuid=self.obj.uuid) - self.update_multiple_refs('virtual_machine_interface', self.obj) - self.update_multiple_refs('route_table', self.obj) - self.update_multiple_refs('bgpvpn', self.obj) - # end update - def evaluate(self): self.update_virtual_networks() self.set_route_target_list() @@ -3974,9 +3955,9 @@ def set_route_target_list(self): old_bgpvpn_export_rt_list = self.bgpvpn_export_rt_list.copy() # Set the system allocated route target - rt_list = self.obj.get_configured_route_target_list()\ + config_rt_list = self.configured_route_target_list\ or RouteTargetList() - self.rt_list = set(rt_list.get_route_target()) + self.rt_list = set(config_rt_list.get_route_target()) # Get BGP VPN's route targets associated to that router self.bgpvpn_rt_list = set() @@ -4025,22 +4006,18 @@ def delete_route_targets(route_targets=None): def handle_st_object_req(self): resp = super(LogicalRouterST, self).handle_st_object_req() - resp.obj_refs = [ - self._get_sandesh_ref_list('virtual_machine_interface'), - self._get_sandesh_ref_list('virtual_network'), - self._get_sandesh_ref_list('bgpvpn'), + resp.obj_refs.extend([ sandesh.RefList('route_target', self.rt_list), - ] - resp.properties = [ + ]) + resp.properties.extend([ sandesh.PropList('route_target', self.route_target), - sandesh.PropList('configured_rt_list', ', '.join(self.rt_list)), sandesh.PropList('bgpvpn_router_target_list', ', '.join(self.bgpvpn_rt_list)), sandesh.PropList('bgpvpn_import_route_targt_list', ', '.join(self.bgpvpn_import_rt_list)), sandesh.PropList('bgpvpn_export_route_target_list', ', '.join(self.bgpvpn_export_rt_list)), - ] + ]) return resp # end handle_st_object_req # end LogicalRouterST @@ -4049,6 +4026,8 @@ def handle_st_object_req(self): class ServiceInstanceST(DBBaseST): _dict = {} obj_type = 'service_instance' + ref_fields = ['virtual_machine', 'service_template'] + prop_fields = ['service_instance_properties'] vn_dict = {} def __init__(self, name, obj=None): @@ -4073,15 +4052,18 @@ def __init__(self, name, obj=None): def update(self, obj=None): self.unset_vn_si_mapping() - self.obj = obj or self.read_vnc_obj(fq_name=self.name) - self.update_multiple_refs('virtual_machine', self.obj) - st_refs = self.obj.get_service_template_refs() - if st_refs: - self.service_template = ':'.join(st_refs[0]['to']) - props = self.obj.get_service_instance_properties() - if props: - self.add_properties(props) + changed = self.update_vnc_obj(obj) + if 'service_template' in changed: + st_refs = self.obj.get_service_template_refs() + if st_refs: + self.service_template = ':'.join(st_refs[0]['to']) + if 'service_instance_properties' in changed: + changed.remove('service_instance_properties') + props = self.obj.get_service_instance_properties() + if props: + changed = self.add_properties(props) self.set_vn_si_mapping() + return changed # end update def get_allocated_interface_ip(self, side, version): @@ -4127,15 +4109,22 @@ def get_virtual_networks(self, si_props): # end get_si_vns def add_properties(self, props): - self.left_vn_str, self.right_vn_str = self.get_virtual_networks(props) + left_vn_str, right_vn_str = self.get_virtual_networks(props) + ret = (self.auto_policy == props.auto_policy) + if (left_vn_str, right_vn_str) != (self.left_vn_str, self.right_vn_str): + self.left_vn_str = left_vn_str + self.right_vn_str = right_vn_str + ret = True if not props.auto_policy: - return self.delete_properties() + self.delete_properties() + return ret self.auto_policy = True if (not self.left_vn_str or not self.right_vn_str): self._logger.error( "%s: route table next hop service instance must " - "have left and right virtual networks"% self.name) - return self.delete_properties() + "have left and right virtual networks" % self.name) + self.delete_properties() + return ret policy_name = "_internal_" + self.name addr1 = AddressType(virtual_network=self.left_vn_str) @@ -4162,13 +4151,14 @@ def add_properties(self, props): def set_vn_si_mapping(self): if self.left_vn_str: - ServiceInstanceST.vn_dict.setdefault(self.left_vn_str, set()).add(self) + ServiceInstanceST.vn_dict.setdefault(self.left_vn_str, + set()).add(self) # end set_vn_si_mapping def unset_vn_si_mapping(self): if self.left_vn_str: try: - ServiceInstanceST.vn_dict.setdefault(self.left_vn_str, set()).remove(self) + ServiceInstanceST.vn_dict[self.left_vn_str].remove(self) if not ServiceInstanceST.vn_dict[self.left_vn_str]: del ServiceInstanceST.vn_dict[self.left_vn_str] except KeyError: @@ -4211,8 +4201,8 @@ def _update_service_template(self): st_obj = self.read_vnc_obj(fq_name=self.service_template, obj_type='service_template') except NoIdError: - self._logger.error("NoIdError while reading service template " - + self.service_template) + self._logger.error("NoIdError while reading service template " + + self.service_template) return st_props = st_obj.get_service_template_properties() self.service_mode = st_props.get_service_mode() or 'transparent' @@ -4235,21 +4225,19 @@ def get_virtualization_type(self): def handle_st_object_req(self): resp = super(ServiceInstanceST, self).handle_st_object_req() - resp.obj_refs = [ - self._get_sandesh_ref_list('virtual_machine'), + resp.obj_refs.extend([ self._get_sandesh_ref_list('port_tuple'), - self._get_sandesh_ref_list('service_template'), self._get_sandesh_ref_list('network_policy'), self._get_sandesh_ref_list('route_table'), - ] - resp.properties = [ + ]) + resp.properties.extend([ sandesh.PropList('left_network', self.left_vn_str), sandesh.PropList('right_network', self.right_vn_str), sandesh.PropList('auto_policy', str(self.auto_policy)), sandesh.PropList('service_mode', self.get_service_mode()), sandesh.PropList('virtualization_type', self.get_virtualization_type()), - ] + ]) return resp # end handle_st_object_req # end ServiceInstanceST @@ -4258,6 +4246,7 @@ def handle_st_object_req(self): class RoutingPolicyST(DBBaseST): _dict = {} obj_type = 'routing_policy' + ref_fields = ['service_instance'] def __init__(self, name, obj=None): self.name = name @@ -4274,7 +4263,9 @@ def __init__(self, name, obj=None): # end __init__ def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) + changed = self.update_vnc_obj(obj) + if 'service_instance' not in changed: + return False new_refs = dict((':'.join(ref['to']), ref['attr']) for ref in self.obj.get_service_instance_refs() or []) for ref in set(self.service_instances.keys()) - set(new_refs.keys()): @@ -4286,6 +4277,7 @@ def update(self, obj=None): if si: si.routing_policys[self.name] = new_refs[ref] self.service_instances = new_refs + return True # end update def add_routing_instance(self, ri, seq): @@ -4308,10 +4300,9 @@ def delete_routing_instance(self, ri): def handle_st_object_req(self): resp = super(RoutingPolicyST, self).handle_st_object_req() - resp.obj_refs = [ - self._get_sandesh_ref_list('service_instance'), + resp.obj_refs.extend([ self._get_sandesh_ref_list('routing_instance'), - ] + ]) return resp # end handle_st_object_req # end RoutingPolicyST @@ -4320,37 +4311,40 @@ def handle_st_object_req(self): class RouteAggregateST(DBBaseST): _dict = {} obj_type = 'route_aggregate' + prop_fields = ['aggregate_route_entries'] + ref_fields = ['service_instance', 'routing_instance'] def __init__(self, name, obj=None): self.name = name self.service_instances = {} self.routing_instances = set() + self.aggregate_route_entries = None self.update(obj) # end __init__ def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) - self.route_entries = self.obj.get_aggregate_route_entries() - new_refs = dict((':'.join(ref['to']), ref['attr'].interface_type) - for ref in self.obj.get_service_instance_refs() or []) - for ref in set(self.service_instances.keys()) - set(new_refs.keys()): - si = ServiceInstanceST.get(ref) - if si and self.name in si.route_aggregates: - del si.route_aggregates[self.name] - for ref in set(new_refs.keys()): - si = ServiceInstanceST.get(ref) - if si: - si.route_aggregates[self.name] = new_refs[ref] - self.service_instances = new_refs - self.update_multiple_refs('routing_instance', self.obj) + changed = self.update_vnc_obj(obj) + if 'service_instance' in changed: + new_refs = dict((':'.join(ref['to']), ref['attr'].interface_type) + for ref in self.obj.get_service_instance_refs() or []) + for ref in set(self.service_instances.keys()) - set(new_refs.keys()): + si = ServiceInstanceST.get(ref) + if si and self.name in si.route_aggregates: + del si.route_aggregates[self.name] + for ref in set(new_refs.keys()): + si = ServiceInstanceST.get(ref) + if si: + si.route_aggregates[self.name] = new_refs[ref] + self.service_instances = new_refs + return changed # end update def add_routing_instance(self, ri): if ri.name in self.routing_instances: return - if not self.route_entries or not self.route_entries.route: + if not self.aggregate_route_entries or not self.aggregate_route_entries.route: return - ip_version = IPNetwork(self.route_entries.route[0]).version + ip_version = IPNetwork(self.aggregate_route_entries.route[0]).version if ip_version == 4: if ri.service_chain_info is None: self._logger.error("No ipv4 service chain info found for %s" @@ -4387,10 +4381,9 @@ def delete_routing_instance(self, ri): def handle_st_object_req(self): resp = super(RouteAggregateST, self).handle_st_object_req() - resp.obj_refs = [ - self._get_sandesh_ref_list('service_instance'), + resp.obj_refs.extend([ self._get_sandesh_ref_list('routing_instance'), - ] + ]) return resp # end handle_st_object_req # end RouteAggregateST @@ -4412,6 +4405,7 @@ def __init__(self, name, obj=None): def update(self, obj=None): self.obj = obj or self.read_vnc_obj(fq_name=self.name) + return False # end update def delete_obj(self): @@ -4451,24 +4445,31 @@ def handle_st_object_req(self): class BgpvpnST(DBBaseST): _dict = {} obj_type = 'bgpvpn' - + prop_fields = ['route_target_list', 'import_route_target_list', + 'export_route_target_list'] def __init__(self, name, obj=None): self.name = name self.virtual_networks = set() self.logical_routers = set() + self.rt_list = set() + self.import_rt_list = set() + self.export_rt_list = set() self.update(obj) - - def update(self, obj=None): - self.obj = obj or self.read_vnc_obj(fq_name=self.name) self.update_multiple_refs('virtual_network', self.obj) self.update_multiple_refs('logical_router', self.obj) - self.get_route_target_lists() + + def update(self, obj=None): + changed = self.update_vnc_obj(obj) + if set(self.prop_fields) & set(changed): + self.get_route_target_lists() + return changed def delete_obj(self): self.update_multiple_refs('virtual_network', {}) self.update_multiple_refs('logical_router', {}) def get_route_target_lists(self): + old_lists = (self.rt_list, self.import_rt_list, self.export_rt_list) rt_list = self.obj.get_route_target_list() if rt_list: self.rt_list = set(rt_list.get_route_target()) @@ -4490,6 +4491,8 @@ def get_route_target_lists(self): # if any RT exists in rt_list, remove it from import/export lists self.import_rt_list -= self.rt_list self.export_rt_list -= self.rt_list + return old_lists != (self.rt_list, self.import_rt_list, + self.export_rt_list) def handle_st_object_req(self): resp = super(BgpvpnST, self).handle_st_object_req() @@ -4497,12 +4500,4 @@ def handle_st_object_req(self): self._get_sandesh_ref_list('virtual_network'), self._get_sandesh_ref_list('logical_router'), ] - resp.properties = [ - sandesh.PropList('bgpvpn_type', self.obj.get_bgpvpn_type()), - sandesh.PropList('rt_list', ', '.join(self.rt_list)), - sandesh.PropList('import_rt_list', - ', '.join(self.import_rt_list)), - sandesh.PropList('export_rt_list', - ', '.join(self.export_rt_list)), - ] return resp diff --git a/src/config/schema-transformer/test/test_route_table.py b/src/config/schema-transformer/test/test_route_table.py index cf74bf5761e..e10bc390807 100644 --- a/src/config/schema-transformer/test/test_route_table.py +++ b/src/config/schema-transformer/test/test_route_table.py @@ -61,7 +61,7 @@ def _match_route_table(vn, prefix, next_hop, communities, found = True break if found != should_be_present: - raise Exception("route " + prefix + "" + next_hop + "not found") + raise Exception("route %s %s not found" % (prefix, next_hop)) return _match_route_table(vn1, "1.1.1.1/0", "10.10.10.10", ['1:1'])