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
This commit is contained in:
Alexey Stepanov 2016-01-12 15:57:48 +03:00
parent 54974cad78
commit fec8282ec4
5 changed files with 65 additions and 30 deletions

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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"):