# Copyright 2014: Mirantis Inc. # All Rights Reserved. # # 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. from rally.common import cfg from rally.task import utils CONF = cfg.CONF cleanup_group = cfg.OptGroup(name="cleanup", title="Cleanup Options") # NOTE(andreykurilin): There are cases when there is no way to use any kind # of "name" for resource as an identifier of alignment resource to the # particular task run and even to Rally itself. Previously, we used empty # strings as a workaround for name matching specific templates, but # theoretically such behaviour can hide other cases when resource should have # a name property, but it is missed. # Let's use instances of specific class to return as a name of resources # which do not have names at all. class NoName(object): def __init__(self, resource_type): self.resource_type = resource_type def __repr__(self): return "" % self.resource_type def resource(service, resource, order=0, admin_required=False, perform_for_admin_only=False, tenant_resource=False, max_attempts=3, timeout=CONF.openstack.resource_deletion_timeout, interval=1, threads=CONF.openstack.cleanup_threads): """Decorator that overrides resource specification. Just put it on top of your resource class and specify arguments that you need. :param service: It is equal to client name for corresponding service. E.g. "nova", "cinder" or "zaqar" :param resource: Client manager name for resource. E.g. in case of nova.servers you should write here "servers" :param order: Used to adjust priority of cleanup for different resource types :param admin_required: Admin user is required :param perform_for_admin_only: Perform cleanup for admin user only :param tenant_resource: Perform deletion only 1 time per tenant :param max_attempts: Max amount of attempts to delete single resource :param timeout: Max duration of deletion in seconds :param interval: Resource status pooling interval :param threads: Amount of threads (workers) that are deleting resources simultaneously """ def inner(cls): # TODO(boris-42): This can be written better I believe =) cls._service = service cls._resource = resource cls._order = order cls._admin_required = admin_required cls._perform_for_admin_only = perform_for_admin_only cls._max_attempts = max_attempts cls._timeout = timeout cls._interval = interval cls._threads = threads cls._tenant_resource = tenant_resource return cls return inner @resource(service=None, resource=None) class ResourceManager(object): """Base class for cleanup plugins for specific resources. You should use @resource decorator to specify major configuration of resource manager. Usually you should specify: service, resource and order. If project python client is very specific, you can override delete(), list() and is_deleted() methods to make them fit to your case. """ def __init__(self, resource=None, admin=None, user=None, tenant_uuid=None): self.admin = admin self.user = user self.raw_resource = resource self.tenant_uuid = tenant_uuid def _manager(self): client = self._admin_required and self.admin or self.user return getattr(getattr(client, self._service)(), self._resource) def id(self): """Returns id of resource.""" return self.raw_resource.id def name(self): """Returns name of resource.""" return self.raw_resource.name def is_deleted(self): """Checks if the resource is deleted. Fetch resource by id from service and check it status. In case of NotFound or status is DELETED or DELETE_COMPLETE returns True, otherwise False. """ try: resource = self._manager().get(self.id()) except Exception as e: return getattr(e, "code", getattr(e, "http_status", 400)) == 404 return utils.get_status(resource) in ("DELETED", "DELETE_COMPLETE") def delete(self): """Delete resource that corresponds to instance of this class.""" self._manager().delete(self.id()) def list(self): """List all resources specific for admin or user.""" return self._manager().list()