def update_floatingip_port_forwarding(self, context, id, floatingip_id, port_forwarding): port_forwarding = port_forwarding.get(apidef.RESOURCE_NAME) new_internal_port_id = None if port_forwarding and port_forwarding.get('internal_port_id'): new_internal_port_id = port_forwarding.get('internal_port_id') self._check_port_has_binding_floating_ip(context, port_forwarding) try: with db_api.CONTEXT_WRITER.using(context): fip_obj = self._get_fip_obj(context, floatingip_id) pf_obj = pf.PortForwarding.get_object(context, id=id) if not pf_obj: raise pf_exc.PortForwardingNotFound(id=id) original_pf_obj = copy.deepcopy(pf_obj) ori_internal_port_id = pf_obj.internal_port_id if new_internal_port_id and (new_internal_port_id != ori_internal_port_id): router_id = self._find_a_router_for_fip_port_forwarding( context, port_forwarding, fip_obj) self._check_router_match(context, fip_obj, router_id, port_forwarding) # As the socket will update when dict contains # internal_ip_address and internal_port. internal_ip_address = port_forwarding.get( 'internal_ip_address') internal_port = port_forwarding.get('internal_port') if any([internal_ip_address, internal_port]): port_forwarding.update({ 'internal_ip_address': internal_ip_address if internal_ip_address else str( pf_obj.internal_ip_address), 'internal_port': internal_port if internal_port else pf_obj.internal_port }) pf_obj.update_fields(port_forwarding, reset_changes=True) self._check_port_forwarding_update(context, pf_obj) pf_obj.update() except obj_exc.NeutronDbObjectDuplicateEntry: (__, conflict_params) = self._find_existing_port_forwarding( context, floatingip_id, pf_obj.to_dict()) message = _("A duplicate port forwarding entry with same " "attributes already exists, conflicting values " "are %s") % conflict_params raise lib_exc.BadRequest(resource=apidef.RESOURCE_NAME, msg=message) if self._rpc_notifications_required: self.push_api.push(context, [pf_obj], rpc_events.UPDATED) registry.notify(pf_consts.PORT_FORWARDING, events.AFTER_UPDATE, self, payload=[ callbacks.PortForwardingPayload( context, current_pf=pf_obj, original_pf=original_pf_obj) ]) return pf_obj
def create_floatingip_port_forwarding(self, context, floatingip_id, port_forwarding): port_forwarding = port_forwarding.get(apidef.RESOURCE_NAME) port_forwarding['floatingip_id'] = floatingip_id self._check_port_has_binding_floating_ip(context, port_forwarding) with db_api.CONTEXT_WRITER.using(context): fip_obj = self._get_fip_obj(context, floatingip_id) if fip_obj.fixed_port_id: raise lib_l3_exc.FloatingIPPortAlreadyAssociated( port_id=port_forwarding['internal_port_id'], fip_id=fip_obj.id, floating_ip_address=fip_obj.floating_ip_address, fixed_ip=str(port_forwarding['internal_ip_address']), net_id=fip_obj.floating_network_id) router_id = self._find_a_router_for_fip_port_forwarding( context, port_forwarding, fip_obj) pf_obj = pf.PortForwarding(context, **port_forwarding) # If this func does not raise an exception, means the # router_id matched. # case1: fip_obj.router_id = None # case2: fip_obj.router_id is the same with we selected. self._check_router_match(context, fip_obj, router_id, port_forwarding) if not fip_obj.router_id: values = {'router_id': router_id, 'fixed_port_id': None} l3_obj.FloatingIP.update_objects(context, values, id=floatingip_id) try: pf_obj.create() except obj_exc.NeutronDbObjectDuplicateEntry: (__, conflict_params) = self._find_existing_port_forwarding( context, floatingip_id, port_forwarding) message = _("A duplicate port forwarding entry with same " "attributes already exists, conflicting " "values are %s") % conflict_params raise lib_exc.BadRequest(resource=apidef.RESOURCE_NAME, msg=message) registry.notify(pf_consts.PORT_FORWARDING, events.AFTER_CREATE, self, payload=[ callbacks.PortForwardingPayload(context, current_pf=pf_obj) ]) if self._rpc_notifications_required: self.push_api.push(context, [pf_obj], rpc_events.CREATED) return pf_obj
def delete_floatingip_port_forwarding(self, context, id, floatingip_id): pf_obj = pf.PortForwarding.get_object(context, id=id) if not pf_obj or pf_obj.floatingip_id != floatingip_id: raise pf_exc.PortForwardingNotFound(id=id) with db_api.CONTEXT_WRITER.using(context): fip_obj = self._get_fip_obj(context, pf_obj.floatingip_id) pf_objs = pf.PortForwarding.get_objects( context, floatingip_id=pf_obj.floatingip_id) if len(pf_objs) == 1 and pf_objs[0].id == pf_obj.id: fip_obj.update_fields({'router_id': None}) fip_obj.update() pf_obj.delete() if self._rpc_notifications_required: self.push_api.push(context, [pf_obj], rpc_events.DELETED) registry.notify(pf_consts.PORT_FORWARDING, events.AFTER_DELETE, self, payload=[callbacks.PortForwardingPayload( context, original_pf=pf_obj)])
def _process_port_request(self, resource, event, trigger, context, **kwargs): # Deleting floatingip will receive port resource with precommit_delete # event, so just return, then check the request in # _check_floatingip_request callback. if kwargs['port']['device_owner'].startswith( lib_consts.DEVICE_OWNER_FLOATINGIP): return # This block is used for checking if there are some fixed ips updates. # Whatever the event is AFTER_UPDATE/PRECOMMIT_DELETE, # we will use the update_ip_set for checking if the possible associated # port forwarding resources need to be deleted for port's AFTER_UPDATE # event. Or get all affected ip addresses for port's PRECOMMIT_DELETE # event. port_id = kwargs['port']['id'] update_fixed_ips = kwargs['port']['fixed_ips'] update_ip_set = set() for update_fixed_ip in update_fixed_ips: if (netaddr.IPNetwork(update_fixed_ip.get('ip_address')).version == lib_consts.IP_VERSION_4): update_ip_set.add(update_fixed_ip.get('ip_address')) if not update_ip_set: return # If the port owner wants to update or delete port, we must elevate the # context to check if the floatingip or port forwarding resources # are owned by other tenants. if not context.is_admin: context = context.elevated() # If the logic arrives here, that means we have got update_ip_set and # its value is not None. So we need to get all port forwarding # resources based on the request port_id for preparing the next # process, such as deleting them. pf_resources = pf.PortForwarding.get_objects(context, internal_port_id=port_id) if not pf_resources: return # If the logic arrives here, that means we have got pf_resources and # its value is not None either. Then we collect all ip addresses # which are used by port forwarding resources to generate used_ip_set, # and we default to set remove_ip_set as used_ip_set which means we # want to delete all port forwarding resources when event is # PRECOMMIT_DELETE. And when event is AFTER_UPDATE, we get the # different part. used_ip_set = set() for pf_resource in pf_resources: used_ip_set.add(str(pf_resource.internal_ip_address)) remove_ip_set = used_ip_set if event == events.AFTER_UPDATE: remove_ip_set = used_ip_set - update_ip_set if not remove_ip_set: return # Here, we get the remove_ip_set, the following block will delete the # port forwarding resources based on remove_ip_set. Just need to note # here, if event is AFTER_UPDATE, and remove_ip_set is empty, the # following block won't be processed. remove_port_forwarding_list = [] with db_api.CONTEXT_WRITER.using(context): for pf_resource in pf_resources: if str(pf_resource.internal_ip_address) in remove_ip_set: pf_objs = pf.PortForwarding.get_objects( context, floatingip_id=pf_resource.floatingip_id) if len(pf_objs) == 1 and pf_objs[0].id == pf_resource.id: fip_obj = l3_obj.FloatingIP.get_object( context, id=pf_resource.floatingip_id) fip_obj.update_fields({'router_id': None}) fip_obj.update() pf_resource.delete() remove_port_forwarding_list.append(pf_resource) if self._rpc_notifications_required: self.push_api.push(context, remove_port_forwarding_list, rpc_events.DELETED) registry_notify_payload = [ callbacks.PortForwardingPayload(context, original_pf=pf_obj) for pf_obj in remove_port_forwarding_list ] registry.notify(pf_consts.PORT_FORWARDING, events.AFTER_DELETE, self, payload=registry_notify_payload)