Fix leak of ports on share server deletion

- Manila share server deletion happens asynchronously. If deletion of
share server fails in between, share server network ports remained as
it is. So we should better delete share network ports first and then
continue share server deletion.
- It may be possible, that there are ports existing without a
corresponding manila network allocation entry in the manila db, because
port create request may have been successfully sent to neutron, but not
stored in db. So query(and delete) those from neutron after db entries
are deleted.

Closes-bug: #2067266
Change-Id: Id86dade1194494e599aea9adad06e4ca6cb119b6
(cherry picked from commit 76d0329698)
This commit is contained in:
Kiran Pawar 2024-05-27 12:43:01 +00:00
parent b46096fcdc
commit 19622387dc
6 changed files with 60 additions and 8 deletions

View File

@ -107,7 +107,8 @@ class NetworkBaseAPI(db_base.Base, metaclass=abc.ABCMeta):
pass pass
@abc.abstractmethod @abc.abstractmethod
def deallocate_network(self, context, share_server_id): def deallocate_network(self, context, share_server_id, share_network=None,
share_network_subnet=None):
pass pass
@abc.abstractmethod @abc.abstractmethod

View File

@ -296,7 +296,8 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
ip_version} ip_version}
raise exception.NetworkBadConfigurationException(reason=msg) raise exception.NetworkBadConfigurationException(reason=msg)
def deallocate_network(self, context, share_server_id): def deallocate_network(self, context, share_server_id,
share_network=None, share_network_subnet=None):
"""Deallocate neutron network resources for the given share server. """Deallocate neutron network resources for the given share server.
Delete previously allocated neutron ports, delete manila db Delete previously allocated neutron ports, delete manila db
@ -304,6 +305,8 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
:param context: RequestContext object :param context: RequestContext object
:param share_server_id: id of share server :param share_server_id: id of share server
:param share_network: share network data
:param share_network_subnet: share network subnet data
:rtype: None :rtype: None
""" """
ports = self.db.network_allocations_get_for_share_server( ports = self.db.network_allocations_get_for_share_server(
@ -312,6 +315,24 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
for port in ports: for port in ports:
self._delete_port(context, port) self._delete_port(context, port)
# It may be possible that there are ports existing without a
# corresponding manila network allocation entry in the manila db,
# because port create request may have been successfully sent to
# neutron, but the response, the created port could not be stored
# in manila due to unreachable db
if share_network_subnet:
ports = []
try:
ports = self.neutron_api.list_ports(
network_id=share_network_subnet['neutron_net_id'],
device_owner='manila:share',
device_id=share_server_id)
except exception.NetworkException:
LOG.warning("Failed to list ports using neutron API during "
"deallocate_network.")
for port in ports:
self._delete_port(context, port, ignore_db=True)
def _get_port_create_args(self, share_server, share_network_subnet, def _get_port_create_args(self, share_server, share_network_subnet,
device_owner, count=0): device_owner, count=0):
return { return {
@ -354,7 +375,7 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
return self.db.network_allocation_create(context, port_dict) return self.db.network_allocation_create(context, port_dict)
def _delete_port(self, context, port): def _delete_port(self, context, port, ignore_db=False):
try: try:
self.neutron_api.delete_port(port['id']) self.neutron_api.delete_port(port['id'])
except exception.NetworkException: except exception.NetworkException:
@ -362,7 +383,8 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
context, port['id'], {'status': constants.STATUS_ERROR}) context, port['id'], {'status': constants.STATUS_ERROR})
raise raise
else: else:
self.db.network_allocation_delete(context, port['id']) if not ignore_db:
self.db.network_allocation_delete(context, port['id'])
def _has_provider_network_extension(self): def _has_provider_network_extension(self):
extensions = self.neutron_api.list_extensions() extensions = self.neutron_api.list_extensions()

View File

@ -314,7 +314,8 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
self.db.network_allocation_create(context, data)) self.db.network_allocation_create(context, data))
return allocations return allocations
def deallocate_network(self, context, share_server_id): def deallocate_network(self, context, share_server_id,
share_network=None, share_network_subnet=None):
"""Deallocate network resources for share server.""" """Deallocate network resources for share server."""
allocations = self.db.network_allocations_get_for_share_server( allocations = self.db.network_allocations_get_for_share_server(
context, share_server_id) context, share_server_id)

View File

@ -960,10 +960,13 @@ class ShareDriver(object):
self.admin_network_api.allocate_network( self.admin_network_api.allocate_network(
context, share_server, **kwargs) context, share_server, **kwargs)
def deallocate_network(self, context, share_server_id): def deallocate_network(self, context, share_server_id, share_network=None,
share_network_subnet=None):
"""Deallocate network resources for the given share server.""" """Deallocate network resources for the given share server."""
if self.get_network_allocations_number(): if self.get_network_allocations_number():
self.network_api.deallocate_network(context, share_server_id) self.network_api.deallocate_network(
context, share_server_id, share_network=share_network,
share_network_subnet=share_network_subnet)
def choose_share_server_compatible_with_share(self, context, share_servers, def choose_share_server_compatible_with_share(self, context, share_servers,
share, snapshot=None, share, snapshot=None,

View File

@ -4605,6 +4605,22 @@ class ShareManager(manager.SchedulerDependentManager):
self.db.share_server_update(context, server_id, self.db.share_server_update(context, server_id,
{'status': constants.STATUS_DELETING}) {'status': constants.STATUS_DELETING})
try: try:
LOG.debug("Deleting network of share server '%s'", server_id)
share_net = None
share_net_subnet = None
if subnet_id:
try:
share_net_subnet = self.db.share_network_subnet_get(
context, subnet_id)
share_net = self.db.share_network_get(
context, share_net_subnet['share_network_id'])
except Exception:
LOG.warning('Share network subnet not found during '
'deletion of share server.')
self.driver.deallocate_network(context, share_server['id'],
share_net,
share_net_subnet)
LOG.debug("Deleting share server '%s'", server_id) LOG.debug("Deleting share server '%s'", server_id)
security_services = [] security_services = []
for ss_name in constants.SECURITY_SERVICES_ALLOWED_TYPES: for ss_name in constants.SECURITY_SERVICES_ALLOWED_TYPES:
@ -4629,7 +4645,6 @@ class ShareManager(manager.SchedulerDependentManager):
LOG.info( LOG.info(
"Share server '%s' has been deleted successfully.", "Share server '%s' has been deleted successfully.",
share_server['id']) share_server['id'])
self.driver.deallocate_network(context, share_server['id'])
@add_hooks @add_hooks
@utils.require_driver_initialized @utils.require_driver_initialized

View File

@ -0,0 +1,10 @@
---
fixes:
- |
Share server deletion happens asynchronously and failure during this delete
results in leakage of neutron ports. This is fixed in two steps, first by
trying to delete ports before share server deletion. Second, after ports
from Manila db entries are deleted, query is made to neutron to get ports
which are allocated for share server and missing in db. And then try to
delete those ports. For more details please check
Launchpad `bug 2067266 <https://bugs.launchpad.net/manila/+bug/2067266>`_