def delete_project(project_id): set_audit_resource_id(project_id) tenant = get_tenant(project_id) # NOTE(imelnikov): server deletion in OpenStack is asynchronous and # takes a lot of time, so to avoid races we don't delete them here if _project_has_servers(project_id): raise exc.InvalidRequest("Can't delete project " "while there are instances in it") # NOTE(imelnikov): image deletion would work OK here, but for consistency # and safety we opt for check instead if _project_has_images(project_id): raise exc.InvalidRequest("Can't delete project " "while there are images in it") # detach all networks net_client = admin_client_set().compute.networks for net in net_client.list(): if net.project_id == tenant.id: net_client.disassociate(net) try: tenant.delete() except osc_exc.NotFound: pass # already deleted by someone else return make_json_response(None, 204)
def create_project(): data = parse_request_data(_SCHEMA.allowed, _SCHEMA.create_required) # first, check network networks = g.client_set.compute.networks network_id = data['network'] try: net = networks.get(data['network']) except osc_exc.NotFound: raise exc.InvalidElementValue('network', 'link object', network_id, 'Network does not exist.') if net.project_id is not None: raise exc.InvalidElementValue('network', 'link object', network_id, 'Network is already used.') tenant = g.client_set.identity_admin.tenants.create( data['name'], data.get('description', '')) set_audit_resource_id(tenant) try: networks.associate(net.id, tenant.id) except osc_exc.BadRequest: tenant.delete() raise exc.InvalidRequest('Failed to associate network %r ' 'with created project' % data['network']) _set_quota(tenant.id, data) result = _project_to_view(tenant, networks.get(net.id), _quotaset_for_project(tenant.id)) return make_json_response(result)
def create_my_ssh_key(): data = parse_request_data(_SCHEMA.allowed, _SCHEMA.required) try: kp = bound_client_set().compute.keypairs.create( data['name'], data.get('public-key')) except osc_exc.BadRequest, e: raise exc.InvalidRequest(str(e))
def check_request_headers(): """Checks that request has all the correct headers""" if request.accept_mimetypes and _JSON not in request.accept_mimetypes: raise exc.InvalidRequest('Unsupported reply mime types: %s' % request.accept_mimetypes) if request.accept_charsets and 'utf-8' not in request.accept_charsets: raise exc.InvalidRequest('Unsupported reply charset: %s' % request.accept_charsets) if any( (key.lower().startswith('if-') for key in request.headers.iterkeys())): raise exc.InvalidRequest('Unsupported conditional header') if not _is_data_request(): # this two functions raise or abort on error: _check_request_content() _check_request_expectations() return None
def _check_value(value, key, max_val_len): if isinstance(value, basestring): if len(value) > max_val_len: raise exc.InvalidRequest('Length of request element "%s" ' 'must not exceed %s' % (key, max_val_len)) elif isinstance(value, list): for v in value: _check_value(v, key, max_val_len)
def _json_object_hook(data): max_key_len = current_app.config['MAX_ELEMENT_NAME_LENGTH'] max_val_len = current_app.config['MAX_PARAMETER_LENGTH'] for key, value in data.iteritems(): if isinstance(key, basestring) and len(key) > max_key_len: raise exc.InvalidRequest('Bad element name: %r' % key) _check_value(value, key, max_val_len) return data
def _check_invite_allowed(email_domain): if not g.config('invitations', 'enabled'): # TODO(imelnikov): consider if this is error 403, not 400 raise exc.InvalidRequest('Invitations disabled') domains_allowed = g.config('invitations', 'domains-allowed') if domains_allowed and email_domain not in domains_allowed: abort(403)
def create_instance_type(): data = parse_request_data(required=_SCHEMA.required) args = _instance_type_for_nova(data) args['flavorid'] = uuid.uuid4().int try: flavor = g.client_set.compute.flavors.create(**args) except osc_exc.HttpException, e: raise exc.InvalidRequest(str(e))
def remove_instance_fw_rule_set(instance_id, set_id): server = fetch_instance(instance_id) sg = _find_sg_on_server(instance_id, set_id) tcs = client_set_for_tenant(server.tenant_id, fallback_to_api=g.is_admin) try: tcs.compute.servers.remove_security_group(server, sg.name) except osc_exc.BadRequest, e: raise exc.InvalidRequest(str(e))
def create_network(): param = parse_request_data(_SCHEMA.required) client = flask.g.client_set # create network try: new_net = client.compute.networks.create(label=param['name'], vlan_start=param['vlan'], cidr=param['cidr']) except osc_exc.BadRequest, e: raise exc.InvalidRequest(str(e))
def _check_request_content(): """Validates request content type and data If request has a body, it must be a JSON object. """ if not request.content_type and not request.data: return if request.content_type: if request.content_type != _JSON: raise exc.InvalidRequest('Unsupported content type: %r' % request.content_type)
def create_users_ssh_key(user_id): data = parse_request_data(required=_SCHEMA.required) if user_id != auth.current_user_id(): auth.assert_admin() fetch_user(user_id, g.is_admin) # check that user exists and is visible mgr = auth.admin_client_set().compute_ext.user_keypairs try: kp = mgr.create(user_id, data['name'], data['public-key']) except osc_exc.BadRequest, e: raise exc.InvalidRequest(str(e))
def get_search_matcher(self, filter_type): """Get search matcher by name. Matcher should take a value from result object (in internal representation, as returned by from_argument) and a value from match string, and return True or False. """ try: return self._search_matchers[filter_type] except KeyError: raise exc.InvalidRequest('No search filter %r defined ' 'for element %r of type %r' % (filter_type, self.name, self.typename))
def send_invite_for_user(user_id): if not g.config('invitations', 'enabled'): # TODO(imelnikov): consider if this is error 403, not 400 raise exc.InvalidRequest('Invitations disabled') data = parse_request_data(_SEND_INVITE_SCHEMA) user = fetch_user(user_id, g.is_admin) if data.get('disable-user', False): auth.assert_admin() update_user_data(user, {'enabled': False, 'password': None}) result = _invite_user(user, data) return make_json_response(result)
def add_instance_fw_rule_set(instance_id): server = fetch_instance(instance_id) set_id = parse_request_data(required=_SCHEMA.required)['id'] set_audit_resource_id(set_id) try: sg = admin_client_set().compute.security_groups.get(set_id) except osc_exc.NotFound: raise exc.InvalidElementValue('id', 'string', set_id, 'Security group does not exist') tcs = client_set_for_tenant(server.tenant_id, fallback_to_api=g.is_admin) try: tcs.compute.servers.add_security_group(server, sg.name) except osc_exc.BadRequest, e: raise exc.InvalidRequest(str(e))
def _find_user(data): if 1 != sum((1 if 'id' in data else 0, 1 if 'name' in data else 0, 1 if 'email' in data else 0)): raise exc.InvalidRequest('Exactly one element of id, name or email' ' must be present') user_mgr = g.client_set.identity_admin.users try: if 'id' in data: return user_mgr.get(data['id']) elif 'email' in data: return user_mgr.find(email=data['email']) elif 'name' in data: return user_mgr.find(name=data['name']) except osc_exc.NotFound: return None
def parse_request_data(allowed=None, required=None): """Parse request body and check satisfies schema This function gets request data from request.json and checks that all elements from required schema are present, and all other elements are from allowed schema. """ # NOTE(imelnikov): we don't use request.json because we want # to raise our custom exception and add our custom validation try: data = json.loads(request.data, object_hook=_json_object_hook, encoding=request.mimetype_params.get('charset')) except ValueError, e: raise exc.InvalidRequest('JSON decoding error: %s' % e)
def upload_image_data(image_id): # first, we have to validate request if request.content_type != 'application/octet-stream': raise exc.InvalidRequest('Unsupported content type: %s' % request.content_type) if request.content_length is None: abort(411) # Length required expect = request.headers.get('Expect', '')[:4] if expect not in ('', '100-', '200-', '204-'): abort(417) # Expectations failed image = _fetch_image(image_id, to_modify=True) # also, checks permissions if image.status != 'queued': abort(405) # Method not allowed image.update(data=request.stream, size=request.content_length) return make_json_response(None, status_code=204)
def parse_filters(param_pairs, schema): """Parse response filters. Returns dictionary of dictionaries name -> filter type -> value. """ result = {} for argname, value in param_pairs: # filters are parameters with colons in name if ':' in argname: name, match_type = argname.split(':', 1) namefilts = result.setdefault(name, {}) if match_type in namefilts: raise exc.InvalidRequest('Duplicated filters: %s' % argname) try: namefilts[match_type] = schema.parse_argument( name, match_type, value) except KeyError: raise exc.UnknownArgument(argname) return result
def create_user(): data = parse_request_data(_SCHEMA.create_allowed, _SCHEMA.create_required) email = data['email'] email_name, email_domain = email.rsplit('@', 1) name = data.get('name', email_name) invite = data.get('invite') if invite: _check_invite_allowed(email_domain) else: if 'password' not in data: raise exc.MissingElement('password') for e in ('send-invite-mail', 'link-template'): if e in data: reason = '%s element is allowed only when inviting user' % e raise exc.UnknownElement(e, reason) user_mgr = g.client_set.identity_admin.users # NOTE(imelnikov): there are some races: any other user can be created or # changed between following check and creation time, but this check # is not for integrity, but for convenience. _check_attributes_unique(user_mgr.list(), name=name, email=email) try: # NOTE(imelnikov): we disable user until she accepts invite new_user = user_mgr.create(name=name, password=data.get('password'), email=email, enabled=not invite) set_audit_resource_id(new_user) if 'fullname' in data: user_mgr.update(new_user, fullname=data['fullname']) if data.get('admin'): _grant_admin(new_user.id) _add_user_to_projects(new_user, data.get('projects')) except osc_exc.BadRequest, e: raise exc.InvalidRequest(str(e))
def _parse_one_sortby_item(item, allowed_names): """Convert one item of sortby list to internal representation.""" elems = item.rsplit(':', 1) name = elems[0] if name not in allowed_names: raise exc.InvalidRequest( 'Could not sort: bad parameter: %r' % elems[0]) if len(elems) == 1 or elems[1] == 'asc': is_asc = True elif elems[1] == 'desc': is_asc = False else: raise exc.InvalidArgumentValue( 'sortby', 'string', item, 'Invalid sorting direction: %r' % elems[1]) keys = name.split('.') if len(keys) == 1: keyfun = lambda x: x.get(name) else: keyfun = lambda x: _get_nested(x, keys) return name, is_asc, keyfun
def _check_attributes_unique(objects, **kwargs): for name, value in kwargs.iteritems(): if any(getattr(obj, name, None) == value for obj in objects): raise exc.InvalidRequest('User with %s %s already exists' % (name, value))
This function gets request data from request.json and checks that all elements from required schema are present, and all other elements are from allowed schema. """ # NOTE(imelnikov): we don't use request.json because we want # to raise our custom exception and add our custom validation try: data = json.loads(request.data, object_hook=_json_object_hook, encoding=request.mimetype_params.get('charset')) except ValueError, e: raise exc.InvalidRequest('JSON decoding error: %s' % e) if not isinstance(data, dict): raise exc.InvalidRequest('Bad %s request: JSON object expected' % request.method) result = {} if required is not None: for t in required.info: if t.name not in data: raise exc.MissingElement(t.name) result[t.name] = t.from_request(data[t.name]) if allowed is not None: for t in allowed.info: if t.name in data: result[t.name] = t.from_request(data[t.name]) for name in data: