def _activate(self, req, image_id, location_data, from_state=None): """ Sets the image status to `active` and the image's location attribute. :param req: The WSGI/Webob Request object :param image_id: Opaque image identifier :param location_data: Location of where Glance stored this image """ image_meta = {} image_meta['location'] = location_data['url'] image_meta['status'] = 'active' image_meta['location_data'] = [location_data] try: s = from_state image_meta_data = registry.update_image_metadata(req.context, image_id, image_meta, from_state=s) self.notifier.info("image.activate", redact_loc(image_meta_data)) self.notifier.info("image.update", redact_loc(image_meta_data)) return image_meta_data except exception.Duplicate: with excutils.save_and_reraise_exception(): # Delete image data since it has been supersceded by another # upload and re-raise. LOG.debug("duplicate operation - deleting image data for " " %(id)s (location:%(location)s)" % {'id': image_id, 'location': image_meta['location']}) upload_utils.initiate_deletion(req, image_meta['location'], image_id, CONF.delayed_delete) except exception.Invalid as e: msg = ("Failed to activate image. Got error: %s" % utils.exception_to_str(e)) LOG.debug(msg) raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain")
def detail(self, req): """ Returns detailed information for all available images :param req: The WSGI/Webob Request object :retval The response body is a mapping of the following form:: {'images': [ {'id': <ID>, 'name': <NAME>, 'size': <SIZE>, 'disk_format': <DISK_FORMAT>, 'container_format': <CONTAINER_FORMAT>, 'checksum': <CHECKSUM>, 'min_disk': <MIN_DISK>, 'min_ram': <MIN_RAM>, 'store': <STORE>, 'status': <STATUS>, 'created_at': <TIMESTAMP>, 'updated_at': <TIMESTAMP>, 'deleted_at': <TIMESTAMP>|<NONE>, 'properties': {'distro': 'Ubuntu 10.04 LTS', ...}}, ... ]} """ self._enforce(req, 'get_images') params = self._get_query_params(req) try: images = registry.get_images_detail(req.context, **params) # Strip out the Location attribute. Temporary fix for # LP Bug #755916. This information is still coming back # from the registry, since the API server still needs access # to it, however we do not return this potential security # information to the API end user... for image in images: redact_loc(image, copy_dict=False) self._enforce_read_protected_props(image, req) except exception.Invalid as e: raise HTTPBadRequest(explanation="%s" % e) return dict(images=images)
def _reserve(self, req, image_meta): """ Adds the image metadata to the registry and assigns an image identifier if one is not supplied in the request headers. Sets the image's status to `queued`. :param req: The WSGI/Webob Request object :param id: The opaque image identifier :raises HTTPConflict if image already exists :raises HTTPBadRequest if image metadata is not valid """ location = image_meta.get('location') if location: store = get_store_from_location(location) # check the store exists before we hit the registry, but we # don't actually care what it is at this point self.get_store_or_400(req, store) image_meta['status'] = 'queued' # Ensure that the size attribute is set to zero for all # queued instances. The size will be set to a non-zero # value during upload image_meta['size'] = image_meta.get('size', 0) try: image_meta = registry.add_image_metadata(req.context, image_meta) return image_meta except exception.Duplicate: msg = (_("An image with identifier %s already exists") % image_meta['id']) logger.error(msg) raise HTTPConflict(msg, request=req, content_type="text/plain") except exception.Invalid, e: msg = (_("Failed to reserve image. Got error: %(e)s") % locals()) for line in msg.split('\n'): logger.error(line) raise HTTPBadRequest(msg, request=req, content_type="text/plain")
def dispatch(self, replicate_args, args): if not hasattr(args, 'pop'): return HTTPBadRequest(body='Invalid object type') op = args.pop(0) drive, partition, hsh = replicate_args if self.mount_check and \ not os.path.ismount(os.path.join(self.root, drive)): return Response(status='507 %s is not mounted' % drive) db_file = os.path.join(self.root, drive, storage_directory(self.datadir, partition, hsh), hsh + '.db') if op == 'rsync_then_merge': return self.rsync_then_merge(drive, db_file, args) if op == 'complete_rsync': return self.complete_rsync(drive, db_file, args) else: # someone might be about to rsync a db to us, # make sure there's a tmp dir to receive it. mkdirs(os.path.join(self.root, drive, 'tmp')) if not os.path.exists(db_file): return HTTPNotFound() return getattr(self, op)(self.broker_class(db_file), args)
def handle_request(self, req): """ Entry point for auth requests (ones that match the self.auth_prefix). Should return a WSGI-style callable (such as webob.Response). :param req: webob.Request object """ req.start_time = time() handler = None try: version, account, user, _junk = split_path(req.path_info, minsegs=1, maxsegs=4, rest_with_last=True) except ValueError: return HTTPNotFound(request=req) if version in ('v1', 'v1.0', 'auth'): if req.method == 'GET': handler = self.handle_get_token if not handler: req.response = HTTPBadRequest(request=req) else: req.response = handler(req) return req.response
def _activate(self, req, image_id, location): """ Sets the image status to `active` and the image's location attribute. :param req: The WSGI/Webob Request object :param image_id: Opaque image identifier :param location: Location of where Glance stored this image """ image_meta = {} image_meta['location'] = location image_meta['status'] = 'active' try: return registry.update_image_metadata(req.context, image_id, image_meta) except exception.Invalid, e: msg = (_("Failed to activate image. Got error: %(e)s") % locals()) for line in msg.split('\n'): logger.error(line) self.notifier.error('image.update', msg) raise HTTPBadRequest(msg, request=req, content_type="text/plain")
def _zero(self, ticket_id, msg): size = validate.integer(msg, "size", minval=0) offset = validate.integer(msg, "offset", minval=0, default=0) flush = validate.boolean(msg, "flush", default=False) ticket = tickets.authorize(ticket_id, "write", offset, size) self.log.info("[%s] ZERO size=%d offset=%d flush=%s ticket=%s", web.client_address(self.request), size, offset, flush, ticket_id) op = directio.Zero(ticket.url.path, size, offset=offset, flush=flush, buffersize=self.config.daemon.buffer_size, clock=self.clock, sparse=ticket.sparse) try: ticket.run(op) except errors.PartialContent as e: raise HTTPBadRequest(str(e)) return web.response()
def h_form(self, env, data): letter_model = self._get_model() form = self._get_form() is_iframe = 'iframe' in env.request.POST if env.request.method == 'POST': response_data = {'valid': None, 'errors': None, 'message': None} if form.accept(env.request.POST): preview = getattr(env.app.cfg, 'PREVIEW', None) if preview: raise HTTPBadRequest("Form submit is forbidden in preview") flood_msg = self._check_flood(env, self.name) if flood_msg: response_data['message'] = flood_msg response_data['valid'] = True return env.json(response_data) letter = letter_model(dt=datetime.now(), section_id=self.section.id, letter_json=form.json_prepared) env.db.add(letter) env.db.commit() letter_id = '{}_{}'.format(self.env.cfg.APP_ID, letter.id) response_data['success'] = True response_data['message'] = self._format_success_msg(letter_id) response_data['valid'] = form.is_valid response_data['errors'] = form.errors to_json = iframe_json if is_iframe else env.json return to_json(response_data) else: return self.response.template( 'form', dict( letter=self.section, form=form, ))
def import_json_to_template(self, req, template): template_id = "" template = json.loads(template.get('template', None)) template_cluster = copy.deepcopy(template) template_name = template_cluster.get('name', None) template_params = {'filters': {'name': template_name}} try: if template_cluster.get('content', None): template_cluster['content'] = json.dumps( template_cluster['content']) if template_cluster.get('hosts', None): template_cluster['hosts'] = json.dumps( template_cluster['hosts']) else: template_cluster['hosts'] = "[]" template_list = registry.template_lists_metadata( req.context, **template_params) if template_list: registry.update_template_metadata(req.context, template_list[0]['id'], template_cluster) template_id = template_list[0]['id'] else: add_template_cluster = registry.add_template_metadata( req.context, template_cluster) template_id = add_template_cluster['id'] if template_id: template_detail = registry.template_detail_metadata( req.context, template_id) del template_detail['deleted'] del template_detail['deleted_at'] except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req) return {"template": template_detail}
def get(self, task_id): if not task_id: raise HTTPBadRequest("Task id is required") status = 200 self.log.info("Retrieving task %s", task_id) try: ctasks = celery.result.AsyncResult(task_id, app=celery_tasks.app) except KeyError: raise HTTPNotFound("No such task %r" % task_id) result = ctasks.result if not result: result = {} if isinstance(result, Exception): result = {'Exception': result.message} result['status'] = ctasks.status return response(payload=result)
def _validate_size(self, params, volume_type, backup=None, source=None): try: size = int(params['size']) except KeyError: raise HTTPBadRequest("Must specify 'size' parameter") except ValueError: raise HTTPPreconditionFailed("'size' parameter must be an " "integer") if size < volume_type.min_size or size > volume_type.max_size: raise HTTPPreconditionFailed( "'size' parameter must be between " "%s and %s" % (volume_type.min_size, volume_type.max_size)) if backup: if size < backup.size: msg = "'size' must be >= backup size: %d" % backup.size raise HTTPPreconditionFailed(msg) if source: if size < source.size: msg = "'size' must be >= source volume size: %d" % source.size raise HTTPPreconditionFailed(msg) return size
def detail(self, req): """ Returns detailed information for all available services :param req: The WSGI/Webob Request object :retval The response body is a mapping of the following form:: {'services': [ {'id': <ID>, 'name': <NAME>, 'description': <DESCRIPTION>, 'created_at': <TIMESTAMP>, 'updated_at': <TIMESTAMP>, 'deleted_at': <TIMESTAMP>|<NONE>,}, ... ]} """ self._enforce(req, 'get_services') params = self._get_query_params(req) try: services = registry.get_services_detail(req.context, **params) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req) return dict(services=services)
def _handle_source(self, req, image_id, image_meta, image_data): copy_from = self._copy_from(req) location = image_meta.get('location') sources = filter(lambda x: x, (copy_from, location, image_data)) if len(sources) >= 2: msg = _("It's invalid to provide multiple image sources.") LOG.debug(msg) raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain") if image_data: image_meta = self._validate_image_for_activation( req, image_id, image_meta) image_meta = self._upload_and_activate(req, image_meta) elif copy_from: msg = _('Triggering asynchronous copy from external source') LOG.info(msg) self.pool.spawn_n(self._upload_and_activate, req, image_meta) else: if location: self._validate_image_for_activation(req, image_id, image_meta) image_meta = self._activate(req, image_id, location) return image_meta
def list_version(self, req): """ Returns list version information for all available versions :param req: The WSGI/Webob Request object :retval The response body is a mapping of the following form:: {'versions': [ {'id': <ID>, 'name': <NAME>, 'description': <DESCRIPTION>, 'created_at': <TIMESTAMP>, 'updated_at': <TIMESTAMP>, 'deleted_at': <TIMESTAMP>|<NONE>,}, ... ]} """ self._enforce(req, 'list_version') params = self._get_query_params(req) try: versions = registry.list_version_metadata(req.context, **params) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req) return dict(versions=versions)
def _do_sync(self, request, payload_json): """ The actual meat of the sync work happens here. """ payload = json.loads(payload_json) repo_data = payload.get('repository', {}) repo_name = pipes.quote(repo_data.get('name')) ref = payload.get('ref') if not repo_name or not ref: raise HTTPBadRequest('Missing or malformed payload') branchname = pipes.quote(ref.split('/')[-1]) repos_path = request.config['repos_path'] repo_path = os.path.join(repos_path, repo_name) hgbin = os.path.join(request.config['bin'], 'hg') if not os.path.isdir(repo_path): # have to clone the repo os.chdir(repos_path) git_repo_url = '/'.join( [request.config['git_base_url'], '%s.git' % repo_name]) # nonzero return code == failure if subprocess.call([hgbin, 'clone', git_repo_url]): return self.abort(payload_json) # have to append the hg repo to the paths hgrc_path = os.path.join(repo_path, '.hg', 'hgrc') with open(hgrc_path, 'a') as hgrc_appender: hg_repo_url = '/'.join( [request.config['hg_base_url'], repo_name]) hgrc_appender.write('\nmoz = %s' % hg_repo_url) os.chdir(repo_path) # should default to original github repo if subprocess.call([hgbin, 'pull']) or subprocess.call([hgbin, 'up']): return self.abort(payload_json) # explicitly push to the 'moz' path we set up if subprocess.call([hgbin, 'push', '-B', branchname, 'moz']): return self.abort(payload_json) return 'repo cloned'
def list_backup_file(self, req): """ Returns detailed information for all available backup files :param req: The WSGI/Webob Request object :retval The response body is a mapping of the following form:: {'backup_files': [ {'id': <ID>, 'name': <NAME>, 'create_time': <TIMESTAMP>}, ... ]} """ self._enforce(req, 'list_backup_file') params = self._get_query_params(req) try: backup_files = [] if not os.path.exists(self.auto_back_path): return dict(backup_files=backup_files) for item in os.listdir(self.auto_back_path): path = os.path.join(self.auto_back_path, item) if not os.path.isfile(path): continue backup_file = { "id": item, "file_name": item, "create_time": os.path.getctime(path) } backup_files.append(backup_file) backup_files.sort(key=lambda x: x['create_time'], reverse=True) for backup_file in backup_files: backup_file['create_time'] = time.strftime( "%Y-%m-%d %H:%M:%S", time.localtime(backup_file['create_time'])) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req) return dict(backup_files=backup_files)
def endpoint(self, request): log.debug('client from %s requested: %s' % (request.remote_addr, request.params)) if 'api' not in request.GET: raise HTTPBadRequest() result = Struct({'api_version': API_VERSION, 'auth': 0}) #@@TODO format = 'xml' if request.GET['api'] == 'xml' else 'json' if 'api_key' in request.POST: api_key = request.POST['api_key'] try: user = User.get((User.api_key == api_key) & (User.is_enabled == True)) except User.DoesNotExist: #@@TODO: raise HTTPUnauthorized ? log.warn('unknown API key %s, unauthorized' % api_key) return self.respond_with_json(result) else: #@@TODO: raise HTTPUnauthorized ? log.warn('missing API key, unauthorized') return self.respond_with_json(result) # Authorized result.auth = 1 # Note: client *can* send multiple commands at time for command, handler in COMMANDS: if command in request.params: handler(request, user, result) #break result.last_refreshed_on_time = get_last_refreshed_on_time() return self.respond_with_json(result)
class AccountController(object): """WSGI controller for the account server.""" def __init__(self, conf): self.logger = get_logger(conf, log_route='account-server') self.root = conf.get('devices', '/srv/node') self.mount_check = conf.get('mount_check', 'true').lower() in \ ('true', 't', '1', 'on', 'yes', 'y') self.replicator_rpc = ReplicatorRpc(self.root, DATADIR, AccountBroker, self.mount_check, logger=self.logger) self.auto_create_account_prefix = \ conf.get('auto_create_account_prefix') or '.' def _get_account_broker(self, drive, part, account): hsh = hash_path(account) db_dir = storage_directory(DATADIR, part, hsh) db_path = os.path.join(self.root, drive, db_dir, hsh + '.db') return AccountBroker(db_path, account=account, logger=self.logger) def DELETE(self, req): """Handle HTTP DELETE request.""" try: drive, part, account = split_path(unquote(req.path), 3) except ValueError, err: return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) if self.mount_check and not check_mount(self.root, drive): return Response(status='507 %s is not mounted' % drive) if 'x-timestamp' not in req.headers or \ not check_float(req.headers['x-timestamp']): return HTTPBadRequest(body='Missing timestamp', request=req, content_type='text/plain') broker = self._get_account_broker(drive, part, account) if broker.is_deleted(): return HTTPNotFound(request=req) broker.delete_db(req.headers['x-timestamp']) return HTTPNoContent(request=req)
def service_disk_update(self, req, id, disk_meta): self._enforce(req, 'service_disk_update') self._service_disk_update_meta_valid(req, id, disk_meta) try: service_disk_meta = registry.update_service_disk_metadata( req.context, id, disk_meta) except exception.Invalid as e: msg = (_("Failed to update role metadata. Got error: %s") % utils.exception_to_str(e)) LOG.warning(msg) raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain") except exception.NotFound as e: msg = (_("Failed to find role to update: %s") % utils.exception_to_str(e)) LOG.warning(msg) raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") except exception.Forbidden as e: msg = (_("Forbidden to update role: %s") % utils.exception_to_str(e)) LOG.warning(msg) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") except (exception.Conflict, exception.Duplicate) as e: LOG.warning(utils.exception_to_str(e)) raise HTTPConflict(body=_('Host operation conflicts'), request=req, content_type='text/plain') else: self.notifier.info('role.update', service_disk_meta) return {'disk_meta': service_disk_meta}
def handle_token(self, request): """ Handles ReST requests from Swift to validate tokens Valid URL paths: * GET /token/<token> If the HTTP request returns with a 204, then the token is valid, the TTL of the token will be available in the X-Auth-Ttl header, and a comma separated list of the "groups" the user belongs to will be in the X-Auth-Groups header. :param request: webob.Request object """ try: _junk, token = split_path(request.path, minsegs=2) except ValueError: return HTTPBadRequest() # Retrieves (TTL, account, user, cfaccount) if valid, False otherwise headers = {} if 'Authorization' in request.headers: validation = self.validate_s3_sign(request, token) if validation: headers['X-Auth-Account-Suffix'] = validation[3] else: validation = self.validate_token(token) if not validation: return HTTPNotFound() groups = ['%s:%s' % (validation[1], validation[2]), validation[1]] if validation[3]: # admin access to a cfaccount or ".reseller_admin" to access to all # accounts, including creating new ones. groups.append(validation[3]) headers['X-Auth-TTL'] = validation[0] headers['X-Auth-Groups'] = ','.join(groups) return HTTPNoContent(headers=headers)
def get_cluster_networks_detail(req, cluster_id): try: networks = registry.get_networks_detail(req.context, cluster_id) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req) return networks
def delete_role_hosts(req, role_id): try: registry.delete_role_host_metadata(req.context, role_id) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req)
def update_role_host(req, host_role_id, role_host): try: registry.update_role_host_metadata(req.context, host_role_id, role_host) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req)
def get_role_detail(req, role_id): try: role = registry.get_role_metadata(req.context, role_id) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req) return role
def get_roles_of_host(req, host_id): try: roles = registry.get_host_roles_by_host_id(req.context, host_id) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req) return roles
def get_hosts_of_role(req, role_id): try: hosts = registry.get_role_host_metadata(req.context, role_id) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req) return hosts
def get_roles_detail(req): try: roles = registry.get_roles_detail(req.context) except exception.Invalid as e: raise HTTPBadRequest(explanation=e.msg, request=req) return roles
def _upload(self, req, image_meta): """ Uploads the payload of the request to a backend store in Glance. If the `x-image-meta-store` header is set, Glance will attempt to use that store, if not, Glance will use the store set by the flag `default_store`. :param req: The WSGI/Webob Request object :param image_meta: Mapping of metadata about image :raises HTTPConflict if image already exists :retval The location where the image was stored """ try: req.get_content_type('application/octet-stream') except exception.InvalidContentType: self._safe_kill(req, image_meta['id']) msg = _("Content-Type must be application/octet-stream") logger.error(msg) raise HTTPBadRequest(explanation=msg) store_name = req.headers.get('x-image-meta-store', self.options['default_store']) store = self.get_store_or_400(req, store_name) image_id = image_meta['id'] logger.debug(_("Setting image %s to status 'saving'"), image_id) registry.update_image_metadata(self.options, req.context, image_id, {'status': 'saving'}) try: logger.debug( _("Uploading image data for image %(image_id)s " "to %(store_name)s store"), locals()) if req.content_length: image_size = int(req.content_length) elif 'x-image-meta-size' in req.headers: image_size = int(req.headers['x-image-meta-size']) else: logger.debug( _("Got request with no content-length and no " "x-image-meta-size header")) image_size = 0 location, size, checksum = store.add(image_meta['id'], req.body_file, image_size) # Verify any supplied checksum value matches checksum # returned from store when adding image supplied_checksum = image_meta.get('checksum') if supplied_checksum and supplied_checksum != checksum: msg = _("Supplied checksum (%(supplied_checksum)s) and " "checksum generated from uploaded image " "(%(checksum)s) did not match. Setting image " "status to 'killed'.") % locals() logger.error(msg) self._safe_kill(req, image_id) raise HTTPBadRequest(msg, content_type="text/plain", request=req) # Update the database with the checksum returned # from the backend store logger.debug( _("Updating image %(image_id)s data. " "Checksum set to %(checksum)s, size set " "to %(size)d"), locals()) registry.update_image_metadata(self.options, req.context, image_id, { 'checksum': checksum, 'size': size }) self.notifier.info('image.upload', image_meta) return location except exception.Duplicate, e: msg = _("Attempt to upload duplicate image: %s") % e logger.error(msg) self._safe_kill(req, image_id) self.notifier.error('image.upload', msg) raise HTTPConflict(msg, request=req)
raise HTTPForbidden(msg, request=req, content_type='text/plain') except Exception, e: tb_info = traceback.format_exc() logger.error(tb_info) self._safe_kill(req, image_id) msg = _("Error uploading image: (%(class_name)s): " "%(exc)s") % ({ 'class_name': e.__class__.__name__, 'exc': str(e) }) self.notifier.error('image.upload', msg) raise HTTPBadRequest(msg, request=req) def _activate(self, req, image_id, location): """ Sets the image status to `active` and the image's location attribute. :param req: The WSGI/Webob Request object :param image_id: Opaque image identifier :param location: Location of where Glance stored this image """ image_meta = {} image_meta['location'] = location image_meta['status'] = 'active' return registry.update_image_metadata(self.options, req.context, image_id, image_meta)
return True else: raise HTTPForbidden() @LoginRequired() @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin') @jsonify def changeset_info(self, repo_name, revision): if request.is_xhr: try: return c.db_repo_scm_instance.get_changeset(revision) except ChangesetDoesNotExistError, e: return EmptyChangeset(message=str(e)) else: raise HTTPBadRequest() @LoginRequired() @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin') @jsonify def changeset_children(self, repo_name, revision): if request.is_xhr: changeset = c.db_repo_scm_instance.get_changeset(revision) result = {"results": []} if changeset.children: result = {"results": changeset.children} return result else: raise HTTPBadRequest()