blob: 351b1d12ff971cd6a5a2d41ec66fa7b9317bcb0f [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:
Oleg Iurchenko87f56322017-10-20 00:40:50 +030046 tenant_id = __salt__['keystoneng.tenant_get'](
Your Name96fdc0a2017-05-05 12:56:28 +000047 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,
Mykyta Karpinefd9e092018-03-29 18:25:12 +030087 dns_domain=None,
88 is_default=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010089 '''
90 Ensure that the neutron network is present with the specified properties.
91 name
92 The name of the network to manage
93 '''
Richard Felklaac256a2017-03-23 15:43:49 +010094 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +000095 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
96
97 existing_networks = _neutron_module_call(
98 'list_networks', name=name, tenant_id=tenant_id,
99 **connection_args)['networks']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100100 network_arguments = _get_non_null_args(
101 name=name,
102 provider_network_type=provider_network_type,
103 provider_physical_network=provider_physical_network,
104 router_external=router_external,
105 admin_state_up=admin_state_up,
106 shared=shared,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100107 tenant_id=tenant_id,
Oleg Iurchenko8cf6cf52017-09-18 15:44:03 +0300108 provider_segmentation_id=provider_segmentation_id,
Mykyta Karpinefd9e092018-03-29 18:25:12 +0300109 dns_domain=dns_domain,
110 is_default=is_default)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100111
Your Name96fdc0a2017-05-05 12:56:28 +0000112 if len(existing_networks) == 0:
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100113 network_arguments.update(connection_args)
Your Name96fdc0a2017-05-05 12:56:28 +0000114 res = _neutron_module_call(
115 'create_network', **network_arguments)['network']
Jiri Broulik5368cc52017-02-08 18:53:59 +0100116
Your Name96fdc0a2017-05-05 12:56:28 +0000117 if res.get('name') == name:
118 return _created(name, 'network', res['name'])
119
120 elif len(existing_networks) > 1:
121 LOG.error("More than one network found with the name: {0}".format(
122 name))
123
124 elif len(existing_networks) == 1:
125 existing_network = existing_networks[0]
126 LOG.info('CONNECTION STRINGS' + str(connection_args))
127 LOG.info('existing ' + str(existing_network))
128 LOG.info('new ' + str(network_arguments))
129 existing_network = dict((key.replace(':', '_', 1), value)
130 for key, value in
131 existing_network.iteritems())
132 # generate differential
133 diff = dict((key, value) for key, value in network_arguments.iteritems()
134 if existing_network.get(key, None) != value)
135 if diff:
136 # update the changes
137 network_arguments = diff.copy()
138 network_arguments.update(connection_args)
139 try:
140 LOG.debug('updating network {0} with changes {1}'.format(
141 name, str(diff)))
142 _neutron_module_call('update_network',
143 existing_network['id'],
144 **network_arguments)
145 changes_dict = _created(name, 'network', diff)
146 changes_dict['comment'] = '{1} {0} updated'.format(name, 'network')
147 return changes_dict
148 except:
149 LOG.error('Could not update network {0}'.format(name))
150 return _update_failed(name, 'network')
151 return _no_change(name, 'network')
152 return _create_failed(name, 'network')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100153
154
155@_test_call
Your Name96fdc0a2017-05-05 12:56:28 +0000156def network_absent(name, network_id=None, profile=None, endpoint_type=None):
Richard Felklaac256a2017-03-23 15:43:49 +0100157 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +0000158 identifier = network_id or name
159 _neutron_module_call(
160 'delete_network', identifier, **connection_args)
161 return _absent(identifier, 'network')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100162
163
164@_test_call
Your Name96fdc0a2017-05-05 12:56:28 +0000165def subnet_present(name,
166 network_name=None,
167 network_id=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100168 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100169 cidr=None,
170 ip_version=4,
171 enable_dhcp=True,
172 allocation_pools=None,
173 gateway_ip=None,
174 dns_nameservers=None,
175 host_routes=None,
Richard Felklaac256a2017-03-23 15:43:49 +0100176 profile=None,
177 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100178 '''
179 Ensure that the neutron subnet is present with the specified properties.
180 name
181 The name of the subnet to manage
182 '''
Your Name96fdc0a2017-05-05 12:56:28 +0000183 if network_name is None and network_id is None:
184 LOG.error("Network identificator name or uuid should be provided.")
185 return _create_failed(name, 'subnet')
186
Richard Felklaac256a2017-03-23 15:43:49 +0100187 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +0000188 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
189
190 existing_subnets = _neutron_module_call(
191 'list_subnets', tenant_id=tenant_id, name=name,
192 **connection_args)['subnets']
193
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100194 subnet_arguments = _get_non_null_args(
195 name=name,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100196 cidr=cidr,
197 ip_version=ip_version,
198 enable_dhcp=enable_dhcp,
199 allocation_pools=allocation_pools,
200 gateway_ip=gateway_ip,
201 dns_nameservers=dns_nameservers,
202 host_routes=host_routes)
Your Name96fdc0a2017-05-05 12:56:28 +0000203
204 if network_id is None and network_name:
205 existing_networks = _neutron_module_call(
206 'list_networks', tenant_id=tenant_id, name=network_name,
207 **connection_args)['networks']
208 if len(existing_networks) == 0:
209 LOG.error("Can't find network with name: {0}".format(network_name))
210 elif len(existing_networks) == 1:
211 network_id = existing_networks[0]['id']
212 elif len(existing_networks) > 1:
213 LOG.error("Multiple networks with name: {0} found.".format(
214 network_name))
215
216 if network_id is None:
217 return _create_failed(name, 'subnet')
218
219 subnet_arguments['network_id'] = network_id
220
221 if len(existing_subnets) == 0:
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100222 subnet_arguments.update(connection_args)
Your Name96fdc0a2017-05-05 12:56:28 +0000223 res = _neutron_module_call('create_subnet', tenant_id=tenant_id,
224 **subnet_arguments)['subnet']
225 if res.get('name') == name:
226 return _created(name, 'subnet', res)
227 return _create_failed(name, 'subnet')
228
229 elif len(existing_subnets) == 1:
230 existing_subnet = existing_subnets[0]
231
232 # create differential
233 LOG.error('existing ' + str(existing_subnet))
234 LOG.error('new ' + str(subnet_arguments))
235 diff = dict((key, value) for key, value in subnet_arguments.iteritems()
236 if existing_subnet.get(key, None) != value)
237 if not diff:
238 return _no_change(name, 'subnet')
239
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100240 # update the changes
241 subnet_arguments = diff.copy()
242 subnet_arguments.update(connection_args)
243 try:
244 LOG.debug('updating subnet {0} with changes {1}'.format(
245 name, str(diff)))
246 _neutron_module_call('update_subnet',
247 existing_subnet['id'],
248 **subnet_arguments)
249 changes_dict = _created(name, 'subnet', diff)
250 changes_dict['comment'] = '{1} {0} updated'.format(name, 'subnet')
251 return changes_dict
252 except:
Your Name96fdc0a2017-05-05 12:56:28 +0000253 LOG.error('Could not update subnet {0}'.format(name))
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100254 return _update_failed(name, 'subnet')
Your Name96fdc0a2017-05-05 12:56:28 +0000255
256 elif len(existing_subnets) > 1:
257 LOG.error("Multiple subnets with name: {0} found".format(
258 name))
259 return _create_failed(name, 'network')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100260
261
262@_test_call
Your Name96fdc0a2017-05-05 12:56:28 +0000263def subnet_absent(name, subnet_id=None, profile=None, endpoint_type=None):
Richard Felklaac256a2017-03-23 15:43:49 +0100264 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +0000265 identifier = subnet_id or name
266 _neutron_module_call(
267 'delete_subnet', identifier, **connection_args)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100268 return _absent(name, 'subnet')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100269
270
271@_test_call
272def router_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100273 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100274 gateway_network=None,
275 interfaces=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100276 admin_state_up=True,
Richard Felklaac256a2017-03-23 15:43:49 +0100277 profile=None,
278 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100279 '''
280 Ensure that the neutron router is present with the specified properties.
281 name
282 The name of the subnet to manage
283 gateway_network
284 The network that would be the router's default gateway
285 interfaces
286 list of subnets the router attaches to
287 '''
Richard Felklaac256a2017-03-23 15:43:49 +0100288 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100289 tenant_name = tenant
290 try:
Oleg Iurchenko87f56322017-10-20 00:40:50 +0300291 tenant_id = __salt__['keystoneng.tenant_get'](
Jiri Broulik5368cc52017-02-08 18:53:59 +0100292 name=tenant_name, **connection_args)[tenant_name]['id']
293 except:
294 tenant_id = None
295 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
296 connection_args['connection_user']))
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100297 existing_router = _neutron_module_call(
298 'list_routers', name=name, **connection_args)
299 if not existing_router:
Jiri Broulik5368cc52017-02-08 18:53:59 +0100300 _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 +0100301 created_router = _neutron_module_call(
302 'list_routers', name=name, **connection_args)
303 if created_router:
304 router_id = created_router[name]['id']
305 network = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100306 'list_networks', name=gateway_network, **connection_args)["networks"]
307 #TODO test for more networks
308 gateway_network_id = network[0]['id']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100309 _neutron_module_call('router_gateway_set',
310 router_id=router_id,
311 external_gateway=gateway_network_id,
312 **connection_args)
313 for interface in interfaces:
314 subnet = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100315 'list_subnets', name=interface, **connection_args)["subnets"]
316 subnet_id = subnet[0]['id']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100317 _neutron_module_call('router_add_interface',
318 router_id=router_id,
319 subnet_id=subnet_id,
320 **connection_args)
321 return _created(name,
322 'router',
323 _neutron_module_call('list_routers',
324 name=name,
325 **connection_args))
326 return _create_failed(name, 'router')
327
328 router_id = existing_router[name]['id']
329 existing_router = existing_router[name]
330 diff = {}
Jiri Broulik5368cc52017-02-08 18:53:59 +0100331 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 +0100332 diff.update({'admin_state_up': admin_state_up})
333 if gateway_network:
334 network = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100335 'list_networks', name=gateway_network, **connection_args)["networks"]
336 gateway_network_id = network[0]['id']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100337 if not existing_router['external_gateway_info'] and not existing_router['external_gateway_info'] == None:
338 if existing_router['external_gateway_info']['network_id'] != gateway_network_id:
339 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulik5368cc52017-02-08 18:53:59 +0100340 elif not existing_router['external_gateway_info'] == None:
341 if not 'network_id' in existing_router['external_gateway_info'] or existing_router['external_gateway_info']['network_id'] != gateway_network_id:
342 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100343 if diff:
344 # update the changes
345 router_args = diff.copy()
346 router_args.update(connection_args)
347 try:
348 _neutron_module_call('update_router', existing_router['id'], **router_args)
349 changes_dict = _created(name, 'router', diff)
350 changes_dict['comment'] = 'Router {0} updated'.format(name)
351 return changes_dict
352 except:
353 LOG.exception('Router {0} could not be updated'.format(name))
354 return _update_failed(name, 'router')
355 return _no_change(name, 'router')
356
Jiri Broulikde2e2902017-02-13 15:03:47 +0100357
358def floatingip_present(name=None,
359 tenant_name=None,
360 subnet=None,
361 tenant=None,
362 network=None,
363 port_id=None,
364 fip_exists=False,
Richard Felklaac256a2017-03-23 15:43:49 +0100365 profile=None,
366 endpoint_type=None):
Jiri Broulikde2e2902017-02-13 15:03:47 +0100367 '''
368 Ensure that the floating ip address is present for an instance
369 '''
370 instance_id = __salt__['novang.server_get'](name=name, tenant_name=tenant_name, profile=profile)
371 subnet_name = subnet
Richard Felklaac256a2017-03-23 15:43:49 +0100372 connection_args = _auth(profile, endpoint_type)
Jiri Broulikde2e2902017-02-13 15:03:47 +0100373 existing_subnet = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100374 'list_subnets', name=subnet_name, **connection_args)["subnets"]
375 subnet_id = existing_subnet[0]['id']
Jiri Broulikde2e2902017-02-13 15:03:47 +0100376
377 ret = {}
378 existing_ports = _neutron_module_call(
379 'list_ports', **connection_args)
380 existing_floatingips = _neutron_module_call(
381 'list_floatingips', **connection_args)
382
Oleg Iurchenko87f56322017-10-20 00:40:50 +0300383 tenant = __salt__['keystoneng.tenant_get'](name=tenant_name, **connection_args)
Jiri Broulikde2e2902017-02-13 15:03:47 +0100384 tenant_id = tenant[tenant_name]['id']
385 existing_network = _neutron_module_call(
Ondrej Smolae90f62a2017-11-02 12:21:15 +0100386 'list_networks', name=network, **connection_args)["networks"]
387 floating_network_id = existing_network[0]['id']
Jiri Broulikde2e2902017-02-13 15:03:47 +0100388
389 for key, value in existing_ports.iteritems():
390 try:
391 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
392 port_id=value['id']
393 except:
394 pass
395 for key, value in existing_floatingips.iteritems():
396 try:
397 if value['floating_network_id'] == floating_network_id and value['port_id'] == port_id and value['tenant_id'] == tenant_id:
398 fip_exists = True
399 break
400 except:
401 pass
402
403 if fip_exists == False:
404 for key, value in existing_ports.iteritems():
405 try:
406 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
407 ret = _neutron_module_call('create_floatingip', floating_network_id=floating_network_id, port_id=value['id'], tenant_id=tenant_id, **connection_args)
408 except:
409 pass
410 return _created('port', 'floatingip', ret)
411 else:
412 return _no_change('for instance {0}'.format(name), 'floatingip')
413
Your Name96fdc0a2017-05-05 12:56:28 +0000414
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100415def security_group_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100416 tenant=None,
Elena Ezhova46b02b02017-07-07 16:58:45 +0400417 description='',
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100418 rules=[],
Richard Felklaac256a2017-03-23 15:43:49 +0100419 profile=None,
420 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100421 '''
422 Ensure that the security group is present with the specified properties.
423 name
424 The name of the security group
425 description
426 The description of the security group
427 rules
428 list of rules to be added to the given security group
429 '''
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100430 # If the user is an admin, he's able to see the security groups from
431 # other tenants. In this case, we'll use the tenant id to get an existing
432 # security group.
Richard Felklaac256a2017-03-23 15:43:49 +0100433 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100434 tenant_name = tenant
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100435 try:
Oleg Iurchenko87f56322017-10-20 00:40:50 +0300436 tenant_id = __salt__['keystoneng.tenant_get'](
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100437 name=tenant_name, **connection_args)[tenant_name]['id']
438 except:
439 tenant_id = None
440 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
441 connection_args['connection_user']))
442 if tenant_id:
443 security_group = _neutron_module_call(
444 'list_security_groups', name=name, tenant_id=tenant_id,
445 **connection_args)
446 else:
447 security_group = _neutron_module_call(
448 'list_security_groups', name=name, **connection_args)
449
450 if not security_group:
451 # Create the security group as it doesn't exist already.
452 security_group_id = _neutron_module_call('create_security_group',
453 name=name,
454 description=description,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100455 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100456 **connection_args)
457 else:
458 security_group_id = security_group[name]['id']
459
460 # Set the missing rules attributes (in case the user didn't specify them
461 # in pillar) to some default values.
462 rules_attributes_defaults = {
463 'direction': 'ingress',
464 'ethertype': 'IPv4',
465 'protocol': 'TCP',
466 'port_range_min': None,
467 'port_range_max': None,
468 'remote_ip_prefix': None
469 }
470 for rule in rules:
471 for attribute in rules_attributes_defaults.keys():
472 if not rule.has_key(attribute):
473 rule[attribute] = rules_attributes_defaults[attribute]
474
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100475 # Remove all the duplicates rules given by the user in pillar.
476 unique_rules = []
477 for rule in rules:
478 if rule not in unique_rules:
479 unique_rules.append(rule)
480
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100481 # Get the existing security group rules.
482 existing_rules = _neutron_module_call(
483 'list_security_groups',
484 id=security_group_id,
485 **connection_args)[name]['security_group_rules']
486
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100487 new_rules = {}
488 for rule in unique_rules:
489 rule_found = False
490 for existing_rule in existing_rules:
491 attributes_match = True
492 # Compare the attributes of the existing security group rule with
493 # the attributes of the rule that we want to add.
494 for attribute in rules_attributes_defaults.keys():
495 existing_attribute = '' if not existing_rule[attribute] \
496 else str(existing_rule[attribute]).lower()
497 attribute = '' if not rule[attribute] \
498 else str(rule[attribute]).lower()
499 if existing_attribute != attribute:
500 attributes_match = False
501 break
502 if attributes_match:
503 rule_found = True
504 break
505 if rule_found:
506 # Skip adding the rule as it already exists.
507 continue
508 rule_index = len(new_rules) + 1
509 new_rules.update({'Rule {0}'.format(rule_index): rule})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100510 _neutron_module_call('create_security_group_rule',
511 security_group_id=security_group_id,
512 direction=rule['direction'],
513 ethertype=rule['ethertype'],
514 protocol=rule['protocol'],
515 port_range_min=rule['port_range_min'],
516 port_range_max=rule['port_range_max'],
517 remote_ip_prefix=rule['remote_ip_prefix'],
Jiri Broulik5368cc52017-02-08 18:53:59 +0100518 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100519 **connection_args)
520
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100521 if not security_group:
522 # The security group didn't exist. It was created and specified
523 # rules were added to it.
524 security_group = _neutron_module_call('list_security_groups',
525 id=security_group_id,
526 **connection_args)[name]
527 return _created(name, 'security_group', security_group)
528 if len(new_rules) == 0:
529 # Security group already exists and specified rules are already
530 # present.
531 return _no_change(name, 'security_group')
532 # Security group already exists, but the specified rules were added to it.
533 return _updated(name, 'security_group', {'New Rules': new_rules})
534
Elena Ezhova20456692017-07-10 14:30:27 +0400535
536def port_present(network_name, profile=None, endpoint_type=None, name=None,
537 tenant=None, description='', fixed_ips=None, device_id=None,
538 device_owner=None, binding_host_id=None, admin_state_up=True,
539 mac_address=None, vnic_type=None, binding_profile=None,
540 security_groups=None, extra_dhcp_opt=None, qos_policy=None,
541 allowed_address_pair=None, dns_name=None):
542 """
543 Ensure the port is present with specified parameters.
544
545 :param network_name: Name of the network to create port in
546 :param profile: Authentication profile
547 :param endpoint_type: Endpoint type
548 :param name: Name of this port
549 :param tenant: Tenant in which the port should be created, avaiable for
550 admin only.
551 :param description: Port description
552 :param fixed_ips: Desired IP and/or subnet for this port:
553 subnet_id=<name_or_id>,ip_address=<ip>.
554 :param device_id: Device ID of this port
555 :param device_owner: Device owner of this port
556 :param binding_host_id: he ID of the host where the port resides.
557 :param admin_state_up: Admin state of this port
558 :param mac_address: MAC address of this port
559 :param vnic_type: VNIC type for this port
560 :param binding_profile: Custom data to be passed as binding:profile
561 :param security_groups: Security group associated with the port
562 :param extra_dhcp_opt: Extra dhcp options to be assigned to this port:
563 opt_na me=<dhcp_option_name>,opt_value=<value>,
564 ip_version={4, 6}
565 :param qos_policy: ID or name of the QoS policy that shouldbe attached to
566 the resource
567 :param allowed_address_pair: ip_address=IP_ADDR|CIDR[,mac_address=MAC_ADDR]
568 Allowed address pair associated with the port.
569 "ip_address" parameter is required. IP address
570 or CIDR can be specified for "ip_address".
571 "mac_address" parameter is optional.
572 :param dns_name: Assign DNS name to the port (requires DNS integration
573 extension)
574 """
575
576 connection_args = _auth(profile, endpoint_type)
577 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
578 network_id = None
579 port_exists = False
580
581 port_arguments = _get_non_null_args(
582 name=name, tenant_id=tenant_id, description=description,
583 fixed_ips=fixed_ips, device_id=device_id, device_owner=device_owner,
584 admin_state_up=admin_state_up,
585 mac_address=mac_address, vnic_type=vnic_type,
586 binding_profile=binding_profile,
587 extra_dhcp_opt=extra_dhcp_opt, qos_policy=qos_policy,
588 allowed_address_pair=allowed_address_pair, dns_name=dns_name)
589 if binding_host_id:
590 port_arguments['binding:host_id'] = binding_host_id
591 if security_groups:
592 sec_group_list = []
593 for sec_group_name in security_groups:
594 security_group = _neutron_module_call(
595 'list_security_groups', name=sec_group_name, **connection_args)
596 if security_group:
597 sec_group_list.append(security_group[sec_group_name]['id'])
598 port_arguments['security_groups'] = sec_group_list
599
600 existing_networks = _neutron_module_call(
601 'list_networks', tenant_id=tenant_id, name=network_name,
602 **connection_args)['networks']
603 if len(existing_networks) == 0:
604 LOG.error("Can't find network with name: {0}".format(network_name))
605 elif len(existing_networks) == 1:
606 network_id = existing_networks[0]['id']
607 elif len(existing_networks) > 1:
608 LOG.error("Multiple networks with name: {0} found.".format(network_name))
609
610 if network_id is None:
611 return _create_failed(name, 'port')
612
613 port_arguments['network_id'] = network_id
614
615 existing_ports = _neutron_module_call(
616 'list_ports', network_id=network_id, tenant_id=tenant_id,
617 **connection_args)
618
619 if name:
620 for key, value in existing_ports.iteritems():
621 try:
622 if value['name'] == name and value['tenant_id'] == tenant_id:
623 port_exists = True
624 break
625 except KeyError:
626 pass
627
628 if not port_exists:
629 port_arguments.update(connection_args)
630 res = _neutron_module_call('create_port', **port_arguments)['port']
631 if res['name'] == name:
632 return _created(name, 'port', res)
633 return _create_failed(name, 'port')
634 else:
635 return _no_change('for instance {0}'.format(name), 'port')
636
637
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100638def _created(name, resource, resource_definition):
639 changes_dict = {'name': name,
640 'changes': resource_definition,
641 'result': True,
642 'comment': '{0} {1} created'.format(resource, name)}
643 return changes_dict
644
645def _updated(name, resource, resource_definition):
646 changes_dict = {'name': name,
647 'changes': resource_definition,
648 'result': True,
649 'comment': '{0} {1} updated'.format(resource, name)}
650 return changes_dict
651
652def _no_change(name, resource, test=False):
653 changes_dict = {'name': name,
654 'changes': {},
655 'result': True}
656 if test:
657 changes_dict['comment'] = \
658 '{0} {1} will be {2}'.format(resource, name, test)
659 else:
660 changes_dict['comment'] = \
661 '{0} {1} is in correct state'.format(resource, name)
662 return changes_dict
663
664
665def _deleted(name, resource, resource_definition):
666 changes_dict = {'name': name,
667 'changes': {},
668 'comment': '{0} {1} removed'.format(resource, name),
669 'result': True}
670 return changes_dict
671
672
673def _absent(name, resource):
674 changes_dict = {'name': name,
675 'changes': {},
676 'comment': '{0} {1} not present'.format(resource, name),
677 'result': True}
678 return changes_dict
679
680
681def _delete_failed(name, resource):
682 changes_dict = {'name': name,
683 'changes': {},
684 'comment': '{0} {1} failed to delete'.format(resource,
685 name),
686 'result': False}
687 return changes_dict
688
689def _create_failed(name, resource):
690 changes_dict = {'name': name,
691 'changes': {},
692 'comment': '{0} {1} failed to create'.format(resource,
693 name),
694 'result': False}
695 return changes_dict
696
697def _update_failed(name, resource):
698 changes_dict = {'name': name,
699 'changes': {},
700 'comment': '{0} {1} failed to update'.format(resource,
701 name),
702 'result': False}
703 return changes_dict
704
705
706def _get_non_null_args(**kwargs):
707 '''
708 Return those kwargs which are not null
709 '''
710 return dict((key, value,) for key, value in kwargs.iteritems()
Your Name96fdc0a2017-05-05 12:56:28 +0000711 if value is not None)