automatically adding recorders, models and rooms and association of those

This commit is contained in:
2019-11-22 14:26:16 +01:00
parent 7700b4381f
commit c4b54357f7
9 changed files with 114 additions and 46 deletions

View File

@@ -15,6 +15,7 @@ from flask import Flask, jsonify
from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth, MultiAuth from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth, MultiAuth
from flask_jwt_extended import JWTManager, decode_token from flask_jwt_extended import JWTManager, decode_token
from flask_login import LoginManager from flask_login import LoginManager
from flask_restplus import abort
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS from flask_cors import CORS
from backend.config import Config from backend.config import Config
@@ -128,10 +129,16 @@ login_manager.init_app(app)
# flask_jwt_extended: to be used usually by API # flask_jwt_extended: to be used usually by API
jwt_extended = JWTManager(app) jwt_extended = JWTManager(app)
# # this is another library.... this is (probaby ->verify) used to check JWTs provided by external sources (KIT, etc.)
jwt_auth = HTTPTokenAuth('Bearer') jwt_auth = HTTPTokenAuth('Bearer')
@jwt_extended.invalid_token_loader
def unauthorized_jwt(token):
main_logger.info("Unauthorized access; invalid token provided: {}".format(token))
abort(401)
@jwt_auth.verify_token @jwt_auth.verify_token
def verify_token(token): def verify_token(token):
"""This function (and HTTPTokenAuth('Bearer')) has been defined to be used together with MultiAuth. For API calls """This function (and HTTPTokenAuth('Bearer')) has been defined to be used together with MultiAuth. For API calls

View File

@@ -8,16 +8,19 @@ import ssl
from jinja2.exceptions import TemplateNotFound from jinja2.exceptions import TemplateNotFound
from backend import app, db from backend import app, db
from backend.models import room_model, recorder_model from backend.models import room_model, recorder_model, RecorderCommand
from backend.recorder_adapters import get_defined_recorder_adapters from backend.recorder_adapters import get_defined_recorder_adapters
from backend.tools.model_updater import update_recorder_models_database from backend.tools.model_updater import update_recorder_models_database, create_default_recorders
def main(): def main():
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
db.drop_all() #db.drop_all()
db.create_all() #db.create_all()
room_model.pre_fill_table()
update_recorder_models_database(drop=False)
create_default_recorders()
print(app.config.get("SERVER_NAME", None)) print(app.config.get("SERVER_NAME", None))
server_name = app.config.get("SERVER_NAME", None) server_name = app.config.get("SERVER_NAME", None)
@@ -34,10 +37,6 @@ def main():
except Exception as e: except Exception as e:
logging.critical(e) logging.critical(e)
room_model.pre_fill_table()
update_recorder_models_database()
app.run(debug=True, host="0.0.0.0") app.run(debug=True, host="0.0.0.0")

View File

