2e290633084e6862173f6c7c3fe0a76471c6f2f2
[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.openstack.create_flavor import FlavorSettings
19 from snaps.openstack.create_instance import (
20     VmInstanceSettings, FloatingIpSettings)
21 from snaps.openstack.create_keypairs import KeypairSettings
22 from snaps.openstack.create_network import (
23     PortSettings, SubnetSettings, NetworkSettings)
24 from snaps.openstack.create_router import RouterSettings
25 from snaps.openstack.create_volume import VolumeSettings
26 from snaps.openstack.create_volume_type import (
27     VolumeTypeSettings, VolumeTypeEncryptionSettings, ControlLocation)
28 from snaps.openstack.utils import (
29     neutron_utils, nova_utils, heat_utils, glance_utils)
30
31
32 def create_network_settings(neutron, network):
33     """
34     Returns a NetworkSettings object
35     :param neutron: the neutron client
36     :param network: a SNAPS-OO Network domain object
37     :return:
38     """
39     return NetworkSettings(
40         name=network.name, network_type=network.type,
41         subnet_settings=create_subnet_settings(neutron, network))
42
43
44 def create_subnet_settings(neutron, network):
45     """
46     Returns a list of SubnetSettings objects for a given network
47     :param neutron: the OpenStack neutron client
48     :param network: the SNAPS-OO Network domain object
49     :return: a list
50     """
51     out = list()
52
53     subnets = neutron_utils.get_subnets_by_network(neutron, network)
54     for subnet in subnets:
55         kwargs = dict()
56         kwargs['cidr'] = subnet.cidr
57         kwargs['ip_version'] = subnet.ip_version
58         kwargs['name'] = subnet.name
59         kwargs['start'] = subnet.start
60         kwargs['end'] = subnet.end
61         kwargs['gateway_ip'] = subnet.gateway_ip
62         kwargs['enable_dhcp'] = subnet.enable_dhcp
63         kwargs['dns_nameservers'] = subnet.dns_nameservers
64         kwargs['host_routes'] = subnet.host_routes
65         kwargs['ipv6_ra_mode'] = subnet.ipv6_ra_mode
66         kwargs['ipv6_address_mode'] = subnet.ipv6_address_mode
67         out.append(SubnetSettings(**kwargs))
68     return out
69
70
71 def create_router_settings(neutron, router):
72     """
73     Returns a RouterSettings object
74     :param neutron: the neutron client
75     :param router: a SNAPS-OO Router domain object
76     :return:
77     """
78     ext_net_name = None
79
80     if router.external_network_id:
81         network = neutron_utils.get_network_by_id(
82             neutron, router.external_network_id)
83         if network:
84             ext_net_name = network.name
85
86     ports_tuple_list = list()
87     if router.port_subnets:
88         for port, subnets in router.port_subnets:
89             network = neutron_utils.get_network_by_id(
90                 neutron, port.network_id)
91
92             ip_addrs = list()
93             if network and router.external_fixed_ips:
94                 for ext_fixed_ips in router.external_fixed_ips:
95                     for subnet in subnets:
96                         if ext_fixed_ips['subnet_id'] == subnet.id:
97                             ip_addrs.append(ext_fixed_ips['ip_address'])
98             else:
99                 for ip in port.ips:
100                     ip_addrs.append(ip)
101
102             ip_list = list()
103             if len(ip_addrs) > 0:
104                 for ip_addr in ip_addrs:
105                     if isinstance(ip_addr, dict):
106                         ip_list.append(ip_addr['ip_address'])
107                     else:
108                         ip_list.append(ip_addr)
109
110             ports_tuple_list.append((network, ip_list))
111
112     port_settings = __create_port_settings(neutron, ports_tuple_list)
113
114     filtered_settings = list()
115     for port_setting in port_settings:
116         if port_setting.network_name != ext_net_name:
117             filtered_settings.append(port_setting)
118
119     return RouterSettings(
120         name=router.name, external_gateway=ext_net_name,
121         admin_state_up=router.admin_state_up,
122         port_settings=filtered_settings)
123
124
125 def create_volume_settings(volume):
126     """
127     Returns a VolumeSettings object
128     :param volume: a SNAPS-OO Volume object
129     """
130
131     return VolumeSettings(
132         name=volume.name, description=volume.description,
133         size=volume.size, type_name=volume.type,
134         availability_zone=volume.availability_zone,
135         multi_attach=volume.multi_attach)
136
137
138 def create_volume_type_settings(volume_type):
139     """
140     Returns a VolumeTypeSettings object
141     :param volume_type: a SNAPS-OO VolumeType object
142     """
143
144     control = None
145     if volume_type.encryption:
146         if (volume_type.encryption.control_location
147                 == ControlLocation.front_end.value):
148             control = ControlLocation.front_end
149         else:
150             control = ControlLocation.back_end
151
152     encrypt_settings = VolumeTypeEncryptionSettings(
153         name=volume_type.encryption.__class__,
154         provider_class=volume_type.encryption.provider,
155         control_location=control,
156         cipher=volume_type.encryption.cipher,
157         key_size=volume_type.encryption.key_size)
158
159     qos_spec_name = None
160     if volume_type.qos_spec:
161         qos_spec_name = volume_type.qos_spec.name
162
163     return VolumeTypeSettings(
164         name=volume_type.name, encryption=encrypt_settings,
165         qos_spec_name=qos_spec_name, public=volume_type.public)
166
167
168 def create_flavor_settings(flavor):
169     """
170     Returns a VolumeSettings object
171     :param flavor: a SNAPS-OO Volume object
172     """
173     return FlavorSettings(
174         name=flavor.name, flavor_id=flavor.id, ram=flavor.ram,
175         disk=flavor.disk, vcpus=flavor.vcpus, ephemeral=flavor.ephemeral,
176         swap=flavor.swap, rxtx_factor=flavor.rxtx_factor,
177         is_public=flavor.is_public)
178
179
180 def create_keypair_settings(heat_cli, stack, keypair, pk_output_key):
181     """
182     Instantiates a KeypairSettings object from a Keypair domain objects
183     :param heat_cli: the heat client
184     :param stack: the Stack domain object
185     :param keypair: the Keypair SNAPS domain object
186     :param pk_output_key: the key to the heat template's outputs for retrieval
187                           of the private key file
188     :return: a KeypairSettings object
189     """
190     if pk_output_key:
191         outputs = heat_utils.get_outputs(heat_cli, stack)
192         for output in outputs:
193             if output.key == pk_output_key:
194                 # Save to file
195                 guid = uuid.uuid4()
196                 key_file = file_utils.save_string_to_file(
197                     output.value, str(guid), 0o400)
198
199                 # Use outputs, file and resources for the KeypairSettings
200                 return KeypairSettings(
201                     name=keypair.name, private_filepath=key_file.name)
202
203     return KeypairSettings(name=keypair.name)
204
205
206 def create_vm_inst_settings(nova, neutron, server):
207     """
208     Returns a NetworkSettings object
209     :param nova: the nova client
210     :param neutron: the neutron client
211     :param server: a SNAPS-OO VmInst domain object
212     :return:
213     """
214
215     flavor_name = nova_utils.get_flavor_by_id(nova, server.flavor_id)
216
217     kwargs = dict()
218     kwargs['name'] = server.name
219     kwargs['flavor'] = flavor_name
220
221     net_tuples = list()
222     for net_name, ips in server.networks.items():
223         network = neutron_utils.get_network(neutron, network_name=net_name)
224         if network:
225             net_tuples.append((network, ips))
226
227     kwargs['port_settings'] = __create_port_settings(
228         neutron, net_tuples)
229     kwargs['security_group_names'] = server.sec_grp_names
230     kwargs['floating_ip_settings'] = __create_floatingip_settings(
231         neutron, kwargs['port_settings'])
232
233     return VmInstanceSettings(**kwargs)
234
235
236 def __create_port_settings(neutron, networks):
237     """
238     Returns a list of port settings based on the networks parameter
239     :param neutron: the neutron client
240     :param networks: a list of tuples where #1 is the SNAPS Network domain
241                      object and #2 is a list of IP addresses
242     :return:
243     """
244     out = list()
245
246     for network, ips in networks:
247         ports = neutron_utils.get_ports(neutron, network, ips)
248         for port in ports:
249             if port.device_owner != 'network:dhcp':
250                 ip_addrs = list()
251                 for ip_dict in port.ips:
252                     subnet = neutron_utils.get_subnet_by_id(
253                         neutron, ip_dict['subnet_id'])
254                     ip_addrs.append({'subnet_name': subnet.name,
255                                      'ip': ip_dict['ip_address']})
256
257                 kwargs = dict()
258                 if port.name:
259                     kwargs['name'] = port.name
260                 kwargs['network_name'] = network.name
261                 kwargs['mac_address'] = port.mac_address
262                 kwargs['allowed_address_pairs'] = port.allowed_address_pairs
263                 kwargs['admin_state_up'] = port.admin_state_up
264                 kwargs['ip_addrs'] = ip_addrs
265                 out.append(PortSettings(**kwargs))
266
267     return out
268
269
270 def __create_floatingip_settings(neutron, port_settings):
271     """
272     Returns a list of FloatingIPSettings objects as they pertain to an
273     existing deployed server instance
274     :param neutron: the neutron client
275     :param port_settings: list of SNAPS-OO PortSettings objects
276     :return: a list of FloatingIPSettings objects or an empty list if no
277              floating IPs have been created
278     """
279     base_fip_name = 'fip-'
280     fip_ctr = 1
281     out = list()
282
283     fip_ports = list()
284     for port_setting in port_settings:
285         setting_port = neutron_utils.get_port(neutron, port_setting)
286         if setting_port:
287             network = neutron_utils.get_network(
288                 neutron, network_name=port_setting.network_name)
289             network_ports = neutron_utils.get_ports(neutron, network)
290             if network_ports:
291                 for setting_port in network_ports:
292                     if port_setting.mac_address == setting_port.mac_address:
293                         fip_ports.append((port_setting.name, setting_port))
294                         break
295
296     floating_ips = neutron_utils.get_floating_ips(neutron, fip_ports)
297
298     for port_id, floating_ip in floating_ips:
299         router = neutron_utils.get_router_by_id(neutron, floating_ip.router_id)
300         setting_port = neutron_utils.get_port_by_id(
301             neutron, floating_ip.port_id)
302         kwargs = dict()
303         kwargs['name'] = base_fip_name + str(fip_ctr)
304         kwargs['port_name'] = setting_port.name
305         kwargs['port_id'] = setting_port.id
306         kwargs['router_name'] = router.name
307
308         if setting_port:
309             for ip_dict in setting_port.ips:
310                 if ('ip_address' in ip_dict and
311                         'subnet_id' in ip_dict and
312                         ip_dict['ip_address'] == floating_ip.fixed_ip_address):
313                     subnet = neutron_utils.get_subnet_by_id(
314                         neutron, ip_dict['subnet_id'])
315                     if subnet:
316                         kwargs['subnet_name'] = subnet.name
317
318         out.append(FloatingIpSettings(**kwargs))
319
320         fip_ctr += 1
321
322     return out
323
324
325 def determine_image_settings(glance, server, image_settings):
326     """
327     Returns a ImageSettings object from the list that matches the name in one
328     of the image_settings parameter
329     :param glance: the glance client
330     :param server: a SNAPS-OO VmInst domain object
331     :param image_settings: list of ImageSettings objects
332     :return: ImageSettings or None
333     """
334     if image_settings:
335         for image_setting in image_settings:
336             image = glance_utils.get_image_by_id(glance, server.image_id)
337             if image and image.name == image_setting.name:
338                 return image_setting
339
340
341 def determine_keypair_settings(heat_cli, stack, server, keypair_settings=None,
342                                priv_key_key=None):
343     """
344     Returns a KeypairSettings object from the list that matches the
345     server.keypair_name value in the keypair_settings parameter if not None,
346     else if the output_key is not None, the output's value when contains the
347     string 'BEGIN RSA PRIVATE KEY', this value will be stored into a file and
348     encoded into the KeypairSettings object returned
349     :param heat_cli: the OpenStack heat client
350     :param stack: a SNAPS-OO Stack domain object
351     :param server: a SNAPS-OO VmInst domain object
352     :param keypair_settings: list of KeypairSettings objects
353     :param priv_key_key: the stack options that holds the private key value
354     :return: KeypairSettings or None
355     """
356     # Existing keypair being used by Heat Template
357     if keypair_settings:
358         for keypair_setting in keypair_settings:
359             if server.keypair_name == keypair_setting.name:
360                 return keypair_setting
361
362     # Keypair created by Heat template
363     if priv_key_key:
364         outputs = heat_utils.get_outputs(heat_cli, stack)
365         for output in outputs:
366             if output.key == priv_key_key:
367                 # Save to file
368                 guid = uuid.uuid4()
369                 key_file = file_utils.save_string_to_file(
370                     output.value, str(guid), 0o400)
371
372                 # Use outputs, file and resources for the KeypairSettings
373                 return KeypairSettings(
374                     name=server.keypair_name, private_filepath=key_file.name)