From e793a3efbafb71f7512e0f362d6edc8801c064d8 Mon Sep 17 00:00:00 2001 From: Bharath Thiruveedula Date: Mon, 5 Dec 2016 23:02:45 +0530 Subject: [PATCH] Adds support for SoftwareDeploymentGroup in HT Change-Id: I7f23bc663ac315d8cab931465ea3a0ca18794d69 Closes-Bug: #1646929 --- translator/hot/syntax/hot_resource.py | 80 ++++++++++++++----- ...hot_software_component_multiple_hosts.yaml | 71 ++++++++++++++++ ...sca_software_component_multiple_hosts.yaml | 55 +++++++++++++ .../tests/test_tosca_hot_translation.py | 9 +++ 4 files changed, 196 insertions(+), 19 deletions(-) create mode 100644 translator/tests/data/hot_output/hot_software_component_multiple_hosts.yaml create mode 100644 translator/tests/data/tosca_software_component_multiple_hosts.yaml diff --git a/translator/hot/syntax/hot_resource.py b/translator/hot/syntax/hot_resource.py index f7ab95e1..3f5dde13 100644 --- a/translator/hot/syntax/hot_resource.py +++ b/translator/hot/syntax/hot_resource.py @@ -139,13 +139,25 @@ class HotResource(object): hosting_server = None if self.nodetemplate.requirements is not None: hosting_server = self._get_hosting_server() + servers = {} + server_key = 'server' + sw_deploy_res = 'OS::Heat::SoftwareDeployment' + if hosting_server is not None: + if len(hosting_server) == 1: + servers['get_resource'] = hosting_server[0] + else: + for server in hosting_server: + servers[server] = {'get_resource': server} + sw_deploy_res += 'Group' + server_key = 'servers' # hosting_server is None if requirements is None - hosting_on_server = hosting_server.name if hosting_server else None + hosting_on_server = hosting_server if hosting_server else None base_type = HotResource.get_base_type_str( self.nodetemplate.type_definition) # if we are on a compute node the host is self if hosting_on_server is None and base_type == 'tosca.nodes.Compute': hosting_on_server = self.name + servers = {'get_resource': self.name} cwd = os.getcwd() for operation in operations.values(): @@ -168,19 +180,17 @@ class HotResource(object): base_type != 'tosca.nodes.Compute': deploy_resource = self self.name = deploy_name - self.type = 'OS::Heat::SoftwareDeployment' + self.type = sw_deploy_res self.properties = {'config': {'get_resource': config_name}, - 'server': {'get_resource': - hosting_on_server}} + server_key: servers} deploy_lookup[operation] = self else: sd_config = {'config': {'get_resource': config_name}, - 'server': {'get_resource': - hosting_on_server}} + server_key: servers} deploy_resource = \ HotResource(self.nodetemplate, deploy_name, - 'OS::Heat::SoftwareDeployment', + sw_deploy_res, sd_config, csar_dir=self.csar_dir) hot_resources.append(deploy_resource) deploy_lookup[operation] = deploy_resource @@ -225,7 +235,7 @@ class HotResource(object): hot.group_dependencies.update(group) roles_deploy_resource = self._handle_ansiblegalaxy_roles( - hot_resources, node_name, hosting_on_server) + hot_resources, node_name, servers) # add a dependency to this ansible roles deploy to # the first "classic" deploy generated for this node @@ -241,6 +251,11 @@ class HotResource(object): hosting_on_server): artifacts = self.get_all_artifacts(self.nodetemplate) install_roles_script = '' + server_key = 'server' + sw_deploy_res = 'OS::Heat::SoftwareDeployment' + if len(hosting_on_server.keys()) > 1: + server_key += 's' + sw_deploy_res += 'Group' for artifact_name, artifact in artifacts.items(): artifact_type = artifact.get('type', '').lower() if artifact_type == 'tosca.artifacts.ansiblegalaxy.role': @@ -263,11 +278,10 @@ class HotResource(object): {'config': install_roles_script}, csar_dir=self.csar_dir)) sd_config = {'config': {'get_resource': config_name}, - 'server': {'get_resource': - hosting_on_server}} + server_key: hosting_on_server} deploy_resource = \ HotResource(self.nodetemplate, deploy_name, - 'OS::Heat::SoftwareDeployment', + sw_deploy_res, sd_config, csar_dir=self.csar_dir) hot_resources.append(deploy_resource) @@ -280,20 +294,31 @@ class HotResource(object): # This hot resource is the software config portion in the HOT template # This method adds the matching software deployment with the proper # target server and dependency + servers = {} + server_key = 'server' + sw_deploy_res = 'OS::Heat::SoftwareDeployment' if config_location == 'target': hosting_server = hot_target._get_hosting_server() hot_depends = hot_target elif config_location == 'source': hosting_server = self._get_hosting_server() hot_depends = hot_source + if hosting_server is not None: + if len(hosting_server) == 1: + servers['get_resource'] = hosting_server[0] + else: + for server in hosting_server: + servers[server] = {'get_resource': server} + sw_deploy_res += 'Group' + server_key = 'servers' deploy_name = tosca_source.name + '_' + tosca_target.name + \ '_connect_deploy' sd_config = {'config': {'get_resource': self.name}, - 'server': {'get_resource': hosting_server.name}} + server_key: servers} deploy_resource = \ HotResource(self.nodetemplate, deploy_name, - 'OS::Heat::SoftwareDeployment', + sw_deploy_res, sd_config, depends_on=[hot_depends], csar_dir=self.csar_dir) connect_inputs = self._get_connect_inputs(config_location, operation) @@ -309,17 +334,29 @@ class HotResource(object): # handle hosting server for the OS:HEAT::SoftwareDeployment # from the TOSCA nodetemplate, traverse the relationship chain # down to the server - if self.type == 'OS::Heat::SoftwareDeployment': + sw_deploy_group = 'OS::Heat::SoftwareDeploymentGroup' + sw_deploy = 'OS::Heat::SoftwareDeployment' + if self.properties.get('servers') and \ + self.properties.get('server'): + del self.properties['server'] + if self.type == sw_deploy_group or self.type == sw_deploy: # skip if already have hosting # If type is NodeTemplate, look up corresponding HotResrouce - host_server = self.properties.get('server') - if host_server is None or not host_server['get_resource']: + host_server = self.properties.get('servers') \ + or self.properties.get('server') + if host_server is None: raise Exception(_("Internal Error: expecting host " "in software deployment")) - elif isinstance(host_server['get_resource'], NodeTemplate): + + elif isinstance(host_server.get('get_resource'), NodeTemplate): self.properties['server']['get_resource'] = \ host_server['get_resource'].name + elif isinstance(host_server, dict) and \ + not host_server.get('get_resource'): + self.properties['servers'] = \ + host_server + def top_of_chain(self): dependent = self.group_dependencies.get(self) if dependent is None: @@ -374,6 +411,8 @@ class HotResource(object): def _get_hosting_server(self, node_template=None): # find the server that hosts this software by checking the # requirements and following the hosting chain + hosting_servers = [] + host_exists = False this_node_template = self.nodetemplate \ if node_template is None else node_template for requirement in this_node_template.requirements: @@ -387,9 +426,12 @@ class HotResource(object): if node_name and node_name == check_node.name: if self._is_container_type(requirement_name, check_node): - return check_node - elif check_node.related_nodes: + hosting_servers.append(check_node.name) + host_exists = True + elif check_node.related_nodes and not host_exists: return self._get_hosting_server(check_node) + if hosting_servers: + return hosting_servers return None def _is_container_type(self, requirement_name, node): diff --git a/translator/tests/data/hot_output/hot_software_component_multiple_hosts.yaml b/translator/tests/data/hot_output/hot_software_component_multiple_hosts.yaml new file mode 100644 index 00000000..02c3b43d --- /dev/null +++ b/translator/tests/data/hot_output/hot_software_component_multiple_hosts.yaml @@ -0,0 +1,71 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with a software component. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + +resources: + server1: + type: OS::Nova::Server + properties: + flavor: m1.small + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + server2: + type: OS::Nova::Server + properties: + flavor: m1.small + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + my_software_create_deploy: + type: OS::Heat::SoftwareDeploymentGroup + properties: + config: + get_resource: my_software_create_config + servers: + server1: + get_resource: server1 + server2: + get_resource: server2 + + my_software_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: software_install.sh + group: script + + my_software_start_deploy: + type: OS::Heat::SoftwareDeploymentGroup + properties: + config: + get_resource: my_software_start_config + servers: + server1: + get_resource: server1 + server2: + get_resource: server2 + depends_on: + - my_software_create_deploy + + my_software_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: software_start.sh + group: script + +outputs: {} diff --git a/translator/tests/data/tosca_software_component_multiple_hosts.yaml b/translator/tests/data/tosca_software_component_multiple_hosts.yaml new file mode 100644 index 00000000..e8aefb7a --- /dev/null +++ b/translator/tests/data/tosca_software_component_multiple_hosts.yaml @@ -0,0 +1,55 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with a software component. + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + default: 1 + + node_templates: + my_software: + type: tosca.nodes.SoftwareComponent + properties: + component_version: 1.0 + requirements: + - host: server1 + - host: server2 + interfaces: + Standard: + create: software_install.sh + start: software_start.sh + + server1: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 1024 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 + server2: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 1024 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 diff --git a/translator/tests/test_tosca_hot_translation.py b/translator/tests/test_tosca_hot_translation.py index 5ae05283..0f53eccc 100644 --- a/translator/tests/test_tosca_hot_translation.py +++ b/translator/tests/test_tosca_hot_translation.py @@ -235,6 +235,15 @@ class ToscaHotTranslationTest(TestCase): 'download_url': 'http://www.software.com/download'} self._test_successful_translation(tosca_file, hot_file, params) + def test_hot_translate_software_component_multiple_hosts(self): + tosca_file = '../tests/data/tosca_software_component'\ + '_multiple_hosts.yaml' + hot_file = '../tests/data/hot_output/hot_software_component'\ + '_multiple_hosts.yaml' + params = {'cpus': '1', + 'download_url': 'http://www.software.com/download'} + self._test_successful_translation(tosca_file, hot_file, params) + def test_hot_translate_web_application(self): tosca_file = '../tests/data/tosca_web_application.yaml' hot_file = '../tests/data/hot_output/hot_web_application.yaml'