blob: f9012cfe13f6164cbbb9c78f35aff3ef6528a1b6 [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
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300489def _created(name, resource, resource_definition):
490 changes_dict = {
491 'name': name,
492 'changes': resource_definition,
493 'result': True,
494 'comment': '{}{} created'.format(resource, name)
495 }
496 return changes_dict
497
498
499def _updated(name, resource, resource_definition):
500 changes_dict = {
501 'name': name,
502 'changes': resource_definition,
503 'result': True,
504 'comment': '{}{} updated'.format(resource, name)
505 }
506 return changes_dict
507
508
509def _no_changes(name, resource):
510 changes_dict = {
511 'name': name,
512 'changes': {},
513 'result': True,
514 'comment': '{}{} is in desired state'.format(resource, name)
515 }
516 return changes_dict
517
518
519def _deleted(name, resource):
520 changes_dict = {
521 'name': name,
522 'changes': {},
523 'result': True,
524 'comment': '{}{} removed'.format(resource, name)
525 }
526 return changes_dict
527
528
529def _absent(name, resource):
530 changes_dict = {'name': name,
531 'changes': {},
532 'comment': '{0} {1} not present'.format(resource, name),
533 'result': True}
534 return changes_dict
535
536
537def _delete_failed(name, resource):
538 changes_dict = {'name': name,
539 'changes': {},
540 'comment': '{0} {1} failed to delete'.format(resource,
541 name),
542 'result': False}
543 return changes_dict
544
545
546def _create_failed(name, resource):
547 changes_dict = {'name': name,
548 'changes': {},
549 'comment': '{0} {1} failed to create'.format(resource,
550 name),
551 'result': False}
552 return changes_dict
553
554
555def _update_failed(name, resource):
556 changes_dict = {'name': name,
557 'changes': {},
558 'comment': '{0} {1} failed to update'.format(resource,
559 name),
560 'result': False}
561 return changes_dict
562
563
564def _find_failed(name, resource):
565 changes_dict = {
566 'name': name,
567 'changes': {},
568 'comment': '{0} {1} found multiple {0}'.format(resource, name),
569 'result': False,
570 }
571 return changes_dict