@@ -29,12 +29,21 @@ recorder_model = api_recorder.model('Recorder', {
'created_at': fields.DateTime(required=False, description='Creation date of the recorder'), 'created_at': fields.DateTime(required=False, description='Creation date of the recorder'),
'last_time_modified': fields.DateTime(required=False, description='Creation date of the recorder'), 'last_time_modified': fields.DateTime(required=False, description='Creation date of the recorder'),
'name': fields.String(min_length=3, required=True, description='The recorder\'s name'), 'name': fields.String(min_length=3, required=True, description='The recorder\'s name'),
'model_name': fields.String(min_length=3, required=True,
description='The recorder\'s model name (might slightly '
'differ from actual name of the model)'),
'serial_number': fields.String(required=False, description='The recorder\'s serial number'),
'firmware_version': fields.String(required=False, description='The recorder\'s firmware'),
'description': fields.String(required=False, description='The recorder\'s description'), 'description': fields.String(required=False, description='The recorder\'s description'),
'locked': fields.Boolean(required=False, description='Indicates whether the recorder settings can be altered'), 'locked': fields.Boolean(required=False, description='Indicates whether the recorder settings can be altered'),
'lock_message': fields.String(required=False, description='Optional: message explaining lock state'), 'lock_message': fields.String(required=False, description='Optional: message explaining lock state'),
'offline': fields.Boolean(required=False, 'offline': fields.Boolean(required=False,
description='Should be set when recorder is disconnected for maintenance, etc.'), description='Should be set when recorder is disconnected for maintenance, etc.'),
'additional_camera_connected': fields.Boolean(required=False,
description='Indicates whether an additional camera is connected'),
'ip': fields.String(required=False, description='The recorder\'s IP address'), 'ip': fields.String(required=False, description='The recorder\'s IP address'),
'mac': fields.String(required=False, description='The recorder\'s IP address'),
'network_name': fields.String(required=False, description='The recorder\'s network name'), 'network_name': fields.String(required=False, description='The recorder\'s network name'),
'ssh_port': fields.Integer(required=True, default=22, description='The recorder\'s SSH port number'), 'ssh_port': fields.Integer(required=True, default=22, description='The recorder\'s SSH port number'),
'telnet_port': fields.Integer(required=True, default=23, description='The recorder\'s telnet port number'), 'telnet_port': fields.Integer(required=True, default=23, description='The recorder\'s telnet port number'),
@@ -91,4 +100,3 @@ recorder_model_model = api_recorder.model('Recorder Model', {
description='Model of the recorder.'), description='Model of the recorder.'),
'commands': fields.List(fields.Nested(recorder_command_model), attribute="recorder_commands") 'commands': fields.List(fields.Nested(recorder_command_model), attribute="recorder_commands")
}) })

Binary file not shown.

View File

@@ -60,11 +60,11 @@ class RecorderModel(db.Model):
return RecorderModel.query.filter(RecorderModel.checksum == md5_sum).first() return RecorderModel.query.filter(RecorderModel.checksum == md5_sum).first()
@hybrid_property @hybrid_property
def requires_user(self): def requires_username(self):
return self._requires_user > 0 return self._requires_user > 0
@requires_user.setter @requires_username.setter
def requires_user(self, val: bool): def requires_username(self, val: bool):
self._requires_user = 1 if val else 0 self._requires_user = 1 if val else 0
@hybrid_property @hybrid_property
@@ -87,10 +87,9 @@ class Recorder(db.Model):
name = db.Column(db.Unicode(63), unique=True, nullable=False) name = db.Column(db.Unicode(63), unique=True, nullable=False)
model_name = db.Column(db.Unicode(63), unique=False, nullable=False) model_name = db.Column(db.Unicode(63), unique=False, nullable=False)
serial_number = db.Column(db.Unicode(63), unique=True, nullable=True) serial_number = db.Column(db.Unicode(63), unique=True, nullable=True)
locked = db.Column(db.Boolean, default=False) locked = db.Column(db.Boolean, default=False) # no modifications allowed
lock_message = db.Column(db.String, nullable=True, default=None) lock_message = db.Column(db.String, nullable=True, default=None)
in_maintenance = db.Column(db.Boolean, default=False) offline = db.Column(db.Boolean, default=False) # maintenance, etc.
offline = db.Column(db.Boolean, default=False)
description = db.Column(db.Unicode(255), unique=False, nullable=True, default="") description = db.Column(db.Unicode(255), unique=False, nullable=True, default="")
_mac = db.Column(db.String(17), unique=True, nullable=True) _mac = db.Column(db.String(17), unique=True, nullable=True)
_ip = db.Column(db.String(15), unique=True, nullable=True, default=None) _ip = db.Column(db.String(15), unique=True, nullable=True, default=None)
@@ -122,6 +121,13 @@ class Recorder(db.Model):
def get_by_identifier(identifier): def get_by_identifier(identifier):
return Recorder.query.filter(Recorder.id == identifier).first() return Recorder.query.filter(Recorder.id == identifier).first()
@staticmethod
def get_by_mac(mac: str):
if mac is None or mac == '':
return None
mac = mac.replace('-', ':').lower()
return Recorder.query.filter(Recorder._mac == mac).first()
@staticmethod @staticmethod
def get_all(): def get_all():
return Recorder.query.all() return Recorder.query.all()

