blob: ddf0da5190424ae0da920230a155475e1d5e41b6 [file] [log] [blame]
Oleg Iurchenko5b1e5322017-10-20 00:29:20 +03001# -*- coding: utf-8 -*-
2'''
3Module for handling openstack keystone calls.
4
5:optdepends: - keystoneclient Python adapter
6:configuration: This module is not usable until the following are specified
7 either in a pillar or in the minion's config file:
8
9 .. code-block:: yaml
10
11 keystone.user: admin
12 keystone.password: verybadpass
13 keystone.tenant: admin
14 keystone.tenant_id: f80919baedab48ec8931f200c65a50df
15 keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
16
17 OR (for token based authentication)
18
19 .. code-block:: yaml
20
21 keystone.token: 'ADMIN'
22 keystone.endpoint: 'http://127.0.0.1:35357/v2.0'
23
24 If configuration for multiple openstack accounts is required, they can be
25 set up as different configuration profiles. For example:
26
27 .. code-block:: yaml
28
29 openstack1:
30 keystone.user: admin
31 keystone.password: verybadpass
32 keystone.tenant: admin
33 keystone.tenant_id: f80919baedab48ec8931f200c65a50df
34 keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
35
36 openstack2:
37 keystone.user: admin
38 keystone.password: verybadpass
39 keystone.tenant: admin
40 keystone.tenant_id: f80919baedab48ec8931f200c65a50df
41 keystone.auth_url: 'http://127.0.0.2:5000/v2.0/'
42
43 openstack_version3:
44 keystone.user: admin
45 keystone.password: verybadpass
46 keystone.tenant: admin
47 keystone.tenant_id: f80919baedab48ec8931f200c65a50df
48 keystone.auth_url: 'http://127.0.0.2:5000/v3'
49
50 openstack_nonversioned:
51 keystone.user: admin
52 keystone.password: verybadpass
53 keystone.tenant: admin
54 keystone.tenant_id: f80919baedab48ec8931f200c65a50df
55 keystone.auth_url: 'http://127.0.0.2:5000'
56
57 With this configuration in place, any of the keystone functions can make use
58 of a configuration profile by declaring it explicitly.
59 For example:
60
61 .. code-block:: bash
62
63 salt '*' keystone.tenant_list profile=openstack1
64'''
65
66# Import Python libs
67from __future__ import absolute_import
68import logging
69
70# Import Salt Libs
71import salt.utils.http
72
73# Import 3rd-party libs
74from salt.ext import six
75HAS_KEYSTONE = False
76try:
77 # pylint: disable=import-error
78 from keystoneclient import client
79 from keystoneclient.v2_0 import client as keystoneclient_v2
80 import keystoneclient.exceptions
81 HAS_KEYSTONE = True
82 from keystoneclient.v3 import client as keystoneclient_v3
83 from keystoneclient import discover
84 from keystoneauth1 import session
85 from keystoneauth1.identity import generic
86 from keystoneauth1 import token_endpoint
87 # pylint: enable=import-error
88except ImportError:
89 pass
90
91log = logging.getLogger(__name__)
92
93
94def __virtual__():
95 '''
96 Only load this module if keystone
97 is installed on this minion.
98 '''
99 if HAS_KEYSTONE:
100 return 'keystoneng'
101 return (False, 'keystone execution module cannot be loaded: keystoneclient python library not available.')
102
103__opts__ = {}
104
105
106def _get_kwargs(profile=None, **connection_args):
107 '''
108 get connection args
109 '''
110 if profile:
111 prefix = profile + ":keystone."
112 else:
113 prefix = "keystone."
114
115 def get(key, default=None):
116 '''
117 look in connection_args first, then default to config file
118 '''
119 return connection_args.get('connection_' + key,
120 __salt__['config.get'](prefix + key, default))
121
122 user = get('user', 'admin')
123 password = get('password', 'ADMIN')
124 tenant = get('tenant', 'admin')
125 tenant_id = get('tenant_id')
126 auth_url = get('auth_url', 'http://127.0.0.1:5000/v2.0/')
127 insecure = get('insecure', False)
128 token = get('token')
129 endpoint = get('endpoint', 'http://127.0.0.1:5000/v2.0')
130 if token:
131 kwargs = {'token': token,
132 'endpoint': endpoint}
133 else:
134 kwargs = {'username': user,
135 'password': password,
136 'tenant_name': tenant,
137 'tenant_id': tenant_id,
138 'auth_url': auth_url}
139 # 'insecure' keyword not supported by all v2.0 keystone clients
140 # this ensures it's only passed in when defineda
141 if get('user_id'): kwargs['user_id']=get('user_id')
142 if get('user_domain_id'): kwargs['user_domain_id']=get('user_domain_id')
143 if get('domain_id'): kwargs['domain_id']=get('domain_id')
144 if get('domain_name'): kwargs['domain_name']=get('domain_name')
145 if get('project_id'): kwargs['project_id']=get('project_id')
146 if get('project_name'): kwargs['project_name']=get('project_name')
147 if get('project_domain_id'): kwargs['project_domain_id']=get('project_domain_id')
148 if get('region_name'): kwargs['region_name']=get('region_name')
149 if get('endpoint'): kwargs['endpoint']=get('endpoint')
150 if get('timeout'): kwargs['timeout']=get('timeout')
151 if get('user_domain_name'): kwargs['user_domain_name']=get('user_domain_name')
152 if get('project_domain_name'): kwargs['project_domain_name']=get('project_domain_name')
153 kwargs['insecure'] = get('insecure', False)
154 if get('version'):
155 version_list = str(get('version')).split('.')
156 if len(version_list) == 2:
157 kwargs['version'] = (version_list[0], version_list[1])
158 else:
159 kwargs['version'] = (version_list[0])
160
161 return kwargs
162
163
164def _client_version(keystone_client):
165 '''
166 Returns keystone client version
167 '''
168 if isinstance(keystone_client, keystoneclient_v3.Client):
169 return 3
170 if isinstance(keystone_client, keystoneclient_v2.Client):
171 return 2
172 return None
173
174
175def _project_mgr(keystone_client):
176 '''
177 Returns keystoneclient.v3.Client.projects object if keystone client version > 2
178 or keystoneclient.v2_0.Client.tenants for other cases.
179 '''
180 if _client_version(keystone_client) > 2:
181 return keystone_client.projects
182 return keystone_client.tenants
183
184
185def api_version(profile=None, **connection_args):
186 '''
187 Returns the API version derived from endpoint's response.
188
189 CLI Example:
190
191 .. code-block:: bash
192
193 salt '*' keystone.api_version
194 '''
195 keystone_client = auth(profile, **connection_args)
196 if isinstance(keystone_client, keystoneclient_v3.Client):
197 return 3
198 if isinstance(keystone_client, keystoneclient_v2.Client):
199 return 2
200 return None
201
202
203def auth(profile=None, **connection_args):
204 '''
205 Set up keystone credentials. Only intended to be used within Keystone-enabled modules.
206
207 CLI Example:
208
209 .. code-block:: bash
210
211 salt '*' keystone.auth
212 '''
213 kwargs = _get_kwargs(profile=profile, **connection_args)
214 if 'token' in kwargs:
215 auth = token_endpoint.Token(endpoint=kwargs['endpoint'], token=kwargs['token'])
216 else:
217 # keystoneauth1 Password class does not accept some args. Therefore remove it from args for auth.
218 auth_connection_args=kwargs.copy()
219 auth_connection_args.pop('region_name', None)
220 auth_connection_args.pop('version', None)
221 auth_connection_args.pop('insecure', None)
222 auth = generic.Password(**auth_connection_args)
223 if 'insecure' in kwargs:
224 certs_verify = False
225 else:
226 certs_verify = True
227 sess = session.Session(auth=auth, verify=certs_verify)
228 keystone_client=client.Client(session=sess, **kwargs)
229 return keystone_client
230
231
232def ec2_credentials_create(user_id=None, name=None,
233 tenant_id=None, tenant=None,
234 profile=None, **connection_args):
235 '''
236 Create EC2-compatible credentials for user per tenant
237
238 CLI Examples:
239
240 .. code-block:: bash
241
242 salt '*' keystone.ec2_credentials_create name=admin tenant=admin
243
244 salt '*' keystone.ec2_credentials_create \
245 user_id=c965f79c4f864eaaa9c3b41904e67082 \
246 tenant_id=722787eb540849158668370dc627ec5f
247 '''
248 kstone = auth(profile, **connection_args)
249
250 if name:
251 user_id = user_get(name=name, profile=profile,
252 **connection_args)[name]['id']
253 if not user_id:
254 return {'Error': 'Could not resolve User ID'}
255
256 if tenant:
257 tenant_id = tenant_get(name=tenant, profile=profile,
258 **connection_args)[tenant]['id']
259 if not tenant_id:
260 return {'Error': 'Could not resolve Tenant ID'}
261
262 newec2 = kstone.ec2.create(user_id, tenant_id)
263 return {'access': newec2.access,
264 'secret': newec2.secret,
265 'tenant_id': newec2.tenant_id,
266 'user_id': newec2.user_id}
267
268
269def ec2_credentials_delete(user_id=None, name=None, access_key=None,
270 profile=None, **connection_args):
271 '''
272 Delete EC2-compatible credentials
273
274 CLI Examples:
275
276 .. code-block:: bash
277
278 salt '*' keystone.ec2_credentials_delete \
279 860f8c2c38ca4fab989f9bc56a061a64 access_key=5f66d2f24f604b8bb9cd28886106f442
280
281 salt '*' keystone.ec2_credentials_delete name=admin \
282 access_key=5f66d2f24f604b8bb9cd28886106f442
283 '''
284 kstone = auth(profile, **connection_args)
285
286 if name:
287 user_id = user_get(name=name, profile=None, **connection_args)[name]['id']
288 if not user_id:
289 return {'Error': 'Could not resolve User ID'}
290 kstone.ec2.delete(user_id, access_key)
291 return 'ec2 key "{0}" deleted under user id "{1}"'.format(access_key,
292 user_id)
293
294
295def ec2_credentials_get(user_id=None, name=None, access=None,
296 profile=None, **connection_args):
297 '''
298 Return ec2_credentials for a user (keystone ec2-credentials-get)
299
300 CLI Examples:
301
302 .. code-block:: bash
303
304 salt '*' keystone.ec2_credentials_get c965f79c4f864eaaa9c3b41904e67082 access=722787eb540849158668370
305 salt '*' keystone.ec2_credentials_get user_id=c965f79c4f864eaaa9c3b41904e67082 access=722787eb540849158668370
306 salt '*' keystone.ec2_credentials_get name=nova access=722787eb540849158668370dc627ec5f
307 '''
308 kstone = auth(profile, **connection_args)
309 ret = {}
310 if name:
311 for user in kstone.users.list():
312 if user.name == name:
313 user_id = user.id
314 break
315 if not user_id:
316 return {'Error': 'Unable to resolve user id'}
317 if not access:
318 return {'Error': 'Access key is required'}
319 ec2_credentials = kstone.ec2.get(user_id=user_id, access=access,
320 profile=profile, **connection_args)
321 ret[ec2_credentials.user_id] = {'user_id': ec2_credentials.user_id,
322 'tenant': ec2_credentials.tenant_id,
323 'access': ec2_credentials.access,
324 'secret': ec2_credentials.secret}
325 return ret
326
327
328def ec2_credentials_list(user_id=None, name=None, profile=None,
329 **connection_args):
330 '''
331 Return a list of ec2_credentials for a specific user (keystone ec2-credentials-list)
332
333 CLI Examples:
334
335 .. code-block:: bash
336
337 salt '*' keystone.ec2_credentials_list 298ce377245c4ec9b70e1c639c89e654
338 salt '*' keystone.ec2_credentials_list user_id=298ce377245c4ec9b70e1c639c89e654
339 salt '*' keystone.ec2_credentials_list name=jack
340 '''
341 kstone = auth(profile, **connection_args)
342 ret = {}
343 if name:
344 for user in kstone.users.list():
345 if user.name == name:
346 user_id = user.id
347 break
348 if not user_id:
349 return {'Error': 'Unable to resolve user id'}
350 for ec2_credential in kstone.ec2.list(user_id):
351 ret[ec2_credential.user_id] = {'user_id': ec2_credential.user_id,
352 'tenant_id': ec2_credential.tenant_id,
353 'access': ec2_credential.access,
354 'secret': ec2_credential.secret}
355 return ret
356
357
358def endpoint_get(service, region=None, profile=None, interface=None, **connection_args):
359 '''
360 Return a specific endpoint (keystone endpoint-get)
361
362 CLI Example:
363
364 .. code-block:: bash
365
366 salt 'v2' keystone.endpoint_get nova [region=RegionOne]
367
368 salt 'v3' keystone.endpoint_get nova interface=admin [region=RegionOne]
369 '''
370 auth(profile, **connection_args)
371 services = service_list(profile, **connection_args)
372 if service not in services:
373 return {'Error': 'Could not find the specified service'}
374 service_id = services[service]['id']
375 endpoints = endpoint_list(profile, **connection_args)
376
377 e = [_f for _f in [e
378 if e['service_id'] == service_id and
379 (e['region'] == region if region else True) and
380 (e['interface'] == interface if interface else True)
381 else None for e in endpoints.values()] if _f]
382 if len(e) > 1:
383 return {'Error': 'Multiple endpoints found ({0}) for the {1} service. Please specify region.'.format(e, service)}
384 if len(e) == 1:
385 return e[0]
386 return {'Error': 'Could not find endpoint for the specified service'}
387
388
389def endpoint_list(profile=None, **connection_args):
390 '''
391 Return a list of available endpoints (keystone endpoints-list)
392
393 CLI Example:
394
395 .. code-block:: bash
396
397 salt '*' keystone.endpoint_list
398 '''
399 kstone = auth(profile, **connection_args)
400 ret = {}
401
402 for endpoint in kstone.endpoints.list():
403 ret[endpoint.id] = dict((value, getattr(endpoint, value)) for value in dir(endpoint)
404 if not value.startswith('_') and
405 isinstance(getattr(endpoint, value), (six.string_types, dict, bool)))
406 return ret
407
408
409def endpoint_create(service, publicurl=None, internalurl=None, adminurl=None,
410 region=None, profile=None, url=None, interface=None, **connection_args):
411 '''
412 Create an endpoint for an Openstack service
413
414 CLI Examples:
415
416 .. code-block:: bash
417
418 salt 'v2' keystone.endpoint_create nova 'http://public/url' 'http://internal/url' 'http://adminurl/url' region
419
420 salt 'v3' keystone.endpoint_create nova url='http://public/url' interface='public' region='RegionOne'
421 '''
422 kstone = auth(profile, **connection_args)
423 keystone_service = service_get(name=service, profile=profile,
424 **connection_args)
425 if not keystone_service or 'Error' in keystone_service:
426 return {'Error': 'Could not find the specified service'}
427
428 if _client_version(kstone) > 2:
429 kstone.endpoints.create(service=keystone_service[service]['id'],
430 region_id=region,
431 url=url,
432 interface=interface)
433 else:
434 kstone.endpoints.create(region=region,
435 service_id=keystone_service[service]['id'],
436 publicurl=publicurl,
437 adminurl=adminurl,
438 internalurl=internalurl)
439 return endpoint_get(service, region, profile, interface, **connection_args)
440
441
442def endpoint_delete(service, region=None, profile=None, interface=None, **connection_args):
443 '''
444 Delete endpoints of an Openstack service
445
446 CLI Examples:
447
448 .. code-block:: bash
449
450 salt 'v2' keystone.endpoint_delete nova [region=RegionOne]
451
452 salt 'v3' keystone.endpoint_delete nova interface=admin [region=RegionOne]
453 '''
454 kstone = auth(profile, **connection_args)
455 endpoint = endpoint_get(service, region, profile, interface, **connection_args)
456 if not endpoint or 'Error' in endpoint:
457 return {'Error': 'Could not find any endpoints for the service'}
458 kstone.endpoints.delete(endpoint['id'])
459 endpoint = endpoint_get(service, region, profile, interface, **connection_args)
460 if not endpoint or 'Error' in endpoint:
461 return True
462
463
464def role_create(name, profile=None, **connection_args):
465 '''
466 Create a named role.
467
468 CLI Example:
469
470 .. code-block:: bash
471
472 salt '*' keystone.role_create admin
473 '''
474
475 kstone = auth(profile, **connection_args)
476 if 'Error' not in role_get(name=name, profile=profile, **connection_args):
477 return {'Error': 'Role "{0}" already exists'.format(name)}
478 kstone.roles.create(name)
479 return role_get(name=name, profile=profile, **connection_args)
480
481
482def role_delete(role_id=None, name=None, profile=None,
483 **connection_args):
484 '''
485 Delete a role (keystone role-delete)
486
487
488 CLI Examples:
489
490 .. code-block:: bash
491
492 salt '*' keystone.role_delete c965f79c4f864eaaa9c3b41904e67082
493 salt '*' keystone.role_delete role_id=c965f79c4f864eaaa9c3b41904e67082
494 salt '*' keystone.role_delete name=admin
495 '''
496 kstone = auth(profile, **connection_args)
497
498 if name:
499 for role in kstone.roles.list():
500 if role.name == name:
501 role_id = role.id
502 break
503 if not role_id:
504 return {'Error': 'Unable to resolve role id'}
505
506 role = kstone.roles.get(role_id)
507 kstone.roles.delete(role)
508
509 ret = 'Role ID {0} deleted'.format(role_id)
510 if name:
511 ret += ' ({0})'.format(name)
512 return ret
513
514
515def role_get(role_id=None, name=None, profile=None, **connection_args):
516 '''
517 Return a specific roles (keystone role-get)
518
519 CLI Examples:
520
521 .. code-block:: bash
522
523 salt '*' keystone.role_get c965f79c4f864eaaa9c3b41904e67082
524 salt '*' keystone.role_get role_id=c965f79c4f864eaaa9c3b41904e67082
525 salt '*' keystone.role_get name=nova
526 '''
527 kstone = auth(profile, **connection_args)
528 ret = {}
529 if name:
530 for role in kstone.roles.list():
531 if role.name == name:
532 role_id = role.id
533 break
534 if not role_id:
535 return {'Error': 'Unable to resolve role id'}
536 role = kstone.roles.get(role_id)
537
538 ret[role.name] = {'id': role.id,
539 'name': role.name}
540 return ret
541
542
543def role_list(profile=None, **connection_args):
544 '''
545 Return a list of available roles (keystone role-list)
546
547 CLI Example:
548
549 .. code-block:: bash
550
551 salt '*' keystone.role_list
552 '''
553 kstone = auth(profile, **connection_args)
554 ret = {}
555 for role in kstone.roles.list():
556 ret[role.name] = dict((value, getattr(role, value)) for value in dir(role)
557 if not value.startswith('_') and
558 isinstance(getattr(role, value), (six.string_types, dict, bool)))
559 return ret
560
561
562def service_create(name, service_type, description=None, profile=None,
563 **connection_args):
564 '''
565 Add service to Keystone service catalog
566
567 CLI Examples:
568
569 .. code-block:: bash
570
571 salt '*' keystone.service_create nova compute \
572'OpenStack Compute Service'
573 '''
574 kstone = auth(profile, **connection_args)
575 service = kstone.services.create(name, service_type, description=description)
576 return service_get(service.id, profile=profile, **connection_args)
577
578
579def service_delete(service_id=None, name=None, profile=None, **connection_args):
580 '''
581 Delete a service from Keystone service catalog
582
583 CLI Examples:
584
585 .. code-block:: bash
586
587 salt '*' keystone.service_delete c965f79c4f864eaaa9c3b41904e67082
588 salt '*' keystone.service_delete name=nova
589 '''
590 kstone = auth(profile, **connection_args)
591 if name:
592 service_id = service_get(name=name, profile=profile,
593 **connection_args)[name]['id']
594 kstone.services.delete(service_id)
595 return 'Keystone service ID "{0}" deleted'.format(service_id)
596
597
598def service_get(service_id=None, name=None, profile=None, **connection_args):
599 '''
600 Return a specific services (keystone service-get)
601
602 CLI Examples:
603
604 .. code-block:: bash
605
606 salt '*' keystone.service_get c965f79c4f864eaaa9c3b41904e67082
607 salt '*' keystone.service_get service_id=c965f79c4f864eaaa9c3b41904e67082
608 salt '*' keystone.service_get name=nova
609 '''
610 kstone = auth(profile, **connection_args)
611 ret = {}
612 if name:
613 for service in kstone.services.list():
614 if service.name == name:
615 service_id = service.id
616 break
617 if not service_id:
618 return {'Error': 'Unable to resolve service id'}
619 service = kstone.services.get(service_id)
620 ret[service.name] = dict((value, getattr(service, value)) for value in dir(service)
621 if not value.startswith('_') and
622 isinstance(getattr(service, value), (six.string_types, dict, bool)))
623 return ret
624
625
626def service_list(profile=None, **connection_args):
627 '''
628 Return a list of available services (keystone services-list)
629
630 CLI Example:
631
632 .. code-block:: bash
633
634 salt '*' keystone.service_list
635 '''
636 kstone = auth(profile, **connection_args)
637 ret = {}
638 for service in kstone.services.list():
639 ret[service.name] = dict((value, getattr(service, value)) for value in dir(service)
640 if not value.startswith('_') and
641 isinstance(getattr(service, value), (six.string_types, dict, bool)))
642 return ret
643
644
645def tenant_create(name, description=None, enabled=True, profile=None,
646 **connection_args):
647 '''
648 Create a keystone tenant
649
650 CLI Examples:
651
652 .. code-block:: bash
653
654 salt '*' keystone.tenant_create nova description='nova tenant'
655 salt '*' keystone.tenant_create test enabled=False
656 '''
657 kstone = auth(profile, **connection_args)
658 new = _project_mgr(kstone).create(name, description, enabled)
659 return tenant_get(new.id, profile=profile, **connection_args)
660
661
662def project_create(name, domain, description=None, enabled=True, profile=None,
663 **connection_args):
664 '''
665 Create a keystone project.
666 Overrides keystone tenant_create form api V2. For keystone api V3.
667
668 .. versionadded:: 2016.11.0
669
670 name
671 The project name, which must be unique within the owning domain.
672
673 domain
674 The domain name.
675
676 description
677 The project description.
678
679 enabled
680 Enables or disables the project.
681
682 profile
683 Configuration profile - if configuration for multiple openstack accounts required.
684
685 CLI Examples:
686
687 .. code-block:: bash
688
689 salt '*' keystone.project_create nova default description='Nova Compute Project'
690 salt '*' keystone.project_create test default enabled=False
691 '''
692 kstone = auth(profile, **connection_args)
693 new = _project_mgr(kstone).create(name=name, domain=domain,
694 description=description, enabled=enabled)
695 return tenant_get(new.id, profile=profile, **connection_args)
696
697
698def tenant_delete(tenant_id=None, name=None, profile=None, **connection_args):
699 '''
700 Delete a tenant (keystone tenant-delete)
701
702 CLI Examples:
703
704 .. code-block:: bash
705
706 salt '*' keystone.tenant_delete c965f79c4f864eaaa9c3b41904e67082
707 salt '*' keystone.tenant_delete tenant_id=c965f79c4f864eaaa9c3b41904e67082
708 salt '*' keystone.tenant_delete name=demo
709 '''
710 kstone = auth(profile, **connection_args)
711 if name:
712 for tenant in _project_mgr(kstone).list():
713 if tenant.name == name:
714 tenant_id = tenant.id
715 break
716 if not tenant_id:
717 return {'Error': 'Unable to resolve tenant id'}
718 _project_mgr(kstone).delete(tenant_id)
719 ret = 'Tenant ID {0} deleted'.format(tenant_id)
720 if name:
721
722 ret += ' ({0})'.format(name)
723 return ret
724
725
726def project_delete(project_id=None, name=None, profile=None, **connection_args):
727 '''
728 Delete a project (keystone project-delete).
729 Overrides keystone tenant-delete form api V2. For keystone api V3 only.
730
731 .. versionadded:: 2016.11.0
732
733 project_id
734 The project id.
735
736 name
737 The project name.
738
739 profile
740 Configuration profile - if configuration for multiple openstack accounts required.
741
742 CLI Examples:
743
744 .. code-block:: bash
745
746 salt '*' keystone.project_delete c965f79c4f864eaaa9c3b41904e67082
747 salt '*' keystone.project_delete project_id=c965f79c4f864eaaa9c3b41904e67082
748 salt '*' keystone.project_delete name=demo
749 '''
750 kstone = auth(profile, **connection_args)
751
752 if _client_version(kstone) > 2:
753 return tenant_delete(tenant_id=project_id, name=name, profile=None, **connection_args)
754 else:
755 return False
756
757
758def tenant_get(tenant_id=None, name=None, profile=None,
759 **connection_args):
760 '''
761 Return a specific tenants (keystone tenant-get)
762
763 CLI Examples:
764
765 .. code-block:: bash
766
767 salt '*' keystone.tenant_get c965f79c4f864eaaa9c3b41904e67082
768 salt '*' keystone.tenant_get tenant_id=c965f79c4f864eaaa9c3b41904e67082
769 salt '*' keystone.tenant_get name=nova
770 '''
771 kstone = auth(profile, **connection_args)
772 ret = {}
773
774 if name:
775 for tenant in _project_mgr(kstone).list():
776 if tenant.name == name:
777 tenant_id = tenant.id
778 break
779 if not tenant_id:
780 return {'Error': 'Unable to resolve tenant id'}
781 tenant = _project_mgr(kstone).get(tenant_id)
782 ret[tenant.name] = dict((value, getattr(tenant, value)) for value in dir(tenant)
783 if not value.startswith('_') and
784 isinstance(getattr(tenant, value), (six.string_types, dict, bool)))
785 return ret
786
787
788def project_get(project_id=None, name=None, profile=None, **connection_args):
789 '''
790 Return a specific projects (keystone project-get)
791 Overrides keystone tenant-get form api V2.
792 For keystone api V3 only.
793
794 .. versionadded:: 2016.11.0
795
796 project_id
797 The project id.
798
799 name
800 The project name.
801
802 profile
803 Configuration profile - if configuration for multiple openstack accounts required.
804
805 CLI Examples:
806
807 .. code-block:: bash
808
809 salt '*' keystone.project_get c965f79c4f864eaaa9c3b41904e67082
810 salt '*' keystone.project_get project_id=c965f79c4f864eaaa9c3b41904e67082
811 salt '*' keystone.project_get name=nova
812 '''
813 kstone = auth(profile, **connection_args)
814
815 if _client_version(kstone) > 2:
816 return tenant_get(tenant_id=project_id, name=name, profile=None, **connection_args)
817 else:
818 return False
819
820
821def tenant_list(profile=None, **connection_args):
822 '''
823 Return a list of available tenants (keystone tenants-list)
824
825 CLI Example:
826
827 .. code-block:: bash
828
829 salt '*' keystone.tenant_list
830 '''
831 kstone = auth(profile, **connection_args)
832 ret = {}
833
834 for tenant in _project_mgr(kstone).list():
835 ret[tenant.name] = dict((value, getattr(tenant, value)) for value in dir(tenant)
836 if not value.startswith('_') and
837 isinstance(getattr(tenant, value), (six.string_types, dict, bool)))
838 return ret
839
840
841def project_list(profile=None, **connection_args):
842 '''
843 Return a list of available projects (keystone projects-list).
844 Overrides keystone tenants-list form api V2.
845 For keystone api V3 only.
846
847 .. versionadded:: 2016.11.0
848
849 profile
850 Configuration profile - if configuration for multiple openstack accounts required.
851
852 CLI Example:
853
854 .. code-block:: bash
855
856 salt '*' keystone.project_list
857 '''
858 kstone = auth(profile, **connection_args)
859
860 if _client_version(kstone) > 2:
861 return tenant_list(profile, **connection_args)
862 else:
863 return False
864
865
866def tenant_update(tenant_id=None, name=None, description=None,
867 enabled=None, profile=None, **connection_args):
868 '''
869 Update a tenant's information (keystone tenant-update)
870 The following fields may be updated: name, description, enabled.
871 Can only update name if targeting by ID
872
873 CLI Examples:
874
875 .. code-block:: bash
876
877 salt '*' keystone.tenant_update name=admin enabled=True
878 salt '*' keystone.tenant_update c965f79c4f864eaaa9c3b41904e67082 name=admin email=admin@domain.com
879 '''
880 kstone = auth(profile, **connection_args)
881
882 if not tenant_id:
883 for tenant in _project_mgr(kstone).list():
884 if tenant.name == name:
885 tenant_id = tenant.id
886 break
887 if not tenant_id:
888 return {'Error': 'Unable to resolve tenant id'}
889
890 tenant = _project_mgr(kstone).get(tenant_id)
891 if not name:
892 name = tenant.name
893 if not description:
894 description = tenant.description
895 if enabled is None:
896 enabled = tenant.enabled
897 updated = _project_mgr(kstone).update(tenant_id, name=name, description=description, enabled=enabled)
898
899 return dict((value, getattr(updated, value)) for value in dir(updated)
900 if not value.startswith('_') and
901 isinstance(getattr(updated, value), (six.string_types, dict, bool)))
902
903
904def project_update(project_id=None, name=None, description=None,
905 enabled=None, profile=None, **connection_args):
906 '''
907 Update a tenant's information (keystone project-update)
908 The following fields may be updated: name, description, enabled.
909 Can only update name if targeting by ID
910
911 Overrides keystone tenant_update form api V2.
912 For keystone api V3 only.
913
914 .. versionadded:: 2016.11.0
915
916 project_id
917 The project id.
918
919 name
920 The project name, which must be unique within the owning domain.
921
922 description
923 The project description.
924
925 enabled
926 Enables or disables the project.
927
928 profile
929 Configuration profile - if configuration for multiple openstack accounts required.
930
931 CLI Examples:
932
933 .. code-block:: bash
934
935 salt '*' keystone.project_update name=admin enabled=True
936 salt '*' keystone.project_update c965f79c4f864eaaa9c3b41904e67082 name=admin email=admin@domain.com
937 '''
938 kstone = auth(profile, **connection_args)
939
940 if _client_version(kstone) > 2:
941 return tenant_update(tenant_id=project_id, name=name, description=description,
942 enabled=enabled, profile=profile, **connection_args)
943 else:
944 return False
945
946
947def token_get(profile=None, **connection_args):
948 '''
949 Return the configured tokens (keystone token-get)
950
951 CLI Example:
952
953 .. code-block:: bash
954
955 salt '*' keystone.token_get c965f79c4f864eaaa9c3b41904e67082
956 '''
957 kstone = auth(profile, **connection_args)
958 token = kstone.service_catalog.get_token()
959 return {'id': token['id'],
960 'expires': token['expires'],
961 'user_id': token['user_id'],
962 'tenant_id': token['tenant_id']}
963
964
965def user_list(profile=None, **connection_args):
966 '''
967 Return a list of available users (keystone user-list)
968
969 CLI Example:
970
971 .. code-block:: bash
972
973 salt '*' keystone.user_list
974 '''
975 kstone = auth(profile, **connection_args)
976 ret = {}
977 for user in kstone.users.list():
978 ret[user.name] = dict((value, getattr(user, value, None)) for value in dir(user)
979 if not value.startswith('_') and
980 isinstance(getattr(user, value, None), (six.string_types, dict, bool)))
981 tenant_id = getattr(user, 'tenantId', None)
982 if tenant_id:
983 ret[user.name]['tenant_id'] = tenant_id
984 return ret
985
986
987def user_get(user_id=None, name=None, profile=None, **connection_args):
988 '''
989 Return a specific users (keystone user-get)
990
991 CLI Examples:
992
993 .. code-block:: bash
994
995 salt '*' keystone.user_get c965f79c4f864eaaa9c3b41904e67082
996 salt '*' keystone.user_get user_id=c965f79c4f864eaaa9c3b41904e67082
997 salt '*' keystone.user_get name=nova
998 '''
999 kstone = auth(profile, **connection_args)
1000 ret = {}
1001 if name:
1002 for user in kstone.users.list():
1003 if user.name == name:
1004 user_id = user.id
1005 break
1006 if not user_id:
1007 return {'Error': 'Unable to resolve user id'}
1008 try:
1009 user = kstone.users.get(user_id)
1010 except keystoneclient.exceptions.NotFound:
1011 msg = 'Could not find user \'{0}\''.format(user_id)
1012 log.error(msg)
1013 return {'Error': msg}
1014
1015 ret[user.name] = dict((value, getattr(user, value, None)) for value in dir(user)
1016 if not value.startswith('_') and
1017 isinstance(getattr(user, value, None), (six.string_types, dict, bool)))
1018
1019 tenant_id = getattr(user, 'tenantId', None)
1020 if tenant_id:
1021 ret[user.name]['tenant_id'] = tenant_id
1022 return ret
1023
1024
1025def user_create(name, password, email, tenant_id=None,
1026 enabled=True, profile=None, project_id=None, description=None, **connection_args):
1027 '''
1028 Create a user (keystone user-create)
1029
1030 CLI Examples:
1031
1032 .. code-block:: bash
1033
1034 salt '*' keystone.user_create name=jack password=zero email=jack@halloweentown.org \
1035 tenant_id=a28a7b5a999a455f84b1f5210264375e enabled=True
1036 '''
1037 kstone = auth(profile, **connection_args)
1038
1039 if _client_version(kstone) > 2:
1040 if tenant_id and not project_id:
1041 project_id = tenant_id
1042 item = kstone.users.create(name=name,
1043 password=password,
1044 email=email,
1045 project_id=project_id,
1046 enabled=enabled,
1047 description=description)
1048 else:
1049 item = kstone.users.create(name=name,
1050 password=password,
1051 email=email,
1052 tenant_id=tenant_id,
1053 enabled=enabled)
1054 return user_get(item.id, profile=profile, **connection_args)
1055
1056
1057def user_delete(user_id=None, name=None, profile=None, **connection_args):
1058 '''
1059 Delete a user (keystone user-delete)
1060
1061 CLI Examples:
1062
1063 .. code-block:: bash
1064
1065 salt '*' keystone.user_delete c965f79c4f864eaaa9c3b41904e67082
1066 salt '*' keystone.user_delete user_id=c965f79c4f864eaaa9c3b41904e67082
1067 salt '*' keystone.user_delete name=nova
1068 '''
1069 kstone = auth(profile, **connection_args)
1070 if name:
1071 for user in kstone.users.list():
1072 if user.name == name:
1073 user_id = user.id
1074 break
1075 if not user_id:
1076 return {'Error': 'Unable to resolve user id'}
1077 kstone.users.delete(user_id)
1078 ret = 'User ID {0} deleted'.format(user_id)
1079 if name:
1080
1081 ret += ' ({0})'.format(name)
1082 return ret
1083
1084
Oleksandr Shyshkoa0b79e22019-02-27 15:02:30 +00001085def user_update(user_id=None, name=None, email=None, enabled=None, options=None,
Oleg Iurchenko5b1e5322017-10-20 00:29:20 +03001086 tenant=None, profile=None, project=None, description=None, **connection_args):
1087 '''
1088 Update a user's information (keystone user-update)
Oleksandr Shyshkoa0b79e22019-02-27 15:02:30 +00001089 The following fields may be updated: name, email, enabled, tenant, options.
Oleg Iurchenko5b1e5322017-10-20 00:29:20 +03001090 Because the name is one of the fields, a valid user id is required.
1091
1092 CLI Examples:
1093
1094 .. code-block:: bash
1095
1096 salt '*' keystone.user_update user_id=c965f79c4f864eaaa9c3b41904e67082 name=newname
1097 salt '*' keystone.user_update c965f79c4f864eaaa9c3b41904e67082 name=newname email=newemail@domain.com
1098 '''
1099 kstone = auth(profile, **connection_args)
1100 if not user_id:
1101 for user in kstone.users.list():
1102 if user.name == name:
1103 user_id = user.id
1104 break
1105 if not user_id:
1106 return {'Error': 'Unable to resolve user id'}
1107 user = kstone.users.get(user_id)
1108 # Keep previous settings if not updating them
1109 if not name:
1110 name = user.name
1111 if not email:
1112 email = user.email
1113 if enabled is None:
1114 enabled = user.enabled
Oleksandr Shyshkoa0b79e22019-02-27 15:02:30 +00001115 if not options:
1116 options = {}
Oleg Iurchenko5b1e5322017-10-20 00:29:20 +03001117
1118 if _client_version(kstone) > 2:
1119 if description is None:
1120 description = getattr(user, 'description', None)
1121 else:
1122 description = str(description)
1123
1124 project_id = None
1125 if project:
1126 for proj in kstone.projects.list():
1127 if proj.name == project:
1128 project_id = proj.id
1129 break
1130 if not project_id:
1131 project_id = getattr(user, 'project_id', None)
1132
1133 kstone.users.update(user=user_id, name=name, email=email, enabled=enabled, description=description,
Oleksandr Shyshkoa0b79e22019-02-27 15:02:30 +00001134 options=options, project_id=project_id)
Oleg Iurchenko5b1e5322017-10-20 00:29:20 +03001135 else:
Oleksandr Shyshkoa0b79e22019-02-27 15:02:30 +00001136 kstone.users.update(user=user_id, name=name, email=email, options=options, enabled=enabled)
Oleg Iurchenko5b1e5322017-10-20 00:29:20 +03001137
1138 tenant_id = None
1139 if tenant:
1140 for tnt in kstone.tenants.list():
1141 if tnt.name == tenant:
1142 tenant_id = tnt.id
1143 break
1144 if tenant_id:
1145 kstone.users.update_tenant(user_id, tenant_id)
1146
1147 ret = 'Info updated for user ID {0}'.format(user_id)
1148 return ret
1149
1150
1151def user_verify_password(user_id=None, name=None, password=None,
1152 profile=None, **connection_args):
1153 '''
1154 Verify a user's password
1155
1156 CLI Examples:
1157
1158 .. code-block:: bash
1159
1160 salt '*' keystone.user_verify_password name=test password=foobar
1161 salt '*' keystone.user_verify_password user_id=c965f79c4f864eaaa9c3b41904e67082 password=foobar
1162 '''
1163 kstone = auth(profile, **connection_args)
1164 if 'connection_endpoint' in connection_args:
1165 auth_url = connection_args.get('connection_endpoint')
1166 else:
1167 if _client_version(kstone) > 2:
1168 auth_url = __salt__['config.option']('keystone.endpoint',
1169 'http://127.0.0.1:35357/v3')
1170 else:
1171 auth_url = __salt__['config.option']('keystone.endpoint',
1172 'http://127.0.0.1:35357/v2.0')
1173
1174 if user_id:
1175 for user in kstone.users.list():
1176 if user.id == user_id:
1177 name = user.name
1178 break
1179 if not name:
1180 return {'Error': 'Unable to resolve user name'}
1181 kwargs = {'username': name,
1182 'password': password,
1183 'auth_url': auth_url}
1184 try:
1185 if _client_version(kstone) > 2:
Vasyl Saienko50719e92019-01-11 15:18:08 +00001186 client3.Client(**kwargs).authenticate()
Oleg Iurchenko5b1e5322017-10-20 00:29:20 +03001187 else:
Vasyl Saienko50719e92019-01-11 15:18:08 +00001188 client.Client(**kwargs).authenticate()
Oleg Iurchenko5b1e5322017-10-20 00:29:20 +03001189 except (keystoneclient.exceptions.Unauthorized,
1190 keystoneclient.exceptions.AuthorizationFailure):
1191 return False
1192 return True
1193
1194
1195def user_password_update(user_id=None, name=None, password=None,
1196 profile=None, **connection_args):
1197 '''
1198 Update a user's password (keystone user-password-update)
1199
1200 CLI Examples:
1201
1202 .. code-block:: bash
1203
1204 salt '*' keystone.user_password_update c965f79c4f864eaaa9c3b41904e67082 password=12345
1205 salt '*' keystone.user_password_update user_id=c965f79c4f864eaaa9c3b41904e67082 password=12345
1206 salt '*' keystone.user_password_update name=nova password=12345
1207 '''
1208 kstone = auth(profile, **connection_args)
1209 if name:
1210 for user in kstone.users.list():
1211 if user.name == name:
1212 user_id = user.id
1213 break
1214 if not user_id:
1215 return {'Error': 'Unable to resolve user id'}
1216
1217 if _client_version(kstone) > 2:
1218 kstone.users.update(user=user_id, password=password)
1219 else:
1220 kstone.users.update_password(user=user_id, password=password)
1221 ret = 'Password updated for user ID {0}'.format(user_id)
1222 if name:
1223 ret += ' ({0})'.format(name)
1224 return ret
1225
1226
1227def user_role_add(user_id=None, user=None, tenant_id=None,
1228 tenant=None, role_id=None, role=None, profile=None,
1229 project_id=None, project_name=None, **connection_args):
1230 '''
1231 Add role for user in tenant (keystone user-role-add)
1232
1233 CLI Examples:
1234
1235 .. code-block:: bash
1236
1237 salt '*' keystone.user_role_add \
1238user_id=298ce377245c4ec9b70e1c639c89e654 \
1239tenant_id=7167a092ece84bae8cead4bf9d15bb3b \
1240role_id=ce377245c4ec9b70e1c639c89e8cead4
1241 salt '*' keystone.user_role_add user=admin tenant=admin role=admin
1242 '''
1243 kstone = auth(profile, **connection_args)
1244
1245 if project_id and not tenant_id:
1246 tenant_id = project_id
1247 elif project_name and not tenant:
1248 tenant = project_name
1249
1250 if user:
1251 user_id = user_get(name=user, profile=profile,
1252 **connection_args)[user].get('id')
1253 else:
1254 user = next(six.iterkeys(user_get(user_id, profile=profile,
1255 **connection_args)))['name']
1256 if not user_id:
1257 return {'Error': 'Unable to resolve user id'}
1258
1259 if tenant:
1260 tenant_id = tenant_get(name=tenant, profile=profile,
1261 **connection_args)[tenant].get('id')
1262 else:
1263 tenant = next(six.iterkeys(tenant_get(tenant_id, profile=profile,
1264 **connection_args)))['name']
1265 if not tenant_id:
1266 return {'Error': 'Unable to resolve tenant/project id'}
1267
1268 if role:
1269 role_id = role_get(name=role, profile=profile,
1270 **connection_args)[role]['id']
1271 else:
1272 role = next(six.iterkeys(role_get(role_id, profile=profile,
1273 **connection_args)))['name']
1274 if not role_id:
1275 return {'Error': 'Unable to resolve role id'}
1276
1277 if _client_version(kstone) > 2:
1278 kstone.roles.grant(role_id, user=user_id, project=tenant_id)
1279 else:
1280 kstone.roles.add_user_role(user_id, role_id, tenant_id)
1281 ret_msg = '"{0}" role added for user "{1}" for "{2}" tenant/project'
1282 return ret_msg.format(role, user, tenant)
1283
1284
1285def user_role_remove(user_id=None, user=None, tenant_id=None,
1286 tenant=None, role_id=None, role=None,
1287 profile=None, project_id=None, project_name=None, **connection_args):
1288 '''
1289 Remove role for user in tenant (keystone user-role-remove)
1290
1291 CLI Examples:
1292
1293 .. code-block:: bash
1294
1295 salt '*' keystone.user_role_remove \
1296user_id=298ce377245c4ec9b70e1c639c89e654 \
1297tenant_id=7167a092ece84bae8cead4bf9d15bb3b \
1298role_id=ce377245c4ec9b70e1c639c89e8cead4
1299 salt '*' keystone.user_role_remove user=admin tenant=admin role=admin
1300 '''
1301 kstone = auth(profile, **connection_args)
1302
1303 if project_id and not tenant_id:
1304 tenant_id = project_id
1305 elif project_name and not tenant:
1306 tenant = project_name
1307
1308 if user:
1309 user_id = user_get(name=user, profile=profile,
1310 **connection_args)[user].get('id')
1311 else:
1312 user = next(six.iterkeys(user_get(user_id, profile=profile,
1313 **connection_args)))['name']
1314 if not user_id:
1315 return {'Error': 'Unable to resolve user id'}
1316
1317 if tenant:
1318 tenant_id = tenant_get(name=tenant, profile=profile,
1319 **connection_args)[tenant].get('id')
1320 else:
1321 tenant = next(six.iterkeys(tenant_get(tenant_id, profile=profile,
1322 **connection_args)))['name']
1323 if not tenant_id:
1324 return {'Error': 'Unable to resolve tenant/project id'}
1325
1326 if role:
1327 role_id = role_get(name=role, profile=profile,
1328 **connection_args)[role]['id']
1329 else:
1330 role = next(six.iterkeys(role_get(role_id)))['name']
1331 if not role_id:
1332 return {'Error': 'Unable to resolve role id'}
1333
1334 if _client_version(kstone) > 2:
1335 kstone.roles.revoke(role_id, user=user_id, project=tenant_id)
1336 else:
1337 kstone.roles.remove_user_role(user_id, role_id, tenant_id)
1338 ret_msg = '"{0}" role removed for user "{1}" under "{2}" tenant'
1339 return ret_msg.format(role, user, tenant)
1340
1341
1342def user_role_list(user_id=None, tenant_id=None, user_name=None,
1343 tenant_name=None, profile=None, project_id=None, project_name=None, **connection_args):
1344 '''
1345 Return a list of available user_roles (keystone user-roles-list)
1346
1347 CLI Examples:
1348
1349 .. code-block:: bash
1350
1351 salt '*' keystone.user_role_list \
1352user_id=298ce377245c4ec9b70e1c639c89e654 \
1353tenant_id=7167a092ece84bae8cead4bf9d15bb3b
1354 salt '*' keystone.user_role_list user_name=admin tenant_name=admin
1355 '''
1356 kstone = auth(profile, **connection_args)
1357 ret = {}
1358
1359 if project_id and not tenant_id:
1360 tenant_id = project_id
1361 elif project_name and not tenant_name:
1362 tenant_name = project_name
1363
1364 if user_name:
1365 for user in kstone.users.list():
1366 if user.name == user_name:
1367 user_id = user.id
1368 break
1369 if tenant_name:
1370 for tenant in _project_mgr(kstone).list():
1371 if tenant.name == tenant_name:
1372 tenant_id = tenant.id
1373 break
1374 if not user_id or not tenant_id:
1375 return {'Error': 'Unable to resolve user or tenant/project id'}
1376
1377 if _client_version(kstone) > 2:
1378 for role in kstone.roles.list(user=user_id, project=tenant_id):
1379 ret[role.name] = dict((value, getattr(role, value)) for value in dir(role)
1380 if not value.startswith('_') and
1381 isinstance(getattr(role, value), (six.string_types, dict, bool)))
1382 else:
1383 for role in kstone.roles.roles_for_user(user=user_id, tenant=tenant_id):
1384 ret[role.name] = {'id': role.id,
1385 'name': role.name,
1386 'user_id': user_id,
1387 'tenant_id': tenant_id}
1388 return ret
1389
1390
1391def _item_list(profile=None, **connection_args):
1392 '''
1393 Template for writing list functions
1394 Return a list of available items (keystone items-list)
1395
1396 CLI Example:
1397
1398 .. code-block:: bash
1399
1400 salt '*' keystone.item_list
1401 '''
1402 kstone = auth(profile, **connection_args)
1403 ret = []
1404 for item in kstone.items.list():
1405 ret.append(item.__dict__)
1406 # ret[item.name] = {
1407 # 'id': item.id,
1408 # 'name': item.name,
1409 # }
1410 return ret
1411
1412 # The following is a list of functions that need to be incorporated in the
1413 # keystone module. This list should be updated as functions are added.
1414 #
1415 # endpoint-create Create a new endpoint associated with a service
1416 # endpoint-delete Delete a service endpoint
1417 # discover Discover Keystone servers and show authentication
1418 # protocols and
1419 # bootstrap Grants a new role to a new user on a new tenant, after
1420 # creating each.