Ejemplo n.º 1
0
def submit_batch(user_name):
    """
    Puts the task request(s) into Redis and returns a list of task UUID(s)
    Parameters
    ----------
    user_name : str
    The primary identity of the user
    POST payload
    ------------
    {
    }
    Returns
    -------
    json
        The task document
    """
    app.logger.debug(f"Submit_batch invoked by user:{user_name}")

    if not user_name:
        abort(400, description="Could not find user. You must be "
                               "logged in to perform this function.")
    try:
        user_id = resolve_user(user_name)
    except Exception:
        app.logger.error("Failed to resolve user_name to user_id")
        return jsonify({'status': 'Failed',
                        'reason': 'Failed to resolve user_name:{}'.format(user_name)})

    # Extract the token for endpoint verification
    token_str = request.headers.get('Authorization')
    token = str.replace(str(token_str), 'Bearer ', '')

    # Parse out the function info
    try:
        post_req = request.json
        endpoints = post_req['endpoints']
        function_uuid = post_req['func']
        input_data = post_req['payload']
        serialize = post_req.get('serialize', None)
    except KeyError as e:
        return jsonify({'status': 'Failed',
                        'reason': "Missing Key {}".format(str(e))})
    except Exception as e:
        return jsonify({'status': 'Failed',
                        'reason': 'Request Malformed. Missing critical information: {}'.format(str(e))})

    return jsonify(auth_and_launch(user_id,
                                   function_uuid,
                                   endpoints,
                                   input_data,
                                   app,
                                   token,
                                   serialize=serialize))
Ejemplo n.º 2
0
def callback():
    """Handles the interaction with Globus Auth."""
    # If we're coming back from Globus Auth in an error state, the error
    # will be in the "error" query string parameter.
    if 'error' in request.args:
        flash("You could not be logged into funcX: " +
              request.args.get('error_description', request.args['error']))
        return redirect(url_for('home'))

    # Set up our Globus Auth/OAuth2 state
    redirect_uri = f"{app.config['HOSTNAME']}/callback"
    client = get_auth_client()
    requested_scopes = [
        'https://auth.globus.org/scopes/facd7ccc-c5f4-42aa-916b-a0e270e2c2a9/all',
        'profile', 'urn:globus:auth:scope:transfer.api.globus.org:all',
        'urn:globus:auth:scope:auth.globus.org:view_identities', 'openid'
    ]
    client.oauth2_start_flow(redirect_uri,
                             requested_scopes=requested_scopes,
                             refresh_tokens=False)

    # If there's no "code" query string parameter, we're in this route
    # starting a Globus Auth login flow.
    if 'code' not in request.args:
        auth_uri = client.oauth2_get_authorize_url()
        return redirect(auth_uri)
    else:
        # If we do have a "code" param, we're coming back from Globus Auth
        # and can start the process of exchanging an auth code for a token.
        code = request.args.get('code')
        tokens = client.oauth2_exchange_code_for_tokens(code)
        app.logger.debug(tokens)
        id_token = tokens.decode_id_token(client)

        # Make sure the user exists in the database
        user_id = resolve_user(id_token.get('preferred_username'))

        session.update(tokens=tokens.by_resource_server,
                       username=id_token.get('preferred_username'),
                       user_id=user_id,
                       name=id_token.get('name'),
                       email=id_token.get('email'),
                       is_authenticated=True)

        return redirect('https://dev.funcx.org/home')
Ejemplo n.º 3
0
def get_ep_stats(user_name, endpoint_id):
    """Retrieve the status updates from an endpoint.

    Parameters
    ----------
    user_name : str
        The primary identity of the user
    endpoint_id : str
        The endpoint uuid to look up

    Returns
    -------
    json
        The status of the endpoint
    """
    alive_threshold = 2 * 60  # time in seconds since last heartbeat to be counted as alive
    last = 10

    if not user_name:
        abort(400, description="Could not find user. You must be "
                               "logged in to perform this function.")

    try:
        user_id = resolve_user(user_name)
    except Exception:
        app.logger.error("Failed to resolve user_name to user_id")
        return jsonify({'status': 'Failed',
                        'reason': 'Failed to resolve user_name:{}'.format(user_name)})

    # Extract the token for endpoint verification
    token_str = request.headers.get('Authorization')
    token = str.replace(str(token_str), 'Bearer ', '')

    if not authorize_endpoint(user_id, endpoint_id, None, token):
        return jsonify({'status': 'Failed',
                        'reason': f'Unauthorized access to endpoint: {endpoint_id}'})

    # TODO add rc to g.
    rc = get_redis_client()

    status = {'status': 'offline', 'logs': []}
    try:
        end = min(rc.llen(f'ep_status_{endpoint_id}'), last)
        print("Total len :", end)
        items = rc.lrange(f'ep_status_{endpoint_id}', 0, end)
        if items:
            for i in items:
                status['logs'].append(json.loads(i))

            # timestamp is created using time.time(), which returns seconds since epoch UTC
            logs = status['logs']  # should have been json loaded already
            newest_timestamp = logs[0]['timestamp']
            now = time.time()
            if now - newest_timestamp < alive_threshold:
                status['status'] = 'online'

    except Exception as e:
        app.logger.error("Unable to retrieve ")
        status = {'status': 'Failed',
                  'reason': f'Unable to retrieve endpoint stats: {endpoint_id}. {e}'}

    return jsonify(status)
