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.'}
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)
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.'}
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.'}
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)
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)
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)
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)
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)
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.'}
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])
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)
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.'}
def get_all() -> List[Task]: """Fetch all tasks from the database.""" return Task.Schema().load(db.tasks.find(), many=True)
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
'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