Beispiel #1
0
def get_task_list(response, name: str = None):
    """
    Retrieve the full list of tasks or a task with a given name.

    If the `name` parameter is specified in the request, this function attempts
    to return the task with that name. If it is not found, returns a 404
    response.

    If no name is given, the function returns the full list of tasks present in
    the system.
    """

    try:
        if name is not None:
            result = TaskService.get_by_name(name)
            return Task.Schema().dump(result)

        results = TaskService.get_all()
        return Task.Schema(
            only=['_id', 'name', 'runtime', 'deadline', 'period']).dump(
                results, many=True)
    except NotFound as e:
        response.status = hug.HTTP_NOT_FOUND
        return {'error': str(e)}
    except Exception:
        response.status = hug.HTTP_INTERNAL_SERVER_ERROR
        return {'error': 'Unable to fetch task list.'}
Beispiel #2
0
    def add_task(node_id: str, task_id: str) -> Node:
        """
        Adds a new task to the given node's taskset.

        Returns the updated node with the new task added to its task list.

        If the node or the task can't be found using the given IDs, raises a
        `NotFound` exception.

        If the task's needed devices aren't present in the target node, raises a
        `MissingDevices` exception.

        If the new taskset doesn't pass the feasibility check, raises a
        `NotFeasible` exception.
        """

        node = db.nodes.find_one({'_id': ObjectId(node_id)})
        if node is None:
            raise NotFound('No node found with the given ID')
        node = Node.Schema().load(node)

        task = db.tasks.find_one({'_id': ObjectId(task_id)})
        if task is None:
            raise NotFound('No task found with the given ID')
        task = Task.Schema().load(task)

        if not set(task.devices).issubset(set(node.devices)):
            raise MissingDevices(
                'The target node doesn\'t have the needed devices for the task'
            )

        if check_feasibility(node.tasks + [task], node.cpu_cores) is False:
            raise NotFeasible('The new taskset isn\'t feasible')

        with fs.get(task.file_id) as task_file:
            deploy_task(task_file, task, node)

        updated_node = db.nodes.find_one_and_update(
            {'_id': ObjectId(node_id)}, {
                '$push': {
                    'tasks': {
                        **Task.Schema().dump(task), '_id': ObjectId(task_id),
                        'file_id': ObjectId(task.file_id)
                    }
                }
            },
            return_document=ReturnDocument.AFTER)
        return Node.Schema().load(updated_node)
Beispiel #3
0
def post_task(body, response):
    """
    Create a new task resource.

    This function attempts to create a new task resource with the data given in
    the request in the form of a tar file. This tar file contains an
    `specs.json` file with the task's specification. The rest of the files of
    contained in the tarball are the source code of the task and its Dockerfile
    for deployment.

    If the operation is succesful, the new task's ID is returned. If the name
    for the new task is already in use, returns a 409 response. If the new
    task's specification data isn't correct, returns a 400 response.
    """

    try:
        file_name = body['file'][0]
        file_body = body['file'][1]
        specs = json.loads(body['specs'])

        new_task = Task.Schema().load(specs)
        new_id = TaskService.create(new_task, file_name, file_body)
        return {'_id': new_id}
    except json.JSONDecodeError as e:
        response.status = hug.HTTP_BAD_REQUEST
        return {'error': e.msg}
    except ValidationError as e:
        response.status = hug.HTTP_BAD_REQUEST
        return {'error': e.messages}
    except AlreadyPresent as e:
        response.status = hug.HTTP_CONFLICT
        return {'error': str(e)}
    except Exception:
        response.status = hug.HTTP_INTERNAL_SERVER_ERROR
        return {'error': 'Unable to create task.'}
Beispiel #4
0
def put_task(task_id: str, body, response):
    """
    Put the values given in the body in a task resource.

    Returns the updated task resource in the response.

    If no task is found, returns a 404 response. If the given ID is invalid,
    returns a 400 response.
    """

    try:
        specs = json.loads(body['specs'])
        file_name = file_body = None
        if 'file' in body:
            file_name = body['file'][0]
            file_body = body['file'][1]
        result = TaskService.update(task_id, specs, file_name, file_body)
        return Task.Schema().dump(result)
    except json.JSONDecodeError as e:
        response.status = hug.HTTP_BAD_REQUEST
        return {'error': e.msg}
    except InvalidId as e:
        response.status = hug.HTTP_BAD_REQUEST
        return {'error': str(e)}
    except NotFound as e:
        response.status = hug.HTTP_NOT_FOUND
        return {'error': str(e)}
    except Exception:
        response.status = hug.HTTP_INTERNAL_SERVER_ERROR
        return {'error': 'Unable to update task.'}
