1 # Copyright (c) 2016 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
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, interface=interface)
107 def get_project(keystone=None, os_creds=None, project_name=None):
109 Returns the first project object or None if not found
110 :param keystone: the Keystone client
111 :param os_creds: the OpenStack credentials used to obtain the Keystone
112 client if the keystone parameter is None
113 :param project_name: the name to query
114 :return: the SNAPS-OO Project domain object or None
121 keystone = keystone_client(os_creds)
123 raise KeystoneException(
124 'Cannot lookup project without the proper credentials')
126 if keystone.version == V2_VERSION_STR:
127 projects = keystone.tenants.list()
129 projects = keystone.projects.list(**{'name': project_name})
131 for project in projects:
133 if keystone.version != V2_VERSION_STR:
134 domain_id = project.domain_id
135 if project.name == project_name:
136 return Project(name=project.name, project_id=project.id,
142 def create_project(keystone, project_settings):
145 :param keystone: the Keystone client
146 :param project_settings: the project configuration
147 :return: SNAPS-OO Project domain object
151 if keystone.version == V2_VERSION_STR:
152 os_project = keystone.tenants.create(
153 project_settings.name, project_settings.description,
154 project_settings.enabled)
156 os_project = keystone.projects.create(
157 project_settings.name, project_settings.domain,
158 description=project_settings.description,
159 enabled=project_settings.enabled)
160 domain_id = os_project.domain_id
163 name=os_project.name, project_id=os_project.id, domain_id=domain_id)
166 def delete_project(keystone, project):
169 :param keystone: the Keystone clien
170 :param project: the SNAPS-OO Project domain object
172 if keystone.version == V2_VERSION_STR:
173 keystone.tenants.delete(project.id)
175 keystone.projects.delete(project.id)
178 def __get_os_user(keystone, user):
180 Returns the OpenStack user object
181 :param keystone: the Keystone client object
182 :param user: the SNAPS-OO User domain object
185 return keystone.users.get(user.id)
188 def get_user(keystone, username, project_name=None):
190 Returns a user for a given name and optionally project
191 :param keystone: the keystone client
192 :param username: the username to lookup
193 :param project_name: the associated project (optional)
194 :return: a SNAPS-OO User domain object or None
196 project = get_project(keystone=keystone, project_name=project_name)
199 users = keystone.users.list(tenant_id=project.id)
201 users = keystone.users.list()
204 if user.name == username:
205 return User(name=user.name, user_id=user.id)
210 def create_user(keystone, user_settings):
213 :param keystone: the Keystone client
214 :param user_settings: the user configuration
215 :return: a SNAPS-OO User domain object
218 if user_settings.project_name:
219 project = get_project(keystone=keystone,
220 project_name=user_settings.project_name)
222 if keystone.version == V2_VERSION_STR:
225 project_id = project.id
226 os_user = keystone.users.create(
227 name=user_settings.name, password=user_settings.password,
228 email=user_settings.email, tenant_id=project_id,
229 enabled=user_settings.enabled)
231 os_user = keystone.users.create(
232 name=user_settings.name, password=user_settings.password,
233 email=user_settings.email, project=project,
234 domain=user_settings.domain_name, enabled=user_settings.enabled)
236 for role_name, role_project in user_settings.roles.items():
237 os_role = get_role_by_name(keystone, role_name)
238 os_project = get_project(keystone=keystone, project_name=role_project)
240 if os_role and os_project:
241 existing_roles = get_roles_by_user(keystone, os_user,
244 for role in existing_roles:
245 if role.id == os_role.id:
249 grant_user_role_to_project(
250 keystone=keystone, user=os_user, role=os_role,
254 return User(name=os_user.name, user_id=os_user.id)
257 def delete_user(keystone, user):
260 :param keystone: the Keystone client
261 :param user: the SNAPS-OO User domain object
263 keystone.users.delete(user.id)
266 def get_role_by_name(keystone, name):
268 Returns an OpenStack role object of a given name or None if not exists
269 :param keystone: the keystone client
270 :param name: the role name
271 :return: the SNAPS-OO Role domain object
273 roles = keystone.roles.list()
275 if role.name == name:
276 return Role(name=role.name, role_id=role.id)
279 def get_roles_by_user(keystone, user, project):
281 Returns a list of SNAPS-OO Role domain objects associated with a user
282 :param keystone: the keystone client
283 :param user: the OpenStack user object
284 :param project: the OpenStack project object (only required for v2)
285 :return: a list of SNAPS-OO Role domain objects
287 if keystone.version == V2_VERSION_STR:
288 os_user = __get_os_user(keystone, user)
289 roles = keystone.roles.roles_for_user(os_user, project)
291 roles = keystone.roles.list(user=user, project=project)
295 out.append(Role(name=role.name, role_id=role.id))
299 def get_role_by_id(keystone, role_id):
301 Returns an OpenStack role object of a given name or None if not exists
302 :param keystone: the keystone client
303 :param role_id: the role ID
304 :return: a SNAPS-OO Role domain object
306 role = keystone.roles.get(role_id)
307 return Role(name=role.name, role_id=role.id)
310 def create_role(keystone, name):
312 Creates an OpenStack role
313 :param keystone: the keystone client
314 :param name: the role name
315 :return: a SNAPS-OO Role domain object
317 role = keystone.roles.create(name)
318 return Role(name=role.name, role_id=role.id)
321 def delete_role(keystone, role):
323 Deletes an OpenStack role
324 :param keystone: the keystone client
325 :param role: the SNAPS-OO Role domain object to delete
328 keystone.roles.delete(role.id)
331 def grant_user_role_to_project(keystone, role, user, project):
333 Grants user and role to a project
334 :param keystone: the Keystone client
335 :param role: the SNAPS-OO Role domain object used to join a project/user
336 :param user: the user to add to the project (SNAPS-OO User Domain object
337 :param project: the project to which to add a user
341 os_role = get_role_by_id(keystone, role.id)
342 if keystone.version == V2_VERSION_STR:
343 keystone.roles.add_user_role(user, os_role, tenant=project)
345 keystone.roles.grant(os_role, user=user, project=project)
348 class KeystoneException(Exception):
350 Exception when calls to the Keystone client cannot be served properly