Synchronize upstream version of 0.9 15/37615/1
authorshangxdy <shang.xiaodong@zte.com.cn>
Mon, 17 Jul 2017 09:40:59 +0000 (17:40 +0800)
committershangxdy <shang.xiaodong@zte.com.cn>
Mon, 17 Jul 2017 09:40:59 +0000 (17:40 +0800)
Synchronize heat-translator wiht upstream versionn of 0.9

JIRA: PARSER-128

Change-Id: I4d2c62a0e81119d5c0305e3ac052415a6d5acee3
Signed-off-by: shangxdy <shang.xiaodong@zte.com.cn>
33 files changed:
tosca2heat/heat-translator/.python-version [new file with mode: 0644]
tosca2heat/heat-translator/CONTRIBUTING.rst [new file with mode: 0644]
tosca2heat/heat-translator/HACKING.rst [new file with mode: 0644]
tosca2heat/heat-translator/MANIFEST.in [deleted file]
tosca2heat/heat-translator/README.rst [moved from tosca2heat/heat-translator/README.md with 100% similarity]
tosca2heat/heat-translator/openstack-common.conf [deleted file]
tosca2heat/heat-translator/requirements.txt
tosca2heat/heat-translator/setup.cfg
tosca2heat/heat-translator/setup.py
tosca2heat/heat-translator/test-requirements.txt
tosca2heat/heat-translator/translator/common/images.py
tosca2heat/heat-translator/translator/common/utils.py
tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py
tosca2heat/heat-translator/translator/hot/syntax/hot_template.py
tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_autoscaling.py
tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_floatingip.py [new file with mode: 0644]
tosca2heat/heat-translator/translator/hot/tosca/tosca_cluster_policies_scaling.py [new file with mode: 0644]
tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
tosca2heat/heat-translator/translator/hot/tosca/tosca_floating.py [new file with mode: 0644]
tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_monitoring.py [new file with mode: 0644]
tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py
tosca2heat/heat-translator/translator/hot/tosca_translator.py
tosca2heat/heat-translator/translator/hot/translate_node_templates.py
tosca2heat/heat-translator/translator/tests/data/autoscaling/tosca_autoscaling.yaml
tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_autoscaling.yaml
tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_cluster_autoscaling.yaml
tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/asg_res.yaml [new file with mode: 0644]
tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/hot_monitoring_scaling.yaml [new file with mode: 0644]
tosca2heat/heat-translator/translator/tests/data/hot_output/nfv/hot_tosca_nfv_autoscaling.yaml
tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_w_multiple_attachment.yaml [new file with mode: 0644]
tosca2heat/heat-translator/translator/tests/data/monitoring/tosca_monitoring_scaling.yaml [new file with mode: 0644]
tosca2heat/heat-translator/translator/tests/data/storage/tosca_multiple_blockstorage_w_multiple_attachment.yaml [new file with mode: 0644]
tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py

diff --git a/tosca2heat/heat-translator/.python-version b/tosca2heat/heat-translator/.python-version
new file mode 100644 (file)
index 0000000..ecc17b8
--- /dev/null
@@ -0,0 +1 @@
+2.7.13
diff --git a/tosca2heat/heat-translator/CONTRIBUTING.rst b/tosca2heat/heat-translator/CONTRIBUTING.rst
new file mode 100644 (file)
index 0000000..e12c422
--- /dev/null
@@ -0,0 +1,16 @@
+If you would like to contribute to the development of OpenStack,
+you must follow the steps in this page:
+
+   http://docs.openstack.org/infra/manual/developers.html
+
+Once those steps have been completed, changes to OpenStack
+should be submitted for review via the Gerrit tool, following
+the workflow documented at:
+
+   http://docs.openstack.org/infra/manual/developers.html#development-workflow
+
+Pull requests submitted through GitHub will be ignored.
+
+Bugs should be filed on Launchpad, not GitHub:
+
+   https://bugs.launchpad.net/heat-translator
\ No newline at end of file
diff --git a/tosca2heat/heat-translator/HACKING.rst b/tosca2heat/heat-translator/HACKING.rst
new file mode 100644 (file)
index 0000000..9c60464
--- /dev/null
@@ -0,0 +1,4 @@
+heat-translator Style Commandments
+===============================================
+
+Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
\ No newline at end of file
diff --git a/tosca2heat/heat-translator/MANIFEST.in b/tosca2heat/heat-translator/MANIFEST.in
deleted file mode 100644 (file)
index 1fc29b3..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-include AUTHORS
-include ChangeLog
-global-include *.py
-global-include *.yaml
-global-include *.sh
-global-include *.txt
-global-include *.csar
-global-include *.zip
-global-include *.meta
-global-include *.conf
-exclude .gitignore
-exclude .gitreview
-
-global-exclude *.pyc
diff --git a/tosca2heat/heat-translator/openstack-common.conf b/tosca2heat/heat-translator/openstack-common.conf
deleted file mode 100644 (file)
index d359e40..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[DEFAULT]
-
-# The list of modules to copy from oslo-incubator.git
-
-# The base module to hold the copy of openstack.common
-base=translator
\ No newline at end of file
index b211383..2de8022 100644 (file)
@@ -1,15 +1,15 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-pbr>=1.8 # Apache-2.0
-Babel>=2.3.4 # BSD
-cliff>=2.3.0 # Apache-2.0
+pbr!=2.1.0,>=2.0.0 # Apache-2.0
+Babel!=2.4.0,>=2.3.4 # BSD
+cliff>=2.6.0 # Apache-2.0
 PyYAML>=3.10.0 # MIT
 python-dateutil>=2.4.2 # BSD
 six>=1.9.0 # MIT
-tosca-parser>=0.7.0 # Apache-2.0
-keystoneauth1>=2.18.0 # Apache-2.0
-python-novaclient>=7.1.0 # Apache-2.0
+tosca-parser>=0.8.1 # Apache-2.0
+keystoneauth1>=2.21.0 # Apache-2.0
+python-novaclient>=9.0.0 # Apache-2.0
 python-heatclient>=1.6.1 # Apache-2.0
-python-glanceclient>=2.5.0 # Apache-2.0
-requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
+python-glanceclient>=2.7.0 # Apache-2.0
+requests>=2.14.2 # Apache-2.0
index 38bc1b9..21d0c6f 100644 (file)
@@ -2,7 +2,7 @@
 name = heat-translator
 summary = Tool to translate non-heat templates to Heat Orchestration Template.
 description-file =
