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