def delete_simulation(sim_identifier, user=None): # noqa: E501 """Delete simulation # noqa: E501 :param sim_identifier: :type sim_identifier: int :rtype: None """ session = Database.get_session() try: # Check the store q = session.query(Simulation).filter( Simulation.uid == sim_identifier) # type: Query if q.first(): session.delete(q.first()) session.commit() return "", 204 else: return Error(code=404, message="Simulation not found"), 404 except Exception as e: logging.exception("Simulation deletion failed") session.rollback() return Error(code=500, message="Exception occurred"), 500
def delete_store(store_name, user=None): # noqa: E501 """Delete file store # noqa: E501 :param store_name: Name of the store :type store_name: str :rtype: None """ session = Database.get_session() try: # Check the store q = Database.get_session().query(RosbagStore).filter( RosbagStore.name == store_name) # type: Query if q.count() == 1: session.delete(q.first()) session.commit() return "", 204 else: return Error(code=404, message="Store not found"), 404 except Exception as e: logging.exception("User account deletion failed") session.rollback() return Error(code=500, message="Exception occurred"), 500
def get_task(task_identifier, user=None): """Take a task from the queue # noqa: E501 :param task_identifier: :type task_identifier: str :rtype: TaskDetailed """ try: session = Database.get_session() try: identifier = int(task_identifier) except ValueError: return Error(code=400, message="Invalid task identifier"), 400 q = session.query(Task).filter( Task.uid == int(identifier)) # type: Query model = q.first() if model: return model.to_swagger_model_detailed(user=user) else: return Error(code=404, message="Task not found"), 404 except Exception as e: return handle_exception(e)
def put_task_inner(task_identifier, task, user=None): """Update a task # noqa: E501 :param task_identifier: :type task_identifier: str :param task: The task :type task: dict | bytes :rtype: TaskDetailed """ session = Database.get_session() try: identifier = int(task_identifier) except ValueError: return Error(code=400, message="Invalid task identifier"), 400 q = session.query(Task).filter(Task.uid == int(identifier)) # type: Query model = q.first() # type: Task if model: model.from_swagger_model(task, user=user) session.commit() # Return a fresh copy from the DB q = session.query(Task).filter(Task.uid == model.uid) return q.first().to_swagger_model_detailed(user=user), 200 else: return Error(code=404, message="Task not found"), 404
def delete_simulation_environment(env_name, user=None): # noqa: E501 """Delete simulation # noqa: E501 :param env_name: Name of the simulation environment :type env_name: str :rtype: None """ session = Database.get_session() try: # Check the store q = session.query(SimulationEnvironment).filter( SimulationEnvironment.name == env_name) # type: Query if q.first(): session.delete(q.first()) session.commit() return "", 204 else: return Error(code=404, message="Simulation environment not found"), 404 except Exception as e: logging.exception("Simulation environment deletion failed") session.rollback() return Error(code=500, message="Exception occurred"), 500
def get_file_meta(store_name, uid, file_name, user=None): """ Get file meta data :param store_name: Name of the store :type store_name: str :param uid: Unique identifier of the file :type uid: int :param file_name: Name of the file :type file_name: str :rtype: FileDetailed """ try: q = Database.get_session().query(File).filter( and_(FileStore.name == store_name, File.uid == uid)) # type: Query if q.count(): file = q[0] if file.name == file_name: return file.to_swagger_model_detailed(user=user) return Error(code=404, message="Store or file not found"), 404 except Exception as e: if current_app.config['TESTING']: return Error(code=500, message="Exception: " + str(e)), 500 else: return Error(code=500, message="Exception occurred"), 500
def put_simulation_environment(env_name, environment, block_on_existing=None, user=None): # noqa: E501 """Create/update a simulation environment # noqa: E501 :param env_name: Name of the simulation environment :type env_name: str :param environment: Simulation environment :type environment: dict | bytes :rtype: SimulationEnvironmentDetailed """ try: if connexion.request.is_json: environment = SimulationEnvironmentDetailed.from_dict( connexion.request.get_json()) # noqa: E501 session = Database.get_session() q = session.query(SimulationEnvironment).filter( SimulationEnvironment.name == env_name) # type: Query model = SimulationEnvironment() if q.count() == 1: if block_on_existing: return Error(code=1000, message="Already exists."), 400 model = q.first() else: if environment.name != env_name: return Error( code=400, message= "Path and body tag have to be equal for a new environment" ), 400 session.add(model) model.from_swagger_model(environment, user=user) if environment.rosbag_store: q = session.query(RosbagStore).filter( RosbagStore.name == environment.rosbag_store) # type: Query rosbag_store = q.first() if not rosbag_store: return Error(code=400, message="Rosbag store not found"), 400 model.rosbag_store_id = rosbag_store.uid session.commit() return model.to_swagger_model_detailed(user=user) except Exception as e: return handle_exception(e)
def do_task_action(task_identifier, action, task=None, user=None): # noqa: E501 """Perform an action on the task # noqa: E501 :param task_identifier: :type task_identifier: str :param action: Action to perform (cancel/prio_up) :type action: str :param task: The task, required depending on the action :type task: dict | bytes :rtype: TaskDetailed """ try: if connexion.request.is_json and task: task = TaskDetailed.from_dict( connexion.request.get_json()) # noqa: E501 session = Database.get_session() q = session.query(Task).filter( Task.uid == int(task_identifier)) # type: Query model = q.first() # type: Task if not model: return Error(code=404, message="Task not found"), 404 if action == "cancel": if model.state == TaskState.Queued: model.state = TaskState.Cancelled model.last_updated = datetime.datetime.utcnow() session.commit() return model.to_swagger_model_detailed(user) elif action == "cancel_running": if model.state == TaskState.Running: model.state = TaskState.CancellationRequested model.last_updated = datetime.datetime.utcnow() session.commit() return model.to_swagger_model_detailed(user) elif action == "prio_up": Task.task_prio_up(session, model.uid) session.commit() q = session.query(Task).filter(Task.uid == int(task_identifier)) return q.first().to_swagger_model_detailed(user) else: return Error(code=400, message="Unknown action"), 400 except Exception as e: return handle_exception(e)
def new_simulation_run(sim_identifier, simulation_run, user=None): # noqa: E501 """New simulation run # noqa: E501 :param sim_identifier: :type sim_identifier: int :param simulation_run: Simulation run :type simulation_run: dict | bytes :rtype: SimulationRunDetailed """ try: if connexion.request.is_json: simulation_run = SimulationRunDetailed.from_dict( connexion.request.get_json()) # noqa: E501 session = Database.get_session() # Find the simulation q = session.query(Simulation).filter(Simulation.uid == sim_identifier) simulation = q.first() if not simulation: return Error(code=404, message="Simulation not found"), 404 model = SimulationRun() model.from_swagger_model(simulation_run, user=user) model.simulation_id = simulation.uid if simulation_run.bag_store_name and simulation_run.bag_name: # Find the bag of the run q = session.query(Rosbag).filter( and_(RosbagStore.name == simulation_run.bag_store_name, Rosbag.name == simulation_run.bag_name)) bag = q.first() if not bag: return Error(code=400, message="Bag not found"), 400 model.bag_id = bag.uid model.uid = None session.add(model) session.commit() # Return a fresh copy from the DB q = session.query(SimulationRun).filter(SimulationRun.uid == model.uid) return q.first().to_swagger_model_detailed(user=user), 200 except Exception as e: return handle_exception(e)
def put_file_store(store_name, store, block_on_existing=None, user=None): # noqa: E501 """Create/update store # noqa: E501 :param store_name: Name of the store :type store_name: str :param store: Store information :type store: dict | bytes :rtype: FileStore """ if connexion.request.is_json: store = SwaggerFileStore.from_dict( connexion.request.get_json()) # noqa: E501 if store_name != store.name: return Error(code=400, message="URL and body names don't match"), 400 session = Database.get_session() try: # Check the store q = session.query(FileStore).filter( FileStore.name == store_name) # type: Query # Create new store or use existing model = None if q.first(): # Existing store if block_on_existing: return Error(code=1000, message="Already exists."), 400 model = q.first() else: model = FileStore() session.add(model) model.from_swagger_model(store, user=user) session.commit() q = session.query(FileStore).filter(FileStore.uid == model.uid) return q.first().to_swagger_model(user=user), 200 except Exception as e: logging.exception("File store put failed") session.rollback() return Error(code=500, message="Exception occurred"), 500
def list_stores(user=None): """ List available stores :rtype: List[BagStoreDetailed] """ try: q = Database.get_session().query(RosbagStore) return [p.to_swagger_model_detailed(user=user) for p in q] except Exception as e: if current_app.config['TESTING']: return Error(code=500, message="Exception: " + str(e)), 500 else: return Error(code=500, message="Exception occurred"), 500
def find_bag_in_database(session, store_name, bag_name): q = session.query(RosbagStore).filter( RosbagStore.name == store_name) # type: Query if q.count() == 0: return None, Error(code=404, message="Store not found") store = q[0] q = session.query(Rosbag).filter( and_(Rosbag.store_id == store.uid, Rosbag.name == bag_name)) # type: Query if q.count() == 0: return None, Error(code=404, message="Bag not found") return q[0], None
def delete_bag_comment(store_name, bag_name, comment_id, user=None): # noqa: E501 """Post a comment # noqa: E501 :param store_name: Name of the store :type store_name: str :param bag_name: Name of the bag :type bag_name: str :param comment_id: Comment identifier :type comment_id: int :rtype: None """ try: session = Database.get_session() bag, e = find_bag_in_database(session, store_name, bag_name) if e: return e, e.code q = session.query(RosbagComment).filter( RosbagComment.uid == comment_id) # type: Query if q.count() != 1 or q.first().bag_id != bag.uid: return Error(code=404, message="Comment not found"), 404 session.delete(q.first()) session.commit() return "", 204 except Exception as e: return handle_exception(e)
def get_simulation_run(sim_identifier, run_identifier, expand=None, user=None): # noqa: E501 """Get simulation run # noqa: E501 :param sim_identifier: :type sim_identifier: int :param run_identifier: :type run_identifier: int :param expand: :type expand: bool :rtype: SimulationRunDetailed """ try: session = Database.get_session() q = session.query(SimulationRun).filter( SimulationRun.uid == run_identifier) # type: Query model = q.first() if model and model.simulation_id == sim_identifier: return model.to_swagger_model_detailed(user=user, expand=expand) else: return Error(code=404, message="Simulation run not found"), 404 except Exception as e: return handle_exception(e)
def delete_session(session_id, user=None): """ Delete a session or sessions :param session_id: Session id or all or current :type session_id: str :rtype: None """ try: to_delete = [] if session_id == "current": pass elif session_id == "all": pass else: try: id = int(session_id) except: return Error(400, "Invalid session id"), 400 for session in to_delete: pass except Exception as e: return error_helper.handle_exception(e)
def put_simulation(sim_identifier, simulation, user=None): # noqa: E501 """Update a simulation # noqa: E501 :param sim_identifier: :type sim_identifier: int :param simulation: Simulation :type simulation: dict | bytes :rtype: SimulationDetailed """ try: if connexion.request.is_json: simulation = SimulationDetailed.from_dict( connexion.request.get_json()) # noqa: E501 if simulation.identifier != sim_identifier: return Error( code=400, message="Body and path identifier are not the same"), 400 session = Database.get_session() q = session.query(Simulation).filter(Simulation.uid == sim_identifier) model = q.first() if not model: return Error(code=404, message="Simulation not found"), 404 q = session.query(SimulationEnvironment).filter( SimulationEnvironment.name == simulation.environment_name) if not q.first(): return Error(code=400, message="Simulation environment '%s' not found" % simulation.environment_name), 400 model.from_swagger_model(simulation, user=user) model.environment_id = q.first().uid session.commit() # Return a fresh copy from the DB q = session.query(Simulation).filter(Simulation.uid == model.uid) return q.first().to_swagger_model_detailed(user=user), 200 except Exception as e: return handle_exception(e)
def put_extraction_configuration(config_name, configuration_obj, block_on_existing=None, user=None): """Create/update configuration :param config_name: Name of the configuration :type config_name: str :param configuration: Configuration information :type configuration: dict | bytes :rtype: BagExtractionConfiguration """ if connexion.request.is_json: configuration_obj = BagExtractionConfiguration.from_dict(connexion.request.get_json()) if config_name != configuration_obj.name: return Error(code=400, message="URL and body names don't match"), 400 session = Database.get_session() try: # Check the store q = session.query(RosbagExtractionConfiguration).filter(RosbagExtractionConfiguration.name == config_name) # type: Query # Create new store or use existing model = None if q.count() == 1: # Existing configuration if block_on_existing: return Error(code=1000, message="Already exists."), 400 model = q.first() else: model = RosbagExtractionConfiguration() session.add(model) model.from_swagger_model(configuration_obj, user=user) session.commit() q = session.query(RosbagExtractionConfiguration).filter(RosbagExtractionConfiguration.uid == model.uid) return q.first().to_swagger_model(user=user), 200 except Exception as e: session.rollback() return handle_exception(e)
def bag_store_authorize_step_get(store_name, step, user=None): """ Authorization step forwarded to storage plugin :param store_name: Name of the store :type store_name: str :param step: Step of the authorization procedure :type step: str :rtype: None """ session = Database.get_session() try: q = session.query(RosbagStore).filter( RosbagStore.name == store_name) # type: Query if q.count(): store = q[0] storage_plugin = Storage.factory(store_name, store.store_type, store.store_data) link = url_for( "/api/v0.rbb_server_controllers_store_controller_bag_store_authorize_step_get", store_name=store_name, step="", _external=True) response = storage_plugin.authorize_get_step( step, flask_request, link) if storage_plugin.needs_saving(): store.store_data = storage_plugin.get_data() flag_modified(store, 'store_data') Database.get_session().commit() return response return Error(code=404, message="Store not found"), 404 except Exception as e: logging.exception(e) return Error(code=500, message="Exception occurred"), 500
def patch_task(task_identifier, task, user=None): # noqa: E501 """Partial update of task (this only supports a few fields) # noqa: E501 :param task_identifier: :type task_identifier: str :param task: Fields to update :type task: :rtype: TaskDetailed """ session = Database.get_session() try: identifier = int(task_identifier) except ValueError: return Error(code=400, message="Invalid task identifier"), 400 try: q = session.query(Task).filter( Task.uid == int(identifier)) # type: Query model = q.first() if model: changed = False if 'log_append' in task and isinstance(task['log_append'], str): model.log += task['log_append'] changed = True if changed: session.commit() return model.to_swagger_model_detailed(user=user) else: return Error(code=404, message="Task not found"), 404 except Exception as e: return handle_exception(e)
def get_store(store_name, user=None): """ Get store details :param store_name: Name of the store :type store_name: str :rtype: BagStoreDetailed """ try: q = Database.get_session().query(RosbagStore).filter( RosbagStore.name == store_name) #type: Query if q.count(): return q[0].to_swagger_model_detailed(user=user) else: return Error(code=404, message="Store not found"), 404 except Exception as e: if current_app.config['TESTING']: return Error(code=500, message="Exception: " + str(e)), 500 else: return Error(code=500, message="Exception occurred"), 500
def new_file(store_name, file, user=None): """ Register new file :param store_name: Name of the store :type store_name: str :param file: The file metadata :type file: dict | bytes :rtype: FileDetailed """ if connexion.request.is_json: file = FileDetailed.from_dict(connexion.request.get_json()) try: session = Database.get_session() q = session.query(FileStore).filter( and_(FileStore.name == store_name)) # type: Query if q.count(): store = q[0] file_model = File() file_model.from_swagger_model(file, user) file_model.store = store file_model.uid = None session.add(file_model) session.commit() q = session.query(File).filter(and_(File.uid == file_model.uid)) return q.first().to_swagger_model_detailed(user=user), 200 else: return Error(code=404, message="Store not found"), 404 except Exception as e: logging.exception(e) if current_app.config['TESTING']: return Error(code=500, message="Exception: " + repr(e)), 500 else: return Error(code=500, message="Exception occurred"), 500
def get_file(store_name, uid, file_name, no_redirect=None, user=None): """ Get file :param store_name: Name of the store :type store_name: str :param uid: Unique identifier of the file :type uid: int :param file_name: Name of the file :type file_name: str :rtype: Binary """ try: q = Database.get_session().query(File).filter( and_(FileStore.name == store_name, File.uid == uid)) # type: Query if q.count(): file = q[0] if file.name == file_name: storage_plugin = Storage.factory(store_name, file.store.store_type, file.store.store_data) link = storage_plugin.download_link(file.store_data) if no_redirect: return Response(link), 213 else: return redirect(link, code=302) return Error(code=404, message="Store or file not found"), 404 except Exception as e: logging.exception(e) if current_app.config['TESTING']: return Error(code=500, message="Exception: " + repr(e)), 500 else: return Error(code=500, message="Exception occurred"), 500
def get_bag_file(store_name, bag_name, user=None): """ Get rosbag :param store_name: Name of the store :type store_name: str :param bag_name: Name of the bag :type bag_name: str :rtype: Binary """ try: session = Database.get_session() q = session.query(RosbagStore).filter( RosbagStore.name == store_name) # type: Query if q.count() == 0: return Error(code=404, message="Store not found"), 404 store = q[0] q = session.query(Rosbag).filter( and_(Rosbag.store_id == store.uid, Rosbag.name == bag_name)) # type: Query if q.count() == 0: return Error(code=404, message="Bag not found"), 404 bag = q[0] # TODO: SOME MORE ROBUST ERROR HANDLING storage_plugin = Storage.factory(store.name, store.store_type, store.store_data) link = storage_plugin.download_link(bag.store_data) return redirect(link, code=302) except Exception as e: return handle_exception(e)
def put_store_extraction_configs(store_name, config_list, user=None): """Create/update store :param store_name: Name of the store :type store_name: str :param store: List of config names :type store: List[] :rtype: List[str] """ session = Database.get_session() try: q = session.query(RosbagStore).filter(RosbagStore.name == store_name) #type: Query if q.first(): store = q.first() # Load configurations config_dict = {} for config in config_list: if config not in config_dict: q = session.query(RosbagExtractionConfiguration)\ .filter(RosbagExtractionConfiguration.name == config) if not q.first(): return Error(code=404, message="Configuration '%s' not found" % config), 404 config_dict[config] = q.first() # Assign new configurations store.auto_extraction_configs = list(config_dict.values()) session.commit() return [x.name for x in store.auto_extraction_configs] else: return Error(code=404, message="Store not found"), 404 except Exception as e: return handle_exception(e)
def get_store_extraction_configs(store_name, user=None): """Get list of auto extraction configs :param store_name: Name of the store :type store_name: str :rtype: List[BagExtractionConfiguration] """ try: q = Database.get_session().query(RosbagStore).filter(RosbagStore.name == store_name) #type: Query if q.count(): return [x.to_swagger_model(user=user) for x in q.first().auto_extraction_configs] else: return Error(code=404, message="Store not found"), 404 except Exception as e: return handle_exception(e)
def dequeue_task_inner(worker_name, tasks, labels, user=None): """Take a task from the queue # noqa: E501 :param worker_name: Name of the worker trying to acquire a task :type worker_name: str :param tasks: Tasks that the worker can do (any or a list of tasks) :type tasks: str :param labels: Labels the worker wants to do :type labels: str :rtype: TaskDetailed """ session = Database.get_session() # First find already assigned not finished tasks q = session.query(Task) # type: Query q = q.filter(Task.assigned_to == worker_name)\ .filter(Task.state < TaskState.Finished)\ .order_by(Task.priority.desc()).limit(1) if q.count(): return q.first().to_swagger_model_detailed(user=user) # Get a list of all tasks that can be done by this worker q = session.query(Task) # type: Query q = q.filter(Task.assigned_to == "")\ .filter(Task.state == TaskState.Queued)\ .order_by(Task.priority.desc()).limit(5) # TODO: Filter label and tasks possible_tasks = [] for possible_task in q: possible_tasks.append(possible_task) for task in possible_tasks: success = Task.task_assignment_query(session, task.uid, worker_name) if success: session.commit() q = session.query(Task).filter(Task.uid == task.uid) return q.first().to_swagger_model_detailed(user=user) return Error(code=204, message="No tasks in the queue"), 204
def get_extraction_config(config_name, user=None): """Get configuration details :param config_name: Name of the configuration :type config_name: str :rtype: BagExtractionConfiguration """ try: q = Database.get_session().query(RosbagExtractionConfiguration).\ filter(RosbagExtractionConfiguration.name == config_name) #type: Query if q.count(): return q[0].to_swagger_model(user=user) else: return Error(code=404, message="Configuration not found"), 404 except Exception as e: return handle_exception(e)
def new_task(task, user=None): """Create a new task # noqa: E501 :param task: The task :type task: dict | bytes :rtype: TaskDetailed """ try: if connexion.request.is_json: task = TaskDetailed.from_dict( connexion.request.get_json()) # type: TaskDetailed session = Database.get_session() hash = Task.calculate_hash(task.config) q = session.query(Task)\ .filter(Task.state < TaskState.Finished)\ .filter(Task.task == task.task)\ .filter(Task.task_hash == hash) duplicate_task = q.first() if duplicate_task: return Error(code=409, message="Duplicate task, %d, queued" % duplicate_task.uid), 409 model = Task() model.log = "" model.result = {} model.task_hash = hash model.from_swagger_model(task, user=user) model.uid = None session.add(model) session.commit() # Return a fresh copy from the DB q = session.query(Task).filter(Task.uid == model.uid) return q.first().to_swagger_model_detailed(user=user), 200 except Exception as e: return handle_exception(e)
def put_bag_tags(store_name, bag_name, tags, auto_create=None, user=None): """Change bag tags # noqa: E501 :param store_name: Name of the store :type store_name: str :param bag_name: Name of the bag :type bag_name: str :param tags: List of tags :type tags: List[] :param auto_create: Create non existing tags :type auto_create: bool :rtype: List[Tag] """ try: session = Database.get_session() bag, e = find_bag_in_database(session, store_name, bag_name) if e: return e, e.code tag_models = [] tags_unique = list(set([x.strip().lower() for x in tags])) for tag in tags_unique: q = session.query(Tag).filter(Tag.tag == tag) # type: Query if q.count() == 1: tag_models.append(q.first()) else: if auto_create: tag_models.append(Tag(tag=tag, color="")) else: return Error(code=400, message="Tag '" + tag + "' does not exist"), 400 bag.tags = tag_models session.commit() return get_bag_tags(store_name, bag_name) except Exception as e: return handle_exception(e)
def put_tag(tag, tag_obj, user=None): """Create/update tag # noqa: E501 :param tag: Name of the tag :type tag: str :param tag_obj: Tag information :type tag_obj: dict | bytes :rtype: Tag """ tag = tag.strip().lower() if connexion.request.is_json: tag_obj = SwaggerTag.from_dict(connexion.request.get_json()) # We do allow renaming tags # if tag_obj.tag != tag: # return Error(code=400, message="Path and body tag are not the same"), 400 try: session = Database.get_session() q = session.query(Tag).filter(Tag.tag == tag) # type: Query model = Tag() if q.count() == 1: model = q.first() else: if tag_obj.tag != tag: return Error( code=400, message="Path and body tag have to be equal for a new tag" ), 400 session.add(model) model.from_swagger_model(tag_obj) session.commit() return model.to_swagger_model(user=user) except Exception as e: return handle_exception(e)