diff --git a/tox.ini b/tox.ini index 68ae01e5b..756f80bbb 100644 --- a/tox.ini +++ b/tox.ini @@ -41,7 +41,7 @@ commands = # E123, E125 skipped as they are invalid PEP-8. show-source=True -ignore=E123,E125,H404,H405,H305 +ignore=E123,E125 builtins= _ exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,*sqlalchemy/alembic/versions/*,demo/ diff --git a/watcher/applier/primitives/wrapper/nova_wrapper.py b/watcher/applier/primitives/wrapper/nova_wrapper.py index 25f50f6fb..5ec43b456 100644 --- a/watcher/applier/primitives/wrapper/nova_wrapper.py +++ b/watcher/applier/primitives/wrapper/nova_wrapper.py @@ -64,21 +64,21 @@ class NovaWrapper(object): keep_original_image_name=True): """This method migrates a given instance - using an image of this instance and creating a new instance - from this image. It saves some configuration information - about the original instance : security group, list of networks - ,list of attached volumes, floating IP, ... - in order to apply the same settings to the new instance. - At the end of the process the original instance is deleted. - It returns True if the migration was successful, - False otherwise. + using an image of this instance and creating a new instance + from this image. It saves some configuration information + about the original instance : security group, list of networks, + list of attached volumes, floating IP, ... + in order to apply the same settings to the new instance. + At the end of the process the original instance is deleted. + It returns True if the migration was successful, + False otherwise. - :param instance_id: the unique id of the instance to migrate. - :param keep_original_image_name: flag indicating whether the - image name from which the original instance was built must be - used as the name of the intermediate image used for migration. - If this flag is False, a temporary image name is built - """ + :param instance_id: the unique id of the instance to migrate. + :param keep_original_image_name: flag indicating whether the + image name from which the original instance was built must be + used as the name of the intermediate image used for migration. + If this flag is False, a temporary image name is built + """ new_image_name = "" @@ -275,12 +275,14 @@ class NovaWrapper(object): return True def built_in_non_live_migrate_instance(self, instance_id, hypervisor_id): - """This method uses the Nova built-in non-live migrate() - action to migrate a given instance. - It returns True if the migration was successful, False otherwise. + """This method does a live migration of a given instance - :param instance_id: the unique id of the instance to migrate. - """ + This method uses the Nova built-in non-live migrate() + action to migrate a given instance. + It returns True if the migration was successful, False otherwise. + + :param instance_id: the unique id of the instance to migrate. + """ LOG.debug( "Trying a Nova built-in non-live " @@ -326,15 +328,18 @@ class NovaWrapper(object): def live_migrate_instance(self, instance_id, dest_hostname, block_migration=True, retry=120): - """This method uses the Nova built-in live_migrate() - action to do a live migration of a given instance. - It returns True if the migration was successful, - False otherwise. + """This method does a live migration of a given instance - :param instance_id: the unique id of the instance to migrate. - :param dest_hostname: the name of the destination compute node. - :param block_migration: No shared storage is required. - """ + This method uses the Nova built-in live_migrate() + action to do a live migration of a given instance. + + It returns True if the migration was successful, + False otherwise. + + :param instance_id: the unique id of the instance to migrate. + :param dest_hostname: the name of the destination compute node. + :param block_migration: No shared storage is required. + """ LOG.debug("Trying a live migrate of instance %s to host '%s'" % ( instance_id, dest_hostname)) @@ -429,16 +434,17 @@ class NovaWrapper(object): def create_image_from_instance(self, instance_id, image_name, metadata={"reason": "instance_migrate"}): """This method creates a new image from a given instance. - It waits for this image to be in 'active' state before returning. - It returns the unique UUID of the created image if successful, - None otherwise - :param instance_id: the uniqueid of - the instance to backup as an image. - :param image_name: the name of the image to create. - :param metadata: a dictionary containing the list of - key-value pairs to associate to the image as metadata. - """ + It waits for this image to be in 'active' state before returning. + It returns the unique UUID of the created image if successful, + None otherwise + + :param instance_id: the uniqueid of + the instance to backup as an image. + :param image_name: the name of the image to create. + :param metadata: a dictionary containing the list of + key-value pairs to associate to the image as metadata. + """ if self.glance is None: glance_endpoint = self.keystone. \ service_catalog.url_for(service_type='image', @@ -495,8 +501,8 @@ class NovaWrapper(object): def delete_instance(self, instance_id): """This method deletes a given instance. - :param instance_id: the unique id of the instance to delete. - """ + :param instance_id: the unique id of the instance to delete. + """ LOG.debug("Trying to remove instance %s ..." % instance_id) @@ -513,8 +519,8 @@ class NovaWrapper(object): def stop_instance(self, instance_id): """This method stops a given instance. - :param instance_id: the unique id of the instance to stop. - """ + :param instance_id: the unique id of the instance to stop. + """ LOG.debug("Trying to stop instance %s ..." % instance_id) @@ -533,14 +539,17 @@ class NovaWrapper(object): return False def wait_for_vm_state(self, server, vm_state, retry, sleep): - """Waits for server to be in vm_state which can be one of the following : - active, stopped + """Waits for server to be in a specific vm_state + + The vm_state can be one of the following : + active, stopped + + :param server: server object. + :param vm_state: for which state we are waiting for + :param retry: how many times to retry + :param sleep: seconds to sleep between the retries + """ - :param server: server object. - :param vm_state: for which state we are waiting for - :param retry: how many times to retry - :param sleep: seconds to sleep between the retries - """ if not server: return False @@ -551,15 +560,18 @@ class NovaWrapper(object): return getattr(server, 'OS-EXT-STS:vm_state') == vm_state def wait_for_instance_status(self, instance, status_list, retry, sleep): - """Waits for instance to be in status which can be one of the following - : BUILD, ACTIVE, ERROR, VERIFY_RESIZE, SHUTOFF + """Waits for instance to be in a specific status + + The status can be one of the following + : BUILD, ACTIVE, ERROR, VERIFY_RESIZE, SHUTOFF + + :param instance: instance object. + :param status_list: tuple containing the list of + status we are waiting for + :param retry: how many times to retry + :param sleep: seconds to sleep between the retries + """ - :param instance: instance object. - :param status_list: tuple containing the list of - status we are waiting for - :param retry: how many times to retry - :param sleep: seconds to sleep between the retries - """ if not instance: return False @@ -577,11 +589,12 @@ class NovaWrapper(object): network_names_list=["demo-net"], keypair_name="mykeys", create_new_floating_ip=True, block_device_mapping_v2=None): - """This method creates a new instance. - It also creates, if requested, a new floating IP and associates - it with the new instance - It returns the unique id of the created instance. - """ + """This method creates a new instance + + It also creates, if requested, a new floating IP and associates + it with the new instance + It returns the unique id of the created instance. + """ LOG.debug( "Trying to create new instance '%s' " diff --git a/watcher/common/ceilometer.py b/watcher/common/ceilometer.py index 1047e0afb..6e9ac7c27 100644 --- a/watcher/common/ceilometer.py +++ b/watcher/common/ceilometer.py @@ -44,6 +44,7 @@ class CeilometerClient(object): def build_query(self, user_id=None, tenant_id=None, resource_id=None, user_ids=None, tenant_ids=None, resource_ids=None): """Returns query built from given parameters. + This query can be then used for querying resources, meters and statistics. :Parameters: @@ -54,6 +55,7 @@ class CeilometerClient(object): - `tenant_ids`: list of tenant_ids - `resource_ids`: list of resource_ids """ + user_ids = user_ids or [] tenant_ids = tenant_ids or [] resource_ids = resource_ids or [] @@ -109,7 +111,8 @@ class CeilometerClient(object): meter_name, period, aggregate='avg'): - """ + """Representing a statistic aggregate by operators + :param resource_id: id :param meter_name: meter names of which we want the statistics :param period: `period`: In seconds. If no period is given, only one @@ -119,7 +122,6 @@ class CeilometerClient(object): :param aggregate: :return: """ - """Representing a statistic aggregate by operators""" query = self.build_query(resource_id=resource_id) statistic = self.query_retry(f=self.cmclient.statistics.list, diff --git a/watcher/common/keystone.py b/watcher/common/keystone.py index e79cf9de9..7dc54fe66 100644 --- a/watcher/common/keystone.py +++ b/watcher/common/keystone.py @@ -73,8 +73,8 @@ class KeystoneClient(object): return auth_version == 'v3.0' or '/v3' in urlparse(auth_url).path def get_keystone_url(self, auth_url, auth_version): - """Gives an http/https url to contact keystone. - """ + """Gives an http/https url to contact keystone.""" + api_v3 = self._is_apiv3(auth_url, auth_version) api_version = 'v3' if api_v3 else 'v2.0' # NOTE(lucasagomes): Get rid of the trailing '/' otherwise urljoin() @@ -82,8 +82,8 @@ class KeystoneClient(object): return urljoin(auth_url.rstrip('/'), api_version) def _get_ksclient(self): - """Get an endpoint and auth token from Keystone. - """ + """Get an endpoint and auth token from Keystone.""" + ks_args = self.get_credentials() auth_version = CONF.keystone_authtoken.auth_version auth_url = CONF.keystone_authtoken.auth_uri diff --git a/watcher/contrib/tempest/tempest/api/infra_optim/admin/base.py b/watcher/contrib/tempest/tempest/api/infra_optim/admin/base.py index d57dc988a..e0669af64 100644 --- a/watcher/contrib/tempest/tempest/api/infra_optim/admin/base.py +++ b/watcher/contrib/tempest/tempest/api/infra_optim/admin/base.py @@ -93,14 +93,13 @@ class BaseInfraOptimTest(test.BaseTestCase): @classmethod @creates('audit_template') def create_audit_template(cls, description=None, expect_errors=False): - """ - Wrapper utility for creating test audit_template. + """Wrapper utility for creating test audit_template. :param description: A description of the audit template. if not supplied, a random value will be generated. :return: Created audit template. - """ + description = description or data_utils.rand_name( 'test-audit_template') resp, body = cls.client.create_audit_template(description=description) @@ -108,12 +107,10 @@ class BaseInfraOptimTest(test.BaseTestCase): @classmethod def delete_audit_template(cls, audit_template_id): - """ - Deletes a audit_template having the specified UUID. + """Deletes a audit_template having the specified UUID. :param uuid: The unique identifier of the audit_template. :return: Server response. - """ resp, body = cls.client.delete_audit_template(audit_template_id) diff --git a/watcher/contrib/tempest/tempest/services/infra_optim/base.py b/watcher/contrib/tempest/tempest/services/infra_optim/base.py index 0ef038c3e..8aa056100 100644 --- a/watcher/contrib/tempest/tempest/services/infra_optim/base.py +++ b/watcher/contrib/tempest/tempest/services/infra_optim/base.py @@ -40,10 +40,7 @@ def handle_errors(f): class InfraOptimClient(service_client.ServiceClient): - """ - Base Tempest REST client for Watcher API. - - """ + """Base Tempest REST client for Watcher API.""" uri_prefix = '' @@ -58,14 +55,13 @@ class InfraOptimClient(service_client.ServiceClient): return json.loads(object_str) def _get_uri(self, resource_name, uuid=None, permanent=False): - """ - Get URI for a specific resource or object. + """Get URI for a specific resource or object. :param resource_name: The name of the REST resource, e.g., 'audits'. :param uuid: The unique identifier of an object in UUID format. :return: Relative URI for the resource or object. - """ + prefix = self.uri_prefix if not permanent else '' return '{pref}/{res}{uuid}'.format(pref=prefix, @@ -73,16 +69,15 @@ class InfraOptimClient(service_client.ServiceClient): uuid='/%s' % uuid if uuid else '') def _make_patch(self, allowed_attributes, **kw): - """ - Create a JSON patch according to RFC 6902. + """Create a JSON patch according to RFC 6902. :param allowed_attributes: An iterable object that contains a set of allowed attributes for an object. :param **kw: Attributes and new values for them. :return: A JSON path that sets values of the specified attributes to the new ones. - """ + def get_change(kw, path='/'): for name, value in six.iteritems(kw): if isinstance(value, dict): @@ -103,15 +98,14 @@ class InfraOptimClient(service_client.ServiceClient): return patch def _list_request(self, resource, permanent=False, **kwargs): - """ - Get the list of objects of the specified type. + """Get the list of objects of the specified type. :param resource: The name of the REST resource, e.g., 'audits'. "param **kw: Parameters for the request. :return: A tuple with the server response and deserialized JSON list of objects - """ + uri = self._get_uri(resource, permanent=permanent) if kwargs: uri += "?%s" % urllib.urlencode(kwargs) @@ -122,13 +116,12 @@ class InfraOptimClient(service_client.ServiceClient): return resp, self.deserialize(body) def _show_request(self, resource, uuid, permanent=False, **kwargs): - """ - Gets a specific object of the specified type. + """Gets a specific object of the specified type. :param uuid: Unique identifier of the object in UUID format. :return: Serialized object as a dictionary. - """ + if 'uri' in kwargs: uri = kwargs['uri'] else: @@ -139,16 +132,15 @@ class InfraOptimClient(service_client.ServiceClient): return resp, self.deserialize(body) def _create_request(self, resource, object_dict): - """ - Create an object of the specified type. + """Create an object of the specified type. :param resource: The name of the REST resource, e.g., 'audits'. :param object_dict: A Python dict that represents an object of the specified type. :return: A tuple with the server response and the deserialized created object. - """ + body = self.serialize(object_dict) uri = self._get_uri(resource) @@ -158,14 +150,13 @@ class InfraOptimClient(service_client.ServiceClient): return resp, self.deserialize(body) def _delete_request(self, resource, uuid): - """ - Delete specified object. + """Delete specified object. :param resource: The name of the REST resource, e.g., 'audits'. :param uuid: The unique identifier of an object in UUID format. :return: A tuple with the server response and the response body. - """ + uri = self._get_uri(resource, uuid) resp, body = self.delete(uri) @@ -173,15 +164,14 @@ class InfraOptimClient(service_client.ServiceClient): return resp, body def _patch_request(self, resource, uuid, patch_object): - """ - Update specified object with JSON-patch. + """Update specified object with JSON-patch. :param resource: The name of the REST resource, e.g., 'audits'. :param uuid: The unique identifier of an object in UUID format. :return: A tuple with the server response and the serialized patched object. - """ + uri = self._get_uri(resource, uuid) patch_body = json.dumps(patch_object) @@ -197,20 +187,17 @@ class InfraOptimClient(service_client.ServiceClient): @handle_errors def get_version_description(self, version='v1'): - """ - Retrieves the description of the API. + """Retrieves the description of the API. :param version: The version of the API. Default: 'v1'. :return: Serialized description of API resources. - """ + return self._list_request(version, permanent=True) def _put_request(self, resource, put_object): - """ - Update specified object with JSON-patch. + """Update specified object with JSON-patch.""" - """ uri = self._get_uri(resource) put_body = json.dumps(put_object) diff --git a/watcher/contrib/tempest/tempest/services/infra_optim/v1/json/infra_optim_client.py b/watcher/contrib/tempest/tempest/services/infra_optim/v1/json/infra_optim_client.py index 233336d5f..aacf23c17 100644 --- a/watcher/contrib/tempest/tempest/services/infra_optim/v1/json/infra_optim_client.py +++ b/watcher/contrib/tempest/tempest/services/infra_optim/v1/json/infra_optim_client.py @@ -14,9 +14,7 @@ from tempest.services.infra_optim import base class InfraOptimClientJSON(base.InfraOptimClient): - """ - Base Tempest REST client for Watcher API v1. - """ + """Base Tempest REST client for Watcher API v1.""" version = '1' uri_prefix = 'v1' @@ -40,45 +38,41 @@ class InfraOptimClientJSON(base.InfraOptimClient): @base.handle_errors def show_audit_template(self, uuid): - """ - Gets a specific audit template. + """Gets a specific audit template. :param uuid: Unique identifier of the audit template in UUID format. :return: Serialized audit template as a dictionary. - """ + return self._show_request('audit_templates', uuid) @base.handle_errors def show_audit_template_by_host_agregate(self, host_agregate_id): - """ - Gets an audit template associated with given host agregate ID. + """Gets an audit template associated with given host agregate ID. :param uuid: Unique identifier of the audit_template in UUID format. :return: Serialized audit_template as a dictionary. - """ + uri = '/audit_templates/detail?host_agregate=%s' % host_agregate_id return self._show_request('audit_templates', uuid=None, uri=uri) @base.handle_errors def show_audit_template_by_goal(self, goal): - """ - Gets an audit template associated with given goal. + """Gets an audit template associated with given goal. :param uuid: Unique identifier of the audit_template in UUID format. :return: Serialized audit_template as a dictionary. - """ + uri = '/audit_templates/detail?goal=%s' % goal return self._show_request('audit_templates', uuid=None, uri=uri) @base.handle_errors def create_audit_template(self, **kwargs): - """ - Creates an audit template with the specified parameters. + """Creates an audit template with the specified parameters. :param name: The name of the audit template. Default: My Audit Template :param description: The description of the audit template. @@ -91,8 +85,8 @@ class InfraOptimClientJSON(base.InfraOptimClient): Default: {} :return: A tuple with the server response and the created audit template. - """ + audit_template = { 'name': kwargs.get('name', 'My Audit Template'), 'description': kwargs.get('description', 'AT Description'), @@ -127,25 +121,22 @@ class InfraOptimClientJSON(base.InfraOptimClient): @base.handle_errors def delete_audit_template(self, uuid): - """ - Deletes an audit template having the specified UUID. + """Deletes an audit template having the specified UUID. :param uuid: The unique identifier of the audit template. :return: A tuple with the server response and the response body. - """ + return self._delete_request('audit_templates', uuid) @base.handle_errors def update_audit_template(self, uuid, patch): - """ - Update the specified audit template. + """Update the specified audit template. :param uuid: The unique identifier of the audit template. :param patch: List of dicts representing json patches. :return: A tuple with the server response and the updated audit template. - """ return self._patch_request('audit_templates', uuid, patch) diff --git a/watcher/decision_engine/actions/migration.py b/watcher/decision_engine/actions/migration.py index d730635fe..934618272 100644 --- a/watcher/decision_engine/actions/migration.py +++ b/watcher/decision_engine/actions/migration.py @@ -32,6 +32,7 @@ class MigrationType(Enum): class Migrate(BaseAction): def __init__(self, vm, src_hypervisor, dest_hypervisor): """Request to migrate a virtual machine from a host to another + :param vm: the virtual machine uuid to migrate :param src_hypervisor: uuid :param dest_hypervisor: uuid diff --git a/watcher/decision_engine/model/mapping.py b/watcher/decision_engine/model/mapping.py index 53c0505a7..b9cd1eb7e 100644 --- a/watcher/decision_engine/model/mapping.py +++ b/watcher/decision_engine/model/mapping.py @@ -32,6 +32,7 @@ class Mapping(object): :param hypervisor: the hypervisor :param vm: the virtual machine or instance """ + try: self.lock.acquire() @@ -55,13 +56,15 @@ class Mapping(object): :param hypervisor: the hypervisor :param vm: the virtual machine or instance """ + self.unmap_from_id(hypervisor.uuid, vm.uuid) def unmap_from_id(self, node_uuid, vm_uuid): - """ + """Remove the instance (by id) from the hypervisor (by id) :rtype : object """ + try: self.lock.acquire() if str(node_uuid) in self._mapping_hypervisors: @@ -91,6 +94,7 @@ class Mapping(object): :param vm: the uuid of the instance :return: hypervisor """ + return self.model.get_hypervisor_from_id( self.get_mapping_vm()[str(vm_uuid)]) @@ -117,6 +121,7 @@ class Mapping(object): :param dest_hypervisor: :return: """ + if src_hypervisor == dest_hypervisor: return False # unmap diff --git a/watcher/decision_engine/solution/default.py b/watcher/decision_engine/solution/default.py index bde4daf4d..8656adfef 100644 --- a/watcher/decision_engine/solution/default.py +++ b/watcher/decision_engine/solution/default.py @@ -24,9 +24,10 @@ LOG = log.getLogger(__name__) class DefaultSolution(Solution): def __init__(self): - """The DefaultSolution class store a set of actions generated by a - strategy in order to achieve the goal. + """Stores a set of actions generated by a strategy + The DefaultSolution class store a set of actions generated by a + strategy in order to achieve the goal. """ super(DefaultSolution, self).__init__() self._actions = [] @@ -39,6 +40,5 @@ class DefaultSolution(Solution): @property def actions(self): - """Get the current actions of the solution - """ + """Get the current actions of the solution""" return self._actions diff --git a/watcher/decision_engine/strategy/base.py b/watcher/decision_engine/strategy/base.py index 37e881aeb..b99f09977 100644 --- a/watcher/decision_engine/strategy/base.py +++ b/watcher/decision_engine/strategy/base.py @@ -27,7 +27,9 @@ LOG = log.getLogger(__name__) @six.add_metaclass(abc.ABCMeta) class BaseStrategy(object): - """A Strategy is an algorithm implementation which is able to find a + """A base class for all the strategies + + A Strategy is an algorithm implementation which is able to find a Solution for a given Goal. """ diff --git a/watcher/objects/audit_template.py b/watcher/objects/audit_template.py index 0fc5d6caf..f98d88681 100644 --- a/watcher/objects/audit_template.py +++ b/watcher/objects/audit_template.py @@ -58,8 +58,7 @@ class AuditTemplate(base.WatcherObject): @classmethod def get(cls, context, audit_template_id): - """Find a audit template based on its id or uuid and return an - :class:`AuditTemplate` object. + """Find an audit template based on its id or uuid :param context: Security context. NOTE: This should only be used internally by the indirection_api. @@ -80,8 +79,7 @@ class AuditTemplate(base.WatcherObject): @classmethod def get_by_id(cls, context, audit_template_id): - """Find an audit template based on its integer id and return a - :class:`AuditTemplate` object. + """Find an audit template based on its integer id :param context: Security context. NOTE: This should only be used internally by the indirection_api. @@ -102,8 +100,7 @@ class AuditTemplate(base.WatcherObject): @classmethod def get_by_uuid(cls, context, uuid): - """Find an audit template based on uuid and return a - :class:`AuditTemplate` object. + """Find an audit template based on uuid :param context: Security context. NOTE: This should only be used internally by the indirection_api. @@ -122,13 +119,13 @@ class AuditTemplate(base.WatcherObject): @classmethod def get_by_name(cls, context, name): - """Find an audit template based on name and return a - :class:`AuditTemplate` object. + """Find an audit template based on name :param name: the logical name of a audit_template. :param context: Security context :returns: a :class:`AuditTemplate` object. """ + db_audit_template = cls.dbapi.get_audit_template_by_name(context, name) audit_template = AuditTemplate._from_db_object(cls(context), db_audit_template) @@ -170,8 +167,8 @@ class AuditTemplate(base.WatcherObject): argument, even though we don't use it. A context should be set when instantiating the object, e.g.: AuditTemplate(context) - """ + values = self.obj_get_changes() goal = values['goal'] if goal not in cfg.CONF.watcher_goals.goals.keys(): @@ -189,6 +186,7 @@ class AuditTemplate(base.WatcherObject): A context should be set when instantiating the object, e.g.: AuditTemplate(context) """ + self.dbapi.destroy_audit_template(self.uuid) self.obj_reset_changes() @@ -205,6 +203,7 @@ class AuditTemplate(base.WatcherObject): A context should be set when instantiating the object, e.g.: AuditTemplate(context) """ + updates = self.obj_get_changes() self.dbapi.update_audit_template(self.uuid, updates) @@ -224,6 +223,7 @@ class AuditTemplate(base.WatcherObject): A context should be set when instantiating the object, e.g.: AuditTemplate(context) """ + current = self.__class__.get_by_uuid(self._context, uuid=self.uuid) for field in self.fields: if (hasattr(self, base.get_attrname(field)) and @@ -240,4 +240,5 @@ class AuditTemplate(base.WatcherObject): A context should be set when instantiating the object, e.g.: AuditTemplate(context) """ + self.dbapi.soft_delete_audit_template(self.uuid)