blob: 9f287d58e22ed3e33437486e465a0b1e33bebf4f [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 Shyshko703c02b2018-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 = (
Oleksandr Shyshko2a274ba2019-02-27 15:02:30 +0000254 'default_project_id', 'domain_id', 'enabled', 'email', 'options'
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300255 )
256 if password_reset:
257 changable += ('password',)
258 to_update = {}
259
260 for key in kwargs:
Oleksandr Shyshko2a274ba2019-02-27 15:02:30 +0000261 if key in changable:
262 if key == 'options':
263 to_update['options'] = {option: value for option, value in kwargs['options'].items()
264 if (option not in exact_user['options'])
265 or (value != exact_user['options'][option])}
266
267 if not len(to_update['options']):
268 del to_update['options']
269
270 elif key not in exact_user or kwargs[key] != exact_user[key]:
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300271 to_update[key] = kwargs[key]
272
273 if to_update:
274 log.info('Updating keystone user {} with: {}'.format(user_id,
275 to_update))
276 try:
277 resp = _keystonev3_call(
278 'user_update', user_id=user_id,
279 cloud_name=cloud_name, **to_update
280 )
281 except Exception as e:
282 log.error('Keystone user update failed with {}'.format(e))
283 return _update_failed(name, 'user')
284 return _updated(name, 'user', resp)
285 else:
286 return _no_changes(name, 'user')
287 else:
288 return _find_failed(name, 'user')
289
290
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300291def user_absent(name, cloud_name):
292 try:
293 _keystonev3_call('user_get_details', user_id=name,
294 cloud_name=cloud_name)
295 except Exception as e:
296 if 'ResourceNotFound' in repr(e):
297 return _absent(name, 'user')
298 else:
299 log.error('Failed to get user {}'.format(e))
300 return _find_failed(name, 'user')
301 try:
302 _keystonev3_call('user_delete', user_id=name, cloud_name=cloud_name)
303 except Exception:
304 return _delete_failed(name, 'user')
305 return _deleted(name, 'user')
306
307
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300308def user_role_assigned(name, role_id, cloud_name, project_id=None,
309 domain_id=None, role_domain_id=None, **kwargs):
310
311 user_id = _keystonev3_call(
312 'user_get_details', name,
313 cloud_name=cloud_name)['user']['id']
314
315 if project_id:
316 project_id = _keystonev3_call(
317 'project_get_details', project_id,
318 cloud_name=cloud_name)['project']['id']
319
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300320 if domain_id:
321 domain_id = _keystonev3_call(
322 'domain_get_details', domain_id,
323 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300324
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300325
326 if (project_id and domain_id) or (not project_id and not domain_id):
327 return {
328 'name': name,
329 'changes': {},
330 'result': False,
331 'comment': 'Use project_id or domain_id (only one of them)'
332 }
333
334
335 if role_domain_id:
336 role_domain_id = _keystonev3_call(
337 'domain_get_details', role_domain_id,
338 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300339
340 if role_id:
341 role_id = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300342 'role_get_details', role_id, domain_id=role_domain_id,
343 cloud_name=cloud_name)['role']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300344
345 req_kwargs = {'role.id': role_id, 'user.id': user_id,
346 'cloud_name': cloud_name}
347 if domain_id:
348 req_kwargs['domain_id'] = domain_id
349 if project_id:
350 req_kwargs['project_id'] = project_id
351
352 role_assignments = _keystonev3_call(
353 'role_assignment_list', **req_kwargs)['role_assignments']
354
355 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
356 'role_id': role_id}
357 if domain_id:
358 req_kwargs['domain_id'] = domain_id
359 if project_id:
360 req_kwargs['project_id'] = project_id
361
362 if not role_assignments:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300363 method_type = 'project' if project_id else 'domain'
364 method = 'role_assign_for_user_on_{}'.format(method_type)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300365 try:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300366 resp = _keystonev3_call(method, **req_kwargs)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300367 except Exception as e:
368 log.error('Keystone user role assignment with {}'.format(e))
369 return _create_failed(name, 'user_role_assignment')
370 # We check for exact assignment when did role_assignment_list
371 # on this stage we already just assigned role if it was missed.
372 return _created(name, 'user_role_assignment', resp)
373 return _no_changes(name, 'user_role_assignment')
374
375
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300376def user_role_unassign(name, role_id, cloud_name, project_id=None,
377 domain_id=None, role_domain_id=None):
Oleksandr Shyshko703c02b2018-12-19 19:20:40 +0200378
379 try:
380 user_id = _keystonev3_call(
381 'user_get_details', name,
382 cloud_name=cloud_name)['user']['id']
383
384 except Exception as e:
385 if 'ResourceNotFound' in repr(e):
386 return _no_changes(name, 'user')
387 else:
388 log.error('Failed to get user {}'.format(e))
389 return _find_failed(name, 'user')
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300390
391 if project_id:
392 project_id = _keystonev3_call(
393 'project_get_details', project_id,
394 cloud_name=cloud_name)['project']['id']
395
396 if domain_id:
397 domain_id = _keystonev3_call(
398 'domain_get_details', domain_id,
399 cloud_name=cloud_name)['domain']['id']
400
401 if (project_id and domain_id) or (not project_id and not domain_id):
402 return {
403 'name': name,
404 'changes': {},
405 'result': False,
406 'comment': 'Use project_id or domain_id (only one of them)'
407 }
408
409 if role_domain_id:
410 role_domain_id = _keystonev3_call(
411 'domain_get_details', role_domain_id,
412 cloud_name=cloud_name)['domain']['id']
413
414 if role_id:
415 role_id = _keystonev3_call(
416 'role_get_details', role_id, domain_id=role_domain_id,
417 cloud_name=cloud_name)['role']['id']
418
419 req_kwargs = {'role.id': role_id, 'user.id': user_id,
420 'cloud_name': cloud_name}
421 if domain_id:
422 req_kwargs['domain_id'] = domain_id
423 if project_id:
424 req_kwargs['project_id'] = project_id
425
426 role_assignments = _keystonev3_call(
427 'role_assignment_list', **req_kwargs)['role_assignments']
428
429 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
430 'role_id': role_id}
431 if domain_id:
432 req_kwargs['domain_id'] = domain_id
433 if project_id:
434 req_kwargs['project_id'] = project_id
435
436 if not role_assignments:
437 return _absent(name, 'user_role_assignment')
438 else:
439 method_type = 'project' if project_id else 'domain'
440 method = 'role_unassign_for_user_on_{}'.format(method_type)
441 try:
442 _keystonev3_call(method, **req_kwargs)
443 except Exception:
444 return _delete_failed(name, 'user_role_assignment')
445 return _deleted(name, 'user_role_assignment')
446
447
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300448def role_present(name, cloud_name, **kwargs):
449
450 roles = _keystonev3_call(
451 'role_list', name=name, cloud_name=cloud_name
452 )['roles']
453
454 if 'domain_id' in kwargs:
455 kwargs['domain_id'] = _keystonev3_call(
456 'domain_get_details', kwargs['domain_id'],
457 cloud_name=cloud_name)['domains']
458
459 if not roles:
460 try:
461 resp = _keystonev3_call(
462 'role_create', name=name, cloud_name=cloud_name, **kwargs
463 )
464 except Exception as e:
465 log.error('Keystone role create failed with {}'.format(e))
466 return _create_failed(name, 'role')
467 return _created(name, 'role', resp)
468 elif len(roles) == 1:
469 exact_role = roles[0]
470 role_id = exact_role['id']
471 changable = ('domain_id')
472 to_update = {}
473
474 for key in kwargs:
475 if (key in changable and (key not in exact_role or
476 kwargs[key] != exact_role[key])):
477 to_update[key] = kwargs[key]
478
479 if to_update:
480 try:
481 resp = _keystonev3_call(
482 'role_update', role_id=role_id,
483 cloud_name=cloud_name, **to_update
484 )
485 except Exception as e:
486 log.error('Keystone role update failed with {}'.format(e))
487 return _update_failed(name, 'role')
488 return _updated(name, 'role', resp)
489 else:
490 return _no_changes(name, 'role')
491 else:
492 return _find_failed(name, 'role')
493
494
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300495def role_absent(name, cloud_name):
496 try:
497 _keystonev3_call('role_get_details', role_id=name,
498 cloud_name=cloud_name)
499 except Exception as e:
500 if 'ResourceNotFound' in repr(e):
501 return _absent(name, 'role')
502 else:
503 log.error('Failed to get role {}'.format(e))
504 return _find_failed(name, 'role')
505 try:
506 _keystonev3_call('role_delete', role_id=name, cloud_name=cloud_name)
507 except Exception:
508 return _delete_failed(name, 'role')
509 return _deleted(name, 'role')
510
511
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400512def domain_present(name, cloud_name, **kwargs):
513
514 domains = _keystonev3_call(
515 'domain_list', name=name, cloud_name=cloud_name)['domains']
516
517 if not domains:
518 try:
519 resp = _keystonev3_call(
520 'domain_create', name=name, cloud_name=cloud_name, **kwargs
521 )
522 except Exception as e:
523 log.error('Keystone domain create failed with {}'.format(e))
524 return _create_failed(name, 'domain')
525 return _created(name, 'domain', resp)
526 elif len(domains) == 1:
527 exact_domain = domains[0]
528 domain_id = exact_domain['id']
529 changable = ('tags', 'enabled', 'description')
530 to_update = {}
531
532 for key in kwargs:
533 if (key in changable and (key not in exact_domain or
534 kwargs[key] != exact_domain[key])):
535 to_update[key] = kwargs[key]
536
537 if to_update:
538 try:
539 resp = _keystonev3_call(
540 'domain_update', domain_id=domain_id,
541 cloud_name=cloud_name, **to_update
542 )
543 except Exception as e:
544 log.error('Keystone domain update failed with {}'.format(e))
545 return _update_failed(name, 'domain')
546 return _updated(name, 'domain', resp)
547 else:
548 return _no_changes(name, 'domain')
549 else:
550 return _find_failed(name, 'domain')
551
552
Oleksandr Shyshko703c02b2018-12-19 19:20:40 +0200553def domain_absent(name, cloud_name, force_delete=False):
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400554 try:
555 _keystonev3_call('domain_get_details',
556 domain_id=name, cloud_name=cloud_name)
557 except Exception as e:
558 if 'ResourceNotFound' in repr(e):
559 return _absent(name, 'domain')
560 else:
561 log.error('Failed to get a domain {}'.format(e))
562 return _find_failed(name, 'domain')
Oleksandr Shyshko703c02b2018-12-19 19:20:40 +0200563
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400564 try:
Oleksandr Shyshko703c02b2018-12-19 19:20:40 +0200565 if force_delete:
566 _keystonev3_call(
567 'domain_update', domain_id=name,
568 enabled=False, cloud_name=cloud_name
569 )
570
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400571 _keystonev3_call('domain_delete', domain_id=name,
572 cloud_name=cloud_name)
573 except Exception:
574 return _delete_failed(name, 'domain')
575 return _deleted(name, 'domain')
576
577
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300578def _created(name, resource, resource_definition):
579 changes_dict = {
580 'name': name,
581 'changes': resource_definition,
582 'result': True,
583 'comment': '{}{} created'.format(resource, name)
584 }
585 return changes_dict
586
587
588def _updated(name, resource, resource_definition):
589 changes_dict = {
590 'name': name,
591 'changes': resource_definition,
592 'result': True,
593 'comment': '{}{} updated'.format(resource, name)
594 }
595 return changes_dict
596
597
598def _no_changes(name, resource):
599 changes_dict = {
600 'name': name,
601 'changes': {},
602 'result': True,
603 'comment': '{}{} is in desired state'.format(resource, name)
604 }
605 return changes_dict
606
607
608def _deleted(name, resource):
609 changes_dict = {
610 'name': name,
Oleksandr Shyshko703c02b2018-12-19 19:20:40 +0200611 'changes': {"resource": resource, "name": name, "status": 'deleted'},
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300612 'result': True,
Oleksandr Shyshko703c02b2018-12-19 19:20:40 +0200613 'comment': '{} {} removed'.format(resource, name)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300614 }
615 return changes_dict
616
617
618def _absent(name, resource):
619 changes_dict = {'name': name,
620 'changes': {},
621 'comment': '{0} {1} not present'.format(resource, name),
622 'result': True}
623 return changes_dict
624
625
626def _delete_failed(name, resource):
627 changes_dict = {'name': name,
628 'changes': {},
629 'comment': '{0} {1} failed to delete'.format(resource,
630 name),
631 'result': False}
632 return changes_dict
633
634
635def _create_failed(name, resource):
636 changes_dict = {'name': name,
637 'changes': {},
638 'comment': '{0} {1} failed to create'.format(resource,
639 name),
640 'result': False}
641 return changes_dict
642
643
644def _update_failed(name, resource):
645 changes_dict = {'name': name,
646 'changes': {},
647 'comment': '{0} {1} failed to update'.format(resource,
648 name),
649 'result': False}
650 return changes_dict
651
652
653def _find_failed(name, resource):
654 changes_dict = {
655 'name': name,
656 'changes': {},
657 'comment': '{0} {1} found multiple {0}'.format(resource, name),
658 'result': False,
659 }
660 return changes_dict