Support python3 uploaded to pypi websit
[parser.git] / tosca2heat / heat-translator / translator / hot / tosca / tosca_compute.py
1 #
2 # Licensed under the Apache License, Version 2.0 (the "License"); you may
3 # not use this file except in compliance with the License. You may obtain
4 # a copy of the License at
5 #
6 # http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11 # License for the specific language governing permissions and limitations
12 # under the License.
13
14 import logging
15
16 from toscaparser.utils.gettextutils import _
17 from translator.common import flavors as nova_flavors
18 from translator.common import images as glance_images
19 import translator.common.utils
20 from translator.hot.syntax.hot_resource import HotResource
21
22
23 log = logging.getLogger('heat-translator')
24
25
26 # Name used to dynamically load appropriate map class.
27 TARGET_CLASS_NAME = 'ToscaCompute'
28
29
30 class ToscaCompute(HotResource):
31     """Translate TOSCA node type tosca.nodes.Compute."""
32
33     COMPUTE_HOST_PROP = (DISK_SIZE, MEM_SIZE, NUM_CPUS) = \
34                         ('disk_size', 'mem_size', 'num_cpus')
35
36     COMPUTE_OS_PROP = (ARCHITECTURE, DISTRIBUTION, TYPE, VERSION) = \
37                       ('architecture', 'distribution', 'type', 'version')
38
39     IMAGE_OS_PROP = (OS_DISTRO, OS_TYPE, OS_VERSION) = \
40                     ('os_distro', 'os_type', 'os_version')
41     toscatype = 'tosca.nodes.Compute'
42
43     ALLOWED_NOVA_SERVER_PROPS = \
44         ('admin_pass', 'availability_zone', 'block_device_mapping',
45          'block_device_mapping_v2', 'config_drive', 'diskConfig', 'flavor',
46          'flavor_update_policy', 'image', 'image_update_policy', 'key_name',
47          'metadata', 'name', 'networks', 'personality', 'reservation_id',
48          'scheduler_hints', 'security_groups', 'software_config_transport',
49          'user_data', 'user_data_format', 'user_data_update_policy')
50
51     def __init__(self, nodetemplate, csar_dir=None):
52         super(ToscaCompute, self).__init__(nodetemplate,
53                                            type='OS::Nova::Server',
54                                            csar_dir=csar_dir)
55         # List with associated hot port resources with this server
56         self.assoc_port_resources = []
57         pass
58
59     def handle_properties(self):
60         self.properties = self.translate_compute_flavor_and_image(
61             self.nodetemplate.get_capability('host'),
62             self.nodetemplate.get_capability('os'))
63         self.properties['user_data_format'] = 'SOFTWARE_CONFIG'
64         self.properties['software_config_transport'] = 'POLL_SERVER_HEAT'
65         tosca_props = self.get_tosca_props()
66         for key, value in tosca_props.items():
67             if key in self.ALLOWED_NOVA_SERVER_PROPS:
68                 self.properties[key] = value
69
70     # To be reorganized later based on new development in Glance and Graffiti
71     def translate_compute_flavor_and_image(self,
72                                            host_capability,
73                                            os_capability):
74         hot_properties = {}
75         host_cap_props = {}
76         os_cap_props = {}
77         image = None
78         flavor = None
79         if host_capability:
80             for prop in host_capability.get_properties_objects():
81                 host_cap_props[prop.name] = prop.value
82             # if HOST properties are not specified, we should not attempt to
83             # find best match of flavor
84             if host_cap_props:
85                 flavor = self._best_flavor(host_cap_props)
86         if os_capability:
87             for prop in os_capability.get_properties_objects():
88                 os_cap_props[prop.name] = prop.value
89             # if OS properties are not specified, we should not attempt to
90             # find best match of image
91             if os_cap_props:
92                 image = self._best_image(os_cap_props)
93         hot_properties['flavor'] = flavor
94         if image:
95             hot_properties['image'] = image
96         else:
97             hot_properties.pop('image', None)
98         return hot_properties
99
100     def _best_flavor(self, properties):
101         log.info(_('Choosing the best flavor for given attributes.'))
102         # Check whether user exported all required environment variables.
103         flavors = nova_flavors.get_flavors()
104
105         # start with all flavors
106         match_all = flavors.keys()
107
108         # TODO(anyone): Handle the case where the value contains something like
109         # get_input instead of a value.
110         # flavors that fit the CPU count
111         cpu = properties.get(self.NUM_CPUS)
112         if cpu is None:
113             self._log_compute_msg(self.NUM_CPUS, 'flavor')
114         match_cpu = self._match_flavors(match_all, flavors, self.NUM_CPUS, cpu)
115
116         # flavors that fit the mem size
117         mem = properties.get(self.MEM_SIZE)
118         if mem:
119             mem = translator.common.utils.MemoryUnit.convert_unit_size_to_num(
120                 mem, 'MB')
121         else:
122             self._log_compute_msg(self.MEM_SIZE, 'flavor')
123         match_cpu_mem = self._match_flavors(match_cpu, flavors,
124                                             self.MEM_SIZE, mem)
125         # flavors that fit the disk size
126         disk = properties.get(self.DISK_SIZE)
127         if disk:
128             disk = translator.common.utils.MemoryUnit.\
129                 convert_unit_size_to_num(disk, 'GB')
130         else:
131             self._log_compute_msg(self.DISK_SIZE, 'flavor')
132         match_cpu_mem_disk = self._match_flavors(match_cpu_mem, flavors,
133                                                  self.DISK_SIZE, disk)
134         # if multiple match, pick the flavor with the least memory
135         # the selection can be based on other heuristic, e.g. pick one with the
136         # least total resource
137         if len(match_cpu_mem_disk) > 1:
138             return self._least_flavor(match_cpu_mem_disk, flavors, 'mem_size')
139         elif len(match_cpu_mem_disk) == 1:
140             return match_cpu_mem_disk[0]
141         else:
142             return None
143
144     def _best_image(self, properties):
145         # Check whether user exported all required environment variables.
146         images = glance_images.get_images()
147         match_all = images.keys()
148         architecture = properties.get(self.ARCHITECTURE)
149         if architecture is None:
150             self._log_compute_msg(self.ARCHITECTURE, 'image')
151         match_arch = self._match_images(match_all, images,
152                                         [self.ARCHITECTURE], architecture)
153
154         image_type = properties.get(self.TYPE)
155         if image_type is None:
156             self._log_compute_msg(self.TYPE, 'image')
157         match_type = self._match_images(match_arch, images, [self.TYPE,
158                                                              self.OS_TYPE],
159                                         image_type)
160
161         distribution = properties.get(self.DISTRIBUTION)
162         if distribution is None:
163             self._log_compute_msg(self.DISTRIBUTION, 'image')
164         match_distribution = self._match_images(match_type, images,
165                                                 [self.DISTRIBUTION,
166                                                  self.OS_DISTRO],
167                                                 distribution)
168         version = properties.get(self.VERSION)
169         if version is None:
170             self._log_compute_msg(self.VERSION, 'image')
171         match_version = self._match_images(match_distribution, images,
172                                            [self.VERSION, self.OS_VERSION],
173                                            version)
174
175         if len(match_version):
176             return list(match_version)[0]
177
178     @staticmethod
179     def _match_flavors(this_list, this_dict, attr, size):
180         """Return from this list all flavors matching the attribute size."""
181         if not size:
182             return list(this_list)
183         matching_flavors = []
184         for flavor in this_list:
185             if isinstance(size, int):
186                 if this_dict[flavor][attr] >= size:
187                     matching_flavors.append(flavor)
188         log.debug(_('Returning list of flavors matching the attribute size.'))
189         return matching_flavors
190
191     @staticmethod
192     def _least_flavor(this_list, this_dict, attr):
193         """Return from this list the flavor with the smallest attr."""
194         least_flavor = this_list[0]
195         for flavor in this_list:
196             if this_dict[flavor][attr] < this_dict[least_flavor][attr]:
197                 least_flavor = flavor
198         return least_flavor
199
200     @staticmethod
201     def _match_images(this_list, this_dict, attr_list, prop):
202         if not prop:
203             return this_list
204         matching_images = []
205         for image in this_list:
206             for attr in attr_list:
207                 if attr in this_dict[image]:
208                     if this_dict[image][attr].lower() == str(prop).lower():
209                         matching_images.insert(0, image)
210                 else:
211                     matching_images.append(image)
212         return matching_images
213
214     def get_hot_attribute(self, attribute, args):
215         attr = {}
216         # Convert from a TOSCA attribute for a nodetemplate to a HOT
217         # attribute for the matching resource.  Unless there is additional
218         # runtime support, this should be a one to one mapping.
219
220         # Note: We treat private and public IP  addresses equally, but
221         # this will change in the future when TOSCA starts to support
222         # multiple private/public IP addresses.
223         log.debug(_('Converting TOSCA attribute for a nodetemplate to a HOT \
224                   attriute.'))
225         if attribute == 'private_address' or \
226            attribute == 'public_address':
227                 attr['get_attr'] = [self.name, 'first_address']
228
229         return attr
230
231     @staticmethod
232     def _log_compute_msg(prop, what):
233         msg = _('No value is provided for Compute capability '
234                 'property "%(prop)s". This may set an undesired "%(what)s" '
235                 'in the template.') % {'prop': prop, 'what': what}
236         log.warning(msg)