4ad30fd26d4c063333440f67752d4ac22a2ad6b4
[snaps.git] / snaps / openstack / utils / settings_utils.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import uuid
16
17 from snaps import file_utils
18 from snaps.config.flavor import FlavorConfig
19 from snaps.config.keypair import KeypairConfig
20 from snaps.config.network import SubnetConfig, PortConfig, NetworkConfig
21 from snaps.config.router import RouterConfig
22 from snaps.config.security_group import (
23     SecurityGroupRuleConfig, SecurityGroupConfig)
24 from snaps.config.vm_inst import VmInstanceConfig, FloatingIpConfig
25 from snaps.config.volume import VolumeConfig
26 from snaps.config.volume_type import (
27     ControlLocation,  VolumeTypeEncryptionConfig, VolumeTypeConfig)
28 from snaps.openstack.utils import (
29     neutron_utils, nova_utils, heat_utils, glance_utils)
30
31
32 def create_network_config(neutron, network):
33     """
34     Returns a NetworkConfig object
35     :param neutron: the neutron client
36     :param network: a SNAPS-OO Network domain object
37     :return:
38     """
39     return NetworkConfig(
40         name=network.name, network_type=network.type,
41         subnet_settings=create_subnet_config(neutron, network))
42
43
44 def create_security_group_config(neutron, security_group):
45     """
46     Returns a SecurityGroupConfig object
47     :param neutron: the neutron client
48     :param security_group: a SNAPS-OO SecurityGroup domain object
49     :return:
50     """
51     rules = neutron_utils.get_rules_by_security_group(neutron, security_group)
52
53     rule_settings = list()
54     for rule in rules:
55         rule_settings.append(SecurityGroupRuleConfig(
56             sec_grp_name=security_group.name, description=rule.description,
57             direction=rule.direction, ethertype=rule.ethertype,
58             port_range_min=rule.port_range_min,
59             port_range_max=rule.port_range_max, protocol=rule.protocol,
60             remote_group_id=rule.remote_group_id,
61             remote_ip_prefix=rule.remote_ip_prefix))
62
63     return SecurityGroupConfig(
64         name=security_group.name, description=security_group.description,
65         rule_settings=rule_settings)
66
67
68 def create_subnet_config(neutron, network):
69     """
70     Returns a list of SubnetConfig objects for a given network
71     :param neutron: the OpenStack neutron client
72     :param network: the SNAPS-OO Network domain object
73     :return: a list
74     """
75     out = list()
76
77     subnets = neutron_utils.get_subnets_by_network(neutron, network)
78     for subnet in subnets:
79         kwargs = dict()
80         kwargs['cidr'] = subnet.cidr
81         kwargs['ip_version'] = subnet.ip_version
82         kwargs['name'] = subnet.name
83         kwargs['start'] = subnet.start
84         kwargs['end'] = subnet.end
85         kwargs['gateway_ip'] = subnet.gateway_ip
86         kwargs['enable_dhcp'] = subnet.enable_dhcp
87         kwargs['dns_nameservers'] = subnet.dns_nameservers
88         kwargs['host_routes'] = subnet.host_routes
89         kwargs['ipv6_ra_mode'] = subnet.ipv6_ra_mode
90         kwargs['ipv6_address_mode'] = subnet.ipv6_address_mode
91         out.append(SubnetConfig(**kwargs))
92     return out
93
94
95 def create_router_config(neutron, router):
96     """
97     Returns a RouterConfig object
98     :param neutron: the neutron client
99     :param router: a SNAPS-OO Router domain object
100     :return:
101     """
102     ext_net_name = None
103
104     if router.external_network_id:
105         network = neutron_utils.get_network_by_id(
106             neutron, router.external_network_id)
107         if network:
108             ext_net_name = network.name
109
110     ports_tuple_list = list()
111     if router.port_subnets:
112         for port, subnets in router.port_subnets:
113             network = neutron_utils.get_network_by_id(
114                 neutron, port.network_id)
115
116             ip_addrs = list()
117             if network and router.external_fixed_ips:
118                 for ext_fixed_ips in router.external_fixed_ips:
119                     for subnet in subnets:
120                         if ext_fixed_ips['subnet_id'] == subnet.id:
121                             ip_addrs.append(ext_fixed_ips['ip_address'])
122             else:
123                 for ip in port.ips:
124                     ip_addrs.append(ip)
125
126             ip_list = list()
127             if len(ip_addrs) > 0:
128                 for ip_addr in ip_addrs:
129                     if isinstance(ip_addr, dict):
130                         ip_list.append(ip_addr['ip_address'])
131                     else:
132                         ip_list.append(ip_addr)
133
134             ports_tuple_list.append((network, ip_list))
135
136     port_settings = __create_port_config(neutron, ports_tuple_list)
137
138     filtered_settings = list()
139     for port_setting in port_settings:
140         if port_setting.network_name != ext_net_name:
141             filtered_settings.append(port_setting)
142
143     return RouterConfig(
144         name=router.name, external_gateway=ext_net_name,
145         admin_state_up=router.admin_state_up,
146         port_settings=filtered_settings)
147
148
149 def create_volume_config(volume):
150     """
151     Returns a VolumeConfig object
152     :param volume: a SNAPS-OO Volume object
153     """
154
155     return VolumeConfig(
156         name=volume.name, description=volume.description,
157         size=volume.size, type_name=volume.type,
158         availability_zone=volume.availability_zone,
159         multi_attach=volume.multi_attach)
160
161
162 def create_volume_type_config(volume_type):
163     """
164     Returns a VolumeTypeConfig object
165     :param volume_type: a SNAPS-OO VolumeType object
166     """
167
168     control = None
169     if volume_type.encryption:
170         if (volume_type.encryption.control_location
171                 == ControlLocation.front_end.value):
172             control = ControlLocation.front_end
173         else:
174             control = ControlLocation.back_end
175
176     if volume_type and volume_type.encryption:
177         encrypt_settings = VolumeTypeEncryptionConfig(
178             name=volume_type.encryption.__class__,
179             provider_class=volume_type.encryption.provider,
180             control_location=control,
181             cipher=volume_type.encryption.cipher,
182             key_size=volume_type.encryption.key_size)
183     else:
184         encrypt_settings = None
185
186     qos_spec_name = None
187     if volume_type.qos_spec:
188         qos_spec_name = volume_type.qos_spec.name
189
190     return VolumeTypeConfig(
191         name=volume_type.name, encryption=encrypt_settings,
192         qos_spec_name=qos_spec_name, public=volume_type.public)
193
194
195 def create_flavor_config(flavor):
196     """
197     Returns a FlavorConfig object
198     :param flavor: a FlavorConfig object
199     """
200     return FlavorConfig(
201         name=flavor.name, flavor_id=flavor.id, ram=flavor.ram,
202         disk=flavor.disk, vcpus=flavor.vcpus, ephemeral=flavor.ephemeral,
203         swap=flavor.swap, rxtx_factor=flavor.rxtx_factor,
204         is_public=flavor.is_public)
205
206
207 def create_keypair_config(heat_cli, stack, keypair, pk_output_key):
208     """
209     Instantiates a KeypairConfig object from a Keypair domain objects
210     :param heat_cli: the heat client
211     :param stack: the Stack domain object
212     :param keypair: the Keypair SNAPS domain object
213     :param pk_output_key: the key to the heat template's outputs for retrieval
214                           of the private key file
215     :return: a KeypairConfig object
216     """
217     if pk_output_key:
218         outputs = heat_utils.get_outputs(heat_cli, stack)
219         for output in outputs:
220             if output.key == pk_output_key:
221                 # Save to file
222                 guid = uuid.uuid4()
223                 key_file = file_utils.save_string_to_file(
224                     output.value, str(guid), 0o400)
225
226                 # Use outputs, file and resources for the KeypairConfig
227                 return KeypairConfig(
228                     name=keypair.name, private_filepath=key_file.name)
229
230     return KeypairConfig(name=keypair.name)
231
232
233 def create_vm_inst_config(nova, neutron, server):
234     """
235     Returns a VmInstanceConfig object
236     note: if the server instance is not active, the PortSettings objects will
237     not be generated resulting in an invalid configuration
238     :param nova: the nova client
239     :param neutron: the neutron client
240     :param server: a SNAPS-OO VmInst domain object
241     :return:
242     """
243
244     flavor_name = nova_utils.get_flavor_by_id(nova, server.flavor_id)
245
246     kwargs = dict()
247     kwargs['name'] = server.name
248     kwargs['flavor'] = flavor_name
249
250     net_tuples = list()
251     for net_name, ips in server.networks.items():
252         network = neutron_utils.get_network(neutron, network_name=net_name)
253         if network:
254             net_tuples.append((network, ips))
255
256     kwargs['port_settings'] = __create_port_config(
257         neutron, net_tuples)
258     kwargs['security_group_names'] = server.sec_grp_names
259     kwargs['floating_ip_settings'] = __create_floatingip_config(
260         neutron, kwargs['port_settings'])
261
262     return VmInstanceConfig(**kwargs)
263
264
265 def __create_port_config(neutron, networks):
266     """
267     Returns a list of PortConfig objects based on the networks parameter
268     :param neutron: the neutron client
269     :param networks: a list of tuples where #1 is the SNAPS Network domain
270                      object and #2 is a list of IP addresses
271     :return:
272     """
273     out = list()
274
275     for network, ips in networks:
276         ports = neutron_utils.get_ports(neutron, network, ips)
277         for port in ports:
278             if port.device_owner != 'network:dhcp':
279                 ip_addrs = list()
280                 for ip_dict in port.ips:
281                     subnet = neutron_utils.get_subnet_by_id(
282                         neutron, ip_dict['subnet_id'])
283                     ip_addrs.append({'subnet_name': subnet.name,
284                                      'ip': ip_dict['ip_address']})
285
286                 kwargs = dict()
287                 if port.name:
288                     kwargs['name'] = port.name
289                 kwargs['network_name'] = network.name
290                 kwargs['mac_address'] = port.mac_address
291                 kwargs['allowed_address_pairs'] = port.allowed_address_pairs
292                 kwargs['admin_state_up'] = port.admin_state_up
293                 kwargs['ip_addrs'] = ip_addrs
294                 out.append(PortConfig(**kwargs))
295
296     return out
297
298
299 def __create_floatingip_config(neutron, port_settings):
300     """
301     Returns a list of FloatingIpConfig objects as they pertain to an
302     existing deployed server instance
303     :param neutron: the neutron client
304     :param port_settings: list of SNAPS-OO PortConfig objects
305     :return: a list of FloatingIpConfig objects or an empty list if no
306              floating IPs have been created
307     """
308     base_fip_name = 'fip-'
309     fip_ctr = 1
310     out = list()
311
312     fip_ports = list()
313     for port_setting in port_settings:
314         setting_port = neutron_utils.get_port(neutron, port_setting)
315         if setting_port:
316             network = neutron_utils.get_network(
317                 neutron, network_name=port_setting.network_name)
318             network_ports = neutron_utils.get_ports(neutron, network)
319             if network_ports:
320                 for setting_port in network_ports:
321                     if port_setting.mac_address == setting_port.mac_address:
322                         fip_ports.append((port_setting.name, setting_port))
323                         break
324
325     floating_ips = neutron_utils.get_floating_ips(neutron, fip_ports)
326
327     for port_id, floating_ip in floating_ips:
328         router = neutron_utils.get_router_by_id(neutron, floating_ip.router_id)
329         setting_port = neutron_utils.get_port_by_id(
330             neutron, floating_ip.port_id)
331         kwargs = dict()
332         kwargs['name'] = base_fip_name + str(fip_ctr)
333         kwargs['port_name'] = setting_port.name
334         kwargs['port_id'] = setting_port.id
335         kwargs['router_name'] = router.name
336
337         if setting_port:
338             for ip_dict in setting_port.ips:
339                 if ('ip_address' in ip_dict and
340                         'subnet_id' in ip_dict and
341                         ip_dict['ip_address'] == floating_ip.fixed_ip_address):
342                     subnet = neutron_utils.get_subnet_by_id(
343                         neutron, ip_dict['subnet_id'])
344                     if subnet:
345                         kwargs['subnet_name'] = subnet.name
346
347         out.append(FloatingIpConfig(**kwargs))
348
349         fip_ctr += 1
350
351     return out
352
353
354 def determine_image_config(glance, server, image_settings):
355     """
356     Returns a ImageConfig object from the list that matches the name in one
357     of the image_settings parameter
358     :param glance: the glance client
359     :param server: a SNAPS-OO VmInst domain object
360     :param image_settings: list of ImageConfig objects
361     :return: ImageConfig or None
362     """
363     if image_settings:
364         for image_setting in image_settings:
365             image = glance_utils.get_image_by_id(glance, server.image_id)
366             if image and image.name == image_setting.name:
367                 return image_setting
368
369
370 def determine_keypair_config(heat_cli, stack, server, keypair_settings=None,
371                              priv_key_key=None):
372     """
373     Returns a KeypairConfig object from the list that matches the
374     server.keypair_name value in the keypair_settings parameter if not None,
375     else if the output_key is not None, the output's value when contains the
376     string 'BEGIN RSA PRIVATE KEY', this value will be stored into a file and
377     encoded into the KeypairConfig object returned
378     :param heat_cli: the OpenStack heat client
379     :param stack: a SNAPS-OO Stack domain object
380     :param server: a SNAPS-OO VmInst domain object
381     :param keypair_settings: list of KeypairConfig objects
382     :param priv_key_key: the stack options that holds the private key value
383     :return: KeypairConfig or None
384     """
385     # Existing keypair being used by Heat Template
386     if keypair_settings:
387         for keypair_setting in keypair_settings:
388             if server.keypair_name == keypair_setting.name:
389                 return keypair_setting
390
391     # Keypair created by Heat template
392     if priv_key_key:
393         outputs = heat_utils.get_outputs(heat_cli, stack)
394         for output in outputs:
395             if output.key == priv_key_key:
396                 # Save to file
397                 guid = uuid.uuid4()
398                 key_file = file_utils.save_string_to_file(
399                     output.value, str(guid), 0o400)
400
401                 # Use outputs, file and resources for the KeypairConfig
402                 return KeypairConfig(
403                     name=server.keypair_name, private_filepath=key_file.name)