View File

@@ -3,6 +3,7 @@
Models for lecture recorder Models for lecture recorder
""" """
import json import json
import logging
from sqlalchemy import MetaData, CheckConstraint from sqlalchemy import MetaData, CheckConstraint
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
@@ -12,6 +13,8 @@ from datetime import datetime, timedelta
from backend import db, app, login_manager from backend import db, app, login_manager
from backend.tools.scrape_rooms import scrape_rooms from backend.tools.scrape_rooms import scrape_rooms
logger = logging.getLogger("lrc."+__name__)
metadata = MetaData() metadata = MetaData()
@@ -74,12 +77,16 @@ class Room(db.Model):
def pre_fill_table(): def pre_fill_table():
rooms = scrape_rooms() rooms = scrape_rooms()
i_tunes_u_mappings = [Room(name=room['name'], number=room['room_number'], logger.debug("tada")
logger.debug("got {} rooms".format(len(rooms)))
db_rooms = [Room(name=room['name'], number=room['room_number'],
building_name=room['building_name'], building_number=room['building_number']) for room in building_name=room['building_name'], building_number=room['building_number']) for room in
rooms] rooms]
try: try:
db.session.bulk_save_objects(i_tunes_u_mappings) db.session.bulk_save_objects(db_rooms)
db.session.commit() db.session.commit()
logger.debug("rooms commited to DB!")
except IntegrityError as e: except IntegrityError as e:
logger.error("Could not add rooms! ({})".format(e))
db.session.rollback() db.session.rollback()

View File

@@ -1,7 +1,6 @@
import hashlib import hashlib
def calculate_md5_checksum(string_to_md5_sum: str): def calculate_md5_checksum(string_to_md5_sum: str):
return hashlib.md5(string_to_md5_sum.encode('utf-8')).hexdigest() return hashlib.md5(string_to_md5_sum.encode('utf-8')).hexdigest()

View File

