diff --git a/heat/engine/resources/__init__.py b/heat/engine/resources/__init__.py index 3343b597eb..6c7a535abe 100644 --- a/heat/engine/resources/__init__.py +++ b/heat/engine/resources/__init__.py @@ -11,6 +11,8 @@ # License for the specific language governing permissions and limitations # under the License. +from stevedore import extension + from heat.engine import environment from heat.engine import plugin_manager @@ -25,6 +27,14 @@ def _register_constraints(env, type_pairs): env.register_constraint(constraint_name, constraint) +def _get_mapping(namespace): + mgr = extension.ExtensionManager( + namespace=namespace, + invoke_on_load=False, + verify_requirements=True) + return [[name, mgr[name].plugin] for name in mgr.names()] + + _environment = None @@ -50,8 +60,9 @@ def _load_global_environment(env): def _load_global_resources(env): - manager = plugin_manager.PluginManager(__name__) + _register_constraints(env, _get_mapping('heat.constraints')) + manager = plugin_manager.PluginManager(__name__) # Sometimes resources should not be available for registration in Heat due # to unsatisfied dependencies. We look first for the function # 'available_resource_mapping', which should return the filtered resources. @@ -61,4 +72,5 @@ def _load_global_resources(env): constraint_mapping = plugin_manager.PluginMapping('constraint') _register_resources(env, resource_mapping.load_all(manager)) + _register_constraints(env, constraint_mapping.load_all(manager)) diff --git a/heat/engine/resources/image.py b/heat/engine/resources/image.py index fa069d0885..14b7e0c5f2 100644 --- a/heat/engine/resources/image.py +++ b/heat/engine/resources/image.py @@ -23,7 +23,3 @@ class ImageConstraint(constraints.BaseCustomConstraint): def validate_with_client(self, client, value): glance_client = client.client('glance') glance_utils.get_image_id(glance_client, value) - - -def constraint_mapping(): - return {'glance.image': ImageConstraint} diff --git a/heat/engine/resources/iso_8601.py b/heat/engine/resources/iso_8601.py index 5d9c8380df..ec86ce4b8f 100644 --- a/heat/engine/resources/iso_8601.py +++ b/heat/engine/resources/iso_8601.py @@ -23,7 +23,3 @@ class ISO8601Constraint(object): return False else: return True - - -def constraint_mapping(): - return {'iso_8601': ISO8601Constraint} diff --git a/heat/engine/resources/neutron/net.py b/heat/engine/resources/neutron/net.py index 74a7a64d2b..0265a3aef5 100644 --- a/heat/engine/resources/neutron/net.py +++ b/heat/engine/resources/neutron/net.py @@ -194,12 +194,6 @@ class NetworkConstraint(constraints.BaseCustomConstraint): neutron_client, 'network', value) -def constraint_mapping(): - if clients.neutronclient is None: - return {} - return {'neutron.network': NetworkConstraint} - - def resource_mapping(): if clients.neutronclient is None: return {} diff --git a/heat/engine/resources/nova_keypair.py b/heat/engine/resources/nova_keypair.py index c92c8ac5b9..12dc80f11b 100644 --- a/heat/engine/resources/nova_keypair.py +++ b/heat/engine/resources/nova_keypair.py @@ -142,9 +142,5 @@ class KeypairConstraint(constraints.BaseCustomConstraint): nova_utils.get_keypair(nova_client, value) -def constraint_mapping(): - return {'nova.keypair': KeypairConstraint} - - def resource_mapping(): return {'OS::Nova::KeyPair': KeyPair} diff --git a/heat/engine/resources/server.py b/heat/engine/resources/server.py index 579c1f3ab9..6b8b240381 100644 --- a/heat/engine/resources/server.py +++ b/heat/engine/resources/server.py @@ -1055,10 +1055,6 @@ class FlavorConstraint(constraints.BaseCustomConstraint): nova_utils.get_flavor_id(nova_client, value) -def constraint_mapping(): - return {'nova.flavor': FlavorConstraint} - - def resource_mapping(): return { 'OS::Nova::Server': Server, diff --git a/heat/tests/test_environment.py b/heat/tests/test_environment.py index a307b2eb45..370ca6add8 100644 --- a/heat/tests/test_environment.py +++ b/heat/tests/test_environment.py @@ -153,6 +153,14 @@ def constraint_mapping(): resources._load_global_environment, env) self.assertEqual("oops", str(error)) + def test_constraints_registry_stevedore(self): + env = environment.Environment({}) + resources._load_global_environment(env) + + self.assertEqual("FlavorConstraint", + env.get_constraint("nova.flavor").__name__) + self.assertIs(None, env.get_constraint("no_constraint")) + class EnvironmentDuplicateTest(common.HeatTestCase): diff --git a/requirements.txt b/requirements.txt index 0e32a75c7b..7c0c207372 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,4 +29,5 @@ Routes>=1.12.3,!=2.0 six>=1.7.0 SQLAlchemy>=0.7.8,!=0.9.5,<=0.9.99 sqlalchemy-migrate>=0.9.1 +stevedore>=0.14 WebOb>=1.2.3 diff --git a/setup.cfg b/setup.cfg index 619abf9e38..2e8a54c11c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,6 +37,14 @@ oslo.config.opts = heat.common.config = heat.common.config:list_opts heat.common.wsgi = heat.common.wsgi:list_opts +heat.constraints = + nova.flavor = heat.engine.resources.server:FlavorConstraint + neutron.network = heat.engine.resources.neutron.net:NetworkConstraint + glance.image = heat.engine.resources.image:ImageConstraint + iso_8601 = heat.engine.resources.iso_8601:ISO8601Constraint + nova.keypair = heat.engine.resources.nova_keypair:KeypairConstraint + + [global] setup-hooks = pbr.hooks.setup_hook