blob: 69622908d4c535f1f7ecfb809b61607097c64c83 [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
Michael Polenchuk4de57c52019-01-22 12:43:12 +0400240 if 'domain_id' in kwargs:
241 kwargs['domain_id'] = _keystonev3_call(
242 'domain_get_details', kwargs['domain_id'],
243 cloud_name=cloud_name)['domain']['id']
244
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300245 if not users:
246 try:
247 resp = _keystonev3_call(
248 'user_create', name=name, cloud_name=cloud_name, **kwargs
249 )
250 except Exception as e:
251 log.error('Keystone user create failed with {}'.format(e))
252 return _create_failed(name, 'user')
253 return _created(name, 'user', resp)
254
255 elif len(users) == 1:
256 exact_user = users[0]
257 user_id = exact_user['id']
258 changable = (
Oleksandr Shyshkoa0b79e22019-02-27 15:02:30 +0000259 'default_project_id', 'domain_id', 'enabled', 'email', 'options'
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300260 )
261 if password_reset:
262 changable += ('password',)
263 to_update = {}
264
265 for key in kwargs:
Oleksandr Shyshkoa0b79e22019-02-27 15:02:30 +0000266 if key in changable:
267 if key == 'options':
268 to_update['options'] = {option: value for option, value in kwargs['options'].items()
269 if (option not in exact_user['options'])
270 or (value != exact_user['options'][option])}
271
272 if not len(to_update['options']):
273 del to_update['options']
274
275 elif key not in exact_user or kwargs[key] != exact_user[key]:
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300276 to_update[key] = kwargs[key]
277
278 if to_update:
279 log.info('Updating keystone user {} with: {}'.format(user_id,
280 to_update))
281 try:
282 resp = _keystonev3_call(
283 'user_update', user_id=user_id,
284 cloud_name=cloud_name, **to_update
285 )
286 except Exception as e:
287 log.error('Keystone user update failed with {}'.format(e))
288 return _update_failed(name, 'user')
289 return _updated(name, 'user', resp)
290 else:
291 return _no_changes(name, 'user')
292 else:
293 return _find_failed(name, 'user')
294
295
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300296def user_absent(name, cloud_name):
297 try:
298 _keystonev3_call('user_get_details', user_id=name,
299 cloud_name=cloud_name)
300 except Exception as e:
301 if 'ResourceNotFound' in repr(e):
302 return _absent(name, 'user')
303 else:
304 log.error('Failed to get user {}'.format(e))
305 return _find_failed(name, 'user')
306 try:
307 _keystonev3_call('user_delete', user_id=name, cloud_name=cloud_name)
308 except Exception:
309 return _delete_failed(name, 'user')
310 return _deleted(name, 'user')
311
312
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300313def user_role_assigned(name, role_id, cloud_name, project_id=None,
314 domain_id=None, role_domain_id=None, **kwargs):
315
316 user_id = _keystonev3_call(
317 'user_get_details', name,
318 cloud_name=cloud_name)['user']['id']
319
320 if project_id:
321 project_id = _keystonev3_call(
322 'project_get_details', project_id,
323 cloud_name=cloud_name)['project']['id']
324
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300325 if domain_id:
326 domain_id = _keystonev3_call(
327 'domain_get_details', domain_id,
328 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300329
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300330
331 if (project_id and domain_id) or (not project_id and not domain_id):
332 return {
333 'name': name,
334 'changes': {},
335 'result': False,
336 'comment': 'Use project_id or domain_id (only one of them)'
337 }
338
339
340 if role_domain_id:
341 role_domain_id = _keystonev3_call(
342 'domain_get_details', role_domain_id,
343 cloud_name=cloud_name)['domain']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300344
345 if role_id:
346 role_id = _keystonev3_call(
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300347 'role_get_details', role_id, domain_id=role_domain_id,
348 cloud_name=cloud_name)['role']['id']
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300349
350 req_kwargs = {'role.id': role_id, 'user.id': user_id,
351 'cloud_name': cloud_name}
352 if domain_id:
353 req_kwargs['domain_id'] = domain_id
354 if project_id:
355 req_kwargs['project_id'] = project_id
356
357 role_assignments = _keystonev3_call(
358 'role_assignment_list', **req_kwargs)['role_assignments']
359
360 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
361 'role_id': role_id}
362 if domain_id:
363 req_kwargs['domain_id'] = domain_id
364 if project_id:
365 req_kwargs['project_id'] = project_id
366
367 if not role_assignments:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300368 method_type = 'project' if project_id else 'domain'
369 method = 'role_assign_for_user_on_{}'.format(method_type)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300370 try:
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300371 resp = _keystonev3_call(method, **req_kwargs)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300372 except Exception as e:
373 log.error('Keystone user role assignment with {}'.format(e))
374 return _create_failed(name, 'user_role_assignment')
375 # We check for exact assignment when did role_assignment_list
376 # on this stage we already just assigned role if it was missed.
377 return _created(name, 'user_role_assignment', resp)
378 return _no_changes(name, 'user_role_assignment')
379
380
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300381def user_role_unassign(name, role_id, cloud_name, project_id=None,
382 domain_id=None, role_domain_id=None):
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200383
384 try:
385 user_id = _keystonev3_call(
386 'user_get_details', name,
387 cloud_name=cloud_name)['user']['id']
388
389 except Exception as e:
390 if 'ResourceNotFound' in repr(e):
391 return _no_changes(name, 'user')
392 else:
393 log.error('Failed to get user {}'.format(e))
394 return _find_failed(name, 'user')
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300395
396 if project_id:
397 project_id = _keystonev3_call(
398 'project_get_details', project_id,
399 cloud_name=cloud_name)['project']['id']
400
401 if domain_id:
402 domain_id = _keystonev3_call(
403 'domain_get_details', domain_id,
404 cloud_name=cloud_name)['domain']['id']
405
406 if (project_id and domain_id) or (not project_id and not domain_id):
407 return {
408 'name': name,
409 'changes': {},
410 'result': False,
411 'comment': 'Use project_id or domain_id (only one of them)'
412 }
413
414 if role_domain_id:
415 role_domain_id = _keystonev3_call(
416 'domain_get_details', role_domain_id,
417 cloud_name=cloud_name)['domain']['id']
418
419 if role_id:
420 role_id = _keystonev3_call(
421 'role_get_details', role_id, domain_id=role_domain_id,
422 cloud_name=cloud_name)['role']['id']
423
424 req_kwargs = {'role.id': role_id, 'user.id': user_id,
425 'cloud_name': cloud_name}
426 if domain_id:
427 req_kwargs['domain_id'] = domain_id
428 if project_id:
429 req_kwargs['project_id'] = project_id
430
431 role_assignments = _keystonev3_call(
432 'role_assignment_list', **req_kwargs)['role_assignments']
433
434 req_kwargs = {'cloud_name': cloud_name, 'user_id': user_id,
435 'role_id': role_id}
436 if domain_id:
437 req_kwargs['domain_id'] = domain_id
438 if project_id:
439 req_kwargs['project_id'] = project_id
440
441 if not role_assignments:
442 return _absent(name, 'user_role_assignment')
443 else:
444 method_type = 'project' if project_id else 'domain'
445 method = 'role_unassign_for_user_on_{}'.format(method_type)
446 try:
447 _keystonev3_call(method, **req_kwargs)
448 except Exception:
449 return _delete_failed(name, 'user_role_assignment')
450 return _deleted(name, 'user_role_assignment')
451
452
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300453def role_present(name, cloud_name, **kwargs):
454
455 roles = _keystonev3_call(
456 'role_list', name=name, cloud_name=cloud_name
457 )['roles']
458
459 if 'domain_id' in kwargs:
460 kwargs['domain_id'] = _keystonev3_call(
461 'domain_get_details', kwargs['domain_id'],
462 cloud_name=cloud_name)['domains']
463
464 if not roles:
465 try:
466 resp = _keystonev3_call(
467 'role_create', name=name, cloud_name=cloud_name, **kwargs
468 )
469 except Exception as e:
470 log.error('Keystone role create failed with {}'.format(e))
471 return _create_failed(name, 'role')
472 return _created(name, 'role', resp)
473 elif len(roles) == 1:
474 exact_role = roles[0]
475 role_id = exact_role['id']
476 changable = ('domain_id')
477 to_update = {}
478
479 for key in kwargs:
480 if (key in changable and (key not in exact_role or
481 kwargs[key] != exact_role[key])):
482 to_update[key] = kwargs[key]
483
484 if to_update:
485 try:
486 resp = _keystonev3_call(
487 'role_update', role_id=role_id,
488 cloud_name=cloud_name, **to_update
489 )
490 except Exception as e:
491 log.error('Keystone role update failed with {}'.format(e))
492 return _update_failed(name, 'role')
493 return _updated(name, 'role', resp)
494 else:
495 return _no_changes(name, 'role')
496 else:
497 return _find_failed(name, 'role')
498
499
Oleksiy Petrenkoe03e2c72018-08-10 13:24:32 +0300500def role_absent(name, cloud_name):
501 try:
502 _keystonev3_call('role_get_details', role_id=name,
503 cloud_name=cloud_name)
504 except Exception as e:
505 if 'ResourceNotFound' in repr(e):
506 return _absent(name, 'role')
507 else:
508 log.error('Failed to get role {}'.format(e))
509 return _find_failed(name, 'role')
510 try:
511 _keystonev3_call('role_delete', role_id=name, cloud_name=cloud_name)
512 except Exception:
513 return _delete_failed(name, 'role')
514 return _deleted(name, 'role')
515
516
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400517def domain_present(name, cloud_name, **kwargs):
518
519 domains = _keystonev3_call(
520 'domain_list', name=name, cloud_name=cloud_name)['domains']
521
522 if not domains:
523 try:
524 resp = _keystonev3_call(
525 'domain_create', name=name, cloud_name=cloud_name, **kwargs
526 )
527 except Exception as e:
528 log.error('Keystone domain create failed with {}'.format(e))
529 return _create_failed(name, 'domain')
530 return _created(name, 'domain', resp)
531 elif len(domains) == 1:
532 exact_domain = domains[0]
533 domain_id = exact_domain['id']
534 changable = ('tags', 'enabled', 'description')
535 to_update = {}
536
537 for key in kwargs:
538 if (key in changable and (key not in exact_domain or
539 kwargs[key] != exact_domain[key])):
540 to_update[key] = kwargs[key]
541
542 if to_update:
543 try:
544 resp = _keystonev3_call(
545 'domain_update', domain_id=domain_id,
546 cloud_name=cloud_name, **to_update
547 )
548 except Exception as e:
549 log.error('Keystone domain update failed with {}'.format(e))
550 return _update_failed(name, 'domain')
551 return _updated(name, 'domain', resp)
552 else:
553 return _no_changes(name, 'domain')
554 else:
555 return _find_failed(name, 'domain')
556
557
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200558def domain_absent(name, cloud_name, force_delete=False):
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400559 try:
560 _keystonev3_call('domain_get_details',
561 domain_id=name, cloud_name=cloud_name)
562 except Exception as e:
563 if 'ResourceNotFound' in repr(e):
564 return _absent(name, 'domain')
565 else:
566 log.error('Failed to get a domain {}'.format(e))
567 return _find_failed(name, 'domain')
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200568
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400569 try:
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200570 if force_delete:
571 _keystonev3_call(
572 'domain_update', domain_id=name,
573 enabled=False, cloud_name=cloud_name
574 )
575
Michael Polenchuk89e1edb2018-11-27 13:06:49 +0400576 _keystonev3_call('domain_delete', domain_id=name,
577 cloud_name=cloud_name)
578 except Exception:
579 return _delete_failed(name, 'domain')
580 return _deleted(name, 'domain')
581
582
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300583def _created(name, resource, resource_definition):
584 changes_dict = {
585 'name': name,
586 'changes': resource_definition,
587 'result': True,
588 'comment': '{}{} created'.format(resource, name)
589 }
590 return changes_dict
591
592
593def _updated(name, resource, resource_definition):
594 changes_dict = {
595 'name': name,
596 'changes': resource_definition,
597 'result': True,
598 'comment': '{}{} updated'.format(resource, name)
599 }
600 return changes_dict
601
602
603def _no_changes(name, resource):
604 changes_dict = {
605 'name': name,
606 'changes': {},
607 'result': True,
608 'comment': '{}{} is in desired state'.format(resource, name)
609 }
610 return changes_dict
611
612
613def _deleted(name, resource):
614 changes_dict = {
615 'name': name,
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200616 'changes': {"resource": resource, "name": name, "status": 'deleted'},
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300617 'result': True,
Oleksandr Shyshkoc0a50e42018-12-19 19:20:40 +0200618 'comment': '{} {} removed'.format(resource, name)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300619 }
620 return changes_dict
621
622
623def _absent(name, resource):
624 changes_dict = {'name': name,
625 'changes': {},
626 'comment': '{0} {1} not present'.format(resource, name),
627 'result': True}
628 return changes_dict
629
630
631def _delete_failed(name, resource):
632 changes_dict = {'name': name,
633 'changes': {},
634 'comment': '{0} {1} failed to delete'.format(resource,
635 name),
636 'result': False}
637 return changes_dict
638
639
640def _create_failed(name, resource):
641 changes_dict = {'name': name,
642 'changes': {},
643 'comment': '{0} {1} failed to create'.format(resource,
644 name),
645 'result': False}
646 return changes_dict
647
648
649def _update_failed(name, resource):
650 changes_dict = {'name': name,
651 'changes': {},
652 'comment': '{0} {1} failed to update'.format(resource,
653 name),
654 'result': False}
655 return changes_dict
656
657
658def _find_failed(name, resource):
659 changes_dict = {
660 'name': name,
661 'changes': {},
662 'comment': '{0} {1} found multiple {0}'.format(resource, name),
663 'result': False,
664 }
665 return changes_dict