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')
31 def get_session_auth(os_creds):
33 Return the session auth for keystone session
34 :param os_creds: the OpenStack credentials (OSCreds) object
37 if os_creds.identity_api_version == 3:
38 auth = v3.Password(auth_url=os_creds.auth_url,
39 username=os_creds.username,
40 password=os_creds.password,
41 project_name=os_creds.project_name,
42 user_domain_id=os_creds.user_domain_id,
43 project_domain_id=os_creds.project_domain_id)
45 auth = v2.Password(auth_url=os_creds.auth_url,
46 username=os_creds.username,
47 password=os_creds.password,
48 tenant_name=os_creds.project_name)
52 def keystone_session(os_creds):
54 Creates a keystone session used for authenticating OpenStack clients
55 :param os_creds: The connection credentials to the OpenStack API
56 :return: the client object
58 logger.debug('Retrieving Keystone Session')
60 auth = get_session_auth(os_creds)
63 if os_creds.proxy_settings:
64 req_session = requests.Session()
65 req_session.proxies = {
67 os_creds.proxy_settings.host + ':' +
68 os_creds.proxy_settings.port}
69 return session.Session(auth=auth, session=req_session,
70 verify=os_creds.cacert)
73 def keystone_client(os_creds):
75 Returns the keystone client
76 :param os_creds: the OpenStack credentials (OSCreds) object
80 version=os_creds.identity_api_version,
81 session=keystone_session(os_creds), interface=os_creds.interface)
84 def get_endpoint(os_creds, service_type, interface='public'):
86 Returns the endpoint of specific service
87 :param os_creds: the OpenStack credentials (OSCreds) object
88 :param service_type: the type of specific service
89 :param interface: the type of interface
90 :return: the endpoint url
92 auth = get_session_auth(os_creds)
93 key_session = keystone_session(os_creds)
94 return key_session.get_endpoint(
95 auth=auth, service_type=service_type, interface=interface)
98 def get_project(keystone=None, os_creds=None, project_name=None):
100 Returns the first project object or None if not found
101 :param keystone: the Keystone client
102 :param os_creds: the OpenStack credentials used to obtain the Keystone
103 client if the keystone parameter is None
104 :param project_name: the name to query
105 :return: the SNAPS-OO Project domain object or None
112 keystone = keystone_client(os_creds)
114 raise KeystoneException(
115 'Cannot lookup project without the proper credentials')
117 if keystone.version == V2_VERSION:
118 projects = keystone.tenants.list()
120 projects = keystone.projects.list(**{'name': project_name})
122 for project in projects:
123 if project.name == project_name:
124 return Project(name=project.name, project_id=project.id)
129 def create_project(keystone, project_settings):
132 :param keystone: the Keystone client
133 :param project_settings: the project configuration
134 :return: SNAPS-OO Project domain object
136 if keystone.version == V2_VERSION:
137 os_project = keystone.tenants.create(
138 project_settings.name, project_settings.description,
139 project_settings.enabled)
141 os_project = keystone.projects.create(
142 project_settings.name, project_settings.domain,
143 description=project_settings.description,
144 enabled=project_settings.enabled)
146 return Project(name=os_project.name, project_id=os_project.id)
149 def delete_project(keystone, project):
152 :param keystone: the Keystone clien
153 :param project: the SNAPS-OO Project domain object
155 if keystone.version == V2_VERSION:
156 keystone.tenants.delete(project.id)
158 keystone.projects.delete(project.id)
161 def __get_os_user(keystone, user):
163 Returns the OpenStack user object
164 :param keystone: the Keystone client object
165 :param user: the SNAPS-OO User domain object
168 return keystone.users.get(user.id)
171 def get_user(keystone, username, project_name=None):
173 Returns a user for a given name and optionally project
174 :param keystone: the keystone client
175 :param username: the username to lookup
176 :param project_name: the associated project (optional)
177 :return: a SNAPS-OO User domain object or None
179 project = get_project(keystone=keystone, project_name=project_name)
182 users = keystone.users.list(tenant_id=project.id)
184 users = keystone.users.list()
187 if user.name == username:
188 return User(name=user.name, user_id=user.id)
193 def create_user(keystone, user_settings):
196 :param keystone: the Keystone client
197 :param user_settings: the user configuration
198 :return: a SNAPS-OO User domain object
201 if user_settings.project_name:
202 project = get_project(keystone=keystone,
203 project_name=user_settings.project_name)
205 if keystone.version == V2_VERSION:
208 project_id = project.id
209 os_user = keystone.users.create(
210 name=user_settings.name, password=user_settings.password,
211 email=user_settings.email, tenant_id=project_id,
212 enabled=user_settings.enabled)
214 os_user = keystone.users.create(
215 name=user_settings.name, password=user_settings.password,
216 email=user_settings.email, project=project,
217 domain=user_settings.domain_name, enabled=user_settings.enabled)
219 for role_name, role_project in user_settings.roles.items():
220 os_role = get_role_by_name(keystone, role_name)
221 os_project = get_project(keystone=keystone, project_name=role_project)
223 if os_role and os_project:
224 existing_roles = get_roles_by_user(keystone, os_user,
227 for role in existing_roles:
228 if role.id == os_role.id:
232 grant_user_role_to_project(
233 keystone=keystone, user=os_user, role=os_role,
237 return User(name=os_user.name, user_id=os_user.id)
240 def delete_user(keystone, user):
243 :param keystone: the Keystone client
244 :param user: the SNAPS-OO User domain object
246 keystone.users.delete(user.id)
249 def get_role_by_name(keystone, name):
251 Returns an OpenStack role object of a given name or None if not exists
252 :param keystone: the keystone client
253 :param name: the role name
254 :return: the SNAPS-OO Role domain object
256 roles = keystone.roles.list()
258 if role.name == name:
259 return Role(name=role.name, role_id=role.id)
262 def get_roles_by_user(keystone, user, project):
264 Returns a list of SNAPS-OO Role domain objects associated with a user
265 :param keystone: the keystone client
266 :param user: the OpenStack user object
267 :param project: the OpenStack project object (only required for v2)
268 :return: a list of SNAPS-OO Role domain objects
270 if keystone.version == V2_VERSION:
271 os_user = __get_os_user(keystone, user)
272 roles = keystone.roles.roles_for_user(os_user, project)
274 roles = keystone.roles.list(user=user, project=project)
278 out.append(Role(name=role.name, role_id=role.id))
282 def get_role_by_id(keystone, role_id):
284 Returns an OpenStack role object of a given name or None if not exists
285 :param keystone: the keystone client
286 :param role_id: the role ID
287 :return: a SNAPS-OO Role domain object
289 role = keystone.roles.get(role_id)
290 return Role(name=role.name, role_id=role.id)
293 def create_role(keystone, name):
295 Creates an OpenStack role
296 :param keystone: the keystone client
297 :param name: the role name
298 :return: a SNAPS-OO Role domain object
300 role = keystone.roles.create(name)
301 return Role(name=role.name, role_id=role.id)
304 def delete_role(keystone, role):
306 Deletes an OpenStack role
307 :param keystone: the keystone client
308 :param role: the SNAPS-OO Role domain object to delete
311 keystone.roles.delete(role.id)
314 def grant_user_role_to_project(keystone, role, user, project):
316 Grants user and role to a project
317 :param keystone: the Keystone client
318 :param role: the SNAPS-OO Role domain object used to join a project/user
319 :param user: the user to add to the project (SNAPS-OO User Domain object
320 :param project: the project to which to add a user
324 os_role = get_role_by_id(keystone, role.id)
325 if keystone.version == V2_VERSION:
326 keystone.roles.add_user_role(user, os_role, tenant=project)
328 keystone.roles.grant(os_role, user=user, project=project)
331 class KeystoneException(Exception):
333 Exception when calls to the Keystone client cannot be served properly