Ejemplo n.º 4
0
def submit(user_name):
    """Puts the task request(s) into Redis and returns a list of task UUID(s)
    Parameters
    ----------
    user_name : str
    The primary identity of the user

    POST payload
    ------------
    {
        tasks: []
    }
    Returns
    -------
    json
        The task document
    """
    app.logger.debug(f"batch_run invoked by user:{user_name}")

    try:
        user_id = resolve_user(user_name)
    except Exception:
        msg = f"Failed to resolve user_name:{user_name} to user_id"
        app.logger.error(msg)
        abort(500, description=msg)

    # Extract the token for endpoint verification
    token_str = request.headers.get('Authorization')
    token = str.replace(str(token_str), 'Bearer ', '')

    # Parse out the function info
    tasks = []
    try:
        post_req = request.json
        if 'tasks' in post_req:
            # new client is being used
            tasks = post_req['tasks']
        else:
            # old client was used and create a new task
            function_uuid = post_req['func']
            endpoint = post_req['endpoint']
            input_data = post_req['payload']
            tasks.append([function_uuid, endpoint, input_data])
        serialize = post_req.get('serialize', None)
    except KeyError as e:
        abort(422, description=f"Missing key: {e}")

    results = {'status': 'Success',
               'task_uuids': [],
               'task_uuid': ""}
    for task in tasks:
        res = auth_and_launch(
            user_id, function_uuid=task[0], endpoints=[task[1]],
            input_data=task[2], app=app, token=token, serialize=serialize
        )
        if res.get('status', 'Failed') != 'Success':
            return res
        else:
            results['task_uuids'].extend(res['task_uuids'])
            # For backwards compatibility. <=0.0.1a5 requires "task_uuid" in result
            # Note: previous versions did not support batching, so returning the first one is ok.
            results['task_uuid'] = res['task_uuids'][0]
    return jsonify(results)
Ejemplo n.º 5
0
def run(user_name):
    """Puts a job in Redis and returns an id

    Parameters
    ----------
    user_name : str
        The primary identity of the user
    Returns
    -------
    json
        The task document
    """

    app.logger.debug(f"Automate submit invoked by user:{user_name}")

    if not user_name:
        abort(400,
              description="Could not find user. You must be "
              "logged in to perform this function.")
    try:
        user_id = resolve_user(user_name)
    except Exception:
        app.logger.error("Failed to resolve user_name to user_id")
        return jsonify({
            'status':
            'Failed',
            'reason':
            'Failed to resolve user_name:{}'.format(user_name)
        })

    # Extract the token for endpoint verification
    token_str = request.headers.get('Authorization')
    token = str.replace(str(token_str), 'Bearer ', '')

    # Parse out the function info
    tasks = []
    try:
        post_req = request.json['body']
        if 'tasks' in post_req:
            tasks = post_req.get('tasks', [])
        else:
            # Check if the old client was used and create a new task
            function_uuid = post_req.get('func', None)
            endpoint = post_req.get('endpoint', None)
            input_data = post_req.get('payload', None)
            tasks.append({
                'func': function_uuid,
                'endpoint': endpoint,
                'payload': input_data
            })

        # Sets serialize to True by default
        serialize = post_req.get('serialize', True)
    except KeyError as e:
        return jsonify({
            'status': 'Failed',
            'reason': "Missing Key {}".format(str(e))
        })
    except Exception as e:
        return jsonify({
            'status':
            'Failed',
            'reason':
            'Request Malformed. Missing critical information: {}'.format(
                str(e))
        })

    results = {'status': 'Success', 'task_uuids': []}
    app.logger.info(f'tasks to submit: {tasks}')
    for task in tasks:
        res = auth_and_launch(user_id,
                              task['func'], [task['endpoint']],
                              task['payload'],
                              app,
                              token,
                              serialize=serialize)
        if res.get('status', 'Failed') != 'Success':
            return res
        else:
            results['task_uuids'].extend(res['task_uuids'])

    # if the batch size is just one, we can return it as the action id
    if len(results['task_uuids']) == 1:
        action_id = results['task_uuids'][0]
    else:
        # Otherwise we need to create an action id for the batch
        action_id = str(uuid.uuid4())
        # Now store the list of ids in redis with this batch id
        rc = get_redis_client()
        rc.hset(f'batch_{action_id}', 'batch',
                json.dumps(results['task_uuids']))

    automate_response = {
        "status": 'ACTIVE',
        "action_id": action_id,
        "details": None,
        "release_after": 'P30D',
        "start_time": str(datetime.datetime.utcnow())
    }
    print(automate_response)
    return jsonify(automate_response)
