blob: ea9c3467f75b77dd484f9924d8bc7e225e3e3bc4 [file] [log] [blame]
Jiri Broulikf1b3aa42017-01-26 17:08:44 +01001# -*- coding: utf-8 -*-
2'''
3Management of Neutron resources
4===============================
5:depends: - neutronclient Python module
6:configuration: See :py:mod:`salt.modules.neutron` for setup instructions.
7.. code-block:: yaml
Your Name96fdc0a2017-05-05 12:56:28 +00008 neutronng network present:
9 neutronng.network_present:
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010010 - name: Netone
11 - provider_physical_network: PHysnet1
12 - provider_network_type: vlan
13'''
14import logging
15from functools import wraps
16LOG = logging.getLogger(__name__)
17
18
19def __virtual__():
20 '''
21 Only load if neutron module is present in __salt__
22 '''
23 return 'neutronng' if 'neutron.list_networks' in __salt__ else False
24
25
26def _test_call(method):
27 (resource, functionality) = method.func_name.split('_')
28 if functionality == 'present':
29 functionality = 'updated'
30 else:
31 functionality = 'removed'
32
33 @wraps(method)
34 def check_for_testing(name, *args, **kwargs):
35 if __opts__.get('test', None):
36 return _no_change(name, resource, test=functionality)
37 return method(name, *args, **kwargs)
38 return check_for_testing
39
40
41def _neutron_module_call(method, *args, **kwargs):
42 return __salt__['neutronng.{0}'.format(method)](*args, **kwargs)
43
Your Name96fdc0a2017-05-05 12:56:28 +000044def _get_tenant_id(tenant_name, *args, **kwargs):
45 try:
46 tenant_id = __salt__['keystone.tenant_get'](
47 name=tenant_name, **kwargs)[tenant_name]['id']
48 except:
49 tenant_id = None
50 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
51 kwargs.get('connection_user')))
52 return tenant_id
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010053
Richard Felklaac256a2017-03-23 15:43:49 +010054def _auth(profile=None, endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010055 '''
56 Set up neutron credentials
57 '''
58 if profile:
59 credentials = __salt__['config.option'](profile)
60 user = credentials['keystone.user']
61 password = credentials['keystone.password']
62 tenant = credentials['keystone.tenant']
63 auth_url = credentials['keystone.auth_url']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010064 kwargs = {
65 'connection_user': user,
66 'connection_password': password,
67 'connection_tenant': tenant,
Richard Felkl22008f92017-03-29 16:14:24 +020068 'connection_auth_url': auth_url,
Your Name96fdc0a2017-05-05 12:56:28 +000069 'connection_endpoint_type': endpoint_type,
70 'profile': profile
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010071 }
72
73 return kwargs
74
75@_test_call
76def network_present(name=None,
Your Name96fdc0a2017-05-05 12:56:28 +000077 network_id=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +010078 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010079 provider_network_type=None,
80 provider_physical_network=None,
81 router_external=None,
82 admin_state_up=None,
83 shared=None,
84 provider_segmentation_id=None,
Richard Felklaac256a2017-03-23 15:43:49 +010085 profile=None,
Oleg Iurchenko8cf6cf52017-09-18 15:44:03 +030086 endpoint_type=None,
87 dns_domain=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010088 '''
89 Ensure that the neutron network is present with the specified properties.
90 name
91 The name of the network to manage
92 '''
Richard Felklaac256a2017-03-23 15:43:49 +010093 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +000094 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
95
96 existing_networks = _neutron_module_call(
97 'list_networks', name=name, tenant_id=tenant_id,
98 **connection_args)['networks']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010099 network_arguments = _get_non_null_args(
100 name=name,
101 provider_network_type=provider_network_type,
102 provider_physical_network=provider_physical_network,
103 router_external=router_external,
104 admin_state_up=admin_state_up,
105 shared=shared,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100106 tenant_id=tenant_id,
Oleg Iurchenko8cf6cf52017-09-18 15:44:03 +0300107 provider_segmentation_id=provider_segmentation_id,
108 dns_domain=dns_domain)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100109
Your Name96fdc0a2017-05-05 12:56:28 +0000110 if len(existing_networks) == 0:
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100111 network_arguments.update(connection_args)
Your Name96fdc0a2017-05-05 12:56:28 +0000112 res = _neutron_module_call(
113 'create_network', **network_arguments)['network']
Jiri Broulik5368cc52017-02-08 18:53:59 +0100114
Your Name96fdc0a2017-05-05 12:56:28 +0000115 if res.get('name') == name:
116 return _created(name, 'network', res['name'])
117
118 elif len(existing_networks) > 1:
119 LOG.error("More than one network found with the name: {0}".format(
120 name))
121
122 elif len(existing_networks) == 1:
123 existing_network = existing_networks[0]
124 LOG.info('CONNECTION STRINGS' + str(connection_args))
125 LOG.info('existing ' + str(existing_network))
126 LOG.info('new ' + str(network_arguments))
127 existing_network = dict((key.replace(':', '_', 1), value)
128 for key, value in
129 existing_network.iteritems())
130 # generate differential
131 diff = dict((key, value) for key, value in network_arguments.iteritems()
132 if existing_network.get(key, None) != value)
133 if diff:
134 # update the changes
135 network_arguments = diff.copy()
136 network_arguments.update(connection_args)
137 try:
138 LOG.debug('updating network {0} with changes {1}'.format(
139 name, str(diff)))
140 _neutron_module_call('update_network',
141 existing_network['id'],
142 **network_arguments)
143 changes_dict = _created(name, 'network', diff)
144 changes_dict['comment'] = '{1} {0} updated'.format(name, 'network')
145 return changes_dict
146 except:
147 LOG.error('Could not update network {0}'.format(name))
148 return _update_failed(name, 'network')
149 return _no_change(name, 'network')
150 return _create_failed(name, 'network')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100151
152
153@_test_call
Your Name96fdc0a2017-05-05 12:56:28 +0000154def network_absent(name, network_id=None, profile=None, endpoint_type=None):
Richard Felklaac256a2017-03-23 15:43:49 +0100155 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +0000156 identifier = network_id or name
157 _neutron_module_call(
158 'delete_network', identifier, **connection_args)
159 return _absent(identifier, 'network')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100160
161
162@_test_call
Your Name96fdc0a2017-05-05 12:56:28 +0000163def subnet_present(name,
164 network_name=None,
165 network_id=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100166 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100167 cidr=None,
168 ip_version=4,
169 enable_dhcp=True,
170 allocation_pools=None,
171 gateway_ip=None,
172 dns_nameservers=None,
173 host_routes=None,
Richard Felklaac256a2017-03-23 15:43:49 +0100174 profile=None,
175 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100176 '''
177 Ensure that the neutron subnet is present with the specified properties.
178 name
179 The name of the subnet to manage
180 '''
Your Name96fdc0a2017-05-05 12:56:28 +0000181 if network_name is None and network_id is None:
182 LOG.error("Network identificator name or uuid should be provided.")
183 return _create_failed(name, 'subnet')
184
Richard Felklaac256a2017-03-23 15:43:49 +0100185 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +0000186 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
187
188 existing_subnets = _neutron_module_call(
189 'list_subnets', tenant_id=tenant_id, name=name,
190 **connection_args)['subnets']
191
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100192 subnet_arguments = _get_non_null_args(
193 name=name,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100194 cidr=cidr,
195 ip_version=ip_version,
196 enable_dhcp=enable_dhcp,
197 allocation_pools=allocation_pools,
198 gateway_ip=gateway_ip,
199 dns_nameservers=dns_nameservers,
200 host_routes=host_routes)
Your Name96fdc0a2017-05-05 12:56:28 +0000201
202 if network_id is None and network_name:
203 existing_networks = _neutron_module_call(
204 'list_networks', tenant_id=tenant_id, name=network_name,
205 **connection_args)['networks']
206 if len(existing_networks) == 0:
207 LOG.error("Can't find network with name: {0}".format(network_name))
208 elif len(existing_networks) == 1:
209 network_id = existing_networks[0]['id']
210 elif len(existing_networks) > 1:
211 LOG.error("Multiple networks with name: {0} found.".format(
212 network_name))
213
214 if network_id is None:
215 return _create_failed(name, 'subnet')
216
217 subnet_arguments['network_id'] = network_id
218
219 if len(existing_subnets) == 0:
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100220 subnet_arguments.update(connection_args)
Your Name96fdc0a2017-05-05 12:56:28 +0000221 res = _neutron_module_call('create_subnet', tenant_id=tenant_id,
222 **subnet_arguments)['subnet']
223 if res.get('name') == name:
224 return _created(name, 'subnet', res)
225 return _create_failed(name, 'subnet')
226
227 elif len(existing_subnets) == 1:
228 existing_subnet = existing_subnets[0]
229
230 # create differential
231 LOG.error('existing ' + str(existing_subnet))
232 LOG.error('new ' + str(subnet_arguments))
233 diff = dict((key, value) for key, value in subnet_arguments.iteritems()
234 if existing_subnet.get(key, None) != value)
235 if not diff:
236 return _no_change(name, 'subnet')
237
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100238 # update the changes
239 subnet_arguments = diff.copy()
240 subnet_arguments.update(connection_args)
241 try:
242 LOG.debug('updating subnet {0} with changes {1}'.format(
243 name, str(diff)))
244 _neutron_module_call('update_subnet',
245 existing_subnet['id'],
246 **subnet_arguments)
247 changes_dict = _created(name, 'subnet', diff)
248 changes_dict['comment'] = '{1} {0} updated'.format(name, 'subnet')
249 return changes_dict
250 except:
Your Name96fdc0a2017-05-05 12:56:28 +0000251 LOG.error('Could not update subnet {0}'.format(name))
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100252 return _update_failed(name, 'subnet')
Your Name96fdc0a2017-05-05 12:56:28 +0000253
254 elif len(existing_subnets) > 1:
255 LOG.error("Multiple subnets with name: {0} found".format(
256 name))
257 return _create_failed(name, 'network')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100258
259
260@_test_call
Your Name96fdc0a2017-05-05 12:56:28 +0000261def subnet_absent(name, subnet_id=None, profile=None, endpoint_type=None):
Richard Felklaac256a2017-03-23 15:43:49 +0100262 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +0000263 identifier = subnet_id or name
264 _neutron_module_call(
265 'delete_subnet', identifier, **connection_args)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100266 return _absent(name, 'subnet')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100267
268
269@_test_call
270def router_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100271 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100272 gateway_network=None,
273 interfaces=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100274 admin_state_up=True,
Richard Felklaac256a2017-03-23 15:43:49 +0100275 profile=None,
276 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100277 '''
278 Ensure that the neutron router is present with the specified properties.
279 name
280 The name of the subnet to manage
281 gateway_network
282 The network that would be the router's default gateway
283 interfaces
284 list of subnets the router attaches to
285 '''
Richard Felklaac256a2017-03-23 15:43:49 +0100286 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100287 tenant_name = tenant
288 try:
289 tenant_id = __salt__['keystone.tenant_get'](
290 name=tenant_name, **connection_args)[tenant_name]['id']
291 except:
292 tenant_id = None
293 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
294 connection_args['connection_user']))
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100295 existing_router = _neutron_module_call(
296 'list_routers', name=name, **connection_args)
297 if not existing_router:
Jiri Broulik5368cc52017-02-08 18:53:59 +0100298 _neutron_module_call('create_router', name=name, tenant_id=tenant_id, admin_state_up=admin_state_up, **connection_args)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100299 created_router = _neutron_module_call(
300 'list_routers', name=name, **connection_args)
301 if created_router:
302 router_id = created_router[name]['id']
303 network = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100304 'list_networks', name=gateway_network, **connection_args)["networks"]
305 #TODO test for more networks
306 gateway_network_id = network[0]['id']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100307 _neutron_module_call('router_gateway_set',
308 router_id=router_id,
309 external_gateway=gateway_network_id,
310 **connection_args)
311 for interface in interfaces:
312 subnet = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100313 'list_subnets', name=interface, **connection_args)["subnets"]
314 subnet_id = subnet[0]['id']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100315 _neutron_module_call('router_add_interface',
316 router_id=router_id,
317 subnet_id=subnet_id,
318 **connection_args)
319 return _created(name,
320 'router',
321 _neutron_module_call('list_routers',
322 name=name,
323 **connection_args))
324 return _create_failed(name, 'router')
325
326 router_id = existing_router[name]['id']
327 existing_router = existing_router[name]
328 diff = {}
Jiri Broulik5368cc52017-02-08 18:53:59 +0100329 if ( admin_state_up == True or admin_state_up == False ) and existing_router['admin_state_up'] != admin_state_up:
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100330 diff.update({'admin_state_up': admin_state_up})
331 if gateway_network:
332 network = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100333 'list_networks', name=gateway_network, **connection_args)["networks"]
334 gateway_network_id = network[0]['id']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100335 if not existing_router['external_gateway_info'] and not existing_router['external_gateway_info'] == None:
336 if existing_router['external_gateway_info']['network_id'] != gateway_network_id:
337 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulik5368cc52017-02-08 18:53:59 +0100338 elif not existing_router['external_gateway_info'] == None:
339 if not 'network_id' in existing_router['external_gateway_info'] or existing_router['external_gateway_info']['network_id'] != gateway_network_id:
340 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100341 if diff:
342 # update the changes
343 router_args = diff.copy()
344 router_args.update(connection_args)
345 try:
346 _neutron_module_call('update_router', existing_router['id'], **router_args)
347 changes_dict = _created(name, 'router', diff)
348 changes_dict['comment'] = 'Router {0} updated'.format(name)
349 return changes_dict
350 except:
351 LOG.exception('Router {0} could not be updated'.format(name))
352 return _update_failed(name, 'router')
353 return _no_change(name, 'router')
354
Jiri Broulikde2e2902017-02-13 15:03:47 +0100355
356def floatingip_present(name=None,
357 tenant_name=None,
358 subnet=None,
359 tenant=None,
360 network=None,
361 port_id=None,
362 fip_exists=False,
Richard Felklaac256a2017-03-23 15:43:49 +0100363 profile=None,
364 endpoint_type=None):
Jiri Broulikde2e2902017-02-13 15:03:47 +0100365 '''
366 Ensure that the floating ip address is present for an instance
367 '''
368 instance_id = __salt__['novang.server_get'](name=name, tenant_name=tenant_name, profile=profile)
369 subnet_name = subnet
Richard Felklaac256a2017-03-23 15:43:49 +0100370 connection_args = _auth(profile, endpoint_type)
Jiri Broulikde2e2902017-02-13 15:03:47 +0100371 existing_subnet = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100372 'list_subnets', name=subnet_name, **connection_args)["subnets"]
373 subnet_id = existing_subnet[0]['id']
Jiri Broulikde2e2902017-02-13 15:03:47 +0100374
375 ret = {}
376 existing_ports = _neutron_module_call(
377 'list_ports', **connection_args)
378 existing_floatingips = _neutron_module_call(
379 'list_floatingips', **connection_args)
380
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100381 tenant = __salt__['keystone.tenant_get'](name=tenant_name, **connection_args)
Jiri Broulikde2e2902017-02-13 15:03:47 +0100382 tenant_id = tenant[tenant_name]['id']
383 existing_network = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100384 'list_networks', name=network, **connection_args)["networks"]
385 floating_network_id = existing_network[0]['id']
Jiri Broulikde2e2902017-02-13 15:03:47 +0100386
387 for key, value in existing_ports.iteritems():
388 try:
389 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
390 port_id=value['id']
391 except:
392 pass
393 for key, value in existing_floatingips.iteritems():
394 try:
395 if value['floating_network_id'] == floating_network_id and value['port_id'] == port_id and value['tenant_id'] == tenant_id:
396 fip_exists = True
397 break
398 except:
399 pass
400
401 if fip_exists == False:
402 for key, value in existing_ports.iteritems():
403 try:
404 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
405 ret = _neutron_module_call('create_floatingip', floating_network_id=floating_network_id, port_id=value['id'], tenant_id=tenant_id, **connection_args)
406 except:
407 pass
408 return _created('port', 'floatingip', ret)
409 else:
410 return _no_change('for instance {0}'.format(name), 'floatingip')
411
Your Name96fdc0a2017-05-05 12:56:28 +0000412
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100413def security_group_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100414 tenant=None,
Elena Ezhova46b02b02017-07-07 16:58:45 +0400415 description='',
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100416 rules=[],
Richard Felklaac256a2017-03-23 15:43:49 +0100417 profile=None,
418 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100419 '''
420 Ensure that the security group is present with the specified properties.
421 name
422 The name of the security group
423 description
424 The description of the security group
425 rules
426 list of rules to be added to the given security group
427 '''
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100428 # If the user is an admin, he's able to see the security groups from
429 # other tenants. In this case, we'll use the tenant id to get an existing
430 # security group.
Richard Felklaac256a2017-03-23 15:43:49 +0100431 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100432 tenant_name = tenant
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100433 try:
434 tenant_id = __salt__['keystone.tenant_get'](
435 name=tenant_name, **connection_args)[tenant_name]['id']
436 except:
437 tenant_id = None
438 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
439 connection_args['connection_user']))
440 if tenant_id:
441 security_group = _neutron_module_call(
442 'list_security_groups', name=name, tenant_id=tenant_id,
443 **connection_args)
444 else:
445 security_group = _neutron_module_call(
446 'list_security_groups', name=name, **connection_args)
447
448 if not security_group:
449 # Create the security group as it doesn't exist already.
450 security_group_id = _neutron_module_call('create_security_group',
451 name=name,
452 description=description,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100453 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100454 **connection_args)
455 else:
456 security_group_id = security_group[name]['id']
457
458 # Set the missing rules attributes (in case the user didn't specify them
459 # in pillar) to some default values.
460 rules_attributes_defaults = {
461 'direction': 'ingress',
462 'ethertype': 'IPv4',
463 'protocol': 'TCP',
464 'port_range_min': None,
465 'port_range_max': None,
466 'remote_ip_prefix': None
467 }
468 for rule in rules:
469 for attribute in rules_attributes_defaults.keys():
470 if not rule.has_key(attribute):
471 rule[attribute] = rules_attributes_defaults[attribute]
472
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100473 # Remove all the duplicates rules given by the user in pillar.
474 unique_rules = []
475 for rule in rules:
476 if rule not in unique_rules:
477 unique_rules.append(rule)
478
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100479 # Get the existing security group rules.
480 existing_rules = _neutron_module_call(
481 'list_security_groups',
482 id=security_group_id,
483 **connection_args)[name]['security_group_rules']
484
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100485 new_rules = {}
486 for rule in unique_rules:
487 rule_found = False
488 for existing_rule in existing_rules:
489 attributes_match = True
490 # Compare the attributes of the existing security group rule with
491 # the attributes of the rule that we want to add.
492 for attribute in rules_attributes_defaults.keys():
493 existing_attribute = '' if not existing_rule[attribute] \
494 else str(existing_rule[attribute]).lower()
495 attribute = '' if not rule[attribute] \
496 else str(rule[attribute]).lower()
497 if existing_attribute != attribute:
498 attributes_match = False
499 break
500 if attributes_match:
501 rule_found = True
502 break
503 if rule_found:
504 # Skip adding the rule as it already exists.
505 continue
506 rule_index = len(new_rules) + 1
507 new_rules.update({'Rule {0}'.format(rule_index): rule})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100508 _neutron_module_call('create_security_group_rule',
509 security_group_id=security_group_id,
510 direction=rule['direction'],
511 ethertype=rule['ethertype'],
512 protocol=rule['protocol'],
513 port_range_min=rule['port_range_min'],
514 port_range_max=rule['port_range_max'],
515 remote_ip_prefix=rule['remote_ip_prefix'],
Jiri Broulik5368cc52017-02-08 18:53:59 +0100516 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100517 **connection_args)
518
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100519 if not security_group:
520 # The security group didn't exist. It was created and specified
521 # rules were added to it.
522 security_group = _neutron_module_call('list_security_groups',
523 id=security_group_id,
524 **connection_args)[name]
525 return _created(name, 'security_group', security_group)
526 if len(new_rules) == 0:
527 # Security group already exists and specified rules are already
528 # present.
529 return _no_change(name, 'security_group')
530 # Security group already exists, but the specified rules were added to it.
531 return _updated(name, 'security_group', {'New Rules': new_rules})
532
Elena Ezhova20456692017-07-10 14:30:27 +0400533
534def port_present(network_name, profile=None, endpoint_type=None, name=None,
535 tenant=None, description='', fixed_ips=None, device_id=None,
536 device_owner=None, binding_host_id=None, admin_state_up=True,
537 mac_address=None, vnic_type=None, binding_profile=None,
538 security_groups=None, extra_dhcp_opt=None, qos_policy=None,
539 allowed_address_pair=None, dns_name=None):
540 """
541 Ensure the port is present with specified parameters.
542
543 :param network_name: Name of the network to create port in
544 :param profile: Authentication profile
545 :param endpoint_type: Endpoint type
546 :param name: Name of this port
547 :param tenant: Tenant in which the port should be created, avaiable for
548 admin only.
549 :param description: Port description
550 :param fixed_ips: Desired IP and/or subnet for this port:
551 subnet_id=<name_or_id>,ip_address=<ip>.
552 :param device_id: Device ID of this port
553 :param device_owner: Device owner of this port
554 :param binding_host_id: he ID of the host where the port resides.
555 :param admin_state_up: Admin state of this port
556 :param mac_address: MAC address of this port
557 :param vnic_type: VNIC type for this port
558 :param binding_profile: Custom data to be passed as binding:profile
559 :param security_groups: Security group associated with the port
560 :param extra_dhcp_opt: Extra dhcp options to be assigned to this port:
561 opt_na me=<dhcp_option_name>,opt_value=<value>,
562 ip_version={4, 6}
563 :param qos_policy: ID or name of the QoS policy that shouldbe attached to
564 the resource
565 :param allowed_address_pair: ip_address=IP_ADDR|CIDR[,mac_address=MAC_ADDR]
566 Allowed address pair associated with the port.
567 "ip_address" parameter is required. IP address
568 or CIDR can be specified for "ip_address".
569 "mac_address" parameter is optional.
570 :param dns_name: Assign DNS name to the port (requires DNS integration
571 extension)
572 """
573
574 connection_args = _auth(profile, endpoint_type)
575 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
576 network_id = None
577 port_exists = False
578
579 port_arguments = _get_non_null_args(
580 name=name, tenant_id=tenant_id, description=description,
581 fixed_ips=fixed_ips, device_id=device_id, device_owner=device_owner,
582 admin_state_up=admin_state_up,
583 mac_address=mac_address, vnic_type=vnic_type,
584 binding_profile=binding_profile,
585 extra_dhcp_opt=extra_dhcp_opt, qos_policy=qos_policy,
586 allowed_address_pair=allowed_address_pair, dns_name=dns_name)
587 if binding_host_id:
588 port_arguments['binding:host_id'] = binding_host_id
589 if security_groups:
590 sec_group_list = []
591 for sec_group_name in security_groups:
592 security_group = _neutron_module_call(
593 'list_security_groups', name=sec_group_name, **connection_args)
594 if security_group:
595 sec_group_list.append(security_group[sec_group_name]['id'])
596 port_arguments['security_groups'] = sec_group_list
597
598 existing_networks = _neutron_module_call(
599 'list_networks', tenant_id=tenant_id, name=network_name,
600 **connection_args)['networks']
601 if len(existing_networks) == 0:
602 LOG.error("Can't find network with name: {0}".format(network_name))
603 elif len(existing_networks) == 1:
604 network_id = existing_networks[0]['id']
605 elif len(existing_networks) > 1:
606 LOG.error("Multiple networks with name: {0} found.".format(network_name))
607
608 if network_id is None:
609 return _create_failed(name, 'port')
610
611 port_arguments['network_id'] = network_id
612
613 existing_ports = _neutron_module_call(
614 'list_ports', network_id=network_id, tenant_id=tenant_id,
615 **connection_args)
616
617 if name:
618 for key, value in existing_ports.iteritems():
619 try:
620 if value['name'] == name and value['tenant_id'] == tenant_id:
621 port_exists = True
622 break
623 except KeyError:
624 pass
625
626 if not port_exists:
627 port_arguments.update(connection_args)
628 res = _neutron_module_call('create_port', **port_arguments)['port']
629 if res['name'] == name:
630 return _created(name, 'port', res)
631 return _create_failed(name, 'port')
632 else:
633 return _no_change('for instance {0}'.format(name), 'port')
634
635
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100636def _created(name, resource, resource_definition):
637 changes_dict = {'name': name,
638 'changes': resource_definition,
639 'result': True,
640 'comment': '{0} {1} created'.format(resource, name)}
641 return changes_dict
642
643def _updated(name, resource, resource_definition):
644 changes_dict = {'name': name,
645 'changes': resource_definition,
646 'result': True,
647 'comment': '{0} {1} updated'.format(resource, name)}
648 return changes_dict
649
650def _no_change(name, resource, test=False):
651 changes_dict = {'name': name,
652 'changes': {},
653 'result': True}
654 if test:
655 changes_dict['comment'] = \
656 '{0} {1} will be {2}'.format(resource, name, test)
657 else:
658 changes_dict['comment'] = \
659 '{0} {1} is in correct state'.format(resource, name)
660 return changes_dict
661
662
663def _deleted(name, resource, resource_definition):
664 changes_dict = {'name': name,
665 'changes': {},
666 'comment': '{0} {1} removed'.format(resource, name),
667 'result': True}
668 return changes_dict
669
670
671def _absent(name, resource):
672 changes_dict = {'name': name,
673 'changes': {},
674 'comment': '{0} {1} not present'.format(resource, name),
675 'result': True}
676 return changes_dict
677
678
679def _delete_failed(name, resource):
680 changes_dict = {'name': name,
681 'changes': {},
682 'comment': '{0} {1} failed to delete'.format(resource,
683 name),
684 'result': False}
685 return changes_dict
686
687def _create_failed(name, resource):
688 changes_dict = {'name': name,
689 'changes': {},
690 'comment': '{0} {1} failed to create'.format(resource,
691 name),
692 'result': False}
693 return changes_dict
694
695def _update_failed(name, resource):
696 changes_dict = {'name': name,
697 'changes': {},
698 'comment': '{0} {1} failed to update'.format(resource,
699 name),
700 'result': False}
701 return changes_dict
702
703
704def _get_non_null_args(**kwargs):
705 '''
706 Return those kwargs which are not null
707 '''
708 return dict((key, value,) for key, value in kwargs.iteritems()
Your Name96fdc0a2017-05-05 12:56:28 +0000709 if value is not None)