From 6dc84b57ac1bf6e0ee5c195c934141a77cfca57d Mon Sep 17 00:00:00 2001 From: VarLeif Date: Mon, 24 Feb 2020 06:53:39 +0200 Subject: [PATCH 1/3] Added GET methods for public terminal, need to complete the API with the PATCH & PUT methods. --- director/api/public.py | 62 +++++++++++++++++++++++++++++++++++++- director/model/terminal.py | 19 +++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/director/api/public.py b/director/api/public.py index 4d1d0aa..dbe9eef 100644 --- a/director/api/public.py +++ b/director/api/public.py @@ -1,4 +1,4 @@ -from flask import Blueprint +from flask import Blueprint, request, Response, jsonify from director.model import User PublicAPI = Blueprint('public_api', __name__, url_prefix='/api/public') @@ -7,3 +7,63 @@ def get_user(username): user = User.query.filter_by(username=username).first_or_404() return user.serialize() + + +from director.model import Terminal as terminal_model + +@PublicAPI.route('/lab//terminal/', methods=["GET"]) +@PublicAPI.route('/lab//terminals', methods=["GET"]) +def get_terminals(lab, terminal = None): + # GET: /terminals + if terminal == None: + terminals = terminal_model.query.filter_by(lab_id=lab).all() + jsonArray = {'terminals': []} + + for _ in terminals: + jsonArray['terminals'].append(_.serialize()) + + return jsonArray + #end if + + # GET: /terminal/ + terminal_result = terminal_model.query.filter_by(lab_id=lab,id=terminal).first_or_404() + + return terminal_result.serialize() + + +@PublicAPI.route('/lab//terminal/', methods=["PATCH"]) +def patch_terminal(lab, terminal): + + # if there is only one element to update then continue + if len(request.json) == 1: + + terminal_result = terminal_model.query.filter_by(lab_id=lab,id=terminal).first_or_404() + + # TODO: Change according to logic of this route + # item = request.json.item + + statusCode = terminal_result.update_item(None) + + resp = Response(status=statusCode) + return resp + + # else return error + error = { + "error": "Bad request" + } + + resp = jsonify(error) + resp.status_code = 400 + + return resp + +# TODO +# @PublicAPI.route('/lab//terminal//', methods=["PATCH"]) +# def patch_terminal_c(lab, terminal): +# command = request.json['command'] +# return {} + +# @PublicAPI.route('/lab//terminal/', methods=["PUT"]) +# def update_terminal(lab, terminal): +# return {} + \ No newline at end of file diff --git a/director/model/terminal.py b/director/model/terminal.py index 4177892..e321f30 100644 --- a/director/model/terminal.py +++ b/director/model/terminal.py @@ -2,8 +2,10 @@ from sqlalchemy import ForeignKey -from director import db +from marshmallow import Schema, fields +from marshmallow_enum import EnumField +from director import db class Status(enum.Enum): down = 0 @@ -11,6 +13,14 @@ class Status(enum.Enum): up = 2 logged_in = 3 +class TerminalSchema(Schema): + id = fields.Int(dump_only=True) + host_name = fields.Str() + ip = fields.Str() + status = EnumField(Status) + room = fields.Str() + lab_id = fields.Int(dump_only=True) + class Terminal(db.Model): id = db.Column(db.Integer, primary_key=True) @@ -21,6 +31,13 @@ class Terminal(db.Model): lab_id = db.Column(db.Integer, ForeignKey('lab.id')) sessions = db.relationship("Session", backref="terminal") + def serialize(self): + return TerminalSchema().dump(self) + + def update_item(self, remote): + # TODO: add what will patch do for one variable + return 204 + def __repr__(self): return '' % (self.id, self.host_name, From 31c68563992c64249be67942903712db9b39cbe2 Mon Sep 17 00:00:00 2001 From: VarLeif Date: Thu, 27 Feb 2020 00:22:50 +0200 Subject: [PATCH 2/3] Almost finished with the public-api-terminal management for now. Need to add implementation with the command (shutdown, restart, etc.) and later add the authentication(?) with the jwt. --- director/api/public.py | 83 +++++++++++++++++++++++++++----------- director/model/terminal.py | 12 ++++-- 2 files changed, 68 insertions(+), 27 deletions(-) diff --git a/director/api/public.py b/director/api/public.py index dbe9eef..fd8bbf3 100644 --- a/director/api/public.py +++ b/director/api/public.py @@ -1,5 +1,6 @@ from flask import Blueprint, request, Response, jsonify from director.model import User +from functools import wraps PublicAPI = Blueprint('public_api', __name__, url_prefix='/api/public') @@ -30,40 +31,74 @@ def get_terminals(lab, terminal = None): return terminal_result.serialize() +# Checks if the client sends a (not-empty-also) json +def empty_json_body(f): + @wraps(f) + def decorated(*args, **kwargs): + try: + # Check if empty + if len(request.get_json()) == 0: + return jsonify({'message': 'Provide some values.'}), 400 + except: + # There was no json in request data + return jsonify({"message": "Provide a json body."}), 400 + return f(*args,**kwargs) + return decorated @PublicAPI.route('/lab//terminal/', methods=["PATCH"]) +@empty_json_body def patch_terminal(lab, terminal): - - # if there is only one element to update then continue - if len(request.json) == 1: - terminal_result = terminal_model.query.filter_by(lab_id=lab,id=terminal).first_or_404() + terminal_result = terminal_model.query.filter_by(lab_id=lab,id=terminal).first_or_404() - # TODO: Change according to logic of this route - # item = request.json.item + valid_options = ['host_name', 'ip', 'status', 'room', 'lab_id'] + changes = {} + # Keep only the values we need + for key, value in dict(request.json).items(): + if key in valid_options: + changes[key] = value + + statusCode = terminal_result.update_items(changes) + + resp = Response(status=statusCode) + return resp + +@PublicAPI.route('/lab//terminal//', methods=["PATCH"]) +def patch_terminal_c(lab, terminal, command): + + valid_commands = ['restart','shutdown','hibernate','log-out'] - statusCode = terminal_result.update_item(None) + if command not in valid_commands: + error = { + "error": "Bad request" + } + + resp = jsonify(error) + resp.status_code = 406 - resp = Response(status=statusCode) return resp - # else return error - error = { - "error": "Bad request" - } - - resp = jsonify(error) - resp.status_code = 400 + # TODO + # communicate with user-agent + + resp = Response(status=501) return resp -# TODO -# @PublicAPI.route('/lab//terminal//', methods=["PATCH"]) -# def patch_terminal_c(lab, terminal): -# command = request.json['command'] -# return {} +@PublicAPI.route('/lab//terminal/', methods=["PUT"]) +@empty_json_body +def update_terminal(lab, terminal): -# @PublicAPI.route('/lab//terminal/', methods=["PUT"]) -# def update_terminal(lab, terminal): -# return {} - \ No newline at end of file + terminal_result = terminal_model.query.filter_by(lab_id=lab,id=terminal).first_or_404() + + valid_options = ['host_name', 'ip', 'status', 'room', 'lab_id'] + changes = {} + # Keep only the values we need + for key, value in dict(request.json).items(): + if key in valid_options: + changes[key] = value + + statusCode = terminal_result.update_items(changes) + + resp = Response(status=statusCode) + return resp \ No newline at end of file diff --git a/director/model/terminal.py b/director/model/terminal.py index e321f30..d40431f 100644 --- a/director/model/terminal.py +++ b/director/model/terminal.py @@ -34,9 +34,15 @@ class Terminal(db.Model): def serialize(self): return TerminalSchema().dump(self) - def update_item(self, remote): - # TODO: add what will patch do for one variable - return 204 + def update_items(self, changes): + madeChanges = 204 + for key, value in changes.items(): + madeChanges = 200 + print("Key is ", key,"\tValue: ",value) + setattr(self, key, value) + db.session.commit() + + return madeChanges def __repr__(self): return '' % (self.id, From 0eab9985a8c6386df166aa7754166650d82d9de0 Mon Sep 17 00:00:00 2001 From: VarLeif Date: Fri, 28 Feb 2020 00:50:23 +0200 Subject: [PATCH 3/3] Need to know what we will do with the status parameter. Right now it only works if the client sends the values down, locked, up and logged_in. Also later I will have to implement the communication with the agent of the terminal for the command execution --- director/api/public.py | 74 ++++++++++++++++++-------------------- director/model/terminal.py | 22 +++++++++--- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/director/api/public.py b/director/api/public.py index fd8bbf3..c192cbd 100644 --- a/director/api/public.py +++ b/director/api/public.py @@ -1,4 +1,4 @@ -from flask import Blueprint, request, Response, jsonify +from flask import Blueprint, request from director.model import User from functools import wraps @@ -24,7 +24,7 @@ def get_terminals(lab, terminal = None): jsonArray['terminals'].append(_.serialize()) return jsonArray - #end if + #end if: GET: /terminals # GET: /terminal/ terminal_result = terminal_model.query.filter_by(lab_id=lab,id=terminal).first_or_404() @@ -38,67 +38,61 @@ def decorated(*args, **kwargs): try: # Check if empty if len(request.get_json()) == 0: - return jsonify({'message': 'Provide some values.'}), 400 + return {'message': 'Provide some values.'}, 400 except: # There was no json in request data - return jsonify({"message": "Provide a json body."}), 400 + return {"message": "Provide a json body."}, 400 return f(*args,**kwargs) return decorated -@PublicAPI.route('/lab//terminal/', methods=["PATCH"]) +@PublicAPI.route('/lab//terminal/', methods=["PATCH", "PUT"]) @empty_json_body def patch_terminal(lab, terminal): terminal_result = terminal_model.query.filter_by(lab_id=lab,id=terminal).first_or_404() - + valid_options = ['host_name', 'ip', 'status', 'room', 'lab_id'] + changes = {} - # Keep only the values we need for key, value in dict(request.json).items(): if key in valid_options: + value = None if value == "" else value changes[key] = value + + # If not any value is assigned, assign the uri's lab otherwise the terminal + # will be lost forever from the client (only the database will be able to see it) + if changes.get("lab_id") == None: + changes["lab_id"] = lab - statusCode = terminal_result.update_items(changes) + # possible TODO: check status value (is it 0 1 2 3) or (down, locked, up, logged_in) + + # PATCH: Update only the parameters we are given + if request.method == 'PATCH': + if changes['ip'] == None: # IP is not nullable + return {"error": "Terminal IP can't be null or empty"}, 400 + + return terminal_result.update(changes) + # end if:PATCH + + # PUT: Update all parameters + for option in valid_options: + if option not in changes.keys(): + changes[option] = None - resp = Response(status=statusCode) - return resp + if changes['ip'] == None: # IP is not nullable + return {"error": "Terminal IP can't be null or empty"}, 400 + + return terminal_result.update_all(changes) @PublicAPI.route('/lab//terminal//', methods=["PATCH"]) def patch_terminal_c(lab, terminal, command): valid_commands = ['restart','shutdown','hibernate','log-out'] - + if command not in valid_commands: - error = { - "error": "Bad request" - } - - resp = jsonify(error) - resp.status_code = 406 - - return resp + return {"error": "Invalid command"}, 406 # TODO # communicate with user-agent - resp = Response(status=501) - - return resp - -@PublicAPI.route('/lab//terminal/', methods=["PUT"]) -@empty_json_body -def update_terminal(lab, terminal): - - terminal_result = terminal_model.query.filter_by(lab_id=lab,id=terminal).first_or_404() - - valid_options = ['host_name', 'ip', 'status', 'room', 'lab_id'] - changes = {} - # Keep only the values we need - for key, value in dict(request.json).items(): - if key in valid_options: - changes[key] = value - - statusCode = terminal_result.update_items(changes) - - resp = Response(status=statusCode) - return resp \ No newline at end of file + return {"message": "Not implemented"}, 501 \ No newline at end of file diff --git a/director/model/terminal.py b/director/model/terminal.py index d40431f..10c7d4a 100644 --- a/director/model/terminal.py +++ b/director/model/terminal.py @@ -34,15 +34,29 @@ class Terminal(db.Model): def serialize(self): return TerminalSchema().dump(self) - def update_items(self, changes): + def update_all(self, changes): + try: + self.host_name = changes["host_name"] + self.ip = changes["ip"] + self.status = Status.down if changes["status"] == None else changes["status"] + self.room = changes["room"] + self.lab_id = changes["lab_id"] + db.session.commit() + except: + return {"error", "Possible duplicate host name or IP."}, 400 + + return {"message": "Update successful"} + + def update(self, changes): madeChanges = 204 for key, value in changes.items(): madeChanges = 200 - print("Key is ", key,"\tValue: ",value) setattr(self, key, value) + try: db.session.commit() - - return madeChanges + except: + return {"error", "Possible duplicate host name or IP."}, 400 + return {"Success": "Updated."}, madeChanges def __repr__(self): return '' % (self.id,