@@ -21,21 +21,27 @@ from backend.models.recorder_model import RecorderModel, RecorderCommand, Record
from backend.recorder_adapters import get_defined_recorder_adapters from backend.recorder_adapters import get_defined_recorder_adapters
from backend.tools.helpers import calculate_md5_checksum from backend.tools.helpers import calculate_md5_checksum
logger = logging.getLogger("lrc." + __name__)
KNOWN_RECORDERS = {re.compile(r'(?P<name>SMP)[\s]*(?P<number>[\d]+)[\s]*.?[\s]*(?P<options>[\S]*)'): 'SMP', KNOWN_RECORDERS = {re.compile(r'(?P<name>SMP)[\s]*(?P<number>[\d]+)[\s]*.?[\s]*(?P<options>[\S]*)'): 'SMP',
re.compile( re.compile(
r'(?P<name>LectureRecorder X2|LectureRecorder|VGADVI Recorder|DVI Broadcaster DL|DVIRecorderDL)'): 'Epiphan'} r'(?P<name>LectureRecorder X2|LectureRecorder|VGADVI Recorder|DVI Broadcaster DL|DVIRecorderDL)'): 'Epiphan'}
def create_recorder_commands_for_recorder_adapter(command_definitions: dict, recorder_model: RecorderModel): def create_recorder_commands_for_recorder_adapter(command_definitions: dict, recorder_model: RecorderModel):
logger.debug("Received {} command definitions to create "
"for recoder model {}".format(len(command_definitions),
recorder_model.record_adapter_id))
existing_recorder_commands = RecorderCommand.query.filter( existing_recorder_commands = RecorderCommand.query.filter(
and_(RecorderCommand.name.in_(command_definitions.keys())), and_(
RecorderCommand.name.in_([recorder_model.record_adapter_id + ":" + k for k in command_definitions.keys()])),
RecorderCommand.recorder_model == recorder_model) RecorderCommand.recorder_model == recorder_model)
existing_commands = set() existing_commands = set()
for existing_command in existing_recorder_commands: for existing_command in existing_recorder_commands:
existing_commands.add(existing_command.name) existing_commands.add(existing_command.name)
args = command_definitions.get(existing_command.name) args = command_definitions.get(existing_command.name)
if dumps(existing_command.parameters) != dumps(args): if dumps(existing_command.parameters) != dumps(args):
logging.warning( logger.warning(
"The function definition {} collides with an existing definition of the same name " "The function definition {} collides with an existing definition of the same name "
"but different parameters!".format( "but different parameters!".format(
existing_command.name)) existing_command.name))
@@ -44,15 +50,21 @@ def create_recorder_commands_for_recorder_adapter(command_definitions: dict, rec
db.session.commit() db.session.commit()
for c_d in set(command_definitions.keys()) - existing_commands: for c_d in set(command_definitions.keys()) - existing_commands:
# print(c_d)
args = command_definitions.get(c_d) args = command_definitions.get(c_d)
# print(args)
# create new recorder command(s) # create new recorder command(s)
r_c = RecorderCommand(name=c_d, parameters=args, recorder_model=recorder_model) r_c = RecorderCommand(name=recorder_model.record_adapter_id + ":" + c_d, parameters=args,
recorder_model=recorder_model)
db.session.add(r_c) db.session.add(r_c)
db.session.flush()
db.session.commit() db.session.commit()
def update_recorder_models_database(): def update_recorder_models_database(drop: bool = False):
if drop:
for r in RecorderModel.get_all():
db.session.delete(r)
db.session.commit()
r_as = get_defined_recorder_adapters() r_as = get_defined_recorder_adapters()
for r_a in r_as: for r_a in r_as:
try: try:
@@ -64,35 +76,40 @@ def update_recorder_models_database():
db.session.add(r_m) db.session.add(r_m)
db.session.flush() db.session.flush()
db.session.refresh(r_m) db.session.refresh(r_m)
logger.debug("Creating command definitions for rec mod adapter {}".format(r_m.record_adapter_id))
create_recorder_commands_for_recorder_adapter(r_a["commands"], r_m)
else: else:
if not r_m.model_name == r_a["name"]: if r_m.model_name != r_a["name"]:
r_m.model_name = r_a["name"] r_m.model_name = r_a["name"]
r_m.last_time_modified = datetime.utcnow() r_m.last_time_modified = datetime.utcnow()
if not model_checksum == r_m.checksum: if model_checksum != r_m.checksum:
r_m.last_time_modified = datetime.utcnow() r_m.last_time_modified = datetime.utcnow()
r_m.checksum = model_checksum r_m.checksum = model_checksum
r_m.last_checksum_change = datetime.utcnow() r_m.last_checksum_change = datetime.utcnow()
logger.debug("Updating command definitions for rec mod adapter {}".format(r_m.record_adapter_id))
create_recorder_commands_for_recorder_adapter(r_a["commands"], r_m) create_recorder_commands_for_recorder_adapter(r_a["commands"], r_m)
except IntegrityError as e: except IntegrityError as e:
logger.error(e)
db.session.rollback() db.session.rollback()
db.session.commit() db.session.commit()
def get_recorder_room(rec: dict) -> Room(): def get_recorder_room(rec: dict) -> Room():
rooms = Room.get_by_building_number(rec.get('building', None)) rooms = Room.get_by_building_number(rec.get('building', None))
if rooms.count() <= 1: if rooms.count() <= 0:
logger.warning("Building {} unknown! Can not find room for recorder {}.".format(rec['building'], rec['name']))
return None
if rooms.count() == 1:
return rooms.first() return rooms.first()
room_name = rec.get('room') room_name = rec.get('room')
room_name = room_name.replace('', ' ')
for room in rooms: for room in rooms:
if all([r_n in room.name for r_n in room_name.split()]): if all([r_n in room.name for r_n in room_name.split()]):
return room return room
logger.warning("No room found for recorder {}".format(rec['name']))
def create_default_recorders(): def create_default_recorders():
models = RecorderModel.get_all()
for m in models:
print(m)
f = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, 'models', 'initial_recorders.json')) f = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, 'models', 'initial_recorders.json'))
with open(f, 'r') as json_file: with open(f, 'r') as json_file:
recorders = json.load(json_file)['recorders'] recorders = json.load(json_file)['recorders']
@@ -112,39 +129,64 @@ def create_default_recorders():
model_number = match.groupdict().get('number', None) model_number = match.groupdict().get('number', None)
options = match.groupdict().get('options', None) options = match.groupdict().get('options', None)
if options is not None and options != "":
options = options.split(',')
if model_number is not None: if model_number is not None:
model_name = name + model_number[:-1] # just get prefix (remove last digit) model_name = name + " " + model_number
model_search_name = name + model_number[:-1] # just get prefix (remove last digit); SMP 35x -> 35
else: else:
model_name = KNOWN_RECORDERS[k_r] model_name = name
rec_model = RecorderModel.get_where_adapter_id_contains(model_name) model_search_name = KNOWN_RECORDERS[k_r]
rec_model = RecorderModel.get_where_adapter_id_contains(model_search_name)
rec = Recorder(name=room_rec_name + " Recorder", model_name=model_name, recorder_model=rec_model, rec = Recorder.get_by_mac(mac)
username=username, password=password, firmware_version=firmware_version) if rec is None:
rec = Recorder.get_by_name(room_rec_name + " Recorder")
if rec is None:
rec = Recorder()
rec.mac = mac rec.mac = mac
db.session.add(rec)
rec.name = room_rec_name + " Recorder"
rec.model_name = model_name
rec.recorder_model = rec_model
rec.username = username
rec.password = password
rec.firmware_version = firmware_version
rec.ip = ip4 rec.ip = ip4
rec.additional_camera_connected = additional_camera rec.additional_camera_connected = additional_camera
rec.additional_note = description rec.additional_note = description
rec.configured_options = options
rec.room = get_recorder_room(r) rec.room = get_recorder_room(r)
print(rec)
db.session.add(rec)
db.session.flush() db.session.flush()
db.session.refresh(rec) db.session.refresh(rec)
print(recorders) db.session.commit()
if __name__ == '__main__': if __name__ == '__main__':
# for r in Room.get_all():
# print(r)
# exit()
commands = RecorderCommand.get_all()
for c in commands:
print(c)
print(c.recorder_model)
for r_m in RecorderModel.get_all():
print(r_m.recorder_commands)
exit()
recorders = Recorder.get_all() recorders = Recorder.get_all()
for r in recorders: for r in recorders:
if r.room is None:
print("{}: {}".format(r, r.room)) print("{}: {}".format(r, r.room))
# db.drop_all() # db.drop_all()
# db.create_all() # db.create_all()
# update_recorder_models_database() update_recorder_models_database()
# create_default_recorders() create_default_recorders()
# db.session.commit() db.session.commit()
# print(get_recorder_room({"room": "Grosser Hörsaal Bauingenieure", "building": "10.50"})) # print(get_recorder_room({"room": "Grosser Hörsaal Bauingenieure", "building": "10.50"}))
# print(get_recorder_room({"room": "Grosser Hörsaal Bauingenieure", "building": "30.95"})) # print(get_recorder_room({"room": "Grosser Hörsaal Bauingenieure", "building": "30.95"}))