-    README.md
+    README.rst
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
 home-page = http://docs.openstack.org/developer/heat-translator/
@@ -35,6 +35,14 @@ openstack.translator.v1 =
 console_scripts =
     heat-translator = translator.shell:main
 
+[build_sphinx]
+source-dir = doc/source
+build-dir = doc/build
+all_files = 1
+
+[upload_sphinx]
+upload-dir = doc/build/html
+
 [compile_catalog]
 directory = translator/locale
 domain = translator
index 782bb21..566d844 100644 (file)
@@ -25,5 +25,5 @@ except ImportError:
     pass
 
 setuptools.setup(
-    setup_requires=['pbr>=1.8'],
+    setup_requires=['pbr>=2.0.0'],
     pbr=True)
index d4b59c3..da69d4f 100644 (file)
@@ -1,13 +1,13 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking<0.11,>=0.10.0
-coverage>=4.0 # Apache-2.0
+hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
+coverage!=4.4,>=4.0 # Apache-2.0
 fixtures>=3.0.0 # Apache-2.0/BSD
 oslotest>=1.10.0 # Apache-2.0
 oslosphinx>=4.7.0 # Apache-2.0
 python-subunit>=0.0.18 # Apache-2.0/BSD
-sphinx>=1.5.1 # BSD
+sphinx>=1.6.2 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
 testscenarios>=0.4 # Apache-2.0/BSD
 testtools>=1.4.0 # MIT
index f9fa4f1..d9b8818 100644 (file)
@@ -1,3 +1,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
@@ -24,37 +25,46 @@ log = logging.getLogger('heat-translator')
 
 PREDEF_IMAGES = {
     'ubuntu-software-config-os-init': {'architecture': 'x86_64',
-                                       'type': 'Linux',
-                                       'distribution': 'Ubuntu',
-                                       'version': '14.04'},
+                                       'os_type': 'linux',
+                                       'os_distro': 'ubuntu',
+                                       'os_version': '14.04'
+                                       },
+
     'ubuntu-12.04-software-config-os-init': {'architecture': 'x86_64',
-                                             'type': 'Linux',
-                                             'distribution': 'Ubuntu',
-                                             'version': '12.04'},
+                                             'os_type': 'linux',
+                                             'os_distro': 'ubuntu',
+                                             'os_version': '12.04'
+                                             },
     'fedora-amd64-heat-config': {'architecture': 'x86_64',
-                                 'type': 'Linux',
-                                 'distribution': 'Fedora',
-                                 'version': '18.0'},
+                                 'os_type': 'linux',
+                                 'os_distro': 'fedora',
+                                 'os_version': '18.0'
+                                 },
     'F18-x86_64-cfntools': {'architecture': 'x86_64',
-                            'type': 'Linux',
-                            'distribution': 'Fedora',
-                            'version': '19'},
+                            'os_type': 'linux',
+                            'os_distro': 'fedora',
+                            'os_version': '19'
+                            },
     'Fedora-x86_64-20-20131211.1-sda': {'architecture': 'x86_64',
-                                        'type': 'Linux',
-                                        'distribution': 'Fedora',
-                                        'version': '20'},
+                                        'os_type': 'linux',
+                                        'os_distro': 'fedora',
+                                        'os_version': '20'
+                                        },
     'cirros-0.3.1-x86_64-uec': {'architecture': 'x86_64',
-                                'type': 'Linux',
-                                'distribution': 'CirrOS',
-                                'version': '0.3.1'},
+                                'os_type': 'linux',
+                                'os_distro': 'cirros',
+                                'os_version': '0.3.1'
+                                },
     'cirros-0.3.2-x86_64-uec': {'architecture': 'x86_64',
-                                'type': 'Linux',
-                                'distribution': 'CirrOS',
-                                'version': '0.3.2'},
+                                'os_type': 'linux',
+                                'os_distro': 'cirros',
+                                'os_version': '0.3.2'
+                                },
     'rhel-6.5-test-image': {'architecture': 'x86_64',
-                            'type': 'Linux',
-                            'distribution': 'RHEL',
-                            'version': '6.5'}
+                            'os_type': 'linux',
+                            'os_distro': 'rhel',
+                            'os_version': '6.5'
+                            }
 }
 
 SESSION = None
@@ -78,7 +88,8 @@ def get_images():
         else:
             for image in client.images.list():
                 image_name = image.name.encode('ascii', 'ignore')
-                metadata = ["architecture", "type", "distribution", "version"]
+                metadata = ["architecture", "type", "distribution", "version",
+                            "os_distro", "os_type", "os_version"]
                 if any(key in image.keys() for key in metadata):
                     IMAGES[image_name] = {}
                     for key in metadata:
index 874c8ec..85af60a 100644 (file)
@@ -216,7 +216,8 @@ class YamlUtils(object):
 class TranslationUtils(object):
 
     @staticmethod