Beispiel #5
0
    def delete(task_id: str) -> Task:
        """
        Removes the task with the given ID from the database.

        Returns the task that has been deleted. If no task is found with the
        given ID, this method raises a `NotFound` error.
        """

        task = db.tasks.find_one({'_id': ObjectId(task_id)})
        if task is None:
            raise NotFound('No task found with the given ID.')

        db.tasks.delete_one({'_id': task['_id']})
        fs.delete(task['file_id'])

        nodes = db.nodes.find({'tasks._id': task['_id']})
        if nodes:
            for node in nodes:
                remove_task(task['name'], Node.Schema().load(node))
                db.nodes.find_one_and_update(
                    {'_id': node['_id']},
                    {'$pull': {
                        'tasks': {
                            '_id': task['_id']
                        }
                    }})

        return Task.Schema().load(task)
Beispiel #6
0
    def remove_task(node_id: str, task_id: str) -> Node:
        """
        Removes a task from the given node's taskset.

        Returns the updated node without the task in its taskset.

        If the node or the task can't be found using the given IDs, raises a
        `NotFound` exception.
        """

        node = db.nodes.find_one({'_id': ObjectId(node_id)})
        if node is None:
            raise NotFound('No node found with the given ID')
        node = Node.Schema().load(node)

        task = db.tasks.find_one({'_id': ObjectId(task_id)})
        if task is None:
            raise NotFound('No task found with the given ID')
        task = Task.Schema().load(task)

        remove_task(task.name, node)

        updated_node = db.nodes.find_one_and_update(
            {'_id': ObjectId(node_id)},
            {'$pull': {
                'tasks': {
                    '_id': ObjectId(task_id)
                }
            }},
            return_document=ReturnDocument.AFTER)
        return Node.Schema().load(updated_node)
Beispiel #7
0
    def create(new_task: Task, file_name: str, file_body: BytesIO) -> str:
        """
        Insert a new task into the database.

        If the name of the new task is already in use, this method raises an
        `AlreadyPresent` error. If not, the new task is inserted and its new ID
        is returned.
        """

        result = db.tasks.find_one({'name': new_task.name})
        if result is not None:
            raise AlreadyPresent('A task already exists with the given name.')

        file_id = fs.put(file_body, filename=file_name)
        new_task.file_id = file_id

        new_id = db.tasks.insert_one(
            Task.Schema(exclude=['_id']).dump(new_task)).inserted_id
        return str(new_id)
Beispiel #8
0
    def get_by_name(task_name: str) -> Task:
        """
        Fetch a task from the database by its name.

        Raises a `NotFound` error if no task is found with the given name.
        """

        result = db.tasks.find_one({'name': task_name})
        if result is None:
            raise NotFound('No task found with the given name.')
        return Task.Schema().load(result)
Beispiel #9
0
    def get_by_id(task_id: str) -> Task:
        """
        Fetch a task from the database by its ID.

        Raises a `NotFound` error if no task is found with the given ID.
        """

        result = db.tasks.find_one({'_id': ObjectId(task_id)})
        if result is None:
            raise NotFound('No task found with the given ID.')
        return Task.Schema().load(result)
Beispiel #10
0
def get_task(task_id: str, response):
    """
    Retrieve the task with the given ID.

    If no task is found, returns a 404 response. If the given ID is invalid,
    returns a 400 response.
    """

    try:
        result = TaskService.get_by_id(task_id)
        return Task.Schema().dump(result)
    except InvalidId as e:
        response.status = hug.HTTP_BAD_REQUEST
        return {'error': str(e)}
    except NotFound as e:
        response.status = hug.HTTP_NOT_FOUND
        return {'error': str(e)}
    except Exception:
        response.status = hug.HTTP_INTERNAL_SERVER_ERROR
        return {'error': 'Unable to fetch task.'}
