diff --git a/doc/ext/driver_doc.py b/doc/ext/driver_doc.py index 2ca2c07..0ec4940 100644 --- a/doc/ext/driver_doc.py +++ b/doc/ext/driver_doc.py @@ -48,7 +48,7 @@ class CloudDriverDocDirective(rst.Directive): subcat = utils.subcategory('{} [{}]'.format(drv_name, types)) subcat.extend(utils.parse_text(doc)) - subcat.extend(utils.parse_text('**Supported services:**')) + subcat.extend(utils.parse_text('**Default services:**')) subcat.extend(utils.rstlist(services)) return [subcat] diff --git a/doc/source/drivers.rst b/doc/source/drivers.rst index 69963f3..6ba3e62 100644 --- a/doc/source/drivers.rst +++ b/doc/source/drivers.rst @@ -25,3 +25,18 @@ Node discover .. driver_doc:: node_list + +Services +-------- + +.. driver_doc:: process + +.. driver_doc:: linux_service + +.. driver_doc:: screen + +.. driver_doc:: salt_service + +.. driver_doc:: pcs_service + +.. driver_doc:: pcs_or_linux_service diff --git a/os_faults/api/cloud_management.py b/os_faults/api/cloud_management.py index b6bdfaa..a261184 100644 --- a/os_faults/api/cloud_management.py +++ b/os_faults/api/cloud_management.py @@ -12,6 +12,7 @@ # limitations under the License. import abc +import copy import logging import six @@ -20,20 +21,21 @@ from os_faults.api import base_driver from os_faults.api import error from os_faults.api import node_collection from os_faults.api import power_management +from os_faults import registry LOG = logging.getLogger(__name__) @six.add_metaclass(abc.ABCMeta) class CloudManagement(base_driver.BaseDriver): - SERVICE_NAME_TO_CLASS = {} - SUPPORTED_SERVICES = [] + SERVICES = {} SUPPORTED_NETWORKS = [] NODE_CLS = node_collection.NodeCollection def __init__(self): self.power_manager = power_management.PowerManager() self.node_discover = None + self.services = copy.deepcopy(self.SERVICES) def add_power_management(self, driver): self.power_manager.add_driver(driver) @@ -75,12 +77,15 @@ class CloudManagement(base_driver.BaseDriver): :param name: name of the serives :return: Service """ - if name in self.SERVICE_NAME_TO_CLASS: - klazz = self.SERVICE_NAME_TO_CLASS[name] - return klazz(node_cls=self.NODE_CLS, cloud_management=self) - raise error.ServiceError( - '{} driver does not support {!r} service'.format( - self.NAME.title(), name)) + if name not in self.services: + raise error.ServiceError( + '{} driver does not support {!r} service'.format( + self.NAME.title(), name)) + + config = self.services[name] + klazz = registry.get_driver(config["driver"]) + return klazz(node_cls=self.NODE_CLS, cloud_management=self, + service_name=name, config=config["args"]) @abc.abstractmethod def execute_on_cloud(self, hosts, task, raise_on_error=True): @@ -98,7 +103,7 @@ class CloudManagement(base_driver.BaseDriver): :return: [String] list of service names """ - return cls.SUPPORTED_SERVICES + return cls.SERVICES.keys() @classmethod def list_supported_networks(cls): diff --git a/os_faults/api/error.py b/os_faults/api/error.py index cd1841c..d287087 100644 --- a/os_faults/api/error.py +++ b/os_faults/api/error.py @@ -38,3 +38,7 @@ class NodeCollectionError(OSFError): class OSFDriverNotFound(OSFError): """Driver Not Found by os-faults registry""" + + +class OSFDriverWithSuchNameExists(OSFError): + """Driver with such name already exists in os-faults registry""" diff --git a/os_faults/api/service.py b/os_faults/api/service.py index a92080c..3262b19 100644 --- a/os_faults/api/service.py +++ b/os_faults/api/service.py @@ -15,11 +15,18 @@ import abc import six +from os_faults.api import base_driver from os_faults.api.util import public @six.add_metaclass(abc.ABCMeta) -class Service(object): +class Service(base_driver.BaseDriver): + + def __init__(self, service_name, config, node_cls, cloud_management): + self.service_name = service_name + self.config = config + self.node_cls = node_cls + self.cloud_management = cloud_management @abc.abstractmethod def get_nodes(self): diff --git a/os_faults/common/service.py b/os_faults/common/service.py index 613115b..0b948f5 100644 --- a/os_faults/common/service.py +++ b/os_faults/common/service.py @@ -21,12 +21,71 @@ from os_faults import utils LOG = logging.getLogger(__name__) +PORT_SCHEMA = { + 'type': 'array', + 'items': [ + {'enum': ['tcp', 'udp']}, + {'type': 'integer', 'minimum': 0, 'maximum': 65535}, + ], + 'minItems': 2, + 'maxItems': 2, +} + class ServiceAsProcess(service.Service): + """Service as process - def __init__(self, node_cls, cloud_management=None): - self.node_cls = node_cls - self.cloud_management = cloud_management + "process" is a basic service driver that uses `ps` and `kill` in + actions like kill / freeze / unfreeze. Commands for start / restart + / terminate should be specified in configuration, otherwise + the commands will fail at runtime. + + **Example configuration:** + + .. code-block:: yaml + + services: + app: + driver: process + args: + grep: my_app + restart_cmd: /bin/my_app --restart + terminate_cmd: /bin/stop_my_app + start_cmd: /bin/my_app + port: ['tcp', 4242] + + parameters: + + - **grep** - regexp for grep to find process PID + - **restart_cmd** - command to restart service (optional) + - **terminate_cmd** - command to terminate service (optional) + - **start_cmd** - command to start service (optional) + - **port** - tuple with two values - potocol, port number (optional) + + """ + + NAME = 'process' + DESCRIPTION = 'Service as process' + CONFIG_SCHEMA = { + 'type': 'object', + 'properties': { + 'grep': {'type': 'string'}, + 'start_cmd': {'type': 'string'}, + 'terminate_cmd': {'type': 'string'}, + 'restart_cmd': {'type': 'string'}, + 'port': PORT_SCHEMA, + }, + 'required': ['grep'], + 'additionalProperties': False, + } + + def __init__(self, *args, **kwargs): + super(ServiceAsProcess, self).__init__(*args, **kwargs) + self.grep = self.config['grep'] + self.start_cmd = self.config.get('start_cmd') + self.terminate_cmd = self.config.get('terminate_cmd') + self.restart_cmd = self.config.get('restart_cmd') + self.port = self.config.get('port') def _run_task(self, task, nodes): ips = nodes.get_ips() @@ -47,7 +106,7 @@ class ServiceAsProcess(service.Service): def get_nodes(self): nodes = self.cloud_management.get_nodes() ips = nodes.get_ips() - cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format(self.GREP) + cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format(self.grep) results = self.cloud_management.execute_on_cloud( ips, {'command': cmd}, False) success_ips = [r.host for r in results @@ -56,81 +115,114 @@ class ServiceAsProcess(service.Service): return self.node_cls(cloud_management=self.cloud_management, hosts=hosts) - @utils.require_variables('RESTART_CMD', 'SERVICE_NAME') + @utils.require_variables('restart_cmd') def restart(self, nodes=None): nodes = nodes if nodes is not None else self.get_nodes() - LOG.info("Restart '%s' service on nodes: %s", self.SERVICE_NAME, + LOG.info("Restart '%s' service on nodes: %s", self.service_name, nodes.get_ips()) - self._run_task({'shell': self.RESTART_CMD}, nodes) + self._run_task({'shell': self.restart_cmd}, nodes) - @utils.require_variables('TERMINATE_CMD', 'SERVICE_NAME') + @utils.require_variables('terminate_cmd') def terminate(self, nodes=None): nodes = nodes if nodes is not None else self.get_nodes() - LOG.info("Terminate '%s' service on nodes: %s", self.SERVICE_NAME, + LOG.info("Terminate '%s' service on nodes: %s", self.service_name, nodes.get_ips()) - self._run_task({'shell': self.TERMINATE_CMD}, nodes) + self._run_task({'shell': self.terminate_cmd}, nodes) - @utils.require_variables('START_CMD', 'SERVICE_NAME') + @utils.require_variables('start_cmd') def start(self, nodes=None): nodes = nodes if nodes is not None else self.get_nodes() - LOG.info("Start '%s' service on nodes: %s", self.SERVICE_NAME, + LOG.info("Start '%s' service on nodes: %s", self.service_name, nodes.get_ips()) - self._run_task({'shell': self.START_CMD}, nodes) + self._run_task({'shell': self.start_cmd}, nodes) - @utils.require_variables('GREP', 'SERVICE_NAME') def kill(self, nodes=None): nodes = nodes if nodes is not None else self.get_nodes() - LOG.info("Kill '%s' service on nodes: %s", self.SERVICE_NAME, + LOG.info("Kill '%s' service on nodes: %s", self.service_name, nodes.get_ips()) - cmd = {'kill': {'grep': self.GREP, 'sig': signal.SIGKILL}} + cmd = {'kill': {'grep': self.grep, 'sig': signal.SIGKILL}} self._run_task(cmd, nodes) - @utils.require_variables('GREP', 'SERVICE_NAME') def freeze(self, nodes=None, sec=None): nodes = nodes if nodes is not None else self.get_nodes() if sec: - cmd = {'freeze': {'grep': self.GREP, 'sec': sec}} + cmd = {'freeze': {'grep': self.grep, 'sec': sec}} else: - cmd = {'kill': {'grep': self.GREP, 'sig': signal.SIGSTOP}} - LOG.info("Freeze '%s' service %son nodes: %s", self.SERVICE_NAME, + cmd = {'kill': {'grep': self.grep, 'sig': signal.SIGSTOP}} + LOG.info("Freeze '%s' service %son nodes: %s", self.service_name, ('for %s sec ' % sec) if sec else '', nodes.get_ips()) self._run_task(cmd, nodes) - @utils.require_variables('GREP', 'SERVICE_NAME') def unfreeze(self, nodes=None): nodes = nodes if nodes is not None else self.get_nodes() - LOG.info("Unfreeze '%s' service on nodes: %s", self.SERVICE_NAME, + LOG.info("Unfreeze '%s' service on nodes: %s", self.service_name, nodes.get_ips()) - cmd = {'kill': {'grep': self.GREP, 'sig': signal.SIGCONT}} + cmd = {'kill': {'grep': self.grep, 'sig': signal.SIGCONT}} self._run_task(cmd, nodes) - @utils.require_variables('PORT', 'SERVICE_NAME') + @utils.require_variables('port') def plug(self, nodes=None): nodes = nodes if nodes is not None else self.get_nodes() LOG.info("Open port %d for '%s' service on nodes: %s", - self.PORT[1], self.SERVICE_NAME, nodes.get_ips()) - self._run_task({'iptables': {'protocol': self.PORT[0], - 'port': self.PORT[1], + self.port[1], self.service_name, nodes.get_ips()) + self._run_task({'iptables': {'protocol': self.port[0], + 'port': self.port[1], 'action': 'unblock', - 'service': self.SERVICE_NAME}}, nodes) + 'service': self.service_name}}, nodes) - @utils.require_variables('PORT', 'SERVICE_NAME') + @utils.require_variables('port') def unplug(self, nodes=None): nodes = nodes if nodes is not None else self.get_nodes() LOG.info("Close port %d for '%s' service on nodes: %s", - self.PORT[1], self.SERVICE_NAME, nodes.get_ips()) - self._run_task({'iptables': {'protocol': self.PORT[0], - 'port': self.PORT[1], + self.port[1], self.service_name, nodes.get_ips()) + self._run_task({'iptables': {'protocol': self.port[0], + 'port': self.port[1], 'action': 'block', - 'service': self.SERVICE_NAME}}, nodes) + 'service': self.service_name}}, nodes) class LinuxService(ServiceAsProcess): + """Linux service + + Service that is defined in init.d and can be controled by `service` + CLI tool. + + **Example configuration:** + + .. code-block:: yaml + + services: + app: + driver: linux_service + args: + linux_service: app + grep: my_app + port: ['tcp', 4242] + + parameters: + + - **linux_service** - name of a service + - **grep** - regexp for grep to find process PID + - **port** - tuple with two values - potocol, port number (optional) + + """ + NAME = 'linux_service' + DESCRIPTION = 'Service in init.d' + CONFIG_SCHEMA = { + 'type': 'object', + 'properties': { + 'linux_service': {'type': 'string'}, + 'grep': {'type': 'string'}, + 'port': PORT_SCHEMA, + }, + 'required': ['grep', 'linux_service'], + 'additionalProperties': False, + } - @utils.require_variables('LINUX_SERVICE') def __init__(self, *args, **kwargs): super(LinuxService, self).__init__(*args, **kwargs) + self.linux_service = self.config['linux_service'] - self.RESTART_CMD = 'service {} restart'.format(self.LINUX_SERVICE) - self.TERMINATE_CMD = 'service {} stop'.format(self.LINUX_SERVICE) - self.START_CMD = 'service {} start'.format(self.LINUX_SERVICE) + self.restart_cmd = 'service {} restart'.format(self.linux_service) + self.terminate_cmd = 'service {} stop'.format(self.linux_service) + self.start_cmd = 'service {} start'.format(self.linux_service) diff --git a/os_faults/drivers/devstack.py b/os_faults/drivers/devstack.py index 1bc6269..0341059 100644 --- a/os_faults/drivers/devstack.py +++ b/os_faults/drivers/devstack.py @@ -18,7 +18,6 @@ from os_faults.api import cloud_management from os_faults.api import node_collection from os_faults.api import node_discover from os_faults.common import service -from os_faults import utils LOG = logging.getLogger(__name__) @@ -33,89 +32,64 @@ class DevStackNode(node_collection.NodeCollection): class ServiceInScreen(service.ServiceAsProcess): + """Service in Screen + + This driver controls service that is started in a window of + `screen` tool. + + **Example configuration:** + + .. code-block:: yaml + + services: + app: + driver: screen + args: + window_name: app + grep: my_app + port: ['tcp', 4242] + + parameters: + + - **window_name** - name of a service + - **grep** - regexp for grep to find process PID + - **port** - tuple with two values - potocol, port number (optional) + + """ + NAME = 'screen' + DESCRIPTION = 'Service in screen' + CONFIG_SCHEMA = { + 'type': 'object', + 'properties': { + 'window_name': {'type': 'string'}, + 'grep': {'type': 'string'}, + 'port': service.PORT_SCHEMA, + }, + 'required': ['grep', 'window_name'], + 'additionalProperties': False, + } - @utils.require_variables('WINDOW_NAME') def __init__(self, *args, **kwargs): super(ServiceInScreen, self).__init__(*args, **kwargs) + self.window_name = self.config['window_name'] # sends ctr+c, arrow up key, enter key - self.RESTART_CMD = ( + self.restart_cmd = ( "screen -S stack -p {window_name} -X " "stuff $'\\003'$'\\033[A'$(printf \\\\r)").format( - window_name=self.WINDOW_NAME) + window_name=self.window_name) # sends ctr+c - self.TERMINATE_CMD = ( + self.terminate_cmd = ( "screen -S stack -p {window_name} -X " "stuff $'\\003'").format( - window_name=self.WINDOW_NAME) + window_name=self.window_name) # sends arrow up key, enter key - self.START_CMD = ( + self.start_cmd = ( "screen -S stack -p {window_name} -X " "stuff $'\\033[A'$(printf \\\\r)").format( - window_name=self.WINDOW_NAME) - - -class KeystoneService(service.ServiceAsProcess): - SERVICE_NAME = 'keystone' - GREP = 'keystone-' - RESTART_CMD = 'sudo service apache2 restart' - TERMINATE_CMD = 'sudo service apache2 stop' - START_CMD = 'sudo service apache2 start' - - -class MySQLService(service.ServiceAsProcess): - SERVICE_NAME = 'mysql' - GREP = 'mysqld' - RESTART_CMD = 'sudo service mysql restart' - TERMINATE_CMD = 'sudo service mysql stop' - START_CMD = 'sudo service mysql start' - PORT = ('tcp', 3307) - - -class RabbitMQService(service.ServiceAsProcess): - SERVICE_NAME = 'rabbitmq' - GREP = 'rabbitmq-server' - RESTART_CMD = 'sudo service rabbitmq-server restart' - TERMINATE_CMD = 'sudo service rabbitmq-server stop' - START_CMD = 'sudo service rabbitmq-server start' - - -class NovaAPIService(ServiceInScreen): - SERVICE_NAME = 'nova-api' - GREP = 'nova-api' - WINDOW_NAME = 'n-api' - - -class GlanceAPIService(ServiceInScreen): - SERVICE_NAME = 'glance-api' - GREP = 'glance-api' - WINDOW_NAME = 'g-api' - - -class NovaComputeService(ServiceInScreen): - SERVICE_NAME = 'nova-compute' - GREP = 'nova-compute' - WINDOW_NAME = 'n-cpu' - - -class NovaSchedulerService(ServiceInScreen): - SERVICE_NAME = 'nova-scheduler' - GREP = 'nova-scheduler' - WINDOW_NAME = 'n-sch' - - -class IronicApiService(ServiceInScreen): - SERVICE_NAME = 'ironic-api' - GREP = 'ironic-api' - WINDOW_NAME = 'ir-api' - - -class IronicConductorService(ServiceInScreen): - SERVICE_NAME = 'ironic-conductor' - GREP = 'ironic-conductor' - WINDOW_NAME = 'ir-cond' + window_name=self.window_name) class DevStackManagement(cloud_management.CloudManagement, @@ -154,18 +128,78 @@ class DevStackManagement(cloud_management.CloudManagement, NAME = 'devstack' DESCRIPTION = 'DevStack management driver' NODE_CLS = DevStackNode - SERVICE_NAME_TO_CLASS = { - 'keystone': KeystoneService, - 'mysql': MySQLService, - 'rabbitmq': RabbitMQService, - 'nova-api': NovaAPIService, - 'glance-api': GlanceAPIService, - 'nova-compute': NovaComputeService, - 'nova-scheduler': NovaSchedulerService, - 'ironic-api': IronicApiService, - 'ironic-conductor': IronicConductorService, + SERVICES = { + 'keystone': { + 'driver': 'process', + 'args': { + 'grep': 'keystone-', + 'restart_cmd': 'sudo service apache2 restart', + 'terminate_cmd': 'sudo service apache2 stop', + 'start_cmd': 'sudo service apache2 start', + } + }, + 'mysql': { + 'driver': 'process', + 'args': { + 'grep': 'mysqld', + 'restart_cmd': 'sudo service mysql restart', + 'terminate_cmd': 'sudo service mysql stop', + 'start_cmd': 'sudo service mysql start', + 'port': ['tcp', 3307], + } + }, + 'rabbitmq': { + 'driver': 'process', + 'args': { + 'grep': 'rabbitmq-server', + 'restart_cmd': 'sudo service rabbitmq-server restart', + 'terminate_cmd': 'sudo service rabbitmq-server stop', + 'start_cmd': 'sudo service rabbitmq-server start', + } + }, + 'nova-api': { + 'driver': 'screen', + 'args': { + 'grep': 'nova-api', + 'window_name': 'n-api', + } + }, + 'glance-api': { + 'driver': 'screen', + 'args': { + 'grep': 'glance-api', + 'window_name': 'g-api', + } + }, + 'nova-compute': { + 'driver': 'screen', + 'args': { + 'grep': 'nova-compute', + 'window_name': 'n-cpu', + } + }, + 'nova-scheduler': { + 'driver': 'screen', + 'args': { + 'grep': 'nova-scheduler', + 'window_name': 'n-sch', + } + }, + 'ironic-api': { + 'driver': 'screen', + 'args': { + 'grep': 'ironic-api', + 'window_name': 'ir-api', + } + }, + 'ironic-conductor': { + 'driver': 'screen', + 'args': { + 'grep': 'ironic-conductor', + 'window_name': 'ir-cond', + } + }, } - SUPPORTED_SERVICES = list(SERVICE_NAME_TO_CLASS.keys()) SUPPORTED_NETWORKS = ['all-in-one'] CONFIG_SCHEMA = { 'type': 'object', diff --git a/os_faults/drivers/fuel.py b/os_faults/drivers/fuel.py index 816468f..009ce14 100644 --- a/os_faults/drivers/fuel.py +++ b/os_faults/drivers/fuel.py @@ -19,7 +19,6 @@ from os_faults.api import cloud_management from os_faults.api import node_collection from os_faults.api import node_discover from os_faults.common import service -from os_faults import utils LOG = logging.getLogger(__name__) @@ -45,299 +44,122 @@ class FuelNodeCollection(node_collection.NodeCollection): class PcsService(service.ServiceAsProcess): + """Service as a resource in Pacemaker + + Service that can be controled by `pcs resource` CLI tool. + + **Example configuration:** + + .. code-block:: yaml + + services: + app: + driver: pcs_service + args: + pcs_service: app + grep: my_app + port: ['tcp', 4242] + + parameters: + + - **pcs_service** - name of a service + - **grep** - regexp for grep to find process PID + - **port** - tuple with two values - potocol, port number (optional) + + """ + + NAME = 'pcs_service' + DESCRIPTION = 'Service in pacemaker' + CONFIG_SCHEMA = { + 'type': 'object', + 'properties': { + 'pcs_service': {'type': 'string'}, + 'grep': {'type': 'string'}, + 'port': service.PORT_SCHEMA, + }, + 'required': ['grep', 'pcs_service'], + 'additionalProperties': False, + } - @utils.require_variables('PCS_SERVICE') def __init__(self, *args, **kwargs): super(PcsService, self).__init__(*args, **kwargs) + self.pcs_service = self.config['pcs_service'] - self.RESTART_CMD = 'pcs resource restart {} $(hostname)'.format( - self.PCS_SERVICE) - self.TERMINATE_CMD = 'pcs resource ban {} $(hostname)'.format( - self.PCS_SERVICE) - self.START_CMD = 'pcs resource clear {} $(hostname)'.format( - self.PCS_SERVICE) + self.restart_cmd = 'pcs resource restart {} $(hostname)'.format( + self.pcs_service) + self.terminate_cmd = 'pcs resource ban {} $(hostname)'.format( + self.pcs_service) + self.start_cmd = 'pcs resource clear {} $(hostname)'.format( + self.pcs_service) class PcsOrLinuxService(service.ServiceAsProcess): + """Service as a resource in Pacemaker or Linux service + + Service that can be controled by `pcs resource` CLI tool or + linux `service` tool. This is a hybrid driver that tries to find + service in Pacemaker and uses linux `service` if it is not found + there. + + **Example configuration:** + + .. code-block:: yaml + + services: + app: + driver: pcs_or_linux_service + args: + pcs_service: p_app + linux_service: app + grep: my_app + port: ['tcp', 4242] + + parameters: + + - **pcs_service** - name of a service in Pacemaker + - **linux_service** - name of a service in init.d + - **grep** - regexp for grep to find process PID + - **port** - tuple with two values - potocol, port number (optional) + + """ + + NAME = 'pcs_or_linux_service' + DESCRIPTION = 'Service in pacemaker or init.d' + CONFIG_SCHEMA = { + 'type': 'object', + 'properties': { + 'pcs_service': {'type': 'string'}, + 'linux_service': {'type': 'string'}, + 'grep': {'type': 'string'}, + 'port': service.PORT_SCHEMA, + }, + 'required': ['grep', 'pcs_service', 'linux_service'], + 'additionalProperties': False, + } - @utils.require_variables('PCS_SERVICE', 'LINUX_SERVICE') def __init__(self, *args, **kwargs): super(PcsOrLinuxService, self).__init__(*args, **kwargs) + self.pcs_service = self.config.get('pcs_service') + self.linux_service = self.config.get('linux_service') - self.RESTART_CMD = ( + self.restart_cmd = ( 'if pcs resource show {pcs_service}; ' 'then pcs resource restart {pcs_service} $(hostname); ' 'else service {linux_service} restart; fi').format( - linux_service=self.LINUX_SERVICE, - pcs_service=self.PCS_SERVICE) - self.TERMINATE_CMD = ( + linux_service=self.linux_service, + pcs_service=self.pcs_service) + self.terminate_cmd = ( 'if pcs resource show {pcs_service}; ' 'then pcs resource ban {pcs_service} $(hostname); ' 'else service {linux_service} stop; fi').format( - linux_service=self.LINUX_SERVICE, - pcs_service=self.PCS_SERVICE) - self.START_CMD = ( + linux_service=self.linux_service, + pcs_service=self.pcs_service) + self.start_cmd = ( 'if pcs resource show {pcs_service}; ' 'then pcs resource clear {pcs_service} $(hostname); ' 'else service {linux_service} start; fi').format( - linux_service=self.LINUX_SERVICE, - pcs_service=self.PCS_SERVICE) - - -class KeystoneService(service.LinuxService): - SERVICE_NAME = 'keystone' - GREP = 'keystone' - LINUX_SERVICE = 'apache2' - - -class HorizonService(service.LinuxService): - SERVICE_NAME = 'horizon' - GREP = 'apache2' - LINUX_SERVICE = 'apache2' - - -class MemcachedService(service.LinuxService): - SERVICE_NAME = 'memcached' - GREP = 'memcached' - LINUX_SERVICE = 'memcached' - - -class MySQLService(PcsService): - SERVICE_NAME = 'mysql' - GREP = 'mysqld' - PCS_SERVICE = 'p_mysqld' - PORT = ('tcp', 3307) - - -class RabbitMQService(PcsService): - SERVICE_NAME = 'rabbitmq' - GREP = 'rabbit tcp_listeners' - PCS_SERVICE = 'p_rabbitmq-server' - - -class GlanceAPIService(service.LinuxService): - SERVICE_NAME = 'glance-api' - GREP = 'glance-api' - LINUX_SERVICE = 'glance-api' - - -class GlanceGlareService(service.LinuxService): - SERVICE_NAME = 'glance-glare' - GREP = 'glance-glare' - LINUX_SERVICE = 'glance-glare' - - -class GlanceRegistryService(service.LinuxService): - SERVICE_NAME = 'glance-registry' - GREP = 'glance-registry' - LINUX_SERVICE = 'glance-registry' - - -class NovaAPIService(service.LinuxService): - SERVICE_NAME = 'nova-api' - GREP = 'nova-api' - LINUX_SERVICE = 'nova-api' - - -class NovaComputeService(service.LinuxService): - SERVICE_NAME = 'nova-compute' - GREP = 'nova-compute' - LINUX_SERVICE = 'nova-compute' - - -class NovaSchedulerService(service.LinuxService): - SERVICE_NAME = 'nova-scheduler' - GREP = 'nova-scheduler' - LINUX_SERVICE = 'nova-scheduler' - - -class NovaCertService(service.LinuxService): - SERVICE_NAME = 'nova-cert' - GREP = 'nova-cert' - LINUX_SERVICE = 'nova-cert' - - -class NovaConductorService(service.LinuxService): - SERVICE_NAME = 'nova-conductor' - GREP = 'nova-conductor' - LINUX_SERVICE = 'nova-conductor' - - -class NovaConsoleAuthService(service.LinuxService): - SERVICE_NAME = 'nova-consoleauth' - GREP = 'nova-consoleauth' - LINUX_SERVICE = 'nova-consoleauth' - - -class NovaNoVNCProxyService(service.LinuxService): - SERVICE_NAME = 'nova-novncproxy' - GREP = 'nova-novncproxy' - LINUX_SERVICE = 'nova-novncproxy' - - -class NeutronServerService(service.LinuxService): - SERVICE_NAME = 'neutron-server' - GREP = 'neutron-server' - LINUX_SERVICE = 'neutron-server' - - -class NeutronDhcpAgentService(PcsService): - SERVICE_NAME = 'neutron-dhcp-agent' - GREP = 'neutron-dhcp-agent' - PCS_SERVICE = 'neutron-dhcp-agent' - - -class NeutronMetadataAgentService(PcsOrLinuxService): - SERVICE_NAME = 'neutron-metadata-agent' - GREP = 'neutron-metadata-agent' - PCS_SERVICE = 'neutron-metadata-agent' - LINUX_SERVICE = 'neutron-metadata-agent' - - -class NeutronOpenvswitchAgentService(PcsOrLinuxService): - SERVICE_NAME = 'neutron-openvswitch-agent' - GREP = 'neutron-openvswitch-agent' - PCS_SERVICE = 'neutron-openvswitch-agent' - LINUX_SERVICE = 'neutron-openvswitch-agent' - - -class NeutronL3AgentService(PcsOrLinuxService): - SERVICE_NAME = 'neutron-l3-agent' - GREP = 'neutron-l3-agent' - PCS_SERVICE = 'neutron-l3-agent' - LINUX_SERVICE = 'neutron-l3-agent' - - -class HeatAPIService(service.LinuxService): - SERVICE_NAME = 'heat-api' - GREP = 'heat-api' - LINUX_SERVICE = 'heat-api' - - -class HeatEngineService(PcsService): - SERVICE_NAME = 'heat-engine' - GREP = 'heat-engine' - PCS_SERVICE = 'p_heat-engine' - - -class CinderAPIService(service.LinuxService): - SERVICE_NAME = 'cinder-api' - GREP = 'cinder-api' - LINUX_SERVICE = 'cinder-api' - - -class CinderSchedulerService(service.LinuxService): - SERVICE_NAME = 'cinder-scheduler' - GREP = 'cinder-scheduler' - LINUX_SERVICE = 'cinder-scheduler' - - -class CinderVolumeService(service.LinuxService): - SERVICE_NAME = 'cinder-volume' - GREP = 'cinder-volume' - LINUX_SERVICE = 'cinder-volume' - - -class CinderBackupService(service.LinuxService): - SERVICE_NAME = 'cinder-backup' - GREP = 'cinder-backup' - LINUX_SERVICE = 'cinder-backup' - - -class IronicApiService(service.LinuxService): - SERVICE_NAME = 'ironic-api' - GREP = 'ironic-api' - LINUX_SERVICE = 'ironic-api' - - -class IronicConductorService(service.LinuxService): - SERVICE_NAME = 'ironic-conductor' - GREP = 'ironic-conductor' - LINUX_SERVICE = 'ironic-conductor' - - -class SwiftAccountService(service.LinuxService): - SERVICE_NAME = 'swift-account' - GREP = 'swift-account' - LINUX_SERVICE = 'swift-account' - - -class SwiftAccountAuditorService(service.LinuxService): - SERVICE_NAME = 'swift-account-auditor' - GREP = 'swift-account-auditor' - LINUX_SERVICE = 'swift-account-auditor' - - -class SwiftAccountReaperService(service.LinuxService): - SERVICE_NAME = 'swift-account-reaper' - GREP = 'swift-account-reaper' - LINUX_SERVICE = 'swift-account-reaper' - - -class SwiftAccountReplicatorService(service.LinuxService): - SERVICE_NAME = 'swift-account-replicator' - GREP = 'swift-account-replicator' - LINUX_SERVICE = 'swift-account-replicator' - - -class SwiftContainerService(service.LinuxService): - SERVICE_NAME = 'swift-container' - GREP = 'swift-container' - LINUX_SERVICE = 'swift-container' - - -class SwiftContainerAuditorService(service.LinuxService): - SERVICE_NAME = 'swift-container-auditor' - GREP = 'swift-container-auditor' - LINUX_SERVICE = 'swift-container-auditor' - - -class SwiftContainerReplicatorService(service.LinuxService): - SERVICE_NAME = 'swift-container-replicator' - GREP = 'swift-container-replicator' - LINUX_SERVICE = 'swift-container-replicator' - - -class SwiftContainerSyncService(service.LinuxService): - SERVICE_NAME = 'swift-container-sync' - GREP = 'swift-container-sync' - LINUX_SERVICE = 'swift-container-sync' - - -class SwiftContainerUpdaterService(service.LinuxService): - SERVICE_NAME = 'swift-container-updater' - GREP = 'swift-container-updater' - LINUX_SERVICE = 'swift-container-updater' - - -class SwiftObjectService(service.LinuxService): - SERVICE_NAME = 'swift-object' - GREP = 'swift-object' - LINUX_SERVICE = 'swift-object' - - -class SwiftObjectAuditorService(service.LinuxService): - SERVICE_NAME = 'swift-object-auditor' - GREP = 'swift-object-auditor' - LINUX_SERVICE = 'swift-object-auditor' - - -class SwiftObjectReplicatorService(service.LinuxService): - SERVICE_NAME = 'swift-object-replicator' - GREP = 'swift-object-replicator' - LINUX_SERVICE = 'swift-object-replicator' - - -class SwiftObjectUpdaterService(service.LinuxService): - SERVICE_NAME = 'swift-object-updater' - GREP = 'swift-object-updater' - LINUX_SERVICE = 'swift-object-updater' - - -class SwiftProxyService(service.LinuxService): - SERVICE_NAME = 'swift-proxy' - GREP = 'swift-proxy' - LINUX_SERVICE = 'swift-proxy' + linux_service=self.linux_service, + pcs_service=self.pcs_service) class FuelManagement(cloud_management.CloudManagement, @@ -370,51 +192,306 @@ class FuelManagement(cloud_management.CloudManagement, NAME = 'fuel' DESCRIPTION = 'Fuel 9.x cloud management driver' NODE_CLS = FuelNodeCollection - SERVICE_NAME_TO_CLASS = { - 'keystone': KeystoneService, - 'horizon': HorizonService, - 'memcached': MemcachedService, - 'mysql': MySQLService, - 'rabbitmq': RabbitMQService, - 'glance-api': GlanceAPIService, - 'glance-glare': GlanceGlareService, - 'glance-registry': GlanceRegistryService, - 'nova-api': NovaAPIService, - 'nova-compute': NovaComputeService, - 'nova-scheduler': NovaSchedulerService, - 'nova-cert': NovaCertService, - 'nova-conductor': NovaConductorService, - 'nova-consoleauth': NovaConsoleAuthService, - 'nova-novncproxy': NovaNoVNCProxyService, - 'neutron-server': NeutronServerService, - 'neutron-dhcp-agent': NeutronDhcpAgentService, - 'neutron-metadata-agent': NeutronMetadataAgentService, - 'neutron-openvswitch-agent': NeutronOpenvswitchAgentService, - 'neutron-l3-agent': NeutronL3AgentService, - 'heat-api': HeatAPIService, - 'heat-engine': HeatEngineService, - 'cinder-api': CinderAPIService, - 'cinder-scheduler': CinderSchedulerService, - 'cinder-volume': CinderVolumeService, - 'cinder-backup': CinderBackupService, - 'ironic-api': IronicApiService, - 'ironic-conductor': IronicConductorService, - 'swift-account': SwiftAccountService, - 'swift-account-auditor': SwiftAccountAuditorService, - 'swift-account-reaper': SwiftAccountReaperService, - 'swift-account-replicator': SwiftAccountReplicatorService, - 'swift-container': SwiftContainerService, - 'swift-container-auditor': SwiftContainerAuditorService, - 'swift-container-replicator': SwiftContainerReplicatorService, - 'swift-container-sync': SwiftContainerSyncService, - 'swift-container-updater': SwiftContainerUpdaterService, - 'swift-object': SwiftObjectService, - 'swift-object-auditor': SwiftObjectAuditorService, - 'swift-object-replicator': SwiftObjectReplicatorService, - 'swift-object-updater': SwiftObjectUpdaterService, - 'swift-proxy': SwiftProxyService, + SERVICES = { + 'keystone': { + 'driver': 'linux_service', + 'args': { + 'grep': 'keystone', + 'linux_service': 'apache2', + } + }, + 'horizon': { + 'driver': 'linux_service', + 'args': { + 'grep': 'apache2', + 'linux_service': 'apache2', + } + }, + 'memcached': { + 'driver': 'linux_service', + 'args': { + 'grep': 'memcached', + 'linux_service': 'memcached', + } + }, + 'mysql': { + 'driver': 'pcs_service', + 'args': { + 'grep': 'mysqld', + 'pcs_service': 'p_mysqld', + 'port': ['tcp', 3307], + } + }, + 'rabbitmq': { + 'driver': 'pcs_service', + 'args': { + 'grep': 'rabbit tcp_listeners', + 'pcs_service': 'p_rabbitmq-server', + } + }, + 'glance-api': { + 'driver': 'linux_service', + 'args': { + 'grep': 'glance-api', + 'linux_service': 'glance-api', + } + }, + 'glance-glare': { + 'driver': 'linux_service', + 'args': { + 'grep': 'glance-glare', + 'linux_service': 'glance-glare', + } + }, + 'glance-registry': { + 'driver': 'linux_service', + 'args': { + 'grep': 'glance-registry', + 'linux_service': 'glance-registry', + } + }, + 'nova-api': { + 'driver': 'linux_service', + 'args': { + 'grep': 'nova-api', + 'linux_service': 'nova-api', + } + }, + 'nova-compute': { + 'driver': 'linux_service', + 'args': { + 'grep': 'nova-compute', + 'linux_service': 'nova-compute', + } + }, + 'nova-scheduler': { + 'driver': 'linux_service', + 'args': { + 'grep': 'nova-scheduler', + 'linux_service': 'nova-scheduler', + } + }, + 'nova-cert': { + 'driver': 'linux_service', + 'args': { + 'grep': 'nova-cert', + 'linux_service': 'nova-cert', + } + }, + 'nova-conductor': { + 'driver': 'linux_service', + 'args': { + 'grep': 'nova-conductor', + 'linux_service': 'nova-conductor', + } + }, + 'nova-consoleauth': { + 'driver': 'linux_service', + 'args': { + 'grep': 'nova-consoleauth', + 'linux_service': 'nova-consoleauth', + } + }, + 'nova-novncproxy': { + 'driver': 'linux_service', + 'args': { + 'grep': 'nova-novncproxy', + 'linux_service': 'nova-novncproxy', + } + }, + 'neutron-server': { + 'driver': 'linux_service', + 'args': { + 'grep': 'neutron-server', + 'linux_service': 'neutron-server', + } + }, + 'neutron-dhcp-agent': { + 'driver': 'pcs_service', + 'args': { + 'grep': 'neutron-dhcp-agent', + 'pcs_service': 'neutron-dhcp-agent', + } + }, + 'neutron-metadata-agent': { + 'driver': 'pcs_or_linux_service', + 'args': { + 'grep': 'neutron-metadata-agent', + 'pcs_service': 'neutron-metadata-agent', + 'linux_service': 'neutron-metadata-agent', + } + }, + 'neutron-openvswitch-agent': { + 'driver': 'pcs_or_linux_service', + 'args': { + 'grep': 'neutron-openvswitch-agent', + 'pcs_service': 'neutron-openvswitch-agent', + 'linux_service': 'neutron-openvswitch-agent', + } + }, + 'neutron-l3-agent': { + 'driver': 'pcs_or_linux_service', + 'args': { + 'grep': 'neutron-l3-agent', + 'pcs_service': 'neutron-l3-agent', + 'linux_service': 'neutron-l3-agent', + } + }, + 'heat-api': { + 'driver': 'linux_service', + 'args': { + 'grep': 'heat-api', + 'linux_service': 'heat-api', + } + }, + 'heat-engine': { + 'driver': 'pcs_service', + 'args': { + 'grep': 'heat-engine', + 'pcs_service': 'p_heat-engine', + } + }, + 'cinder-api': { + 'driver': 'linux_service', + 'args': { + 'grep': 'cinder-api', + 'linux_service': 'cinder-api', + } + }, + 'cinder-scheduler': { + 'driver': 'linux_service', + 'args': { + 'grep': 'cinder-scheduler', + 'linux_service': 'cinder-scheduler', + } + }, + 'cinder-volume': { + 'driver': 'linux_service', + 'args': { + 'grep': 'cinder-volume', + 'linux_service': 'cinder-volume', + } + }, + 'cinder-backup': { + 'driver': 'linux_service', + 'args': { + 'grep': 'cinder-backup', + 'linux_service': 'cinder-backup', + } + }, + 'ironic-api': { + 'driver': 'linux_service', + 'args': { + 'grep': 'ironic-api', + 'linux_service': 'ironic-api', + } + }, + 'ironic-conductor': { + 'driver': 'linux_service', + 'args': { + 'grep': 'ironic-conductor', + 'linux_service': 'ironic-conductor', + } + }, + 'swift-account': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-account', + 'linux_service': 'swift-account', + } + }, + 'swift-account-auditor': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-account-auditor', + 'linux_service': 'swift-account-auditor', + } + }, + 'swift-account-reaper': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-account-reaper', + 'linux_service': 'swift-account-reaper', + } + }, + 'swift-account-replicator': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-account-replicator', + 'linux_service': 'swift-account-replicator', + } + }, + 'swift-container': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-container', + 'linux_service': 'swift-container', + } + }, + 'swift-container-auditor': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-container-auditor', + 'linux_service': 'swift-container-auditor', + } + }, + 'swift-container-replicator': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-container-replicator', + 'linux_service': 'swift-container-replicator', + } + }, + 'swift-container-sync': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-container-sync', + 'linux_service': 'swift-container-sync', + } + }, + 'swift-container-updater': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-container-updater', + 'linux_service': 'swift-container-updater', + } + }, + 'swift-object': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-object', + 'linux_service': 'swift-object', + } + }, + 'swift-object-auditor': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-object-auditor', + 'linux_service': 'swift-object-auditor', + } + }, + 'swift-object-replicator': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-object-replicator', + 'linux_service': 'swift-object-replicator', + } + }, + 'swift-object-updater': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-object-updater', + 'linux_service': 'swift-object-updater', + } + }, + 'swift-proxy': { + 'driver': 'linux_service', + 'args': { + 'grep': 'swift-proxy', + 'linux_service': 'swift-proxy', + } + }, } - SUPPORTED_SERVICES = list(SERVICE_NAME_TO_CLASS.keys()) SUPPORTED_NETWORKS = ['management', 'private', 'public', 'storage'] CONFIG_SCHEMA = { 'type': 'object', @@ -424,7 +501,6 @@ class FuelManagement(cloud_management.CloudManagement, 'username': {'type': 'string'}, 'private_key_file': {'type': 'string'}, 'slave_direct_ssh': {'type': 'boolean'}, - }, 'required': ['address', 'username'], 'additionalProperties': False, diff --git a/os_faults/drivers/tcpcloud.py b/os_faults/drivers/tcpcloud.py index cef85be..8dc60e6 100644 --- a/os_faults/drivers/tcpcloud.py +++ b/os_faults/drivers/tcpcloud.py @@ -21,7 +21,6 @@ from os_faults.api import node_collection from os_faults.api import node_discover from os_faults.common import service from os_faults import error -from os_faults import utils LOG = logging.getLogger(__name__) @@ -42,195 +41,50 @@ SALT_START = SALT_CALL + 'service.start {service}' class SaltService(service.ServiceAsProcess): + """Salt service + + Service that can be controled by `salt service.*` commands. + + **Example configuration:** + + .. code-block:: yaml + + services: + app: + driver: salt_service + args: + salt_service: app + grep: my_app + port: ['tcp', 4242] + + parameters: + + - **salt_service** - name of a service + - **grep** - regexp for grep to find process PID + - **port** - tuple with two values - potocol, port number (optional) + + """ + + NAME = 'salt_service' + DESCRIPTION = 'Service in salt' + CONFIG_SCHEMA = { + 'type': 'object', + 'properties': { + 'salt_service': {'type': 'string'}, + 'grep': {'type': 'string'}, + 'port': service.PORT_SCHEMA, + }, + 'required': ['grep', 'salt_service'], + 'additionalProperties': False, + } - @utils.require_variables('SALT_SERVICE') def __init__(self, *args, **kwargs): super(SaltService, self).__init__(*args, **kwargs) + self.salt_service = self.config['salt_service'] - self.RESTART_CMD = SALT_RESTART.format(service=self.SALT_SERVICE) - self.TERMINATE_CMD = SALT_TERMINATE.format(service=self.SALT_SERVICE) - self.START_CMD = SALT_START.format(service=self.SALT_SERVICE) - - -class KeystoneService(SaltService): - SERVICE_NAME = 'keystone' - GREP = 'keystone-all' - SALT_SERVICE = 'keystone' - - -class HorizonService(SaltService): - SERVICE_NAME = 'horizon' - GREP = 'apache2' - SALT_SERVICE = 'apache2' - - -class MemcachedService(SaltService): - SERVICE_NAME = 'memcached' - GREP = 'memcached' - SALT_SERVICE = 'memcached' - - -class MySQLService(SaltService): - SERVICE_NAME = 'mysql' - GREP = 'mysqld' - SALT_SERVICE = 'mysql' - PORT = ('tcp', 3307) - - -class RabbitMQService(SaltService): - SERVICE_NAME = 'rabbitmq' - GREP = 'beam\.smp .*rabbitmq_server' - SALT_SERVICE = 'rabbitmq-server' - - -class GlanceAPIService(SaltService): - SERVICE_NAME = 'glance-api' - GREP = 'glance-api' - SALT_SERVICE = 'glance-api' - - -class GlanceRegistryService(SaltService): - SERVICE_NAME = 'glance-registry' - GREP = 'glance-registry' - SALT_SERVICE = 'glance-registry' - - -class NovaAPIService(SaltService): - SERVICE_NAME = 'nova-api' - GREP = 'nova-api' - SALT_SERVICE = 'nova-api' - - -class NovaComputeService(SaltService): - SERVICE_NAME = 'nova-compute' - GREP = 'nova-compute' - SALT_SERVICE = 'nova-compute' - - -class NovaSchedulerService(SaltService): - SERVICE_NAME = 'nova-scheduler' - GREP = 'nova-scheduler' - SALT_SERVICE = 'nova-scheduler' - - -class NovaCertService(SaltService): - SERVICE_NAME = 'nova-cert' - GREP = 'nova-cert' - SALT_SERVICE = 'nova-cert' - - -class NovaConductorService(SaltService): - SERVICE_NAME = 'nova-conductor' - GREP = 'nova-conductor' - SALT_SERVICE = 'nova-conductor' - - -class NovaConsoleAuthService(SaltService): - SERVICE_NAME = 'nova-consoleauth' - GREP = 'nova-consoleauth' - SALT_SERVICE = 'nova-consoleauth' - - -class NovaNoVNCProxyService(SaltService): - SERVICE_NAME = 'nova-novncproxy' - GREP = 'nova-novncproxy' - SALT_SERVICE = 'nova-novncproxy' - - -class NeutronServerService(SaltService): - SERVICE_NAME = 'neutron-server' - GREP = 'neutron-server' - SALT_SERVICE = 'neutron-server' - - -class NeutronDhcpAgentService(SaltService): - SERVICE_NAME = 'neutron-dhcp-agent' - GREP = 'neutron-dhcp-agent' - SALT_SERVICE = 'neutron-dhcp-agent' - - -class NeutronMetadataAgentService(SaltService): - SERVICE_NAME = 'neutron-metadata-agent' - GREP = 'neutron-metadata-agent' - SALT_SERVICE = 'neutron-metadata-agent' - - -class NeutronOpenvswitchAgentService(SaltService): - SERVICE_NAME = 'neutron-openvswitch-agent' - GREP = 'neutron-openvswitch-agent' - SALT_SERVICE = 'neutron-openvswitch-agent' - - -class NeutronL3AgentService(SaltService): - SERVICE_NAME = 'neutron-l3-agent' - GREP = 'neutron-l3-agent' - SALT_SERVICE = 'neutron-l3-agent' - - -class HeatAPIService(SaltService): - SERVICE_NAME = 'heat-api' - GREP = 'heat-api ' # space at the end filters heat-api-* services - SALT_SERVICE = 'heat-api' - - -class HeatEngineService(SaltService): - SERVICE_NAME = 'heat-engine' - GREP = 'heat-engine' - SALT_SERVICE = 'heat-engine' - - -class CinderAPIService(SaltService): - SERVICE_NAME = 'cinder-api' - GREP = 'cinder-api' - SALT_SERVICE = 'cinder-api' - - -class CinderSchedulerService(SaltService): - SERVICE_NAME = 'cinder-scheduler' - GREP = 'cinder-scheduler' - SALT_SERVICE = 'cinder-scheduler' - - -class CinderVolumeService(SaltService): - SERVICE_NAME = 'cinder-volume' - GREP = 'cinder-volume' - SALT_SERVICE = 'cinder-volume' - - -class CinderBackupService(SaltService): - SERVICE_NAME = 'cinder-backup' - GREP = 'cinder-backup' - SALT_SERVICE = 'cinder-backup' - - -class ElasticSearchService(SaltService): - SERVICE_NAME = 'elasticsearch' - GREP = 'java .*elasticsearch' - SALT_SERVICE = 'elasticsearch' - - -class GrafanaServerService(SaltService): - SERVICE_NAME = 'grafana-server' - GREP = 'grafana-server' - SALT_SERVICE = 'grafana-server' - - -class InfluxDBService(SaltService): - SERVICE_NAME = 'influxdb' - GREP = 'influxd' - SALT_SERVICE = 'influxdb' - - -class KibanaService(SaltService): - SERVICE_NAME = 'kibana' - GREP = 'kibana' - SALT_SERVICE = 'kibana' - - -class Nagios3Service(SaltService): - SERVICE_NAME = 'nagios3' - GREP = 'nagios3' - SALT_SERVICE = 'nagios3' + self.restart_cmd = SALT_RESTART.format(service=self.salt_service) + self.terminate_cmd = SALT_TERMINATE.format(service=self.salt_service) + self.start_cmd = SALT_START.format(service=self.salt_service) class TCPCloudManagement(cloud_management.CloudManagement, @@ -279,39 +133,220 @@ class TCPCloudManagement(cloud_management.CloudManagement, NAME = 'tcpcloud' DESCRIPTION = 'TCPCloud management driver' NODE_CLS = TCPCloudNodeCollection - SERVICE_NAME_TO_CLASS = { - 'keystone': KeystoneService, - 'horizon': HorizonService, - 'memcached': MemcachedService, - 'mysql': MySQLService, - 'rabbitmq': RabbitMQService, - 'glance-api': GlanceAPIService, - 'glance-registry': GlanceRegistryService, - 'nova-api': NovaAPIService, - 'nova-compute': NovaComputeService, - 'nova-scheduler': NovaSchedulerService, - 'nova-cert': NovaCertService, - 'nova-conductor': NovaConductorService, - 'nova-consoleauth': NovaConsoleAuthService, - 'nova-novncproxy': NovaNoVNCProxyService, - 'neutron-server': NeutronServerService, - 'neutron-dhcp-agent': NeutronDhcpAgentService, - 'neutron-metadata-agent': NeutronMetadataAgentService, - 'neutron-openvswitch-agent': NeutronOpenvswitchAgentService, - 'neutron-l3-agent': NeutronL3AgentService, - 'heat-api': HeatAPIService, - 'heat-engine': HeatEngineService, - 'cinder-api': CinderAPIService, - 'cinder-scheduler': CinderSchedulerService, - 'cinder-volume': CinderVolumeService, - 'cinder-backup': CinderBackupService, - 'elasticsearch': ElasticSearchService, - 'grafana-server': GrafanaServerService, - 'influxdb': InfluxDBService, - 'kibana': KibanaService, - 'nagios3': Nagios3Service, + SERVICES = { + 'keystone': { + 'driver': 'salt_service', + 'args': { + 'grep': 'keystone-all', + 'salt_service': 'keystone', + } + }, + 'horizon': { + 'driver': 'salt_service', + 'args': { + 'grep': 'apache2', + 'salt_service': 'apache2', + } + }, + 'memcached': { + 'driver': 'salt_service', + 'args': { + 'grep': 'memcached', + 'salt_service': 'memcached', + } + }, + 'mysql': { + 'driver': 'salt_service', + 'args': { + 'grep': 'mysqld', + 'salt_service': 'mysql', + 'port': ['tcp', 3307], + } + }, + 'rabbitmq': { + 'driver': 'salt_service', + 'args': { + 'grep': 'beam\.smp .*rabbitmq_server', + 'salt_service': 'rabbitmq-server', + } + }, + 'glance-api': { + 'driver': 'salt_service', + 'args': { + 'grep': 'glance-api', + 'salt_service': 'glance-api', + } + }, + 'glance-registry': { + 'driver': 'salt_service', + 'args': { + 'grep': 'glance-registry', + 'salt_service': 'glance-registry', + } + }, + 'nova-api': { + 'driver': 'salt_service', + 'args': { + 'grep': 'nova-api', + 'salt_service': 'nova-api', + } + }, + 'nova-compute': { + 'driver': 'salt_service', + 'args': { + 'grep': 'nova-compute', + 'salt_service': 'nova-compute', + } + }, + 'nova-scheduler': { + 'driver': 'salt_service', + 'args': { + 'grep': 'nova-scheduler', + 'salt_service': 'nova-scheduler', + } + }, + 'nova-cert': { + 'driver': 'salt_service', + 'args': { + 'grep': 'nova-cert', + 'salt_service': 'nova-cert', + } + }, + 'nova-conductor': { + 'driver': 'salt_service', + 'args': { + 'grep': 'nova-conductor', + 'salt_service': 'nova-conductor', + } + }, + 'nova-consoleauth': { + 'driver': 'salt_service', + 'args': { + 'grep': 'nova-consoleauth', + 'salt_service': 'nova-consoleauth', + } + }, + 'nova-novncproxy': { + 'driver': 'salt_service', + 'args': { + 'grep': 'nova-novncproxy', + 'salt_service': 'nova-novncproxy', + } + }, + 'neutron-server': { + 'driver': 'salt_service', + 'args': { + 'grep': 'neutron-server', + 'salt_service': 'neutron-server', + } + }, + 'neutron-dhcp-agent': { + 'driver': 'salt_service', + 'args': { + 'grep': 'neutron-dhcp-agent', + 'salt_service': 'neutron-dhcp-agent', + } + }, + 'neutron-metadata-agent': { + 'driver': 'salt_service', + 'args': { + 'grep': 'neutron-metadata-agent', + 'salt_service': 'neutron-metadata-agent', + } + }, + 'neutron-openvswitch-agent': { + 'driver': 'salt_service', + 'args': { + 'grep': 'neutron-openvswitch-agent', + 'salt_service': 'neutron-openvswitch-agent', + } + }, + 'neutron-l3-agent': { + 'driver': 'salt_service', + 'args': { + 'grep': 'neutron-l3-agent', + 'salt_service': 'neutron-l3-agent', + } + }, + 'heat-api': { + 'driver': 'salt_service', + 'args': { + # space at the end filters heat-api-* services + 'grep': 'heat-api ', + 'salt_service': 'heat-api', + } + }, + 'heat-engine': { + 'driver': 'salt_service', + 'args': { + 'grep': 'heat-engine', + 'salt_service': 'heat-engine', + } + }, + 'cinder-api': { + 'driver': 'salt_service', + 'args': { + 'grep': 'cinder-api', + 'salt_service': 'cinder-api', + } + }, + 'cinder-scheduler': { + 'driver': 'salt_service', + 'args': { + 'grep': 'cinder-scheduler', + 'salt_service': 'cinder-scheduler', + } + }, + 'cinder-volume': { + 'driver': 'salt_service', + 'args': { + 'grep': 'cinder-volume', + 'salt_service': 'cinder-volume', + } + }, + 'cinder-backup': { + 'driver': 'salt_service', + 'args': { + 'grep': 'cinder-backup', + 'salt_service': 'cinder-backup', + } + }, + 'elasticsearch': { + 'driver': 'salt_service', + 'args': { + 'grep': 'java .*elasticsearch', + 'salt_service': 'elasticsearch', + } + }, + 'grafana-server': { + 'driver': 'salt_service', + 'args': { + 'grep': 'grafana-server', + 'salt_service': 'grafana-server', + } + }, + 'influxdb': { + 'driver': 'salt_service', + 'args': { + 'grep': 'influxd', + 'salt_service': 'influxdb', + } + }, + 'kibana': { + 'driver': 'salt_service', + 'args': { + 'grep': 'kibana', + 'salt_service': 'kibana', + } + }, + 'nagios3': { + 'driver': 'salt_service', + 'args': { + 'grep': 'nagios3', + 'salt_service': 'nagios3', + } + }, } - SUPPORTED_SERVICES = list(SERVICE_NAME_TO_CLASS.keys()) SUPPORTED_NETWORKS = [] CONFIG_SCHEMA = { 'type': 'object', diff --git a/os_faults/registry.py b/os_faults/registry.py index 0631cb1..38766c0 100644 --- a/os_faults/registry.py +++ b/os_faults/registry.py @@ -17,21 +17,22 @@ import sys from oslo_utils import importutils +import os_faults from os_faults.api import base_driver from os_faults.api import error -from os_faults import drivers DRIVERS = {} def _import_modules_from_package(): - drivers_folder = os.path.dirname(drivers.__file__) - library_root = os.path.normpath(os.path.join( - os.path.join(drivers_folder, os.pardir), os.pardir)) + folder = os.path.dirname(os_faults.__file__) + library_root = os.path.normpath(os.path.join(folder, os.pardir)) - for root, dirs, files in os.walk(drivers_folder): + for root, dirs, files in os.walk(folder): for filename in files: - if filename.startswith('__') or not filename.endswith('.py'): + if (filename.startswith('__') or + filename.startswith('test') or + not filename.endswith('.py')): continue relative_path = os.path.relpath(os.path.join(root, filename), @@ -56,16 +57,33 @@ def _list_drivers(): for class_info in class_info_list: klazz = class_info[1] + if not issubclass(klazz, base_driver.BaseDriver): + continue + if 'NAME' not in vars(klazz): # driver must have a name + continue + if klazz.NAME == 'base': # skip base class + continue - if issubclass(klazz, base_driver.BaseDriver): - yield klazz + yield klazz def get_drivers(): global DRIVERS if not DRIVERS: - DRIVERS = dict((k.get_driver_name(), k) for k in _list_drivers()) + DRIVERS = {} + for k in _list_drivers(): + driver_name = k.get_driver_name() + if driver_name in DRIVERS: + orig_k = DRIVERS[driver_name] + orig_path = orig_k.__module__ + '.' + orig_k.__name__ + dup_path = k.__module__ + '.' + k.__name__ + + raise error.OSFDriverWithSuchNameExists( + 'Driver "%s" already defined in %s. ' + 'Found a duplicate in %s ' % ( + driver_name, orig_path, dup_path)) + DRIVERS[driver_name] = k return DRIVERS diff --git a/os_faults/tests/unit/drivers/test_devstack.py b/os_faults/tests/unit/drivers/test_devstack.py index 1b996f9..faa0024 100644 --- a/os_faults/tests/unit/drivers/test_devstack.py +++ b/os_faults/tests/unit/drivers/test_devstack.py @@ -163,10 +163,8 @@ class DevStackManagementTestCase(test.TestCase): nodes.hosts) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*devstack.DevStackManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_get_service_nodes(self, service_name, service_cls, - mock_ansible_runner): + @ddt.data(*devstack.DevStackManagement.SERVICES.keys()) + def test_get_service_nodes(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [fakes.FakeAnsibleResult(payload={'stdout': '09:7b:74:90:63:c1'}, @@ -177,12 +175,10 @@ class DevStackManagementTestCase(test.TestCase): devstack_management = devstack.DevStackManagement(self.conf) service = devstack_management.get_service(service_name) - self.assertIsInstance(service, service_cls) - nodes = service.get_nodes() cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call( ['10.0.0.2'], {'command': 'cat /sys/class/net/eth0/address'}), @@ -202,9 +198,8 @@ class DevStackServiceTestCase(test.TestCase): self.conf = {'address': '10.0.0.2', 'username': 'root'} @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*devstack.DevStackManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_restart(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*devstack.DevStackManagement.SERVICES.keys()) + def test_restart(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [fakes.FakeAnsibleResult(payload={'stdout': '09:7b:74:90:63:c1'}, @@ -216,23 +211,20 @@ class DevStackServiceTestCase(test.TestCase): devstack_management = devstack.DevStackManagement(self.conf) service = devstack_management.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.restart() cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call( ['10.0.0.2'], {'command': 'cat /sys/class/net/eth0/address'}), mock.call(['10.0.0.2'], {'command': cmd}, []), - mock.call(['10.0.0.2'], {'shell': service.RESTART_CMD}) + mock.call(['10.0.0.2'], {'shell': service.restart_cmd}) ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*devstack.DevStackManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_terminate(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*devstack.DevStackManagement.SERVICES.keys()) + def test_terminate(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [fakes.FakeAnsibleResult(payload={'stdout': '09:7b:74:90:63:c1'}, @@ -244,23 +236,20 @@ class DevStackServiceTestCase(test.TestCase): devstack_management = devstack.DevStackManagement(self.conf) service = devstack_management.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.terminate() cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call( ['10.0.0.2'], {'command': 'cat /sys/class/net/eth0/address'}), mock.call(['10.0.0.2'], {'command': cmd}, []), - mock.call(['10.0.0.2'], {'shell': service.TERMINATE_CMD}) + mock.call(['10.0.0.2'], {'shell': service.terminate_cmd}) ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*devstack.DevStackManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_start(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*devstack.DevStackManagement.SERVICES.keys()) + def test_start(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [fakes.FakeAnsibleResult(payload={'stdout': '09:7b:74:90:63:c1'}, @@ -272,15 +261,13 @@ class DevStackServiceTestCase(test.TestCase): devstack_management = devstack.DevStackManagement(self.conf) service = devstack_management.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.start() cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call( ['10.0.0.2'], {'command': 'cat /sys/class/net/eth0/address'}), mock.call(['10.0.0.2'], {'command': cmd}, []), - mock.call(['10.0.0.2'], {'shell': service.START_CMD}) + mock.call(['10.0.0.2'], {'shell': service.start_cmd}) ]) diff --git a/os_faults/tests/unit/drivers/test_fuel_management.py b/os_faults/tests/unit/drivers/test_fuel_management.py index 3131630..10c1c54 100644 --- a/os_faults/tests/unit/drivers/test_fuel_management.py +++ b/os_faults/tests/unit/drivers/test_fuel_management.py @@ -157,10 +157,8 @@ class FuelManagementTestCase(test.TestCase): self.assertEqual(nodes.hosts, hosts) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*fuel.FuelManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_get_service_nodes(self, service_name, service_cls, - mock_ansible_runner): + @ddt.data(*fuel.FuelManagement.SERVICES.keys()) + def test_get_service_nodes(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -177,11 +175,10 @@ class FuelManagementTestCase(test.TestCase): }) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) nodes = service.get_nodes() cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], diff --git a/os_faults/tests/unit/drivers/test_fuel_service.py b/os_faults/tests/unit/drivers/test_fuel_service.py index 7844108..12af2ad 100644 --- a/os_faults/tests/unit/drivers/test_fuel_service.py +++ b/os_faults/tests/unit/drivers/test_fuel_service.py @@ -34,9 +34,8 @@ class FuelServiceTestCase(test.TestCase): }) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*fuel.FuelManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_kill(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*fuel.FuelManagement.SERVICES.keys()) + def test_kill(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -53,24 +52,21 @@ class FuelServiceTestCase(test.TestCase): fuel_managment = fuel.FuelManagement(self.conf) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.kill() get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'kill': {'grep': service_cls.GREP, 'sig': 9}}), + {'kill': {'grep': service.grep, 'sig': 9}}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*fuel.FuelManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_freeze(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*fuel.FuelManagement.SERVICES.keys()) + def test_freeze(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -87,24 +83,21 @@ class FuelServiceTestCase(test.TestCase): fuel_managment = fuel.FuelManagement(self.conf) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.freeze() get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'kill': {'grep': service_cls.GREP, 'sig': 19}}), + {'kill': {'grep': service.grep, 'sig': 19}}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*fuel.FuelManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_freeze_sec(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*fuel.FuelManagement.SERVICES.keys()) + def test_freeze_sec(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -121,26 +114,23 @@ class FuelServiceTestCase(test.TestCase): fuel_managment = fuel.FuelManagement(self.conf) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - delay_sec = 10 service.freeze(nodes=None, sec=delay_sec) get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'freeze': {'grep': service_cls.GREP, + {'freeze': {'grep': service.grep, 'sec': delay_sec}}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*fuel.FuelManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_unfreeze(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*fuel.FuelManagement.SERVICES.keys()) + def test_unfreeze(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -157,24 +147,21 @@ class FuelServiceTestCase(test.TestCase): fuel_managment = fuel.FuelManagement(self.conf) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.unfreeze() get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'kill': {'grep': service_cls.GREP, 'sig': 18}}), + {'kill': {'grep': service.grep, 'sig': 18}}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(('mysql', fuel.MySQLService)) - @ddt.unpack - def test_unplug(self, service_name, service_cls, mock_ansible_runner): + @ddt.data('mysql') + def test_unplug(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -191,27 +178,24 @@ class FuelServiceTestCase(test.TestCase): fuel_managment = fuel.FuelManagement(self.conf) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.unplug() get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'iptables': {'protocol': service_cls.PORT[0], - 'port': service_cls.PORT[1], + {'iptables': {'protocol': service.port[0], + 'port': service.port[1], 'action': 'block', - 'service': service_cls.SERVICE_NAME}}), + 'service': service.service_name}}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(('mysql', fuel.MySQLService)) - @ddt.unpack - def test_plug(self, service_name, service_cls, mock_ansible_runner): + @ddt.data('mysql') + def test_plug(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -228,27 +212,24 @@ class FuelServiceTestCase(test.TestCase): fuel_managment = fuel.FuelManagement(self.conf) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.plug() get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'iptables': {'protocol': service_cls.PORT[0], - 'port': service_cls.PORT[1], + {'iptables': {'protocol': service.port[0], + 'port': service.port[1], 'action': 'unblock', - 'service': service_cls.SERVICE_NAME}}), + 'service': service.service_name}}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*fuel.FuelManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_restart(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*fuel.FuelManagement.SERVICES.keys()) + def test_restart(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -265,24 +246,21 @@ class FuelServiceTestCase(test.TestCase): fuel_managment = fuel.FuelManagement(self.conf) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.restart() get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'shell': service.RESTART_CMD}), + {'shell': service.restart_cmd}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*fuel.FuelManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_terminate(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*fuel.FuelManagement.SERVICES.keys()) + def test_terminate(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -299,24 +277,21 @@ class FuelServiceTestCase(test.TestCase): fuel_managment = fuel.FuelManagement(self.conf) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.terminate() get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'shell': service.TERMINATE_CMD}), + {'shell': service.terminate_cmd}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*fuel.FuelManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_start(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*fuel.FuelManagement.SERVICES.keys()) + def test_start(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -333,18 +308,16 @@ class FuelServiceTestCase(test.TestCase): fuel_managment = fuel.FuelManagement(self.conf) service = fuel_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.start() get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'shell': service.START_CMD}), + {'shell': service.start_cmd}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) @@ -370,13 +343,13 @@ class FuelServiceTestCase(test.TestCase): self.assertEqual('Task failed on some nodes', str(exception)) get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': get_nodes_cmd}, []), mock.call(['10.0.0.2', '10.0.0.3'], - {'shell': service.RESTART_CMD}), + {'shell': service.restart_cmd}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) @@ -399,7 +372,7 @@ class FuelServiceTestCase(test.TestCase): self.assertEqual('Node collection is empty', str(exception)) get_nodes_cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['fuel.local'], {'command': 'fuel node --json'}), mock.call(['10.0.0.2', '10.0.0.3'], diff --git a/os_faults/tests/unit/drivers/test_tcpcloud.py b/os_faults/tests/unit/drivers/test_tcpcloud.py index 332b438..bdde6e8 100644 --- a/os_faults/tests/unit/drivers/test_tcpcloud.py +++ b/os_faults/tests/unit/drivers/test_tcpcloud.py @@ -230,10 +230,8 @@ class TCPCloudManagementTestCase(test.TestCase): self.assertEqual(nodes.hosts, hosts) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*tcpcloud.TCPCloudManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_get_service_nodes(self, service_name, service_cls, - mock_ansible_runner): + @ddt.data(*tcpcloud.TCPCloudManagement.SERVICES.keys()) + def test_get_service_nodes(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -248,11 +246,9 @@ class TCPCloudManagementTestCase(test.TestCase): tcp_managment = tcpcloud.TCPCloudManagement(self.tcp_conf) service = tcp_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - nodes = service.get_nodes() cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['tcp.local'], {'command': self.get_nodes_cmd}), mock.call(['tcp.local'], {'command': self.get_ips_cmd}), @@ -303,9 +299,8 @@ class TcpServiceTestCase(test.TestCase): "pillar.get _param:single_address --out=yaml") @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*tcpcloud.TCPCloudManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_restart(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*tcpcloud.TCPCloudManagement.SERVICES.keys()) + def test_restart(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -324,24 +319,21 @@ class TcpServiceTestCase(test.TestCase): tcp_managment = tcpcloud.TCPCloudManagement(self.tcp_conf) service = tcp_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.restart() cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['tcp.local'], {'command': self.get_nodes_cmd}), mock.call(['tcp.local'], {'command': self.get_ips_cmd}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': cmd}, []), - mock.call(['10.0.0.3'], {'shell': service.RESTART_CMD}), + mock.call(['10.0.0.3'], {'shell': service.restart_cmd}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*tcpcloud.TCPCloudManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_terminate(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*tcpcloud.TCPCloudManagement.SERVICES.keys()) + def test_terminate(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -360,24 +352,21 @@ class TcpServiceTestCase(test.TestCase): tcp_managment = tcpcloud.TCPCloudManagement(self.tcp_conf) service = tcp_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.terminate() cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['tcp.local'], {'command': self.get_nodes_cmd}), mock.call(['tcp.local'], {'command': self.get_ips_cmd}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': cmd}, []), - mock.call(['10.0.0.3'], {'shell': service.TERMINATE_CMD}), + mock.call(['10.0.0.3'], {'shell': service.terminate_cmd}), ]) @mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True) - @ddt.data(*tcpcloud.TCPCloudManagement.SERVICE_NAME_TO_CLASS.items()) - @ddt.unpack - def test_start(self, service_name, service_cls, mock_ansible_runner): + @ddt.data(*tcpcloud.TCPCloudManagement.SERVICES.keys()) + def test_start(self, service_name, mock_ansible_runner): ansible_runner_inst = mock_ansible_runner.return_value ansible_runner_inst.execute.side_effect = [ [self.fake_ansible_result], @@ -396,16 +385,14 @@ class TcpServiceTestCase(test.TestCase): tcp_managment = tcpcloud.TCPCloudManagement(self.tcp_conf) service = tcp_managment.get_service(service_name) - self.assertIsInstance(service, service_cls) - service.start() cmd = 'bash -c "ps ax | grep -v grep | grep \'{}\'"'.format( - service_cls.GREP) + service.grep) ansible_runner_inst.execute.assert_has_calls([ mock.call(['tcp.local'], {'command': self.get_nodes_cmd}), mock.call(['tcp.local'], {'command': self.get_ips_cmd}), mock.call(['10.0.0.2', '10.0.0.3'], {'command': cmd}, []), - mock.call(['10.0.0.3'], {'shell': service.START_CMD}), + mock.call(['10.0.0.3'], {'shell': service.start_cmd}), ]) diff --git a/os_faults/tests/unit/test_registry.py b/os_faults/tests/unit/test_registry.py index ceb915a..eaa5301 100644 --- a/os_faults/tests/unit/test_registry.py +++ b/os_faults/tests/unit/test_registry.py @@ -16,6 +16,7 @@ import sys import mock from os_faults.api import base_driver +from os_faults.api import error from os_faults import drivers from os_faults import registry from os_faults.tests.unit import test @@ -39,7 +40,19 @@ class RegistryTestCase(test.TestCase): @mock.patch('os.walk') def test_get_drivers(self, mock_os_walk, mock_import_module): drivers_folder = os.path.dirname(drivers.__file__) - mock_os_walk.return_value = [(drivers_folder, [], ['test_driver.py'])] + mock_os_walk.return_value = [(drivers_folder, [], ['driver.py'])] mock_import_module.return_value = sys.modules[__name__] self.assertEqual({'test': TestDriver}, registry.get_drivers()) + + @mock.patch('os_faults.registry._list_drivers') + def test_name_collision(self, mock_list_drivers): + class TestDriver1(base_driver.BaseDriver): + NAME = 'test' + + class TestDriver2(base_driver.BaseDriver): + NAME = 'test' + + mock_list_drivers.return_value = [TestDriver1, TestDriver2] + self.assertRaises(error.OSFDriverWithSuchNameExists, + registry.get_drivers) diff --git a/os_faults/tests/unit/test_utils.py b/os_faults/tests/unit/test_utils.py index eb8dcda..e04c113 100644 --- a/os_faults/tests/unit/test_utils.py +++ b/os_faults/tests/unit/test_utils.py @@ -61,6 +61,9 @@ class UtilsTestCase(test.TestCase): class MyClass(object): FOO = 10 + def __init__(self): + self.BAR = None + @utils.require_variables('FOO') def method(self, a, b): return self.FOO + a + b diff --git a/os_faults/utils.py b/os_faults/utils.py index edfe77a..3f0c238 100644 --- a/os_faults/utils.py +++ b/os_faults/utils.py @@ -51,7 +51,7 @@ def require_variables(*variables): def wrapper(self, *args, **kawrgs): missing_vars = [] for var in variables: - if not hasattr(self, var): + if not getattr(self, var, None): missing_vars.append(var) if missing_vars: missing_vars = ', '.join(missing_vars)