diff --git a/src/api-lib/vnc_api.py b/src/api-lib/vnc_api.py index cd48791d0fc..b5ddc4c8bf3 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 8fb157485f9..b938b15581f 100644 --- a/src/config/common/vnc_amqp.py +++ b/src/config/common/vnc_amqp.py @@ -129,7 +129,14 @@ def handle_update(self): return try: - self.obj.update() + ret = self.obj.update() + if ret is not None and not(ret): + # If update returns None, the function may not support a + # return value, hence treat it as if something might have + # changed. If a valus is returned, use its truth value. + # If it True, 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 b701a9ea912..5e862179136 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 @@ -179,6 +180,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') @@ -192,14 +194,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): @@ -214,6 +219,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') @@ -226,6 +232,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: @@ -235,8 +243,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') @@ -249,17 +264,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 @@ -293,6 +312,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 3640ede375f..6c982a3b8c3 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() @@ -259,13 +280,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': @@ -313,7 +336,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() @@ -324,30 +347,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) + 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, @@ -415,7 +438,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. @@ -431,8 +455,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() @@ -478,12 +502,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): @@ -532,7 +559,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() @@ -631,11 +658,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): @@ -672,8 +699,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: @@ -697,6 +725,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): @@ -737,7 +767,7 @@ def set_route_target_list(self, obj): (old_export_rt_list - self.export_rt_list) | (old_import_rt_list - self.import_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) if ri: @@ -753,7 +783,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, @@ -785,6 +815,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 @@ -802,7 +833,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 @@ -842,13 +873,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 @@ -1038,17 +1070,16 @@ 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) if ((prule.direction == "<>") and (sa != da or sp != dp)): 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 @@ -1287,21 +1318,21 @@ 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') - ] - resp.properties = [ + ]) + 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)), + sandesh.PropList('export_rt_list', + ', '.join(self.export_rt_list)), + ]) return resp # end handle_st_object_req @@ -1338,8 +1369,7 @@ def reinit(cls): for ri, val in cls._cassandra._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._cassandra.free_route_target(ri) # end reinit @@ -1354,7 +1384,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): @@ -1373,6 +1403,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 = {} @@ -1420,14 +1451,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() @@ -1462,6 +1499,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): @@ -1517,6 +1555,7 @@ def handle_st_object_req(self): class RouteTableST(DBBaseST): _dict = {} obj_type = 'route_table' + prop_fields = ['routes'] _service_instances = {} @@ -1528,20 +1567,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): @@ -1596,6 +1635,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): @@ -1607,11 +1647,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': @@ -1625,20 +1666,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 = [] @@ -1674,11 +1716,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: @@ -1705,6 +1745,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): @@ -1717,12 +1758,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._cassandra.free_sg_id(sg_id) else: self._cassandra.free_sg_id(sg_id-SGID_MIN_ALLOC) - self.rule_entries = None + self.security_group_entries = None self.process_referred_sgs() # end delete_obj @@ -1733,8 +1774,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 = [] @@ -1829,11 +1870,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 @@ -1904,7 +1944,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): @@ -2225,7 +2265,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: @@ -2250,8 +2291,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) @@ -2498,7 +2540,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): @@ -2880,7 +2922,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) @@ -2930,18 +2972,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): @@ -2979,9 +3028,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): @@ -3027,17 +3077,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 @@ -3113,7 +3163,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), @@ -3127,14 +3176,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__ @@ -3148,23 +3207,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())): @@ -3209,9 +3262,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, @@ -3232,23 +3285,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 @@ -3264,28 +3308,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_single_ref('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): @@ -3309,31 +3346,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): @@ -3468,21 +3513,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', @@ -3547,15 +3592,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), @@ -3597,139 +3638,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: @@ -3751,13 +3743,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 @@ -3766,14 +3757,17 @@ def handle_st_object_req(self): class LogicalRouterST(DBBaseST): _dict = {} obj_type = 'logical_router' - + ref_fields = ['virtual_machine_interface', 'route_table'] + 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.obj = obj or self.read_vnc_obj(fq_name=name) + self.configured_route_target_list = None + self.update_vnc_obj() + rt_ref = self.obj.get_route_target_refs() old_rt_key = None if rt_ref: @@ -3793,17 +3787,17 @@ 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_virtual_networks() + changed = self.update_vnc_obj(obj) + if not changed: + return False + if 'virtual_machine_interface' in changed: + self.update_virtual_networks() rt_list = self.obj.get_configured_route_target_list() or RouteTargetList() self.set_route_target_list(rt_list) + return changed # end update def delete_obj(self): @@ -3882,14 +3876,12 @@ def set_route_target_list(self, rt_list): 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'), - sandesh.RefList('route_target', self.rt_list) - ] - resp.properties = [ + resp.obj_refs.extend([ + sandesh.RefList('route_target', self.rt_list), + ]) + resp.properties.extend([ sandesh.PropList('route_target', self.route_target), - ] + ]) return resp # end handle_st_object_req # end LogicalRouterST @@ -3898,6 +3890,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): @@ -3922,15 +3916,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): @@ -3976,15 +3973,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) @@ -4011,13 +4015,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: @@ -4060,8 +4065,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' @@ -4084,21 +4089,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 @@ -4107,6 +4110,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 @@ -4123,7 +4127,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()): @@ -4135,6 +4141,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): @@ -4157,10 +4164,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 @@ -4168,37 +4174,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" @@ -4235,10 +4244,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 @@ -4260,6 +4268,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): 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'])