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_settings=None,
110 Returns the first project object or None if not found
111 :param keystone: the Keystone client
112 :param os_creds: the OpenStack credentials used to obtain the Keystone
113 client if the keystone parameter is None
114 :param project_settings: a ProjectSettings object
115 :param project_name: the name to query
116 :return: the SNAPS-OO Project domain object or None
120 keystone = keystone_client(os_creds)
122 raise KeystoneException(
123 'Cannot lookup project without the proper credentials')
128 proj_filter['name'] = project_name
129 elif project_settings:
130 proj_filter['name'] = project_settings.name
131 proj_filter['description'] = project_settings.description
132 proj_filter['domain'] = project_settings.domain
133 proj_filter['enabled'] = project_settings.enabled
135 if keystone.version == V2_VERSION_STR:
136 projects = keystone.tenants.list()
138 projects = keystone.projects.list(**proj_filter)
140 for project in projects:
141 if project.name == proj_filter['name']:
143 if keystone.version != V2_VERSION_STR:
144 domain_id = project.domain_id
146 return Project(name=project.name, project_id=project.id,
150 def create_project(keystone, project_settings):
153 :param keystone: the Keystone client
154 :param project_settings: the project configuration
155 :return: SNAPS-OO Project domain object
159 if keystone.version == V2_VERSION_STR:
160 os_project = keystone.tenants.create(
161 project_settings.name, project_settings.description,
162 project_settings.enabled)
164 os_project = keystone.projects.create(
165 project_settings.name, project_settings.domain,
166 description=project_settings.description,
167 enabled=project_settings.enabled)
168 domain_id = os_project.domain_id
171 name=os_project.name, project_id=os_project.id, domain_id=domain_id)
174 def delete_project(keystone, project):
177 :param keystone: the Keystone clien
178 :param project: the SNAPS-OO Project domain object
180 if keystone.version == V2_VERSION_STR:
181 keystone.tenants.delete(project.id)
183 keystone.projects.delete(project.id)
186 def __get_os_user(keystone, user):
188 Returns the OpenStack user object
189 :param keystone: the Keystone client object
190 :param user: the SNAPS-OO User domain object
193 return keystone.users.get(user.id)
196 def get_user(keystone, username, project_name=None):
198 Returns a user for a given name and optionally project
199 :param keystone: the keystone client
200 :param username: the username to lookup
201 :param project_name: the associated project (optional)
202 :return: a SNAPS-OO User domain object or None
206 project = get_project(keystone=keystone, project_name=project_name)
209 users = keystone.users.list(tenant_id=project.id)
211 users = keystone.users.list()
214 if user.name == username:
215 return User(name=user.name, user_id=user.id)
220 def create_user(keystone, user_settings):
223 :param keystone: the Keystone client
224 :param user_settings: the user configuration
225 :return: a SNAPS-OO User domain object
228 if user_settings.project_name:
229 project = get_project(keystone=keystone,
230 project_name=user_settings.project_name)
232 if keystone.version == V2_VERSION_STR:
235 project_id = project.id
236 os_user = keystone.users.create(
237 name=user_settings.name, password=user_settings.password,
238 email=user_settings.email, tenant_id=project_id,
239 enabled=user_settings.enabled)
241 os_user = keystone.users.create(
242 name=user_settings.name, password=user_settings.password,
243 email=user_settings.email, project=project,
244 domain=user_settings.domain_name, enabled=user_settings.enabled)
246 for role_name, role_project in user_settings.roles.items():
247 os_role = get_role_by_name(keystone, role_name)
248 os_project = get_project(keystone=keystone, project_name=role_project)
250 if os_role and os_project:
251 existing_roles = get_roles_by_user(keystone, os_user,
254 for role in existing_roles:
255 if role.id == os_role.id:
259 grant_user_role_to_project(
260 keystone=keystone, user=os_user, role=os_role,
264 return User(name=os_user.name, user_id=os_user.id)
267 def delete_user(keystone, user):
270 :param keystone: the Keystone client
271 :param user: the SNAPS-OO User domain object
273 keystone.users.delete(user.id)
276 def get_role_by_name(keystone, name):
278 Returns an OpenStack role object of a given name or None if not exists
279 :param keystone: the keystone client
280 :param name: the role name
281 :return: the SNAPS-OO Role domain object
283 roles = keystone.roles.list()
285 if role.name == name:
286 return Role(name=role.name, role_id=role.id)
289 def get_roles_by_user(keystone, user, project):
291 Returns a list of SNAPS-OO Role domain objects associated with a user
292 :param keystone: the keystone client
293 :param user: the OpenStack user object
294 :param project: the OpenStack project object (only required for v2)
295 :return: a list of SNAPS-OO Role domain objects
297 if keystone.version == V2_VERSION_STR:
298 os_user = __get_os_user(keystone, user)
299 roles = keystone.roles.roles_for_user(os_user, project)
301 roles = keystone.roles.list(user=user, project=project)
305 out.append(Role(name=role.name, role_id=role.id))
309 def get_role_by_id(keystone, role_id):
311 Returns an OpenStack role object of a given name or None if not exists
312 :param keystone: the keystone client
313 :param role_id: the role ID
314 :return: a SNAPS-OO Role domain object
316 role = keystone.roles.get(role_id)
317 return Role(name=role.name, role_id=role.id)
320 def create_role(keystone, name):
322 Creates an OpenStack role
323 :param keystone: the keystone client
324 :param name: the role name
325 :return: a SNAPS-OO Role domain object
327 role = keystone.roles.create(name)
328 return Role(name=role.name, role_id=role.id)
331 def delete_role(keystone, role):
333 Deletes an OpenStack role
334 :param keystone: the keystone client
335 :param role: the SNAPS-OO Role domain object to delete
338 keystone.roles.delete(role.id)
341 def grant_user_role_to_project(keystone, role, user, project):
343 Grants user and role to a project
344 :param keystone: the Keystone client
345 :param role: the SNAPS-OO Role domain object used to join a project/user
346 :param user: the user to add to the project (SNAPS-OO User Domain object
347 :param project: the project to which to add a user
351 os_role = get_role_by_id(keystone, role.id)
352 if keystone.version == V2_VERSION_STR:
353 keystone.roles.add_user_role(user, os_role, tenant=project)
355 keystone.roles.grant(os_role, user=user, project=project)
358 class KeystoneException(Exception):
360 Exception when calls to the Keystone client cannot be served properly