From f7b240c6893a48d71da29974e54cb560b6575eb3 Mon Sep 17 00:00:00 2001 From: shangxdy Date: Sun, 26 Feb 2017 16:22:30 +0800 Subject: Sync heat-translator code Sync heat-translator code from upstream JIRA:PARSER-119 Change-Id: I2287b78ad38bc54f7740fd1ee5f08989d5e680bf Signed-off-by: shangxdy --- .../hot/tosca/tests/test_tosca_autoscaling.py | 91 +++++++++++ .../hot/tosca/tests/test_tosca_compute.py | 134 +++++------------ .../translator/hot/tosca/tosca_block_storage.py | 7 +- .../hot/tosca/tosca_block_storage_attachment.py | 8 +- .../translator/hot/tosca/tosca_compute.py | 167 ++++----------------- .../translator/hot/tosca/tosca_database.py | 4 +- .../translator/hot/tosca/tosca_dbms.py | 4 +- .../translator/hot/tosca/tosca_network_network.py | 7 +- .../translator/hot/tosca/tosca_network_port.py | 5 +- .../translator/hot/tosca/tosca_object_storage.py | 5 +- .../translator/hot/tosca/tosca_policies.py | 5 +- .../translator/hot/tosca/tosca_policies_scaling.py | 131 ++++++++++++++++ .../hot/tosca/tosca_software_component.py | 5 +- .../translator/hot/tosca/tosca_web_application.py | 5 +- .../translator/hot/tosca/tosca_webserver.py | 5 +- 15 files changed, 323 insertions(+), 260 deletions(-) create mode 100644 tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_autoscaling.py create mode 100644 tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py (limited to 'tosca2heat/heat-translator/translator/hot/tosca') 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 new file mode 100644 index 0000000..978e965 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_autoscaling.py @@ -0,0 +1,91 @@ +# 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.policy import Policy +from toscaparser.tests.base import TestCase +import toscaparser.utils.yamlparser +from translator.hot.tosca.tosca_compute import ToscaCompute +from translator.hot.tosca.tosca_policies_scaling import ToscaAutoscaling + + +class AutoscalingTest(TestCase): + + def _tosca_scaling_test(self, tpl_snippet, expectedprops): + nodetemplates = (toscaparser.utils.yamlparser. + simple_parse(tpl_snippet)['node_templates']) + policies = (toscaparser.utils.yamlparser. + simple_parse(tpl_snippet)['policies']) + name = list(nodetemplates.keys())[0] + policy_name = list(policies[0].keys())[0] + for policy in policies: + tpl = policy[policy_name] + targets = tpl["targets"] + properties = tpl["properties"] + try: + nodetemplate = NodeTemplate(name, nodetemplates) + toscacompute = ToscaCompute(nodetemplate) + toscacompute.handle_properties() + policy = Policy(policy_name, tpl, targets, + properties, "node_templates") + toscascaling = ToscaAutoscaling(policy) + parameters = toscascaling.handle_properties([toscacompute]) + self.assertEqual(parameters[0].properties, expectedprops) + except Exception: + raise + + def test_compute_with_scaling(self): + tpl_snippet = ''' + 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] + triggers: + resize_compute: + description: trigger + condition: + constraint: utilization greater_than 50% + period: 60 + evaluations: 1 + method: average + properties: + min_instances: 2 + max_instances: 10 + default_instances: 3 + increment: 1 + ''' + + expectedprops = {'desired_capacity': 3, + 'max_size': 10, + 'min_size': 2, + 'resource': {'type': 'asg_res.yaml'}} + + self._tosca_scaling_test( + tpl_snippet, + expectedprops) diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py index 408ee8b..1a135f4 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py @@ -10,13 +10,10 @@ # License for the specific language governing permissions and limitations # under the License. -import json import mock -from mock import patch from toscaparser.nodetemplate import NodeTemplate from toscaparser.tests.base import TestCase -from toscaparser.utils.gettextutils import _ import toscaparser.utils.yamlparser from translator.hot.tosca.tosca_compute import ToscaCompute @@ -27,22 +24,12 @@ class ToscaComputeTest(TestCase): nodetemplates = (toscaparser.utils.yamlparser. simple_parse(tpl_snippet)['node_templates']) name = list(nodetemplates.keys())[0] - try: - nodetemplate = NodeTemplate(name, nodetemplates) - nodetemplate.validate() - toscacompute = ToscaCompute(nodetemplate) - toscacompute.handle_properties() - if not self._compare_properties(toscacompute.properties, - expectedprops): - raise Exception(_("Hot Properties are not" - " same as expected properties")) - except Exception: - # for time being rethrowing. Will be handled future based - # on new development in Glance and Graffiti - raise + nodetemplate = NodeTemplate(name, nodetemplates) + nodetemplate.validate() + toscacompute = ToscaCompute(nodetemplate) + toscacompute.handle_properties() - def _compare_properties(self, hotprops, expectedprops): - return all(item in hotprops.items() for item in expectedprops.items()) + self.assertEqual(expectedprops, toscacompute.properties) def test_node_compute_with_host_and_os_capabilities(self): tpl_snippet = ''' @@ -84,7 +71,6 @@ class ToscaComputeTest(TestCase): #left intentionally ''' expectedprops = {'flavor': 'm1.large', - 'image': None, 'user_data_format': 'SOFTWARE_CONFIG', 'software_config_transport': 'POLL_SERVER_HEAT'} self._tosca_compute_test( @@ -123,7 +109,6 @@ class ToscaComputeTest(TestCase): #left intentionally ''' expectedprops = {'flavor': None, - 'image': None, 'user_data_format': 'SOFTWARE_CONFIG', 'software_config_transport': 'POLL_SERVER_HEAT'} self._tosca_compute_test( @@ -137,7 +122,6 @@ class ToscaComputeTest(TestCase): type: tosca.nodes.Compute ''' expectedprops = {'flavor': None, - 'image': None, 'user_data_format': 'SOFTWARE_CONFIG', 'software_config_transport': 'POLL_SERVER_HEAT'} self._tosca_compute_test( @@ -155,7 +139,6 @@ class ToscaComputeTest(TestCase): #left intentionally ''' expectedprops = {'flavor': None, - 'image': None, 'user_data_format': 'SOFTWARE_CONFIG', 'software_config_transport': 'POLL_SERVER_HEAT'} self._tosca_compute_test( @@ -174,7 +157,6 @@ class ToscaComputeTest(TestCase): mem_size: 4 GB ''' expectedprops = {'flavor': 'm1.large', - 'image': None, 'user_data_format': 'SOFTWARE_CONFIG', 'software_config_transport': 'POLL_SERVER_HEAT'} self._tosca_compute_test( @@ -193,7 +175,6 @@ class ToscaComputeTest(TestCase): disk_size: 10 GB ''' expectedprops = {'flavor': 'm1.large', - 'image': None, 'user_data_format': 'SOFTWARE_CONFIG', 'software_config_transport': 'POLL_SERVER_HEAT'} self._tosca_compute_test( @@ -211,18 +192,14 @@ class ToscaComputeTest(TestCase): num_cpus: 4 ''' expectedprops = {'flavor': 'm1.large', - 'image': None, 'user_data_format': 'SOFTWARE_CONFIG', 'software_config_transport': 'POLL_SERVER_HEAT'} self._tosca_compute_test( tpl_snippet, expectedprops) - @patch('requests.post') - @patch('requests.get') - @patch('os.getenv') - def test_node_compute_with_nova_flavor(self, mock_os_getenv, - mock_get, mock_post): + @mock.patch('translator.common.flavors.get_flavors') + def test_node_compute_with_nova_flavor(self, mock_flavor): tpl_snippet = ''' node_templates: server: @@ -234,56 +211,19 @@ class ToscaComputeTest(TestCase): disk_size: 1 GB mem_size: 1 GB ''' - with patch('translator.common.utils.' - 'check_for_env_variables') as mock_check_env: - mock_check_env.return_value = True - mock_os_getenv.side_effect = ['demo', 'demo', - 'demo', 'http://abc.com/5000/', - 'demo', 'demo', - 'demo', 'http://abc.com/5000/'] - mock_ks_response = mock.MagicMock() - mock_ks_response.status_code = 200 - mock_ks_content = { - 'access': { - 'token': { - 'id': 'd1dfa603-3662-47e0-b0b6-3ae7914bdf76' - }, - 'serviceCatalog': [{ - 'type': 'compute', - 'endpoints': [{ - 'publicURL': 'http://abc.com' - }] - }] - } - } - mock_ks_response.content = json.dumps(mock_ks_content) - mock_nova_response = mock.MagicMock() - mock_nova_response.status_code = 200 - mock_flavor_content = { - 'flavors': [{ - 'name': 'm1.mock_flavor', - 'ram': 1024, - 'disk': 1, - 'vcpus': 1 - }] - } - mock_nova_response.content = \ - json.dumps(mock_flavor_content) - mock_post.return_value = mock_ks_response - mock_get.return_value = mock_nova_response - expectedprops = {'flavor': 'm1.mock_flavor', - 'image': None, - 'user_data_format': 'SOFTWARE_CONFIG', - 'software_config_transport': 'POLL_SERVER_HEAT'} - self._tosca_compute_test( - tpl_snippet, - expectedprops) + mock_flavor.return_value = { + 'm1.mock_flavor': { + 'mem_size': 1024, + 'disk_size': 1, + 'num_cpus': 1} + } + expectedprops = {'flavor': 'm1.mock_flavor', + 'user_data_format': 'SOFTWARE_CONFIG', + 'software_config_transport': 'POLL_SERVER_HEAT'} + self._tosca_compute_test(tpl_snippet, expectedprops) - @patch('requests.post') - @patch('requests.get') - @patch('os.getenv') - def test_node_compute_without_nova_flavor(self, mock_os_getenv, - mock_get, mock_post): + @mock.patch('translator.common.images.get_images') + def test_node_compute_with_glance_image(self, mock_images): tpl_snippet = ''' node_templates: server: @@ -294,19 +234,25 @@ class ToscaComputeTest(TestCase): num_cpus: 1 disk_size: 1 GB mem_size: 1 GB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fake Distribution + version: 19.0 ''' - with patch('translator.common.utils.' - 'check_for_env_variables') as mock_check_env: - mock_check_env.return_value = True - mock_os_getenv.side_effect = ['demo', 'demo', - 'demo', 'http://abc.com/5000/'] - mock_ks_response = mock.MagicMock() - mock_ks_content = {} - mock_ks_response.content = json.dumps(mock_ks_content) - expectedprops = {'flavor': 'm1.small', - 'image': None, - 'user_data_format': 'SOFTWARE_CONFIG', - 'software_config_transport': 'POLL_SERVER_HEAT'} - self._tosca_compute_test( - tpl_snippet, - expectedprops) + mock_images.return_value = { + 'fake-image-foobar': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'Fake Distribution', + 'version': '19.0'}, + 'fake-image-foobar-old': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'Fake Distribution', + 'version': '18.0'} + } + expectedprops = {'flavor': 'm1.small', + 'image': 'fake-image-foobar', + 'user_data_format': 'SOFTWARE_CONFIG', + 'software_config_transport': 'POLL_SERVER_HEAT'} + self._tosca_compute_test(tpl_snippet, expectedprops) diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py index d4b2f44..27c1033 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py @@ -29,9 +29,10 @@ class ToscaBlockStorage(HotResource): toscatype = 'tosca.nodes.BlockStorage' - def __init__(self, nodetemplate): + def __init__(self, nodetemplate, csar_dir=None): super(ToscaBlockStorage, self).__init__(nodetemplate, - type='OS::Cinder::Volume') + type='OS::Cinder::Volume', + csar_dir=csar_dir) pass def handle_properties(self): @@ -67,5 +68,5 @@ class ToscaBlockStorage(HotResource): # attribute for the matching resource. Unless there is additional # runtime support, this should be a one to one mapping. if attribute == 'volume_id': - attr['get_resource'] = args[0] + attr['get_resource'] = self.name return attr diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py index 71b9822..f471b83 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py @@ -23,9 +23,11 @@ class ToscaBlockStorageAttachment(HotResource): toscatype = 'tosca.nodes.BlockStorageAttachment' - def __init__(self, template, nodetemplates, instance_uuid, volume_id): + def __init__(self, template, nodetemplates, instance_uuid, volume_id, + csar_dir=None): super(ToscaBlockStorageAttachment, - self).__init__(template, type='OS::Cinder::VolumeAttachment') + self).__init__(template, type='OS::Cinder::VolumeAttachment', + csar_dir=csar_dir) self.nodetemplates = nodetemplates self.instance_uuid = {'get_resource': instance_uuid} self.volume_id = {'get_resource': volume_id} @@ -50,4 +52,4 @@ class ToscaBlockStorageAttachment(HotResource): self.properties.pop('device') def handle_life_cycle(self): - pass + return None, None, None diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py index 9d6a459..85f312d 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py @@ -11,68 +11,21 @@ # License for the specific language governing permissions and limitations # under the License. -import json import logging -import requests from toscaparser.utils.gettextutils import _ +from translator.common import flavors as nova_flavors +from translator.common import images as glance_images import translator.common.utils from translator.hot.syntax.hot_resource import HotResource + log = logging.getLogger('heat-translator') # Name used to dynamically load appropriate map class. TARGET_CLASS_NAME = 'ToscaCompute' -# A design issue to be resolved is how to translate the generic TOSCA server -# properties to OpenStack flavors and images. At the Atlanta design summit, -# there was discussion on using Glance to store metadata and Graffiti to -# describe artifacts. We will follow these projects to see if they can be -# leveraged for this TOSCA translation. -# For development purpose at this time, we temporarily hardcode a list of -# flavors and images here -FLAVORS = {'m1.xlarge': {'mem_size': 16384, 'disk_size': 160, 'num_cpus': 8}, - 'm1.large': {'mem_size': 8192, 'disk_size': 80, 'num_cpus': 4}, - 'm1.medium': {'mem_size': 4096, 'disk_size': 40, 'num_cpus': 2}, - 'm1.small': {'mem_size': 2048, 'disk_size': 20, 'num_cpus': 1}, - 'm1.tiny': {'mem_size': 512, 'disk_size': 1, 'num_cpus': 1}, - 'm1.micro': {'mem_size': 128, 'disk_size': 0, 'num_cpus': 1}, - 'm1.nano': {'mem_size': 64, 'disk_size': 0, 'num_cpus': 1}} - -IMAGES = {'ubuntu-software-config-os-init': {'architecture': 'x86_64', - 'type': 'Linux', - 'distribution': 'Ubuntu', - 'version': '14.04'}, - 'ubuntu-12.04-software-config-os-init': {'architecture': 'x86_64', - 'type': 'Linux', - 'distribution': 'Ubuntu', - 'version': '12.04'}, - 'fedora-amd64-heat-config': {'architecture': 'x86_64', - 'type': 'Linux', - 'distribution': 'Fedora', - 'version': '18.0'}, - 'F18-x86_64-cfntools': {'architecture': 'x86_64', - 'type': 'Linux', - 'distribution': 'Fedora', - 'version': '19'}, - 'Fedora-x86_64-20-20131211.1-sda': {'architecture': 'x86_64', - 'type': 'Linux', - 'distribution': 'Fedora', - 'version': '20'}, - 'cirros-0.3.1-x86_64-uec': {'architecture': 'x86_64', - 'type': 'Linux', - 'distribution': 'CirrOS', - 'version': '0.3.1'}, - 'cirros-0.3.2-x86_64-uec': {'architecture': 'x86_64', - 'type': 'Linux', - 'distribution': 'CirrOS', - 'version': '0.3.2'}, - 'rhel-6.5-test-image': {'architecture': 'x86_64', - 'type': 'Linux', - 'distribution': 'RHEL', - 'version': '6.5'}} - class ToscaCompute(HotResource): '''Translate TOSCA node type tosca.nodes.Compute.''' @@ -84,9 +37,18 @@ class ToscaCompute(HotResource): ('architecture', 'distribution', 'type', 'version') toscatype = 'tosca.nodes.Compute' - def __init__(self, nodetemplate): + ALLOWED_NOVA_SERVER_PROPS = \ + ('admin_pass', 'availability_zone', 'block_device_mapping', + 'block_device_mapping_v2', 'config_drive', 'diskConfig', 'flavor', + 'flavor_update_policy', 'image', 'image_update_policy', 'key_name', + 'metadata', 'name', 'networks', 'personality', 'reservation_id', + 'scheduler_hints', 'security_groups', 'software_config_transport', + 'user_data', 'user_data_format', 'user_data_update_policy') + + def __init__(self, nodetemplate, csar_dir=None): super(ToscaCompute, self).__init__(nodetemplate, - type='OS::Nova::Server') + type='OS::Nova::Server', + csar_dir=csar_dir) # List with associated hot port resources with this server self.assoc_port_resources = [] pass @@ -99,7 +61,8 @@ class ToscaCompute(HotResource): self.properties['software_config_transport'] = 'POLL_SERVER_HEAT' tosca_props = self.get_tosca_props() for key, value in tosca_props.items(): - self.properties[key] = value + if key in self.ALLOWED_NOVA_SERVER_PROPS: + self.properties[key] = value # To be reorganized later based on new development in Glance and Graffiti def translate_compute_flavor_and_image(self, @@ -125,91 +88,16 @@ class ToscaCompute(HotResource): if os_cap_props: image = self._best_image(os_cap_props) hot_properties['flavor'] = flavor - hot_properties['image'] = image + if image: + hot_properties['image'] = image + else: + hot_properties.pop('image', None) return hot_properties - def _create_nova_flavor_dict(self): - '''Populates and returns the flavors dict using Nova ReST API''' - try: - access_dict = translator.common.utils.get_ks_access_dict() - access_token = translator.common.utils.get_token_id(access_dict) - if access_token is None: - return None - nova_url = translator.common.utils.get_url_for(access_dict, - 'compute') - if not nova_url: - return None - nova_response = requests.get(nova_url + '/flavors/detail', - headers={'X-Auth-Token': - access_token}) - if nova_response.status_code != 200: - return None - flavors = json.loads(nova_response.content)['flavors'] - flavor_dict = dict() - for flavor in flavors: - flavor_name = str(flavor['name']) - flavor_dict[flavor_name] = { - 'mem_size': flavor['ram'], - 'disk_size': flavor['disk'], - 'num_cpus': flavor['vcpus'], - } - except Exception as e: - # Handles any exception coming from openstack - log.warn(_('Choosing predefined flavors since received ' - 'Openstack Exception: %s') % str(e)) - return None - return flavor_dict - - def _populate_image_dict(self): - '''Populates and returns the images dict using Glance ReST API''' - images_dict = {} - try: - access_dict = translator.common.utils.get_ks_access_dict() - access_token = translator.common.utils.get_token_id(access_dict) - if access_token is None: - return None - glance_url = translator.common.utils.get_url_for(access_dict, - 'image') - if not glance_url: - return None - glance_response = requests.get(glance_url + '/v2/images', - headers={'X-Auth-Token': - access_token}) - if glance_response.status_code != 200: - return None - images = json.loads(glance_response.content)["images"] - for image in images: - image_resp = requests.get(glance_url + '/v2/images/' + - image["id"], - headers={'X-Auth-Token': - access_token}) - if image_resp.status_code != 200: - continue - metadata = ["architecture", "type", "distribution", "version"] - image_data = json.loads(image_resp.content) - if any(key in image_data.keys() for key in metadata): - images_dict[image_data["name"]] = dict() - for key in metadata: - if key in image_data.keys(): - images_dict[image_data["name"]][key] = \ - image_data[key] - else: - continue - - except Exception as e: - # Handles any exception coming from openstack - log.warn(_('Choosing predefined flavors since received ' - 'Openstack Exception: %s') % str(e)) - return images_dict - def _best_flavor(self, properties): log.info(_('Choosing the best flavor for given attributes.')) # Check whether user exported all required environment variables. - flavors = FLAVORS - if translator.common.utils.check_for_env_variables(): - resp = self._create_nova_flavor_dict() - if resp: - flavors = resp + flavors = nova_flavors.get_flavors() # start with all flavors match_all = flavors.keys() @@ -252,11 +140,7 @@ class ToscaCompute(HotResource): def _best_image(self, properties): # Check whether user exported all required environment variables. - images = IMAGES - if translator.common.utils.check_for_env_variables(): - resp = self._populate_image_dict() - if resp and len(resp.keys()) > 0: - images = resp + images = glance_images.get_images() match_all = images.keys() architecture = properties.get(self.ARCHITECTURE) if architecture is None: @@ -307,7 +191,10 @@ class ToscaCompute(HotResource): return this_list matching_images = [] for image in this_list: - if this_dict[image][attr].lower() == str(prop).lower(): + 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 @@ -324,7 +211,7 @@ class ToscaCompute(HotResource): attriute.')) if attribute == 'private_address' or \ attribute == 'public_address': - attr['get_attr'] = [self.name, 'first_address'] + attr['get_attr'] = [self.name, 'networks', 'private', 0] return attr diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_database.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_database.py index 26c9d4d..7c8bc45 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_database.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_database.py @@ -22,8 +22,8 @@ class ToscaDatabase(HotResource): toscatype = 'tosca.nodes.Database' - def __init__(self, nodetemplate): - super(ToscaDatabase, self).__init__(nodetemplate) + def __init__(self, nodetemplate, csar_dir=None): + super(ToscaDatabase, self).__init__(nodetemplate, csar_dir=csar_dir) pass def handle_properties(self): diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_dbms.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_dbms.py index 38c31bd..3136792 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_dbms.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_dbms.py @@ -22,8 +22,8 @@ class ToscaDbms(HotResource): toscatype = 'tosca.nodes.DBMS' - def __init__(self, nodetemplate): - super(ToscaDbms, self).__init__(nodetemplate) + def __init__(self, nodetemplate, csar_dir=None): + super(ToscaDbms, self).__init__(nodetemplate, csar_dir=csar_dir) pass def handle_properties(self): diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_network.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_network.py index a4e565e..10e6405 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_network.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_network.py @@ -28,9 +28,10 @@ class ToscaNetwork(HotResource): existing_resource_id = None - def __init__(self, nodetemplate): + def __init__(self, nodetemplate, csar_dir=None): super(ToscaNetwork, self).__init__(nodetemplate, - type='OS::Neutron::Net') + type='OS::Neutron::Net', + csar_dir=csar_dir) pass def handle_properties(self): @@ -57,8 +58,6 @@ class ToscaNetwork(HotResource): self.existing_resource_id = value break elif key == 'segmentation_id': - # net_props['segmentation_id'] = \ - # tosca_props['segmentation_id'] # Hardcode to vxlan for now until we add the network type # and physical network to the spec. net_props['value_specs'] = {'provider:segmentation_id': diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_port.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_port.py index 4fd2d70..86733e4 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_port.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_port.py @@ -24,9 +24,10 @@ class ToscaNetworkPort(HotResource): toscatype = 'tosca.nodes.network.Port' - def __init__(self, nodetemplate): + def __init__(self, nodetemplate, csar_dir=None): super(ToscaNetworkPort, self).__init__(nodetemplate, - type='OS::Neutron::Port') + type='OS::Neutron::Port', + csar_dir=csar_dir) # Default order self.order = 0 pass diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_object_storage.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_object_storage.py index 177503f..e30c46c 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_object_storage.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_object_storage.py @@ -23,9 +23,10 @@ class ToscaObjectStorage(HotResource): toscatype = 'tosca.nodes.ObjectStorage' - def __init__(self, nodetemplate): + def __init__(self, nodetemplate, csar_dir=None): super(ToscaObjectStorage, self).__init__(nodetemplate, - type='OS::Swift::Container') + type='OS::Swift::Container', + csar_dir=csar_dir) pass def handle_properties(self): diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies.py index b32fc1d..12b40d5 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies.py @@ -22,9 +22,10 @@ class ToscaPolicies(HotResource): toscatype = 'tosca.policies.Placement' - def __init__(self, policy): + def __init__(self, policy, csar_dir=None): super(ToscaPolicies, self).__init__(policy, - type='OS::Nova::ServerGroup') + type='OS::Nova::ServerGroup', + csar_dir=csar_dir) self.policy = policy def handle_properties(self, resources): diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py new file mode 100644 index 0000000..1b63f24 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_scaling.py @@ -0,0 +1,131 @@ +# +# 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 OrderedDict +import yaml + +from translator.hot.syntax.hot_resource import HotResource +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaAutoscaling' +HEAT_TEMPLATE_BASE = """ +heat_template_version: 2013-05-23 +""" +ALARM_STATISTIC = {'average': 'avg'} +SCALING_RESOURCES = ["OS::Heat::ScalingPolicy", "OS::Heat::AutoScalingGroup", + "OS::Aodh::Alarm"] + + +class ToscaAutoscaling(HotResource): + '''Translate TOSCA node type tosca.policies.Scaling''' + + toscatype = 'tosca.policies.Scaling' + + def __init__(self, policy, csar_dir=None): + hot_type = "OS::Heat::ScalingPolicy" + super(ToscaAutoscaling, self).__init__(policy, + type=hot_type, + csar_dir=csar_dir) + self.policy = policy + + def handle_expansion(self): + if self.policy.entity_tpl.get('triggers'): + sample = self.policy.\ + entity_tpl["triggers"]["resize_compute"]["condition"] + prop = {} + prop["description"] = self.policy.entity_tpl.get('description') + prop["meter_name"] = "cpu_util" + if sample: + prop["statistic"] = ALARM_STATISTIC[sample["method"]] + prop["period"] = sample["period"] + prop["threshold"] = sample["evaluations"] + prop["comparison_operator"] = "gt" + alarm_name = self.name.replace('_scale_in', '').\ + replace('_scale_out', '') + ceilometer_resources = HotResource(self.nodetemplate, + type='OS::Aodh::Alarm', + name=alarm_name + '_alarm', + properties=prop) + hot_resources = [ceilometer_resources] + return hot_resources + + def represent_ordereddict(self, dumper, data): + nodes = [] + for key, value in data.items(): + node_key = dumper.represent_data(key) + node_value = dumper.represent_data(value) + nodes.append((node_key, node_value)) + return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', nodes) + + def _handle_nested_template(self, scale_res): + template_dict = yaml.safe_load(HEAT_TEMPLATE_BASE) + template_dict['description'] = 'Tacker Scaling template' + template_dict["resources"] = {} + dict_res = OrderedDict() + for res in scale_res: + dict_res = res.get_dict_output() + res_name = list(dict_res.keys())[0] + template_dict["resources"][res_name] = \ + dict_res[res_name] + + yaml.add_representer(OrderedDict, self.represent_ordereddict) + yaml.add_representer(dict, self.represent_ordereddict) + yaml_string = yaml.dump(template_dict, default_flow_style=False) + yaml_string = yaml_string.replace('\'', '') .replace('\n\n', '\n') + self.nested_template = { + self.policy.name + '_res.yaml': yaml_string + } + + def handle_properties(self, resources): + self.properties = {} + self.properties["auto_scaling_group_id"] = { + 'get_resource': self.policy.name + '_group' + } + self.properties["adjustment_type"] = "change_in_capacity " + self.properties["scaling_adjustment"] = self.\ + policy.entity_tpl["properties"]["increment"] + delete_res_names = [] + scale_res = [] + for index, resource in enumerate(resources): + if resource.name in self.policy.targets and \ + resource.type != 'OS::Heat::AutoScalingGroup': + temp = self.policy.entity_tpl["properties"] + props = {} + res = {} + res["min_size"] = temp["min_instances"] + res["max_size"] = temp["max_instances"] + res["desired_capacity"] = temp["default_instances"] + props['type'] = resource.type + props['properties'] = resource.properties + res['resource'] = {'type': self.policy.name + '_res.yaml'} + scaling_resources = \ + HotResource(resource, + type='OS::Heat::AutoScalingGroup', + name=self.policy.name + '_group', + properties=res) + + if resource.type not in SCALING_RESOURCES: + delete_res_names.append(resource.name) + scale_res.append(resource) + self._handle_nested_template(scale_res) + resources = [tmp_res + for tmp_res in resources + if tmp_res.name not in delete_res_names] + resources.append(scaling_resources) + return resources + + def extract_substack_templates(self, base_filename, hot_template_version): + return self.nested_template + + def embed_substack_templates(self, hot_template_version): + pass diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_software_component.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_software_component.py index 044de43..9b0819e 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_software_component.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_software_component.py @@ -22,8 +22,9 @@ class ToscaSoftwareComponent(HotResource): toscatype = 'tosca.nodes.SoftwareComponent' - def __init__(self, nodetemplate): - super(ToscaSoftwareComponent, self).__init__(nodetemplate) + def __init__(self, nodetemplate, csar_dir=None): + super(ToscaSoftwareComponent, self).__init__(nodetemplate, + csar_dir=csar_dir) pass def handle_properties(self): diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_web_application.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_web_application.py index d0a9c5d..03474aa 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_web_application.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_web_application.py @@ -22,8 +22,9 @@ class ToscaWebApplication(HotResource): toscatype = 'tosca.nodes.WebApplication' - def __init__(self, nodetemplate): - super(ToscaWebApplication, self).__init__(nodetemplate) + def __init__(self, nodetemplate, csar_dir=None): + super(ToscaWebApplication, self).__init__(nodetemplate, + csar_dir=csar_dir) pass def handle_properties(self): diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_webserver.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_webserver.py index 83bda80..32b80c5 100644 --- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_webserver.py +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_webserver.py @@ -22,8 +22,9 @@ class ToscaWebserver(HotResource): toscatype = 'tosca.nodes.WebServer' - def __init__(self, nodetemplate): - super(ToscaWebserver, self).__init__(nodetemplate) + def __init__(self, nodetemplate, csar_dir): + super(ToscaWebserver, self).__init__(nodetemplate, + csar_dir=csar_dir) pass def handle_properties(self): -- cgit 1.2.3-korg