1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 # and others. All rights reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 from keystoneclient.client import Client
18 from keystoneauth1.identity import v3, v2
19 from keystoneauth1 import session
22 from snaps.domain.project import Project, Domain
23 from snaps.domain.role import Role
24 from snaps.domain.user import User
26 logger = logging.getLogger('keystone_utils')
29 V2_VERSION_STR = 'v' + str(V2_VERSION_NUM)
32 def get_session_auth(os_creds):
34 Return the session auth for keystone session
35 :param os_creds: the OpenStack credentials (OSCreds) object
38 if os_creds.identity_api_version == 3:
39 auth = v3.Password(auth_url=os_creds.auth_url,
40 username=os_creds.username,
41 password=os_creds.password,
42 project_name=os_creds.project_name,
43 user_domain_id=os_creds.user_domain_id,
44 user_domain_name=os_creds.user_domain_name,
45 project_domain_id=os_creds.project_domain_id,
46 project_domain_name=os_creds.project_domain_name)
48 auth = v2.Password(auth_url=os_creds.auth_url,
49 username=os_creds.username,
50 password=os_creds.password,
51 tenant_name=os_creds.project_name)
55 def keystone_session(os_creds):
57 Creates a keystone session used for authenticating OpenStack clients
58 :param os_creds: The connection credentials to the OpenStack API
59 :return: the client object
61 logger.debug('Retrieving Keystone Session')
63 auth = get_session_auth(os_creds)
66 if os_creds.proxy_settings:
67 req_session = requests.Session()
68 req_session.proxies = {
70 os_creds.proxy_settings.host + ':' +
71 os_creds.proxy_settings.port,
73 os_creds.proxy_settings.https_host + ':' +
74 os_creds.proxy_settings.https_port
76 return session.Session(auth=auth, session=req_session,
77 verify=os_creds.cacert)
80 def keystone_client(os_creds):
82 Returns the keystone client
83 :param os_creds: the OpenStack credentials (OSCreds) object
87 version=os_creds.identity_api_version,
88 session=keystone_session(os_creds),
89 interface=os_creds.interface,
90 region_name=os_creds.region_name)
93 def get_endpoint(os_creds, service_type, interface='public'):
95 Returns the endpoint of specific service
96 :param os_creds: the OpenStack credentials (OSCreds) object
97 :param service_type: the type of specific service
98 :param interface: the type of interface
99 :return: the endpoint url
101 auth = get_session_auth(os_creds)
102 key_session = keystone_session(os_creds)
103 return key_session.get_endpoint(
104 auth=auth, service_type=service_type, region_name=os_creds.region_name,
108 def get_project(keystone=None, os_creds=None, project_settings=None,
111 Returns the first project where the project_settings is used for the query
112 if not None, else the project_name parameter is used for the query. If both
113 parameters are None, None is returned
114 :param keystone: the Keystone client
115 :param os_creds: the OpenStack credentials used to obtain the Keystone
116 client if the keystone parameter is None
117 :param project_settings: a ProjectSettings object
118 :param project_name: the name to query
119 :return: the SNAPS-OO Project domain object or None
123 keystone = keystone_client(os_creds)
125 raise KeystoneException(
126 'Cannot lookup project without the proper credentials')
131 proj_filter['name'] = project_name
132 elif project_settings:
133 proj_filter['name'] = project_settings.name
134 proj_filter['description'] = project_settings.description
135 proj_filter['domain_name'] = project_settings.domain_name
136 proj_filter['enabled'] = project_settings.enabled
140 if keystone.version == V2_VERSION_STR:
141 projects = keystone.tenants.list()
143 projects = keystone.projects.list(**proj_filter)
145 for project in projects:
146 if project.name == proj_filter['name']:
148 if keystone.version != V2_VERSION_STR:
149 domain_id = project.domain_id
151 return Project(name=project.name, project_id=project.id,
155 def create_project(keystone, project_settings):
158 :param keystone: the Keystone client
159 :param project_settings: the project configuration
160 :return: SNAPS-OO Project domain object
164 if keystone.version == V2_VERSION_STR:
165 os_project = keystone.tenants.create(
166 project_settings.name, project_settings.description,
167 project_settings.enabled)
169 os_domain = __get_os_domain_by_name(
170 keystone, project_settings.domain_name)
172 os_domain = project_settings.domain_name
173 os_project = keystone.projects.create(
174 project_settings.name, os_domain,
175 description=project_settings.description,
176 enabled=project_settings.enabled)
177 domain_id = os_project.domain_id
180 name=os_project.name, project_id=os_project.id, domain_id=domain_id)
183 def delete_project(keystone, project):
186 :param keystone: the Keystone clien
187 :param project: the SNAPS-OO Project domain object
189 if keystone.version == V2_VERSION_STR:
190 keystone.tenants.delete(project.id)
192 keystone.projects.delete(project.id)
195 def __get_os_user(keystone, user):
197 Returns the OpenStack user object
198 :param keystone: the Keystone client object
199 :param user: the SNAPS-OO User domain object
202 return keystone.users.get(user.id)
205 def get_user(keystone, username, project_name=None):
207 Returns a user for a given name and optionally project
208 :param keystone: the keystone client
209 :param username: the username to lookup
210 :param project_name: the associated project (optional)
211 :return: a SNAPS-OO User domain object or None
215 project = get_project(keystone=keystone, project_name=project_name)
218 users = keystone.users.list(tenant_id=project.id)
220 users = keystone.users.list()
223 if user.name == username:
224 return User(name=user.name, user_id=user.id)
229 def create_user(keystone, user_settings):
232 :param keystone: the Keystone client
233 :param user_settings: the user configuration
234 :return: a SNAPS-OO User domain object
237 if user_settings.project_name:
238 project = get_project(keystone=keystone,
239 project_name=user_settings.project_name)
241 if keystone.version == V2_VERSION_STR:
244 project_id = project.id
245 os_user = keystone.users.create(
246 name=user_settings.name, password=user_settings.password,
247 email=user_settings.email, tenant_id=project_id,
248 enabled=user_settings.enabled)
250 os_domain = __get_os_domain_by_name(
251 keystone, user_settings.domain_name)
253 os_domain = user_settings.domain_name
254 os_user = keystone.users.create(
255 name=user_settings.name, password=user_settings.password,
256 email=user_settings.email, project=project,
257 domain=os_domain, enabled=user_settings.enabled)
259 for role_name, role_project in user_settings.roles.items():
260 os_role = get_role_by_name(keystone, role_name)
261 os_project = get_project(keystone=keystone, project_name=role_project)
263 if os_role and os_project:
264 existing_roles = get_roles_by_user(keystone, os_user, os_project)
266 for role in existing_roles:
267 if role.id == os_role.id:
271 grant_user_role_to_project(
272 keystone=keystone, user=os_user, role=os_role,
276 return User(name=os_user.name, user_id=os_user.id)
279 def delete_user(keystone, user):
282 :param keystone: the Keystone client
283 :param user: the SNAPS-OO User domain object
285 keystone.users.delete(user.id)
288 def get_role_by_name(keystone, name):
290 Returns an OpenStack role object of a given name or None if not exists
291 :param keystone: the keystone client
292 :param name: the role name
293 :return: the SNAPS-OO Role domain object
295 roles = keystone.roles.list()
297 if role.name == name:
298 return Role(name=role.name, role_id=role.id)
301 def get_roles_by_user(keystone, user, project):
303 Returns a list of SNAPS-OO Role domain objects associated with a user
304 :param keystone: the keystone client
305 :param user: the OpenStack user object
306 :param project: the OpenStack project object (only required for v2)
307 :return: a list of SNAPS-OO Role domain objects
309 if keystone.version == V2_VERSION_STR:
310 os_user = __get_os_user(keystone, user)
311 roles = keystone.roles.roles_for_user(os_user, project)
313 roles = keystone.roles.list(user=user, project=project)
317 out.append(Role(name=role.name, role_id=role.id))
321 def get_role_by_id(keystone, role_id):
323 Returns an OpenStack role object of a given name or None if not exists
324 :param keystone: the keystone client
325 :param role_id: the role ID
326 :return: a SNAPS-OO Role domain object
328 role = keystone.roles.get(role_id)
329 return Role(name=role.name, role_id=role.id)
332 def create_role(keystone, name):
334 Creates an OpenStack role
335 :param keystone: the keystone client
336 :param name: the role name
337 :return: a SNAPS-OO Role domain object
339 role = keystone.roles.create(name)
340 return Role(name=role.name, role_id=role.id)
343 def delete_role(keystone, role):
345 Deletes an OpenStack role
346 :param keystone: the keystone client
347 :param role: the SNAPS-OO Role domain object to delete
350 keystone.roles.delete(role.id)
353 def grant_user_role_to_project(keystone, role, user, project):
355 Grants user and role to a project
356 :param keystone: the Keystone client
357 :param role: the SNAPS-OO Role domain object used to join a project/user
358 :param user: the user to add to the project (SNAPS-OO User Domain object
359 :param project: the project to which to add a user
363 os_role = get_role_by_id(keystone, role.id)
364 if keystone.version == V2_VERSION_STR:
365 keystone.roles.add_user_role(user, os_role, tenant=project)
367 keystone.roles.grant(os_role, user=user, project=project)
370 def get_domain_by_id(keystone, domain_id):
372 Returns the first OpenStack domain with the given name else None
373 :param keystone: the Keystone client
374 :param domain_id: the domain ID to retrieve
375 :return: the SNAPS-OO Domain domain object
377 if keystone.version != V2_VERSION_STR:
378 domain = keystone.domains.get(domain_id)
380 return Domain(name=domain.name, domain_id=domain.id)
383 def __get_os_domain_by_name(keystone, domain_name):
385 Returns the first OpenStack domain with the given name else None
386 :param keystone: the Keystone client
387 :param domain_name: the domain name to lookup
388 :return: the OpenStack domain object
390 domains = keystone.domains.list(name=domain_name)
391 for domain in domains:
392 if domain.name == domain_name:
396 class KeystoneException(Exception):
398 Exception when calls to the Keystone client cannot be served properly