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 project_domain_id=os_creds.project_domain_id)
46 auth = v2.Password(auth_url=os_creds.auth_url,
47 username=os_creds.username,
48 password=os_creds.password,
49 tenant_name=os_creds.project_name)
53 def keystone_session(os_creds):
55 Creates a keystone session used for authenticating OpenStack clients
56 :param os_creds: The connection credentials to the OpenStack API
57 :return: the client object
59 logger.debug('Retrieving Keystone Session')
61 auth = get_session_auth(os_creds)
64 if os_creds.proxy_settings:
65 req_session = requests.Session()
66 req_session.proxies = {
68 os_creds.proxy_settings.host + ':' +
69 os_creds.proxy_settings.port,
71 os_creds.proxy_settings.https_host + ':' +
72 os_creds.proxy_settings.https_port
74 return session.Session(auth=auth, session=req_session,
75 verify=os_creds.cacert)
78 def keystone_client(os_creds):
80 Returns the keystone client
81 :param os_creds: the OpenStack credentials (OSCreds) object
85 version=os_creds.identity_api_version,
86 session=keystone_session(os_creds),
87 interface=os_creds.interface,
88 region_name=os_creds.region_name)
91 def get_endpoint(os_creds, service_type, interface='public'):
93 Returns the endpoint of specific service
94 :param os_creds: the OpenStack credentials (OSCreds) object
95 :param service_type: the type of specific service
96 :param interface: the type of interface
97 :return: the endpoint url
99 auth = get_session_auth(os_creds)
100 key_session = keystone_session(os_creds)
101 return key_session.get_endpoint(
102 auth=auth, service_type=service_type, interface=interface)
105 def get_project(keystone=None, os_creds=None, project_name=None):
107 Returns the first project object or None if not found
108 :param keystone: the Keystone client
109 :param os_creds: the OpenStack credentials used to obtain the Keystone
110 client if the keystone parameter is None
111 :param project_name: the name to query
112 :return: the SNAPS-OO Project domain object or None
119 keystone = keystone_client(os_creds)
121 raise KeystoneException(
122 'Cannot lookup project without the proper credentials')
124 if keystone.version == V2_VERSION_STR:
125 projects = keystone.tenants.list()
127 projects = keystone.projects.list(**{'name': project_name})
129 for project in projects:
130 if project.name == project_name:
131 return Project(name=project.name, project_id=project.id)
136 def create_project(keystone, project_settings):
139 :param keystone: the Keystone client
140 :param project_settings: the project configuration
141 :return: SNAPS-OO Project domain object
143 if keystone.version == V2_VERSION_STR:
144 os_project = keystone.tenants.create(
145 project_settings.name, project_settings.description,
146 project_settings.enabled)
148 os_project = keystone.projects.create(
149 project_settings.name, project_settings.domain,
150 description=project_settings.description,
151 enabled=project_settings.enabled)
153 return Project(name=os_project.name, project_id=os_project.id)
156 def delete_project(keystone, project):
159 :param keystone: the Keystone clien
160 :param project: the SNAPS-OO Project domain object
162 if keystone.version == V2_VERSION_STR:
163 keystone.tenants.delete(project.id)
165 keystone.projects.delete(project.id)
168 def __get_os_user(keystone, user):
170 Returns the OpenStack user object
171 :param keystone: the Keystone client object
172 :param user: the SNAPS-OO User domain object
175 return keystone.users.get(user.id)
178 def get_user(keystone, username, project_name=None):
180 Returns a user for a given name and optionally project
181 :param keystone: the keystone client
182 :param username: the username to lookup
183 :param project_name: the associated project (optional)
184 :return: a SNAPS-OO User domain object or None
186 project = get_project(keystone=keystone, project_name=project_name)
189 users = keystone.users.list(tenant_id=project.id)
191 users = keystone.users.list()
194 if user.name == username:
195 return User(name=user.name, user_id=user.id)
200 def create_user(keystone, user_settings):
203 :param keystone: the Keystone client
204 :param user_settings: the user configuration
205 :return: a SNAPS-OO User domain object
208 if user_settings.project_name:
209 project = get_project(keystone=keystone,
210 project_name=user_settings.project_name)
212 if keystone.version == V2_VERSION_STR:
215 project_id = project.id
216 os_user = keystone.users.create(
217 name=user_settings.name, password=user_settings.password,
218 email=user_settings.email, tenant_id=project_id,
219 enabled=user_settings.enabled)
221 os_user = keystone.users.create(
222 name=user_settings.name, password=user_settings.password,
223 email=user_settings.email, project=project,
224 domain=user_settings.domain_name, enabled=user_settings.enabled)
226 for role_name, role_project in user_settings.roles.items():
227 os_role = get_role_by_name(keystone, role_name)
228 os_project = get_project(keystone=keystone, project_name=role_project)
230 if os_role and os_project:
231 existing_roles = get_roles_by_user(keystone, os_user,
234 for role in existing_roles:
235 if role.id == os_role.id:
239 grant_user_role_to_project(
240 keystone=keystone, user=os_user, role=os_role,
244 return User(name=os_user.name, user_id=os_user.id)
247 def delete_user(keystone, user):
250 :param keystone: the Keystone client
251 :param user: the SNAPS-OO User domain object
253 keystone.users.delete(user.id)
256 def get_role_by_name(keystone, name):
258 Returns an OpenStack role object of a given name or None if not exists
259 :param keystone: the keystone client
260 :param name: the role name
261 :return: the SNAPS-OO Role domain object
263 roles = keystone.roles.list()
265 if role.name == name:
266 return Role(name=role.name, role_id=role.id)
269 def get_roles_by_user(keystone, user, project):
271 Returns a list of SNAPS-OO Role domain objects associated with a user
272 :param keystone: the keystone client
273 :param user: the OpenStack user object
274 :param project: the OpenStack project object (only required for v2)
275 :return: a list of SNAPS-OO Role domain objects
277 if keystone.version == V2_VERSION_STR:
278 os_user = __get_os_user(keystone, user)
279 roles = keystone.roles.roles_for_user(os_user, project)
281 roles = keystone.roles.list(user=user, project=project)
285 out.append(Role(name=role.name, role_id=role.id))
289 def get_role_by_id(keystone, role_id):
291 Returns an OpenStack role object of a given name or None if not exists
292 :param keystone: the keystone client
293 :param role_id: the role ID
294 :return: a SNAPS-OO Role domain object
296 role = keystone.roles.get(role_id)
297 return Role(name=role.name, role_id=role.id)
300 def create_role(keystone, name):
302 Creates an OpenStack role
303 :param keystone: the keystone client
304 :param name: the role name
305 :return: a SNAPS-OO Role domain object
307 role = keystone.roles.create(name)
308 return Role(name=role.name, role_id=role.id)
311 def delete_role(keystone, role):
313 Deletes an OpenStack role
314 :param keystone: the keystone client
315 :param role: the SNAPS-OO Role domain object to delete
318 keystone.roles.delete(role.id)
321 def grant_user_role_to_project(keystone, role, user, project):
323 Grants user and role to a project
324 :param keystone: the Keystone client
325 :param role: the SNAPS-OO Role domain object used to join a project/user
326 :param user: the user to add to the project (SNAPS-OO User Domain object
327 :param project: the project to which to add a user
331 os_role = get_role_by_id(keystone, role.id)
332 if keystone.version == V2_VERSION_STR:
333 keystone.roles.add_user_role(user, os_role, tenant=project)
335 keystone.roles.grant(os_role, user=user, project=project)
338 class KeystoneException(Exception):
340 Exception when calls to the Keystone client cannot be served properly