Created new class KeystoneException
[snaps.git] / snaps / openstack / utils / keystone_utils.py
1 # Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
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:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15 import logging
16
17 from keystoneclient.client import Client
18 from keystoneauth1.identity import v3, v2
19 from keystoneauth1 import session
20 import requests
21
22 from snaps.domain.project import Project
23 from snaps.domain.role import Role
24 from snaps.domain.user import User
25
26 logger = logging.getLogger('keystone_utils')
27
28 V2_VERSION = 'v2.0'
29
30
31 def get_session_auth(os_creds):
32     """
33     Return the session auth for keystone session
34     :param os_creds: the OpenStack credentials (OSCreds) object
35     :return: the auth
36     """
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)
44     else:
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)
49     return auth
50
51
52 def keystone_session(os_creds):
53     """
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
57     """
58     logger.debug('Retrieving Keystone Session')
59
60     auth = get_session_auth(os_creds)
61
62     req_session = None
63     if os_creds.proxy_settings:
64         req_session = requests.Session()
65         req_session.proxies = {
66             'http':
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)
71
72
73 def keystone_client(os_creds):
74     """
75     Returns the keystone client
76     :param os_creds: the OpenStack credentials (OSCreds) object
77     :return: the client
78     """
79     return Client(
80         version=os_creds.identity_api_version,
81         session=keystone_session(os_creds), interface=os_creds.interface)
82
83
84 def get_endpoint(os_creds, service_type, interface='public'):
85     """
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
91     """
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)
96
97
98 def get_project(keystone=None, os_creds=None, project_name=None):
99     """
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 ID or None
106     """
107     if not project_name:
108         return None
109
110     if not keystone:
111         if os_creds:
112             keystone = keystone_client(os_creds)
113         else:
114             raise KeystoneException(
115                 'Cannot lookup project without the proper credentials')
116
117     if keystone.version == V2_VERSION:
118         projects = keystone.tenants.list()
119     else:
120         projects = keystone.projects.list(**{'name': project_name})
121
122     for project in projects:
123         if project.name == project_name:
124             return Project(name=project.name, project_id=project.id)
125
126     return None
127
128
129 def create_project(keystone, project_settings):
130     """
131     Creates a project
132     :param keystone: the Keystone client
133     :param project_settings: the project configuration
134     :return: SNAPS-OO Project domain object
135     """
136     if keystone.version == V2_VERSION:
137         return keystone.tenants.create(
138             project_settings.name, project_settings.description,
139             project_settings.enabled)
140
141     return keystone.projects.create(
142         project_settings.name, project_settings.domain,
143         description=project_settings.description,
144         enabled=project_settings.enabled)
145
146
147 def delete_project(keystone, project):
148     """
149     Deletes a project
150     :param keystone: the Keystone clien
151     :param project: the SNAPS-OO Project domain object
152     """
153     if keystone.version == V2_VERSION:
154         keystone.tenants.delete(project.id)
155     else:
156         keystone.projects.delete(project.id)
157
158
159 def __get_os_user(keystone, user):
160     """
161     Returns the OpenStack user object
162     :param keystone: the Keystone client object
163     :param user: the SNAPS-OO User domain object
164     :return:
165     """
166     return keystone.users.get(user.id)
167
168
169 def get_user(keystone, username, project_name=None):
170     """
171     Returns a user for a given name and optionally project
172     :param keystone: the keystone client
173     :param username: the username to lookup
174     :param project_name: the associated project (optional)
175     :return: a SNAPS-OO User domain object or None
176     """
177     project = get_project(keystone=keystone, project_name=project_name)
178
179     if project:
180         users = keystone.users.list(tenant_id=project.id)
181     else:
182         users = keystone.users.list()
183
184     for user in users:
185         if user.name == username:
186             return User(name=user.name, user_id=user.id)
187
188     return None
189
190
191 def create_user(keystone, user_settings):
192     """
193     Creates a user
194     :param keystone: the Keystone client
195     :param user_settings: the user configuration
196     :return: a SNAPS-OO User domain object
197     """
198     project = None
199     if user_settings.project_name:
200         project = get_project(keystone=keystone,
201                               project_name=user_settings.project_name)
202
203     if keystone.version == V2_VERSION:
204         project_id = None
205         if project:
206             project_id = project.id
207         os_user = keystone.users.create(
208             name=user_settings.name, password=user_settings.password,
209             email=user_settings.email, tenant_id=project_id,
210             enabled=user_settings.enabled)
211     else:
212         os_user = keystone.users.create(
213             name=user_settings.name, password=user_settings.password,
214             email=user_settings.email, project=project,
215             domain=user_settings.domain_name, enabled=user_settings.enabled)
216
217     for role_name, role_project in user_settings.roles.items():
218         os_role = _get_os_role_by_name(keystone, role_name)
219         os_project = get_project(keystone=keystone, project_name=role_project)
220
221         if os_role and os_project:
222             existing_roles = _get_os_roles_by_user(keystone, os_user,
223                                                    os_project)
224             found = False
225             for role in existing_roles:
226                 if role.id == os_role.id:
227                     found = True
228
229             if not found:
230                 grant_user_role_to_project(
231                     keystone=keystone, user=os_user, role=os_role,
232                     project=os_project)
233
234     if os_user:
235         return User(name=os_user.name, user_id=os_user.id)
236
237
238 def delete_user(keystone, user):
239     """
240     Deletes a user
241     :param keystone: the Keystone client
242     :param user: the SNAPS-OO User domain object
243     """
244     keystone.users.delete(user.id)
245
246
247 def _get_os_role_by_name(keystone, name):
248     """
249     Returns an OpenStack role object of a given name or None if not exists
250     :param keystone: the keystone client
251     :param name: the role name
252     :return: the SNAPS-OO Role domain object
253     """
254     roles = keystone.roles.list()
255     for role in roles:
256         if role.name == name:
257             return Role(name=role.name, role_id=role.id)
258
259
260 def _get_os_roles_by_user(keystone, user, project):
261     """
262     Returns a list of OpenStack role object associated with a user
263     :param keystone: the keystone client
264     :param user: the OpenStack user object
265     :param project: the OpenStack project object (only required for v2)
266     :return: a list of SNAPS-OO Role domain objects
267     """
268     if keystone.version == V2_VERSION:
269         os_user = __get_os_user(keystone, user)
270         roles = keystone.roles.roles_for_user(os_user, project)
271     else:
272         roles = keystone.roles.list(user=user, project=project)
273
274     out = list()
275     for role in roles:
276         out.append(Role(name=role.name, role_id=role.id))
277     return out
278
279
280 def __get_os_role_by_id(keystone, role_id):
281     """
282     Returns an OpenStack role object of a given name or None if not exists
283     :param keystone: the keystone client
284     :param role_id: the role ID
285     :return: a SNAPS-OO Role domain object
286     """
287     role = keystone.roles.get(role_id)
288     return Role(name=role.name, role_id=role.id)
289
290
291 def create_role(keystone, name):
292     """
293     Creates an OpenStack role
294     :param keystone: the keystone client
295     :param name: the role name
296     :return: a SNAPS-OO Role domain object
297     """
298     role = keystone.roles.create(name)
299     return Role(name=role.name, role_id=role.id)
300
301
302 def delete_role(keystone, role):
303     """
304     Deletes an OpenStack role
305     :param keystone: the keystone client
306     :param role: the SNAPS-OO Role domain object to delete
307     :return:
308     """
309     keystone.roles.delete(role.id)
310
311
312 def grant_user_role_to_project(keystone, role, user, project):
313     """
314     Grants user and role to a project
315     :param keystone: the Keystone client
316     :param role: the SNAPS-OO Role domain object used to join a project/user
317     :param user: the user to add to the project (SNAPS-OO User Domain object
318     :param project: the project to which to add a user
319     :return:
320     """
321
322     os_role = __get_os_role_by_id(keystone, role.id)
323     if keystone.version == V2_VERSION:
324         keystone.roles.add_user_role(user, os_role, tenant=project)
325     else:
326         keystone.roles.grant(os_role, user=user, project=project)
327
328
329 class KeystoneException(Exception):
330     """
331     Exception when calls to the Keystone client cannot be served properly
332     """