summaryrefslogtreecommitdiffstats
path: root/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
diff options
context:
space:
mode:
authorshangxdy <shang.xiaodong@zte.com.cn>2016-04-07 14:08:49 -0400
committershangxdy <shang.xiaodong@zte.com.cn>2016-04-07 14:59:30 -0400
commitc8201c119ec686e79797721156767685fe848aca (patch)
treecce6aa15ded38d89c18a14b76c21f21e0b1a24f7 /tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
parent4e4c86a06d58b1816c074c7f9da4c58bbf10368c (diff)
Update tosca lib to version 0.5
Use tosca-parser and heat-translator to analyze to the basic nfv-tosca type definitions, and use simple tosca new feature such as policy, group and trigger, which are now supported by the latest version of tosca-parser and heat-translator. JIRA:PARSER-18 Change-Id: I797bcacbb5b32005d0aeb0f3f32851ac96e30f01 Signed--off-by: shangxdy <shang.xiaodong@zte.com.cn> Signed-off-by: shangxdy <shang.xiaodong@zte.com.cn>
Diffstat (limited to 'tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py')
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py280
1 files changed, 280 insertions, 0 deletions
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
new file mode 100644
index 0000000..e2ac130
--- /dev/null
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
@@ -0,0 +1,280 @@
+#
+# 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 json
+import logging
+import requests
+
+from toscaparser.utils.gettextutils import _
+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.'''
+
+ 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')
+ toscatype = 'tosca.nodes.Compute'
+
+ def __init__(self, nodetemplate):
+ super(ToscaCompute, self).__init__(nodetemplate,
+ type='OS::Nova::Server')
+ # List with associated hot port resources with this server
+ self.assoc_port_resources = []
+ pass
+
+ def handle_properties(self):
+ self.properties = self.translate_compute_flavor_and_image(
+ self.nodetemplate.get_capability('host'),
+ self.nodetemplate.get_capability('os'))
+ self.properties['user_data_format'] = 'SOFTWARE_CONFIG'
+ tosca_props = self.get_tosca_props()
+ for key, value in tosca_props.items():
+ self.properties[key] = value
+
+ # To be reorganized later based on new development in Glance and Graffiti
+ def translate_compute_flavor_and_image(self,
+ host_capability,
+ os_capability):
+ hot_properties = {}
+ host_cap_props = {}
+ os_cap_props = {}
+ image = None
+ flavor = None
+ if host_capability:
+ for prop in host_capability.get_properties_objects():
+ host_cap_props[prop.name] = prop.value
+ flavor = self._best_flavor(host_cap_props)
+ if os_capability:
+ for prop in os_capability.get_properties_objects():
+ os_cap_props[prop.name] = prop.value
+ image = self._best_image(os_cap_props)
+ hot_properties['flavor'] = flavor
+ hot_properties['image'] = image
+ 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 _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
+
+ # start with all flavors
+ match_all = flavors.keys()
+
+ # TODO(anyone): Handle the case where the value contains something like
+ # get_input instead of a value.
+ # flavors that fit the CPU count
+ cpu = properties.get(self.NUM_CPUS)
+ if cpu is None:
+ self._log_compute_msg(self.NUM_CPUS, 'flavor')
+ match_cpu = self._match_flavors(match_all, flavors, self.NUM_CPUS, cpu)
+
+ # flavors that fit the mem size
+ mem = properties.get(self.MEM_SIZE)
+ if mem:
+ mem = translator.common.utils.MemoryUnit.convert_unit_size_to_num(
+ mem, 'MB')
+ else:
+ self._log_compute_msg(self.MEM_SIZE, 'flavor')
+ match_cpu_mem = self._match_flavors(match_cpu, flavors,
+ self.MEM_SIZE, mem)
+ # flavors that fit the disk size
+ disk = properties.get(self.DISK_SIZE)
+ if disk:
+ disk = translator.common.utils.MemoryUnit.\
+ convert_unit_size_to_num(disk, 'GB')
+ else:
+ self._log_compute_msg(self.DISK_SIZE, 'flavor')
+ match_cpu_mem_disk = self._match_flavors(match_cpu_mem, flavors,
+ self.DISK_SIZE, disk)
+ # if multiple match, pick the flavor with the least memory
+ # the selection can be based on other heuristic, e.g. pick one with the
+ # least total resource
+ if len(match_cpu_mem_disk) > 1:
+ return self._least_flavor(match_cpu_mem_disk, flavors, 'mem_size')
+ elif len(match_cpu_mem_disk) == 1:
+ return match_cpu_mem_disk[0]
+ else:
+ return None
+
+ def _best_image(self, properties):
+ match_all = IMAGES.keys()
+ architecture = properties.get(self.ARCHITECTURE)
+ 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._log_compute_msg(self.TYPE, 'image')
+ match_type = self._match_images(match_arch, IMAGES, self.TYPE, 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,
+ 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)
+
+ 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.'''
+ if not size:
+ return list(this_list)
+ matching_flavors = []
+ for flavor in this_list:
+ if isinstance(size, int):
+ if this_dict[flavor][attr] >= size:
+ matching_flavors.append(flavor)
+ 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.'''
+ 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):
+ if not prop:
+ return this_list
+ matching_images = []
+ for image in this_list:
+ if this_dict[image][attr].lower() == str(prop).lower():
+ matching_images.append(image)
+ return matching_images
+
+ def get_hot_attribute(self, attribute, args):
+ attr = {}
+ # Convert from a TOSCA attribute for a nodetemplate to a HOT
+ # attribute for the matching resource. Unless there is additional
+ # runtime support, this should be a one to one mapping.
+
+ # Note: We treat private and public IP addresses equally, but
+ # this will change in the future when TOSCA starts to support
+ # multiple private/public IP addresses.
+ log.debug(_('Converting TOSCA attribute for a nodetemplate to a HOT \
+ attriute.'))
+ if attribute == 'private_address' or \
+ attribute == 'public_address':
+ attr['get_attr'] = [self.name, 'networks', 'private', 0]
+
+ return attr
+
+ def _log_compute_msg(self, 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)