blob: d390c14f042d3a6fec328b2651eaaf20c33f011b [file] [log] [blame]
Vasyl Saienko4eda4f22018-04-26 19:30:39 +03001import logging
2
3
4def __virtual__():
5 return 'keystonev3' if 'keystonev3.endpoint_list' in __salt__ else False # noqa
6
7
8log = logging.getLogger(__name__)
9
10
11def _keystonev3_call(fname, *args, **kwargs):
12 return __salt__['keystonev3.{}'.format(fname)](*args, **kwargs) # noqa
13
14
15def endpoint_present(name, url, interface, service_id, cloud_name, **kwargs):
16
Vasyl Saienkoa8fc6ee2019-04-25 07:48:08 +000017 ekwargs = {}
18 if 'region_id' in kwargs:
19 ekwargs['region_id'] = kwargs['region_id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030020 service_id = _keystonev3_call(
21 'service_get_details', service_id,
22 cloud_name=cloud_name)['service']['id']
23
24 endpoints = _keystonev3_call(
25 'endpoint_list', name=name, service_id=service_id, interface=interface,
Vasyl Saienkoa8fc6ee2019-04-25 07:48:08 +000026 cloud_name=cloud_name, **ekwargs)['endpoints']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030027
28 if not endpoints:
29 try:
30 resp = _keystonev3_call(
31 'endpoint_create', url=url, interface=interface,
32 service_id=service_id, cloud_name=cloud_name, **kwargs
33 )
34 except Exception as e:
35 log.error('Keystone endpoint create failed with {}'.format(e))
36 return _create_failed(name, 'endpoint')
37 return _created(name, 'endpoint', resp)
38 elif len(endpoints) == 1:
39 exact_endpoint = endpoints[0]
40 endpoint_id = exact_endpoint['id']
41 changable = (
42 'url', 'region', 'interface', 'service_id'
43 )
44 to_update = {}
45 to_check = {'url': url}
46 to_check.update(kwargs)
47
48 for key in to_check:
49 if (key in changable and (key not in exact_endpoint or
50 to_check[key] != exact_endpoint[key])):
51 to_update[key] = to_check[key]
52 if to_update:
53 try:
54 resp = _keystonev3_call(
55 'endpoint_update', endpoint_id=endpoint_id,
56 cloud_name=cloud_name, **to_update
57 )
58 except Exception as e:
59 log.error('Keystone endpoint update failed with {}'.format(e))
60 return _update_failed(name, 'endpoint')
61 return _updated(name, 'endpoint', resp)
62 else:
63 return _no_changes(name, 'endpoint')
64 else:
65 return _find_failed(name, 'endpoint')
66
67
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +030068def endpoint_absent(name, service_id, interface, cloud_name):
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +020069
70 try:
71 service_id = _keystonev3_call(
72 'service_get_details', service_id,
73 cloud_name=cloud_name)['service']['id']
74
75 except Exception as e:
76 if 'ResourceNotFound' in repr(e):
77 return _absent(name, 'service')
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +030078
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030079 endpoints = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +030080 'endpoint_list', name=name, service_id=service_id, interface=interface,
81 cloud_name=cloud_name)['endpoints']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030082 if not endpoints:
83 return _absent(name, 'endpoint')
84 elif len(endpoints) == 1:
85 try:
86 _keystonev3_call(
87 'endpoint_delete', endpoints[0]['id'], cloud_name=cloud_name
88 )
89 except Exception as e:
90 log.error('Keystone delete endpoint failed with {}'.format(e))
91 return _delete_failed(name, 'endpoint')
92 return _deleted(name, 'endpoint')
93 else:
94 return _find_failed(name, 'endpoint')
95
96
97def service_present(name, type, cloud_name, **kwargs):
98
99 service_id = ''
100
101 try:
102 exact_service = _keystonev3_call(
103 'service_get_details', name,
104 cloud_name=cloud_name)['service']
105 service_id = exact_service['id']
106 except Exception as e:
107 if 'ResourceNotFound' in repr(e):
108 pass
109 else:
110 log.error('Failed to get service {}'.format(e))
111 return _create_failed(name, 'service')
112
113 if not service_id:
114 try:
115 resp = _keystonev3_call(
116 'service_create', name=name, type=type,
117 cloud_name=cloud_name, **kwargs
118 )
119 except Exception as e:
120 log.error('Keystone service create failed with {}'.format(e))
121 return _create_failed(name, 'service')
122 return _created(name, 'service', resp)
123
124 else:
125 changable = ('type', 'enabled', 'description')
126 to_update = {}
127 to_check = {'type': type}
128 to_check.update(kwargs)
129
130 for key in to_check:
131 if (key in changable and (key not in exact_service or
132 to_check[key] != exact_service[key])):
133 to_update[key] = to_check[key]
134 if to_update:
135 try:
136 resp = _keystonev3_call(
137 'service_update', service_id=service_id,
138 cloud_name=cloud_name, **to_update
139 )
140 except Exception as e:
141 log.error('Keystone service update failed with {}'.format(e))
142 return _update_failed(name, 'service')
143 return _updated(name, 'service', resp)
144 else:
145 return _no_changes(name, 'service')
146 return _find_failed(name, 'service')
147
148
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300149def service_absent(name, cloud_name):
150 try:
151 _keystonev3_call(
152 'service_get_details', name,
153 cloud_name=cloud_name)['service']
154 except Exception as e:
155 if 'ResourceNotFound' in repr(e):
156 return _absent(name, 'service')
157 else:
158 log.error('Failed to get service {}'.format(e))
159 return _find_failed(name, 'service')
160 try:
161 _keystonev3_call('service_delete', name, cloud_name=cloud_name)
162 except Exception:
163 return _delete_failed(name, 'service')
164 return _deleted(name, 'service')
165
166
167
168def project_present(name, domain_id, cloud_name, **kwargs):
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300169
170 projects = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300171 'project_list', name=name, domain_id=domain_id, cloud_name=cloud_name
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300172 )['projects']
173
174 if not projects:
175 try:
176 resp = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300177 'project_create', domain_id=domain_id, name=name,
178 cloud_name=cloud_name, **kwargs
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300179 )
180 except Exception as e:
181 log.error('Keystone project create failed with {}'.format(e))
182 return _create_failed(name, 'project')
183 return _created(name, 'project', resp)
184 elif len(projects) == 1:
185 exact_project = projects[0]
186 project_id = exact_project['id']
187 changable = (
188 'is_domain', 'description', 'domain_id', 'enabled',
189 'parent_id', 'tags'
190 )
191 to_update = {}
192
193 for key in kwargs:
194 if (key in changable and (key not in exact_project or
195 kwargs[key] != exact_project[key])):
196 to_update[key] = kwargs[key]
197
198 if to_update:
199 try:
200 resp = _keystonev3_call(
201 'project_update', project_id=project_id,
202 cloud_name=cloud_name, **to_update
203 )
204 except Exception as e:
205 log.error('Keystone project update failed with {}'.format(e))
206 return _update_failed(name, 'project')
207 return _updated(name, 'project', resp)
208 else:
209 return _no_changes(name, 'project')
210 else:
211 return _find_failed(name, 'project')
212
213
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300214def project_absent(name, cloud_name):
215 try:
216 _keystonev3_call('project_get_details',
217 project_id=name, cloud_name=cloud_name)
218 except Exception as e:
219 if 'ResourceNotFound' in repr(e):
220 return _absent(name, 'project')
221 else:
222 log.error('Failed to get project {}'.format(e))
223 return _find_failed(name, 'project')
224 try:
225 _keystonev3_call('project_delete', project_id=name,
226 cloud_name=cloud_name)
227 except Exception:
228 return _delete_failed(name, 'project')
229 return _deleted(name, 'project')
230
231
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300232def user_present(name, cloud_name, password_reset=False, **kwargs):
233
234 users = _keystonev3_call(
235 'user_list', name=name, cloud_name=cloud_name
236 )['users']
237
238 if 'default_project_id' in kwargs:
239 kwargs['default_project_id'] = _keystonev3_call(
240 'project_get_details', kwargs['default_project_id'],
241 cloud_name=cloud_name)['project']['id']
242
Michael Polenchuk4de57c52019-01-22 12:43:12 +0400243 if 'domain_id' in kwargs:
244 kwargs['domain_id'] = _keystonev3_call(
245 'domain_get_details', kwargs['domain_id'],
246 cloud_name=cloud_name)['domain']['id']
247
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300248 if not users:
249 try:
250 resp = _keystonev3_call(
251 'user_create', name=name, cloud_name=cloud_name, **kwargs
252 )
253 except Exception as e:
Oleksandr Shyshkodaaa1ae2019-04-18 13:16:31 +0000254 log.error('Keystone user create failed with: {}'.format(e))
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300255 return _create_failed(name, 'user')
256 return _created(name, 'user', resp)
257
258 elif len(users) == 1:
259 exact_user = users[0]
260 user_id = exact_user['id']
261 changable = (
Oleksandr Shyshkoa0b79e22019-02-27 15:02:30 +0000262 'default_project_id', 'domain_id', 'enabled', 'email', 'options'
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300263 )
264 if password_reset:
265 changable += ('password',)
266 to_update = {}
267
268 for key in kwargs:
Oleksandr Shyshkoa0b79e22019-02-27 15:02:30 +0000269 if key in changable:
270 if key == 'options':
271 to_update['options'] = {option: value for option, value in kwargs['options'].items()
272 if (option not in exact_user['options'])
273 or (value != exact_user['options'][option])}
274
275 if not len(to_update['options']):
276 del to_update['options']
277
278 elif key not in exact_user or kwargs[key] != exact_user[key]:
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300279 to_update[key] = kwargs[key]
280
281 if to_update:
282 log.info('Updating keystone user {} with: {}'.format(user_id,
283 to_update))
284 try:
285 resp = _keystonev3_call(
286 'user_update', user_id=user_id,
287 cloud_name=cloud_name, **to_update
288 )
289 except Exception as e:
290 log.error('Keystone user update failed with {}'.format(e))
291 return _update_failed(name, 'user')
292 return _updated(name, 'user', resp)
293 else:
294 return _no_changes(name, 'user')
295 else:
296 return _find_failed(name, 'user')
297
298
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300299def user_absent(name, cloud_name):
300 try:
301 _keystonev3_call('user_get_details', user_id=name,
302 cloud_name=cloud_name)
303 except Exception as e:
304 if 'ResourceNotFound' in repr(e):
305 return _absent(name, 'user')
306 else:
307 log.error('Failed to get user {}'.format(e))
308 return _find_failed(name, 'user')
309 try:
310 _keystonev3_call('user_delete', user_id=name, cloud_name=cloud_name)
311 except Exception:
312 return _delete_failed(name, 'user')
313 return _deleted(name, 'user')
314
315
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300316def user_role_assigned(name, role_id, cloud_name, project_id=None,
317 domain_id=None, role_domain_id=None, **kwargs):
318
319 user_id = _keystonev3_call(
320 'user_get_details', name,
321 cloud_name=cloud_name)['user']['id']
322
323 if project_id:
324 project_id = _keystonev3_call(
325 'project_get_details', project_id,
326 cloud_name=cloud_name)['project']['id']
327
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300328 if domain_id:
329 domain_id = _keystonev3_call(
330 'domain_get_details', domain_id,
331 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300332
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300333
334 if (project_id and domain_id) or (not project_id and not domain_id):
335 return {
336 'name': name,
337 'changes': {},
338 'result': False,
339 'comment': 'Use project_id or domain_id (only one of them)'
340 }
341
342
343 if role_domain_id:
344 role_domain_id = _keystonev3_call(
345 'domain_get_details', role_domain_id,
346 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300347
348 if role_id:
349 role_id = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300350 'role_get_details', role_id, domain_id=role_domain_id,
351 cloud_name=cloud_name)['role']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300352
353 req_kwargs = {'role.id': role_id, 'user.id': user_id,
354 'cloud_name': cloud_name}
355 if domain_id:
356 req_kwargs['domain_id'] = domain_id
357 if project_id:
358 req_kwargs['project_id'] = project_id
359
360 role_assignments = _keystonev3_call(
361 'role_assignment_list', **req_kwargs)['role_assignments']
362
363 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
364 'role_id': role_id}
365 if domain_id:
366 req_kwargs['domain_id'] = domain_id
367 if project_id:
368 req_kwargs['project_id'] = project_id
369
370 if not role_assignments:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300371 method_type = 'project' if project_id else 'domain'
372 method = 'role_assign_for_user_on_{}'.format(method_type)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300373 try:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300374 resp = _keystonev3_call(method, **req_kwargs)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300375 except Exception as e:
376 log.error('Keystone user role assignment with {}'.format(e))
377 return _create_failed(name, 'user_role_assignment')
378 # We check for exact assignment when did role_assignment_list
379 # on this stage we already just assigned role if it was missed.
380 return _created(name, 'user_role_assignment', resp)
381 return _no_changes(name, 'user_role_assignment')
382
383
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300384def user_role_unassign(name, role_id, cloud_name, project_id=None,
385 domain_id=None, role_domain_id=None):
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200386
387 try:
388 user_id = _keystonev3_call(
389 'user_get_details', name,
390 cloud_name=cloud_name)['user']['id']
391
392 except Exception as e:
393 if 'ResourceNotFound' in repr(e):
394 return _no_changes(name, 'user')
395 else:
396 log.error('Failed to get user {}'.format(e))
397 return _find_failed(name, 'user')
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300398
399 if project_id:
400 project_id = _keystonev3_call(
401 'project_get_details', project_id,
402 cloud_name=cloud_name)['project']['id']
403
404 if domain_id:
405 domain_id = _keystonev3_call(
406 'domain_get_details', domain_id,
407 cloud_name=cloud_name)['domain']['id']
408
409 if (project_id and domain_id) or (not project_id and not domain_id):
410 return {
411 'name': name,
412 'changes': {},
413 'result': False,
414 'comment': 'Use project_id or domain_id (only one of them)'
415 }
416
417 if role_domain_id:
418 role_domain_id = _keystonev3_call(
419 'domain_get_details', role_domain_id,
420 cloud_name=cloud_name)['domain']['id']
421
422 if role_id:
423 role_id = _keystonev3_call(
424 'role_get_details', role_id, domain_id=role_domain_id,
425 cloud_name=cloud_name)['role']['id']
426
427 req_kwargs = {'role.id': role_id, 'user.id': user_id,
428 'cloud_name': cloud_name}
429 if domain_id:
430 req_kwargs['domain_id'] = domain_id
431 if project_id:
432 req_kwargs['project_id'] = project_id
433
434 role_assignments = _keystonev3_call(
435 'role_assignment_list', **req_kwargs)['role_assignments']
436
437 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
438 'role_id': role_id}
439 if domain_id:
440 req_kwargs['domain_id'] = domain_id
441 if project_id:
442 req_kwargs['project_id'] = project_id
443
444 if not role_assignments:
445 return _absent(name, 'user_role_assignment')
446 else:
447 method_type = 'project' if project_id else 'domain'
448 method = 'role_unassign_for_user_on_{}'.format(method_type)
449 try:
450 _keystonev3_call(method, **req_kwargs)
451 except Exception:
452 return _delete_failed(name, 'user_role_assignment')
453 return _deleted(name, 'user_role_assignment')
454
455
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300456def role_present(name, cloud_name, **kwargs):
457
458 roles = _keystonev3_call(
459 'role_list', name=name, cloud_name=cloud_name
460 )['roles']
461
462 if 'domain_id' in kwargs:
463 kwargs['domain_id'] = _keystonev3_call(
464 'domain_get_details', kwargs['domain_id'],
465 cloud_name=cloud_name)['domains']
466
467 if not roles:
468 try:
469 resp = _keystonev3_call(
470 'role_create', name=name, cloud_name=cloud_name, **kwargs
471 )
472 except Exception as e:
473 log.error('Keystone role create failed with {}'.format(e))
474 return _create_failed(name, 'role')
475 return _created(name, 'role', resp)
476 elif len(roles) == 1:
477 exact_role = roles[0]
478 role_id = exact_role['id']
479 changable = ('domain_id')
480 to_update = {}
481
482 for key in kwargs:
483 if (key in changable and (key not in exact_role or
484 kwargs[key] != exact_role[key])):
485 to_update[key] = kwargs[key]
486
487 if to_update:
488 try:
489 resp = _keystonev3_call(
490 'role_update', role_id=role_id,
491 cloud_name=cloud_name, **to_update
492 )
493 except Exception as e:
494 log.error('Keystone role update failed with {}'.format(e))
495 return _update_failed(name, 'role')
496 return _updated(name, 'role', resp)
497 else:
498 return _no_changes(name, 'role')
499 else:
500 return _find_failed(name, 'role')
501
502
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300503def role_absent(name, cloud_name):
504 try:
505 _keystonev3_call('role_get_details', role_id=name,
506 cloud_name=cloud_name)
507 except Exception as e:
508 if 'ResourceNotFound' in repr(e):
509 return _absent(name, 'role')
510 else:
511 log.error('Failed to get role {}'.format(e))
512 return _find_failed(name, 'role')
513 try:
514 _keystonev3_call('role_delete', role_id=name, cloud_name=cloud_name)
515 except Exception:
516 return _delete_failed(name, 'role')
517 return _deleted(name, 'role')
518
519
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400520def domain_present(name, cloud_name, **kwargs):
521
522 domains = _keystonev3_call(
523 'domain_list', name=name, cloud_name=cloud_name)['domains']
524
525 if not domains:
526 try:
527 resp = _keystonev3_call(
528 'domain_create', name=name, cloud_name=cloud_name, **kwargs
529 )
530 except Exception as e:
531 log.error('Keystone domain create failed with {}'.format(e))
532 return _create_failed(name, 'domain')
533 return _created(name, 'domain', resp)
534 elif len(domains) == 1:
535 exact_domain = domains[0]
536 domain_id = exact_domain['id']
537 changable = ('tags', 'enabled', 'description')
538 to_update = {}
539
540 for key in kwargs:
541 if (key in changable and (key not in exact_domain or
542 kwargs[key] != exact_domain[key])):
543 to_update[key] = kwargs[key]
544
545 if to_update:
546 try:
547 resp = _keystonev3_call(
548 'domain_update', domain_id=domain_id,
549 cloud_name=cloud_name, **to_update
550 )
551 except Exception as e:
552 log.error('Keystone domain update failed with {}'.format(e))
553 return _update_failed(name, 'domain')
554 return _updated(name, 'domain', resp)
555 else:
556 return _no_changes(name, 'domain')
557 else:
558 return _find_failed(name, 'domain')
559
560
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200561def domain_absent(name, cloud_name, force_delete=False):
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400562 try:
563 _keystonev3_call('domain_get_details',
564 domain_id=name, cloud_name=cloud_name)
565 except Exception as e:
566 if 'ResourceNotFound' in repr(e):
567 return _absent(name, 'domain')
568 else:
569 log.error('Failed to get a domain {}'.format(e))
570 return _find_failed(name, 'domain')
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200571
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400572 try:
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200573 if force_delete:
574 _keystonev3_call(
575 'domain_update', domain_id=name,
576 enabled=False, cloud_name=cloud_name
577 )
578
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400579 _keystonev3_call('domain_delete', domain_id=name,
580 cloud_name=cloud_name)
581 except Exception:
582 return _delete_failed(name, 'domain')
583 return _deleted(name, 'domain')
584
585
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300586def _created(name, resource, resource_definition):
587 changes_dict = {
588 'name': name,
589 'changes': resource_definition,
590 'result': True,
591 'comment': '{}{} created'.format(resource, name)
592 }
593 return changes_dict
594
595
596def _updated(name, resource, resource_definition):
597 changes_dict = {
598 'name': name,
599 'changes': resource_definition,
600 'result': True,
601 'comment': '{}{} updated'.format(resource, name)
602 }
603 return changes_dict
604
605
606def _no_changes(name, resource):
607 changes_dict = {
608 'name': name,
609 'changes': {},
610 'result': True,
611 'comment': '{}{} is in desired state'.format(resource, name)
612 }
613 return changes_dict
614
615
616def _deleted(name, resource):
617 changes_dict = {
618 'name': name,
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200619 'changes': {"resource": resource, "name": name, "status": 'deleted'},
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300620 'result': True,
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200621 'comment': '{} {} removed'.format(resource, name)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300622 }
623 return changes_dict
624
625
626def _absent(name, resource):
627 changes_dict = {'name': name,
628 'changes': {},
629 'comment': '{0} {1} not present'.format(resource, name),
630 'result': True}
631 return changes_dict
632
633
634def _delete_failed(name, resource):
635 changes_dict = {'name': name,
636 'changes': {},
637 'comment': '{0} {1} failed to delete'.format(resource,
638 name),
639 'result': False}
640 return changes_dict
641
642
643def _create_failed(name, resource):
644 changes_dict = {'name': name,
645 'changes': {},
646 'comment': '{0} {1} failed to create'.format(resource,
647 name),
648 'result': False}
649 return changes_dict
650
651
652def _update_failed(name, resource):
653 changes_dict = {'name': name,
654 'changes': {},
655 'comment': '{0} {1} failed to update'.format(resource,
656 name),
657 'result': False}
658 return changes_dict
659
660
661def _find_failed(name, resource):
662 changes_dict = {
663 'name': name,
664 'changes': {},
665 'comment': '{0} {1} found multiple {0}'.format(resource, name),
666 'result': False,
667 }
668 return changes_dict