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