summaryrefslogtreecommitdiffstats
path: root/tosca2heat/heat-translator/translator
diff options
context:
space:
mode:
Diffstat (limited to 'tosca2heat/heat-translator/translator')
-rw-r--r--tosca2heat/heat-translator/translator/common/images.py61
-rw-r--r--tosca2heat/heat-translator/translator/common/utils.py9
-rw-r--r--tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py3
-rw-r--r--tosca2heat/heat-translator/translator/hot/syntax/hot_template.py9
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_autoscaling.py2
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_floatingip.py71
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_cluster_policies_scaling.py195
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py52
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_floating.py48
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_monitoring.py82
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py3
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca_translator.py5
-rw-r--r--tosca2heat/heat-translator/translator/hot/translate_node_templates.py24
-rw-r--r--tosca2heat/heat-translator/translator/tests/data/autoscaling/tosca_autoscaling.yaml1
-rw-r--r--tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_autoscaling.yaml3
-rw-r--r--tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_cluster_autoscaling.yaml1
-rw-r--r--tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/asg_res.yaml10
-rw-r--r--tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/hot_monitoring_scaling.yaml53
-rw-r--r--tosca2heat/heat-translator/translator/tests/data/hot_output/nfv/hot_tosca_nfv_autoscaling.yaml3
-rw-r--r--tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_w_multiple_attachment.yaml96
-rw-r--r--tosca2heat/heat-translator/translator/tests/data/monitoring/tosca_monitoring_scaling.yaml60
-rw-r--r--tosca2heat/heat-translator/translator/tests/data/storage/tosca_multiple_blockstorage_w_multiple_attachment.yaml79
-rw-r--r--tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py41
23 files changed, 845 insertions, 66 deletions
diff --git a/tosca2heat/heat-translator/translator/common/images.py b/tosca2heat/heat-translator/translator/common/images.py
index f9fa4f1..d9b8818 100644
--- a/tosca2heat/heat-translator/translator/common/images.py
+++ b/tosca2heat/heat-translator/translator/common/images.py
@@ -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:
diff --git a/tosca2heat/heat-translator/translator/common/utils.py b/tosca2heat/heat-translator/translator/common/utils.py
index 874c8ec..85af60a 100644
--- a/tosca2heat/heat-translator/translator/common/utils.py
+++ b/tosca2heat/heat-translator/translator/common/utils.py
@@ -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] = \
diff --git a/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py b/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py
index 80a62ff..ff2111a 100644
--- a/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py
+++ b/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py
@@ -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')
diff --git a/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py b/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py
index 7fae022..f279997 100644
--- a/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py
+++ b/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py
@@ -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
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_autoscaling.py b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_autoscaling.py
index 978e965..f093c2e 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_autoscaling.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_autoscaling.py
@@ -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
index 0000000..445390d
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_floatingip.py
@@ -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
index 0000000..1de0158
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_cluster_policies_scaling.py
@@ -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
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
index 5f6b751..40dc799 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
@@ -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
index 0000000..6126653
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_floating.py
@@ -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
index 0000000..7aac1c7
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_monitoring.py
@@ -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
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py
index 1b63f24..d34acd4 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py
@@ -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'}
diff --git a/tosca2heat/heat-translator/translator/hot/tosca_translator.py b/tosca2heat/heat-translator/translator/hot/tosca_translator.py
index b9d4c77..a637a8c 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca_translator.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca_translator.py
@@ -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,
diff --git a/tosca2heat/heat-translator/translator/hot/translate_node_templates.py b/tosca2heat/heat-translator/translator/hot/translate_node_templates.py
index 1a1a4d7..78ab1c4 100644
--- a/tosca2heat/heat-translator/translator/hot/translate_node_templates.py
+++ b/tosca2heat/heat-translator/translator/hot/translate_node_templates.py
@@ -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:
diff --git a/tosca2heat/heat-translator/translator/tests/data/autoscaling/tosca_autoscaling.yaml b/tosca2heat/heat-translator/translator/tests/data/autoscaling/tosca_autoscaling.yaml
index f58d727..d5356be 100644
--- a/tosca2heat/heat-translator/translator/tests/data/autoscaling/tosca_autoscaling.yaml
+++ b/tosca2heat/heat-translator/translator/tests/data/autoscaling/tosca_autoscaling.yaml
@@ -38,3 +38,4 @@ topology_template:
max_instances: 10
default_instances: 3
increment: 1
+ cooldown: 60
diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_autoscaling.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_autoscaling.yaml
index 1cd2c03..a83f019 100644
--- a/tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_autoscaling.yaml
+++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_autoscaling.yaml
@@ -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:
diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_cluster_autoscaling.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_cluster_autoscaling.yaml
index ca0fb3a..e0dbb7f 100644
--- a/tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_cluster_autoscaling.yaml
+++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/autoscaling/hot_cluster_autoscaling.yaml
@@ -12,6 +12,7 @@ resources:
properties:
flavor: m1.medium
image: rhel-6.5-test-image
+ software_config_transport: POLL_SERVER_HEAT
networks:
- network: net0
cluster_scaling_scale_out:
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
index 0000000..d3415ea
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/asg_res.yaml
@@ -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
index 0000000..85ff54d
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/monitoring/hot_monitoring_scaling.yaml
@@ -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: {}
diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/nfv/hot_tosca_nfv_autoscaling.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/nfv/hot_tosca_nfv_autoscaling.yaml
index 7d1e2f6..dde597b 100644
--- a/tosca2heat/heat-translator/translator/tests/data/hot_output/nfv/hot_tosca_nfv_autoscaling.yaml
+++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/nfv/hot_tosca_nfv_autoscaling.yaml
@@ -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
index 0000000..34f408e
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_w_multiple_attachment.yaml
@@ -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
index 0000000..0c36236
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/tests/data/monitoring/tosca_monitoring_scaling.yaml
@@ -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
index 0000000..7eb7843
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_multiple_blockstorage_w_multiple_attachment.yaml
@@ -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] }
diff --git a/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py b/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py
index 6d0d316..95df72a 100644
--- a/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py
+++ b/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py
@@ -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)