def COPY(self, req): """HTTP COPY request handler.""" dest = req.headers.get('Destination') if not dest: return HTTPPreconditionFailed(request=req, body='Destination header required') dest = unquote(dest) if not dest.startswith('/'): dest = '/' + dest try: _junk, dest_container, dest_object = dest.split('/', 2) except ValueError: return HTTPPreconditionFailed( request=req, body='Destination header must be of the form ' '<container name>/<object name>') source = '/' + self.container_name + '/' + self.object_name self.container_name = dest_container self.object_name = dest_object # re-write the existing request as a PUT instead of creating a new one # since this one is already attached to the posthooklogger req.method = 'PUT' req.path_info = '/' + self.account_name + dest req.headers['Content-Length'] = 0 req.headers['X-Copy-From'] = quote(source) del req.headers['Destination'] return self.PUT(req)
def create(self, request): """ POST /v1.0/{account_id}/nodes Create volume """ params, meta_params = filter_update_params(request, Node) if not params.get('name'): raise HTTPPreconditionFailed("Must specify a 'name' parameter") self._validate_volume_type(params) try: params['size'] = int(params.get('size', 0)) except ValueError: raise HTTPPreconditionFailed("'size' parameter must be an integer") params['meta'] = meta_params node = self.db.query(Node).filter_by(name=params['name']).first() if not node or node.status in ('DELETED', 'ERROR'): # create or update name = params.pop('name') params['status'] = params.get('status', 'ACTIVE') node, created = self.db.update_or_create(Node, updates=params, name=name) else: raise HTTPConflict("Node '%s' already exists" % params['name']) self.db.refresh(node) return Response(dict(node))
def create(self, req, lock): try: source = self.helper.volumes.get(self.volume_id) except NotFound: raise HTTPNotFound("No volume named '%s'" % self.volume_id) try: iqn = req.params['iqn'] except KeyError: raise HTTPBadRequest("Must specify an export iqn") try: iscsi_ip = req.params['iscsi_ip'] except KeyError: raise HTTPBadRequest("Must specify iscsi ip") try: iscsi_port = int(req.params.get('iscsi_port', 3260)) except ValueError: raise HTTPPreconditionFailed("Port must be an integer") try: mgmt_host = req.params['mgmt_host'] except KeyError: raise HTTPBadRequest("Must specify mgmt_host") try: mgmt_port = req.params['mgmt_port'] except ValueError: raise HTTPBadRequest("Must specify mgmt_port") except KeyError: raise HTTPPreconditionFailed("Must specify mgmt_port") # We're updating the status for a volume on a different node. try: cinder_host = req.params['cinder_host'] except KeyError: raise HTTPBadRequest("Must specify cinder_host") cinder = None account = req.params.get('account') if account: cinder = self.helper.get_cinder(account) def callback(): path = '/volumes/%s/export' % self.id self.helper.node_request(mgmt_host, mgmt_port, 'DELETE', path) self.helper.make_api_request('volumes', self.id, data={'status': 'ACTIVE'}, cinder_host=cinder_host) if cinder: cinder.delete_volume_metadata(self.id, 'clone-progress') self.helper.volumes.create_clone(self.volume_id, self.id, iqn, iscsi_ip, iscsi_port, cinder=cinder, callback=callback, lock=lock) return Response(source)
def _validate_size_range(self, min_size, max_size): if min_size is None and max_size is None: return if min_size is None or max_size is None: raise HTTPPreconditionFailed("'min_size' and 'max_size' must be " "both omitted or specified") if min_size > max_size: raise HTTPPreconditionFailed("'min_size' parameter must be <= " "'max_size' parameter")
def _validate_size(self, req): try: size = int(req.params['size']) except KeyError: raise HTTPBadRequest("Must specify size") except ValueError: raise HTTPPreconditionFailed("'size' parameter must be an integer") if size < 0: raise HTTPPreconditionFailed("'size' parameter can not be " "negative") return size
def _fetch_iops(self, params, key): val = params.get(key, 0) if val: try: val = int(val) if val < 0: raise HTTPPreconditionFailed("'%s' must be > 0 " % key) except ValueError: raise HTTPPreconditionFailed("'%s' parameter must be an " "integer, not: %s" % (key, val)) return val
def _validate_backup(self, params): backup = params.get('backup') if not backup: return None try: backup = self.account_query(Backup).filter_by(id=backup).one() except NoResultFound: raise HTTPPreconditionFailed("No Backup '%s'" % backup) if backup.status != 'AVAILABLE': raise HTTPPreconditionFailed("Backup '%s' must be AVAILABLE, " "not '%s'" % (backup.id, backup.status)) return backup
def _validate_affinity(self, params): affinity = params.get('affinity') if not affinity: return '' try: affinity_type, affinity_rule = affinity.split(':') except ValueError: msg = "Invalid affinity: %s" % affinity raise HTTPPreconditionFailed(msg) if affinity_type not in ('different_node', 'different_group'): msg = "Invalid affinity type: %s" % affinity_type raise HTTPPreconditionFailed(msg) return affinity
def _filter_ip(self, ip): if not ip: return None try: return netaddr.IPAddress(ip) except: raise HTTPPreconditionFailed("Invalid ip: '%s'" % ip)
def __call__(self, env, start_response): start_time = time.time() req = Request(env) self.logger.txn_id = req.headers.get('x-trans-id', None) if not check_utf8(req.path_info): res = HTTPPreconditionFailed(body='Invalid UTF8') else: try: if hasattr(self, req.method): res = getattr(self, req.method)(req) else: res = HTTPMethodNotAllowed() except (Exception, Timeout): self.logger.exception(_('ERROR __call__ error with %(method)s' ' %(path)s '), {'method': req.method, 'path': req.path}) res = HTTPInternalServerError(body=traceback.format_exc()) trans_time = '%.4f' % (time.time() - start_time) additional_info = '' if res.headers.get('x-container-timestamp') is not None: additional_info += 'x-container-timestamp: %s' % \ res.headers['x-container-timestamp'] log_message = '%s - - [%s] "%s %s" %s %s "%s" "%s" "%s" %s "%s"' % ( req.remote_addr, time.strftime('%d/%b/%Y:%H:%M:%S +0000', time.gmtime()), req.method, req.path, res.status.split()[0], res.content_length or '-', req.headers.get('x-trans-id', '-'), req.referer or '-', req.user_agent or '-', trans_time, additional_info) if req.method.upper() == 'REPLICATE': self.logger.debug(log_message) else: self.logger.info(log_message) return res(env, start_response)
def __call__(self, env, start_response): start_time = time.time() req = Request(env) self.logger.txn_id = req.headers.get('x-trans-id', None) if not check_utf8(req.path_info): res = HTTPPreconditionFailed(body='Invalid UTF8') else: try: # disallow methods which have not been marked 'public' try: method = getattr(self, req.method) getattr(method, 'publicly_accessible') except AttributeError: res = HTTPMethodNotAllowed() else: res = method(req) except (Exception, Timeout): self.logger.exception(_('ERROR __call__ error with %(method)s' ' %(path)s '), {'method': req.method, 'path': req.path}) res = HTTPInternalServerError(body=traceback.format_exc()) trans_time = '%.4f' % (time.time() - start_time) log_message = '%s - - [%s] "%s %s" %s %s "%s" "%s" "%s" %s' % ( req.remote_addr, time.strftime('%d/%b/%Y:%H:%M:%S +0000', time.gmtime()), req.method, req.path, res.status.split()[0], res.content_length or '-', req.headers.get('x-trans-id', '-'), req.referer or '-', req.user_agent or '-', trans_time) if req.method.upper() == 'REPLICATE': self.logger.debug(log_message) else: self.logger.info(log_message) return res(env, start_response)
def request_to_ks(self, req, servers, port): """ Routing multiple keystone servers. """ succ_resps = [] fail_resps = [] auth_tokens = self._split_auth_token(req, servers) bodies = self._split_body(req, servers) for site, token, body in zip(servers, auth_tokens, bodies): for node in site: parsed = urlparse(self._combinate_ks_url(node, port, req)) connector = HTTPSConnection if parsed.scheme == 'https' else HTTPConnection try: with ConnectionTimeout(self.conn_timeout): (host, port) = parsed.netloc.split(':') headers = req.headers if req.headers.has_key('Host'): headers['Host'] = host + ':' + str(port) if token: headers['X-Auth-Token'] = token if req.headers.has_key('Content-Length'): del headers['Content-Length'] conn = connector(host, port) conn.request(req.method, parsed.path, body, headers) with Timeout(self.timeout): resp = conn.getresponse() if resp.status >= 200 and resp.status <= 300: succ_resps.append(resp) break else: fail_resps.append(resp) except ValueError, err: fail_resps.append(HTTPPreconditionFailed(request=req)) except (Exception, TimeoutError), err: fail_resps.append(HTTPServiceUnavailable(request=req))
def server_id_validator(event): request = event.request server_id = event.request.registry.server_id cookies = SimpleCookie(request.environ.get('HTTP_COOKIE')) cookie_server_id = cookies.get('SERVER_ID', None) if cookie_server_id: value = cookie_server_id.value decrypted = decrypt(server_id, value) if not decrypted or not decrypted.startswith(server_id): logger.info('Invalid cookie: {}'.format( value, extra={'MESSAGE_ID': 'serverid_invalid'})) response_cookie = SimpleCookie() value, time = encrypt(server_id) response_cookie['SERVER_ID'] = value response_cookie['SERVER_ID']['path'] = '/' request.response = HTTPPreconditionFailed( headers={ 'Set-Cookie': response_cookie['SERVER_ID'].OutputString() }) request.response.empty_body = True logger.info('New cookie: {} ({})'.format(value, time), extra={'MESSAGE_ID': 'serverid_new'}) raise request.response else: time = decrypted[len(server_id):] logger.debug('Valid cookie: {} ({})'.format(value, time), extra={'MESSAGE_ID': 'serverid_valid'}) elif request.method in ['POST', 'PATCH', 'PUT', 'DELETE']: value, time = encrypt(server_id) response_cookie = SimpleCookie() response_cookie['SERVER_ID'] = value response_cookie['SERVER_ID']['path'] = '/' request.response = HTTPPreconditionFailed( headers={ 'Set-Cookie': response_cookie['SERVER_ID'].OutputString() }) request.response.empty_body = True logger.info('New cookie: {} ({})'.format(value, time), extra={'MESSAGE_ID': 'serverid_new'}) raise request.response if not cookie_server_id: value, time = encrypt(server_id) request.response.set_cookie(name='SERVER_ID', value=value) logger.info('New cookie: {} ({})'.format(value, time), extra={'MESSAGE_ID': 'serverid_new'}) return request.response
def _validate_source(self, params): source_volume = params.get('source_volume') if not source_volume: return None try: source = self.account_query(Volume) \ .filter_by(id=source_volume) \ .one() except NoResultFound: raise HTTPPreconditionFailed("No source '%s'" % source_volume) if source.status != 'ACTIVE': raise HTTPPreconditionFailed("Source '%s' must be 'ACTIVE', " "not '%s'" % (source.id, source.status)) if not source.node: raise HTTPPreconditionFailed("Source has no node.") return source
def _validate_ip(self, ip): if not ip: # Use '' because None gets urlencoded to "None"... return '' try: netaddr.IPAddress(ip) return ip except: raise HTTPPreconditionFailed("Invalid ip: '%s'" % ip)
def _validate_iops(self, req): try: read_iops = int(req.params.get('read_iops', 0)) except ValueError: raise HTTPPreconditionFailed("'read_iops' parameter must be an " "integer") if read_iops < 0: raise HTTPPreconditionFailed("'read_iops' parameter can not be " "negative") try: write_iops = int(req.params.get('write_iops', 0)) except ValueError: raise HTTPPreconditionFailed("'write_iops' parameter must be an " "integer") if write_iops < 0: raise HTTPPreconditionFailed("'write_iops' parameter can not be " "negative") return {'read_iops': read_iops, 'write_iops': write_iops}
def _validate_backup_params(self, req): backup_id = req.params['backup_id'] if len(backup_id) > 60: raise HTTPPreconditionFailed( "length of 'backup_id' parameter cannot exceed 60") try: backup_source_volume_id = req.params['backup_source_volume_id'] if len(backup_source_volume_id) > 60: raise HTTPPreconditionFailed( "length of 'backup_source_volume_id' parameter cannot" " exceed 60") except KeyError: raise HTTPBadRequest("Must specify backup_source_volume_id") return { 'backup_source_volume_id': backup_source_volume_id, 'backup_id': backup_id, }
def _validate_volume_type(self, params): try: volume_type_name = params['volume_type_name'] except KeyError: raise HTTPBadRequest("Must specify 'volume_type_name'") volume_type = self.db.query(VolumeType).get(volume_type_name) if not volume_type or volume_type.status != "ACTIVE": raise HTTPPreconditionFailed("Invalid volume type '%s'" % volume_type_name) return volume_type
def _validate_force_node(self, params): force_node = params.get('force_node') if not force_node: return None try: self.db.query(Node).filter( or_(Node.id == force_node, Node.name == force_node)).one() except NoResultFound: raise HTTPPreconditionFailed('Invalid force_node: %s' % force_node) return force_node
def __call__(self, env, start_response): """ """ req = Request(env) self.loc.reload() if self.loc.age == 0: self.logger.warn( 'dispatcher relay rule is invalid, using old rules now.') if not self.is_keystone_proxy_path(req): return self.app(env, start_response) try: (loc_prefix, api_type) = self.location_api_check(req) except Exception: return HTTPPreconditionFailed(body='invalid PATH')(env, start_response) ks_port = self.keystone_auth_port \ if api_type == self.keystone_proxy_auth_path \ else self.keystone_admin_port servers = self.loc.swift_of(loc_prefix) if not servers: return HTTPPreconditionFailed(body='invalid Location prefix')( env, start_response) (succ_resps, fail_resps) = self.request_to_ks(req, servers, ks_port) if len(succ_resps) == 0: resp = fail_resps[0] if isinstance(resp, HTTPException): return resp(env, start_response) start_response('%s %s' % (resp.status, resp.reason), resp.getheaders()) return resp.read() if self.loc.is_merged(loc_prefix): try: (body, header) = self.ks_merge_response(succ_resps, loc_prefix) except Exception, err: return HTTPServerError(body=err)(env, start_response) res = Response(status='200 OK') res.headerlist = header res.body = body return res(env, start_response)
def create(self, req, lock): try: timestamp = req.params['timestamp'] except KeyError: raise HTTPBadRequest("Most specify timestamp") if '.' in self.id: raise HTTPPreconditionFailed("backup id cannot contain '.'") try: snapshot = self.helper.volumes.create_snapshot( self.volume_id, self.id, timestamp) except NotFound, e: raise HTTPNotFound(str(e))
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 __call__(self, req): C = SimpleCookie(req.environ.get('HTTP_COOKIE')) server_id = C.get(self.cookie_name, None) if server_id: value = server_id.value decrypted = decrypt(self.m_id, value) if not decrypted or not decrypted.startswith(self.b_id): LOGGER.info("Invalid cookie: %s", value, extra={'MESSAGE_ID': 'serverid_invalid'}) value, time = encrypt(self.m_id, self.b_id) C = SimpleCookie() C[self.cookie_name] = value C[self.cookie_name]['path'] = '/' LOGGER.info("New cookie: %s (%s)", value, time, extra={'MESSAGE_ID': 'serverid_new'}) response = HTTPPreconditionFailed(headers={'Set-Cookie': C[self.cookie_name].OutputString()}) response.empty_body = True raise response else: time = decrypted[len(self.b_id):] LOGGER.debug("Valid cookie: %s (%s)", value, time, extra={'MESSAGE_ID': 'serverid_valid'}) elif req.method in ['POST', 'PATCH', 'PUT', 'DELETE']: value, time = encrypt(self.m_id, self.b_id) C = SimpleCookie() C[self.cookie_name] = value C[self.cookie_name]['path'] = '/' LOGGER.info("New cookie: %s (%s)", value, time, extra={'MESSAGE_ID': 'serverid_new'}) response = HTTPPreconditionFailed(headers={'Set-Cookie': C[self.cookie_name].OutputString()}) response.empty_body = True raise response response = req.get_response(self.application) if not server_id: value, time = encrypt(self.m_id, self.b_id) C = SimpleCookie() C[self.cookie_name] = value C[self.cookie_name]['path'] = '/' LOGGER.info("New cookie: %s (%s)", value, time, extra={'MESSAGE_ID': 'serverid_new'}) response.headers.add('Set-Cookie', C[self.cookie_name].OutputString()) return response
def create(self, req, lock): if len(self.id) > 94: raise HTTPPreconditionFailed( "length of volume id cannot exceed 94") if '.' in self.id: raise HTTPPreconditionFailed("volume id cannot contain '.'") params = {'lock': lock} params['size'] = self._validate_size(req) iops = self._validate_iops(req) # Create from backup. if req.params.get('backup_id'): params.update(self._validate_backup_params(req)) params['callback'] = self._create_from_backup_cb(req, iops) account = req.params.get('account') if account: params['cinder'] = self.helper.get_cinder(account) try: self.helper.volumes.create(self.id, **params) except AlreadyExists, e: raise HTTPConflict(str(e)) volume = self.helper.volumes.get(self.id) volume['status'] = 'BUILDING'
def _validate_source_params(self, req): source_volume_id = req.params['source_volume_id'] if len(source_volume_id) > 60: raise HTTPPreconditionFailed( "length of 'source_volume_id' parameter " "cannot exceed 60") try: source_host = req.params['source_host'] except KeyError: raise HTTPBadRequest("Must specify source_host") try: source_port = req.params['source_port'] except KeyError: raise HTTPBadRequest("Must specify source_port") return { 'id': source_volume_id, 'host': source_host, 'port': source_port, }
def __call__(self, env, start_response): """ WSGI entry point. Wraps env in webob.Request object and passes it down. :param env: WSGI environment dictionary :param start_response: WSGI callable """ try: if self.memcache is None: self.memcache = cache_from_env(env) req = self.update_request(Request(env)) return self.handle_request(req)(env, start_response) except UnicodeError: err = HTTPPreconditionFailed(request=req, body='Invalid UTF8') return err(env, start_response) except (Exception, Timeout): start_response('500 Server Error', [('Content-Type', 'text/plain')]) return ['Internal server error.\n']
def __call__(self, env, start_response): """WSGI Application entry point for the Swift Object Server.""" start_time = time.time() req = Request(env) if not check_utf8(req.path_info): res = HTTPPreconditionFailed(body='Invalid UTF8') else: try: if hasattr(self, req.method): res = getattr(self, req.method)(req) else: res = HTTPMethodNotAllowed() except: res = HTTPInternalServerError(body=traceback.format_exc()) trans_time = time.time() - start_time if req.method in ('PUT', 'DELETE'): slow = self.slow - trans_time if slow > 0: sleep(slow) return res(env, start_response)
def set_item(self, request): """Sets a single WBO object.""" storage = self._get_storage(request) if storage.use_quota: left = self._check_quota(request) else: left = 0. user_id = request.user['userid'] collection_name = request.sync_info['collection'] item_id = request.sync_info['item'] if self._was_modified(request, user_id, collection_name): raise HTTPPreconditionFailed(collection_name) try: data = json.loads(request.body) except ValueError: raise HTTPJsonBadRequest(WEAVE_MALFORMED_JSON) try: wbo = WBO(data) except ValueError: raise HTTPJsonBadRequest(WEAVE_INVALID_WBO) consistent, msg = wbo.validate() if not consistent: raise HTTPJsonBadRequest(WEAVE_INVALID_WBO) if self._has_modifiers(wbo): wbo['modified'] = request.server_time try: res = storage.set_item(user_id, collection_name, item_id, storage_time=request.server_time, **wbo) except StorageConflictError: raise HTTPJsonBadRequest(WEAVE_INVALID_WRITE) response = json_response(res) if storage.use_quota and left <= _ONE_MEG: response.headers['X-Weave-Quota-Remaining'] = str(left) return response
def delete_item(self, request): """Deletes a single WBO object.""" collection_name = request.sync_info['collection'] item_id = request.sync_info['item'] user_id = request.user['userid'] if self._was_modified(request, user_id, collection_name): raise HTTPPreconditionFailed(collection_name) self._get_storage(request).delete_item(user_id, collection_name, item_id, storage_time=request.server_time) # Not logging this event for now. Infrasec may want it again in future # #if collection_name == 'crypto' and item_id == 'keys': # msg = 'Crypto keys deleted' # username = request.user['username'] # log_cef(msg, 5, request.environ, self.app.config, username) return json_response(request.server_time)
def delete_collection(self, request, **kw): """Deletes the collection and all contents. Additional request parameters may modify the selection of which items to delete. """ kw = self._convert_args(kw) collection_name = request.sync_info['collection'] user_id = request.user['userid'] if self._was_modified(request, user_id, collection_name): raise HTTPPreconditionFailed(collection_name) self._get_storage(request).delete_items(user_id, collection_name, kw.get('ids'), kw['filters'], limit=kw.get('limit'), offset=kw.get('offset'), sort=kw.get('sort'), storage_time=request.server_time) return json_response(request.server_time)
def __call__(self, env, start_response): """ """ req = Request(env) self.loc.reload() if self.loc.age == 0: self.logger.warn( 'dispatcher relay rule is invalid, using old rules now.') loc_prefix = self.location_check(req) if not self.loc.has_location(loc_prefix): resp = HTTPNotFound(request=req) start_response(resp.status, resp.headerlist) return resp.body if self.loc.is_merged(loc_prefix): self.logger.debug('enter merge mode') if req.method == 'COPY': try: req = self.copy_to_put(req) except Exception, e: resp = HTTPPreconditionFailed(request=req, body=e.message) start_response(resp.status, resp.headerlist) return resp.body resp = self.dispatch_in_merge(req, loc_prefix)