def put(self, rse): """ Update RSE usage information. .. :quickref: Usage; Update RSE usage. :param rse: The RSE name. :<json dict parameter: Dictionary with 'source', 'used', 'free' values to update. :status 200: OK. :status 400: Cannot decode json parameter dictionary. :status 401: Invalid Auth Token. :status 404: RSE not found. """ parameters = json_parameters() kwargs = {'source': None, 'used': None, 'free': None} for keyword in kwargs.keys(): kwargs[keyword] = param_get(parameters, keyword, default=kwargs[keyword]) try: set_rse_usage(rse=rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), **kwargs) except AccessDenied as error: return generate_http_error_flask(401, error) except RSENotFound as error: return generate_http_error_flask(404, error) return '', 200
def post(self): """ List the DIDs associated to a list of replicas. .. :quickref: ReplicasDIDs; List DIDs for replicas. :<json string pfns: The list of PFNs. :<json string rse: The RSE name. :resheader Content-Type: application/x-json-string :status 200: OK. :status 400: Cannot decode json parameter list. :status 406: Not Acceptable. :returns: A list of dictionaries containing the mapping PFNs to DIDs. """ parameters = json_parameters() pfns = param_get(parameters, 'pfns', default=[]) rse = param_get(parameters, 'rse') try: def generate(vo): for pfn in get_did_from_pfns(pfns, rse, vo=vo): yield dumps(pfn) + '\n' return try_stream(generate(vo=request.environ.get('vo'))) except AccessDenied as error: return generate_http_error_flask(401, error)
def post(self): """ List dataset replicas for multiple DIDs. .. :quickref: DatasetReplicas; List replicas for multiple DIDs. :<json list dids: List of DIDs for querying the datasets. :resheader Content-Type: application/x-json-stream :status 200: OK. :status 400: Bad Request. :status 401: Invalid auth token. :status 406: Not Acceptable. :returns: A dictionary containing all replicas information. """ parameters = json_parameters(parse_response) dids = param_get(parameters, 'dids') if len(dids) == 0: return generate_http_error_flask(400, ValueError.__name__, 'List of DIDs is empty') try: def generate(vo): for row in list_dataset_replicas_bulk(dids=dids, vo=vo): yield dumps(row, cls=APIEncoder) + '\n' return try_stream(generate(vo=request.environ.get('vo'))) except InvalidObject as error: return generate_http_error_flask(400, error, f'Cannot validate DIDs: {error}')
def delete(self, scope_name): """ Detach data identifiers from data identifiers. .. :quickref: DIDs; Detach DID from DID. :param scope_name: data identifier (scope)/(name). :<json dicts data: Must contain key 'dids' with list of dids to detach. :status 200: DIDs successfully detached :status 401: Invalid Auth Token :status 404: DID not found """ try: scope, name = parse_scope_name(scope_name, request.environ.get('vo')) except ValueError as error: return generate_http_error_flask(400, error) parameters = json_parameters() dids = param_get(parameters, 'dids') try: detach_dids(scope=scope, name=name, dids=dids, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except UnsupportedOperation as error: return generate_http_error_flask(409, error) except DataIdentifierNotFound as error: return generate_http_error_flask(404, error) except AccessDenied as error: return generate_http_error_flask(401, error) return '', 200
def put(self): """ Update a file replicas state at a given RSE. .. :quickref: Replicas; update replicas state. :<json string rse: The RSE name. :<json list files: list of dicts with 'scope', 'name' and 'state'. :status 201: Replica successfully updated. :status 400: Cannot decode json parameter list. :status 401: Invalid auth token. """ parameters = json_parameters(parse_response) rse = param_get(parameters, 'rse') files = param_get(parameters, 'files') try: update_replicas_states(rse=rse, files=files, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except AccessDenied as error: return generate_http_error_flask(401, error) return '', 200
def post(self, account, key): """ Add attributes to an account. .. :quickref: Attributes; Add account attribute :param account: Account identifier. :param key: The attribute key. :<json string key: The attribute key. :<json string value: The attribute value. :status 201: Successfully created. :status 401: Invalid auth token. :status 409: Attribute already exists. :status 404: Account not found. """ parameters = json_parameters() key = param_get(parameters, 'key') value = param_get(parameters, 'value') try: add_account_attribute(key=key, value=value, account=account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except AccessDenied as error: return generate_http_error_flask(401, error) except Duplicate as error: return generate_http_error_flask(409, error) except AccountNotFound as error: return generate_http_error_flask(404, error) return 'Created', 201
def post(self, scope_name): """ Mark the input DID as being followed by the given account. .. :quickref: Follow; Follow DID. HTTP Success: 201 Created HTTP Error: 401 Unauthorized 404 Not Found 500 Internal Error :param scope_name: data identifier (scope)/(name). """ try: scope, name = parse_scope_name(scope_name, request.environ.get('vo')) except ValueError as error: return generate_http_error_flask(400, error) parameters = json_parameters() account = param_get(parameters, 'account') try: add_did_to_followed(scope=scope, name=name, account=account, vo=request.environ.get('vo')) except DataIdentifierNotFound as error: return generate_http_error_flask(404, error) except AccessDenied as error: return generate_http_error_flask(401, error)
def post(self, rule_id): """ --- summary: Reduce a replication rule tags: - Rule parameters: - name: rule_id in: path description: The id of the replication rule. schema: type: string style: simple requestBody: content: 'application/json': schema: type: object required: - copies properties: copies: description: Number of copies to keep. type: integer responses: 200: description: OK content: application/json: schema: type: array items: type: string description: Rule id. 401: description: Invalid Auth Token 404: description: No rule found for the given id 409: description: Rule replace failed. """ parameters = json_parameters() copies = param_get(parameters, 'copies') exclude_expression = param_get(parameters, 'exclude_expression', default=None) try: rule_ids = reduce_replication_rule( rule_id=rule_id, copies=copies, exclude_expression=exclude_expression, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) # TODO: Add all other error cases here except RuleReplaceFailed as error: return generate_http_error_flask(409, error) except RuleNotFound as error: return generate_http_error_flask(404, error) return Response(dumps(rule_ids), status=201)
def post(self, rule_id): """ Reduce a replication rule. .. :quickref: ReduceRule; reduce rule :status 200: Rule found. :status 401: Invalid Auth Token. :status 404: no rule found for id. :status 409: Rule replace failed. :returns: List of rule ids """ parameters = json_parameters() copies = param_get(parameters, 'copies') exclude_expression = param_get(parameters, 'exclude_expression', default=None) try: rule_ids = reduce_replication_rule( rule_id=rule_id, copies=copies, exclude_expression=exclude_expression, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) # TODO: Add all other error cases here except RuleReplaceFailed as error: return generate_http_error_flask(409, error) except RuleNotFound as error: return generate_http_error_flask(404, error) return Response(dumps(rule_ids), status=201)
def post(self): """ Accepts a heartbeat. .. :quickref: Heartbeat; Accepts a heartbeat. :<json dict parameter: Dictionary with 'executable', 'hostname', 'pid', 'thread', 'older_than', 'payload' :status 200: OK. :status 400: Cannot decode json parameter list. :status 401: Invalid Auth Token. :status 404: Key not Found. """ parameters = json_parameters() try: create_heartbeat( executable=param_get(parameters, 'executable'), hostname=param_get(parameters, 'hostname'), pid=param_get(parameters, 'pid'), thread=param_get(parameters, 'thread'), older_than=param_get(parameters, 'older_than', default=None), payload=param_get(parameters, 'payload', default=None), issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), ) except (UnsupportedValueType, UnsupportedKeyType) as error: return generate_http_error_flask(400, error) except AccessDenied as error: return generate_http_error_flask(401, error) except KeyNotFound as error: return generate_http_error_flask(404, error)
def delete(self, rule_id): """ --- summary: Delete a replication rule tags: - Rule parameters: - name: rule_id in: path description: The id of the replication rule. schema: type: string style: simple responses: 200: description: OK 401: description: Invalid Auth Token 404: description: No rule found for the given id """ parameters = json_parameters() purge_replicas = param_get(parameters, 'purge_replicas', default=None) try: delete_replication_rule(rule_id=rule_id, purge_replicas=purge_replicas, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except (AccessDenied, UnsupportedOperation) as error: return generate_http_error_flask(401, error) except RuleNotFound as error: return generate_http_error_flask(404, error) return '', 200
def post(self): """ Import data into Rucio. .. :quickref: Import data into Rucio. **Example request**: .. sourcecode:: http POST /import HTTP/1.1 Host: rucio.cern.ch { "rses": [{"rse": "MOCK", "rse_type": "TAPE"}] } **Example response**: .. sourcecode:: http HTTP/1.1 201 OK Vary: Accept Created :status 200: DIDs found :status 401: Invalid Auth Token :returns: dictionary with rucio data """ data = json_parameters(parse_response) import_data(data=data, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) return 'Created', 201
def post(self, rse): """ Create RSE with given name. .. :quickref: RSE; create a new RSE. :param rse: The RSE name. :<json bool deterministic: Boolean to know if the pfn is generated deterministically. :<json bool volatile: Boolean for RSE cache. :<json string city: City for the RSE. :<json bool staging_area: Staging area. :<json string region_code: The region code for the RSE. :<json string country_name: The country. :<json string continent: The continent. :<json string time_zone: Timezone. :<json string ISP: Internet Service Provider. :<json string rse_type: RSE type. :<json number latitude: Latitude coordinate of RSE. :<json number longitude: Longitude coordinate of RSE. :<json string ASN: Access service network. :<json integer availability: Availability. :status 201: RSE created successfully. :status 400: Cannot decode json parameter dictionary. :status 401: Invalid Auth Token. :status 409: RSE already exists. :status 409: RSE not found. """ kwargs = { 'deterministic': True, 'volatile': False, 'city': None, 'staging_area': False, 'region_code': None, 'country_name': None, 'continent': None, 'time_zone': None, 'ISP': None, 'rse_type': None, 'latitude': None, 'longitude': None, 'ASN': None, 'availability': None, } if request.get_data(as_text=True): parameters = json_parameters() for keyword in kwargs.keys(): kwargs[keyword] = param_get(parameters, keyword, default=kwargs[keyword]) kwargs['issuer'] = request.environ.get('issuer') kwargs['vo'] = request.environ.get('vo') try: add_rse(rse, **kwargs) except InvalidObject as error: return generate_http_error_flask(400, error) except AccessDenied as error: return generate_http_error_flask(401, error) except RSENotFound as error: return generate_http_error_flask(404, error) except Duplicate as error: return generate_http_error_flask(409, error) return 'Created', 201
def put(self, source, destination): """ Update distance information between source RSE and destination RSE. .. :quickref: Distance; Update RSE distance. :param source: The source RSE name. :param destination: The destination RSE name. :status 200: OK. :status 400: Cannot decode json parameter dictionary. :status 401: Invalid Auth Token. :status 404: RSE Not Found. """ parameters = json_parameters() try: update_distance( source=source, destination=destination, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), parameters=parameters, ) except AccessDenied as error: return generate_http_error_flask(401, error) except RSENotFound as error: return generate_http_error_flask(404, error) return '', 200
def post(self, account): """ create account with given account name. .. :quickref: AccountParameter; Add account. :param account: The account identifier. :<json string type: The account type. :<json string email: The account email. :status 201: Successfully created. :status 401: Invalid auth token. :status 409: Account already exists. """ parameters = json_parameters() type_param = param_get(parameters, 'type') email = param_get(parameters, 'email') try: add_account(account, type_param, email, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except Duplicate as error: return generate_http_error_flask(409, error) except AccessDenied as error: return generate_http_error_flask(401, error) except InvalidObject as error: return generate_http_error_flask(400, error) return 'Created', 201
def post(self, rule_id): """ Move a replication rule. .. :quickref: MoveRule; move rule :status 200: Rule found :status 401: Invalid Auth Token :status 404: no rule found for id :status 409: Rule replace failed. :returns: List of rule ids. """ parameters = json_parameters() rse_expression = param_get(parameters, 'rse_expression') rule_id = param_get(parameters, 'rule_id', default=rule_id) try: rule_ids = move_replication_rule( rule_id=rule_id, rse_expression=rse_expression, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except RuleReplaceFailed as error: return generate_http_error_flask(409, error) except RuleNotFound as error: return generate_http_error_flask(404, error) return Response(dumps(rule_ids), status=201)
def delete(self, account): """ Delete an account's identity mapping. .. :quickref: Identities; Remove identity from account. :param account: Account identifier. :<json string identity: The identity name. :<json string authtype: The authentication type. :status 200: Successfully deleted. :status 401: Invalid auth token. :status 404: Account not found. :status 404: Identity not found. """ parameters = json_parameters() identity = param_get(parameters, 'identity') authtype = param_get(parameters, 'authtype') try: del_account_identity(identity, authtype, account, request.environ.get('issuer'), vo=request.environ.get('vo')) except AccessDenied as error: return generate_http_error_flask(401, error) except (AccountNotFound, IdentityError) as error: return generate_http_error_flask(404, error) return '', 200
def put(self, rule_id): """ Update the replication rules locked flag . .. :quickref: Rule; update rule :status 200: Rule found :status 401: Invalid Auth Token :status 404: no rule found for id """ parameters = json_parameters() options = param_get(parameters, 'options') try: update_replication_rule(rule_id=rule_id, options=options, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except AccessDenied as error: return generate_http_error_flask(401, error) except (RuleNotFound, AccountNotFound) as error: return generate_http_error_flask(404, error) except (ScratchDiskLifetimeConflict, UnsupportedOperation) as error: return generate_http_error_flask(409, error) return '', 200
def post(self, account, rse_expression): """ Create or update an account limit. .. :quickref: GlobalAccountLimit; Create/update global account limits. :param account: Account name. :param rse_expression: RSE name. :status 201: Successfully created or updated. :status 401: Invalid auth token. :status 404: RSE not found. :status 404: Account not found """ parameters = json_parameters() bytes_param = param_get(parameters, 'bytes') try: set_global_account_limit( account=account, rse_expression=rse_expression, bytes_=bytes_param, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), ) except AccessDenied as error: return generate_http_error_flask(401, error) except (RSENotFound, AccountNotFound) as error: return generate_http_error_flask(404, error) return 'Created', 201
def post(self, vo): """ Add a VO with a given name. .. :quickref: VO; Add a VOs. :param vo: VO to be added. :<json string description: Desciption of VO. :<json string email: Admin email for VO. :status 201: VO created successfully. :status 401: Invalid Auth Token. :status 409: Unsupported operation. """ parameters = json_parameters(optional=True) kwargs = {'description': None, 'email': None} for keyword in kwargs.keys(): kwargs[keyword] = param_get(parameters, keyword, default=kwargs[keyword]) kwargs['issuer'] = request.environ.get('issuer') kwargs['vo'] = request.environ.get('vo') try: add_vo(new_vo=vo, **kwargs) except AccessDenied as error: return generate_http_error_flask(401, error) except (UnsupportedOperation, Duplicate) as error: return generate_http_error_flask(409, error) return 'Created', 201
def delete(self, scope_name): """ Mark the input DID as not followed .. :quickref: Follow; Unfollow DID. HTTP Success: 200 OK HTTP Error: 401 Unauthorized 500 InternalError :param scope_name: data identifier (scope)/(name). """ try: scope, name = parse_scope_name(scope_name, request.environ.get('vo')) except ValueError as error: return generate_http_error_flask(400, error) parameters = json_parameters() account = param_get(parameters, 'account') try: remove_did_from_followed(scope=scope, name=name, account=account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except DataIdentifierNotFound as error: return generate_http_error_flask(404, error) return '', 200
def post(self): """ Trace endpoint used by the pilot and CLI clients to post data access information. .. :quickref: Trace; Send trace. :<json dict payload: Dictionary contain the trace information. :status 201: Created. :status 400: Cannot decode json data. """ headers = self.get_headers() payload = json_parameters() # generate entry timestamp payload['traceTimeentry'] = datetime.datetime.utcnow() payload['traceTimeentryUnix'] = calendar.timegm( payload['traceTimeentry'].timetuple( )) + payload['traceTimeentry'].microsecond / 1e6 # guess client IP payload['traceIp'] = request.headers.get('X-Forwarded-For', default=request.remote_addr) # generate unique ID payload['traceId'] = str(uuid.uuid4()).replace('-', '').lower() trace(payload=payload) return 'Created', 201, headers
def post(self): """ List all meta of a list of data identifiers. .. :quickref: Meta; List metadata of multiple DIDs :resheader Content-Type: application/x-json-stream :status 200: OK :status 400: Bad Request :status 401: Unauthorized :status 404: DataIdentifierNotFound :returns: A list of dictionaries containing all meta. """ parameters = json_parameters() dids = param_get(parameters, 'dids') try: def generate(vo): for meta in get_metadata_bulk(dids, vo=vo): yield render_json(**meta) + '\n' return try_stream(generate(vo=request.environ.get('vo'))) except ValueError as error: return generate_http_error_flask(400, error, 'Cannot decode json parameter list') except DataIdentifierNotFound as error: return generate_http_error_flask(404, error)
def post(self, key): """ Create a new allowed key (value is NULL). .. :quickref: Meta; Create new key. :<json dict parameter: Dictionary with 'value_type', 'value_regexp' and 'key_type'. :status 201: Created. :status 400: Cannot decode json parameter list. :status 400: Unsupported Value Type. :status 401: Invalid Auth Token. :status 409: Key already exists. """ parameters = json_parameters() try: add_key( key=key, key_type=param_get(parameters, 'key_type', default=None), value_type=param_get(parameters, 'value_type', default=None), value_regexp=param_get(parameters, 'value_regexp', default=None), issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), ) except Duplicate as error: return generate_http_error_flask(409, error) except (UnsupportedValueType, UnsupportedKeyType) as error: return generate_http_error_flask(400, error) return 'Created', 201
def delete(self): """ Delete file replicas at a given RSE. .. :quickref: Replicas; Delete replica at RSE. :<json string rse: The RSE name. :<json list files: list of dicts with 'scope', 'name'. :<json bool ignore_availability: Flag to ignore the RSE blacklisting. :status 200: Replica successfully deleted. :status 400: Cannot decode json parameter list. :status 401: Invalid auth token. :status 404: RSE not found. :status 404: Replica not found. """ parameters = json_parameters(parse_response) rse = param_get(parameters, 'rse') files = param_get(parameters, 'files') try: delete_replicas( rse=rse, files=files, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), ignore_availability=param_get(parameters, 'ignore_availability', default=False), ) except AccessDenied as error: return generate_http_error_flask(401, error) except (RSENotFound, ReplicaNotFound) as error: return generate_http_error_flask(404, error) except ResourceTemporaryUnavailable as error: return generate_http_error_flask(503, error) return '', 200
def post(self, key): """ Create a new value for a key. .. :quickref: Values; Create new value. :<json dict parameter: Dictionary with 'value'. :status 201: Created. :status 400: Cannot decode json parameter list. :status 400: Invalid Value For Key. :status 401: Invalid Auth Token. :status 404: Key Not Found. :status 409: Value already exists. """ parameters = json_parameters() value = param_get(parameters, 'value') try: add_value(key=key, value=value, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except Duplicate as error: return generate_http_error_flask(409, error) except InvalidValueForKey as error: return generate_http_error_flask(400, error) except KeyNotFound as error: return generate_http_error_flask(404, error) return 'Created', 201
def post(self): """ Declare a list of bad replicas. .. :quickref: BadReplicasStates; Declare bad replicas. :<json string pfns: The list of PFNs. :<json string reason: The reason of the loss. :resheader Content-Type: application/json :status 201: Created. :status 400: Cannot decode json parameter list. :status 401: Invalid auth token. :status 404: RSE not found. :status 404: Replica not found. :returns: A list of not successfully declared files. """ parameters = json_parameters() pfns = param_get(parameters, 'pfns', default=[]) reason = param_get(parameters, 'reason', default=None) try: not_declared_files = declare_bad_file_replicas(pfns=pfns, reason=reason, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) return not_declared_files, 201 except AccessDenied as error: return generate_http_error_flask(401, error) except (RSENotFound, ReplicaNotFound) as error: return generate_http_error_flask(404, error)
def put(self, account): """ update a parameter for a given account name .. :quickref: AccountParameter; Update account information. :param account: The account identifier. :status 200: OK. :status 400: Unknown status. :status 401: Invalid auth token. :status 404: Account not found. """ parameters = json_parameters() for key, value in parameters.items(): try: update_account(account, key=key, value=value, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')) except ValueError: return generate_http_error_flask(400, ValueError.__name__, f'Unknown value {value}') except AccessDenied as error: return generate_http_error_flask(401, error) except AccountNotFound as error: return generate_http_error_flask(404, error) return '', 200
def post(self): """ Set a tombstone on a list of replicas. .. :quickref: Tombstone; Set a tombstone on a list of replicas. :<json string replicas: list fo replicas :resheader Content-Type: application/x-json-string :status 201: Created. :status 401: Invalid auth token. :status 404: ReplicaNotFound. """ parameters = json_parameters(parse_response) replicas = param_get(parameters, 'replicas', default=[]) try: for replica in replicas: set_tombstone( rse=replica['rse'], scope=replica['scope'], name=replica['name'], issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), ) except ReplicaNotFound as error: return generate_http_error_flask(404, error) except ReplicaIsLocked as error: return generate_http_error_flask(423, error) return 'Created', 201
def post(self, rse, scheme): """ Create a protocol for a given RSE. .. :quickref: Protocol; Create an RSE protocol. :param rse: The RSE name. :param scheme: The protocol identifier. :<json dict paramaters: parameter of the new protocol entry. :status 201: Created. :status 400: Cannot decode json parameter dictionary. :status 401: Invalid Auth Token. :status 404: RSE not found. :status 404: RSE Protocol Domain Not Supported. :status 409: RSE Protocol Priority Error. """ parameters = json_parameters() # Fill defaults and check mandatory parameters parameters['scheme'] = scheme try: add_protocol(rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), data=parameters) except (RSENotFound, RSEProtocolDomainNotSupported) as error: return generate_http_error_flask(404, error) except AccessDenied as error: return generate_http_error_flask(401, error) except (Duplicate, RSEProtocolPriorityError) as error: return generate_http_error_flask(409, error) except InvalidObject as error: return generate_http_error_flask(400, error) return 'Created', 201