Switch to plugin base deployengines and serverproviders
This patch switch to plugin base and unify deployengines and serverproviders plugins. Co-authored-by: Olga Kopylova <olkonami@gmail.com> Co-authored-by: Boris Pavlovic <boris@pavlovic.me> Change-Id: I06fdef2a176266ab37325a421c10d386045cc2b9
This commit is contained in:
parent
21513a78ae
commit
6339d8c886
@ -262,10 +262,11 @@ class InfoCommands(object):
|
||||
scenario_groups = list(set(s.split(".")[0] for s in scenarios))
|
||||
scenario_methods = list(set(s.split(".")[1] for s in scenarios))
|
||||
sla_info = [cls.get_name() for cls in sla.SLA.get_all()]
|
||||
deploy_engines = [cls.get_name() for cls in utils.itersubclasses(
|
||||
deploy.EngineFactory)]
|
||||
server_providers = [cls.get_name() for cls in utils.itersubclasses(
|
||||
serverprovider.ProviderFactory)]
|
||||
deploy_engines = [cls.get_name() for cls in
|
||||
deploy.EngineFactory.get_all()]
|
||||
server_providers = [cls.get_name() for cls in
|
||||
serverprovider.ProviderFactory.get_all()]
|
||||
|
||||
candidates = (scenarios + scenario_groups + scenario_methods +
|
||||
sla_info + deploy_engines + server_providers)
|
||||
suggestions = []
|
||||
@ -339,24 +340,24 @@ class InfoCommands(object):
|
||||
|
||||
def _get_deploy_engine_info(self, query):
|
||||
try:
|
||||
deploy_engine = deploy.EngineFactory.get_by_name(query)
|
||||
deploy_engine = deploy.EngineFactory.get(query)
|
||||
header = "%s (deploy engine)" % deploy_engine.get_name()
|
||||
info = self._make_header(header)
|
||||
info += "\n\n"
|
||||
info += utils.format_docstring(deploy_engine.__doc__)
|
||||
return info
|
||||
except exceptions.NoSuchEngine:
|
||||
except exceptions.PluginNotFound:
|
||||
return None
|
||||
|
||||
def _get_server_provider_info(self, query):
|
||||
try:
|
||||
server_provider = serverprovider.ProviderFactory.get_by_name(query)
|
||||
server_provider = serverprovider.ProviderFactory.get(query)
|
||||
header = "%s (server provider)" % server_provider.get_name()
|
||||
info = self._make_header(header)
|
||||
info += "\n\n"
|
||||
info += utils.format_docstring(server_provider.__doc__)
|
||||
return info
|
||||
except exceptions.NoSuchVMProvider:
|
||||
except exceptions.PluginNotFound:
|
||||
return None
|
||||
|
||||
def _make_header(self, string):
|
||||
|
@ -20,6 +20,7 @@ import six
|
||||
|
||||
from rally.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common.plugin import plugin
|
||||
from rally.common import utils
|
||||
from rally import consts
|
||||
from rally.deploy.serverprovider import provider
|
||||
@ -29,8 +30,17 @@ from rally import exceptions
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def configure(name, namespace="default"):
|
||||
return plugin.configure(name, namespace=namespace)
|
||||
|
||||
|
||||
# FIXME(boris-42): We should make decomposition of this class.
|
||||
# it should be called DeploymentManager
|
||||
# and it should just manages server providers and engines
|
||||
# engines class should have own base.
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class EngineFactory(object):
|
||||
@configure("base_engine")
|
||||
class EngineFactory(plugin.Plugin):
|
||||
"""Base class of all deployment engines.
|
||||
|
||||
It's a base class with self-discovery of subclasses. Each subclass
|
||||
@ -73,41 +83,25 @@ class EngineFactory(object):
|
||||
if hasattr(self, "CONFIG_SCHEMA"):
|
||||
jsonschema.validate(self.config, self.CONFIG_SCHEMA)
|
||||
|
||||
# FIXME(boris-42): Get rid of this method
|
||||
def get_provider(self):
|
||||
if "provider" in self.config:
|
||||
return provider.ProviderFactory.get_provider(
|
||||
self.config["provider"], self.deployment)
|
||||
|
||||
@staticmethod
|
||||
def get_by_name(name):
|
||||
"""Return Engine class by name."""
|
||||
for engine in utils.itersubclasses(EngineFactory):
|
||||
if name == engine.__name__:
|
||||
return engine
|
||||
raise exceptions.NoSuchEngine(engine_name=name)
|
||||
|
||||
# TODO(boris-42): Remove after switching to plugin base.
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return cls.__name__
|
||||
|
||||
# FIXME(boris-42): Get rid of this method
|
||||
@staticmethod
|
||||
def get_engine(name, deployment):
|
||||
"""Returns instance of a deploy engine with corresponding name."""
|
||||
try:
|
||||
engine_cls = EngineFactory.get_by_name(name)
|
||||
engine_cls = EngineFactory.get(name)
|
||||
return engine_cls(deployment)
|
||||
except exceptions.NoSuchEngine:
|
||||
except exceptions.PluginNotFound:
|
||||
LOG.error(_("Deployment %(uuid)s: Deploy engine for %(name)s "
|
||||
"does not exist.") %
|
||||
{"uuid": deployment["uuid"], "name": name})
|
||||
deployment.update_status(consts.DeployStatus.DEPLOY_FAILED)
|
||||
raise exceptions.NoSuchEngine(engine_name=name)
|
||||
|
||||
@staticmethod
|
||||
def get_available_engines():
|
||||
"""Returns a list of names of available engines."""
|
||||
return [e.__name__ for e in utils.itersubclasses(EngineFactory)]
|
||||
raise exceptions.PluginNotFound(engine_name=name)
|
||||
|
||||
@abc.abstractmethod
|
||||
def deploy(self):
|
||||
|
@ -43,6 +43,7 @@ def get_updated_server(server, **kwargs):
|
||||
return provider.Server.from_credentials(credentials)
|
||||
|
||||
|
||||
@engine.configure(name="DevstackEngine")
|
||||
class DevstackEngine(engine.EngineFactory):
|
||||
"""Deploy Devstack cloud.
|
||||
|
||||
|
@ -18,6 +18,7 @@ from rally.deploy import engine
|
||||
from rally import objects
|
||||
|
||||
|
||||
@engine.configure(name="ExistingCloud")
|
||||
class ExistingCloud(engine.EngineFactory):
|
||||
"""Just use an existing OpenStack deployment without deploying anything.
|
||||
|
||||
|
@ -76,6 +76,7 @@ NETWORKS_SCHEMA = {
|
||||
}
|
||||
|
||||
|
||||
@engine.configure(name="FuelEngine")
|
||||
class FuelEngine(engine.EngineFactory):
|
||||
"""Deploy with FuelWeb.
|
||||
|
||||
|
@ -36,6 +36,7 @@ def get_script_path(name):
|
||||
"lxc", name)
|
||||
|
||||
|
||||
@engine.configure(name="LxcEngine")
|
||||
class LxcEngine(engine.EngineFactory):
|
||||
"""Deploy with other engines in lxc containers.
|
||||
|
||||
|
@ -24,6 +24,7 @@ from rally.deploy import engine
|
||||
from rally import objects
|
||||
|
||||
|
||||
@engine.configure(name="MultihostEngine")
|
||||
class MultihostEngine(engine.EngineFactory):
|
||||
"""Deploy multihost cloud with existing engines.
|
||||
|
||||
|
@ -18,9 +18,9 @@ import abc
|
||||
import jsonschema
|
||||
import six
|
||||
|
||||
from rally.common.plugin import plugin
|
||||
from rally.common import sshutils
|
||||
from rally.common import utils
|
||||
from rally import exceptions
|
||||
|
||||
|
||||
class Server(utils.ImmutableMixin):
|
||||
@ -91,8 +91,13 @@ class ResourceManager(object):
|
||||
self.deployment.delete_resource(resource_id)
|
||||
|
||||
|
||||
def configure(name, namespace="default"):
|
||||
return plugin.configure(name, namespace=namespace)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ProviderFactory(object):
|
||||
@configure(name="ProviderFactory")
|
||||
class ProviderFactory(plugin.Plugin):
|
||||
"""Base class of all server providers.
|
||||
|
||||
It's a base class with self-discovery of subclasses. Each subclass
|
||||
@ -131,30 +136,13 @@ class ProviderFactory(object):
|
||||
if hasattr(self, "CONFIG_SCHEMA"):
|
||||
jsonschema.validate(self.config, self.CONFIG_SCHEMA)
|
||||
|
||||
@staticmethod
|
||||
def get_by_name(name):
|
||||
"""Return Server Provider class by type."""
|
||||
for provider in utils.itersubclasses(ProviderFactory):
|
||||
if name == provider.__name__:
|
||||
return provider
|
||||
raise exceptions.NoSuchVMProvider(vm_provider_name=name)
|
||||
|
||||
# TODO(boris-42): Remove after switching to plugin base.
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return cls.__name__
|
||||
|
||||
# FIXME(boris-42): Remove this method. And explicit create provider
|
||||
@staticmethod
|
||||
def get_provider(config, deployment):
|
||||
"""Returns instance of server provider by name."""
|
||||
provider_cls = ProviderFactory.get_by_name(config["type"])
|
||||
provider_cls = ProviderFactory.get(config["type"])
|
||||
return provider_cls(deployment, config)
|
||||
|
||||
@staticmethod
|
||||
def get_available_providers():
|
||||
"""Returns list of names of available engines."""
|
||||
return [e.__name__ for e in utils.itersubclasses(ProviderFactory)]
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_servers(self, image_uuid=None, type_id=None, amount=1):
|
||||
"""Create VMs with chosen image.
|
||||
|
@ -17,6 +17,7 @@
|
||||
from rally.deploy.serverprovider import provider
|
||||
|
||||
|
||||
@provider.configure(name="ExistingServers")
|
||||
class ExistingServers(provider.ProviderFactory):
|
||||
"""Just return endpoints from its own configuration.
|
||||
|
||||
|
@ -252,6 +252,7 @@ class LxcHost(object):
|
||||
yield self.get_server_object(name, wait)
|
||||
|
||||
|
||||
@provider.configure(name="LxcProvider")
|
||||
class LxcProvider(provider.ProviderFactory):
|
||||
"""Provide lxc container(s) on given host.
|
||||
|
||||
|
@ -34,6 +34,7 @@ SERVER_TYPE = "server"
|
||||
KEYPAIR_TYPE = "keypair"
|
||||
|
||||
|
||||
@provider.configure(name="OpenStackProvider")
|
||||
class OpenStackProvider(provider.ProviderFactory):
|
||||
"""Provide VMs using an existing OpenStack cloud.
|
||||
|
||||
|
@ -23,6 +23,7 @@ import netaddr
|
||||
from rally.deploy.serverprovider import provider
|
||||
|
||||
|
||||
@provider.configure(name="VirshProvider")
|
||||
class VirshProvider(provider.ProviderFactory):
|
||||
"""Create VMs from prebuilt templates.
|
||||
|
||||
|
@ -125,14 +125,6 @@ class PluginWithSuchNameExists(RallyException):
|
||||
"`%(namespace)s` namespace")
|
||||
|
||||
|
||||
class NoSuchEngine(NotFoundException):
|
||||
msg_fmt = _("There is no engine with name `%(engine_name)s`.")
|
||||
|
||||
|
||||
class NoSuchVMProvider(NotFoundException):
|
||||
msg_fmt = _("There is no vm provider with name `%(vm_provider_name)s`.")
|
||||
|
||||
|
||||
class NoSuchScenario(NotFoundException):
|
||||
msg_fmt = _("There is no benchmark scenario with name `%(name)s`.")
|
||||
|
||||
|
@ -43,10 +43,10 @@ class InfoCommandsTestCase(test.TestCase):
|
||||
|
||||
@mock.patch(SCENARIO + ".get_by_name",
|
||||
return_value=dummy.Dummy)
|
||||
def test_find_dummy_scenario_group(self, mock_get_by_name):
|
||||
def test_find_dummy_scenario_group(self, mock_get):
|
||||
query = "Dummy"
|
||||
status = self.info.find(query)
|
||||
mock_get_by_name.assert_called_once_with(query)
|
||||
mock_get.assert_called_once_with(query)
|
||||
self.assertIsNone(status)
|
||||
|
||||
@mock.patch(SCENARIO + ".get_scenario_by_name",
|
||||
@ -72,20 +72,20 @@ class InfoCommandsTestCase(test.TestCase):
|
||||
mock_get.assert_called_once_with(query)
|
||||
self.assertIsNone(status)
|
||||
|
||||
@mock.patch(ENGINE + ".get_by_name",
|
||||
@mock.patch(ENGINE + ".get",
|
||||
return_value=existing_cloud.ExistingCloud)
|
||||
def test_find_existing_cloud(self, mock_get_by_name):
|
||||
def test_find_existing_cloud(self, mock_get):
|
||||
query = "ExistingCloud"
|
||||
status = self.info.find(query)
|
||||
mock_get_by_name.assert_called_once_with(query)
|
||||
mock_get.assert_called_once_with(query)
|
||||
self.assertIsNone(status)
|
||||
|
||||
@mock.patch(PROVIDER + ".get_by_name",
|
||||
@mock.patch(PROVIDER + ".get",
|
||||
return_value=existing_servers.ExistingServers)
|
||||
def test_find_existing_servers(self, mock_get_by_name):
|
||||
def test_find_existing_servers(self, mock_get):
|
||||
query = "ExistingServers"
|
||||
status = self.info.find(query)
|
||||
mock_get_by_name.assert_called_once_with(query)
|
||||
mock_get.assert_called_once_with(query)
|
||||
self.assertIsNone(status)
|
||||
|
||||
@mock.patch(COMMANDS + ".ServerProviders")
|
||||
|
@ -69,30 +69,10 @@ class ProviderTestCase(test.TestCase):
|
||||
fake_validate.assert_called_once_with()
|
||||
|
||||
def test_get_provider_not_found(self):
|
||||
self.assertRaises(exceptions.NoSuchVMProvider,
|
||||
self.assertRaises(exceptions.PluginNotFound,
|
||||
ProviderFactory.get_provider,
|
||||
{"type": "fail"}, None)
|
||||
|
||||
def test_get_provider(self):
|
||||
for p in FAKE_PROVIDERS:
|
||||
p_inst = ProviderFactory.get_provider({"type": p.__name__},
|
||||
None)
|
||||
self.assertIsInstance(p_inst, p)
|
||||
|
||||
def test_get_by_name(self):
|
||||
for p in FAKE_PROVIDERS:
|
||||
self.assertEqual(p, ProviderFactory.get_by_name(p.__name__))
|
||||
|
||||
def test_get_by_name_not_found(self):
|
||||
self.assertRaises(exceptions.NoSuchVMProvider,
|
||||
ProviderFactory.get_by_name,
|
||||
"NonExistingServers")
|
||||
|
||||
def test_get_available_providers(self):
|
||||
providers = set([p.__name__ for p in FAKE_PROVIDERS])
|
||||
real_providers = set(ProviderFactory.get_available_providers())
|
||||
self.assertEqual(providers & real_providers, providers)
|
||||
|
||||
def test_vm_prvoider_factory_is_abstract(self):
|
||||
self.assertRaises(TypeError, ProviderFactory)
|
||||
|
||||
|
@ -55,6 +55,7 @@ class FakeDeployment(object):
|
||||
pass
|
||||
|
||||
|
||||
@deploy.configure(name="FakeEngine")
|
||||
class FakeEngine(deploy.EngineFactory):
|
||||
"""Fake deployment engine.
|
||||
|
||||
@ -83,36 +84,11 @@ class EngineMixIn(object):
|
||||
pass
|
||||
|
||||
|
||||
class EngineFake1(EngineMixIn, deploy.EngineFactory):
|
||||
"""Fake deployment engine.
|
||||
|
||||
Used for tests.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class EngineFake2(EngineMixIn, deploy.EngineFactory):
|
||||
"""Fake deployment engine.
|
||||
|
||||
Used for tests.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class EngineFake3(EngineFake2):
|
||||
"""Fake deployment engine.
|
||||
|
||||
Used for tests.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class EngineFactoryTestCase(test.TestCase):
|
||||
FAKE_ENGINES = [EngineFake1, EngineFake2, EngineFake3]
|
||||
|
||||
def test_get_engine_not_found(self):
|
||||
deployment = make_fake_deployment()
|
||||
self.assertRaises(exceptions.NoSuchEngine,
|
||||
self.assertRaises(exceptions.PluginNotFound,
|
||||
deploy.EngineFactory.get_engine,
|
||||
"non_existing_engine", deployment)
|
||||
self.assertEqual(consts.DeployStatus.DEPLOY_FAILED,
|
||||
@ -228,26 +204,9 @@ class EngineFactoryTestCase(test.TestCase):
|
||||
|
||||
def test_get_engine(self):
|
||||
deployment = make_fake_deployment()
|
||||
engines = EngineFactoryTestCase.FAKE_ENGINES
|
||||
for e in engines:
|
||||
engine_inst = deploy.EngineFactory.get_engine(e.__name__,
|
||||
deployment)
|
||||
self.assertIsInstance(engine_inst, e)
|
||||
|
||||
def test_get_by_name(self):
|
||||
engines = EngineFactoryTestCase.FAKE_ENGINES
|
||||
for e in engines:
|
||||
self.assertEqual(e, deploy.EngineFactory.get_by_name(e.__name__))
|
||||
|
||||
def test_get_by_name_not_found(self):
|
||||
self.assertRaises(exceptions.NoSuchEngine,
|
||||
deploy.EngineFactory.get_by_name,
|
||||
"NonExistingEngine")
|
||||
|
||||
def test_get_available_engines(self):
|
||||
engines = set([e.__name__ for e in EngineFactoryTestCase.FAKE_ENGINES])
|
||||
real_engines = set(deploy.EngineFactory.get_available_engines())
|
||||
self.assertEqual(engines & real_engines, engines)
|
||||
engine_inst = deploy.EngineFactory.get_engine("FakeEngine",
|
||||
deployment)
|
||||
self.assertIsInstance(engine_inst, FakeEngine)
|
||||
|
||||
def test_engine_factory_is_abstract(self):
|
||||
self.assertRaises(TypeError, deploy.EngineFactory)
|
||||
|
@ -80,11 +80,11 @@ class DocstringsTestCase(test.TestCase):
|
||||
long_description=False)
|
||||
|
||||
def test_all_deploy_engines_have_docstrings(self):
|
||||
for deploy_engine in utils.itersubclasses(deploy.EngineFactory):
|
||||
for deploy_engine in deploy.EngineFactory.get_all():
|
||||
self._assert_class_has_docstrings(deploy_engine)
|
||||
|
||||
def test_all_server_providers_have_docstrings(self):
|
||||
for provider in utils.itersubclasses(serverprovider.ProviderFactory):
|
||||
for provider in serverprovider.ProviderFactory.get_all():
|
||||
self._assert_class_has_docstrings(provider)
|
||||
|
||||
def test_all_SLA_have_docstrings(self):
|
||||
|
Loading…
Reference in New Issue
Block a user