-    def compare_tosca_translation_with_hot(tosca_file, hot_files, params):
+    def compare_tosca_translation_with_hot(tosca_file, hot_files, params,
+                                           nested_resources=False):
         '''Verify tosca translation against the given hot specification.
 
         inputs:
@@ -247,6 +248,12 @@ class TranslationUtils(object):
 
         basename = os.path.basename(hot_files[0])
         output_hot_templates = translate.translate_to_yaml_files_dict(basename)
+
+        if nested_resources:
+            basename = os.path.basename(hot_files[0])
+            output_hot_templates =\
+                translate.translate_to_yaml_files_dict(basename, True)
+
         output_dict = {}
         for output_hot_template_name in output_hot_templates:
             output_dict[output_hot_template_name] = \
index 80a62ff..ff2111a 100644 (file)
@@ -30,7 +30,8 @@ policy_type = ['tosca.policies.Placement',
                'tosca.policies.Scaling',
                'tosca.policies.Scaling.Cluster',
                'tosca.policies.Placement.Colocate',
-               'tosca.policies.Placement.Antilocate']
+               'tosca.policies.Placement.Antilocate',
+               'tosca.policies.Monitoring']
 
 log = logging.getLogger('heat-translator')
 
index 7fae022..f279997 100644 (file)
@@ -46,7 +46,8 @@ class HotTemplate(object):
         return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', nodes)
 
     def output_to_yaml_files_dict(self, base_filename,
-                                  hot_template_version=LATEST):
+                                  hot_template_version=LATEST,
+                                  embed_substack_templates=False):
         yaml_files_dict = {}
         base_filename, ext = os.path.splitext(base_filename)
 
@@ -55,9 +56,9 @@ class HotTemplate(object):
             yaml_files_dict.update(
                 resource.extract_substack_templates(base_filename,
                                                     hot_template_version))
-
-        yaml_files_dict[base_filename + ext] = \
-            self.output_to_yaml(hot_template_version, False)
+        if not embed_substack_templates:
+            yaml_files_dict[base_filename + ext] = \
+                self.output_to_yaml(hot_template_version, False)
 
         return yaml_files_dict
 
index 978e965..f093c2e 100644 (file)
@@ -79,11 +79,13 @@ class AutoscalingTest(TestCase):
                 max_instances: 10
                 default_instances: 3
                 increment: 1
+                cooldown: 60
               '''
 
         expectedprops = {'desired_capacity': 3,
                          'max_size': 10,
                          'min_size': 2,
+                         'cooldown': 60,
                          'resource': {'type': 'asg_res.yaml'}}
 
         self._tosca_scaling_test(
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_floatingip.py b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_floatingip.py
new file mode 100644 (file)
index 0000000..445390d
--- /dev/null
@@ -0,0 +1,71 @@
+#    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 toscaparser.nodetemplate import NodeTemplate
+from toscaparser.tests.base import TestCase
+import toscaparser.utils.yamlparser
+from translator.hot.tosca.tosca_floating import ToscaFloatingIP
+
+
+class ToscaFloatingIPTest(TestCase):
+
+    def _tosca_floatingip_test(self, tpl_snippet, expectedprops, name=None):
+        nodetemplates = (toscaparser.utils.yamlparser.
+                         simple_parse(tpl_snippet)['node_templates'])
+        if not name:
+            name = list(nodetemplates.keys())[0]
+        nodetemplate = NodeTemplate(name, nodetemplates, custom_def=[])
+        nodetemplate.validate()
+        tosca_floatingip = ToscaFloatingIP(nodetemplate)
+        tosca_floatingip.handle_properties()
+        self.assertEqual(expectedprops, tosca_floatingip.properties)
+
+    def test_node_floatingip_with_properties(self):
+        tpl_snippet = '''
+        node_templates:
+          floating_ip:
+            type: tosca.nodes.network.FloatingIP
+            properties:
+              floating_network: public
+              floating_ip_address: 192.168.56.8
+              port_id: abcd
+        '''
+        expectedprops = {'floating_network': 'public',
+                         'floating_ip_address': '192.168.56.8',
+                         'port_id': 'abcd'}
+        self._tosca_floatingip_test(
+            tpl_snippet,
+            expectedprops)
+
+    def test_node_floatingip_with_properties_and_link_requirements(self):
+        tpl_snippet = '''
+        node_templates:
+          floating_ip:
+            type: tosca.nodes.network.FloatingIP
+            properties:
+              floating_network: public
+              floating_ip_address: 192.168.56.8
+            requirements:
+              - link:
+                  node: port1
+          port1:
+            type: tosca.nodes.network.Port
+            properties:
+              ip_address: 10.0.0.6
+        '''
+        expectedprops = {'floating_network': 'public',
+                         'floating_ip_address': '192.168.56.8',
+                         'port_id': '{ get_resource: port1 }'}
+        self._tosca_floatingip_test(
+            tpl_snippet,
+            expectedprops,
+            name='floating_ip')
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_cluster_policies_scaling.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_cluster_policies_scaling.py
new file mode 100644 (file)
index 0000000..1de0158
--- /dev/null
@@ -0,0 +1,195 @@
+#
+# 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 collections import defaultdict
+
+from translator.hot.syntax.hot_resource import HotResource
+# Name used to dynamically load appropriate map class.
+TARGET_CLASS_NAME = 'ToscaClusterAutoscaling'
+
+SCALE_POLICY = 'senlin.policy.scaling-1.0'
+SERVER_TYPE = 'os.nova.server-1.0'
+SCALE_TYPE = {'SCALE_IN': 'CLUSTER_SCALE_IN',
+              'SCALE_OUT': 'CLUSTER_SCALE_OUT'}
+
+ALARM_METER_NAME = {'utilization': 'cpu_util'}
+ALARM_COMPARISON_OPERATOR = {'greater_than': 'gt', 'gerater_equal': 'ge',
+                             'less_than': 'lt', 'less_equal': 'le',
+                             'equal': 'eq', 'not_equal': 'ne'}
+ALARM_STATISTIC = {'average': 'avg'}
+
+
+class ToscaClusterAutoscaling(HotResource):
+    '''Translate TOSCA node type tosca.policies.Scaling.Cluster'''
+
+    toscatype = 'tosca.policies.Scaling.Cluster'
+
+    def __init__(self, policy, csar_dir=None):
+        hot_type = "OS::Senlin::Policy"
+        super(ToscaClusterAutoscaling, self).__init__(policy,
+                                                      type=hot_type,
+                                                      csar_dir=csar_dir)
+        self.policy = policy
+
+    def _generate_scale_properties(self,
+                                   target_cluster_nodes,
+                                   cluster_scale_type):
+        properties = {}
+        bindings = []
+        policy_res = {}
+        adjustment = {}
+        properties["type"] = SCALE_POLICY
+        for cluster_node in target_cluster_nodes:
+            bindings.append({'cluster': cluster_node})
+        properties["bindings"] = bindings
+        policy_res["event"] = cluster_scale_type
+        adjustment["type"] = "CHANGE_IN_CAPACITY"
+        adjustment["number"] = self.\
+            policy.entity_tpl["properties"]["increment"]
+        policy_res["adjustment"] = adjustment
+        properties["properties"] = policy_res
+        return properties
+
+    def handle_expansion(self):
+        hot_resources = []
+        trigger_receivers = defaultdict(list)
+        for node in self.policy.targets:
+            for trigger in self.policy.entity_tpl['triggers']:
+                for action in self.policy.\
+                    entity_tpl['triggers'][trigger]['action']:
+                    scale_name = action
+                    action_sample = self.policy.\
+                        entity_tpl['triggers'][trigger]['action'][action]
+                    scale_type = action_sample['type']
+                    scale_implement = action_sample['implementation']
+                (entity, method) = scale_implement.split('.')
+                receiver_prop = {}
+                receiver_prop['cluster'] = {
+                    "get_resource": "%s_cluster" % node
+                    }
+                receiver_prop['action'] = SCALE_TYPE[scale_type]
+                receiver_prop['type'] = method
+                receiver_name = node + '_' + scale_name + '_receiver'
+                trigger_receivers[trigger].append(receiver_name)
+                receiver_resources = HotResource(self.nodetemplate,
+                                                 type='OS::Senlin::Receiver',
+                                                 name=receiver_name,
+                                                 properties=receiver_prop)
+                hot_resources.append(receiver_resources)
+
+        for trigger in self.policy.entity_tpl['triggers']:
+            sample = self.policy.\
+                entity_tpl['triggers'][trigger]['condition']
+            (meter_name, comparison_operator, threshold) = \
+                sample["constraint"].split()
+            threshold = threshold.strip("%")
+            alarm_prop = {}
+            alarm_prop["description"] = self.policy.entity_tpl['description']
+            alarm_prop["meter_name"] = self.policy.\
+                entity_tpl['triggers'][trigger]['event_type']['metrics']
+            alarm_prop["statistic"] = ALARM_STATISTIC[sample['method']]
+            alarm_prop["period"] = sample["period"]
+            alarm_prop["evaluation_periods"] = sample["evaluations"]
+            alarm_prop["threshold"] = threshold
+            alarm_prop["comparison_operator"] = \
+                ALARM_COMPARISON_OPERATOR[comparison_operator]
+            alarm_prop["repeat_actions"] = "True"
+            alarm_prop["alarm_actions"] = []
+            for index in range(len(trigger_receivers[trigger])):
+                alarm_prop["alarm_actions"].\
+                    append({'get_attr': [trigger_receivers[trigger][index],
+                                         'channel',
+                                         'alarm_url']})
+            ceilometer_resources = HotResource(self.nodetemplate,
+                                               type='OS::Aodh::Alarm',
+                                               name=trigger + '_alarm',
+                                               properties=alarm_prop)
+            hot_resources.append(ceilometer_resources)
+        return hot_resources
+
+    def handle_properties(self, resources):
+        remove_resources = []
+        networks = defaultdict(list)
+        for index, resource in enumerate(resources):
+            if resource.type == 'OS::Neutron::Port':
+                for hot_resource in resource.depends_on_nodes:
+                    if hot_resource.type != 'OS::Neutron::Net':
+                        networks[hot_resource.name].\
+                            append(
+                            {'network': '%s' % resource.properties['network']}
+                            )
+                remove_resources.append(resource)
+            elif resource.type == 'OS::Neutron::Net':
+                remove_resources.append(resource)
+            elif resource.name in self.policy.targets and \
+                resource.type != 'OS::Senlin::Policy':
+                props = {}
+                del resource.properties['user_data_format']
+                del resource.properties['networks']
+                props['type'] = SERVER_TYPE
+                props['properties'] = resource.properties
+                profile_resources = \
+                    HotResource(resource,
+                                type='OS::Senlin::Profile',
+                                name=resource.name,
+                                properties=props)
+                resources.pop(index)
+                resources.insert(index, profile_resources)
+        for remove_resource in remove_resources:
+            resources.remove(remove_resource)
+
+        for index, resource in enumerate(resources):
+            if resource.name in self.policy.targets:
+                resource.properties['properties']['networks'] = \
+                    networks[resource.name]
+
+        for node in self.policy.targets:
+            props = {}
+            props["profile"] = {'get_resource': '%s' % node}
+            temp = self.policy.entity_tpl["properties"]
+            props["min_size"] = temp["min_instances"]
+            props["max_size"] = temp["max_instances"]
+            props["desired_capacity"] = temp["default_instances"]
+            self.cluster_name = '%s_cluster' % node
+            cluster_resources = \
+                HotResource(self.nodetemplate,
+                            type='OS::Senlin::Cluster',
+                            name=self.cluster_name,
+                            properties=props)
+            resources.append(cluster_resources)
+
+        trigger_num = len(self.policy.entity_tpl['triggers'])
+        for num, trigger in enumerate(self.policy.entity_tpl['triggers']):
+            target_cluster_nodes = []
+            for action in self.policy.\
+                entity_tpl['triggers'][trigger]['action']:
+                scale_type = self.policy.\
+                    entity_tpl['triggers'][trigger]['action'][action]['type']
+            for node in self.policy.targets:
+                target_cluster_nodes.\
+                    append({"get_resource": "%s_cluster" % node})
+            cluster_scale_type = SCALE_TYPE[scale_type]
+            scale_in_props = \
+                self._generate_scale_properties(target_cluster_nodes,
+                                                cluster_scale_type)
+            if num == trigger_num - 1:
+                self.name = self.name + '_' + trigger
+                self.properties = scale_in_props
+                break
+            policy_resources = \
+                HotResource(self.nodetemplate,
+                            type='OS::Senlin::Policy',
+                            name=self.name + '_' + trigger,
+                            properties=scale_in_props)
+            resources.append(policy_resources)
+        return resources
index 5f6b751..40dc799 100644 (file)
@@ -28,13 +28,16 @@ TARGET_CLASS_NAME = 'ToscaCompute'
 
 
 class ToscaCompute(HotResource):
-    '''Translate TOSCA node type tosca.nodes.Compute.'''
+    """Translate TOSCA node type tosca.nodes.Compute."""
 
     COMPUTE_HOST_PROP = (DISK_SIZE, MEM_SIZE, NUM_CPUS) = \
                         ('disk_size', 'mem_size', 'num_cpus')
 
     COMPUTE_OS_PROP = (ARCHITECTURE, DISTRIBUTION, TYPE, VERSION) = \
                       ('architecture', 'distribution', 'type', 'version')
+
+    IMAGE_OS_PROP = (OS_DISTRO, OS_TYPE, OS_VERSION) = \
+                    ('os_distro', 'os_type', 'os_version')
     toscatype = 'tosca.nodes.Compute'
 
     ALLOWED_NOVA_SERVER_PROPS = \
@@ -146,28 +149,35 @@ class ToscaCompute(HotResource):
         if architecture is None:
             self._log_compute_msg(self.ARCHITECTURE, 'image')
         match_arch = self._match_images(match_all, images,
-                                        self.ARCHITECTURE, architecture)
-        type = properties.get(self.TYPE)
-        if type is None:
+                                        [self.ARCHITECTURE], architecture)
+
+        image_type = properties.get(self.TYPE)
+        if image_type is None:
             self._log_compute_msg(self.TYPE, 'image')
