]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Hot SoftwareConfig model part
authorJUN JIE NAN <nanjj@cn.ibm.com>
Wed, 7 Aug 2013 06:35:13 +0000 (14:35 +0800)
committerJUN JIE NAN <nanjj@cn.ibm.com>
Tue, 13 Aug 2013 10:24:36 +0000 (18:24 +0800)
Provide components model and related methods

Implements blueprint hot-software-config

Change-Id: I251d10f25943513ef346883263a4dd2e0f6fb7a6

heat/engine/components.py [new file with mode: 0644]
heat/tests/test_components.py [new file with mode: 0644]

diff --git a/heat/engine/components.py b/heat/engine/components.py
new file mode 100644 (file)
index 0000000..e4bf087
--- /dev/null
@@ -0,0 +1,99 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+(TYPE, PROPERTIES, SCRIPTS, RELATIONSHIPS) = (
+    'type', 'properties', 'scripts', 'relationships')
+
+(SOFTWARE_CONFIG_TYPE, HOSTED_ON, DEPENDS_ON) = (
+    'OS::Heat::SoftwareConfig', 'hosted_on', 'depends_on')
+
+
+class Component(dict):
+    """
+    Model for hot component.
+    """
+
+    def __init__(self, schema={}):
+        super(Component, self).__init__(schema)
+
+    @property
+    def properties(self):
+        return self.get(PROPERTIES, {})
+
+    @property
+    def type(self):
+        return self.get(TYPE, SOFTWARE_CONFIG_TYPE)
+
+    @property
+    def scripts(self):
+        return self.get(SCRIPTS, {})
+
+    @property
+    def relations(self):
+        return self.get(RELATIONSHIPS, [])
+
+    def hosted_on(self):
+        for rel in self.relations:
+            if HOSTED_ON in rel:
+                return rel[HOSTED_ON]
+        return None
+
+    def depends(self):
+        deps = []
+        rels = self.relations
+        for rel in rels:
+            if DEPENDS_ON in rel:
+                deps.append(rel[DEPENDS_ON])
+        return deps
+
+
+class Components(dict):
+    """
+    Model for hot components.
+    """
+
+    def __init__(self, schema):
+        items = schema.iteritems()
+        schema = dict(map(lambda x: (x[0], Component(x[1])), items))
+        super(Components, self).__init__(schema)
+
+    def depends(self):
+        deps = []
+        for (k, v) in self.iteritems():
+            for dep in v.depends():
+                if dep not in deps:
+                    deps.append(dep)
+        return deps
+
+    def filter(self, hosted):
+        return map(lambda x: x[0],
+                   filter(lambda x: x[1].hosted_on() == hosted,
+                          self.iteritems()))
+
+    def validate(self):
+        deps = self.depends()
+        for dep in deps:
+            if dep not in self.iterkeys():
+                raise ValueError('component %s is not defined.' % dep)
+            comp = self[dep]
+            if dep in comp.depends():
+                raise ValueError('component %s depends on itself.' % dep)
+        for (name, comp) in self.iteritems():
+            cdeps = comp.depends()
+            for dep in cdeps:
+                if cdeps.count(dep) > 1:
+                    msg = 'duplicated %s in %s depends on.' % (dep, name)
+                    raise ValueError(msg)
+        return True
diff --git a/heat/tests/test_components.py b/heat/tests/test_components.py
new file mode 100644 (file)
index 0000000..0c89848
--- /dev/null
@@ -0,0 +1,218 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from heat.engine.components import Component
+from heat.engine.components import Components
+from heat.tests.common import HeatTestCase
+
+
+class ComponentTest(HeatTestCase):
+
+    def test_init(self):
+        comp = Component()
+        self.assertEqual(comp.type, 'OS::Heat::SoftwareConfig')
+        self.assertEqual(comp.properties, {})
+        self.assertEqual(comp.scripts, {})
+        self.assertEqual(comp.relations, [])
+        self.assertEqual(comp.hosted_on(), None)
+        self.assertEqual(comp.depends(), [])
+
+    def test_hosted_on(self):
+        schema = {
+            'relationships': [
+                {'hosted_on': 'wordpress'}
+            ]
+        }
+        comp = Component(schema)
+        self.assertEqual(comp.hosted_on(), 'wordpress')
+
+    def test_depends(self):
+        schema = {
+            'relationships': [
+                {'depends_on': 'config_mysql'}
+            ]
+        }
+        comp = Component(schema)
+        self.assertEqual(comp.depends(), ['config_mysql'])
+
+        comp['relationships'].append({'depends_on': 'config_wordpress'})
+        self.assertEqual(comp.depends(),
+                         ['config_mysql', 'config_wordpress'])
+
+
+class ComponentsTest(HeatTestCase):
+
+    def test_init(self):
+        schema = {}
+        comps = Components(schema)
+        self.assertEqual(0, len(comps))
+
+        schema['config_mysql'] = {}
+        comps = Components(schema)
+        self.assertEquals(1, len(comps))
+        comp = comps['config_mysql']
+        self.assertIsInstance(comp, Component)
+
+    def test_depends(self):
+        schema = {
+            'install_mysql': {
+            },
+            'config_mysql': {
+                'relationships': [
+                    {'depends_on': 'install_mysql'}
+                ]
+            },
+            'start_mysql': {
+                'relationships': [
+                    {'depends_on': 'config_mysql'}
+                ]
+            }
+        }
+        comps = Components(schema)
+        self.assertEqual(3, len(comps))
+        deps = comps.depends()
+        self.assertEqual(2, len(deps))
+        self.assertIn('install_mysql', deps)
+        self.assertIn('config_mysql', deps)
+
+    def test_multi_depends(self):
+        schema = {
+            'install_mysql': {
+            },
+            'config_mysql': {
+                'relationships': [
+                    {'depends_on': 'install_mysql'}
+                ]
+            },
+            'start_mysql': {
+                'relationships': [
+                    {'depends_on': 'config_mysql'}
+                ]
+            },
+            'install_wordpress': {},
+            'config_wordpress': {
+                'relationships': [
+                    {'depends_on': 'install_wordpress'}
+                ]
+            },
+            'start_wordpress': {
+                'relationships': [
+                    {'depends_on': 'config_wordpress'},
+                    {'depends_on': 'start_mysql'}
+                ]
+            }
+        }
+        comps = Components(schema)
+        deps = comps.depends()
+        self.assertEqual(5, len(deps))
+        self.assertNotIn('start_wordpress', deps)
+        self.assertIn('install_wordpress', deps)
+        self.assertIn('config_wordpress', deps)
+        self.assertIn('start_mysql', deps)
+        self.assertIn('config_mysql', deps)
+        self.assertIn('install_mysql', deps)
+
+    def test_filter(self):
+        schema = {
+            'install_mysql': {
+                'relationships': [
+                    {'hosted_on': 'mysql'}
+                ]
+            },
+            'config_mysql': {
+                'relationships': [
+                    {'hosted_on': 'mysql'},
+                    {'depends_on': 'install_mysql'}
+                ]
+            },
+            'start_mysql': {
+                'relationships': [
+                    {'hosted_on': 'mysql'},
+                    {'depends_on': 'config_mysql'}
+                ]
+            },
+            'install_wordpress': {
+                'relationships': [
+                    {'hosted_on': 'wordpress'}
+                ]
+            },
+            'config_wordpress': {
+                'relationships': [
+                    {'hosted_on': 'wordpress'},
+                    {'depends_on': 'install_wordpress'}
+                ]
+            },
+            'start_wordpress': {
+                'relationships': [
+                    {'hosted_on': 'wordpress'},
+                    {'depends_on': 'config_wordpress'},
+                    {'depends_on': 'start_mysql'}
+                ]
+            }
+        }
+
+        comps = Components(schema)
+        names = comps.filter('mysql')
+        self.assertEqual(3, len(names))
+        self.assertIn('config_mysql', names)
+        self.assertIn('install_mysql', names)
+        self.assertIn('start_mysql', names)
+
+        names = comps.filter('wordpress')
+        self.assertEqual(3, len(names))
+        self.assertIn('config_wordpress', names)
+        self.assertIn('install_wordpress', names)
+        self.assertIn('start_wordpress', names)
+
+    def test_validate(self):
+        schema = {'install_mysql': {}}
+        comps = Components(schema)
+        self.assertTrue(comps.validate())
+
+        schema = {
+            'config_mysql': {
+                'relationships': [
+                    {'depends_on': 'config_mysql'}
+                ]
+            }
+        }
+        comps = Components(schema)
+        err = self.assertRaises(ValueError, comps.validate)
+        self.assertIn('component config_mysql depends on itself.', str(err))
+
+        schema = {
+            'config_mysql': {
+                'relationships': [
+                    {'depends_on': 'install_mysql'}
+                ]
+            }
+        }
+        comps = Components(schema)
+        err = self.assertRaises(ValueError, comps.validate)
+        self.assertIn('component install_mysql is not defined.', str(err))
+
+        schema = {
+            'install_mysql': {
+            },
+            'config_mysql': {
+                'relationships': [
+                    {'depends_on': 'install_mysql'},
+                    {'depends_on': 'install_mysql'}
+                ]
+            }
+        }
+        comps = Components(schema)
+        err = self.assertRaises(ValueError, comps.validate)
+        self.assertIn('duplicated install_mysql in config_mysql depends on.',
+                      str(err))