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 f0029d82207..5262af939e7 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) @@ -2575,7 +2614,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): @@ -2960,7 +2999,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) @@ -3010,18 +3049,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): @@ -3059,9 +3105,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): @@ -3107,17 +3154,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 @@ -3193,7 +3240,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), @@ -3207,14 +3253,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__ @@ -3228,23 +3284,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())): @@ -3289,9 +3339,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, @@ -3312,23 +3362,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 @@ -3344,28 +3385,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): @@ -3390,31 +3424,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): @@ -3556,21 +3598,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', @@ -3611,15 +3653,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): @@ -3636,15 +3678,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), @@ -3686,139 +3724,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: @@ -3840,13 +3829,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 @@ -3855,18 +3843,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 @@ -3887,17 +3877,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() @@ -3987,9 +3968,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() @@ -4038,22 +4019,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 @@ -4062,6 +4039,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): @@ -4086,15 +4065,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): @@ -4140,15 +4122,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) @@ -4175,13 +4164,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: @@ -4224,8 +4214,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' @@ -4248,21 +4238,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 @@ -4271,6 +4259,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 @@ -4287,7 +4276,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()): @@ -4299,6 +4290,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): @@ -4321,10 +4313,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 @@ -4333,37 +4324,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" @@ -4400,10 +4394,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 @@ -4425,6 +4418,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): @@ -4464,24 +4458,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()) @@ -4503,6 +4504,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() @@ -4510,12 +4513,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'])