Move placement exceptions into the placement package

This change moves the exceptions raised within the placement service
into the nova.api.openstack.placement package. This is part of the
process of unifying all placement code into one subdirectory so that
a future lift and shift is easier and cleaner.

This is mostly a straightforward change of imports and a moving of
existing exceptions in the placements handlers and objects with a few
caveats:

* Dealing with nova/db/sqlalchemy/resource_class_cache.py has not
  yet been accomplished, so it remains where it is and imports
  exceptions from the placement hierarchy. A TODO indicating some of the
  options is left for future work.
* Exceptions with the name ResourceProviderInUse and InventoryInUse are
  used on both sides of the nova/placement interaction. This is
  noted with comments in both nova/exception.py and
  placement/exception.py.
* test_report_client.py has had a TODO added to make it clear that the
  exceptions it uses are nova-side, and perhaps the test file should be
  moved.
* The base class of the original exceptions, NoveException, does a bit
  more than is required on the placement side so a new private class
  _BaseException is created which removes support for handling 'code'.
  This is not required because all exceptions in placement are supposed
  to be manually caught in handlers and explicitly transformed into http
  exceptions.

blueprint placement-extract

Change-Id: I2b94945a0963d6a61af931505b69afe2d4733759
This commit is contained in:
Chris Dent 2018-03-05 19:46:36 +00:00
parent 37b4f77b5e
commit 1a072eab33
17 changed files with 192 additions and 88 deletions

View File

