blob: 67a088ceec820d3008f7f75d586f0137dfa1cf53 [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
8 neutron network present:
9 neutron.network_present:
10 - 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
44
Richard Felklaac256a2017-03-23 15:43:49 +010045def _auth(profile=None, endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010046 '''
47 Set up neutron credentials
48 '''
49 if profile:
50 credentials = __salt__['config.option'](profile)
51 user = credentials['keystone.user']
52 password = credentials['keystone.password']
53 tenant = credentials['keystone.tenant']
54 auth_url = credentials['keystone.auth_url']
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010055 kwargs = {
56 'connection_user': user,
57 'connection_password': password,
58 'connection_tenant': tenant,
Richard Felkl22008f92017-03-29 16:14:24 +020059 'connection_auth_url': auth_url,
Richard Felklaac256a2017-03-23 15:43:49 +010060 'connection_endpoint_type': endpoint_type
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010061 }
62
63 return kwargs
64
65@_test_call
66def network_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +010067 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010068 provider_network_type=None,
69 provider_physical_network=None,
70 router_external=None,
71 admin_state_up=None,
72 shared=None,
73 provider_segmentation_id=None,
Richard Felklaac256a2017-03-23 15:43:49 +010074 profile=None,
75 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010076 '''
77 Ensure that the neutron network is present with the specified properties.
78 name
79 The name of the network to manage
80 '''
Jiri Broulik5368cc52017-02-08 18:53:59 +010081 tenant_name = tenant
Richard Felklaac256a2017-03-23 15:43:49 +010082 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +010083 try:
84 tenant_id = __salt__['keystone.tenant_get'](
85 name=tenant_name, **connection_args)[tenant_name]['id']
86 except:
87 tenant_id = None
88 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
89 connection_args['connection_user']))
Richard Felklaac256a2017-03-23 15:43:49 +010090 existing_network = _neutron_module_call(
91 'list_networks', name=name, **connection_args)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +010092 network_arguments = _get_non_null_args(
93 name=name,
94 provider_network_type=provider_network_type,
95 provider_physical_network=provider_physical_network,
96 router_external=router_external,
97 admin_state_up=admin_state_up,
98 shared=shared,
Jiri Broulik5368cc52017-02-08 18:53:59 +010099 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100100 provider_segmentation_id=provider_segmentation_id)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100101
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100102 if not existing_network:
103 network_arguments.update(connection_args)
104 _neutron_module_call('create_network', **network_arguments)
Richard Felklb97cbe72017-02-28 22:37:59 +0100105 existing_networks = _neutron_module_call(
Richard Felkl22008f92017-03-29 16:14:24 +0200106 'list_networks',name=name, **connection_args)
Richard Felklb97cbe72017-02-28 22:37:59 +0100107 for network in existing_networks:
108 if network.get(name) == name:
109 existing_network = network
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100110 if existing_network:
111 return _created(name, 'network', existing_network[name])
112 return _update_failed(name, 'network')
Jiri Broulik5368cc52017-02-08 18:53:59 +0100113
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100114 LOG.info('CONNECTION STRINGS' + str(connection_args))
115 LOG.info('existing ' + str(existing_network))
116 LOG.info('new ' + str(network_arguments))
117 existing_network = dict((key.replace(':', '_', 1), value)
118 for key, value in
119 existing_network[name].iteritems())
120 # generate differential
121 diff = dict((key, value) for key, value in network_arguments.iteritems()
122 if existing_network.get(key, None) != value)
123 if diff:
124 # update the changes
125 network_arguments = diff.copy()
126 network_arguments.update(connection_args)
127 try:
128 LOG.debug('updating network {0} with changes {1}'.format(
129 name, str(diff)))
130 _neutron_module_call('update_network',
131 existing_network['id'],
132 **network_arguments)
133 changes_dict = _created(name, 'network', diff)
134 changes_dict['comment'] = '{1} {0} updated'.format(name, 'network')
135 return changes_dict
136 except:
137 LOG.exception('Could not update network {0}'.format(name))
138 return _update_failed(name, 'network')
139 return _no_change(name, 'network')
140
141
142@_test_call
Richard Felklaac256a2017-03-23 15:43:49 +0100143def network_absent(name, profile=None,endpoint_type=None):
144 connection_args = _auth(profile, endpoint_type)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100145 existing_network = _neutron_module_call(
146 'list_networks', name=name, **connection_args)
147 if existing_network:
148 _neutron_module_call(
149 'delete_network', existing_network[name]['id'], **connection_args)
150 if _neutron_module_call('list_networks', name=name, **connection_args):
151 return _delete_failed(name, 'network')
152 return _deleted(name, 'network', existing_network[name])
153 return _absent(name, 'network')
154
155
156@_test_call
157def subnet_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100158 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100159 network=None,
160 cidr=None,
161 ip_version=4,
162 enable_dhcp=True,
163 allocation_pools=None,
164 gateway_ip=None,
165 dns_nameservers=None,
166 host_routes=None,
Richard Felklaac256a2017-03-23 15:43:49 +0100167 profile=None,
168 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100169 '''
170 Ensure that the neutron subnet is present with the specified properties.
171 name
172 The name of the subnet to manage
173 '''
Richard Felklaac256a2017-03-23 15:43:49 +0100174 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100175 tenant_name = tenant
176 try:
177 tenant_id = __salt__['keystone.tenant_get'](
178 name=tenant_name, **connection_args)[tenant_name]['id']
179 except:
180 tenant_id = None
181 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
182 connection_args['connection_user']))
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100183 existing_subnet = _neutron_module_call(
Jiri Broulik5368cc52017-02-08 18:53:59 +0100184 'list_subnets', tenant_id=tenant_id, name=name, **connection_args)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100185 subnet_arguments = _get_non_null_args(
186 name=name,
187 network=network,
188 cidr=cidr,
189 ip_version=ip_version,
190 enable_dhcp=enable_dhcp,
191 allocation_pools=allocation_pools,
192 gateway_ip=gateway_ip,
193 dns_nameservers=dns_nameservers,
194 host_routes=host_routes)
195 # replace network with network_id
196 if 'network' in subnet_arguments:
197 network = subnet_arguments.pop('network', None)
198 existing_network = _neutron_module_call(
Jiri Broulik5368cc52017-02-08 18:53:59 +0100199 'list_networks', tenant_id=tenant_id, name=network, **connection_args)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100200 if existing_network:
201 subnet_arguments['network_id'] = existing_network[network]['id']
202 if not existing_subnet:
203 subnet_arguments.update(connection_args)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100204 _neutron_module_call('create_subnet', tenant_id=tenant_id, **subnet_arguments)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100205 existing_subnet = _neutron_module_call(
Jiri Broulik5368cc52017-02-08 18:53:59 +0100206 'list_subnets', tenant_id=tenant_id, name=name, **connection_args)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100207 if existing_subnet:
208 return _created(name, 'subnet', existing_subnet[name])
209 return _update_failed(name, 'subnet')
210 # change from internal representation
211 existing_subnet = existing_subnet[name]
212 # create differential
213 LOG.error('existing ' + str(existing_subnet))
214 LOG.error('new ' + str(subnet_arguments))
215 diff = dict((key, value) for key, value in subnet_arguments.iteritems()
216 if existing_subnet.get(key, None) != value)
217 if diff:
218 # update the changes
219 subnet_arguments = diff.copy()
220 subnet_arguments.update(connection_args)
221 try:
222 LOG.debug('updating subnet {0} with changes {1}'.format(
223 name, str(diff)))
224 _neutron_module_call('update_subnet',
225 existing_subnet['id'],
226 **subnet_arguments)
227 changes_dict = _created(name, 'subnet', diff)
228 changes_dict['comment'] = '{1} {0} updated'.format(name, 'subnet')
229 return changes_dict
230 except:
231 LOG.exception('Could not update subnet {0}'.format(name))
232 return _update_failed(name, 'subnet')
233 return _no_change(name, 'subnet')
234
235
236@_test_call
Richard Felklaac256a2017-03-23 15:43:49 +0100237def subnet_absent(name, profile=None, endpoint_type=None):
238 connection_args = _auth(profile, endpoint_type)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100239 existing_subnet = _neutron_module_call(
Jiri Broulik5368cc52017-02-08 18:53:59 +0100240 'list_subnets', tenant_id=tenant_id, name=name, **connection_args)
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100241 if existing_subnet:
242 _neutron_module_call(
243 'delete_subnet', existing_subnet[name]['id'], **connection_args)
244 if _neutron_module_call('list_subnets', name=name, **connection_args):
245 return _delete_failed(name, 'subnet')
246 return _deleted(name, 'subnet', existing_subnet[name])
247 return _absent(name, 'subnet')
248 return _absent(name, 'network')
249
250
251@_test_call
252def router_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100253 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100254 gateway_network=None,
255 interfaces=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100256 admin_state_up=True,
Richard Felklaac256a2017-03-23 15:43:49 +0100257 profile=None,
258 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100259 '''
260 Ensure that the neutron router is present with the specified properties.
261 name
262 The name of the subnet to manage
263 gateway_network
264 The network that would be the router's default gateway
265 interfaces
266 list of subnets the router attaches to
267 '''
Richard Felklaac256a2017-03-23 15:43:49 +0100268 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100269 tenant_name = tenant
270 try:
271 tenant_id = __salt__['keystone.tenant_get'](
272 name=tenant_name, **connection_args)[tenant_name]['id']
273 except:
274 tenant_id = None
275 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
276 connection_args['connection_user']))
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100277 existing_router = _neutron_module_call(
278 'list_routers', name=name, **connection_args)
279 if not existing_router:
Jiri Broulik5368cc52017-02-08 18:53:59 +0100280 _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 +0100281 created_router = _neutron_module_call(
282 'list_routers', name=name, **connection_args)
283 if created_router:
284 router_id = created_router[name]['id']
285 network = _neutron_module_call(
286 'list_networks', name=gateway_network, **connection_args)
287 gateway_network_id = network[gateway_network]['id']
288 _neutron_module_call('router_gateway_set',
289 router_id=router_id,
290 external_gateway=gateway_network_id,
291 **connection_args)
292 for interface in interfaces:
293 subnet = _neutron_module_call(
294 'list_subnets', name=interface, **connection_args)
295 subnet_id = subnet[interface]['id']
296 _neutron_module_call('router_add_interface',
297 router_id=router_id,
298 subnet_id=subnet_id,
299 **connection_args)
300 return _created(name,
301 'router',
302 _neutron_module_call('list_routers',
303 name=name,
304 **connection_args))
305 return _create_failed(name, 'router')
306
307 router_id = existing_router[name]['id']
308 existing_router = existing_router[name]
309 diff = {}
Jiri Broulik5368cc52017-02-08 18:53:59 +0100310 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 +0100311 diff.update({'admin_state_up': admin_state_up})
312 if gateway_network:
313 network = _neutron_module_call(
314 'list_networks', name=gateway_network, **connection_args)
315 gateway_network_id = network[gateway_network]['id']
316 if not existing_router['external_gateway_info'] and not existing_router['external_gateway_info'] == None:
317 if existing_router['external_gateway_info']['network_id'] != gateway_network_id:
318 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulik5368cc52017-02-08 18:53:59 +0100319 elif not existing_router['external_gateway_info'] == None:
320 if not 'network_id' in existing_router['external_gateway_info'] or existing_router['external_gateway_info']['network_id'] != gateway_network_id:
321 diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100322 if diff:
323 # update the changes
324 router_args = diff.copy()
325 router_args.update(connection_args)
326 try:
327 _neutron_module_call('update_router', existing_router['id'], **router_args)
328 changes_dict = _created(name, 'router', diff)
329 changes_dict['comment'] = 'Router {0} updated'.format(name)
330 return changes_dict
331 except:
332 LOG.exception('Router {0} could not be updated'.format(name))
333 return _update_failed(name, 'router')
334 return _no_change(name, 'router')
335
Jiri Broulikde2e2902017-02-13 15:03:47 +0100336
337def floatingip_present(name=None,
338 tenant_name=None,
339 subnet=None,
340 tenant=None,
341 network=None,
342 port_id=None,
343 fip_exists=False,
Richard Felklaac256a2017-03-23 15:43:49 +0100344 profile=None,
345 endpoint_type=None):
Jiri Broulikde2e2902017-02-13 15:03:47 +0100346 '''
347 Ensure that the floating ip address is present for an instance
348 '''
349 instance_id = __salt__['novang.server_get'](name=name, tenant_name=tenant_name, profile=profile)
350 subnet_name = subnet
Richard Felklaac256a2017-03-23 15:43:49 +0100351 connection_args = _auth(profile, endpoint_type)
Jiri Broulikde2e2902017-02-13 15:03:47 +0100352 existing_subnet = _neutron_module_call(
353 'list_subnets', name=subnet_name, **connection_args)
354 subnet_id = existing_subnet[subnet_name]['id']
355
356 ret = {}
357 existing_ports = _neutron_module_call(
358 'list_ports', **connection_args)
359 existing_floatingips = _neutron_module_call(
360 'list_floatingips', **connection_args)
361
362 tenant = __salt__['keystone.tenant_get'](name=tenant_name, profile=profile, **connection_args)
363 tenant_id = tenant[tenant_name]['id']
364 existing_network = _neutron_module_call(
365 'list_networks', name=network, **connection_args)
366 floating_network_id = existing_network[network]['id']
367
368 for key, value in existing_ports.iteritems():
369 try:
370 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
371 port_id=value['id']
372 except:
373 pass
374 for key, value in existing_floatingips.iteritems():
375 try:
376 if value['floating_network_id'] == floating_network_id and value['port_id'] == port_id and value['tenant_id'] == tenant_id:
377 fip_exists = True
378 break
379 except:
380 pass
381
382 if fip_exists == False:
383 for key, value in existing_ports.iteritems():
384 try:
385 if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
386 ret = _neutron_module_call('create_floatingip', floating_network_id=floating_network_id, port_id=value['id'], tenant_id=tenant_id, **connection_args)
387 except:
388 pass
389 return _created('port', 'floatingip', ret)
390 else:
391 return _no_change('for instance {0}'.format(name), 'floatingip')
392
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100393def security_group_present(name=None,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100394 tenant=None,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100395 description=None,
396 rules=[],
Richard Felklaac256a2017-03-23 15:43:49 +0100397 profile=None,
398 endpoint_type=None):
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100399 '''
400 Ensure that the security group is present with the specified properties.
401 name
402 The name of the security group
403 description
404 The description of the security group
405 rules
406 list of rules to be added to the given security group
407 '''
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100408 # If the user is an admin, he's able to see the security groups from
409 # other tenants. In this case, we'll use the tenant id to get an existing
410 # security group.
Richard Felklaac256a2017-03-23 15:43:49 +0100411 connection_args = _auth(profile, endpoint_type)
Jiri Broulik5368cc52017-02-08 18:53:59 +0100412 tenant_name = tenant
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100413 try:
414 tenant_id = __salt__['keystone.tenant_get'](
415 name=tenant_name, **connection_args)[tenant_name]['id']
416 except:
417 tenant_id = None
418 LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
419 connection_args['connection_user']))
420 if tenant_id:
421 security_group = _neutron_module_call(
422 'list_security_groups', name=name, tenant_id=tenant_id,
423 **connection_args)
424 else:
425 security_group = _neutron_module_call(
426 'list_security_groups', name=name, **connection_args)
427
428 if not security_group:
429 # Create the security group as it doesn't exist already.
430 security_group_id = _neutron_module_call('create_security_group',
431 name=name,
432 description=description,
Jiri Broulik5368cc52017-02-08 18:53:59 +0100433 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100434 **connection_args)
435 else:
436 security_group_id = security_group[name]['id']
437
438 # Set the missing rules attributes (in case the user didn't specify them
439 # in pillar) to some default values.
440 rules_attributes_defaults = {
441 'direction': 'ingress',
442 'ethertype': 'IPv4',
443 'protocol': 'TCP',
444 'port_range_min': None,
445 'port_range_max': None,
446 'remote_ip_prefix': None
447 }
448 for rule in rules:
449 for attribute in rules_attributes_defaults.keys():
450 if not rule.has_key(attribute):
451 rule[attribute] = rules_attributes_defaults[attribute]
452
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100453 # Remove all the duplicates rules given by the user in pillar.
454 unique_rules = []
455 for rule in rules:
456 if rule not in unique_rules:
457 unique_rules.append(rule)
458
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100459 # Get the existing security group rules.
460 existing_rules = _neutron_module_call(
461 'list_security_groups',
462 id=security_group_id,
463 **connection_args)[name]['security_group_rules']
464
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100465 new_rules = {}
466 for rule in unique_rules:
467 rule_found = False
468 for existing_rule in existing_rules:
469 attributes_match = True
470 # Compare the attributes of the existing security group rule with
471 # the attributes of the rule that we want to add.
472 for attribute in rules_attributes_defaults.keys():
473 existing_attribute = '' if not existing_rule[attribute] \
474 else str(existing_rule[attribute]).lower()
475 attribute = '' if not rule[attribute] \
476 else str(rule[attribute]).lower()
477 if existing_attribute != attribute:
478 attributes_match = False
479 break
480 if attributes_match:
481 rule_found = True
482 break
483 if rule_found:
484 # Skip adding the rule as it already exists.
485 continue
486 rule_index = len(new_rules) + 1
487 new_rules.update({'Rule {0}'.format(rule_index): rule})
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100488 _neutron_module_call('create_security_group_rule',
489 security_group_id=security_group_id,
490 direction=rule['direction'],
491 ethertype=rule['ethertype'],
492 protocol=rule['protocol'],
493 port_range_min=rule['port_range_min'],
494 port_range_max=rule['port_range_max'],
495 remote_ip_prefix=rule['remote_ip_prefix'],
Jiri Broulik5368cc52017-02-08 18:53:59 +0100496 tenant_id=tenant_id,
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100497 **connection_args)
498
Jiri Broulikf1b3aa42017-01-26 17:08:44 +0100499 if not security_group:
500 # The security group didn't exist. It was created and specified
501 # rules were added to it.
502 security_group = _neutron_module_call('list_security_groups',
503 id=security_group_id,
504 **connection_args)[name]
505 return _created(name, 'security_group', security_group)
506 if len(new_rules) == 0:
507 # Security group already exists and specified rules are already
508 # present.
509 return _no_change(name, 'security_group')
510 # Security group already exists, but the specified rules were added to it.
511 return _updated(name, 'security_group', {'New Rules': new_rules})
512
513def _created(name, resource, resource_definition):
514 changes_dict = {'name': name,
515 'changes': resource_definition,
516 'result': True,
517 'comment': '{0} {1} created'.format(resource, name)}
518 return changes_dict
519
520def _updated(name, resource, resource_definition):
521 changes_dict = {'name': name,
522 'changes': resource_definition,
523 'result': True,
524 'comment': '{0} {1} updated'.format(resource, name)}
525 return changes_dict
526
527def _no_change(name, resource, test=False):
528 changes_dict = {'name': name,
529 'changes': {},
530 'result': True}
531 if test:
532 changes_dict['comment'] = \
533 '{0} {1} will be {2}'.format(resource, name, test)
534 else:
535 changes_dict['comment'] = \
536 '{0} {1} is in correct state'.format(resource, name)
537 return changes_dict
538
539
540def _deleted(name, resource, resource_definition):
541 changes_dict = {'name': name,
542 'changes': {},
543 'comment': '{0} {1} removed'.format(resource, name),
544 'result': True}
545 return changes_dict
546
547
548def _absent(name, resource):
549 changes_dict = {'name': name,
550 'changes': {},
551 'comment': '{0} {1} not present'.format(resource, name),
552 'result': True}
553 return changes_dict
554
555
556def _delete_failed(name, resource):
557 changes_dict = {'name': name,
558 'changes': {},
559 'comment': '{0} {1} failed to delete'.format(resource,
560 name),
561 'result': False}
562 return changes_dict
563
564def _create_failed(name, resource):
565 changes_dict = {'name': name,
566 'changes': {},
567 'comment': '{0} {1} failed to create'.format(resource,
568 name),
569 'result': False}
570 return changes_dict
571
572def _update_failed(name, resource):
573 changes_dict = {'name': name,
574 'changes': {},
575 'comment': '{0} {1} failed to update'.format(resource,
576 name),
577 'result': False}
578 return changes_dict
579
580
581def _get_non_null_args(**kwargs):
582 '''
583 Return those kwargs which are not null
584 '''
585 return dict((key, value,) for key, value in kwargs.iteritems()
586 if value is not None)