1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 # and others. All rights reserved.
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:
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
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)
32 def create_network_config(neutron, network):
34 Returns a NetworkConfig object
35 :param neutron: the neutron client
36 :param network: a SNAPS-OO Network domain object
40 name=network.name, network_type=network.type,
41 subnet_settings=create_subnet_config(neutron, network))
44 def create_security_group_config(neutron, security_group):
46 Returns a SecurityGroupConfig object
47 :param neutron: the neutron client
48 :param security_group: a SNAPS-OO SecurityGroup domain object
51 rules = neutron_utils.get_rules_by_security_group(neutron, security_group)
53 rule_settings = list()
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))
63 return SecurityGroupConfig(
64 name=security_group.name, description=security_group.description,
65 rule_settings=rule_settings)
68 def create_subnet_config(neutron, network):
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
77 subnets = neutron_utils.get_subnets_by_network(neutron, network)
78 for subnet in subnets:
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))
95 def create_router_config(neutron, router):
97 Returns a RouterConfig object
98 :param neutron: the neutron client
99 :param router: a SNAPS-OO Router domain object
104 if router.external_network_id:
105 network = neutron_utils.get_network_by_id(
106 neutron, router.external_network_id)
108 ext_net_name = network.name
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)
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'])
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'])
132 ip_list.append(ip_addr)
134 ports_tuple_list.append((network, ip_list))
136 port_settings = __create_port_config(neutron, ports_tuple_list)
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)
144 name=router.name, external_gateway=ext_net_name,
145 admin_state_up=router.admin_state_up,
146 port_settings=filtered_settings)
149 def create_volume_config(volume):
151 Returns a VolumeConfig object
152 :param volume: a SNAPS-OO Volume object
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)
162 def create_volume_type_config(volume_type):
164 Returns a VolumeTypeConfig object
165 :param volume_type: a SNAPS-OO VolumeType object
169 if volume_type.encryption:
170 if (volume_type.encryption.control_location
171 == ControlLocation.front_end.value):
172 control = ControlLocation.front_end
174 control = ControlLocation.back_end
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)
184 encrypt_settings = None
187 if volume_type.qos_spec:
188 qos_spec_name = volume_type.qos_spec.name
190 return VolumeTypeConfig(
191 name=volume_type.name, encryption=encrypt_settings,
192 qos_spec_name=qos_spec_name, public=volume_type.public)
195 def create_flavor_config(flavor):
197 Returns a FlavorConfig object
198 :param flavor: a FlavorConfig object
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)
207 def create_keypair_config(heat_cli, stack, keypair, pk_output_key):
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
218 outputs = heat_utils.get_outputs(heat_cli, stack)
219 for output in outputs:
220 if output.key == pk_output_key:
223 key_file = file_utils.save_string_to_file(
224 output.value, str(guid), 0o400)
226 # Use outputs, file and resources for the KeypairConfig
227 return KeypairConfig(
228 name=keypair.name, private_filepath=key_file.name)
230 return KeypairConfig(name=keypair.name)
233 def create_vm_inst_config(nova, neutron, server):
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
244 flavor_name = nova_utils.get_flavor_by_id(nova, server.flavor_id)
247 kwargs['name'] = server.name
248 kwargs['flavor'] = flavor_name
251 for net_name, ips in server.networks.items():
252 network = neutron_utils.get_network(neutron, network_name=net_name)
254 net_tuples.append((network, ips))
256 kwargs['port_settings'] = __create_port_config(
258 kwargs['security_group_names'] = server.sec_grp_names
259 kwargs['floating_ip_settings'] = __create_floatingip_config(
260 neutron, kwargs['port_settings'])
262 return VmInstanceConfig(**kwargs)
265 def __create_port_config(neutron, networks):
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
275 for network, ips in networks:
276 ports = neutron_utils.get_ports(neutron, network, ips)
278 if port.device_owner != 'network:dhcp':
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']})
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))
299 def __create_floatingip_config(neutron, port_settings):
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
308 base_fip_name = 'fip-'
313 for port_setting in port_settings:
314 setting_port = neutron_utils.get_port(neutron, port_setting)
316 network = neutron_utils.get_network(
317 neutron, network_name=port_setting.network_name)
318 network_ports = neutron_utils.get_ports(neutron, network)
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))
325 floating_ips = neutron_utils.get_floating_ips(neutron, fip_ports)
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)
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
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'])
345 kwargs['subnet_name'] = subnet.name
347 out.append(FloatingIpConfig(**kwargs))
354 def determine_image_config(glance, server, image_settings):
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
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:
370 def determine_keypair_config(heat_cli, stack, server, keypair_settings=None,
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
385 # Existing keypair being used by Heat Template
387 for keypair_setting in keypair_settings:
388 if server.keypair_name == keypair_setting.name:
389 return keypair_setting
391 # Keypair created by Heat template
393 outputs = heat_utils.get_outputs(heat_cli, stack)
394 for output in outputs:
395 if output.key == priv_key_key:
398 key_file = file_utils.save_string_to_file(
399 output.value, str(guid), 0o400)
401 # Use outputs, file and resources for the KeypairConfig
402 return KeypairConfig(
403 name=server.keypair_name, private_filepath=key_file.name)