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