From 1848f661053df36848735201b7e0e93c3f0978cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89douard=20Thuleau?= Date: Wed, 27 Jun 2018 18:18:56 +0200 Subject: [PATCH] [config] fix notify hooks Fix attribute type in notify hook methods in vnc_cfg_types and improve retruned error in that methods. Change-Id: I65eab626f9a8aeff361c9a02bc7416a7793de1e3 Closes-Bug: #1769959 (cherry picked from commit b2469a50b1847758fe43b86efc1b69f823002010) --- .../vnc_cfg_api_server/vnc_addr_mgmt.py | 86 ++++++-------- .../vnc_cfg_api_server/vnc_cfg_types.py | 110 ++++++++++++------ .../api-server/vnc_cfg_api_server/vnc_db.py | 54 +++++++-- .../vnc_cfg_api_server/vnc_quota.py | 7 +- 4 files changed, 161 insertions(+), 96 deletions(-) diff --git a/src/config/api-server/vnc_cfg_api_server/vnc_addr_mgmt.py b/src/config/api-server/vnc_cfg_api_server/vnc_addr_mgmt.py index 30714d3c02c..73d2c3396ad 100644 --- a/src/config/api-server/vnc_cfg_api_server/vnc_addr_mgmt.py +++ b/src/config/api-server/vnc_cfg_api_server/vnc_addr_mgmt.py @@ -900,22 +900,7 @@ def net_create_req(self, obj_dict): alloc_pool_change=[]) # end net_create_req - def net_create_notify(self, obj_id): - db_conn = self._get_db_conn() - try: - (ok, result) = db_conn.dbe_read( - 'virtual_network', - obj_id=obj_id, - obj_fields=['fq_name', 'network_ipam_refs']) - except cfgm_common.exceptions.NoIdError: - return - - if not ok: - db_conn.config_log("Error: %s in net_create_notify" %(result), - level=SandeshLevel.SYS_ERR) - return - - vn_dict = result + def net_create_notify(self, obj_id, vn_dict): vn_fq_name_str = ':'.join(vn_dict['fq_name']) self._create_net_subnet_objs(vn_fq_name_str, obj_id, vn_dict, should_persist=False, @@ -979,18 +964,15 @@ def net_update_req(self, vn_fq_name, db_vn_dict, req_vn_dict, obj_uuid=None): def net_update_notify(self, obj_id): db_conn = self._get_db_conn() try: - (ok, result) = db_conn.dbe_read( - obj_type='virtual_network', - obj_id=obj_id, - obj_fields=['fq_name', 'network_ipam_refs']) - except cfgm_common.exceptions.NoIdError: - return - + ok, result = db_conn.dbe_read( + obj_type='virtual_network', + obj_id=obj_id, + obj_fields=['fq_name', 'network_ipam_refs'], + ) + except cfgm_common.exceptions.NoIdError as e: + return False, (404, str(e)) if not ok: - db_conn.config_log("Error: %s in net_update_notify" %(result), - level=SandeshLevel.SYS_ERR) - return - + return False, result vn_dict = result vn_fq_name_str = ':'.join(vn_dict['fq_name']) @@ -1008,6 +990,8 @@ def net_update_notify(self, obj_id): self._create_net_subnet_objs(vn_fq_name_str, obj_id, vn_dict, should_persist=False, alloc_pool_change=[]) + + return True, '' # end net_update_notify def get_subnet_quota_counter(self, obj_dict, proj_dict, db_vn_dict=None): @@ -2134,11 +2118,11 @@ def _ipam_ip_free_notify(self, ip_addr, vn_uuid, ipam_refs=None): if not ipam_refs: # Read in the VN obj_fields=['network_ipam_refs'] - (ok, vn_dict) = self._uuid_to_obj_dict('virtual_network', vn_uuid, - obj_fields) + ok, result = self._uuid_to_obj_dict('virtual_network', vn_uuid, + obj_fields) if not ok: - raise cfgm_common.exceptions.VncError(vn_dict) - ipam_refs = vn_dict['network_ipam_refs'] + raise False, result + ipam_refs = result['network_ipam_refs'] for ipam_ref in ipam_refs: ipam_uuid = ipam_ref['uuid'] @@ -2147,9 +2131,9 @@ def _ipam_ip_free_notify(self, ip_addr, vn_uuid, ipam_refs=None): subnet_obj = self._subnet_objs[ipam_uuid][subnet_name] if subnet_obj.ip_belongs(ip_addr): subnet_obj.ip_reset_in_use(ip_addr) - return True + return True, '' - return False + return True, '' # end _ipam_ip_free_notify def _net_ip_free_notify(self, ip_addr, vn_uuid): @@ -2161,25 +2145,32 @@ def _net_ip_free_notify(self, ip_addr, vn_uuid): return False # end _net_ip_free_notify - def ip_free_notify(self, ip_addr, vn_fq_name, alloc_id=None, ipam_refs=None): + def ip_free_notify(self, ip_addr, vn_fq_name, alloc_id=None, + ipam_refs=None): db_conn = self._get_db_conn() if ipam_refs: - self._ipam_ip_free_notify(ip_addr, None, ipam_refs) - return - vn_uuid = db_conn.fq_name_to_uuid('virtual_network', vn_fq_name) + return self._ipam_ip_free_notify(ip_addr, None, ipam_refs) + + try: + vn_uuid = db_conn.fq_name_to_uuid('virtual_network', vn_fq_name) + except cfgm_common.exceptions.NoIdError: + return False, (400, str(e)) + if alloc_id: # In case of inconsistency in the zk db, we should read and check - # the allocated IP belongs to the interface we are freing. If not + # the allocated IP belongs to the interface we are freeing. If not # continuing to delete the interface and keeping the zk IP lock. # That permits to recover from the zk inconsistency. # https://bugs.launchpad.net/juniperopenstack/+bug/1702596 allocated_id = self.is_ip_allocated(ip_addr, vn_fq_name, vn_uuid=vn_uuid) if allocated_id is not None and alloc_id != allocated_id: - return + return True, '' if not (self._net_ip_free_notify(ip_addr, vn_uuid)): - self._ipam_ip_free_notify(ip_addr, vn_uuid) + return self._ipam_ip_free_notify(ip_addr, vn_uuid) + + return True, '' # end _ip_free_notify def mac_alloc(self, obj_dict): @@ -2314,16 +2305,13 @@ def ipam_update_req(self, ipam_fq_name, db_ipam_dict, req_ipam_dict, def ipam_update_notify(self, obj_id): db_conn = self._get_db_conn() try: - (ok, result) = db_conn.dbe_read('network_ipam', obj_id=obj_id) - except cfgm_common.exceptions.NoIdError: - return - + ok, result = db_conn.dbe_read('network_ipam', obj_id=obj_id) + except cfgm_common.exceptions.NoIdError as e: + return False, (404, str(e)) if not ok: - db_conn.config_log("Error: %s in ipam_update_notify" %(result), - level=SandeshLevel.SYS_ERR) - return - + return False, result ipam_dict = result + ipam_fq_name_str = ':'.join(ipam_dict['fq_name']) ipam_list_subnets = self._ipam_to_subnets(ipam_dict) or [] if obj_id in self._subnet_objs: @@ -2337,6 +2325,8 @@ def ipam_update_notify(self, obj_id): self._create_ipam_subnet_objs(obj_id, ipam_dict, should_persist=False) + + return True, '' # end ipam_update_notify def _ipam_is_gateway_ip(self, vn_dict, ip_addr): diff --git a/src/config/api-server/vnc_cfg_api_server/vnc_cfg_types.py b/src/config/api-server/vnc_cfg_api_server/vnc_cfg_types.py index 339e4ff6553..4a583dea61c 100644 --- a/src/config/api-server/vnc_cfg_api_server/vnc_cfg_types.py +++ b/src/config/api-server/vnc_cfg_api_server/vnc_cfg_types.py @@ -70,9 +70,10 @@ def get_quota_for_resource(cls, obj_type, obj_dict, db_conn): if proj_uuid is None: return True, -1, None - (ok, proj_dict) = QuotaHelper.get_project_dict_for_quota(proj_uuid, db_conn) + ok, result = QuotaHelper.get_project_dict_for_quota(proj_uuid, db_conn) if not ok: - return (False, (500, 'Internal error : ' + pformat(proj_dict)), None) + return False, result + proj_dict = result quota_limit = QuotaHelper.get_quota_limit(proj_dict, obj_type) return True, quota_limit, proj_uuid @@ -122,15 +123,15 @@ def post_dbe_delete(cls, id, obj_dict, db_conn): @classmethod def dbe_create_notification(cls, db_conn, obj_id, obj_dict): - pass + return True, '' @classmethod def dbe_update_notification(cls, obj_id, extra_dict=None): - pass + return True, '' @classmethod def dbe_delete_notification(cls, obj_id, obj_dict): - pass + return True, '' @classmethod def pre_dbe_read(cls, id, fq_name, db_conn): @@ -922,19 +923,22 @@ def dbe_create_notification(cls, db_conn, obj_id, obj_dict): fip_addr = obj_dict['floating_ip_address'] vn_fq_name = obj_dict['fq_name'][:-2] cls.addr_mgmt.ip_alloc_notify(fip_addr, vn_fq_name) + + return True, '' # end dbe_create_notification @classmethod def dbe_delete_notification(cls, obj_id, obj_dict): if obj_dict['parent_type'] == 'instance-ip': - return + return True, '' fip_addr = obj_dict['floating_ip_address'] vn_fq_name = obj_dict['fq_name'][:-2] - cls.addr_mgmt.ip_free_notify(fip_addr, vn_fq_name, - alloc_id=obj_dict['uuid']) - # end dbe_delete_notification + return cls.addr_mgmt.ip_free_notify(fip_addr, vn_fq_name, + alloc_id=obj_dict['uuid']) + return True, '' + # end dbe_delete_notification # end class FloatingIpServer @@ -990,14 +994,16 @@ def dbe_create_notification(cls, db_conn, obj_id, obj_dict): aip_addr = obj_dict['alias_ip_address'] vn_fq_name = obj_dict['fq_name'][:-2] cls.addr_mgmt.ip_alloc_notify(aip_addr, vn_fq_name) + + return True, '' # end dbe_create_notification @classmethod def dbe_delete_notification(cls, obj_id, obj_dict): aip_addr = obj_dict['alias_ip_address'] vn_fq_name = obj_dict['fq_name'][:-2] - cls.addr_mgmt.ip_free_notify(aip_addr, vn_fq_name, - alloc_id=obj_dict['uuid']) + return cls.addr_mgmt.ip_free_notify(aip_addr, vn_fq_name, + alloc_id=obj_dict['uuid']) # end dbe_delete_notification # end class AliasIpServer @@ -1266,6 +1272,8 @@ def dbe_create_notification(cls, db_conn, obj_id, obj_dict): else: return True, '' cls.addr_mgmt.ip_alloc_notify(ip_addr, vn_fq_name, ipam_refs=ipam_refs) + + return True, '' # end dbe_create_notification @classmethod @@ -1273,7 +1281,7 @@ def dbe_delete_notification(cls, obj_id, obj_dict): try: ip_addr = obj_dict['instance_ip_address'] except KeyError: - return + return True, '' vn_fq_name = None ipam_refs = None if obj_dict.get('virtual_network_refs', []): @@ -1282,7 +1290,8 @@ def dbe_delete_notification(cls, obj_id, obj_dict): ipam_refs = obj_dict['network_ipam_refs'] else: return True, '' - cls.addr_mgmt.ip_free_notify(ip_addr, vn_fq_name, ipam_refs=ipam_refs) + return cls.addr_mgmt.ip_free_notify(ip_addr, vn_fq_name, + ipam_refs=ipam_refs) # end dbe_delete_notification # end class InstanceIpServer @@ -2475,9 +2484,11 @@ def post_dbe_delete(cls, id, obj_dict, db_conn): @classmethod def dbe_create_notification(cls, db_conn, obj_id, obj_dict): - vn_id = cls.vnc_zk_client.alloc_tag_type_id( + cls.vnc_zk_client.alloc_tag_type_id( ':'.join(obj_dict['fq_name']), int(obj_dict['tag_type_id'], 0)) + return True, '' + @classmethod def dbe_delete_notification(cls, obj_id, obj_dict): # Deallocate in memory tag-type ID @@ -2485,6 +2496,8 @@ def dbe_delete_notification(cls, obj_id, obj_dict): obj_dict['fq_name'][-1], notify=True) + return True, '' + @classmethod def get_tag_type_id(cls, type_str): if type_str in constants.TagTypeNameToId: @@ -2609,12 +2622,14 @@ def post_dbe_delete(cls, id, obj_dict, db_conn): @classmethod def dbe_create_notification(cls, db_conn, obj_id, obj_dict): - vn_id = cls.vnc_zk_client.alloc_tag_value_id( + cls.vnc_zk_client.alloc_tag_value_id( obj_dict['tag_type_name'], ':'.join(obj_dict['fq_name']), int(obj_dict['tag_id'], 0) & 0x0000ffff, ) + return True, '' + @classmethod def dbe_delete_notification(cls, obj_id, obj_dict): cls.vnc_zk_client.free_tag_value_id( @@ -2624,6 +2639,8 @@ def dbe_delete_notification(cls, obj_id, obj_dict): notify=True, ) + return True, '' + class FirewallRuleServer(SecurityResourceBase, FirewallRule): @@ -3779,11 +3796,11 @@ def post_dbe_update(cls, id, fq_name, obj_dict, db_conn, **kwargs): def pre_dbe_delete(cls, id, obj_dict, db_conn): cls.addr_mgmt.net_delete_req(obj_dict) if obj_dict['id_perms'].get('user_visible', True) is not False: - (ok, proj_dict) = QuotaHelper.get_project_dict_for_quota( + ok, result = QuotaHelper.get_project_dict_for_quota( obj_dict['parent_uuid'], db_conn) if not ok: - return (False, - (500, 'Bad Project error : ' + pformat(proj_dict))) + return False, result + proj_dict = result ok, (subnet_count, counter) = cls.addr_mgmt.get_subnet_quota_counter( obj_dict, proj_dict) if subnet_count: @@ -3884,16 +3901,18 @@ def subnet_ip_count(cls, vn_fq_name, subnet_list): @classmethod def dbe_create_notification(cls, db_conn, obj_id, obj_dict): - vn_id = cls.vnc_zk_client.alloc_vn_id( + cls.vnc_zk_client.alloc_vn_id( ':'.join(obj_dict['fq_name']), obj_dict['virtual_network_network_id'], ) - cls.addr_mgmt.net_create_notify(obj_id) + cls.addr_mgmt.net_create_notify(obj_id, obj_dict) + + return True, '' # end dbe_create_notification @classmethod def dbe_update_notification(cls, obj_id, extra_dict=None): - cls.addr_mgmt.net_update_notify(obj_id) + return cls.addr_mgmt.net_update_notify(obj_id) # end dbe_update_notification @classmethod @@ -3904,6 +3923,8 @@ def dbe_delete_notification(cls, obj_id, obj_dict): notify=True, ) cls.addr_mgmt.net_delete_notify(obj_id, obj_dict) + + return True, '' # end dbe_delete_notification # end class VirtualNetworkServer @@ -4123,16 +4144,20 @@ def undo(): @classmethod def dbe_create_notification(cls, db_conn, obj_id, obj_dict): cls.addr_mgmt.ipam_create_notify(obj_dict) + + return True, '' # end dbe_create_notification @classmethod def dbe_update_notification(cls, obj_id, extra_dict=None): - cls.addr_mgmt.ipam_update_notify(obj_id) + return cls.addr_mgmt.ipam_update_notify(obj_id) # end dbe_update_notification @classmethod def dbe_delete_notification(cls, obj_id, obj_dict): cls.addr_mgmt.ipam_delete_notify(obj_id, obj_dict) + + return True, '' # end dbe_delete_notification @classmethod @@ -4563,10 +4588,11 @@ def pre_dbe_create(cls, tenant_name, obj_dict, db_conn): return (False, (403, "Cannot set the security group ID")) if obj_dict['id_perms'].get('user_visible', True): - (ok, proj_dict) = QuotaHelper.get_project_dict_for_quota( + ok, result = QuotaHelper.get_project_dict_for_quota( obj_dict['parent_uuid'], db_conn) if not ok: - return (False, (500, 'Bad Project error : ' + pformat(proj_dict))) + return False, result + proj_dict = result rule_count = len( obj_dict.get('security_group_entries', @@ -4613,10 +4639,11 @@ def pre_dbe_update(cls, id, fq_name, obj_dict, db_conn, **kwargs): return ok, result if sg_dict['id_perms'].get('user_visible', True): - (ok, proj_dict) = QuotaHelper.get_project_dict_for_quota( + ok, result = QuotaHelper.get_project_dict_for_quota( sg_dict['parent_uuid'], db_conn) if not ok: - return (False, (500, 'Bad Project error : ' + pformat(proj_dict))) + return False, result + proj_dict = result new_rule_count = len( obj_dict.get('security_group_entries', {}).get('policy_rule', [])) @@ -4642,10 +4669,11 @@ def pre_dbe_delete(cls, id, obj_dict, db_conn): sg_dict = result if sg_dict['id_perms'].get('user_visible', True) is not False: - (ok, proj_dict) = QuotaHelper.get_project_dict_for_quota( + ok, result = QuotaHelper.get_project_dict_for_quota( sg_dict['parent_uuid'], db_conn) if not ok: - return (False, (500, 'Bad Project error : ' + pformat(proj_dict))) + return False, result + proj_dict = result obj_type = 'security_group_rule' quota_limit = QuotaHelper.get_quota_limit(proj_dict, obj_type) @@ -4692,12 +4720,14 @@ def _notify_sg_id_modified(cls, obj_dict, def dbe_create_notification(cls, db_conn, obj_id, obj_dict): cls._notify_sg_id_modified(obj_dict) + return True, '' + @classmethod def dbe_update_notification(cls, obj_id, extra_dict=None): ok, result = cls.dbe_read(cls.db_conn, cls.object_type, obj_id, obj_fields=['fq_name', 'security_group_id']) if not ok: - return + return False, result obj_dict = result if extra_dict is not None: @@ -4706,6 +4736,8 @@ def dbe_update_notification(cls, obj_id, extra_dict=None): else: cls._notify_sg_id_modified(obj_dict) + return True, '' + @classmethod def dbe_delete_notification(cls, obj_id, obj_dict): cls.vnc_zk_client.free_sg_id( @@ -4713,6 +4745,8 @@ def dbe_delete_notification(cls, obj_id, obj_dict): ':'.join(obj_dict['fq_name']), notify=True, ) + + return True, '' # end class SecurityGroupServer @@ -5185,31 +5219,37 @@ def pre_dbe_read(cls, id, fq_name, db_conn): return cls._ensure_default_application_policy_set(id, fq_name) @classmethod - def dbe_update_notification(cls, obj_ids, extra_dict=None): + def dbe_update_notification(cls, obj_id, extra_dict=None): quota_counter = cls.server.quota_counter db_conn = cls.server._db_conn - ok, proj_dict = QuotaHelper.get_project_dict_for_quota(obj_ids, db_conn) + ok, result = QuotaHelper.get_project_dict_for_quota(obj_id, db_conn) if not ok: - raise cfgm_common.exceptions.NoIdError(pformat(proj_dict)) + return False, result + proj_dict = result + for obj_type, quota_limit in proj_dict.get('quota', {}).items(): - path_prefix = _DEFAULT_ZK_COUNTER_PATH_PREFIX + obj_ids + path_prefix = _DEFAULT_ZK_COUNTER_PATH_PREFIX + obj_id path = path_prefix + "/" + obj_type if (quota_counter.get(path) and (quota_limit == -1 or quota_limit is None)): # free the counter from cache for resources updated # with unlimted quota del quota_counter[path] + + return True, '' #end dbe_update_notification @classmethod - def dbe_delete_notification(cls, obj_ids, obj_dict): + def dbe_delete_notification(cls, obj_id, obj_dict): quota_counter = cls.server.quota_counter for obj_type in obj_dict.get('quota', {}).keys(): - path_prefix = _DEFAULT_ZK_COUNTER_PATH_PREFIX + obj_ids['uuid'] + path_prefix = _DEFAULT_ZK_COUNTER_PATH_PREFIX + obj_id path = path_prefix + "/" + obj_type if quota_counter.get(path): # free the counter from cache del quota_counter[path] + + return True, '' #end dbe_delete_notification # end ProjectServer diff --git a/src/config/api-server/vnc_cfg_api_server/vnc_db.py b/src/config/api-server/vnc_cfg_api_server/vnc_db.py index fb2bc3087ca..287573c1d84 100644 --- a/src/config/api-server/vnc_cfg_api_server/vnc_db.py +++ b/src/config/api-server/vnc_cfg_api_server/vnc_db.py @@ -320,9 +320,24 @@ def _dbe_create_notification(self, obj_info): try: r_class = self._db_client_mgr.get_resource_class(obj_type) - if r_class: - r_class.dbe_create_notification(self._db_client_mgr, obj_uuid, - obj_info.get('obj_dict')) + ok, result = r_class.dbe_create_notification( + self._db_client_mgr, + obj_uuid, + obj_info.get('obj_dict'), + ) + if not ok: + if result[0] == 404 and obj_uuid in result[1]: + raise NoIdError(obj_uuid) + else: + raise VncError(result) + except NoIdError as e: + # if NoIdError is for obj itself (as opposed to say for parent + # or ref), ignore notification + if e._unknown_id == obj_uuid: + msg = ("Create notification ignored as resource %s '%s' does " + "not exists anymore" % (obj_type, obj_uuid)) + self.config_log(msg, level=SandeshLevel.SYS_DEBUG) + return except Exception as e: err_msg = ("Failed in dbe_create_notification: " + str(e)) self.config_log(err_msg, level=SandeshLevel.SYS_ERR) @@ -336,8 +351,20 @@ def _dbe_update_notification(self, obj_info): try: r_class = self._db_client_mgr.get_resource_class(obj_type) - if r_class: - r_class.dbe_update_notification(obj_uuid, extra_dict) + ok, result = r_class.dbe_update_notification(obj_uuid, extra_dict) + if not ok: + if result[0] == 404 and obj_uuid in result[1]: + raise NoIdError(obj_uuid) + else: + raise VncError(result) + except NoIdError as e: + # if NoIdError is for obj itself (as opposed to say for parent + # or ref), ignore notification + if e._unknown_id == obj_uuid: + msg = ("Update notification ignored as resource %s '%s' does " + "not exists anymore" % (obj_type, obj_uuid)) + self.config_log(msg, level=SandeshLevel.SYS_DEBUG) + return except Exception as e: msg = "Failure in dbe_update_notification: " + str(e) self.config_log(msg, level=SandeshLevel.SYS_ERR) @@ -354,14 +381,25 @@ def _dbe_delete_notification(self, obj_info): try: r_class = self._db_client_mgr.get_resource_class(obj_type) - if r_class: - r_class.dbe_delete_notification(obj_uuid, obj_dict) + ok, result = r_class.dbe_delete_notification(obj_uuid, obj_dict) + if not ok: + if result[0] == 404 and obj_uuid in result[1]: + raise NoIdError(obj_uuid) + else: + raise VncError(result) + except NoIdError as e: + # if NoIdError is for obj itself (as opposed to say for parent + # or ref), ignore notification + if e._unknown_id == obj_uuid: + msg = ("Delete notification ignored as resource %s '%s' does " + "not exists anymore" % (obj_type, obj_uuid)) + self.config_log(msg, level=SandeshLevel.SYS_DEBUG) + return except Exception as e: msg = "Failure in dbe_delete_notification: " + str(e) self.config_log(msg, level=SandeshLevel.SYS_ERR) raise # end _dbe_delete_notification - # end class VncKombuClient diff --git a/src/config/api-server/vnc_cfg_api_server/vnc_quota.py b/src/config/api-server/vnc_cfg_api_server/vnc_quota.py index cdb56177c2a..8ffa9c5226c 100644 --- a/src/config/api-server/vnc_cfg_api_server/vnc_quota.py +++ b/src/config/api-server/vnc_cfg_api_server/vnc_quota.py @@ -15,12 +15,9 @@ class QuotaHelper(object): @classmethod def get_project_dict_for_quota(cls, proj_uuid, db_conn): try: - (ok, proj_dict) = db_conn.dbe_read('project', proj_uuid, - obj_fields=['quota']) + return db_conn.dbe_read('project', proj_uuid, obj_fields=['quota']) except cfgm_common.exceptions.NoIdError as e: - return (False, str(e)) - - return (ok, proj_dict) + return False, (404, str(e)) @classmethod def get_quota_limit(cls, proj_dict, obj_type):