blob: 55d14e9c84ab2de016d2fd5b77183f05390df55e [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):
66 service_id = _keystonev3_call(
67 'service_get_details', service_id,
68 cloud_name=cloud_name)['service']['id']
69
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030070 endpoints = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +030071 'endpoint_list', name=name, service_id=service_id, interface=interface,
72 cloud_name=cloud_name)['endpoints']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030073 if not endpoints:
74 return _absent(name, 'endpoint')
75 elif len(endpoints) == 1:
76 try:
77 _keystonev3_call(
78 'endpoint_delete', endpoints[0]['id'], cloud_name=cloud_name
79 )
80 except Exception as e:
81 log.error('Keystone delete endpoint failed with {}'.format(e))
82 return _delete_failed(name, 'endpoint')
83 return _deleted(name, 'endpoint')
84 else:
85 return _find_failed(name, 'endpoint')
86
87
88def service_present(name, type, cloud_name, **kwargs):
89
90 service_id = ''
91
92 try:
93 exact_service = _keystonev3_call(
94 'service_get_details', name,
95 cloud_name=cloud_name)['service']
96 service_id = exact_service['id']
97 except Exception as e:
98 if 'ResourceNotFound' in repr(e):
99 pass
100 else:
101 log.error('Failed to get service {}'.format(e))
102 return _create_failed(name, 'service')
103
104 if not service_id:
105 try:
106 resp = _keystonev3_call(
107 'service_create', name=name, type=type,
108 cloud_name=cloud_name, **kwargs
109 )
110 except Exception as e:
111 log.error('Keystone service create failed with {}'.format(e))
112 return _create_failed(name, 'service')
113 return _created(name, 'service', resp)
114
115 else:
116 changable = ('type', 'enabled', 'description')
117 to_update = {}
118 to_check = {'type': type}
119 to_check.update(kwargs)
120
121 for key in to_check:
122 if (key in changable and (key not in exact_service or
123 to_check[key] != exact_service[key])):
124 to_update[key] = to_check[key]
125 if to_update:
126 try:
127 resp = _keystonev3_call(
128 'service_update', service_id=service_id,
129 cloud_name=cloud_name, **to_update
130 )
131 except Exception as e:
132 log.error('Keystone service update failed with {}'.format(e))
133 return _update_failed(name, 'service')
134 return _updated(name, 'service', resp)
135 else:
136 return _no_changes(name, 'service')
137 return _find_failed(name, 'service')
138
139
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300140def service_absent(name, cloud_name):
141 try:
142 _keystonev3_call(
143 'service_get_details', name,
144 cloud_name=cloud_name)['service']
145 except Exception as e:
146 if 'ResourceNotFound' in repr(e):
147 return _absent(name, 'service')
148 else:
149 log.error('Failed to get service {}'.format(e))
150 return _find_failed(name, 'service')
151 try:
152 _keystonev3_call('service_delete', name, cloud_name=cloud_name)
153 except Exception:
154 return _delete_failed(name, 'service')
155 return _deleted(name, 'service')
156
157
158
159def project_present(name, domain_id, cloud_name, **kwargs):
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300160
161 projects = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300162 'project_list', name=name, domain_id=domain_id, cloud_name=cloud_name
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300163 )['projects']
164
165 if not projects:
166 try:
167 resp = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300168 'project_create', domain_id=domain_id, name=name,
169 cloud_name=cloud_name, **kwargs
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300170 )
171 except Exception as e:
172 log.error('Keystone project create failed with {}'.format(e))
173 return _create_failed(name, 'project')
174 return _created(name, 'project', resp)
175 elif len(projects) == 1:
176 exact_project = projects[0]
177 project_id = exact_project['id']
178 changable = (
179 'is_domain', 'description', 'domain_id', 'enabled',
180 'parent_id', 'tags'
181 )
182 to_update = {}
183
184 for key in kwargs:
185 if (key in changable and (key not in exact_project or
186 kwargs[key] != exact_project[key])):
187 to_update[key] = kwargs[key]
188
189 if to_update:
190 try:
191 resp = _keystonev3_call(
192 'project_update', project_id=project_id,
193 cloud_name=cloud_name, **to_update
194 )
195 except Exception as e:
196 log.error('Keystone project update failed with {}'.format(e))
197 return _update_failed(name, 'project')
198 return _updated(name, 'project', resp)
199 else:
200 return _no_changes(name, 'project')
201 else:
202 return _find_failed(name, 'project')
203
204
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300205def project_absent(name, cloud_name):
206 try:
207 _keystonev3_call('project_get_details',
208 project_id=name, cloud_name=cloud_name)
209 except Exception as e:
210 if 'ResourceNotFound' in repr(e):
211 return _absent(name, 'project')
212 else:
213 log.error('Failed to get project {}'.format(e))
214 return _find_failed(name, 'project')
215 try:
216 _keystonev3_call('project_delete', project_id=name,
217 cloud_name=cloud_name)
218 except Exception:
219 return _delete_failed(name, 'project')
220 return _deleted(name, 'project')
221
222
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300223def user_present(name, cloud_name, password_reset=False, **kwargs):
224
225 users = _keystonev3_call(
226 'user_list', name=name, cloud_name=cloud_name
227 )['users']
228
229 if 'default_project_id' in kwargs:
230 kwargs['default_project_id'] = _keystonev3_call(
231 'project_get_details', kwargs['default_project_id'],
232 cloud_name=cloud_name)['project']['id']
233
234 if not users:
235 try:
236 resp = _keystonev3_call(
237 'user_create', name=name, cloud_name=cloud_name, **kwargs
238 )
239 except Exception as e:
240 log.error('Keystone user create failed with {}'.format(e))
241 return _create_failed(name, 'user')
242 return _created(name, 'user', resp)
243
244 elif len(users) == 1:
245 exact_user = users[0]
246 user_id = exact_user['id']
247 changable = (
248 'default_project_id', 'domain_id', 'enabled', 'email'
249 )
250 if password_reset:
251 changable += ('password',)
252 to_update = {}
253
254 for key in kwargs:
255 if (key in changable and (key not in exact_user or
256 kwargs[key] != exact_user[key])):
257 to_update[key] = kwargs[key]
258
259 if to_update:
260 log.info('Updating keystone user {} with: {}'.format(user_id,
261 to_update))
262 try:
263 resp = _keystonev3_call(
264 'user_update', user_id=user_id,
265 cloud_name=cloud_name, **to_update
266 )
267 except Exception as e:
268 log.error('Keystone user update failed with {}'.format(e))
269 return _update_failed(name, 'user')
270 return _updated(name, 'user', resp)
271 else:
272 return _no_changes(name, 'user')
273 else:
274 return _find_failed(name, 'user')
275
276
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300277def user_absent(name, cloud_name):
278 try:
279 _keystonev3_call('user_get_details', user_id=name,
280 cloud_name=cloud_name)
281 except Exception as e:
282 if 'ResourceNotFound' in repr(e):
283 return _absent(name, 'user')
284 else:
285 log.error('Failed to get user {}'.format(e))
286 return _find_failed(name, 'user')
287 try:
288 _keystonev3_call('user_delete', user_id=name, cloud_name=cloud_name)
289 except Exception:
290 return _delete_failed(name, 'user')
291 return _deleted(name, 'user')
292
293
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300294def user_role_assigned(name, role_id, cloud_name, project_id=None,
295 domain_id=None, role_domain_id=None, **kwargs):
296
297 user_id = _keystonev3_call(
298 'user_get_details', name,
299 cloud_name=cloud_name)['user']['id']
300
301 if project_id:
302 project_id = _keystonev3_call(
303 'project_get_details', project_id,
304 cloud_name=cloud_name)['project']['id']
305
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300306 if domain_id:
307 domain_id = _keystonev3_call(
308 'domain_get_details', domain_id,
309 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300310
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300311
312 if (project_id and domain_id) or (not project_id and not domain_id):
313 return {
314 'name': name,
315 'changes': {},
316 'result': False,
317 'comment': 'Use project_id or domain_id (only one of them)'
318 }
319
320
321 if role_domain_id:
322 role_domain_id = _keystonev3_call(
323 'domain_get_details', role_domain_id,
324 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300325
326 if role_id:
327 role_id = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300328 'role_get_details', role_id, domain_id=role_domain_id,
329 cloud_name=cloud_name)['role']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300330
331 req_kwargs = {'role.id': role_id, 'user.id': user_id,
332 'cloud_name': cloud_name}
333 if domain_id:
334 req_kwargs['domain_id'] = domain_id
335 if project_id:
336 req_kwargs['project_id'] = project_id
337
338 role_assignments = _keystonev3_call(
339 'role_assignment_list', **req_kwargs)['role_assignments']
340
341 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
342 'role_id': role_id}
343 if domain_id:
344 req_kwargs['domain_id'] = domain_id
345 if project_id:
346 req_kwargs['project_id'] = project_id
347
348 if not role_assignments:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300349 method_type = 'project' if project_id else 'domain'
350 method = 'role_assign_for_user_on_{}'.format(method_type)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300351 try:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300352 resp = _keystonev3_call(method, **req_kwargs)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300353 except Exception as e:
354 log.error('Keystone user role assignment with {}'.format(e))
355 return _create_failed(name, 'user_role_assignment')
356 # We check for exact assignment when did role_assignment_list
357 # on this stage we already just assigned role if it was missed.
358 return _created(name, 'user_role_assignment', resp)
359 return _no_changes(name, 'user_role_assignment')
360
361
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300362def user_role_unassign(name, role_id, cloud_name, project_id=None,
363 domain_id=None, role_domain_id=None):
364 user_id = _keystonev3_call(
365 'user_get_details', name,
366 cloud_name=cloud_name)['user']['id']
367
368 if project_id:
369 project_id = _keystonev3_call(
370 'project_get_details', project_id,
371 cloud_name=cloud_name)['project']['id']
372
373 if domain_id:
374 domain_id = _keystonev3_call(
375 'domain_get_details', domain_id,
376 cloud_name=cloud_name)['domain']['id']
377
378 if (project_id and domain_id) or (not project_id and not domain_id):
379 return {
380 'name': name,
381 'changes': {},
382 'result': False,
383 'comment': 'Use project_id or domain_id (only one of them)'
384 }
385
386 if role_domain_id:
387 role_domain_id = _keystonev3_call(
388 'domain_get_details', role_domain_id,
389 cloud_name=cloud_name)['domain']['id']
390
391 if role_id:
392 role_id = _keystonev3_call(
393 'role_get_details', role_id, domain_id=role_domain_id,
394 cloud_name=cloud_name)['role']['id']
395
396 req_kwargs = {'role.id': role_id, 'user.id': user_id,
397 'cloud_name': cloud_name}
398 if domain_id:
399 req_kwargs['domain_id'] = domain_id
400 if project_id:
401 req_kwargs['project_id'] = project_id
402
403 role_assignments = _keystonev3_call(
404 'role_assignment_list', **req_kwargs)['role_assignments']
405
406 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
407 'role_id': role_id}
408 if domain_id:
409 req_kwargs['domain_id'] = domain_id
410 if project_id:
411 req_kwargs['project_id'] = project_id
412
413 if not role_assignments:
414 return _absent(name, 'user_role_assignment')
415 else:
416 method_type = 'project' if project_id else 'domain'
417 method = 'role_unassign_for_user_on_{}'.format(method_type)
418 try:
419 _keystonev3_call(method, **req_kwargs)
420 except Exception:
421 return _delete_failed(name, 'user_role_assignment')
422 return _deleted(name, 'user_role_assignment')
423
424
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300425def role_present(name, cloud_name, **kwargs):
426
427 roles = _keystonev3_call(
428 'role_list', name=name, cloud_name=cloud_name
429 )['roles']
430
431 if 'domain_id' in kwargs:
432 kwargs['domain_id'] = _keystonev3_call(
433 'domain_get_details', kwargs['domain_id'],
434 cloud_name=cloud_name)['domains']
435
436 if not roles:
437 try:
438 resp = _keystonev3_call(
439 'role_create', name=name, cloud_name=cloud_name, **kwargs
440 )
441 except Exception as e:
442 log.error('Keystone role create failed with {}'.format(e))
443 return _create_failed(name, 'role')
444 return _created(name, 'role', resp)
445 elif len(roles) == 1:
446 exact_role = roles[0]
447 role_id = exact_role['id']
448 changable = ('domain_id')
449 to_update = {}
450
451 for key in kwargs:
452 if (key in changable and (key not in exact_role or
453 kwargs[key] != exact_role[key])):
454 to_update[key] = kwargs[key]
455
456 if to_update:
457 try:
458 resp = _keystonev3_call(
459 'role_update', role_id=role_id,
460 cloud_name=cloud_name, **to_update
461 )
462 except Exception as e:
463 log.error('Keystone role update failed with {}'.format(e))
464 return _update_failed(name, 'role')
465 return _updated(name, 'role', resp)
466 else:
467 return _no_changes(name, 'role')
468 else:
469 return _find_failed(name, 'role')
470
471
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300472def role_absent(name, cloud_name):
473 try:
474 _keystonev3_call('role_get_details', role_id=name,
475 cloud_name=cloud_name)
476 except Exception as e:
477 if 'ResourceNotFound' in repr(e):
478 return _absent(name, 'role')
479 else:
480 log.error('Failed to get role {}'.format(e))
481 return _find_failed(name, 'role')
482 try:
483 _keystonev3_call('role_delete', role_id=name, cloud_name=cloud_name)
484 except Exception:
485 return _delete_failed(name, 'role')
486 return _deleted(name, 'role')
487
488
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400489def domain_present(name, cloud_name, **kwargs):
490
491 domains = _keystonev3_call(
492 'domain_list', name=name, cloud_name=cloud_name)['domains']
493
494 if not domains:
495 try:
496 resp = _keystonev3_call(
497 'domain_create', name=name, cloud_name=cloud_name, **kwargs
498 )
499 except Exception as e:
500 log.error('Keystone domain create failed with {}'.format(e))
501 return _create_failed(name, 'domain')
502 return _created(name, 'domain', resp)
503 elif len(domains) == 1:
504 exact_domain = domains[0]
505 domain_id = exact_domain['id']
506 changable = ('tags', 'enabled', 'description')
507 to_update = {}
508
509 for key in kwargs:
510 if (key in changable and (key not in exact_domain or
511 kwargs[key] != exact_domain[key])):
512 to_update[key] = kwargs[key]
513
514 if to_update:
515 try:
516 resp = _keystonev3_call(
517 'domain_update', domain_id=domain_id,
518 cloud_name=cloud_name, **to_update
519 )
520 except Exception as e:
521 log.error('Keystone domain update failed with {}'.format(e))
522 return _update_failed(name, 'domain')
523 return _updated(name, 'domain', resp)
524 else:
525 return _no_changes(name, 'domain')
526 else:
527 return _find_failed(name, 'domain')
528
529
530def domain_absent(name, cloud_name):
531 try:
532 _keystonev3_call('domain_get_details',
533 domain_id=name, cloud_name=cloud_name)
534 except Exception as e:
535 if 'ResourceNotFound' in repr(e):
536 return _absent(name, 'domain')
537 else:
538 log.error('Failed to get a domain {}'.format(e))
539 return _find_failed(name, 'domain')
540 try:
541 _keystonev3_call('domain_delete', domain_id=name,
542 cloud_name=cloud_name)
543 except Exception:
544 return _delete_failed(name, 'domain')
545 return _deleted(name, 'domain')
546
547
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300548def _created(name, resource, resource_definition):
549 changes_dict = {
550 'name': name,
551 'changes': resource_definition,
552 'result': True,
553 'comment': '{}{} created'.format(resource, name)
554 }
555 return changes_dict
556
557
558def _updated(name, resource, resource_definition):
559 changes_dict = {
560 'name': name,
561 'changes': resource_definition,
562 'result': True,
563 'comment': '{}{} updated'.format(resource, name)
564 }
565 return changes_dict
566
567
568def _no_changes(name, resource):
569 changes_dict = {
570 'name': name,
571 'changes': {},
572 'result': True,
573 'comment': '{}{} is in desired state'.format(resource, name)
574 }
575 return changes_dict
576
577
578def _deleted(name, resource):
579 changes_dict = {
580 'name': name,
581 'changes': {},
582 'result': True,
583 'comment': '{}{} removed'.format(resource, name)
584 }
585 return changes_dict
586
587
588def _absent(name, resource):
589 changes_dict = {'name': name,
590 'changes': {},
591 'comment': '{0} {1} not present'.format(resource, name),
592 'result': True}
593 return changes_dict
594
595
596def _delete_failed(name, resource):
597 changes_dict = {'name': name,
598 'changes': {},
599 'comment': '{0} {1} failed to delete'.format(resource,
600 name),
601 'result': False}
602 return changes_dict
603
604
605def _create_failed(name, resource):
606 changes_dict = {'name': name,
607 'changes': {},
608 'comment': '{0} {1} failed to create'.format(resource,
609 name),
610 'result': False}
611 return changes_dict
612
613
614def _update_failed(name, resource):
615 changes_dict = {'name': name,
616 'changes': {},
617 'comment': '{0} {1} failed to update'.format(resource,
618 name),
619 'result': False}
620 return changes_dict
621
622
623def _find_failed(name, resource):
624 changes_dict = {
625 'name': name,
626 'changes': {},
627 'comment': '{0} {1} found multiple {0}'.format(resource, name),
628 'result': False,
629 }
630 return changes_dict