Package pulp :: Package server :: Package webservices :: Package controllers :: Module repositories
[hide private]
[frames] | no frames]

Source Code for Module pulp.server.webservices.controllers.repositories

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # Copyright © 2010 Red Hat, Inc. 
  5  # 
  6  # This software is licensed to you under the GNU General Public License, 
  7  # version 2 (GPLv2). There is NO WARRANTY for this software, express or 
  8  # implied, including the implied warranties of MERCHANTABILITY or FITNESS 
  9  # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 
 10  # along with this software; if not, see 
 11  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 
 12  # 
 13  # Red Hat trademarks are not licensed under GPLv2. No permission is 
 14  # granted to use or replicate Red Hat trademarks that are incorporated 
 15  # in this software or its documentation. 
 16   
 17  import itertools 
 18  import logging 
 19   
 20  import web 
 21   
 22  from pulp.server.api.repo import RepoApi 
 23  from pulp.server.api.repo_sync import yum_rhn_progress_callback 
 24  from pulp.server.webservices import http 
 25  from pulp.server.webservices import mongo 
 26  from pulp.server.webservices.controllers.base import JSONController, AsyncController 
 27  from pulp.server.webservices.role_check import RoleCheck 
 28   
 29  # globals --------------------------------------------------------------------- 
 30   
 31  api = RepoApi() 
 32  log = logging.getLogger('pulp') 
 33   
 34  # default fields for repositories being sent to the client 
 35  default_fields = [ 
 36      'id', 
 37      'source', 
 38      'name',  
 39      'arch',  
 40      'sync_schedule',  
 41      'use_symlinks',  
 42      'productid', 
 43      'relative_path',] 
