From b678c5bfea0f1b142c2a4a5bdee1e3309a021887 Mon Sep 17 00:00:00 2001 From: zhipengh Date: Tue, 22 Dec 2015 15:04:53 +0800 Subject: JIRA:PARSER-15 Provide Standalone Heat-Translator Liberty Pypi Packages --- .../translator/hot/syntax/hot_resource.py | 308 +++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 tosca2heat/heat-translator-0.3.0/translator/hot/syntax/hot_resource.py (limited to 'tosca2heat/heat-translator-0.3.0/translator/hot/syntax/hot_resource.py') diff --git a/tosca2heat/heat-translator-0.3.0/translator/hot/syntax/hot_resource.py b/tosca2heat/heat-translator-0.3.0/translator/hot/syntax/hot_resource.py new file mode 100644 index 0000000..3ca9b03 --- /dev/null +++ b/tosca2heat/heat-translator-0.3.0/translator/hot/syntax/hot_resource.py @@ -0,0 +1,308 @@ +# +# 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 six + +from toscaparser.functions import GetInput +from toscaparser.nodetemplate import NodeTemplate +from toscaparser.utils.gettextutils import _ + + +SECTIONS = (TYPE, PROPERTIES, MEDADATA, DEPENDS_ON, UPDATE_POLICY, + DELETION_POLICY) = \ + ('type', 'properties', 'metadata', + 'depends_on', 'update_policy', 'deletion_policy') + + +class HotResource(object): + '''Base class for TOSCA node type translation to Heat resource type.''' + + def __init__(self, nodetemplate, name=None, type=None, properties=None, + metadata=None, depends_on=None, + update_policy=None, deletion_policy=None): + self.nodetemplate = nodetemplate + if name: + self.name = name + else: + self.name = nodetemplate.name + self.type = type + self.properties = properties or {} + # special case for HOT softwareconfig + if type == 'OS::Heat::SoftwareConfig': + self.properties['group'] = 'script' + self.metadata = metadata + + # The difference between depends_on and depends_on_nodes is + # that depends_on defines dependency in the context of the + # HOT template and it is used during the template output. + # Depends_on_nodes defines the direct dependency between the + # tosca nodes and is not used during the output of the + # HOT template but for internal processing only. When a tosca + # node depends on another node it will be always added to + # depends_on_nodes but not always to depends_on. For example + # if the source of dependency is a server, the dependency will + # be added as properties.get_resource and not depends_on + if depends_on: + self.depends_on = depends_on + self.depends_on_nodes = depends_on + else: + self.depends_on = [] + self.depends_on_nodes = [] + self.update_policy = update_policy + self.deletion_policy = deletion_policy + self.group_dependencies = {} + # if hide_resource is set to true, then this resource will not be + # generated in the output yaml. + self.hide_resource = False + + def handle_properties(self): + # the property can hold a value or the intrinsic function get_input + # for value, copy it + # for get_input, convert to get_param + for prop in self.nodetemplate.get_properties_objects(): + pass + + def handle_life_cycle(self): + hot_resources = [] + deploy_lookup = {} + # TODO(anyone): sequence for life cycle needs to cover different + # scenarios and cannot be fixed or hard coded here + interfaces_deploy_sequence = ['create', 'configure', 'start'] + + # create HotResource for each interface used for deployment: + # create, start, configure + # ignore the other interfaces + # observe the order: create, start, configure + # use the current HotResource for the first interface in this order + + # hold the original name since it will be changed during + # the transformation + node_name = self.name + reserve_current = 'NONE' + interfaces_actual = [] + for interface in self.nodetemplate.interfaces: + interfaces_actual.append(interface.name) + for operation in interfaces_deploy_sequence: + if operation in interfaces_actual: + reserve_current = operation + break + + # create the set of SoftwareDeployment and SoftwareConfig for + # the interface operations + hosting_server = None + if self.nodetemplate.requirements is not None: + hosting_server = self._get_hosting_server() + for interface in self.nodetemplate.interfaces: + if interface.name in interfaces_deploy_sequence: + config_name = node_name + '_' + interface.name + '_config' + deploy_name = node_name + '_' + interface.name + '_deploy' + hot_resources.append( + HotResource(self.nodetemplate, + config_name, + 'OS::Heat::SoftwareConfig', + {'config': + {'get_file': interface.implementation}})) + + # hosting_server is None if requirements is None + hosting_on_server = (hosting_server.name if + hosting_server else None) + if interface.name == reserve_current: + deploy_resource = self + self.name = deploy_name + self.type = 'OS::Heat::SoftwareDeployment' + self.properties = {'config': {'get_resource': config_name}, + 'server': {'get_resource': + hosting_on_server}} + deploy_lookup[interface.name] = self + else: + sd_config = {'config': {'get_resource': config_name}, + 'server': {'get_resource': + hosting_on_server}} + deploy_resource = \ + HotResource(self.nodetemplate, + deploy_name, + 'OS::Heat::SoftwareDeployment', + sd_config) + hot_resources.append(deploy_resource) + deploy_lookup[interface.name] = deploy_resource + lifecycle_inputs = self._get_lifecycle_inputs(interface) + if lifecycle_inputs: + deploy_resource.properties['input_values'] = \ + lifecycle_inputs + + # Add dependencies for the set of HOT resources in the sequence defined + # in interfaces_deploy_sequence + # TODO(anyone): find some better way to encode this implicit sequence + group = {} + for op, hot in deploy_lookup.items(): + # position to determine potential preceding nodes + op_index = interfaces_deploy_sequence.index(op) + for preceding_op in \ + reversed(interfaces_deploy_sequence[:op_index]): + preceding_hot = deploy_lookup.get(preceding_op) + if preceding_hot: + hot.depends_on.append(preceding_hot) + hot.depends_on_nodes.append(preceding_hot) + group[preceding_hot] = hot + break + + # save this dependency chain in the set of HOT resources + self.group_dependencies.update(group) + for hot in hot_resources: + hot.group_dependencies.update(group) + + return hot_resources + + def handle_connectsto(self, tosca_source, tosca_target, hot_source, + hot_target, config_location, operation): + # The ConnectsTo relationship causes a configuration operation in + # the target. + # 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 + 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 + deploy_name = tosca_source.name + '_' + tosca_target.name + \ + '_connect_deploy' + sd_config = {'config': {'get_resource': self.name}, + 'server': {'get_resource': hosting_server.name}} + deploy_resource = \ + HotResource(self.nodetemplate, + deploy_name, + 'OS::Heat::SoftwareDeployment', + sd_config, + depends_on=[hot_depends]) + connect_inputs = self._get_connect_inputs(config_location, operation) + if connect_inputs: + deploy_resource.properties['input_values'] = connect_inputs + + return deploy_resource + + def handle_expansion(self): + pass + + def handle_hosting(self): + # 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': + # 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']: + raise Exception(_("Internal Error: expecting host " + "in software deployment")) + elif isinstance(host_server['get_resource'], NodeTemplate): + self.properties['server']['get_resource'] = \ + host_server['get_resource'].name + + def top_of_chain(self): + dependent = self.group_dependencies.get(self) + if dependent is None: + return self + else: + return dependent.top_of_chain() + + def get_dict_output(self): + resource_sections = OrderedDict() + resource_sections[TYPE] = self.type + if self.properties: + resource_sections[PROPERTIES] = self.properties + if self.metadata: + resource_sections[MEDADATA] = self.metadata + if self.depends_on: + resource_sections[DEPENDS_ON] = [] + for depend in self.depends_on: + resource_sections[DEPENDS_ON].append(depend.name) + if self.update_policy: + resource_sections[UPDATE_POLICY] = self.update_policy + if self.deletion_policy: + resource_sections[DELETION_POLICY] = self.deletion_policy + + return {self.name: resource_sections} + + def _get_lifecycle_inputs(self, interface): + # check if this lifecycle operation has input values specified + # extract and convert to HOT format + if isinstance(interface.value, six.string_types): + # the interface has a static string + return {} + else: + # the interface is a dict {'implemenation': xxx, 'input': yyy} + inputs = interface.value.get('inputs') + deploy_inputs = {} + if inputs: + for name, value in six.iteritems(inputs): + deploy_inputs[name] = value + return deploy_inputs + + def _get_connect_inputs(self, config_location, operation): + if config_location == 'target': + inputs = operation.get('pre_configure_target').get('inputs') + elif config_location == 'source': + inputs = operation.get('pre_configure_source').get('inputs') + deploy_inputs = {} + if inputs: + for name, value in six.iteritems(inputs): + deploy_inputs[name] = value + return deploy_inputs + + def _get_hosting_server(self, node_template=None): + # find the server that hosts this software by checking the + # requirements and following the hosting chain + this_node_template = self.nodetemplate \ + if node_template is None else node_template + for requirement in this_node_template.requirements: + for requirement_name, assignment in six.iteritems(requirement): + for check_node in this_node_template.related_nodes: + # check if the capability is Container + if isinstance(assignment, dict): + node_name = assignment.get('node') + else: + node_name = assignment + 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: + return self._get_hosting_server(check_node) + return None + + def _is_container_type(self, requirement_name, node): + # capability is a list of dict + # For now just check if it's type tosca.nodes.Compute + # TODO(anyone): match up requirement and capability + if node.type == 'tosca.nodes.Compute': + return True + else: + return False + + def get_hot_attribute(self, attribute, args): + # this is a place holder and should be implemented by the subclass + # if translation is needed for the particular attribute + raise Exception(_("No translation in TOSCA type {0} for attribute " + "{1}").format(self.nodetemplate.type, attribute)) + + def _get_tosca_props(self, properties): + tosca_props = {} + for prop in self.nodetemplate.get_properties_objects(): + if isinstance(prop.value, GetInput): + tosca_props[prop.name] = {'get_param': prop.value.input_name} + else: + tosca_props[prop.name] = prop.value + return tosca_props -- cgit 1.2.3-korg