nova/nova/api/openstack/placement/exception.py
Jay Pipes 03d80cf0de placement: always create consumer records
Adds objects for Consumer, Project, and User data models, in their own
files. They do not contain logic that comes from the API microversions
and are meant to be plain-old-data objects that represent the current
schema in the database. Project, user and consumer information all are
stored in separate tables in the DB and represent actual things in the
placement data modeling. Giving them actual objects makes that
consistent with the other objects in the data model, including resource
providers, allocations, inventories, resource classes and traits.

The patch modifies the allocation handler to always ensure that a
consumer record exists for the supplied consumer UUID and an associated
projects and users table record exists for that consumer. If an
allocation is created using API microversion <1.8, which doesn't supply
the project or user for the consumer, we use the value of two new CONF
options that indicate the project and user ID for incomplete consumer
records.

Includes an online data migration for the nova-manage
online_data_migrations command that creates consumer records for
incomplete consumers.

Change-Id: Id609789ef6b4a4c745550cde80dd49cabe03869a
2018-06-11 12:45:41 -04:00

203 lines
6.5 KiB
Python

# 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.
from oslo_log import log as logging
from nova.i18n import _
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('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 Exists(_BaseException):
msg_fmt = _("Resource already exists.")
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.")
class InvalidInventoryCapacityReservedCanBeTotal(InvalidInventoryCapacity):
msg_fmt = _("Invalid inventory for '%(resource_class)s' on "
"resource provider '%(resource_provider)s'. "
"The reserved value is greater than 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 PolicyNotAuthorized(_BaseException):
msg_fmt = _("Policy does not allow %(action)s to be performed.")
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.")
class ProjectNotFound(NotFound):
msg_fmt = _("No such project(s): %(external_id)s.")
class ProjectExists(Exists):
msg_fmt = _("The project %(external_id)s already exists.")
class UserNotFound(NotFound):
msg_fmt = _("No such user(s): %(external_id)s.")
class UserExists(Exists):
msg_fmt = _("The user %(external_id)s already exists.")
class ConsumerNotFound(NotFound):
msg_fmt = _("No such consumer(s): %(uuid)s.")
class ConsumerExists(Exists):
msg_fmt = _("The consumer %(uuid)s already exists.")