44 45 # restful controllers --------------------------------------------------------- 46 47 -class Repositories(JSONController):
48 49 @JSONController.error_handler 50 @RoleCheck(admin=True)
51 - def GET(self):
52 """ 53 List all available repositories. 54 @return: a list of all available repositories 55 """ 56 valid_filters = ['id', 'name', 'arch', 'productid'] 57 58 filters = self.filters(valid_filters) 59 spec = mongo.filters_to_re_spec(filters) 60 61 repositories = api.repositories(spec, default_fields) 62 63 for repo in repositories: 64 repo['uri_ref'] = http.extend_uri_path(repo['id']) 65 for field in RepositoryDeferredFields.exposed_fields: 66 repo[field] = http.extend_uri_path('/'.join((repo['id'], field))) 67 68 return self.ok(repositories)
69 70 @JSONController.error_handler 71 @RoleCheck(admin=True)
72 - def PUT(self):
73 """ 74 Create a new repository. 75 @return: repository meta data on successful creation of repository 76 """ 77 repo_data = self.params() 78 79 id = repo_data['id'] 80 if api.repository(id, default_fields) is not None: 81 return self.conflict('A repository with the id, %s, already exists' % id) 82 83 repo = api.create(id, 84 repo_data['name'], 85 repo_data['arch'], 86 feed=repo_data.get('feed', None), 87 symlinks=repo_data.get('use_symlinks', False), 88 sync_schedule=repo_data.get('sync_schedule', None), 89 cert_data=repo_data.get('cert_data', None)) 90 91 path = http.extend_uri_path(repo.id) 92 repo['uri_ref'] = path 93 return self.created(path, repo)
94 95 @JSONController.error_handler 96 @RoleCheck(admin=True)
97 - def DELETE(self):
98 """ 99 @return: True on successful deletion of all repositories 100 """ 101 api.clean() 102 return self.ok(True)
103
104 105 -class Repository(JSONController):
106 107 @JSONController.error_handler 108 @RoleCheck(consumer=True, admin=True)
109 - def GET(self, id):
110 """ 111 Get information on a single repository. 112 @param id: repository id 113 @return: repository meta data 114 """ 115 repo = api.repository(id, default_fields) 116 if repo is None: 117 return self.not_found('No repository %s' % id) 118 for field in RepositoryDeferredFields.exposed_fields: 119 repo[field] = http.extend_uri_path(field) 120 repo['uri_ref'] = http.uri_path() 121 return self.ok(repo)
122 123 @JSONController.error_handler 124 @RoleCheck(admin=True, consumer=True)
125 - def PUT(self, id):
126 """ 127 Change a repository. 128 @param id: repository id 129 @return: True on successful update of repository meta data 130 """ 131 repo_data = self.params() 132 if repo_data['id'] != id: 133 return self.bad_request('You cannot change a repository id') 134 # we need to remove the substituted uri references 135 # XXX we probably need to add the original data back as well 136 for field in itertools.chain(['uri_ref'], # web services only field 137 RepositoryDeferredFields.exposed_fields): 138 if field in repo_data and isinstance(repo_data[field], basestring): 139 repo_data.pop(field, None) 140 api.update(repo_data) 141 return self.ok(True)
142 143 @JSONController.error_handler 144 @RoleCheck(admin=True)
145 - def DELETE(self, id):
146 """ 147 Delete a repository. 148 @param id: repository id 149 @return: True on successful deletion of repository 150 """ 151 api.delete(id=id) 152 return self.ok(True)
153
154 155 -class RepositoryDeferredFields(JSONController):
156 157 # NOTE the intersection of exposed_fields and exposed_actions must be empty 158 exposed_fields = ( 159 'packages', 160 'packagegroups', 161 'packagegroupcategories', 162 'errata' 163 ) 164 165 @JSONController.error_handler
166 - def packages(self, id):
167 valid_filters = ('name', 'arch') 168 filters = self.filters(valid_filters) 169 repo = api.repository(id, ['id', 'packages']) 170 if repo is None: 171 return self.not_found('No repository %s' % id) 172 filtered_packages = self.filter_results(repo.get('packages', []), filters) 173 return self.ok(filtered_packages)
174 175 @JSONController.error_handler
176 - def packagegroups(self, id):
177 repo = api.repository(id, ['id', 'packagegroups']) 178 if repo is None: 179 return self.not_found('No repository %s' % id) 180 return self.ok(repo.get('packagegroups'))
181 182 @JSONController.error_handler
183 - def packagegroupcategories(self, id):
184 repo = api.repository(id, ['id', 'packagegroupcategories']) 185 if repo is None: 186 return self.not_found('No repository %s' % id) 187 return self.ok(repo.get('packagegroupcategories', []))
188 189 @JSONController.error_handler
190 - def errata(self, id):
191 """ 192 list applicable errata for a given repo. 193 filter by errata type if any 194 """ 195 valid_filters = ('type') 196 types = self.filters(valid_filters)['type'] 197 return self.ok(api.errata(id, types))
198 199 @JSONController.error_handler 200 @RoleCheck(consumer=True, admin=True)
201 - def GET(self, id, field_name):
202 field = getattr(self, field_name, None) 203 if field is None: 204 return self.internal_server_error('No implementation for %s found' % field_name) 205 return field(id)
206
207 208 -class RepositoryActions(AsyncController):
209 210 # All actions have been gathered here into one controller class for both 211 # convenience and automatically generate the regular expression that will 212 # map valid actions to this class. This also provides a single point for 213 # querying existing tasks. 214 # 215 # There are two steps to implementing a new action: 216 # 1. The action name must be added to the tuple of exposed_actions 217 # 2. You must add a method to this class with the same name as the action 218 # that takes two positional arguments: 'self' and 'id' where id is the 219 # the repository id. Additional parameters from the body can be 220 # fetched and de-serialized via the self.params() call. 221 222 # NOTE the intersection of exposed_actions and exposed_fields must be empty 223 exposed_actions = ( 224 'sync', 225 'upload', 226 'add_package', 227 'get_package', 228 'add_packages_to_group', 229 'delete_package_from_group', 230 'delete_packagegroup', 231 'create_packagegroup', 232 'add_errata', 233 'list_errata', 234 'delete_errata', 235 ) 236 237 @JSONController.error_handler 238 @RoleCheck(admin=True)
239 - def sync(self, id):
240 """ 241 Sync a repository from its feed. 242 @param id: repository id 243 @return: True on successful sync of repository from feed 244 """ 245 timeout = self.timeout(self.params()) 246 task = self.start_task(api.sync, [id], timeout=timeout, unique=True) 247 if not task: 248 return self.conflict('Sync already in process for repo [%s]' % id) 249 repo = api.repository(id, fields=['source']) 250 if repo['source'] is not None and repo['source']['type'] in ('yum', 'rhn'): 251 task.set_progress('progress_callback', yum_rhn_progress_callback) 252 task_info = self._task_to_dict(task) 253 task_info['status_path'] = self._status_path(task.id) 254 return self.accepted(task_info)
255 256 @JSONController.error_handler 257 @RoleCheck(admin=True)
258 - def upload(self, id):
259 """ 260 Upload a package to a repository. 261 @param id: repository id 262 @return: True on successful upload 263 """ 264 data = self.params() 265 api.upload(id, 266 data['pkginfo'], 267 data['pkgstream']) 268 return self.ok(True)
269 270 @JSONController.error_handler 271 @RoleCheck(admin=True)
272 - def add_package(self, id):
273 """ 274 @param id: repository id 275 @return: True on successful addition of package to repository 276 """ 277 data = self.params() 278 api.add_package(id, data['packageid']) 279 return self.ok(True)
280 281 @JSONController.error_handler 282 @RoleCheck(admin=True)
283 - def get_package(self, id):
284 """ 285 Get package info from a repository. 286 @deprecated: user deferred fields: packages with filters instead 287 @param id: repository id 288 @return: matched package object available in corresponding repository 289 """ 290 name = self.params() 291 return self.ok(api.get_package(id, name))
292 293 @JSONController.error_handler 294 @RoleCheck(admin=True)
295 - def add_packages_to_group(self, id):
296 """ 297 Add a package to an existing package group 298 @param id: repository id 299 @return: True/False 300 """ 301 p = self.params() 302 if "groupid" not in p: 303 return self.not_found('No groupid specified') 304 if "packagenames" not in p: 305 return self.not_found('No package name specified') 306 groupid = p["groupid"] 307 pkg_names = p.get('packagenames', []) 308 gtype = "default" 309 if p.has_key("type"): 310 gtype = p["type"] 311 return self.ok(api.add_packages_to_group(id, groupid, pkg_names, gtype))
312 313 @JSONController.error_handler 314 @RoleCheck(admin=True)
315 - def delete_package_from_group(self, id):
316 """ 317 Removes a package from an existing package group 318 @param id: repository id 319 @return: True/False 320 """ 321 p = self.params() 322 if "groupid" not in p: 323 return self.not_found('No groupid specified') 324 if "name" not in p: 325 return self.not_found('No package name specified') 326 groupid = p["groupid"] 327 pkg_name = p["name"] 328 gtype = "default" 329 if p.has_key("type"): 330 gtype = p["type"] 331 return self.ok(api.delete_package_from_group(id, groupid, pkg_name, gtype))
332 333 @JSONController.error_handler 334 @RoleCheck(admin=True)
335 - def create_packagegroup(self, id):
336 """ 337 Creates a packagegroup in the referenced repository 338 @param id: repository id 339 @return: 340 """ 341 p = self.params() 342 if "groupid" not in p: 343 return self.not_found('No groupid specified') 344 groupid = p["groupid"] 345 if "groupname" not in p: 346 return self.not_found('No groupname specified') 347 groupname = p["groupname"] 348 if "description" not in p: 349 return self.not_found('No description specified') 350 descrp = p["description"] 351 return self.ok(api.create_packagegroup(id, groupid, groupname, 352 descrp))
353 354 @JSONController.error_handler 355 @RoleCheck(admin=True)
356 - def delete_packagegroup(self, id):
357 """ 358 Removes a packagegroup from a repository 359 @param id: repository id 360 @return: 361 """ 362 p = self.params() 363 if "groupid" not in p: 364 return self.not_found('No groupid specified') 365 groupid = p["groupid"] 366 return self.ok(api.delete_packagegroup(id, groupid))
367 368 @JSONController.error_handler 369 @RoleCheck(admin=True)
370 - def add_errata(self, id):
371 """ 372 @param id: repository id 373 @return: True on successful addition of errata to repository 374 """ 375 data = self.params() 376 api.add_errata(id, data['errataid']) 377 return self.ok(True)
378 379 @JSONController.error_handler 380 @RoleCheck(admin=True)
381 - def delete_errata(self, id):
382 """ 383 @param id: repository id 384 @return: True on successful deletion of errata from repository 385 """ 386 data = self.params() 387 api.delete_errata(id, data['errataid']) 388 return self.ok(True)
389 390 @JSONController.error_handler 391 @RoleCheck(admin=True)
392 - def list_errata(self, id):
393 """ 394 list applicable errata for a given repo. 395 filter by errata type if any 396 """ 397 data = self.params() 398 return self.ok(api.errata(id, data['types']))
399 400 @JSONController.error_handler 401 @RoleCheck(admin=True)
402 - def POST(self, id, action_name):
403 """ 404 Action dispatcher. This method checks to see if the action is exposed, 405 and if so, implemented. It then calls the corresponding method (named 406 the same as the action) to handle the request. 407 @type id: str 408 @param id: repository id 409 @type action_name: str 410 @param action_name: name of the action 411 @return: http response 412 """ 413 repo = api.repository(id, fields=['id']) 414 if not repo: 415 return self.not_found('No repository with id %s found' % id) 416 action = getattr(self, action_name, None) 417 if action is None: 418 return self.internal_server_error('No implementation for %s found' % action_name) 419 return action(id)
420
421 422 -class RepositoryActionStatus(AsyncController):
423 424 @JSONController.error_handler 425 @RoleCheck(admin=True)
426 - def GET(self, id, action_name, action_id):
427 """ 428 Check the status of a sync operation. 429 @param id: repository id 430 @param action_name: name of the action 431 @param action_id: action id 432 @return: action status information 433 """ 434 task_info = self.task_status(action_id) 435 if task_info is None: 436 return self.not_found('No %s with id %s found' % (action_name, action_id)) 437 return self.ok(task_info)
438 439 @JSONController.error_handler 440 @RoleCheck(admin=True)
441 - def DELETE(self, id, action_name, action_id):
442 """ 443 Cancel an action 444 """ 445 task = self.find_task(action_id) 446 if task is None: 447 return self.not_found('No %s with id %s found' % (action_name, action_id)) 448 if self.cancel_task(task): 449 return self.accepted(self._task_to_dict(task)) 450 # action is complete and, therefore, not canceled 451 # a no-content return means the client should *not* adjust its view of 452 # the resource 453 return self.no_content()
454
455 456 -class Schedules(JSONController):
457 458 @JSONController.error_handler 459 @RoleCheck(admin=True)
460 - def GET(self):
461 ''' 462 Retrieve a map of all repository IDs to their associated synchronization 463 schedules. 464 465 @return: key - repository ID, value - synchronization schedule 466 ''' 467 # XXX this returns all scheduled tasks, it should only return those 468 # tasks that are specified by the action_name 469 schedules = api.all_schedules() 470 return self.ok(schedules)
471 472 # web.py application ---------------------------------------------------------- 473 474 urls = ( 475 '/$', 'Repositories', 476 '/schedules/', 'Schedules', 477 '/([^/]+)/$', 'Repository', 478 479 '/([^/]+)/(%s)/$' % '|'.join(RepositoryDeferredFields.exposed_fields), 480 'RepositoryDeferredFields', 481 482 '/([^/]+)/(%s)/$' % '|'.join(RepositoryActions.exposed_actions), 483 'RepositoryActions', 484 485 '/([^/]+)/(%s)/([^/]+)/$' % '|'.join(RepositoryActions.exposed_actions), 486 'RepositoryActionStatus', 487 ) 488 489 application = web.application(urls, globals()) 490