-        match_type = self._match_images(match_arch, images, self.TYPE, type)
+        match_type = self._match_images(match_arch, images, [self.TYPE,
+                                                             self.OS_TYPE],
+                                        image_type)
+
         distribution = properties.get(self.DISTRIBUTION)
         if distribution is None:
             self._log_compute_msg(self.DISTRIBUTION, 'image')
         match_distribution = self._match_images(match_type, images,
-                                                self.DISTRIBUTION,
+                                                [self.DISTRIBUTION,
+                                                 self.OS_DISTRO],
                                                 distribution)
         version = properties.get(self.VERSION)
         if version is None:
             self._log_compute_msg(self.VERSION, 'image')
         match_version = self._match_images(match_distribution, images,
-                                           self.VERSION, version)
+                                           [self.VERSION, self.OS_VERSION],
+                                           version)
 
         if len(match_version):
             return list(match_version)[0]
 
-    def _match_flavors(self, this_list, this_dict, attr, size):
-        '''Return from this list all flavors matching the attribute size.'''
+    @staticmethod
+    def _match_flavors(this_list, this_dict, attr, size):
+        """Return from this list all flavors matching the attribute size."""
         if not size:
             return list(this_list)
         matching_flavors = []
@@ -178,24 +188,27 @@ class ToscaCompute(HotResource):
         log.debug(_('Returning list of flavors matching the attribute size.'))
         return matching_flavors
 
