1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
|
# Copyright 2011 OpenStack Foundation
# Copyright 2011 Nebula, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six.moves.urllib as urllib
from keystoneclient import base
from keystoneclient import exceptions
from keystoneclient.i18n import _
class Project(base.Resource):
"""Represents an Identity project.
Attributes:
* id: a uuid that identifies the project
* name: project name
* description: project description
* enabled: boolean to indicate if project is enabled
* parent_id: a uuid representing this project's parent in hierarchy
* parents: a list or a structured dict containing the parents of this
project in the hierarchy
* subtree: a list or a structured dict containing the subtree of this
project in the hierarchy
"""
def update(self, name=None, description=None, enabled=None):
kwargs = {
'name': name if name is not None else self.name,
'description': (description
if description is not None
else self.description),
'enabled': enabled if enabled is not None else self.enabled,
}
try:
retval = self.manager.update(self.id, **kwargs)
self = retval
except Exception:
retval = None
return retval
def add_tag(self, tag):
self.manager.add_tag(self, tag)
def update_tags(self, tags):
return self.manager.update_tags(self, tags)
def delete_tag(self, tag):
self.manager.delete_tag(self, tag)
def delete_all_tags(self):
return self.manager.update_tags(self, [])
def list_tags(self):
return self.manager.list_tags(self)
def check_tag(self, tag):
return self.manager.check_tag(self, tag)
class ProjectManager(base.CrudManager):
"""Manager class for manipulating Identity projects."""
resource_class = Project
collection_key = 'projects'
key = 'project'
def create(self, name, domain, description=None,
enabled=True, parent=None, **kwargs):
"""Create a project.
:param str name: the name of the project.
:param domain: the domain of the project.
:type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param str description: the description of the project.
:param bool enabled: whether the project is enabled.
:param parent: the parent of the project in the hierarchy.
:type parent: str or :class:`keystoneclient.v3.projects.Project`
:param kwargs: any other attribute provided will be passed to the
server.
:returns: the created project returned from server.
:rtype: :class:`keystoneclient.v3.projects.Project`
"""
# NOTE(rodrigods): the API must be backwards compatible, so if an
# application was passing a 'parent_id' before as kwargs, the call
# should not fail. If both 'parent' and 'parent_id' are provided,
# 'parent' will be preferred.
if parent:
kwargs['parent_id'] = base.getid(parent)
return super(ProjectManager, self).create(
domain_id=base.getid(domain),
name=name,
description=description,
enabled=enabled,
**kwargs)
def list(self, domain=None, user=None, **kwargs):
"""List projects.
:param domain: the domain of the projects to be filtered on.
:type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param user: filter in projects the specified user has role
assignments on.
:type user: str or :class:`keystoneclient.v3.users.User`
:param kwargs: any other attribute provided will filter projects on.
Project tags filter keyword: ``tags``, ``tags_any``,
``not_tags``, and ``not_tags_any``. tag attribute type
string. Pass in a comma separated string to filter
with multiple tags.
:returns: a list of projects.
:rtype: list of :class:`keystoneclient.v3.projects.Project`
"""
base_url = '/users/%s' % base.getid(user) if user else None
projects = super(ProjectManager, self).list(
base_url=base_url,
domain_id=base.getid(domain),
fallback_to_auth=True,
**kwargs)
for p in projects:
p.tags = self._encode_tags(getattr(p, 'tags', []))
return projects
def _check_not_parents_as_ids_and_parents_as_list(self, parents_as_ids,
parents_as_list):
if parents_as_ids and parents_as_list:
msg = _('Specify either parents_as_ids or parents_as_list '
'parameters, not both')
raise exceptions.ValidationError(msg)
def _check_not_subtree_as_ids_and_subtree_as_list(self, subtree_as_ids,
subtree_as_list):
if subtree_as_ids and subtree_as_list:
msg = _('Specify either subtree_as_ids or subtree_as_list '
'parameters, not both')
raise exceptions.ValidationError(msg)
def get(self, project, subtree_as_list=False, parents_as_list=False,
subtree_as_ids=False, parents_as_ids=False):
"""Retrieve a project.
:param project: the project to be retrieved from the server.
:type project: str or :class:`keystoneclient.v3.projects.Project`
:param bool subtree_as_list: retrieve projects below this project in
the hierarchy as a flat list. It only
includes the projects in which the current
user has role assignments on.
:param bool parents_as_list: retrieve projects above this project in
the hierarchy as a flat list. It only
includes the projects in which the current
user has role assignments on.
:param bool subtree_as_ids: retrieve the IDs from the projects below
this project in the hierarchy as a
structured dictionary.
:param bool parents_as_ids: retrieve the IDs from the projects above
this project in the hierarchy as a
structured dictionary.
:returns: the specified project returned from server.
:rtype: :class:`keystoneclient.v3.projects.Project`
:raises keystoneclient.exceptions.ValidationError: if subtree_as_list
and subtree_as_ids or parents_as_list and parents_as_ids are
included at the same time in the call.
"""
self._check_not_parents_as_ids_and_parents_as_list(
parents_as_ids, parents_as_list)
self._check_not_subtree_as_ids_and_subtree_as_list(
subtree_as_ids, subtree_as_list)
# According to the API spec, the query params are key only
query_params = []
if subtree_as_list:
query_params.append('subtree_as_list')
if subtree_as_ids:
query_params.append('subtree_as_ids')
if parents_as_list:
query_params.append('parents_as_list')
if parents_as_ids:
query_params.append('parents_as_ids')
query = self.build_key_only_query(query_params)
dict_args = {'project_id': base.getid(project)}
url = self.build_url(dict_args_in_out=dict_args)
p = self._get(url + query, self.key)
p.tags = self._encode_tags(getattr(p, 'tags', []))
return p
def find(self, **kwargs):
p = super(ProjectManager, self).find(**kwargs)
p.tags = self._encode_tags(getattr(p, 'tags', []))
return p
def update(self, project, name=None, domain=None, description=None,
enabled=None, **kwargs):
"""Update a project.
:param project: the project to be updated on the server.
:type project: str or :class:`keystoneclient.v3.projects.Project`
:param str name: the new name of the project.
:param domain: the new domain of the project.
:type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param str description: the new description of the project.
:param bool enabled: whether the project is enabled.
:param kwargs: any other attribute provided will be passed to server.
:returns: the updated project returned from server.
:rtype: :class:`keystoneclient.v3.projects.Project`
"""
return super(ProjectManager, self).update(
project_id=base.getid(project),
domain_id=base.getid(domain),
name=name,
description=description,
enabled=enabled,
**kwargs)
def delete(self, project):
"""Delete a project.
:param project: the project to be deleted on the server.
:type project: str or :class:`keystoneclient.v3.projects.Project`
:returns: Response object with 204 status.
:rtype: :class:`requests.models.Response`
"""
return super(ProjectManager, self).delete(
project_id=base.getid(project))
def _encode_tags(self, tags):
"""Encode tags to non-unicode string in python2.
:param tags: list of unicode tags
:returns: List of strings
"""
return [str(t) for t in tags]
def add_tag(self, project, tag):
"""Add a tag to a project.
:param project: project to add a tag to.
:param tag: str name of tag.
"""
url = "/projects/%s/tags/%s" % (base.getid(project),
urllib.parse.quote(tag))
self.client.put(url)
def update_tags(self, project, tags):
"""Update tag list of a project.
Replaces current tag list with list specified in tags parameter.
:param project: project to update.
:param tags: list of str tag names to add to the project
:returns: list of tags
"""
url = "/projects/%s/tags" % base.getid(project)
for tag in tags:
tag = urllib.parse.quote(tag)
resp, body = self.client.put(url, body={"tags": tags})
return body['tags']
def delete_tag(self, project, tag):
"""Remove tag from project.
:param projectd: project to remove tag from.
:param tag: str name of tag to remove from project
"""
self._delete(
"/projects/%s/tags/%s" % (base.getid(project),
urllib.parse.quote(tag)))
def list_tags(self, project):
"""List tags associated with project.
:param project: project to list tags for.
:returns: list of str tag names
"""
url = "/projects/%s/tags" % base.getid(project)
resp, body = self.client.get(url)
return self._encode_tags(body['tags'])
def check_tag(self, project, tag):
"""Check if tag is associated with project.
:param project: project to check tags for.
:param tag: str name of tag
:returns: true if tag is associated, false otherwise
"""
url = "/projects/%s/tags/%s" % (base.getid(project),
urllib.parse.quote(tag))
try:
self.client.head(url)
# no errors means found the tag
return True
except exceptions.NotFound:
# 404 means tag not in project
return False
|