Ejemplo n.º 6
0
def get_ep_stats(user_name, endpoint_id):
    """Retrieve the status updates from an endpoint.

    Parameters
    ----------
    user_name : str
        The primary identity of the user
    endpoint_id : str
        The endpoint uuid to look up

    Returns
    -------
    json
        The status of the endpoint
    """

    last = 10

    if not user_name:
        abort(400,
              description="Could not find user. You must be "
              "logged in to perform this function.")

    try:
        user_id = resolve_user(user_name)
    except Exception:
        app.logger.error("Failed to resolve user_name to user_id")
        return jsonify({
            'status':
            'Failed',
            'reason':
            'Failed to resolve user_name:{}'.format(user_name)
        })

    # Extract the token for endpoint verification
    token_str = request.headers.get('Authorization')
    token = str.replace(str(token_str), 'Bearer ', '')

    if not authorize_endpoint(user_id, endpoint_id, None, token):
        return jsonify({
            'status':
            'Failed',
            'reason':
            f'Unauthorized access to endpoint: {endpoint_id}'
        })

    # TODO add rc to g.
    rc = get_redis_client()

    stats = []
    try:
        end = min(rc.llen(f'ep_status_{endpoint_id}'), last)
        print("Total len :", end)
        items = rc.lrange(f'ep_status_{endpoint_id}', 0, end)
        if items:
            for i in items:
                stats.append(json.loads(i))
    except Exception as e:
        stats = {
            'status': 'Failed',
            'reason': f'Unable to retrieve endpoint stats: {endpoint_id}. {e}'
        }

    return jsonify(stats)
Ejemplo n.º 7
0
def submit(user_name):
    """Puts the task request into Redis and returns a task UUID
    Parameters
    ----------
    user_name : str
    The primary identity of the user

    POST payload
    ------------
    {
    }
    Returns
    -------
    json
        The task document
    """
    app.logger.debug(f"Submit invoked by user:{user_name}")

    if not user_name:
        abort(400,
              description="Could not find user. You must be "
              "logged in to perform this function.")
    try:
        user_id = resolve_user(user_name)
    except Exception:
        app.logger.error("Failed to resolve user_name to user_id")
        return jsonify({
            'status':
            'Failed',
            'reason':
            'Failed to resolve user_name:{}'.format(user_name)
        })

    # Extract the token for endpoint verification
    token_str = request.headers.get('Authorization')
    token = str.replace(str(token_str), 'Bearer ', '')

    # Parse out the function info
    try:
        post_req = request.json
        endpoint = post_req['endpoint']
        function_uuid = post_req['func']
        input_data = post_req['payload']
        serializer = None
        if 'serializer' in post_req:
            serializer = post_req['serializer']
    except KeyError as e:
        return jsonify({
            'status': 'Failed',
            'reason': "Missing Key {}".format(str(e))
        })
    except Exception as e:
        return jsonify({
            'status':
            'Failed',
            'reason':
            'Request Malformed. Missing critical information: {}'.format(
                str(e))
        })

    # Check if the user is allowed to access the function
    if not authorize_function(user_id, function_uuid, token):
        return jsonify({
            'status':
            'Failed',
            'reason':
            f'Unauthorized access to function: {function_uuid}'
        })

    try:
        fn_code, fn_entry, container_uuid = resolve_function(
            user_id, function_uuid)
    except Exception as e:
        return jsonify({
            'status':
            'Failed',
            'reason':
            f'Function UUID:{function_uuid} could not be resolved. {e}'
        })

    if isinstance(endpoint, str):
        endpoint = [endpoint]

    # Make sure the user is allowed to use the function on this endpoint
    for ep in endpoint:
        if not authorize_endpoint(user_id, ep, function_uuid, token):
            return jsonify({
                'status': 'Failed',
                'reason': f'Unauthorized access to endpoint: {ep}'
            })

    task_id = str(uuid.uuid4())

    app.logger.debug("Got function container_uuid :{}".format(container_uuid))

    # At this point the packed function body and the args are concatable strings
    payload = fn_code + input_data
    app.logger.debug("Payload : {}".format(payload))

    if not container_uuid:
        container_uuid = 'RAW'

    if not serializer:
        serializer = "ANY"

    task_header = f"{task_id};{container_uuid};{serializer}"

    # TODO: Store redis connections in g
    rc = get_redis_client()

    for ep in endpoint:
        redis_task_queue = RedisQueue(f"task_{ep}",
                                      hostname=app.config['REDIS_HOST'],
                                      port=app.config['REDIS_PORT'])
        redis_task_queue.connect()

        redis_task_queue.put(task_header, 'task', payload)
        app.logger.debug(f"Task:{task_id} forwarded to Endpoint:{ep}")
        app.logger.debug("Redis Queue : {}".format(redis_task_queue))

        # TODO: creating these connections each will be slow.
        # increment the counter
        rc.incr('funcx_invocation_counter')
        # add an invocation to the database
        log_invocation(user_id, task_id, function_uuid, ep)

    return jsonify({'status': 'Success', 'task_uuid': task_id})