-    def _least_flavor(self, this_list, this_dict, attr):
-        '''Return from this list the flavor with the smallest attr.'''
+    @staticmethod
+    def _least_flavor(this_list, this_dict, attr):
+        """Return from this list the flavor with the smallest attr."""
         least_flavor = this_list[0]
         for flavor in this_list:
             if this_dict[flavor][attr] < this_dict[least_flavor][attr]:
                 least_flavor = flavor
         return least_flavor
 
-    def _match_images(self, this_list, this_dict, attr, prop):
+    @staticmethod
+    def _match_images(this_list, this_dict, attr_list, prop):
         if not prop:
             return this_list
         matching_images = []
         for image in this_list:
-            if attr in this_dict[image]:
-                if this_dict[image][attr].lower() == str(prop).lower():
-                    matching_images.insert(0, image)
-            else:
-                matching_images.append(image)
+            for attr in attr_list:
+                if attr in this_dict[image]:
+                    if this_dict[image][attr].lower() == str(prop).lower():
+                        matching_images.insert(0, image)
+                else:
+                    matching_images.append(image)
         return matching_images
 
     def get_hot_attribute(self, attribute, args):
@@ -215,8 +228,9 @@ class ToscaCompute(HotResource):
 
         return attr
 
-    def _log_compute_msg(self, prop, what):
+    @staticmethod
+    def _log_compute_msg(prop, what):
         msg = _('No value is provided for Compute capability '
                 'property "%(prop)s". This may set an undesired "%(what)s" '
                 'in the template.') % {'prop': prop, 'what': what}
-        log.warn(msg)
+        log.warning(msg)
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_floating.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_floating.py
new file mode 100644 (file)
index 0000000..6126653
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# 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 translator.hot.syntax.hot_resource import HotResource
+
+# Name used to dynamically load appropriate map class.
+TARGET_CLASS_NAME = 'ToscaFloatingIP'
+TOSCA_LINKS_TO = 'tosca.relationships.network.LinksTo'
+
+
+class ToscaFloatingIP(HotResource):
+    '''Translate TOSCA node type tosca.nodes.network.FloatingIP'''
+
+    toscatype = 'tosca.nodes.network.FloatingIP'
+
+    def __init__(self, nodetemplate, csar_dir=None):
+        super(ToscaFloatingIP, self).__init__(nodetemplate,
+                                              type='OS::Neutron::FloatingIP',
+                                              csar_dir=csar_dir)
+
+    def handle_properties(self):
+        tosca_props = self.get_tosca_props()
+        fip_props = {}
+        for key, value in tosca_props.items():
+            fip_props[key] = value
+
+        links_to = None
+        for rel, node in self.nodetemplate.relationships.items():
+            if not links_to and rel.is_derived_from(TOSCA_LINKS_TO):
+                links_to = node
+                for hot_resource in self.depends_on_nodes:
+                    if links_to.name == hot_resource.name:
+                        self.depends_on.remove(hot_resource)
+                        break
+                fip_props['port_id'] =\
+                    '{ get_resource: %s }' % (links_to.name)
+
+        self.properties = fip_props
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_monitoring.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_monitoring.py
new file mode 100644 (file)
index 0000000..7aac1c7
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# 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.
+
+import logging
+from toscaparser.common.exception import InvalidPropertyValueError
+from translator.hot.syntax.hot_resource import HotResource
+
+# Name used to dynamically load appropriate map class.
+TARGET_CLASS_NAME = 'ToscaMonitoring'
+
+log = logging.getLogger('heat-translator')
+
+ALARM_STATISTIC = {'average': 'avg', 'summary': 'sum',
+                   'maximum': 'max', 'minimum': 'min'}
+
+
+class ToscaMonitoring(HotResource):
+    '''Translate TOSCA node type tosca.policies.Monitoring'''
+
+    toscatype = 'tosca.policies.Monitoring'
+
+    def __init__(self, policy, csar_dir=None):
+        hot_type = "OS::Aodh::Alarm"
+        super(ToscaMonitoring, self).__init__(policy,
+                                              type=hot_type,
+                                              csar_dir=csar_dir)
+        self.policy = policy
+        self.filter = list()
+
+    def handle_expansion(self):
+        '''handle monitoring resources in case of multiple triggers'''
+        extra_resources = list()
+        extra_triggers = self.policy.entity_tpl["triggers"]
+        for trigger_name, trigger_dict in extra_triggers.items():
+            if trigger_name not in self.filter:
+                self.filter.append(trigger_name)
+                prop = self._get_monitoring_prop(trigger_dict)
+                mon_resources = HotResource(self.nodetemplate,
+                                            type='OS::Aodh::Alarm',
+                                            name=trigger_name,
+                                            properties=prop)
+                extra_resources.append(mon_resources)
+        return extra_resources
+
+    def handle_properties(self):
+        self.properties = {}
+        if self.policy.entity_tpl.get('triggers'):
+            triggers = self.policy.entity_tpl["triggers"]
+            trigger_name, trigger_dict = list(triggers.items())[0]
+            self.filter.append(trigger_name)
+            self.properties = self._get_monitoring_prop(trigger_dict)
+            self.name = trigger_name
+        return self.properties
+
+    def _get_monitoring_prop(self, trigger):
+        sample = trigger.get('condition')
+        prop = dict()
+        prop["description"] = sample.get('constraint')
+        prop["meter_name"] = trigger.get('meter_name')
+        if sample.get('method') not in ALARM_STATISTIC:
+            msg = _('method should be one of given statistics')
+            log.error(msg)
+            raise InvalidPropertyValueError(what=msg)
+        prop["statistic"] = ALARM_STATISTIC[sample["method"]]
+        prop["period"] = sample.get("period")
+        prop["threshold"] = sample.get("threshold")
+        prop["comparison_operator"] = sample.get('comparison_operator')
+        prop['evaluation_periods'] = sample.get('evaluations')
+        return prop
+
+    def embed_substack_templates(self, hot_template_version):
+        pass
index 1b63f24..d34acd4 100644 (file)
@@ -94,6 +94,8 @@ class ToscaAutoscaling(HotResource):
         self.properties["adjustment_type"] = "change_in_capacity "
         self.properties["scaling_adjustment"] = self.\
             policy.entity_tpl["properties"]["increment"]