@ -0,0 +1,167 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Exceptions for use in the Placement API."""
# NOTE(cdent): The exceptions are copied from nova.exception, where they
# were originally used. To prepare for extracting placement to its own
# repository we wish to no longer do that. Instead, exceptions used by
# placement should be in the placement hierarchy.
# TODO(cdent): Because of the duplication it is likely that some of the
# functionality provided by _BaseException is redundant and can be factored
# out.
from oslo_log import log as logging
from nova.i18n import _, _LE
LOG = logging.getLogger(__name__)
class _BaseException(Exception):
"""Base Exception
To correctly use this class, inherit from it and define
a 'msg_fmt' property. That msg_fmt will get printf'd
with the keyword arguments provided to the constructor.
"""
msg_fmt = _("An unknown exception occurred.")
def __init__(self, message=None, **kwargs):
self.kwargs = kwargs
if not message:
try:
message = self.msg_fmt % kwargs
except Exception:
# NOTE(melwitt): This is done in a separate method so it can be
# monkey-patched during testing to make it a hard failure.
self._log_exception()
message = self.msg_fmt
self.message = message
super(_BaseException, self).__init__(message)
def _log_exception(self):
# kwargs doesn't match a variable in the message
# log the issue and the kwargs
LOG.exception(_LE('Exception in string format operation'))
for name, value in self.kwargs.items():
LOG.error("%s: %s" % (name, value)) # noqa
def format_message(self):
# Use the first argument to the python Exception object which
# should be our full exception message, (see __init__).
return self.args[0]
class NotFound(_BaseException):
msg_fmt = _("Resource could not be found.")
class InvalidInventory(_BaseException):
msg_fmt = _("Inventory for '%(resource_class)s' on "
"resource provider '%(resource_provider)s' invalid.")
class CannotDeleteParentResourceProvider(_BaseException):
msg_fmt = _("Cannot delete resource provider that is a parent of "
"another. Delete child providers first.")
class ConcurrentUpdateDetected(_BaseException):
msg_fmt = _("Another thread concurrently updated the data. "
"Please retry your update")
class InvalidAllocationCapacityExceeded(InvalidInventory):
msg_fmt = _("Unable to create allocation for '%(resource_class)s' on "
"resource provider '%(resource_provider)s'. The requested "
"amount would exceed the capacity.")
class InvalidAllocationConstraintsViolated(InvalidInventory):
msg_fmt = _("Unable to create allocation for '%(resource_class)s' on "
"resource provider '%(resource_provider)s'. The requested "
"amount would violate inventory constraints.")
class InvalidInventoryCapacity(InvalidInventory):
msg_fmt = _("Invalid inventory for '%(resource_class)s' on "
"resource provider '%(resource_provider)s'. "
"The reserved value is greater than or equal to total.")
# An exception with this name is used on both sides of the placement/
# nova interaction.
class InventoryInUse(InvalidInventory):
# NOTE(mriedem): This message cannot change without impacting the
# nova.scheduler.client.report._RE_INV_IN_USE regex.
msg_fmt = _("Inventory for '%(resource_classes)s' on "
"resource provider '%(resource_provider)s' in use.")
class InventoryWithResourceClassNotFound(NotFound):
msg_fmt = _("No inventory of class %(resource_class)s found.")
class MaxDBRetriesExceeded(_BaseException):
msg_fmt = _("Max retries of DB transaction exceeded attempting to "
"perform %(action)s.")
class ObjectActionError(_BaseException):
msg_fmt = _('Object action %(action)s failed because: %(reason)s')
class ResourceClassCannotDeleteStandard(_BaseException):
msg_fmt = _("Cannot delete standard resource class %(resource_class)s.")
class ResourceClassCannotUpdateStandard(_BaseException):
msg_fmt = _("Cannot update standard resource class %(resource_class)s.")
class ResourceClassExists(_BaseException):
msg_fmt = _("Resource class %(resource_class)s already exists.")
class ResourceClassInUse(_BaseException):
msg_fmt = _("Cannot delete resource class %(resource_class)s. "
"Class is in use in inventory.")
class ResourceClassNotFound(NotFound):
msg_fmt = _("No such resource class %(resource_class)s.")
# An exception with this name is used on both sides of the placement/
# nova interaction.
class ResourceProviderInUse(_BaseException):
msg_fmt = _("Resource provider has allocations.")
class TraitCannotDeleteStandard(_BaseException):
msg_fmt = _("Cannot delete standard trait %(name)s.")
class TraitExists(_BaseException):
msg_fmt = _("The Trait %(name)s already exists")
class TraitInUse(_BaseException):
msg_fmt = _("The trait %(name)s is in use by a resource provider.")
class TraitNotFound(NotFound):
msg_fmt = _("No such trait(s): %(names)s.")

View File

@ -28,6 +28,7 @@ import webob
from oslo_log import log as logging
from nova.api.openstack.placement import exception
from nova.api.openstack.placement.handlers import aggregate
from nova.api.openstack.placement.handlers import allocation
from nova.api.openstack.placement.handlers import allocation_candidate
@ -39,7 +40,6 @@ from nova.api.openstack.placement.handlers import trait
from nova.api.openstack.placement.handlers import usage
from nova.api.openstack.placement import policy
from nova.api.openstack.placement import util
from nova import exception
from nova.i18n import _
LOG = logging.getLogger(__name__)

View File

@ -19,12 +19,12 @@ from oslo_utils import encodeutils
from oslo_utils import timeutils
import webob
from nova.api.openstack.placement import exception
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova.api.openstack.placement.schemas import allocation as schema
from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
from nova import exception
from nova.i18n import _

View File

@ -20,12 +20,12 @@ from oslo_utils import timeutils
import six
import webob
from nova.api.openstack.placement import exception
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova.api.openstack.placement.schemas import allocation_candidate as schema
from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
from nova import exception
from nova.i18n import _

View File

@ -18,13 +18,13 @@ from oslo_serialization import jsonutils
from oslo_utils import encodeutils
import webob
from nova.api.openstack.placement import exception
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova.api.openstack.placement.schemas import inventory as schema
from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
from nova.db import constants as db_const
from nova import exception
from nova.i18n import _

View File

@ -16,12 +16,12 @@ from oslo_utils import encodeutils
from oslo_utils import timeutils
import webob
from nova.api.openstack.placement import exception
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova.api.openstack.placement.schemas import resource_class as schema
from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
from nova import exception
from nova.i18n import _

View File

@ -18,12 +18,12 @@ from oslo_utils import timeutils
from oslo_utils import uuidutils
import webob
from nova.api.openstack.placement import exception
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova.api.openstack.placement.schemas import resource_provider as rp_schema
from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
from nova import exception
from nova.i18n import _

View File

@ -17,12 +17,12 @@ from oslo_utils import encodeutils
from oslo_utils import timeutils
import webob
from nova.api.openstack.placement import exception
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova.api.openstack.placement.schemas import trait as schema
from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
from nova import exception
from nova.i18n import _

View File

@ -16,12 +16,12 @@ from oslo_utils import encodeutils
from oslo_utils import timeutils
import webob
from nova.api.openstack.placement import exception
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova.api.openstack.placement.schemas import usage as schema
from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
from nova import exception
from nova.i18n import _

View File

@ -36,10 +36,10 @@ from sqlalchemy import func
from sqlalchemy import sql
from sqlalchemy.sql import null
from nova.api.openstack.placement import exception
from nova.db.sqlalchemy import api as db_api
from nova.db.sqlalchemy import api_models as models
from nova.db.sqlalchemy import resource_class_cache as rc_cache
from nova import exception
from nova.i18n import _
from nova import rc_fields

View File

@ -14,9 +14,12 @@ from oslo_concurrency import lockutils
import six
import sqlalchemy as sa
# TODO(cdent): This file and its location is problematic for placement
# extraction but we probably want to switch to os-resource-classes (like
# os-traits) instead of moving it?
from nova.api.openstack.placement import exception
from nova.db.sqlalchemy import api as db_api
from nova.db.sqlalchemy import api_models as models
from nova import exception
from nova import rc_fields as fields
_RC_TBL = models.ResourceClass.__table__

View File

@ -2068,11 +2068,6 @@ class LibguestfsCannotReadKernel(Invalid):
msg_fmt = _("Libguestfs does not have permission to read host kernel.")
class MaxDBRetriesExceeded(NovaException):
msg_fmt = _("Max retries of DB transaction exceeded attempting to "
"perform %(action)s.")
class RealtimePolicyNotSupported(Invalid):
msg_fmt = _("Realtime policy not supported by hypervisor")
@ -2107,20 +2102,8 @@ class InvalidReservedMemoryPagesOption(Invalid):
"config-reference.")
class ConcurrentUpdateDetected(NovaException):
msg_fmt = _("Another thread concurrently updated the data. "
"Please retry your update")
class ResourceClassNotFound(NotFound):
msg_fmt = _("No such resource class %(resource_class)s.")
class CannotDeleteParentResourceProvider(NovaException):
msg_fmt = _("Cannot delete resource provider that is a parent of "
"another. Delete child providers first.")
# An exception with this name is used on both sides of the placement/
# nova interaction.
class ResourceProviderInUse(NovaException):
msg_fmt = _("Resource provider has allocations.")
@ -2168,31 +2151,10 @@ class ResourceProviderUpdateConflict(PlacementAPIConflict):
"provider %(uuid)s (generation %(generation)d): %(error)s")
class InventoryWithResourceClassNotFound(NotFound):
msg_fmt = _("No inventory of class %(resource_class)s found.")
class InvalidResourceClass(Invalid):
msg_fmt = _("Resource class '%(resource_class)s' invalid.")
class ResourceClassExists(NovaException):
msg_fmt = _("Resource class %(resource_class)s already exists.")
class ResourceClassInUse(Invalid):
msg_fmt = _("Cannot delete resource class %(resource_class)s. "
"Class is in use in inventory.")
class ResourceClassCannotDeleteStandard(Invalid):
msg_fmt = _("Cannot delete standard resource class %(resource_class)s.")
class ResourceClassCannotUpdateStandard(Invalid):
msg_fmt = _("Cannot update standard resource class %(resource_class)s.")
class InvalidResourceAmount(Invalid):
msg_fmt = _("Resource amounts must be integers. Received '%(amount)s'.")
@ -2202,6 +2164,8 @@ class InvalidInventory(Invalid):
"resource provider '%(resource_provider)s' invalid.")
# An exception with this name is used on both sides of the placement/
# nova interaction.
class InventoryInUse(InvalidInventory):
# NOTE(mriedem): This message cannot change without impacting the
# nova.scheduler.client.report._RE_INV_IN_USE regex.
@ -2209,24 +2173,6 @@ class InventoryInUse(InvalidInventory):
"resource provider '%(resource_provider)s' in use.")
class InvalidInventoryCapacity(InvalidInventory):
msg_fmt = _("Invalid inventory for '%(resource_class)s' on "
"resource provider '%(resource_provider)s'. "
"The reserved value is greater than or equal to total.")
class InvalidAllocationCapacityExceeded(InvalidInventory):
msg_fmt = _("Unable to create allocation for '%(resource_class)s' on "
"resource provider '%(resource_provider)s'. The requested "
"amount would exceed the capacity.")
class InvalidAllocationConstraintsViolated(InvalidInventory):
msg_fmt = _("Unable to create allocation for '%(resource_class)s' on "
"resource provider '%(resource_provider)s'. The requested "
"amount would violate inventory constraints.")
class UnsupportedPointerModelRequested(Invalid):
msg_fmt = _("Pointer model '%(model)s' requested is not supported by "
"host.")
@ -2268,22 +2214,6 @@ class PowerVMAPIFailed(NovaException):
"%(reason)s")
class TraitNotFound(NotFound):
msg_fmt = _("No such trait(s): %(names)s.")
class TraitExists(NovaException):
msg_fmt = _("The Trait %(name)s already exists")
class TraitCannotDeleteStandard(Invalid):
msg_fmt = _("Cannot delete standard trait %(name)s.")
class TraitInUse(Invalid):
msg_fmt = _("The trait %(name)s is in use by a resource provider.")
class TraitRetrievalFailed(NovaException):
msg_fmt = _("Failed to retrieve traits from the placement API: %(error)s")

View File

@ -20,6 +20,10 @@ from wsgi_intercept import interceptor
from nova.api.openstack.placement import deploy
from nova import conf
from nova import context
# TODO(cdent): This points to the nova, not placement, exception for
# InvalidResourceClass. This test should probably move out of the
# placement hierarchy since it expects a "standard" placement server
# and is not testing the placement service itself.
from nova import exception
from nova import objects
from nova import rc_fields as fields

View File

@ -13,10 +13,10 @@ import os_traits
from oslo_utils import uuidutils
import sqlalchemy as sa
from nova.api.openstack.placement import exception
from nova.api.openstack.placement import lib as placement_lib
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova import context
from nova import exception
from nova import rc_fields as fields
from nova import test
from nova.tests import fixtures

View File

@ -15,8 +15,8 @@ import mock
from oslo_utils import timeutils
from nova.api.openstack.placement import exception
from nova.db.sqlalchemy import resource_class_cache as rc_cache
from nova import exception
from nova import rc_fields as fields
from nova import test
from nova.tests import fixtures

View File

@ -17,9 +17,9 @@ from oslo_db import exception as db_exc
import sqlalchemy as sa
import nova
from nova.api.openstack.placement import exception
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova import context
from nova import exception
from nova import rc_fields as fields
from nova import test
from nova.tests import fixtures

View File

@ -16,10 +16,10 @@ import six
from oslo_utils import timeutils
import nova
from nova.api.openstack.placement import exception
from nova.api.openstack.placement.objects import resource_provider
from nova import context
from nova.db.sqlalchemy import api_models as models
from nova import exception
from nova import rc_fields as fields
from nova import test
from nova.tests.unit.objects import test_objects