blob: 046fb79459f9c32eb2569fd41c2d440c75608789 [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(
304 'list_networks', name=gateway_network, **connection_args)
305 gateway_network_id = network[gateway_network]['id']
306 _neutron_module_call('router_gateway_set',
307 router_id=router_id,
308 external_gateway=gateway_network_id,
309 **connection_args)
310 for interface in interfaces:
311 subnet = _neutron_module_call(
312 'list_subnets', name=interface, **connection_args)
313 subnet_id = subnet[interface]['id']
314 _neutron_module_call('router_add_interface',
315 router_id=router_id,
316 subnet_id=subnet_id,
317 **connection_args)
318 return _created(name,
319 'router',
320 _neutron_module_call('list_routers',
321 name=name,
322 **connection_args))
323 return _create_failed(name, 'router')
324
325 router_id = existing_router[name]['id']
326 existing_router = existing_router[name]
327 diff = {}
Jiri Broulik5368cc52017-02-08 18:53:59 +0100328 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 +0100329 diff.update({'admin_state_up': admin_state_up})
330 if gateway_network:
331 network = _neutron_module_call(
332 'list_networks', name=gateway_network, **connection_args)
333 gateway_network_id = network[gateway_network]['id']
334 if not existing_router['external_gateway_info'] and not existing_router['external_gateway_info'] == None:
335 if existing_router['external_gateway_info']['network_id'] != gateway_network_id:
336 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulik5368cc52017-02-08 18:53:59 +0100337 elif not existing_router['external_gateway_info'] == None:
338 if not 'network_id' in existing_router['external_gateway_info'] or existing_router['external_gateway_info']['network_id'] != gateway_network_id:
339 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100340 if diff:
341 # update the changes
342 router_args = diff.copy()
343 router_args.update(connection_args)
344 try:
345 _neutron_module_call('update_router', existing_router['id'], **router_args)
346 changes_dict = _created(name, 'router', diff)
347 changes_dict['comment'] = 'Router {0} updated'.format(name)
348 return changes_dict
349 except:
350 LOG.exception('Router {0} could not be updated'.format(name))
351 return _update_failed(name, 'router')
352 return _no_change(name, 'router')
353
Jiri Broulikde2e2902017-02-13 15:03:47 +0100354
355def floatingip_present(name=None,
356 tenant_name=None,
357 subnet=None,
358 tenant=None,
359 network=None,
360 port_id=None,
361 fip_exists=False,
Richard Felklaac256a2017-03-23 15:43:49 +0100362 profile=None,
363 endpoint_type=None):
Jiri Broulikde2e2902017-02-13 15:03:47 +0100364 '''
365 Ensure that the floating ip address is present for an instance
366 '''
367 instance_id = __salt__['novang.server_get'](name=name, tenant_name=tenant_name, profile=profile)
368 subnet_name = subnet
Richard Felklaac256a2017-03-23 15:43:49 +0100369 connection_args = _auth(profile, endpoint_type)
Jiri Broulikde2e2902017-02-13 15:03:47 +0100370 existing_subnet = _neutron_module_call(
371 'list_subnets', name=subnet_name, **connection_args)
372 subnet_id = existing_subnet[subnet_name]['id']
373
374 ret = {}
375 existing_ports = _neutron_module_call(
376 'list_ports', **connection_args)
377 existing_floatingips = _neutron_module_call(
378 'list_floatingips', **connection_args)
379
380 tenant = __salt__['keystone.tenant_get'](name=tenant_name, profile=profile, **connection_args)
381 tenant_id = tenant[tenant_name]['id']
382 existing_network = _neutron_module_call(
383 'list_networks', name=network, **connection_args)
384 floating_network_id = existing_network[network]['id']
385
386 for key, value in existing_ports.iteritems():
387 try:
388 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
389 port_id=value['id']
390 except:
391 pass
392 for key, value in existing_floatingips.iteritems():
393 try:
394 if value['floating_network_id'] == floating_network_id and value['port_id'] == port_id and value['tenant_id'] == tenant_id:
395 fip_exists = True
396 break
397 except:
398 pass
399
400 if fip_exists == False:
401 for key, value in existing_ports.iteritems():
402 try:
403 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
404 ret = _neutron_module_call('create_floatingip', floating_network_id=floating_network_id, port_id=value['id'], tenant_id=tenant_id, **connection_args)
405 except:
406 pass
407 return _created('port', 'floatingip', ret)
408 else:
409 return _no_change('for instance {0}'.format(name), 'floatingip')
410
Your Name96fdc0a2017-05-05 12:56:28 +0000411
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100412def security_group_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100413 tenant=None,
Elena Ezhova46b02b02017-07-07 16:58:45 +0400414 description='',
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100415 rules=[],
Richard Felklaac256a2017-03-23 15:43:49 +0100416 profile=None,
417 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100418 '''
419 Ensure that the security group is present with the specified properties.
420 name
421 The name of the security group
422 description
423 The description of the security group
424 rules
425 list of rules to be added to the given security group
426 '''
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100427 # If the user is an admin, he's able to see the security groups from
428 # other tenants. In this case, we'll use the tenant id to get an existing
429 # security group.
Richard Felklaac256a2017-03-23 15:43:49 +0100430 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100431 tenant_name = tenant
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100432 try:
433 tenant_id = __salt__['keystone.tenant_get'](
434 name=tenant_name, **connection_args)[tenant_name]['id']
435 except:
436 tenant_id = None
437 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
438 connection_args['connection_user']))
439 if tenant_id:
440 security_group = _neutron_module_call(
441 'list_security_groups', name=name, tenant_id=tenant_id,
442 **connection_args)
443 else:
444 security_group = _neutron_module_call(
445 'list_security_groups', name=name, **connection_args)
446
447 if not security_group:
448 # Create the security group as it doesn't exist already.
449 security_group_id = _neutron_module_call('create_security_group',
450 name=name,
451 description=description,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100452 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100453 **connection_args)
454 else:
455 security_group_id = security_group[name]['id']
456
457 # Set the missing rules attributes (in case the user didn't specify them
458 # in pillar) to some default values.
459 rules_attributes_defaults = {
460 'direction': 'ingress',
461 'ethertype': 'IPv4',
462 'protocol': 'TCP',
463 'port_range_min': None,
464 'port_range_max': None,
465 'remote_ip_prefix': None
466 }
467 for rule in rules:
468 for attribute in rules_attributes_defaults.keys():
469 if not rule.has_key(attribute):
470 rule[attribute] = rules_attributes_defaults[attribute]
471
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100472 # Remove all the duplicates rules given by the user in pillar.
473 unique_rules = []
474 for rule in rules:
475 if rule not in unique_rules:
476 unique_rules.append(rule)
477
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100478 # Get the existing security group rules.
479 existing_rules = _neutron_module_call(
480 'list_security_groups',
481 id=security_group_id,
482 **connection_args)[name]['security_group_rules']
483
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100484 new_rules = {}
485 for rule in unique_rules:
486 rule_found = False
487 for existing_rule in existing_rules:
488 attributes_match = True
489 # Compare the attributes of the existing security group rule with
490 # the attributes of the rule that we want to add.
491 for attribute in rules_attributes_defaults.keys():
492 existing_attribute = '' if not existing_rule[attribute] \
493 else str(existing_rule[attribute]).lower()
494 attribute = '' if not rule[attribute] \
495 else str(rule[attribute]).lower()
496 if existing_attribute != attribute:
497 attributes_match = False
498 break
499 if attributes_match:
500 rule_found = True
501 break
502 if rule_found:
503 # Skip adding the rule as it already exists.
504 continue
505 rule_index = len(new_rules) + 1
506 new_rules.update({'Rule {0}'.format(rule_index): rule})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100507 _neutron_module_call('create_security_group_rule',
508 security_group_id=security_group_id,
509 direction=rule['direction'],
510 ethertype=rule['ethertype'],
511 protocol=rule['protocol'],
512 port_range_min=rule['port_range_min'],
513 port_range_max=rule['port_range_max'],
514 remote_ip_prefix=rule['remote_ip_prefix'],
Jiri Broulik5368cc52017-02-08 18:53:59 +0100515 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100516 **connection_args)
517
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100518 if not security_group:
519 # The security group didn't exist. It was created and specified
520 # rules were added to it.
521 security_group = _neutron_module_call('list_security_groups',
522 id=security_group_id,
523 **connection_args)[name]
524 return _created(name, 'security_group', security_group)
525 if len(new_rules) == 0:
526 # Security group already exists and specified rules are already
527 # present.
528 return _no_change(name, 'security_group')
529 # Security group already exists, but the specified rules were added to it.
530 return _updated(name, 'security_group', {'New Rules': new_rules})
531
Elena Ezhova20456692017-07-10 14:30:27 +0400532
533def port_present(network_name, profile=None, endpoint_type=None, name=None,
534 tenant=None, description='', fixed_ips=None, device_id=None,
535 device_owner=None, binding_host_id=None, admin_state_up=True,
536 mac_address=None, vnic_type=None, binding_profile=None,
537 security_groups=None, extra_dhcp_opt=None, qos_policy=None,
538 allowed_address_pair=None, dns_name=None):
539 """
540 Ensure the port is present with specified parameters.
541
542 :param network_name: Name of the network to create port in
543 :param profile: Authentication profile
544 :param endpoint_type: Endpoint type
545 :param name: Name of this port
546 :param tenant: Tenant in which the port should be created, avaiable for
547 admin only.
548 :param description: Port description
549 :param fixed_ips: Desired IP and/or subnet for this port:
550 subnet_id=<name_or_id>,ip_address=<ip>.
551 :param device_id: Device ID of this port
552 :param device_owner: Device owner of this port
553 :param binding_host_id: he ID of the host where the port resides.
554 :param admin_state_up: Admin state of this port
555 :param mac_address: MAC address of this port
556 :param vnic_type: VNIC type for this port
557 :param binding_profile: Custom data to be passed as binding:profile
558 :param security_groups: Security group associated with the port
559 :param extra_dhcp_opt: Extra dhcp options to be assigned to this port:
560 opt_na me=<dhcp_option_name>,opt_value=<value>,
561 ip_version={4, 6}
562 :param qos_policy: ID or name of the QoS policy that shouldbe attached to
563 the resource
564 :param allowed_address_pair: ip_address=IP_ADDR|CIDR[,mac_address=MAC_ADDR]
565 Allowed address pair associated with the port.
566 "ip_address" parameter is required. IP address
567 or CIDR can be specified for "ip_address".
568 "mac_address" parameter is optional.
569 :param dns_name: Assign DNS name to the port (requires DNS integration
570 extension)
571 """
572
573 connection_args = _auth(profile, endpoint_type)
574 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
575 network_id = None
576 port_exists = False
577
578 port_arguments = _get_non_null_args(
579 name=name, tenant_id=tenant_id, description=description,
580 fixed_ips=fixed_ips, device_id=device_id, device_owner=device_owner,
581 admin_state_up=admin_state_up,
582 mac_address=mac_address, vnic_type=vnic_type,
583 binding_profile=binding_profile,
584 extra_dhcp_opt=extra_dhcp_opt, qos_policy=qos_policy,
585 allowed_address_pair=allowed_address_pair, dns_name=dns_name)
586 if binding_host_id:
587 port_arguments['binding:host_id'] = binding_host_id
588 if security_groups:
589 sec_group_list = []
590 for sec_group_name in security_groups:
591 security_group = _neutron_module_call(
592 'list_security_groups', name=sec_group_name, **connection_args)
593 if security_group:
594 sec_group_list.append(security_group[sec_group_name]['id'])
595 port_arguments['security_groups'] = sec_group_list
596
597 existing_networks = _neutron_module_call(
598 'list_networks', tenant_id=tenant_id, name=network_name,
599 **connection_args)['networks']
600 if len(existing_networks) == 0:
601 LOG.error("Can't find network with name: {0}".format(network_name))
602 elif len(existing_networks) == 1:
603 network_id = existing_networks[0]['id']
604 elif len(existing_networks) > 1:
605 LOG.error("Multiple networks with name: {0} found.".format(network_name))
606
607 if network_id is None:
608 return _create_failed(name, 'port')
609
610 port_arguments['network_id'] = network_id
611
612 existing_ports = _neutron_module_call(
613 'list_ports', network_id=network_id, tenant_id=tenant_id,
614 **connection_args)
615
616 if name:
617 for key, value in existing_ports.iteritems():
618 try:
619 if value['name'] == name and value['tenant_id'] == tenant_id:
620 port_exists = True
621 break
622 except KeyError:
623 pass
624
625 if not port_exists:
626 port_arguments.update(connection_args)
627 res = _neutron_module_call('create_port', **port_arguments)['port']
628 if res['name'] == name:
629 return _created(name, 'port', res)
630 return _create_failed(name, 'port')
631 else:
632 return _no_change('for instance {0}'.format(name), 'port')
633
634
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100635def _created(name, resource, resource_definition):
636 changes_dict = {'name': name,
637 'changes': resource_definition,
638 'result': True,
639 'comment': '{0} {1} created'.format(resource, name)}
640 return changes_dict
641
642def _updated(name, resource, resource_definition):
643 changes_dict = {'name': name,
644 'changes': resource_definition,
645 'result': True,
646 'comment': '{0} {1} updated'.format(resource, name)}
647 return changes_dict
648
649def _no_change(name, resource, test=False):
650 changes_dict = {'name': name,
651 'changes': {},
652 'result': True}
653 if test:
654 changes_dict['comment'] = \
655 '{0} {1} will be {2}'.format(resource, name, test)
656 else:
657 changes_dict['comment'] = \
658 '{0} {1} is in correct state'.format(resource, name)
659 return changes_dict
660
661
662def _deleted(name, resource, resource_definition):
663 changes_dict = {'name': name,
664 'changes': {},
665 'comment': '{0} {1} removed'.format(resource, name),
666 'result': True}
667 return changes_dict
668
669
670def _absent(name, resource):
671 changes_dict = {'name': name,
672 'changes': {},
673 'comment': '{0} {1} not present'.format(resource, name),
674 'result': True}
675 return changes_dict
676
677
678def _delete_failed(name, resource):
679 changes_dict = {'name': name,
680 'changes': {},
681 'comment': '{0} {1} failed to delete'.format(resource,
682 name),
683 'result': False}
684 return changes_dict
685
686def _create_failed(name, resource):
687 changes_dict = {'name': name,
688 'changes': {},
689 'comment': '{0} {1} failed to create'.format(resource,
690 name),
691 'result': False}
692 return changes_dict
693
694def _update_failed(name, resource):
695 changes_dict = {'name': name,
696 'changes': {},
697 'comment': '{0} {1} failed to update'.format(resource,
698 name),
699 'result': False}
700 return changes_dict
701
702
703def _get_non_null_args(**kwargs):
704 '''
705 Return those kwargs which are not null
706 '''
707 return dict((key, value,) for key, value in kwargs.iteritems()
Your Name96fdc0a2017-05-05 12:56:28 +0000708 if value is not None)