+        self.properties["cooldown"] =\
+            self.policy.entity_tpl["properties"]["cooldown"]
         delete_res_names = []
         scale_res = []
         for index, resource in enumerate(resources):
@@ -105,6 +107,7 @@ class ToscaAutoscaling(HotResource):
                 res["min_size"] = temp["min_instances"]
                 res["max_size"] = temp["max_instances"]
                 res["desired_capacity"] = temp["default_instances"]
+                res["cooldown"] = temp["cooldown"]
                 props['type'] = resource.type
                 props['properties'] = resource.properties
                 res['resource'] = {'type': self.policy.name + '_res.yaml'}
index b9d4c77..a637a8c 100644 (file)
@@ -71,7 +71,8 @@ class TOSCATranslator(object):
 
         return yaml_files["output.yaml"]
 
-    def translate_to_yaml_files_dict(self, base_filename):
+    def translate_to_yaml_files_dict(self, base_filename,
+                                     nested_resources=False):
         """Translate to HOT YAML
 
         This method produces a translated output containing main and
@@ -82,7 +83,7 @@ class TOSCATranslator(object):
         self._translate_to_hot_yaml()
         return self.hot_template.output_to_yaml_files_dict(
             base_filename,
-            self.node_translator.hot_template_version)
+            self.node_translator.hot_template_version, nested_resources)
 
     def _translate_inputs(self):
         translator = TranslateInputs(self.tosca.inputs, self.parsed_params,
index 1a1a4d7..78ab1c4 100644 (file)
@@ -228,11 +228,11 @@ class TranslateNodeTemplates(object):
             # BlockStorage Attachment is a special case,
             # which doesn't match to Heat Resources 1 to 1.
             if base_type == "tosca.nodes.Compute":
-                volume_name = None
                 requirements = node.requirements
                 if requirements:
                     # Find the name of associated BlockStorage node
                     for requires in requirements:
+                        volume_name = None
                         for value in requires.values():
                             if isinstance(value, dict):
                                 for node_name in value.values():
@@ -250,11 +250,12 @@ class TranslateNodeTemplates(object):
                                         volume_name = node_name
                                         break
 
-                    suffix = suffix + 1
-                    attachment_node = self._get_attachment_node(
-                        node, suffix, volume_name)
-                    if attachment_node:
-                        self.hot_resources.append(attachment_node)
+                        if volume_name:
+                            suffix = suffix + 1
+                            attachment_node = self._get_attachment_node(
+                                node, suffix, volume_name)
+                            if attachment_node:
+                                self.hot_resources.append(attachment_node)
                 for i in self.tosca.inputs:
                     if (i.name == 'key_name' and
                             node.get_property_value('key_name') is None):
@@ -269,8 +270,12 @@ class TranslateNodeTemplates(object):
                policy_type.type != 'tosca.policies.Scaling.Cluster':
                 TOSCA_TO_HOT_TYPE[policy_type.type] = \
                     TOSCA_TO_HOT_TYPE['tosca.policies.Scaling']
-            if not policy.is_derived_from('tosca.policies.Scaling') and \
-               policy_type.type not in TOSCA_TO_HOT_TYPE:
+            if policy.is_derived_from('tosca.policies.Monitoring'):
+                TOSCA_TO_HOT_TYPE[policy_type.type] = \
+                    TOSCA_TO_HOT_TYPE['tosca.policies.Monitoring']
+            if not policy.is_derived_from('tosca.policies.Monitoring') and \
+                    not policy.is_derived_from('tosca.policies.Scaling') and \
+                    policy_type.type not in TOSCA_TO_HOT_TYPE:
                 raise UnsupportedTypeError(type=_('%s') % policy_type.type)
             elif policy_type.type == 'tosca.policies.Scaling.Cluster':
                 self.hot_template_version = '2016-04-08'
@@ -587,7 +592,8 @@ class TranslateNodeTemplates(object):
         for key_r, value_n in node.relationships.items():
             if key_r.is_derived_from('tosca.relationships.AttachesTo'):
                 if value_n.is_derived_from('tosca.nodes.BlockStorage'):
-                    attach = True
+                    if volume_name == value_n.name:
+                        attach = True
             if attach:
                 relationship_tpl = None
                 for req in node.requirements:
index 1cd2c03..a83f019 100644 (file)
@@ -10,6 +10,7 @@ resources:
     properties:
       min_size: 2
       desired_capacity: 3
+      cooldown: 60
       resource:
         type: asg_res.yaml
       max_size: 10
@@ -20,6 +21,7 @@ resources:
         get_resource: asg_group
       adjustment_type: change_in_capacity
       scaling_adjustment: 1
+      cooldown: 60
   asg_scale_in:
     type: OS::Heat::ScalingPolicy
     properties:
@@ -27,6 +29,7 @@ resources:
         get_resource: asg_group
       adjustment_type: change_in_capacity
       scaling_adjustment: -1
+      cooldown: 60
   asg_alarm:
     type: OS::Aodh::Alarm
     properties:
index ca0fb3a..e0dbb7f 100644 (file)
@@ -12,6 +12,7 @@ resources:
       properties:\r
         flavor: m1.medium\r
         image: rhel-6.5-test-image\r
+        software_config_transport: POLL_SERVER_HEAT\r
         networks:\r
         - network: net0\r
   cluster_scaling_scale_out:\r
diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/asg_res.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/asg_res.yaml
new file mode 100644 (file)
index 0000000..d3415ea
--- /dev/null
@@ -0,0 +1,10 @@
+heat_template_version: 2013-05-23
+description: Tacker Scaling template
+resources:
+  my_server_1:
+    type: OS::Nova::Server
+    properties:
+      flavor: m1.medium
+      user_data_format: SOFTWARE_CONFIG
+      software_config_transport: POLL_SERVER_HEAT
+      image: rhel-6.5-test-image
diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/hot_monitoring_scaling.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/hot_monitoring_scaling.yaml
new file mode 100644 (file)
index 0000000..85ff54d
--- /dev/null
@@ -0,0 +1,53 @@
+heat_template_version: 2013-05-23
+
+description: >
+  Template for deploying servers based on policies.
+
+parameters: {}
+resources:
+  asg_scale_out:
+    type: OS::Heat::ScalingPolicy
+    properties:
+      auto_scaling_group_id:
+        get_resource: asg_group
+      adjustment_type: change_in_capacity
+      scaling_adjustment: 1
+      cooldown: 60
+  low_cpu_usage:
+    type: OS::Aodh::Alarm
+    properties:
+      meter_name: cpu_util
+      description: utilization less_than 20%
+      evaluation_periods: 1
+      period: 600
+      statistic: avg
+      threshold: 20
+      comparison_operator: gt
+  asg_group:
+    type: OS::Heat::AutoScalingGroup
+    properties:
+      min_size: 2
+      desired_capacity: 3
+      resource:
+        type: asg_res.yaml
+      max_size: 10
+      cooldown: 60
+  asg_scale_in:
+    type: OS::Heat::ScalingPolicy
+    properties:
+      auto_scaling_group_id:
+        get_resource: asg_group
+      adjustment_type: change_in_capacity
+      scaling_adjustment: -1
+      cooldown: 60
+  high_cpu_usage:
+    type: OS::Aodh::Alarm
+    properties:
+      meter_name: cpu_util
+      description: utilization greater_than 60%
+      evaluation_periods: 1
+      period: 600
+      statistic: avg
+      threshold: 60
+      comparison_operator: gt
+outputs: {}
index 7d1e2f6..dde597b 100644 (file)
@@ -12,6 +12,7 @@ resources:
         get_resource: SP1_group
       adjustment_type: change_in_capacity
       scaling_adjustment: 1
+      cooldown: 120
   SP1_group:
     type: OS::Heat::AutoScalingGroup
     properties:
@@ -20,6 +21,7 @@ resources:
       resource:
         type: SP1_res.yaml
       max_size: 3
+      cooldown: 120
   SP1_scale_in:
     type: OS::Heat::ScalingPolicy
     properties:
@@ -27,4 +29,5 @@ resources:
         get_resource: SP1_group
       adjustment_type: change_in_capacity
       scaling_adjustment: -1
+      cooldown: 120
 outputs: {}
\ No newline at end of file
diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_w_multiple_attachment.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_w_multiple_attachment.yaml
new file mode 100644 (file)
index 0000000..34f408e
--- /dev/null
@@ -0,0 +1,96 @@
+heat_template_version: 2013-05-23
+
+description: >
+  TOSCA simple profile with 1 server attached 2 block storages.
+
+parameters:
+  cpus:
+    type: number
+    description: Number of CPUs for the server.
+    default: 1
+    constraints:
+    - allowed_values:
+      - 1
+      - 2
+      - 4
+      - 8
+  storage_location1:
+    type: string
+    description: Block storage mount point (filesystem path).
+    default: /dev/vdb
+  storage_location2:
+    type: string
+    description: Block storage mount point (filesystem path).
+    default: /dev/vdc
+  storage_size:
+    type: number
+    description: Size of the storage to be created.
+    default: 1
+  storage_snapshot_id:
+    type: string
+    description: Optional identifier for an existing snapshot to use when creating storage.
+    default: ssid
+
+resources:
+  my_server:
+    type: OS::Nova::Server
+    properties:
+      flavor: m1.medium
+      image: fedora-amd64-heat-config
+      software_config_transport: POLL_SERVER_HEAT
+      user_data_format: SOFTWARE_CONFIG
+    depends_on:
+    - my_storage1
+    - my_storage2
+
+  my_storage1:
+    type: OS::Cinder::Volume
+    properties:
+      size:
+        get_param: storage_size
+      snapshot_id:
+        get_param: storage_snapshot_id
+
+  my_storage2:
+    type: OS::Cinder::Volume
+    properties:
+      size:
+        get_param: storage_size
+      snapshot_id:
+        get_param: storage_snapshot_id
+
+  attachesto_1:
+    type: OS::Cinder::VolumeAttachment
+    properties:
+      instance_uuid:
+        get_resource: my_server
+      mountpoint:
+        get_param: storage_location1
+      volume_id:
+        get_resource: my_storage1
+
+  attachesto_2:
+    type: OS::Cinder::VolumeAttachment
+    properties:
+      instance_uuid:
+        get_resource: my_server
+      mountpoint:
+        get_param: storage_location2
+      volume_id:
+        get_resource: my_storage2
+
+outputs:
+  server_ip:
+    description: The private IP address of the applications server.
+    value:
+      get_attr:
+      - my_server
+      - first_address
+  volume_id_1:
+    description: The volume id of the first block storage instance.
+    value:
+      get_resource: my_storage1
+  volume_id_2:
+    description: The volume id of the second block storage instance.
+    value:
+      get_resource: my_storage2
diff --git a/tosca2heat/heat-translator/translator/tests/data/monitoring/tosca_monitoring_scaling.yaml b/tosca2heat/heat-translator/translator/tests/data/monitoring/tosca_monitoring_scaling.yaml
new file mode 100644 (file)
index 0000000..0c36236
--- /dev/null
@@ -0,0 +1,60 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+  Template for deploying servers based on policies.
+
+topology_template:
+  node_templates:
+    my_server_1:
+      type: tosca.nodes.Compute
+      capabilities:
+        host:
+         properties:
+           num_cpus: 2
+           disk_size: 10 GB
+           mem_size: 512 MB
+        os:
+         properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: Linux
+            distribution: RHEL
+            version: 6.5
+  policies:
+    - asg:
+        type: tosca.policies.Scaling
+        description: Simple node autoscaling
+        targets: [my_server_1]
+        properties:
+          min_instances: 2
+          max_instances: 10
+          default_instances: 3
+          increment: 1
+          cooldown: 60
+
+    - cpu_monitoring:
+        type: tosca.policies.Monitoring
+        description: Simple node monitoring
+        targets: [my_server_1]
+        triggers:
+          high_cpu_usage:
+            description: trigger
+            meter_name: cpu_util
+            condition:
+              constraint: utilization greater_than 60%
+              threshold: 60
+              period: 600
+              evaluations: 1
+              method: average
+              comparison_operator: gt
+
+          low_cpu_usage:
+            description: trigger
+            meter_name: cpu_util
+            condition:
+              constraint: utilization less_than 20%
+              threshold: 20
+              period: 600
+              evaluations: 1
+              method: average
+              comparison_operator: gt
\ No newline at end of file
diff --git a/tosca2heat/heat-translator/translator/tests/data/storage/tosca_multiple_blockstorage_w_multiple_attachment.yaml b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_multiple_blockstorage_w_multiple_attachment.yaml
new file mode 100644 (file)
index 0000000..7eb7843
--- /dev/null
@@ -0,0 +1,79 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+  TOSCA simple profile with 1 server attached 2 block storages.
+
+topology_template:
+  inputs:
+    cpus:
+      type: integer
+      description: Number of CPUs for the server.
+      constraints:
+        - valid_values: [ 1, 2, 4, 8 ]
+    storage_size:
+      type: scalar-unit.size
+      default: 1 GB
+      description: Size of the storage to be created.
+    storage_snapshot_id:
+      type: string
+      description: >
+        Optional identifier for an existing snapshot to use when creating storage.
+    storage_location1:
+      type: string
+      description: >
+        Block storage mount point (filesystem path).
+    storage_location2:
+      type: string
+      description: >
+        Block storage mount point (filesystem path).
+
+  node_templates:
+    my_server:
+      type: tosca.nodes.Compute
+      capabilities:
+        host:
+          properties:
+            disk_size: 10 GB
+            num_cpus: { get_input: cpus }
+            mem_size: 4096 MB
+        os:
+          properties:
+            architecture: x86_64
+            type: Linux
+            distribution: Fedora
+            version: 18.0
+      requirements:
+        - local_storage1:
+            node: my_storage1
+            relationship:
+              type: AttachesTo
+              properties:
+                location: { get_input: storage_location1 }
+        - local_storage2:
+            node: my_storage2
+            relationship:
+              type: AttachesTo
+              properties:
+                location: { get_input: storage_location2 }
+    my_storage1:
+      type: tosca.nodes.BlockStorage
+      properties:
+        size: { get_input: storage_size }
+        snapshot_id: { get_input: storage_snapshot_id }
+
+    my_storage2:
+      type: tosca.nodes.BlockStorage
+      properties:
+        size: { get_input: storage_size }
+        snapshot_id: { get_input: storage_snapshot_id }
+
+  outputs:
+    server_ip:
+      description: The private IP address of the application's server.
+      value: { get_attribute: [my_server, private_address] }
+    volume_id_1:
+      description: The volume id of the first block storage instance.
+      value: { get_attribute: [my_storage1, volume_id] }
+    volume_id_2:
+      description: The volume id of the second block storage instance.
+      value: { get_attribute: [my_storage2, volume_id] }
index 6d0d316..95df72a 100644 (file)
@@ -26,14 +26,15 @@ from translator.tests.base import TestCase
 
 class ToscaHotTranslationTest(TestCase):
 
-    def _test_successful_translation(self, tosca_file, hot_files, params=None):
+    def _test_successful_translation(self, tosca_file, hot_files, params=None,
+                                     nested_resources=False):
         if not params:
             params = {}
         if not isinstance(hot_files, list):
             hot_files = [hot_files]
-        diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file,
-                                                                   hot_files,
-                                                                   params)
+        diff =\
+            TranslationUtils.compare_tosca_translation_with_hot(
+                tosca_file, hot_files, params, nested_resources)
         self.assertEqual({}, diff, '<difference> : ' +
                          json.dumps(diff, indent=4, separators=(', ', ': ')))
 
@@ -191,6 +192,18 @@ class ToscaHotTranslationTest(TestCase):
         except Exception:
             self._test_successful_translation(tosca_file, hot_file2, params)
 
+    def test_hot_translate_multiple_blockstorage_w_multiple_attachment(self):
+        tosca_file = '../tests/data/storage/' \
+                     'tosca_multiple_blockstorage_w_multiple_attachment.yaml'
+        hot_file = '../tests/data/hot_output/storage/' \
+                   'hot_multiple_blockstorage_w_multiple_attachment.yaml'
+        params = {'cpus': 1,
+                  'storage_location1': '/dev/vdb',
+                  'storage_location2': '/dev/vdc',
+                  'storage_size': '1 GB',
+                  'storage_snapshot_id': 'ssid'}
+        self._test_successful_translation(tosca_file, hot_file, params)
+
     def test_hot_translate_single_object_store(self):
         tosca_file = '../tests/data/storage/tosca_single_object_store.yaml'
         hot_file = '../tests/data/hot_output/hot_single_object_store.yaml'
@@ -515,6 +528,15 @@ class ToscaHotTranslationTest(TestCase):
         params = {}
         self._test_successful_translation(tosca_file, hot_files, params)
 
+    def test_hot_translate_scaling_nested_plate(self):
+        tosca_file = '../tests/data/autoscaling/tosca_autoscaling.yaml'
+        hot_files = [
+            '../tests/data/hot_output/autoscaling/asg_res.yaml'
+        ]
+        params = {}
+        self._test_successful_translation(tosca_file, hot_files, params,
+                                          nested_resources=True)
+
     def test_translate_unsupported_tosca_type(self):
         tosca_file = '../tests/data/test_tosca_unsupported_type.yaml'
         tosca_tpl = os.path.normpath(os.path.join(
@@ -528,7 +550,7 @@ class ToscaHotTranslationTest(TestCase):
                                 .translate)
         self.assertEqual(expected_msg, err.__str__())
 
-    def _translate_nodetemplates(self):
+    def test_hot_translate_cluster_scaling_policy(self):
         tosca_file = '../tests/data/autoscaling/tosca_cluster_autoscaling.yaml'
         hot_file = '../tests/data/hot_output/autoscaling/' \
                    'hot_cluster_autoscaling.yaml'
@@ -543,3 +565,12 @@ class ToscaHotTranslationTest(TestCase):
             ]
         params = {}
         self._test_successful_translation(tosca_file, hot_files, params)
+
+    def test_hot_translate_mon_scaling_policy(self):
+        tosca_file = '../tests/data/monitoring/tosca_monitoring_scaling.yaml'
+        hot_files = [
+            '../tests/data/hot_output/monitoring/hot_monitoring_scaling.yaml',
+            '../tests/data/hot_output/monitoring/asg_res.yaml',
+        ]
+        params = {}
+        self._test_successful_translation(tosca_file, hot_files, params)