9bfc64733950fc26beb1d853b7445fb2d9cc5438
[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 SNAPS-OO Project domain object 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         os_project = keystone.tenants.create(
138             project_settings.name, project_settings.description,
139             project_settings.enabled)
140     else:
141         os_project = keystone.projects.create(
142             project_settings.name, project_settings.domain,
143             description=project_settings.description,
144             enabled=project_settings.enabled)
145
146     return Project(name=os_project.name, project_id=os_project.id)
147
148
149 def delete_project(keystone, project):
150     """
151     Deletes a project
152     :param keystone: the Keystone clien
153     :param project: the SNAPS-OO Project domain object
154     """
155     if keystone.version == V2_VERSION:
156         keystone.tenants.delete(project.id)
157     else:
158         keystone.projects.delete(project.id)
159
160
161 def __get_os_user(keystone, user):
162     """
163     Returns the OpenStack user object
164     :param keystone: the Keystone client object
165     :param user: the SNAPS-OO User domain object
166     :return:
167     """
168     return keystone.users.get(user.id)
169
170
171 def get_user(keystone, username, project_name=None):
172     """
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
178     """
179     project = get_project(keystone=keystone, project_name=project_name)
180
181     if project:
182         users = keystone.users.list(tenant_id=project.id)
183     else:
184         users = keystone.users.list()
185
186     for user in users:
187         if user.name == username:
188             return User(name=user.name, user_id=user.id)
189
190     return None
191
192
193 def create_user(keystone, user_settings):
194     """
195     Creates a user
196     :param keystone: the Keystone client
197     :param user_settings: the user configuration
198     :return: a SNAPS-OO User domain object
199     """
200     project = None
201     if user_settings.project_name:
202         project = get_project(keystone=keystone,
203                               project_name=user_settings.project_name)
204
205     if keystone.version == V2_VERSION:
206         project_id = None
207         if project:
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)
213     else:
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)
218
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)
222
223         if os_role and os_project:
224             existing_roles = get_roles_by_user(keystone, os_user,
225                                                os_project)
226             found = False
227             for role in existing_roles:
228                 if role.id == os_role.id:
229                     found = True
230
231             if not found:
232                 grant_user_role_to_project(
233                     keystone=keystone, user=os_user, role=os_role,
234                     project=os_project)
235
236     if os_user:
237         return User(name=os_user.name, user_id=os_user.id)
238
239
240 def delete_user(keystone, user):
241     """
242     Deletes a user
243     :param keystone: the Keystone client
244     :param user: the SNAPS-OO User domain object
245     """
246     keystone.users.delete(user.id)
247
248
249 def get_role_by_name(keystone, name):
250     """
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
255     """
256     roles = keystone.roles.list()
257     for role in roles:
258         if role.name == name:
259             return Role(name=role.name, role_id=role.id)
260
261
262 def get_roles_by_user(keystone, user, project):
263     """
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
269     """
270     if keystone.version == V2_VERSION:
271         os_user = __get_os_user(keystone, user)
272         roles = keystone.roles.roles_for_user(os_user, project)
273     else:
274         roles = keystone.roles.list(user=user, project=project)
275
276     out = list()
277     for role in roles:
278         out.append(Role(name=role.name, role_id=role.id))
279     return out
280
281
282 def get_role_by_id(keystone, role_id):
283     """
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
288     """
289     role = keystone.roles.get(role_id)
290     return Role(name=role.name, role_id=role.id)
291
292
293 def create_role(keystone, name):
294     """
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
299     """
300     role = keystone.roles.create(name)
301     return Role(name=role.name, role_id=role.id)
302
303
304 def delete_role(keystone, role):
305     """
306     Deletes an OpenStack role
307     :param keystone: the keystone client
308     :param role: the SNAPS-OO Role domain object to delete
309     :return:
310     """
311     keystone.roles.delete(role.id)
312
313
314 def grant_user_role_to_project(keystone, role, user, project):
315     """
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
321     :return:
322     """
323
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)
327     else:
328         keystone.roles.grant(os_role, user=user, project=project)
329
330
331 class KeystoneException(Exception):
332     """
333     Exception when calls to the Keystone client cannot be served properly
334     """