From fec8282ec442571e251e509bc3c3677dba6b7a5e Mon Sep 17 00:00:00 2001 From: Alexey Stepanov Date: Tue, 12 Jan 2016 15:57:48 +0300 Subject: [PATCH] Minor refactor 1. SingletonMeta -> modified to allow handling multiple classes and moved to metaclasses too. 2. @logwrap : process asserts too Changes reason: @logwrap was changed during test debug (was assert in wrapped function and stacktrace was not enough for reason understand) and moved out of test changest due to not mandatory for this test. Pros: Now assert, raised in wrapped function is recorded in log with details. SingletonMeta: was changed as "nice to have" during work on plugin helpers. Later plugin helpers was moved out of SshManager, and all code changes was moved to this changeset. Pros: Single way for handling the same tasks in different places. Previously SshManager used Metaclass, which could not be reused for more, than one class and Environment used __new__ directly written in class code. Related-Bug: #1518979 Related-Bug: #1519050 Change-Id: I798302879400747909229cc208f97a669f25bbf3 --- doc/helpers.rst | 5 ++++ fuelweb_test/__init__.py | 15 ++++++++--- fuelweb_test/helpers/metaclasses.py | 27 ++++++++++++++++++++ fuelweb_test/helpers/ssh_manager.py | 39 ++++++++++++++--------------- fuelweb_test/models/environment.py | 9 ++----- 5 files changed, 65 insertions(+), 30 deletions(-) create mode 100644 fuelweb_test/helpers/metaclasses.py diff --git a/doc/helpers.rst b/doc/helpers.rst index c7ea422d3..78b7e4d96 100644 --- a/doc/helpers.rst +++ b/doc/helpers.rst @@ -28,6 +28,11 @@ Decorators .. automodule:: fuelweb_test.helpers.decorators :members: +Metaclasses +----------- +.. automodule:: fuelweb_test.helpers.metaclasses + :members: + Eb tables --------- .. automodule:: fuelweb_test.helpers.eb_tables diff --git a/fuelweb_test/__init__.py b/fuelweb_test/__init__.py index d9a9271f5..03aee5d0e 100644 --- a/fuelweb_test/__init__.py +++ b/fuelweb_test/__init__.py @@ -13,6 +13,7 @@ # under the License. import functools import logging +import traceback import os from fuelweb_test.settings import LOGS_DIR @@ -54,9 +55,17 @@ def debug(logger): func.__name__, args, kwargs ) ) - result = func(*args, **kwargs) - logger.debug( - "Done: {} with result: {}".format(func.__name__, result)) + try: + result = func(*args, **kwargs) + logger.debug( + "Done: {} with result: {}".format(func.__name__, result)) + except BaseException as e: + tb = traceback.format_exc() + logger.error( + '{func} raised: {exc!r}\n' + 'Traceback: {tb!s}'.format( + func=func.__name__, exc=e, tb=tb)) + raise return result return wrapped return wrapper diff --git a/fuelweb_test/helpers/metaclasses.py b/fuelweb_test/helpers/metaclasses.py new file mode 100644 index 000000000..6e1e79bcf --- /dev/null +++ b/fuelweb_test/helpers/metaclasses.py @@ -0,0 +1,27 @@ +# Copyright 2016 Mirantis, Inc. +# +# 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. + + +class SingletonMeta(type): + """Metaclass for Singleton + + Main goals: not need to implement __new__ in singleton classes + """ + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super( + SingletonMeta, cls).__call__(*args, **kwargs) + return cls._instances[cls] diff --git a/fuelweb_test/helpers/ssh_manager.py b/fuelweb_test/helpers/ssh_manager.py index 9fff6887e..55a35457a 100644 --- a/fuelweb_test/helpers/ssh_manager.py +++ b/fuelweb_test/helpers/ssh_manager.py @@ -1,4 +1,4 @@ -# Copyright 2015 Mirantis, Inc. +# Copyright 2016 Mirantis, Inc. # # 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 @@ -20,32 +20,27 @@ import json from paramiko import RSAKey from devops.models.node import SSHClient from fuelweb_test import logger - - -class SingletonMeta(type): - def __init__(cls, name, bases, dict): - super(SingletonMeta, cls).__init__(name, bases, dict) - cls.instance = None - - def __call__(self, *args, **kw): - if self.instance is None: - self.instance = super(SingletonMeta, self).__call__(*args, **kw) - return self.instance - - def __getattr__(cls, name): - return getattr(cls(), name) +from fuelweb_test.helpers import metaclasses class SSHManager(object): - __metaclass__ = SingletonMeta + __metaclass__ = metaclasses.SingletonMeta + # Slots is used to prevent uncontrolled attributes set or remove. + __slots__ = [ + '__connections', 'admin_ip', 'admin_port', 'login', '__password' + ] def __init__(self): logger.debug('SSH_MANAGER: Run constructor SSHManager') - self.connections = {} + self.__connections = {} # Disallow direct type change and deletion self.admin_ip = None self.admin_port = None self.login = None - self.password = None + self.__password = None + + @property + def connections(self): + return self.__connections def initialize(self, admin_ip, login, password): """ It will be moved to __init__ @@ -58,7 +53,7 @@ class SSHManager(object): self.admin_ip = admin_ip self.admin_port = 22 self.login = login - self.password = password + self.__password = password def _connect(self, remote): """ Check if connection is stable and return this one @@ -97,7 +92,7 @@ class SSHManager(object): host=ip, port=port, username=self.login, - password=self.password, + password=self.__password, private_keys=keys ) logger.debug('SSH_MANAGER:Return existed connection for ' @@ -192,6 +187,10 @@ class SSHManager(object): return result + def execute_async_on_remote(self, ip, cmd, port=22): + remote = self._get_remote(ip=ip, port=port) + return remote.execute_async(cmd) + def _json_deserialize(self, json_string): """ Deserialize json_string and return object diff --git a/fuelweb_test/models/environment.py b/fuelweb_test/models/environment.py index 165ad8f64..0e4947d50 100644 --- a/fuelweb_test/models/environment.py +++ b/fuelweb_test/models/environment.py @@ -28,6 +28,7 @@ from proboscis.asserts import assert_true from fuelweb_test.helpers.decorators import revert_info from fuelweb_test.helpers.decorators import update_rpm_packages from fuelweb_test.helpers.decorators import upload_manifests +from fuelweb_test.helpers.metaclasses import SingletonMeta from fuelweb_test.helpers.eb_tables import Ebtables from fuelweb_test.helpers.fuel_actions import AdminActions from fuelweb_test.helpers.fuel_actions import BaseActions @@ -51,13 +52,7 @@ from fuelweb_test import logger class EnvironmentModel(object): """EnvironmentModel.""" # TODO documentation - _instance = None - - def __new__(cls, *args, **kwargs): - if not cls._instance: - cls._instance = super(EnvironmentModel, cls).__new__( - cls, *args, **kwargs) - return cls._instance + __metaclass__ = SingletonMeta def __init__(self, config=None): if not hasattr(self, "_virt_env"):