Merge "Handle Neuton Subnet Full Exception" into stable/2025.1
This commit is contained in:
@@ -538,6 +538,10 @@ class PortLimitExceeded(QuotaError):
|
||||
message = _("Maximum number of ports exceeded.")
|
||||
|
||||
|
||||
class IpAddressGenerationFailureClient(ManilaException):
|
||||
message = _("No free IP addresses available in neutron subnet.")
|
||||
|
||||
|
||||
class ShareAccessExists(ManilaException):
|
||||
message = _("Share access %(access_type)s:%(access)s exists.")
|
||||
|
||||
|
||||
@@ -169,6 +169,14 @@ class Detail(object):
|
||||
'031',
|
||||
_("Metadata delete operation includes driver updatable metadata, and "
|
||||
"it is not passed to share driver to perform required operation."))
|
||||
NEUTRON_SUBNET_FULL = (
|
||||
'033',
|
||||
_("Share Driver failed to create share server on share network "
|
||||
"due no more free IP addresses in the neutron subnet."
|
||||
"You may free some IP addresses in the subnet "
|
||||
"or create a new subnet/share network. If this doesn't work, "
|
||||
"contact your administrator to troubleshoot "
|
||||
"issues with your network."))
|
||||
|
||||
ALL = (
|
||||
UNKNOWN_ERROR,
|
||||
@@ -201,7 +209,8 @@ class Detail(object):
|
||||
SHARE_BACKEND_NOT_READY_YET,
|
||||
UPDATE_METADATA_SUCCESS,
|
||||
UPDATE_METADATA_FAILURE,
|
||||
UPDATE_METADATA_NOT_DELETED
|
||||
UPDATE_METADATA_NOT_DELETED,
|
||||
NEUTRON_SUBNET_FULL
|
||||
)
|
||||
|
||||
# Exception and detail mappings
|
||||
|
||||
@@ -197,6 +197,9 @@ class API(object):
|
||||
raise exception.PortLimitExceeded()
|
||||
raise exception.NetworkException(code=e.status_code,
|
||||
message=e.message)
|
||||
except neutron_client_exc.IpAddressGenerationFailureClient:
|
||||
LOG.warning('No free IP addresses in neutron subnet %s', subnet_id)
|
||||
raise exception.IpAddressGenerationFailureClient()
|
||||
except ks_exec.ConnectFailure:
|
||||
LOG.warning('Create Port: Neutron connection failure')
|
||||
# check if port is created in neutron else re-raise connectFailure
|
||||
|
||||
@@ -2156,6 +2156,22 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
resource_id=share_id,
|
||||
detail=(message_field.Detail
|
||||
.SECURITY_SERVICE_FAILED_AUTH))
|
||||
except exception.IpAddressGenerationFailureClient:
|
||||
with excutils.save_and_reraise_exception():
|
||||
error = ("Creation of share instance %s failed: "
|
||||
"No Free IP's in neutron subnet.")
|
||||
LOG.error(error, share_instance_id)
|
||||
self.db.share_instance_update(
|
||||
context, share_instance_id,
|
||||
{'status': constants.STATUS_ERROR}
|
||||
)
|
||||
self.message_api.create(
|
||||
context,
|
||||
message_field.Action.CREATE,
|
||||
share['project_id'],
|
||||
resource_type=message_field.Resource.SHARE,
|
||||
resource_id=share_id,
|
||||
detail=message_field.Detail.NEUTRON_SUBNET_FULL)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
error = ("Creation of share instance %s failed: "
|
||||
|
||||
@@ -2671,6 +2671,71 @@ class ShareManagerTestCase(test.TestCase):
|
||||
detail=(
|
||||
message_field.Detail.SHARE_NETWORK_PORT_QUOTA_LIMIT_EXCEEDED))
|
||||
|
||||
def test_create_share_instance_with_ip_address_generation_failure(self):
|
||||
share_network = db_utils.create_share_network(id='fake_sn_id')
|
||||
share_net_subnet = db_utils.create_share_network_subnet(
|
||||
id='fake_sns_id', share_network_id=share_network['id']
|
||||
)
|
||||
share_type = db_utils.create_share_type()
|
||||
fake_share = db_utils.create_share(
|
||||
share_network_id=share_network['id'], size=1,
|
||||
share_type_id=share_type['id'])
|
||||
fake_metadata = {
|
||||
'request_host': 'fake_host',
|
||||
'share_type_id': 'fake_share_type_id',
|
||||
}
|
||||
fake_server = db_utils.create_share_server(
|
||||
id='fake_srv_id', status=constants.STATUS_CREATING,
|
||||
share_network_subnets=[share_net_subnet])
|
||||
|
||||
self.mock_object(self.share_manager, '_build_server_metadata',
|
||||
mock.Mock(return_value=fake_metadata))
|
||||
self.mock_object(db, 'share_server_create',
|
||||
mock.Mock(return_value=fake_server))
|
||||
self.mock_object(db, 'share_instance_update',
|
||||
mock.Mock(return_value=fake_share.instance))
|
||||
self.mock_object(db, 'share_instance_get',
|
||||
mock.Mock(return_value=fake_share.instance))
|
||||
self.mock_object(
|
||||
db, 'share_network_subnets_get_all_by_availability_zone_id',
|
||||
mock.Mock(return_value=[share_net_subnet]))
|
||||
self.mock_object(manager.LOG, 'error')
|
||||
|
||||
def raise_manila_exception(*args, **kwargs):
|
||||
raise exception.IpAddressGenerationFailureClient()
|
||||
|
||||
self.mock_object(self.share_manager, '_setup_server',
|
||||
mock.Mock(side_effect=raise_manila_exception))
|
||||
|
||||
self.assertRaises(
|
||||
exception.IpAddressGenerationFailureClient,
|
||||
self.share_manager.create_share_instance,
|
||||
self.context,
|
||||
fake_share.instance['id'],
|
||||
)
|
||||
db.share_server_create.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext), mock.ANY)
|
||||
db.share_instance_update.assert_has_calls([
|
||||
mock.call(
|
||||
utils.IsAMatcher(context.RequestContext),
|
||||
fake_share.instance['id'],
|
||||
{'status': constants.STATUS_ERROR},
|
||||
)
|
||||
])
|
||||
self.share_manager._setup_server.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext), fake_server,
|
||||
fake_metadata)
|
||||
manager.LOG.error.assert_called_with(mock.ANY,
|
||||
fake_share.instance['id'])
|
||||
self.share_manager.message_api.create.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext),
|
||||
message_field.Action.CREATE,
|
||||
str(fake_share.project_id),
|
||||
resource_type=message_field.Resource.SHARE,
|
||||
resource_id=fake_share['id'],
|
||||
detail=(
|
||||
message_field.Detail.NEUTRON_SUBNET_FULL))
|
||||
|
||||
def test_create_share_instance_with_share_network_server_fail(self):
|
||||
share_network = db_utils.create_share_network(id='fake_sn_id')
|
||||
share_net_subnet = db_utils.create_share_network_subnet(
|
||||
|
||||
@@ -144,6 +144,11 @@ class ManilaExceptionTestCase(test.TestCase):
|
||||
self.assertEqual(500, e.code)
|
||||
self.assertIn(reason, e.msg)
|
||||
|
||||
def test_ip_address_generation_failure(self):
|
||||
# verify response code for exception.IpAddressGenerationFailureClient
|
||||
e = exception.IpAddressGenerationFailureClient()
|
||||
self.assertEqual(500, e.code)
|
||||
|
||||
|
||||
class ManilaExceptionResponseCode400(test.TestCase):
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Manila will report IpAddressGenerationFailureClient exception if neutron
|
||||
subnet is full i.e. no more ports can be created on neutron subnet. For
|
||||
more details, please refer to
|
||||
`launchpad bug #2120176 <https://bugs.launchpad.net/manila/+bug/2120176>`_
|
||||
Reference in New Issue
Block a user