blob: 92afb7ac5c173c481227a21de29b662c9f6af23a [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,
86 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010087 '''
88 Ensure that the neutron network is present with the specified properties.
89 name
90 The name of the network to manage
91 '''
Richard Felklaac256a2017-03-23 15:43:49 +010092 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +000093 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
94
95 existing_networks = _neutron_module_call(
96 'list_networks', name=name, tenant_id=tenant_id,
97 **connection_args)['networks']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010098 network_arguments = _get_non_null_args(
99 name=name,
100 provider_network_type=provider_network_type,
101 provider_physical_network=provider_physical_network,
102 router_external=router_external,
103 admin_state_up=admin_state_up,
104 shared=shared,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100105 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100106 provider_segmentation_id=provider_segmentation_id)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100107
Your Name96fdc0a2017-05-05 12:56:28 +0000108 if len(existing_networks) == 0:
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100109 network_arguments.update(connection_args)
Your Name96fdc0a2017-05-05 12:56:28 +0000110 res = _neutron_module_call(
111 'create_network', **network_arguments)['network']
Jiri Broulik5368cc52017-02-08 18:53:59 +0100112
Your Name96fdc0a2017-05-05 12:56:28 +0000113 if res.get('name') == name:
114 return _created(name, 'network', res['name'])
115
116 elif len(existing_networks) > 1:
117 LOG.error("More than one network found with the name: {0}".format(
118 name))
119
120 elif len(existing_networks) == 1:
121 existing_network = existing_networks[0]
122 LOG.info('CONNECTION STRINGS' + str(connection_args))
123 LOG.info('existing ' + str(existing_network))
124 LOG.info('new ' + str(network_arguments))
125 existing_network = dict((key.replace(':', '_', 1), value)
126 for key, value in
127 existing_network.iteritems())
128 # generate differential
129 diff = dict((key, value) for key, value in network_arguments.iteritems()
130 if existing_network.get(key, None) != value)
131 if diff:
132 # update the changes
133 network_arguments = diff.copy()
134 network_arguments.update(connection_args)
135 try:
136 LOG.debug('updating network {0} with changes {1}'.format(
137 name, str(diff)))
138 _neutron_module_call('update_network',
139 existing_network['id'],
140 **network_arguments)
141 changes_dict = _created(name, 'network', diff)
142 changes_dict['comment'] = '{1} {0} updated'.format(name, 'network')
143 return changes_dict
144 except:
145 LOG.error('Could not update network {0}'.format(name))
146 return _update_failed(name, 'network')
147 return _no_change(name, 'network')
148 return _create_failed(name, 'network')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100149
150
151@_test_call
Your Name96fdc0a2017-05-05 12:56:28 +0000152def network_absent(name, network_id=None, profile=None, endpoint_type=None):
Richard Felklaac256a2017-03-23 15:43:49 +0100153 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +0000154 identifier = network_id or name
155 _neutron_module_call(
156 'delete_network', identifier, **connection_args)
157 return _absent(identifier, 'network')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100158
159
160@_test_call
Your Name96fdc0a2017-05-05 12:56:28 +0000161def subnet_present(name,
162 network_name=None,
163 network_id=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100164 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100165 cidr=None,
166 ip_version=4,
167 enable_dhcp=True,
168 allocation_pools=None,
169 gateway_ip=None,
170 dns_nameservers=None,
171 host_routes=None,
Richard Felklaac256a2017-03-23 15:43:49 +0100172 profile=None,
173 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100174 '''
175 Ensure that the neutron subnet is present with the specified properties.
176 name
177 The name of the subnet to manage
178 '''
Your Name96fdc0a2017-05-05 12:56:28 +0000179 if network_name is None and network_id is None:
180 LOG.error("Network identificator name or uuid should be provided.")
181 return _create_failed(name, 'subnet')
182
Richard Felklaac256a2017-03-23 15:43:49 +0100183 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +0000184 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
185
186 existing_subnets = _neutron_module_call(
187 'list_subnets', tenant_id=tenant_id, name=name,
188 **connection_args)['subnets']
189
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100190 subnet_arguments = _get_non_null_args(
191 name=name,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100192 cidr=cidr,
193 ip_version=ip_version,
194 enable_dhcp=enable_dhcp,
195 allocation_pools=allocation_pools,
196 gateway_ip=gateway_ip,
197 dns_nameservers=dns_nameservers,
198 host_routes=host_routes)
Your Name96fdc0a2017-05-05 12:56:28 +0000199
200 if network_id is None and network_name:
201 existing_networks = _neutron_module_call(
202 'list_networks', tenant_id=tenant_id, name=network_name,
203 **connection_args)['networks']
204 if len(existing_networks) == 0:
205 LOG.error("Can't find network with name: {0}".format(network_name))
206 elif len(existing_networks) == 1:
207 network_id = existing_networks[0]['id']
208 elif len(existing_networks) > 1:
209 LOG.error("Multiple networks with name: {0} found.".format(
210 network_name))
211
212 if network_id is None:
213 return _create_failed(name, 'subnet')
214
215 subnet_arguments['network_id'] = network_id
216
217 if len(existing_subnets) == 0:
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100218 subnet_arguments.update(connection_args)
Your Name96fdc0a2017-05-05 12:56:28 +0000219 res = _neutron_module_call('create_subnet', tenant_id=tenant_id,
220 **subnet_arguments)['subnet']
221 if res.get('name') == name:
222 return _created(name, 'subnet', res)
223 return _create_failed(name, 'subnet')
224
225 elif len(existing_subnets) == 1:
226 existing_subnet = existing_subnets[0]
227
228 # create differential
229 LOG.error('existing ' + str(existing_subnet))
230 LOG.error('new ' + str(subnet_arguments))
231 diff = dict((key, value) for key, value in subnet_arguments.iteritems()
232 if existing_subnet.get(key, None) != value)
233 if not diff:
234 return _no_change(name, 'subnet')
235
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100236 # update the changes
237 subnet_arguments = diff.copy()
238 subnet_arguments.update(connection_args)
239 try:
240 LOG.debug('updating subnet {0} with changes {1}'.format(
241 name, str(diff)))
242 _neutron_module_call('update_subnet',
243 existing_subnet['id'],
244 **subnet_arguments)
245 changes_dict = _created(name, 'subnet', diff)
246 changes_dict['comment'] = '{1} {0} updated'.format(name, 'subnet')
247 return changes_dict
248 except:
Your Name96fdc0a2017-05-05 12:56:28 +0000249 LOG.error('Could not update subnet {0}'.format(name))
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100250 return _update_failed(name, 'subnet')
Your Name96fdc0a2017-05-05 12:56:28 +0000251
252 elif len(existing_subnets) > 1:
253 LOG.error("Multiple subnets with name: {0} found".format(
254 name))
255 return _create_failed(name, 'network')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100256
257
258@_test_call
Your Name96fdc0a2017-05-05 12:56:28 +0000259def subnet_absent(name, subnet_id=None, profile=None, endpoint_type=None):
Richard Felklaac256a2017-03-23 15:43:49 +0100260 connection_args = _auth(profile, endpoint_type)
Your Name96fdc0a2017-05-05 12:56:28 +0000261 identifier = subnet_id or name
262 _neutron_module_call(
263 'delete_subnet', identifier, **connection_args)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100264 return _absent(name, 'subnet')
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100265
266
267@_test_call
268def router_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100269 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100270 gateway_network=None,
271 interfaces=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100272 admin_state_up=True,
Richard Felklaac256a2017-03-23 15:43:49 +0100273 profile=None,
274 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100275 '''
276 Ensure that the neutron router is present with the specified properties.
277 name
278 The name of the subnet to manage
279 gateway_network
280 The network that would be the router's default gateway
281 interfaces
282 list of subnets the router attaches to
283 '''
Richard Felklaac256a2017-03-23 15:43:49 +0100284 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100285 tenant_name = tenant
286 try:
287 tenant_id = __salt__['keystone.tenant_get'](
288 name=tenant_name, **connection_args)[tenant_name]['id']
289 except:
290 tenant_id = None
291 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
292 connection_args['connection_user']))
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100293 existing_router = _neutron_module_call(
294 'list_routers', name=name, **connection_args)
295 if not existing_router:
Jiri Broulik5368cc52017-02-08 18:53:59 +0100296 _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 +0100297 created_router = _neutron_module_call(
298 'list_routers', name=name, **connection_args)
299 if created_router:
300 router_id = created_router[name]['id']
301 network = _neutron_module_call(
302 'list_networks', name=gateway_network, **connection_args)
303 gateway_network_id = network[gateway_network]['id']
304 _neutron_module_call('router_gateway_set',
305 router_id=router_id,
306 external_gateway=gateway_network_id,
307 **connection_args)
308 for interface in interfaces:
309 subnet = _neutron_module_call(
310 'list_subnets', name=interface, **connection_args)
311 subnet_id = subnet[interface]['id']
312 _neutron_module_call('router_add_interface',
313 router_id=router_id,
314 subnet_id=subnet_id,
315 **connection_args)
316 return _created(name,
317 'router',
318 _neutron_module_call('list_routers',
319 name=name,
320 **connection_args))
321 return _create_failed(name, 'router')
322
323 router_id = existing_router[name]['id']
324 existing_router = existing_router[name]
325 diff = {}
Jiri Broulik5368cc52017-02-08 18:53:59 +0100326 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 +0100327 diff.update({'admin_state_up': admin_state_up})
328 if gateway_network:
329 network = _neutron_module_call(
330 'list_networks', name=gateway_network, **connection_args)
331 gateway_network_id = network[gateway_network]['id']
332 if not existing_router['external_gateway_info'] and not existing_router['external_gateway_info'] == None:
333 if existing_router['external_gateway_info']['network_id'] != gateway_network_id:
334 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulik5368cc52017-02-08 18:53:59 +0100335 elif not existing_router['external_gateway_info'] == None:
336 if not 'network_id' in existing_router['external_gateway_info'] or existing_router['external_gateway_info']['network_id'] != gateway_network_id:
337 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100338 if diff:
339 # update the changes
340 router_args = diff.copy()
341 router_args.update(connection_args)
342 try:
343 _neutron_module_call('update_router', existing_router['id'], **router_args)
344 changes_dict = _created(name, 'router', diff)
345 changes_dict['comment'] = 'Router {0} updated'.format(name)
346 return changes_dict
347 except:
348 LOG.exception('Router {0} could not be updated'.format(name))
349 return _update_failed(name, 'router')
350 return _no_change(name, 'router')
351
Jiri Broulikde2e2902017-02-13 15:03:47 +0100352
353def floatingip_present(name=None,
354 tenant_name=None,
355 subnet=None,
356 tenant=None,
357 network=None,
358 port_id=None,
359 fip_exists=False,
Richard Felklaac256a2017-03-23 15:43:49 +0100360 profile=None,
361 endpoint_type=None):
Jiri Broulikde2e2902017-02-13 15:03:47 +0100362 '''
363 Ensure that the floating ip address is present for an instance
364 '''
365 instance_id = __salt__['novang.server_get'](name=name, tenant_name=tenant_name, profile=profile)
366 subnet_name = subnet
Richard Felklaac256a2017-03-23 15:43:49 +0100367 connection_args = _auth(profile, endpoint_type)
Jiri Broulikde2e2902017-02-13 15:03:47 +0100368 existing_subnet = _neutron_module_call(
369 'list_subnets', name=subnet_name, **connection_args)
370 subnet_id = existing_subnet[subnet_name]['id']
371
372 ret = {}
373 existing_ports = _neutron_module_call(
374 'list_ports', **connection_args)
375 existing_floatingips = _neutron_module_call(
376 'list_floatingips', **connection_args)
377
378 tenant = __salt__['keystone.tenant_get'](name=tenant_name, profile=profile, **connection_args)
379 tenant_id = tenant[tenant_name]['id']
380 existing_network = _neutron_module_call(
381 'list_networks', name=network, **connection_args)
382 floating_network_id = existing_network[network]['id']
383
384 for key, value in existing_ports.iteritems():
385 try:
386 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
387 port_id=value['id']
388 except:
389 pass
390 for key, value in existing_floatingips.iteritems():
391 try:
392 if value['floating_network_id'] == floating_network_id and value['port_id'] == port_id and value['tenant_id'] == tenant_id:
393 fip_exists = True
394 break
395 except:
396 pass
397
398 if fip_exists == False:
399 for key, value in existing_ports.iteritems():
400 try:
401 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
402 ret = _neutron_module_call('create_floatingip', floating_network_id=floating_network_id, port_id=value['id'], tenant_id=tenant_id, **connection_args)
403 except:
404 pass
405 return _created('port', 'floatingip', ret)
406 else:
407 return _no_change('for instance {0}'.format(name), 'floatingip')
408
Your Name96fdc0a2017-05-05 12:56:28 +0000409
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100410def security_group_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100411 tenant=None,
Elena Ezhova46b02b02017-07-07 16:58:45 +0400412 description='',
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100413 rules=[],
Richard Felklaac256a2017-03-23 15:43:49 +0100414 profile=None,
415 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100416 '''
417 Ensure that the security group is present with the specified properties.
418 name
419 The name of the security group
420 description
421 The description of the security group
422 rules
423 list of rules to be added to the given security group
424 '''
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100425 # If the user is an admin, he's able to see the security groups from
426 # other tenants. In this case, we'll use the tenant id to get an existing
427 # security group.
Richard Felklaac256a2017-03-23 15:43:49 +0100428 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100429 tenant_name = tenant
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100430 try:
431 tenant_id = __salt__['keystone.tenant_get'](
432 name=tenant_name, **connection_args)[tenant_name]['id']
433 except:
434 tenant_id = None
435 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
436 connection_args['connection_user']))
437 if tenant_id:
438 security_group = _neutron_module_call(
439 'list_security_groups', name=name, tenant_id=tenant_id,
440 **connection_args)
441 else:
442 security_group = _neutron_module_call(
443 'list_security_groups', name=name, **connection_args)
444
445 if not security_group:
446 # Create the security group as it doesn't exist already.
447 security_group_id = _neutron_module_call('create_security_group',
448 name=name,
449 description=description,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100450 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100451 **connection_args)
452 else:
453 security_group_id = security_group[name]['id']
454
455 # Set the missing rules attributes (in case the user didn't specify them
456 # in pillar) to some default values.
457 rules_attributes_defaults = {
458 'direction': 'ingress',
459 'ethertype': 'IPv4',
460 'protocol': 'TCP',
461 'port_range_min': None,
462 'port_range_max': None,
463 'remote_ip_prefix': None
464 }
465 for rule in rules:
466 for attribute in rules_attributes_defaults.keys():
467 if not rule.has_key(attribute):
468 rule[attribute] = rules_attributes_defaults[attribute]
469
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100470 # Remove all the duplicates rules given by the user in pillar.
471 unique_rules = []
472 for rule in rules:
473 if rule not in unique_rules:
474 unique_rules.append(rule)
475
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100476 # Get the existing security group rules.
477 existing_rules = _neutron_module_call(
478 'list_security_groups',
479 id=security_group_id,
480 **connection_args)[name]['security_group_rules']
481
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100482 new_rules = {}
483 for rule in unique_rules:
484 rule_found = False
485 for existing_rule in existing_rules:
486 attributes_match = True
487 # Compare the attributes of the existing security group rule with
488 # the attributes of the rule that we want to add.
489 for attribute in rules_attributes_defaults.keys():
490 existing_attribute = '' if not existing_rule[attribute] \
491 else str(existing_rule[attribute]).lower()
492 attribute = '' if not rule[attribute] \
493 else str(rule[attribute]).lower()
494 if existing_attribute != attribute:
495 attributes_match = False
496 break
497 if attributes_match:
498 rule_found = True
499 break
500 if rule_found:
501 # Skip adding the rule as it already exists.
502 continue
503 rule_index = len(new_rules) + 1
504 new_rules.update({'Rule {0}'.format(rule_index): rule})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100505 _neutron_module_call('create_security_group_rule',
506 security_group_id=security_group_id,
507 direction=rule['direction'],
508 ethertype=rule['ethertype'],
509 protocol=rule['protocol'],
510 port_range_min=rule['port_range_min'],
511 port_range_max=rule['port_range_max'],
512 remote_ip_prefix=rule['remote_ip_prefix'],
Jiri Broulik5368cc52017-02-08 18:53:59 +0100513 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100514 **connection_args)
515
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100516 if not security_group:
517 # The security group didn't exist. It was created and specified
518 # rules were added to it.
519 security_group = _neutron_module_call('list_security_groups',
520 id=security_group_id,
521 **connection_args)[name]
522 return _created(name, 'security_group', security_group)
523 if len(new_rules) == 0:
524 # Security group already exists and specified rules are already
525 # present.
526 return _no_change(name, 'security_group')
527 # Security group already exists, but the specified rules were added to it.
528 return _updated(name, 'security_group', {'New Rules': new_rules})
529
Elena Ezhova20456692017-07-10 14:30:27 +0400530
531def port_present(network_name, profile=None, endpoint_type=None, name=None,
532 tenant=None, description='', fixed_ips=None, device_id=None,
533 device_owner=None, binding_host_id=None, admin_state_up=True,
534 mac_address=None, vnic_type=None, binding_profile=None,
535 security_groups=None, extra_dhcp_opt=None, qos_policy=None,
536 allowed_address_pair=None, dns_name=None):
537 """
538 Ensure the port is present with specified parameters.
539
540 :param network_name: Name of the network to create port in
541 :param profile: Authentication profile
542 :param endpoint_type: Endpoint type
543 :param name: Name of this port
544 :param tenant: Tenant in which the port should be created, avaiable for
545 admin only.
546 :param description: Port description
547 :param fixed_ips: Desired IP and/or subnet for this port:
548 subnet_id=<name_or_id>,ip_address=<ip>.
549 :param device_id: Device ID of this port
550 :param device_owner: Device owner of this port
551 :param binding_host_id: he ID of the host where the port resides.
552 :param admin_state_up: Admin state of this port
553 :param mac_address: MAC address of this port
554 :param vnic_type: VNIC type for this port
555 :param binding_profile: Custom data to be passed as binding:profile
556 :param security_groups: Security group associated with the port
557 :param extra_dhcp_opt: Extra dhcp options to be assigned to this port:
558 opt_na me=<dhcp_option_name>,opt_value=<value>,
559 ip_version={4, 6}
560 :param qos_policy: ID or name of the QoS policy that shouldbe attached to
561 the resource
562 :param allowed_address_pair: ip_address=IP_ADDR|CIDR[,mac_address=MAC_ADDR]
563 Allowed address pair associated with the port.
564 "ip_address" parameter is required. IP address
565 or CIDR can be specified for "ip_address".
566 "mac_address" parameter is optional.
567 :param dns_name: Assign DNS name to the port (requires DNS integration
568 extension)
569 """
570
571 connection_args = _auth(profile, endpoint_type)
572 tenant_id = _get_tenant_id(tenant_name=tenant, **connection_args)
573 network_id = None
574 port_exists = False
575
576 port_arguments = _get_non_null_args(
577 name=name, tenant_id=tenant_id, description=description,
578 fixed_ips=fixed_ips, device_id=device_id, device_owner=device_owner,
579 admin_state_up=admin_state_up,
580 mac_address=mac_address, vnic_type=vnic_type,
581 binding_profile=binding_profile,
582 extra_dhcp_opt=extra_dhcp_opt, qos_policy=qos_policy,
583 allowed_address_pair=allowed_address_pair, dns_name=dns_name)
584 if binding_host_id:
585 port_arguments['binding:host_id'] = binding_host_id
586 if security_groups:
587 sec_group_list = []
588 for sec_group_name in security_groups:
589 security_group = _neutron_module_call(
590 'list_security_groups', name=sec_group_name, **connection_args)
591 if security_group:
592 sec_group_list.append(security_group[sec_group_name]['id'])
593 port_arguments['security_groups'] = sec_group_list
594
595 existing_networks = _neutron_module_call(
596 'list_networks', tenant_id=tenant_id, name=network_name,
597 **connection_args)['networks']
598 if len(existing_networks) == 0:
599 LOG.error("Can't find network with name: {0}".format(network_name))
600 elif len(existing_networks) == 1:
601 network_id = existing_networks[0]['id']
602 elif len(existing_networks) > 1:
603 LOG.error("Multiple networks with name: {0} found.".format(network_name))
604
605 if network_id is None:
606 return _create_failed(name, 'port')
607
608 port_arguments['network_id'] = network_id
609
610 existing_ports = _neutron_module_call(
611 'list_ports', network_id=network_id, tenant_id=tenant_id,
612 **connection_args)
613
614 if name:
615 for key, value in existing_ports.iteritems():
616 try:
617 if value['name'] == name and value['tenant_id'] == tenant_id:
618 port_exists = True
619 break
620 except KeyError:
621 pass
622
623 if not port_exists:
624 port_arguments.update(connection_args)
625 res = _neutron_module_call('create_port', **port_arguments)['port']
626 if res['name'] == name:
627 return _created(name, 'port', res)
628 return _create_failed(name, 'port')
629 else:
630 return _no_change('for instance {0}'.format(name), 'port')
631
632
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100633def _created(name, resource, resource_definition):
634 changes_dict = {'name': name,
635 'changes': resource_definition,
636 'result': True,
637 'comment': '{0} {1} created'.format(resource, name)}
638 return changes_dict
639
640def _updated(name, resource, resource_definition):
641 changes_dict = {'name': name,
642 'changes': resource_definition,
643 'result': True,
644 'comment': '{0} {1} updated'.format(resource, name)}
645 return changes_dict
646
647def _no_change(name, resource, test=False):
648 changes_dict = {'name': name,
649 'changes': {},
650 'result': True}
651 if test:
652 changes_dict['comment'] = \
653 '{0} {1} will be {2}'.format(resource, name, test)
654 else:
655 changes_dict['comment'] = \
656 '{0} {1} is in correct state'.format(resource, name)
657 return changes_dict
658
659
660def _deleted(name, resource, resource_definition):
661 changes_dict = {'name': name,
662 'changes': {},
663 'comment': '{0} {1} removed'.format(resource, name),
664 'result': True}
665 return changes_dict
666
667
668def _absent(name, resource):
669 changes_dict = {'name': name,
670 'changes': {},
671 'comment': '{0} {1} not present'.format(resource, name),
672 'result': True}
673 return changes_dict
674
675
676def _delete_failed(name, resource):
677 changes_dict = {'name': name,
678 'changes': {},
679 'comment': '{0} {1} failed to delete'.format(resource,
680 name),
681 'result': False}
682 return changes_dict
683
684def _create_failed(name, resource):
685 changes_dict = {'name': name,
686 'changes': {},
687 'comment': '{0} {1} failed to create'.format(resource,
688 name),
689 'result': False}
690 return changes_dict
691
692def _update_failed(name, resource):
693 changes_dict = {'name': name,
694 'changes': {},
695 'comment': '{0} {1} failed to update'.format(resource,
696 name),
697 'result': False}
698 return changes_dict
699
700
701def _get_non_null_args(**kwargs):
702 '''
703 Return those kwargs which are not null
704 '''
705 return dict((key, value,) for key, value in kwargs.iteritems()
Your Name96fdc0a2017-05-05 12:56:28 +0000706 if value is not None)