Beispiel #11
0
    def setUp(self):
        inserted_ids = mockdb.nodes.insert_many(
            Node.Schema(exclude=['_id']).dump(test_nodes,
                                              many=True)).inserted_ids

        for i in range(len(test_nodes)):
            test_nodes[i]._id = inserted_ids[i]

        for i in range(len(test_tasks)):
            with mockfs.new_file() as file:
                test_tasks[i].file_id = file._id

        inserted_ids = mockdb.tasks.insert_many(
            Task.Schema(exclude=['_id']).dump(test_tasks,
                                              many=True)).inserted_ids

        for i in range(len(test_tasks)):
            test_tasks[i]._id = inserted_ids[i]

        test_nodes[1].tasks.append(test_tasks[0])
Beispiel #12
0
    def update(task_id: str, new_values: dict, file_name: str,
               file_body: BytesIO) -> Task:
        """
        Updates an existing task.

        The task is retrieved using the given ID and updated with the values
        specified in the given dictionary. If a new file is also given, it
        replaces the old one. Returns the updated task.

        If no task is found with the given ID, raises a `NotFound` exception.
        """

        task = db.tasks.find_one({'_id': ObjectId(task_id)})
        if task is None:
            raise NotFound('No task found with the given ID.')

        if file_body:
            old_file_id = task['file_id']
            new_file_id = fs.put(file_body, filename=file_name)
            new_values = {**new_values, 'file_id': new_file_id}

        updated_task = db.tasks.find_one_and_update(
            {'_id': task['_id']}, {'$set': new_values},
            return_document=ReturnDocument.AFTER)

        if file_body:
            fs.delete(old_file_id)

        nodes = db.nodes.find({'tasks._id': task['_id']})
        if nodes:
            for node in nodes:
                remove_task(task['name'], Node.Schema().load(node))
                db.nodes.update_one({'_id': node['_id']},
                                    {'$pull': {
                                        'tasks': {
                                            '_id': task['_id']
                                        }
                                    }})

        return Task.Schema().load(updated_task)
Beispiel #13
0
def delete_task(task_id: str, response):
    """
    Delete the task with the given ID.

    Returns the deleted task's data in the response.

    If no task is found, returns a 404 response. If the given ID is invalid,
    returns a 400 response.
    """

    try:
        result = TaskService.delete(task_id)
        return Task.Schema(exclude=['_id', 'file_id']).dump(result)
    except InvalidId as e:
        response.status = hug.HTTP_BAD_REQUEST
        return {'error': str(e)}
    except NotFound as e:
        response.status = hug.HTTP_NOT_FOUND
        return {'error': str(e)}
    except Exception:
        response.status = hug.HTTP_INTERNAL_SERVER_ERROR
        return {'error': 'Unable to delete task.'}
Beispiel #14
0
    def get_all() -> List[Task]:
        """Fetch all tasks from the database."""

        return Task.Schema().load(db.tasks.find(), many=True)
Beispiel #15
0
from io import BytesIO

from bson.objectid import ObjectId

from shipyard.errors import AlreadyPresent, NotFound
from shipyard.task import controllers
from shipyard.task.model import Task

test_tasks = Task.Schema().load([{
    '_id': str(ObjectId()),
    'file_id': str(ObjectId()),
    'name': 'Test1',
    'runtime': 1000,
    'deadline': 1000,
    'period': 1000
}, {
    '_id': str(ObjectId()),
    'file_id': str(ObjectId()),
    'name': 'Test2',
    'runtime': 1000,
    'deadline': 1000,
    'period': 1000
}],
                                many=True)


class MockService():
    @staticmethod
    def get_all() -> List[Task]:
        return test_tasks

    @staticmethod
Beispiel #16
0
    'name': 'Test2',
    'ip': '2.2.2.2',
    'ssh_user': '******',
    'cpu_cores': 1
}],
                                many=True)
test_tasks = Task.Schema().load([{
    'name': 'Test1',
    'runtime': 1000,
    'deadline': 1000,
    'period': 1000,
    'devices': ['/dev/test1']
}, {
    'name': 'Test2',
    'runtime': 1000,
    'deadline': 1000,
    'period': 1000,
    'devices': ['/dev/test1', '/dev/test3']
}, {
    'name': 'Test3',
    'runtime': 1000,
    'deadline': 1000,
    'period': 1000,
    'devices': ['/dev/test1']
}],
                                many=True)


def mock_check_feasibility(tasks: List[Task], cpu_cores: int) -> bool:
    if tasks[-1].name == 'Test1':
        return True
    return False