Changed keystone_utils#get_project to accept settings
[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_NUM = 2.0
29 V2_VERSION_STR = 'v' + str(V2_VERSION_NUM)
30
31
32 def get_session_auth(os_creds):
33     """
34     Return the session auth for keystone session
35     :param os_creds: the OpenStack credentials (OSCreds) object
36     :return: the auth
37     """
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)
47     else:
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)
52     return auth
53
54
55 def keystone_session(os_creds):
56     """
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
60     """
61     logger.debug('Retrieving Keystone Session')
62
63     auth = get_session_auth(os_creds)
64
65     req_session = None
66     if os_creds.proxy_settings:
67         req_session = requests.Session()
68         req_session.proxies = {
69             'http':
70                 os_creds.proxy_settings.host + ':' +
71                 os_creds.proxy_settings.port,
72             'https':
73                 os_creds.proxy_settings.https_host + ':' +
74                 os_creds.proxy_settings.https_port
75         }
76     return session.Session(auth=auth, session=req_session,
77                            verify=os_creds.cacert)
78
79
80 def keystone_client(os_creds):
81     """
82     Returns the keystone client
83     :param os_creds: the OpenStack credentials (OSCreds) object
84     :return: the client
85     """
86     return Client(
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)
91
92
93 def get_endpoint(os_creds, service_type, interface='public'):
94     """
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
100     """
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)
105
106
107 def get_project(keystone=None, os_creds=None, project_settings=None,
108                 project_name=None):
109     """
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
117     """
118     if not keystone:
119         if os_creds:
120             keystone = keystone_client(os_creds)
121         else:
122             raise KeystoneException(
123                 'Cannot lookup project without the proper credentials')
124
125     proj_filter = dict()
126
127     if project_name:
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
134
135     if keystone.version == V2_VERSION_STR:
136         projects = keystone.tenants.list()
137     else:
138         projects = keystone.projects.list(**proj_filter)
139
140     for project in projects:
141         if project.name == proj_filter['name']:
142             domain_id = None
143             if keystone.version != V2_VERSION_STR:
144                 domain_id = project.domain_id
145
146             return Project(name=project.name, project_id=project.id,
147                            domain_id=domain_id)
148
149
150 def create_project(keystone, project_settings):
151     """
152     Creates a project
153     :param keystone: the Keystone client
154     :param project_settings: the project configuration
155     :return: SNAPS-OO Project domain object
156     """
157     domain_id = None
158
159     if keystone.version == V2_VERSION_STR:
160         os_project = keystone.tenants.create(
161             project_settings.name, project_settings.description,
162             project_settings.enabled)
163     else:
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
169
170     return Project(
171         name=os_project.name, project_id=os_project.id, domain_id=domain_id)
172
173
174 def delete_project(keystone, project):
175     """
176     Deletes a project
177     :param keystone: the Keystone clien
178     :param project: the SNAPS-OO Project domain object
179     """
180     if keystone.version == V2_VERSION_STR:
181         keystone.tenants.delete(project.id)
182     else:
183         keystone.projects.delete(project.id)
184
185
186 def __get_os_user(keystone, user):
187     """
188     Returns the OpenStack user object
189     :param keystone: the Keystone client object
190     :param user: the SNAPS-OO User domain object
191     :return:
192     """
193     return keystone.users.get(user.id)
194
195
196 def get_user(keystone, username, project_name=None):
197     """
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
203     """
204     project = None
205     if project_name:
206         project = get_project(keystone=keystone, project_name=project_name)
207
208     if project:
209         users = keystone.users.list(tenant_id=project.id)
210     else:
211         users = keystone.users.list()
212
213     for user in users:
214         if user.name == username:
215             return User(name=user.name, user_id=user.id)
216
217     return None
218
219
220 def create_user(keystone, user_settings):
221     """
222     Creates a user
223     :param keystone: the Keystone client
224     :param user_settings: the user configuration
225     :return: a SNAPS-OO User domain object
226     """
227     project = None
228     if user_settings.project_name:
229         project = get_project(keystone=keystone,
230                               project_name=user_settings.project_name)
231
232     if keystone.version == V2_VERSION_STR:
233         project_id = None
234         if project:
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)
240     else:
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)
245
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)
249
250         if os_role and os_project:
251             existing_roles = get_roles_by_user(keystone, os_user,
252                                                os_project)
253             found = False
254             for role in existing_roles:
255                 if role.id == os_role.id:
256                     found = True
257
258             if not found:
259                 grant_user_role_to_project(
260                     keystone=keystone, user=os_user, role=os_role,
261                     project=os_project)
262
263     if os_user:
264         return User(name=os_user.name, user_id=os_user.id)
265
266
267 def delete_user(keystone, user):
268     """
269     Deletes a user
270     :param keystone: the Keystone client
271     :param user: the SNAPS-OO User domain object
272     """
273     keystone.users.delete(user.id)
274
275
276 def get_role_by_name(keystone, name):
277     """
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
282     """
283     roles = keystone.roles.list()
284     for role in roles:
285         if role.name == name:
286             return Role(name=role.name, role_id=role.id)
287
288
289 def get_roles_by_user(keystone, user, project):
290     """
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
296     """
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)
300     else:
301         roles = keystone.roles.list(user=user, project=project)
302
303     out = list()
304     for role in roles:
305         out.append(Role(name=role.name, role_id=role.id))
306     return out
307
308
309 def get_role_by_id(keystone, role_id):
310     """
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
315     """
316     role = keystone.roles.get(role_id)
317     return Role(name=role.name, role_id=role.id)
318
319
320 def create_role(keystone, name):
321     """
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
326     """
327     role = keystone.roles.create(name)
328     return Role(name=role.name, role_id=role.id)
329
330
331 def delete_role(keystone, role):
332     """
333     Deletes an OpenStack role
334     :param keystone: the keystone client
335     :param role: the SNAPS-OO Role domain object to delete
336     :return:
337     """
338     keystone.roles.delete(role.id)
339
340
341 def grant_user_role_to_project(keystone, role, user, project):
342     """
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
348     :return:
349     """
350
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)
354     else:
355         keystone.roles.grant(os_role, user=user, project=project)
356
357
358 class KeystoneException(Exception):
359     """
360     Exception when calls to the Keystone client cannot be served properly
361     """