summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tosca2heat/heat-translator/.python-version1
-rw-r--r--tosca2heat/heat-translator/CONTRIBUTING.rst16
-rw-r--r--tosca2heat/heat-translator/HACKING.rst4
-rw-r--r--tosca2heat/heat-translator/MANIFEST.in14
-rw-r--r--tosca2heat/heat-translator/README.rst (renamed from tosca2heat/heat-translator/README.md)0
-rw-r--r--tosca2heat/heat-translator/openstack-common.conf6
-rw-r--r--tosca2heat/heat-translator/requirements.txt16
-rw-r--r--tosca2heat/heat-translator/setup.cfg10
-rw-r--r--tosca2heat/heat-translator/setup.py2
-rw-r--r--tosca2heat/heat-translator/test-requirements.txt6
-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
-rw-r--r--tosca2heat/tosca-parser/CONTRIBUTING.rst16
-rw-r--r--tosca2heat/tosca-parser/HACKING.rst4
-rw-r--r--tosca2heat/tosca-parser/README.rst (renamed from tosca2heat/tosca-parser/README.md)0
-rw-r--r--tosca2heat/tosca-parser/requirements.txt8
-rw-r--r--tosca2heat/tosca-parser/setup.cfg10
-rw-r--r--tosca2heat/tosca-parser/setup.py2
-rw-r--r--tosca2heat/tosca-parser/test-requirements.txt6
-rw-r--r--tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml25
-rw-r--r--tosca2heat/tosca-parser/toscaparser/elements/nodetype.py2
-rw-r--r--tosca2heat/tosca-parser/toscaparser/elements/policytype.py2
-rw-r--r--tosca2heat/tosca-parser/toscaparser/functions.py57
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/container_cap_child.yaml33
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/custom_cap.yaml22
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml7
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_container_cap_child.yaml28
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml37
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml25
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/policies/tosca_policy_template.yaml26
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/test_custom_capabilty.yaml23
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/definitions.yaml2
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/test_functions.py24
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py2
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py53
-rw-r--r--tosca2heat/tosca-parser/toscaparser/triggers.py26
-rw-r--r--tosca2heat/tosca-parser/tox.ini5
58 files changed, 1292 insertions, 139 deletions
diff --git a/tosca2heat/heat-translator/.python-version b/tosca2heat/heat-translator/.python-version
new file mode 100644
index 0000000..ecc17b8
--- /dev/null
+++ b/tosca2heat/heat-translator/.python-version
@@ -0,0 +1 @@
+2.7.13
diff --git a/tosca2heat/heat-translator/CONTRIBUTING.rst b/tosca2heat/heat-translator/CONTRIBUTING.rst
new file mode 100644
index 0000000..e12c422
--- /dev/null
+++ b/tosca2heat/heat-translator/CONTRIBUTING.rst
@@ -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
index 0000000..9c60464
--- /dev/null
+++ b/tosca2heat/heat-translator/HACKING.rst
@@ -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
index 1fc29b3..0000000
--- a/tosca2heat/heat-translator/MANIFEST.in
+++ /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/README.md b/tosca2heat/heat-translator/README.rst
index c8af42a..c8af42a 100644
--- a/tosca2heat/heat-translator/README.md
+++ b/tosca2heat/heat-translator/README.rst
diff --git a/tosca2heat/heat-translator/openstack-common.conf b/tosca2heat/heat-translator/openstack-common.conf
deleted file mode 100644
index d359e40..0000000
--- a/tosca2heat/heat-translator/openstack-common.conf
+++ /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
diff --git a/tosca2heat/heat-translator/requirements.txt b/tosca2heat/heat-translator/requirements.txt
index b211383..2de8022 100644
--- a/tosca2heat/heat-translator/requirements.txt
+++ b/tosca2heat/heat-translator/requirements.txt
@@ -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
diff --git a/tosca2heat/heat-translator/setup.cfg b/tosca2heat/heat-translator/setup.cfg
index 38bc1b9..21d0c6f 100644
--- a/tosca2heat/heat-translator/setup.cfg
+++ b/tosca2heat/heat-translator/setup.cfg
@@ -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
diff --git a/tosca2heat/heat-translator/setup.py b/tosca2heat/heat-translator/setup.py
index 782bb21..566d844 100644
--- a/tosca2heat/heat-translator/setup.py
+++ b/tosca2heat/heat-translator/setup.py
@@ -25,5 +25,5 @@ except ImportError:
pass
setuptools.setup(
- setup_requires=['pbr>=1.8'],
+ setup_requires=['pbr>=2.0.0'],
pbr=True)
diff --git a/tosca2heat/heat-translator/test-requirements.txt b/tosca2heat/heat-translator/test-requirements.txt
index d4b59c3..da69d4f 100644
--- a/tosca2heat/heat-translator/test-requirements.txt
+++ b/tosca2heat/heat-translator/test-requirements.txt
@@ -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
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)
diff --git a/tosca2heat/tosca-parser/CONTRIBUTING.rst b/tosca2heat/tosca-parser/CONTRIBUTING.rst
new file mode 100644
index 0000000..7366825
--- /dev/null
+++ b/tosca2heat/tosca-parser/CONTRIBUTING.rst
@@ -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/tosca-parser \ No newline at end of file
diff --git a/tosca2heat/tosca-parser/HACKING.rst b/tosca2heat/tosca-parser/HACKING.rst
new file mode 100644
index 0000000..60505f1
--- /dev/null
+++ b/tosca2heat/tosca-parser/HACKING.rst
@@ -0,0 +1,4 @@
+tosca-parser Style Commandments
+===============================================
+
+Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ \ No newline at end of file
diff --git a/tosca2heat/tosca-parser/README.md b/tosca2heat/tosca-parser/README.rst
index 0f94072..0f94072 100644
--- a/tosca2heat/tosca-parser/README.md
+++ b/tosca2heat/tosca-parser/README.rst
diff --git a/tosca2heat/tosca-parser/requirements.txt b/tosca2heat/tosca-parser/requirements.txt
index 45a19f0..807d749 100644
--- a/tosca2heat/tosca-parser/requirements.txt
+++ b/tosca2heat/tosca-parser/requirements.txt
@@ -1,10 +1,10 @@
# 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
-requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
+requests>=2.14.2 # Apache-2.0
diff --git a/tosca2heat/tosca-parser/setup.cfg b/tosca2heat/tosca-parser/setup.cfg
index 45a5ae7..77e1b2e 100644
--- a/tosca2heat/tosca-parser/setup.cfg
+++ b/tosca2heat/tosca-parser/setup.cfg
@@ -3,7 +3,7 @@ name = tosca-parser
url = https://launchpad.net/tosca-parser
summary = Parser for TOSCA Simple Profile in YAML.
description-file =
- README.md
+ README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://docs.openstack.org/developer/tosca-parser/
@@ -28,6 +28,14 @@ packages =
console_scripts =
tosca-parser = toscaparser.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 = toscaparser/locale
domain = toscaparser
diff --git a/tosca2heat/tosca-parser/setup.py b/tosca2heat/tosca-parser/setup.py
index 782bb21..566d844 100644
--- a/tosca2heat/tosca-parser/setup.py
+++ b/tosca2heat/tosca-parser/setup.py
@@ -25,5 +25,5 @@ except ImportError:
pass
setuptools.setup(
- setup_requires=['pbr>=1.8'],
+ setup_requires=['pbr>=2.0.0'],
pbr=True)
diff --git a/tosca2heat/tosca-parser/test-requirements.txt b/tosca2heat/tosca-parser/test-requirements.txt
index d4b59c3..da69d4f 100644
--- a/tosca2heat/tosca-parser/test-requirements.txt
+++ b/tosca2heat/tosca-parser/test-requirements.txt
@@ -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
diff --git a/tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml b/tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml
index 9f3369e..57c9bf9 100644
--- a/tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml
@@ -339,6 +339,26 @@ node_types:
relationship: tosca.relationships.network.LinksTo
node: tosca.nodes.network.Network
+ tosca.nodes.network.FloatingIP:
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA FloatingIP node represents a floating IP that can associate to a Port.
+ properties:
+ floating_network:
+ type: string
+ required: true
+ floating_ip_address:
+ type: string
+ required: false
+ port_id:
+ type: string
+ required: false
+ requirements:
+ - link:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ node: tosca.nodes.network.Port
+
tosca.nodes.ObjectStorage:
derived_from: tosca.nodes.Root
description: >
@@ -928,6 +948,11 @@ policy_types:
description: The TOSCA Policy Type definition that is used to govern
scaling of TOSCA nodes or groups of nodes.
+ tosca.policies.Monitoring:
+ derived_from: tosca.policies.Root
+ description: The TOSCA Policy Type definition that is used to govern
+ monitoring of TOSCA nodes or groups of nodes.
+
tosca.policies.Update:
derived_from: tosca.policies.Root
description: The TOSCA Policy Type definition that is used to govern
diff --git a/tosca2heat/tosca-parser/toscaparser/elements/nodetype.py b/tosca2heat/tosca-parser/toscaparser/elements/nodetype.py
index 7f3da2d..4889ed8 100644
--- a/tosca2heat/tosca-parser/toscaparser/elements/nodetype.py
+++ b/tosca2heat/tosca-parser/toscaparser/elements/nodetype.py
@@ -83,8 +83,6 @@ class NodeType(StatefulEntityType):
captype = value['capability']
value = (self.
_get_node_type_by_cap(captype))
- # _get_node_type_by_cap(key, captype))
- # relation = self._get_relation(key, value)
keyword = key
node_type = value
rtype = RelationshipType(relation, keyword, self.custom_def)
diff --git a/tosca2heat/tosca-parser/toscaparser/elements/policytype.py b/tosca2heat/tosca-parser/toscaparser/elements/policytype.py
index a922d26..82aed0a 100644
--- a/tosca2heat/tosca-parser/toscaparser/elements/policytype.py
+++ b/tosca2heat/tosca-parser/toscaparser/elements/policytype.py
@@ -113,7 +113,7 @@ class PolicyType(StatefulEntityType):
for entry_schema, entry_schema_type in meta_data.items():
if isinstance(entry_schema_type, dict) and not \
- entry_schema_type.get('type') == 'string':
+ entry_schema_type.get('type') == 'string':
ExceptionCollector.appendException(
InvalidTypeError(what='"%s" defined in policy for '
'metadata "%s"'
diff --git a/tosca2heat/tosca-parser/toscaparser/functions.py b/tosca2heat/tosca-parser/toscaparser/functions.py
index d498229..b64f1bc 100644
--- a/tosca2heat/tosca-parser/toscaparser/functions.py
+++ b/tosca2heat/tosca-parser/toscaparser/functions.py
@@ -158,6 +158,8 @@ class GetAttribute(Function):
# then check the req or caps
attr = self._find_req_or_cap_attribute(self.args[1],
self.args[2])
+ if not attr:
+ return
value_type = attr.schema['type']
if len(self.args) > index:
@@ -236,8 +238,8 @@ class GetAttribute(Function):
target_node = self._find_node_template(target_name)
target_type = target_node.type_definition
for capability in target_type.get_capabilities_objects():
- if capability.type in \
- hosted_on_rel['valid_target_types']:
+ if capability.inherits_from(
+ hosted_on_rel['valid_target_types']):
if self._attribute_exists_in_type(target_type):
return target_node
return self._find_host_containing_attribute(
@@ -412,6 +414,8 @@ class GetProperty(Function):
def _find_req_or_cap_property(self, req_or_cap, property_name):
node_tpl = self._find_node_template(self.args[0])
+ if node_tpl is None:
+ return None
# Find property in node template's requirements
for r in node_tpl.requirements:
for req, node_name in r.items():
@@ -429,7 +433,8 @@ class GetProperty(Function):
def _get_capability_property(self,
node_template,
capability_name,
- property_name):
+ property_name,
+ throw_errors=True):
"""Gets a node template capability property."""
caps = node_template.get_capabilities()
if caps and capability_name in caps.keys():
@@ -438,7 +443,7 @@ class GetProperty(Function):
props = cap.get_properties()
if props and property_name in props.keys():
property = props[property_name].value
- if not property:
+ if property is None and throw_errors:
ExceptionCollector.appendException(
KeyError(_('Property "%(prop)s" was not found in '
'capability "%(cap)s" of node template '
@@ -448,12 +453,15 @@ class GetProperty(Function):
'ntpl1': node_template.name,
'ntpl2': self.context.name}))
return property
- msg = _('Requirement/Capability "{0}" referenced from node template '
- '"{1}" was not found in node template "{2}".').format(
- capability_name,
- self.context.name,
- node_template.name)
- ExceptionCollector.appendException(KeyError(msg))
+ if throw_errors:
+ msg = _('Requirement/Capability "{0}" referenced from '
+ 'node template "{1}" was not found in node template'
+ ' "{2}".').format(capability_name,
+ self.context.name,
+ node_template.name)
+ ExceptionCollector.appendException(KeyError(msg))
+ else:
+ return None
def _find_property(self, property_name):
node_tpl = self._find_node_template(self.args[0])
@@ -475,7 +483,18 @@ class GetProperty(Function):
return self.context
# enable the HOST value in the function
if node_template_name == HOST:
- return self._find_host_containing_property()
+ node = self._find_host_containing_property()
+ if node is None:
+ ExceptionCollector.appendException(
+ KeyError(_(
+ "Property '{0}' not found in capability/requirement"
+ " '{1}' referenced from node template {2}").
+ format(self.args[2],
+ self.args[1],
+ self.context.name)))
+ return None
+ else:
+ return node
if node_template_name == TARGET:
if not isinstance(self.context.type_definition, RelationshipType):
ExceptionCollector.appendException(
@@ -498,7 +517,9 @@ class GetProperty(Function):
ExceptionCollector.appendException(
KeyError(_(
'Node template "{0}" was not found.'
- ).format(node_template_name)))
+ ' referenced from node template {1}'
+ ).format(node_template_name,
+ self.context.name)))
def _get_index_value(self, value, index):
if isinstance(value, list):
@@ -555,9 +576,19 @@ class GetProperty(Function):
target_node = self._find_node_template(target_name)
target_type = target_node.type_definition
for capability in target_type.get_capabilities_objects():
- if capability.type in hosted_on_rel['valid_target_types']:
+ if capability.inherits_from(
+ hosted_on_rel['valid_target_types']):
if self._property_exists_in_type(target_type):
return target_node
+ # If requirement was not found, look in node
+ # template's capabilities
+ if (len(self.args) > 2 and
+ self._get_capability_property(target_node,
+ self.args[1],
+ self.args[2],
+ False)
+ is not None):
+ return target_node
return self._find_host_containing_property(
target_name)
return None
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/container_cap_child.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/container_cap_child.yaml
new file mode 100644
index 0000000..1df09dd
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/container_cap_child.yaml
@@ -0,0 +1,33 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ Define a capability class that inherits from tosca.capabilities.Container
+
+capability_types:
+
+ tosca.capabilities.ContainerChild:
+ derived_from: tosca.capabilities.Container
+
+node_types:
+
+ tosca.nodes.SomeNode:
+ derived_from: tosca.nodes.Root
+ properties:
+ some_prop:
+ type: string
+ requirements:
+ - host_child:
+ capability: tosca.capabilities.ContainerChild
+ node: tosca.nodes.SomeNode2
+ relationship: tosca.relationships.HostedOn
+
+ tosca.nodes.SomeNode2:
+ derived_from: tosca.nodes.Root
+ capabilities:
+ host_child:
+ type: tosca.capabilities.ContainerChild
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/custom_cap.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/custom_cap.yaml
new file mode 100644
index 0000000..018bcf6
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/custom_cap.yaml
@@ -0,0 +1,22 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+capability_types:
+
+ tosca.capabilities.SomeCap:
+ derived_from: tosca.capabilities.Container
+
+node_types:
+
+ tosca.nodes.NodeWithReq:
+ derived_from: tosca.nodes.SoftwareComponent
+ requirements:
+ - host:
+ capability: tosca.capabilities.SomeCap
+ relationship: tosca.relationships.HostedOn
+ occurrences: [1, 1]
+
+ tosca.nodes.NodeWithCap:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ host:
+ type: tosca.capabilities.SomeCap
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml
index b17513f..332f830 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml
@@ -6,7 +6,7 @@ description: >
capability_types:
tosca.capabilities.SomeCap:
- derived_from: tosca.capabilities.Root
+ derived_from: tosca.capabilities.Container
properties:
type:
type: string
@@ -19,6 +19,11 @@ node_types:
tosca.nodes.SomeNode:
derived_from: tosca.nodes.Root
+ properties:
+ some_prop:
+ type: string
+ required: false
+ default: some
requirements:
- some_req:
capability: tosca.capabilities.SomeCap
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_container_cap_child.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_container_cap_child.yaml
new file mode 100644
index 0000000..84118c8
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_container_cap_child.yaml
@@ -0,0 +1,28 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ TOSCA simple profile to test the get attribute function with HOST parameter
+ using a node that has as capability a child class of Container
+
+imports:
+ - ../custom_types/container_cap_child.yaml
+
+topology_template:
+
+ node_templates:
+
+ test_node:
+ type: tosca.nodes.SomeNode
+ properties:
+ some_prop: { get_attribute: [ HOST, public_address ] }
+ requirements:
+ - host_child: test_node2
+
+ test_node2:
+ type: tosca.nodes.SomeNode2
+ requirements:
+ - host: server
+
+ server:
+ type: tosca.nodes.Compute
+
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml
new file mode 100644
index 0000000..d9c4c1c
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml
@@ -0,0 +1,37 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA test for boolean properties
+
+node_types:
+
+ tosca.nodes.SoftwareComponentTest:
+ derived_from: tosca.nodes.SoftwareComponent
+ properties:
+ some_prop:
+ type: boolean
+
+ tosca.nodes.ComputeTest:
+ derived_from: tosca.nodes.Compute
+ capabilities:
+ endpoint:
+ type: tosca.capabilities.Endpoint
+
+topology_template:
+
+ node_templates:
+
+ software:
+ type: tosca.nodes.SoftwareComponentTest
+ properties:
+ some_prop: { get_property: [ HOST, endpoint, secure ] }
+ requirements:
+ - host: server
+
+ server:
+ type: tosca.nodes.ComputeTest
+ capabilities:
+ endpoint:
+ properties:
+ network_name: PUBLIC
+ secure: false
+
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml
new file mode 100644
index 0000000..7fcb4a7
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml
@@ -0,0 +1,25 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA test for the IM
+
+imports:
+ - test: ../custom_types/node_with_cap.yaml
+
+topology_template:
+
+ node_templates:
+
+ some_node:
+ type: tosca.nodes.SomeNode
+ properties:
+ some_prop: { get_property: [ HOST, some_req, type ] }
+ requirements:
+ - some_req: server
+
+ server:
+ type: tosca.nodes.NodeWithCap
+ capabilities:
+ some_req:
+ properties:
+ type: someval
+
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/policies/tosca_policy_template.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/policies/tosca_policy_template.yaml
index 47f7870..4c18d9d 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/data/policies/tosca_policy_template.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/policies/tosca_policy_template.yaml
@@ -75,6 +75,32 @@ topology_template:
inputs:
strategy: LEAST_USED
implementation: Senlin.webhook()
+ 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
+ metadata: SG1
+ action: [SP1]
+
+ 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
+ metadata: SG1
+ action: [SP1]
+
- my_groups_placement:
type: mycompany.mytypes.myScalingPolicy
targets: [ webserver_group ]
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/test_custom_capabilty.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/test_custom_capabilty.yaml
new file mode 100644
index 0000000..03a8a07
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/test_custom_capabilty.yaml
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA simple profile to test a custom defined capability
+
+imports:
+ - custom_types/custom_cap.yaml
+
+topology_template:
+
+ node_templates:
+
+ node_req:
+ type: tosca.nodes.NodeWithReq
+ requirements:
+ - host: node_cap
+
+ node_cap:
+ type: tosca.nodes.NodeWithCap
+ requirements:
+ - host: server
+
+ server:
+ type: tosca.nodes.Compute
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/definitions.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/definitions.yaml
index ba5eac1..300bb8a 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/definitions.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/definitions.yaml
@@ -49,7 +49,7 @@ node_types:
derived_from: tosca.nodes.Database
example.SomeApp:
- derived_from: tosca.nodes.SoftwareComponent
+ derived_from: tosca.nodes.WebApplication
properties:
admin_user:
type: string
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/test_functions.py b/tosca2heat/tosca-parser/toscaparser/tests/test_functions.py
index fa60140..2e1d71e 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/test_functions.py
+++ b/tosca2heat/tosca-parser/toscaparser/tests/test_functions.py
@@ -188,6 +188,26 @@ class IntrinsicFunctionsTest(TestCase):
self.assertIsInstance(source_port, functions.GetProperty)
self.assertEqual(3306, source_port.result())
+ def test_get_prop_cap_host(self):
+ tosca_tpl = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/functions/test_get_prop_cap_host.yaml")
+ some_node = self._get_node('some_node',
+ ToscaTemplate(tosca_tpl))
+ some_prop = some_node.get_properties()['some_prop']
+ self.assertIsInstance(some_prop.value, functions.GetProperty)
+ self.assertEqual('someval', some_prop.value.result())
+
+ def test_get_prop_cap_bool(self):
+ tosca_tpl = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/functions/test_get_prop_cap_bool.yaml")
+ some_node = self._get_node('software',
+ ToscaTemplate(tosca_tpl))
+ some_prop = some_node.get_properties()['some_prop']
+ self.assertIsInstance(some_prop.value, functions.GetProperty)
+ self.assertEqual(False, some_prop.value.result())
+
class GetAttributeTest(TestCase):
@@ -318,6 +338,10 @@ class GetAttributeTest(TestCase):
self.assertIsNotNone(self._load_template(
'functions/test_get_implicit_attribute.yaml'))
+ def test_get_attribute_capability_inheritance(self):
+ self.assertIsNotNone(self._load_template(
+ 'functions/test_container_cap_child.yaml'))
+
class ConcatTest(TestCase):
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py b/tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py
index 3aabc9b..a0d6dc3 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py
+++ b/tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py
@@ -91,7 +91,7 @@ class TopologyTemplateTest(TestCase):
tpl_name = "app"
expected_type = "example.SomeApp"
expected_properties = ['admin_user', 'pool_size']
- expected_capabilities = ['feature', 'message_receiver']
+ expected_capabilities = ['app_endpoint', 'feature', 'message_receiver']
expected_requirements = [{'host': {'node': 'websrv'}}]
expected_relationshp = ['tosca.relationships.HostedOn']
expected_host = ['websrv']
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py b/tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py
index 911867f..f7c22ab 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py
+++ b/tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py
@@ -1581,7 +1581,7 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml
lambda: Policy(name, policies[name], None, None))
self.assertEqual(expectedmessage, err.__str__())
- def test_policy_trigger_valid_keyname(self):
+ def test_policy_trigger_valid_keyname_senlin_resources(self):
tpl_snippet = '''
triggers:
- resize_compute:
@@ -1610,7 +1610,28 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml
name = list(triggers.keys())[0]
Triggers(name, triggers[name])
- def test_policy_trigger_invalid_keyname(self):
+ def test_policy_trigger_valid_keyname_heat_resources(self):
+ tpl_snippet = '''
+ 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
+ metadata: SG1
+ action: [SP1]
+ '''
+ triggers = (toscaparser.utils.yamlparser.
+ simple_parse(tpl_snippet))['triggers'][0]
+ name = list(triggers.keys())[0]
+ Triggers(name, triggers[name])
+
+ def test_policy_trigger_invalid_keyname_senlin_resources(self):
tpl_snippet = '''
triggers:
- resize_compute:
@@ -1646,6 +1667,34 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml
lambda: Triggers(name, triggers[name]))
self.assertEqual(expectedmessage, err.__str__())
+ def test_policy_trigger_invalid_keyname_heat_resources(self):
+ tpl_snippet = '''
+ 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
+ metadata1: SG1
+ action: [SP1]
+ '''
+ triggers = (toscaparser.utils.yamlparser.
+ simple_parse(tpl_snippet))['triggers'][0]
+ name = list(triggers.keys())[0]
+ expectedmessage = _(
+ 'Triggers "high_cpu_usage" contains unknown field '
+ '"metadata1". Refer to the definition '
+ 'to verify valid values.')
+ err = self.assertRaises(
+ exception.UnknownFieldError,
+ lambda: Triggers(name, triggers[name]))
+ self.assertEqual(expectedmessage, err.__str__())
+
def test_policy_missing_required_keyname(self):
tpl_snippet = '''
policies:
diff --git a/tosca2heat/tosca-parser/toscaparser/triggers.py b/tosca2heat/tosca-parser/toscaparser/triggers.py
index 9edeef4..8e47eee 100644
--- a/tosca2heat/tosca-parser/toscaparser/triggers.py
+++ b/tosca2heat/tosca-parser/toscaparser/triggers.py
@@ -16,12 +16,16 @@ import logging
from toscaparser.common.exception import ExceptionCollector
from toscaparser.common.exception import UnknownFieldError
from toscaparser.entity_template import EntityTemplate
-
-SECTIONS = (DESCRIPTION, EVENT, SCHEDULE, TARGET_FILTER, CONDITION, ACTION) = \
- ('description', 'event_type', 'schedule',
- 'target_filter', 'condition', 'action')
-CONDITION_KEYNAMES = (CONTRAINT, PERIOD, EVALUATIONS, METHOD) = \
- ('constraint', 'period', 'evaluations', 'method')
+from toscaparser.utils import validateutils
+
+SECTIONS = (DESCRIPTION, EVENT, SCHEDULE, METER_NAME, METADATA,
+ TARGET_FILTER, CONDITION, ACTION) = \
+ ('description', 'event_type', 'schedule', 'meter_name',
+ 'metadata', 'target_filter', 'condition', 'action')
+CONDITION_KEYNAMES = (CONSTRAINT, PERIOD, EVALUATIONS, METHOD,
+ THRESHOLD, COMPARISON_OPERATOR) = \
+ ('constraint', 'period', 'evaluations',
+ 'method', 'threshold', 'comparison_operator')
log = logging.getLogger('tosca')
@@ -34,6 +38,7 @@ class Triggers(EntityTemplate):
self.trigger_tpl = trigger_tpl
self._validate_keys()
self._validate_condition()
+ self._validate_input()
def get_description(self):
return self.trigger_tpl['description']
@@ -66,3 +71,12 @@ class Triggers(EntityTemplate):
ExceptionCollector.appendException(
UnknownFieldError(what='Triggers "%s"' % self.name,
field=key))
+
+ def _validate_input(self):
+ for key, value in self.get_condition().items():
+ if key in [PERIOD, EVALUATIONS]:
+ validateutils.validate_integer(value)
+ elif key == THRESHOLD:
+ validateutils.validate_numeric(value)
+ elif key in [METER_NAME, METHOD]:
+ validateutils.validate_string(value)
diff --git a/tosca2heat/tosca-parser/tox.ini b/tosca2heat/tosca-parser/tox.ini
index 8fc2a88..3603a18 100644
--- a/tosca2heat/tosca-parser/tox.ini
+++ b/tosca2heat/tosca-parser/tox.ini
@@ -1,5 +1,5 @@
[tox]
-minversion = 1.6
+minversion = 2.0
envlist = py27,pep8
skipsdist = True
@@ -28,9 +28,6 @@ commands = python setup.py build_sphinx
commands = oslo_debug_helper -t toscaparser/tests {posargs}
[flake8]
-# H803 skipped on purpose per list discussion.
-# E123, E125 skipped as they are invalid PEP-8.
-
show-source = True
ignore = E123,E125,H803
builtins = _