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:
131 if keystone.version != V2_VERSION_STR:
132 domain_id = project.domain_id
133 if project.name == project_name:
134 return Project(name=project.name, project_id=project.id,
140 def create_project(keystone, project_settings):
143 :param keystone: the Keystone client
144 :param project_settings: the project configuration
145 :return: SNAPS-OO Project domain object
149 if keystone.version == V2_VERSION_STR:
150 os_project = keystone.tenants.create(
151 project_settings.name, project_settings.description,
152 project_settings.enabled)
154 os_project = keystone.projects.create(
155 project_settings.name, project_settings.domain,
156 description=project_settings.description,
157 enabled=project_settings.enabled)
158 domain_id = os_project.domain_id
161 name=os_project.name, project_id=os_project.id, domain_id=domain_id)
164 def delete_project(keystone, project):
167 :param keystone: the Keystone clien
168 :param project: the SNAPS-OO Project domain object
170 if keystone.version == V2_VERSION_STR:
171 keystone.tenants.delete(project.id)
173 keystone.projects.delete(project.id)
176 def __get_os_user(keystone, user):
178 Returns the OpenStack user object
179 :param keystone: the Keystone client object
180 :param user: the SNAPS-OO User domain object
183 return keystone.users.get(user.id)
186 def get_user(keystone, username, project_name=None):
188 Returns a user for a given name and optionally project
189 :param keystone: the keystone client
190 :param username: the username to lookup
191 :param project_name: the associated project (optional)
192 :return: a SNAPS-OO User domain object or None
194 project = get_project(keystone=keystone, project_name=project_name)
197 users = keystone.users.list(tenant_id=project.id)
199 users = keystone.users.list()
202 if user.name == username:
203 return User(name=user.name, user_id=user.id)
208 def create_user(keystone, user_settings):
211 :param keystone: the Keystone client
212 :param user_settings: the user configuration
213 :return: a SNAPS-OO User domain object
216 if user_settings.project_name:
217 project = get_project(keystone=keystone,
218 project_name=user_settings.project_name)
220 if keystone.version == V2_VERSION_STR:
223 project_id = project.id
224 os_user = keystone.users.create(
225 name=user_settings.name, password=user_settings.password,
226 email=user_settings.email, tenant_id=project_id,
227 enabled=user_settings.enabled)
229 os_user = keystone.users.create(
230 name=user_settings.name, password=user_settings.password,
231 email=user_settings.email, project=project,
232 domain=user_settings.domain_name, enabled=user_settings.enabled)
234 for role_name, role_project in user_settings.roles.items():
235 os_role = get_role_by_name(keystone, role_name)
236 os_project = get_project(keystone=keystone, project_name=role_project)
238 if os_role and os_project:
239 existing_roles = get_roles_by_user(keystone, os_user,
242 for role in existing_roles:
243 if role.id == os_role.id:
247 grant_user_role_to_project(
248 keystone=keystone, user=os_user, role=os_role,
252 return User(name=os_user.name, user_id=os_user.id)
255 def delete_user(keystone, user):
258 :param keystone: the Keystone client
259 :param user: the SNAPS-OO User domain object
261 keystone.users.delete(user.id)
264 def get_role_by_name(keystone, name):
266 Returns an OpenStack role object of a given name or None if not exists
267 :param keystone: the keystone client
268 :param name: the role name
269 :return: the SNAPS-OO Role domain object
271 roles = keystone.roles.list()
273 if role.name == name:
274 return Role(name=role.name, role_id=role.id)
277 def get_roles_by_user(keystone, user, project):
279 Returns a list of SNAPS-OO Role domain objects associated with a user
280 :param keystone: the keystone client
281 :param user: the OpenStack user object
282 :param project: the OpenStack project object (only required for v2)
283 :return: a list of SNAPS-OO Role domain objects
285 if keystone.version == V2_VERSION_STR:
286 os_user = __get_os_user(keystone, user)
287 roles = keystone.roles.roles_for_user(os_user, project)
289 roles = keystone.roles.list(user=user, project=project)
293 out.append(Role(name=role.name, role_id=role.id))
297 def get_role_by_id(keystone, role_id):
299 Returns an OpenStack role object of a given name or None if not exists
300 :param keystone: the keystone client
301 :param role_id: the role ID
302 :return: a SNAPS-OO Role domain object
304 role = keystone.roles.get(role_id)
305 return Role(name=role.name, role_id=role.id)
308 def create_role(keystone, name):
310 Creates an OpenStack role
311 :param keystone: the keystone client
312 :param name: the role name
313 :return: a SNAPS-OO Role domain object
315 role = keystone.roles.create(name)
316 return Role(name=role.name, role_id=role.id)
319 def delete_role(keystone, role):
321 Deletes an OpenStack role
322 :param keystone: the keystone client
323 :param role: the SNAPS-OO Role domain object to delete
326 keystone.roles.delete(role.id)
329 def grant_user_role_to_project(keystone, role, user, project):
331 Grants user and role to a project
332 :param keystone: the Keystone client
333 :param role: the SNAPS-OO Role domain object used to join a project/user
334 :param user: the user to add to the project (SNAPS-OO User Domain object
335 :param project: the project to which to add a user
339 os_role = get_role_by_id(keystone, role.id)
340 if keystone.version == V2_VERSION_STR:
341 keystone.roles.add_user_role(user, os_role, tenant=project)
343 keystone.roles.grant(os_role, user=user, project=project)
346 class KeystoneException(Exception):
348 Exception when calls to the Keystone client cannot be served properly