/
consumer_groups.py
314 lines (262 loc) · 12 KB
/
consumer_groups.py
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
# -*- coding: utf-8 -*-
#
# Copyright © 2012 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the License
# (GPLv2) or (at your option) any later version.
# There is NO WARRANTY for this software, express or implied, including the
# implied warranties of MERCHANTABILITY, NON-INFRINGEMENT, or FITNESS FOR A
# PARTICULAR PURPOSE.
# You should have received a copy of GPLv2 along with this software; if not,
# see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
import web
from web.webapi import BadRequest
from pulp.server import exceptions as pulp_exceptions
from pulp.server.auth import authorization
from pulp.server.db.model.consumer import ConsumerGroup
from pulp.server.db.model.criteria import Criteria
from pulp.server.exceptions import MissingResource, InvalidValue
from pulp.server.managers import factory as managers_factory
from pulp.server.managers.consumer.group.cud import bind, unbind
from pulp.server.webservices import serialization
from pulp.server.webservices.controllers.base import JSONController
from pulp.server.webservices.controllers.decorators import auth_required
from pulp.server.webservices.controllers.search import SearchController
# consumer group collection ----------------------------------------------------
class ConsumerGroupCollection(JSONController):
@auth_required(authorization.READ)
def GET(self):
collection = ConsumerGroup.get_collection()
cursor = collection.find({})
groups = []
for group in cursor:
group.update(serialization.link.child_link_obj(group['id']))
groups.append(group)
return self.ok(groups)
@auth_required(authorization.CREATE)
def POST(self):
group_data = self.params()
group_id = group_data.pop('id', None)
if group_id is None:
raise pulp_exceptions.MissingValue(['id'])
display_name = group_data.pop('display_name', None)
description = group_data.pop('description', None)
consumer_ids = group_data.pop('consumer_ids', None)
notes = group_data.pop('notes', None)
if group_data:
raise pulp_exceptions.InvalidValue(group_data.keys())
manager = managers_factory.consumer_group_manager()
group = manager.create_consumer_group(group_id, display_name, description, consumer_ids,
notes)
group.update(serialization.link.child_link_obj(group['id']))
return self.created(group['_href'], group)
class ConsumerGroupSearch(SearchController):
def __init__(self):
super(ConsumerGroupSearch, self).__init__(
managers_factory.consumer_group_query_manager().find_by_criteria)
def GET(self):
items = self._get_query_results_from_get()
for item in items:
item.update(serialization.link.search_safe_link_obj(item['id']))
return self.ok(items)
def POST(self):
items = self._get_query_results_from_post()
for item in items:
item.update(serialization.link.search_safe_link_obj(item['id']))
return self.ok(items)
class ConsumerGroupResource(JSONController):
@auth_required(authorization.READ)
def GET(self, consumer_group_id):
collection = ConsumerGroup.get_collection()
group = collection.find_one({'id': consumer_group_id})
if group is None:
raise pulp_exceptions.MissingResource(consumer_group=consumer_group_id)
group.update(serialization.link.current_link_obj())
return self.ok(group)
@auth_required(authorization.DELETE)
def DELETE(self, consumer_group_id):
manager = managers_factory.consumer_group_manager()
result = manager.delete_consumer_group(consumer_group_id)
return self.ok(result)
@auth_required(authorization.UPDATE)
def PUT(self, consumer_group_id):
update_data = self.params()
manager = managers_factory.consumer_group_manager()
group = manager.update_consumer_group(consumer_group_id, **update_data)
group.update(serialization.link.current_link_obj())
return self.ok(group)
class ConsumerGroupAssociateAction(JSONController):
@auth_required(authorization.EXECUTE)
def POST(self, consumer_group_id):
criteria = Criteria.from_client_input(self.params().get('criteria', {}))
manager = managers_factory.consumer_group_manager()
manager.associate(consumer_group_id, criteria)
query_manager = managers_factory.consumer_group_query_manager()
group = query_manager.get_group(consumer_group_id)
return self.ok(group['consumer_ids'])
class ConsumerGroupUnassociateAction(JSONController):
@auth_required(authorization.EXECUTE)
def POST(self, consumer_group_id):
criteria = Criteria.from_client_input(self.params().get('criteria', {}))
manager = managers_factory.consumer_group_manager()
manager.unassociate(consumer_group_id, criteria)
query_manager = managers_factory.consumer_group_query_manager()
group = query_manager.get_group(consumer_group_id)
return self.ok(group['consumer_ids'])
class ConsumerGroupContentAction(JSONController):
@auth_required(authorization.CREATE)
def POST(self, consumer_group_id, action):
"""
Content actions.
"""
method = getattr(self, action, None)
if method:
return method(consumer_group_id)
else:
raise BadRequest()
def install(self, consumer_group_id):
"""
Install content (units) on the consumers in a consumer group.
Expected body: {units:[], options:<dict>}
where unit is: {type_id:<str>, unit_key={}} and the
options is a dict of install options.
@param consumer_group_id: A consumer group ID.
@type consumer_group_id: str
@return: list of call requests
@rtype: list
"""
body = self.params()
units = body.get('units')
options = body.get('options')
task = managers_factory.consumer_group_manager().install_content(consumer_group_id,
units, options)
raise pulp_exceptions.OperationPostponed(task)
def update(self, consumer_group_id):
"""
Update content (units) on the consumer in a consumer group.
Expected body: {units:[], options:<dict>}
where unit is: {type_id:<str>, unit_key={}} and the
options is a dict of update options.
@param consumer_group_id: A consumer group ID.
@type consumer_group_id: str
@return: list of call requests
@rtype: list
"""
body = self.params()
units = body.get('units')
options = body.get('options')
task = managers_factory.consumer_group_manager().update_content(consumer_group_id,
units, options)
raise pulp_exceptions.OperationPostponed(task)
def uninstall(self, consumer_group_id):
"""
Uninstall content (units) from the consumers in a consumer group.
Expected body: {units:[], options:<dict>}
where unit is: {type_id:<str>, unit_key={}} and the
options is a dict of uninstall options.
@param consumer_group_id: A consumer group ID.
@type consumer_group_id: str
@return: list of call requests
@rtype: list
"""
body = self.params()
units = body.get('units')
options = body.get('options')
task = managers_factory.consumer_group_manager().uninstall_content(consumer_group_id,
units, options)
raise pulp_exceptions.OperationPostponed(task)
class ConsumerGroupBindings(JSONController):
@auth_required(authorization.CREATE)
def POST(self, group_id):
"""
Create a bind association between the consumers belonging to the given
consumer group by id included in the URL path and a repo-distributor
specified in the POST body: {repo_id:<str>, distributor_id:<str>}.
Designed to be idempotent so only MissingResource is expected to
be raised by manager.
:param group_id: The consumer group to bind.
:type group_id: str
:return: list of call requests
:rtype: list
"""
body = self.params()
repo_id = body.get('repo_id')
distributor_id = body.get('distributor_id')
binding_config = body.get('binding_config', None)
options = body.get('options', {})
notify_agent = body.get('notify_agent', True)
missing_resources = verify_group_resources(group_id, repo_id, distributor_id)
if missing_resources:
if 'group_id' in missing_resources:
raise MissingResource(**missing_resources)
else:
raise InvalidValue(list(missing_resources))
bind_args_tuple = (group_id, repo_id, distributor_id, notify_agent, binding_config,
options)
async_task = bind.apply_async(bind_args_tuple)
raise pulp_exceptions.OperationPostponed(async_task)
class ConsumerGroupBinding(JSONController):
"""
Represents a specific consumer group binding.
"""
@auth_required(authorization.DELETE)
def DELETE(self, group_id, repo_id, distributor_id):
"""
Delete a bind association between the consumers belonging to the specified
consumer group and repo-distributor. Designed to be idempotent.
:param group_id: A consumer group ID.
:type group_id: str
:param repo_id: A repo ID.
:type repo_id: str
:param distributor_id: A distributor ID.
:type distributor_id: str
:return: list of call requests
:rtype: list
"""
missing_resources = verify_group_resources(group_id, repo_id, distributor_id)
if missing_resources:
raise MissingResource(**missing_resources)
unbind_args_tuple = (group_id, repo_id, distributor_id, {})
async_task = unbind.apply_async(unbind_args_tuple)
raise pulp_exceptions.OperationPostponed(async_task)
def verify_group_resources(group_id, repo_id, distributor_id):
"""
Confirm the group, repository, and distributor exist
:param group_id: The consumer group id to verify the existence of
:type group_id: str
:param repo_id: The repository id to confirm the existence of
:type repo_id: str
:param distributor_id: The distributor id to confirm the existence of on the repository
:type distributor_id: str
:return: A dictionary of the missing resources
:rtype: dict
"""
missing_resources = {}
group_manager = managers_factory.consumer_group_query_manager()
repo_manager = managers_factory.repo_query_manager()
distributor_manager = managers_factory.repo_distributor_manager()
try:
group_manager.get_group(group_id)
except MissingResource:
missing_resources['group_id'] = group_id
repo = repo_manager.find_by_id(repo_id)
if repo is None:
missing_resources['repo_id'] = repo_id
try:
distributor_manager.get_distributor(repo_id, distributor_id)
except MissingResource:
missing_resources['distributor_id'] = distributor_id
return missing_resources
# web.py application -----------------------------------------------------------
_URLS = ('/$', ConsumerGroupCollection,
'/search/$', ConsumerGroupSearch, # resource search
'/([^/]+)/$', ConsumerGroupResource,
'/([^/]+)/bindings/$', ConsumerGroupBindings,
'/([^/]+)/bindings/([^/]+)/([^/]+)/$', ConsumerGroupBinding,
'/([^/]+)/actions/associate/$', ConsumerGroupAssociateAction,
'/([^/]+)/actions/unassociate/$', ConsumerGroupUnassociateAction,
'/([^/]+)/actions/content/(install|update|uninstall)/$',
ConsumerGroupContentAction,
)
application = web.application(_URLS, globals())