blob: 2dd651dcfaa7d36afa777126a5da645dad56127c [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
17 service_id = _keystonev3_call(
18 'service_get_details', service_id,
19 cloud_name=cloud_name)['service']['id']
20
21 endpoints = _keystonev3_call(
22 'endpoint_list', name=name, service_id=service_id, interface=interface,
23 cloud_name=cloud_name)['endpoints']
24
25 if not endpoints:
26 try:
27 resp = _keystonev3_call(
28 'endpoint_create', url=url, interface=interface,
29 service_id=service_id, cloud_name=cloud_name, **kwargs
30 )
31 except Exception as e:
32 log.error('Keystone endpoint create failed with {}'.format(e))
33 return _create_failed(name, 'endpoint')
34 return _created(name, 'endpoint', resp)
35 elif len(endpoints) == 1:
36 exact_endpoint = endpoints[0]
37 endpoint_id = exact_endpoint['id']
38 changable = (
39 'url', 'region', 'interface', 'service_id'
40 )
41 to_update = {}
42 to_check = {'url': url}
43 to_check.update(kwargs)
44
45 for key in to_check:
46 if (key in changable and (key not in exact_endpoint or
47 to_check[key] != exact_endpoint[key])):
48 to_update[key] = to_check[key]
49 if to_update:
50 try:
51 resp = _keystonev3_call(
52 'endpoint_update', endpoint_id=endpoint_id,
53 cloud_name=cloud_name, **to_update
54 )
55 except Exception as e:
56 log.error('Keystone endpoint update failed with {}'.format(e))
57 return _update_failed(name, 'endpoint')
58 return _updated(name, 'endpoint', resp)
59 else:
60 return _no_changes(name, 'endpoint')
61 else:
62 return _find_failed(name, 'endpoint')
63
64
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +030065def endpoint_absent(name, service_id, interface, cloud_name):
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +020066
67 try:
68 service_id = _keystonev3_call(
69 'service_get_details', service_id,
70 cloud_name=cloud_name)['service']['id']
71
72 except Exception as e:
73 if 'ResourceNotFound' in repr(e):
74 return _absent(name, 'service')
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +030075
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030076 endpoints = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +030077 'endpoint_list', name=name, service_id=service_id, interface=interface,
78 cloud_name=cloud_name)['endpoints']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030079 if not endpoints:
80 return _absent(name, 'endpoint')
81 elif len(endpoints) == 1:
82 try:
83 _keystonev3_call(
84 'endpoint_delete', endpoints[0]['id'], cloud_name=cloud_name
85 )
86 except Exception as e:
87 log.error('Keystone delete endpoint failed with {}'.format(e))
88 return _delete_failed(name, 'endpoint')
89 return _deleted(name, 'endpoint')
90 else:
91 return _find_failed(name, 'endpoint')
92
93
94def service_present(name, type, cloud_name, **kwargs):
95
96 service_id = ''
97
98 try:
99 exact_service = _keystonev3_call(
100 'service_get_details', name,
101 cloud_name=cloud_name)['service']
102 service_id = exact_service['id']
103 except Exception as e:
104 if 'ResourceNotFound' in repr(e):
105 pass
106 else:
107 log.error('Failed to get service {}'.format(e))
108 return _create_failed(name, 'service')
109
110 if not service_id:
111 try:
112 resp = _keystonev3_call(
113 'service_create', name=name, type=type,
114 cloud_name=cloud_name, **kwargs
115 )
116 except Exception as e:
117 log.error('Keystone service create failed with {}'.format(e))
118 return _create_failed(name, 'service')
119 return _created(name, 'service', resp)
120
121 else:
122 changable = ('type', 'enabled', 'description')
123 to_update = {}
124 to_check = {'type': type}
125 to_check.update(kwargs)
126
127 for key in to_check:
128 if (key in changable and (key not in exact_service or
129 to_check[key] != exact_service[key])):
130 to_update[key] = to_check[key]
131 if to_update:
132 try:
133 resp = _keystonev3_call(
134 'service_update', service_id=service_id,
135 cloud_name=cloud_name, **to_update
136 )
137 except Exception as e:
138 log.error('Keystone service update failed with {}'.format(e))
139 return _update_failed(name, 'service')
140 return _updated(name, 'service', resp)
141 else:
142 return _no_changes(name, 'service')
143 return _find_failed(name, 'service')
144
145
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300146def service_absent(name, cloud_name):
147 try:
148 _keystonev3_call(
149 'service_get_details', name,
150 cloud_name=cloud_name)['service']
151 except Exception as e:
152 if 'ResourceNotFound' in repr(e):
153 return _absent(name, 'service')
154 else:
155 log.error('Failed to get service {}'.format(e))
156 return _find_failed(name, 'service')
157 try:
158 _keystonev3_call('service_delete', name, cloud_name=cloud_name)
159 except Exception:
160 return _delete_failed(name, 'service')
161 return _deleted(name, 'service')
162
163
164
165def project_present(name, domain_id, cloud_name, **kwargs):
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300166
167 projects = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300168 'project_list', name=name, domain_id=domain_id, cloud_name=cloud_name
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300169 )['projects']
170
171 if not projects:
172 try:
173 resp = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300174 'project_create', domain_id=domain_id, name=name,
175 cloud_name=cloud_name, **kwargs
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300176 )
177 except Exception as e:
178 log.error('Keystone project create failed with {}'.format(e))
179 return _create_failed(name, 'project')
180 return _created(name, 'project', resp)
181 elif len(projects) == 1:
182 exact_project = projects[0]
183 project_id = exact_project['id']
184 changable = (
185 'is_domain', 'description', 'domain_id', 'enabled',
186 'parent_id', 'tags'
187 )
188 to_update = {}
189
190 for key in kwargs:
191 if (key in changable and (key not in exact_project or
192 kwargs[key] != exact_project[key])):
193 to_update[key] = kwargs[key]
194
195 if to_update:
196 try:
197 resp = _keystonev3_call(
198 'project_update', project_id=project_id,
199 cloud_name=cloud_name, **to_update
200 )
201 except Exception as e:
202 log.error('Keystone project update failed with {}'.format(e))
203 return _update_failed(name, 'project')
204 return _updated(name, 'project', resp)
205 else:
206 return _no_changes(name, 'project')
207 else:
208 return _find_failed(name, 'project')
209
210
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300211def project_absent(name, cloud_name):
212 try:
213 _keystonev3_call('project_get_details',
214 project_id=name, cloud_name=cloud_name)
215 except Exception as e:
216 if 'ResourceNotFound' in repr(e):
217 return _absent(name, 'project')
218 else:
219 log.error('Failed to get project {}'.format(e))
220 return _find_failed(name, 'project')
221 try:
222 _keystonev3_call('project_delete', project_id=name,
223 cloud_name=cloud_name)
224 except Exception:
225 return _delete_failed(name, 'project')
226 return _deleted(name, 'project')
227
228
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300229def user_present(name, cloud_name, password_reset=False, **kwargs):
230
231 users = _keystonev3_call(
232 'user_list', name=name, cloud_name=cloud_name
233 )['users']
234
235 if 'default_project_id' in kwargs:
236 kwargs['default_project_id'] = _keystonev3_call(
237 'project_get_details', kwargs['default_project_id'],
238 cloud_name=cloud_name)['project']['id']
239
240 if not users:
241 try:
242 resp = _keystonev3_call(
243 'user_create', name=name, cloud_name=cloud_name, **kwargs
244 )
245 except Exception as e:
246 log.error('Keystone user create failed with {}'.format(e))
247 return _create_failed(name, 'user')
248 return _created(name, 'user', resp)
249
250 elif len(users) == 1:
251 exact_user = users[0]
252 user_id = exact_user['id']
253 changable = (
254 'default_project_id', 'domain_id', 'enabled', 'email'
255 )
256 if password_reset:
257 changable += ('password',)
258 to_update = {}
259
260 for key in kwargs:
261 if (key in changable and (key not in exact_user or
262 kwargs[key] != exact_user[key])):
263 to_update[key] = kwargs[key]
264
265 if to_update:
266 log.info('Updating keystone user {} with: {}'.format(user_id,
267 to_update))
268 try:
269 resp = _keystonev3_call(
270 'user_update', user_id=user_id,
271 cloud_name=cloud_name, **to_update
272 )
273 except Exception as e:
274 log.error('Keystone user update failed with {}'.format(e))
275 return _update_failed(name, 'user')
276 return _updated(name, 'user', resp)
277 else:
278 return _no_changes(name, 'user')
279 else:
280 return _find_failed(name, 'user')
281
282
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300283def user_absent(name, cloud_name):
284 try:
285 _keystonev3_call('user_get_details', user_id=name,
286 cloud_name=cloud_name)
287 except Exception as e:
288 if 'ResourceNotFound' in repr(e):
289 return _absent(name, 'user')
290 else:
291 log.error('Failed to get user {}'.format(e))
292 return _find_failed(name, 'user')
293 try:
294 _keystonev3_call('user_delete', user_id=name, cloud_name=cloud_name)
295 except Exception:
296 return _delete_failed(name, 'user')
297 return _deleted(name, 'user')
298
299
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300300def user_role_assigned(name, role_id, cloud_name, project_id=None,
301 domain_id=None, role_domain_id=None, **kwargs):
302
303 user_id = _keystonev3_call(
304 'user_get_details', name,
305 cloud_name=cloud_name)['user']['id']
306
307 if project_id:
308 project_id = _keystonev3_call(
309 'project_get_details', project_id,
310 cloud_name=cloud_name)['project']['id']
311
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300312 if domain_id:
313 domain_id = _keystonev3_call(
314 'domain_get_details', domain_id,
315 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300316
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300317
318 if (project_id and domain_id) or (not project_id and not domain_id):
319 return {
320 'name': name,
321 'changes': {},
322 'result': False,
323 'comment': 'Use project_id or domain_id (only one of them)'
324 }
325
326
327 if role_domain_id:
328 role_domain_id = _keystonev3_call(
329 'domain_get_details', role_domain_id,
330 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300331
332 if role_id:
333 role_id = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300334 'role_get_details', role_id, domain_id=role_domain_id,
335 cloud_name=cloud_name)['role']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300336
337 req_kwargs = {'role.id': role_id, 'user.id': user_id,
338 'cloud_name': cloud_name}
339 if domain_id:
340 req_kwargs['domain_id'] = domain_id
341 if project_id:
342 req_kwargs['project_id'] = project_id
343
344 role_assignments = _keystonev3_call(
345 'role_assignment_list', **req_kwargs)['role_assignments']
346
347 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
348 'role_id': role_id}
349 if domain_id:
350 req_kwargs['domain_id'] = domain_id
351 if project_id:
352 req_kwargs['project_id'] = project_id
353
354 if not role_assignments:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300355 method_type = 'project' if project_id else 'domain'
356 method = 'role_assign_for_user_on_{}'.format(method_type)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300357 try:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300358 resp = _keystonev3_call(method, **req_kwargs)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300359 except Exception as e:
360 log.error('Keystone user role assignment with {}'.format(e))
361 return _create_failed(name, 'user_role_assignment')
362 # We check for exact assignment when did role_assignment_list
363 # on this stage we already just assigned role if it was missed.
364 return _created(name, 'user_role_assignment', resp)
365 return _no_changes(name, 'user_role_assignment')
366
367
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300368def user_role_unassign(name, role_id, cloud_name, project_id=None,
369 domain_id=None, role_domain_id=None):
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200370
371 try:
372 user_id = _keystonev3_call(
373 'user_get_details', name,
374 cloud_name=cloud_name)['user']['id']
375
376 except Exception as e:
377 if 'ResourceNotFound' in repr(e):
378 return _no_changes(name, 'user')
379 else:
380 log.error('Failed to get user {}'.format(e))
381 return _find_failed(name, 'user')
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300382
383 if project_id:
384 project_id = _keystonev3_call(
385 'project_get_details', project_id,
386 cloud_name=cloud_name)['project']['id']
387
388 if domain_id:
389 domain_id = _keystonev3_call(
390 'domain_get_details', domain_id,
391 cloud_name=cloud_name)['domain']['id']
392
393 if (project_id and domain_id) or (not project_id and not domain_id):
394 return {
395 'name': name,
396 'changes': {},
397 'result': False,
398 'comment': 'Use project_id or domain_id (only one of them)'
399 }
400
401 if role_domain_id:
402 role_domain_id = _keystonev3_call(
403 'domain_get_details', role_domain_id,
404 cloud_name=cloud_name)['domain']['id']
405
406 if role_id:
407 role_id = _keystonev3_call(
408 'role_get_details', role_id, domain_id=role_domain_id,
409 cloud_name=cloud_name)['role']['id']
410
411 req_kwargs = {'role.id': role_id, 'user.id': user_id,
412 'cloud_name': cloud_name}
413 if domain_id:
414 req_kwargs['domain_id'] = domain_id
415 if project_id:
416 req_kwargs['project_id'] = project_id
417
418 role_assignments = _keystonev3_call(
419 'role_assignment_list', **req_kwargs)['role_assignments']
420
421 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
422 'role_id': role_id}
423 if domain_id:
424 req_kwargs['domain_id'] = domain_id
425 if project_id:
426 req_kwargs['project_id'] = project_id
427
428 if not role_assignments:
429 return _absent(name, 'user_role_assignment')
430 else:
431 method_type = 'project' if project_id else 'domain'
432 method = 'role_unassign_for_user_on_{}'.format(method_type)
433 try:
434 _keystonev3_call(method, **req_kwargs)
435 except Exception:
436 return _delete_failed(name, 'user_role_assignment')
437 return _deleted(name, 'user_role_assignment')
438
439
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300440def role_present(name, cloud_name, **kwargs):
441
442 roles = _keystonev3_call(
443 'role_list', name=name, cloud_name=cloud_name
444 )['roles']
445
446 if 'domain_id' in kwargs:
447 kwargs['domain_id'] = _keystonev3_call(
448 'domain_get_details', kwargs['domain_id'],
449 cloud_name=cloud_name)['domains']
450
451 if not roles:
452 try:
453 resp = _keystonev3_call(
454 'role_create', name=name, cloud_name=cloud_name, **kwargs
455 )
456 except Exception as e:
457 log.error('Keystone role create failed with {}'.format(e))
458 return _create_failed(name, 'role')
459 return _created(name, 'role', resp)
460 elif len(roles) == 1:
461 exact_role = roles[0]
462 role_id = exact_role['id']
463 changable = ('domain_id')
464 to_update = {}
465
466 for key in kwargs:
467 if (key in changable and (key not in exact_role or
468 kwargs[key] != exact_role[key])):
469 to_update[key] = kwargs[key]
470
471 if to_update:
472 try:
473 resp = _keystonev3_call(
474 'role_update', role_id=role_id,
475 cloud_name=cloud_name, **to_update
476 )
477 except Exception as e:
478 log.error('Keystone role update failed with {}'.format(e))
479 return _update_failed(name, 'role')
480 return _updated(name, 'role', resp)
481 else:
482 return _no_changes(name, 'role')
483 else:
484 return _find_failed(name, 'role')
485
486
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300487def role_absent(name, cloud_name):
488 try:
489 _keystonev3_call('role_get_details', role_id=name,
490 cloud_name=cloud_name)
491 except Exception as e:
492 if 'ResourceNotFound' in repr(e):
493 return _absent(name, 'role')
494 else:
495 log.error('Failed to get role {}'.format(e))
496 return _find_failed(name, 'role')
497 try:
498 _keystonev3_call('role_delete', role_id=name, cloud_name=cloud_name)
499 except Exception:
500 return _delete_failed(name, 'role')
501 return _deleted(name, 'role')
502
503
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400504def domain_present(name, cloud_name, **kwargs):
505
506 domains = _keystonev3_call(
507 'domain_list', name=name, cloud_name=cloud_name)['domains']
508
509 if not domains:
510 try:
511 resp = _keystonev3_call(
512 'domain_create', name=name, cloud_name=cloud_name, **kwargs
513 )
514 except Exception as e:
515 log.error('Keystone domain create failed with {}'.format(e))
516 return _create_failed(name, 'domain')
517 return _created(name, 'domain', resp)
518 elif len(domains) == 1:
519 exact_domain = domains[0]
520 domain_id = exact_domain['id']
521 changable = ('tags', 'enabled', 'description')
522 to_update = {}
523
524 for key in kwargs:
525 if (key in changable and (key not in exact_domain or
526 kwargs[key] != exact_domain[key])):
527 to_update[key] = kwargs[key]
528
529 if to_update:
530 try:
531 resp = _keystonev3_call(
532 'domain_update', domain_id=domain_id,
533 cloud_name=cloud_name, **to_update
534 )
535 except Exception as e:
536 log.error('Keystone domain update failed with {}'.format(e))
537 return _update_failed(name, 'domain')
538 return _updated(name, 'domain', resp)
539 else:
540 return _no_changes(name, 'domain')
541 else:
542 return _find_failed(name, 'domain')
543
544
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200545def domain_absent(name, cloud_name, force_delete=False):
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400546 try:
547 _keystonev3_call('domain_get_details',
548 domain_id=name, cloud_name=cloud_name)
549 except Exception as e:
550 if 'ResourceNotFound' in repr(e):
551 return _absent(name, 'domain')
552 else:
553 log.error('Failed to get a domain {}'.format(e))
554 return _find_failed(name, 'domain')
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200555
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400556 try:
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200557 if force_delete:
558 _keystonev3_call(
559 'domain_update', domain_id=name,
560 enabled=False, cloud_name=cloud_name
561 )
562
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400563 _keystonev3_call('domain_delete', domain_id=name,
564 cloud_name=cloud_name)
565 except Exception:
566 return _delete_failed(name, 'domain')
567 return _deleted(name, 'domain')
568
569
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300570def _created(name, resource, resource_definition):
571 changes_dict = {
572 'name': name,
573 'changes': resource_definition,
574 'result': True,
575 'comment': '{}{} created'.format(resource, name)
576 }
577 return changes_dict
578
579
580def _updated(name, resource, resource_definition):
581 changes_dict = {
582 'name': name,
583 'changes': resource_definition,
584 'result': True,
585 'comment': '{}{} updated'.format(resource, name)
586 }
587 return changes_dict
588
589
590def _no_changes(name, resource):
591 changes_dict = {
592 'name': name,
593 'changes': {},
594 'result': True,
595 'comment': '{}{} is in desired state'.format(resource, name)
596 }
597 return changes_dict
598
599
600def _deleted(name, resource):
601 changes_dict = {
602 'name': name,
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200603 'changes': {"resource": resource, "name": name, "status": 'deleted'},
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300604 'result': True,
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200605 'comment': '{} {} removed'.format(resource, name)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300606 }
607 return changes_dict
608
609
610def _absent(name, resource):
611 changes_dict = {'name': name,
612 'changes': {},
613 'comment': '{0} {1} not present'.format(resource, name),
614 'result': True}
615 return changes_dict
616
617
618def _delete_failed(name, resource):
619 changes_dict = {'name': name,
620 'changes': {},
621 'comment': '{0} {1} failed to delete'.format(resource,
622 name),
623 'result': False}
624 return changes_dict
625
626
627def _create_failed(name, resource):
628 changes_dict = {'name': name,
629 'changes': {},
630 'comment': '{0} {1} failed to create'.format(resource,
631 name),
632 'result': False}
633 return changes_dict
634
635
636def _update_failed(name, resource):
637 changes_dict = {'name': name,
638 'changes': {},
639 'comment': '{0} {1} failed to update'.format(resource,
640 name),
641 'result': False}
642 return changes_dict
643
644
645def _find_failed(name, resource):
646 changes_dict = {
647 'name': name,
648 'changes': {},
649 'comment': '{0} {1} found multiple {0}'.format(resource, name),
650 'result': False,
651 }
652 return changes_dict