diff --git a/requirements.txt b/requirements.txt index eec70c7..aca0b9b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,12 +2,5 @@ sentry_sdk[flask] gunicorn Flask Flask-RESTful -sqlalchemy -flask_sqlalchemy -marshmallow -marshmallow-sqlalchemy -flask-marshmallow paho-mqtt -flask-mqtt -psycopg2-binary -py-healthcheck \ No newline at end of file +flask-mqtt \ No newline at end of file diff --git a/src/app.py b/src/app.py index 8235483..111f5d7 100644 --- a/src/app.py +++ b/src/app.py @@ -1,21 +1,17 @@ #!/usr/bin/env python3 import logging from flask import Flask +from flask_mqtt import logger from flask_restful import Api import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration from sentry_sdk.integrations.logging import LoggingIntegration -from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration -from healthcheck import HealthCheck from resources import * from config import * -from db import db -from marshm import ma from mqtt_flask_instance import mqtt from mqtt_methods import handle_status_message -from healthchecks import health_database_status """ Main Flask RESTful API @@ -33,7 +29,7 @@ if SENTRY_DSN: ) sentry_sdk.init( dsn=SENTRY_DSN, - integrations=[FlaskIntegration(), sentry_logging, SqlalchemyIntegration()], + integrations=[FlaskIntegration(), sentry_logging], traces_sample_rate=1.0, send_default_pii=True, release=RELEASE_ID, @@ -48,18 +44,10 @@ app.config['MQTT_BROKER_PORT'] = MQTT_PORT app.config['MQTT_USERNAME'] = MQTT_USERNAME app.config['MQTT_PASSWORD'] = MQTT_PASSWORD app.config['MQTT_REFRESH_TIME'] = 1.0 # refresh time in seconds -app.config['SQLALCHEMY_DATABASE_URI'] = \ - f"postgresql://{POSTGRES_USERNAME}:{POSTGRES_PASSWORD}@{POSTGRES_HOSTNAME}:5432/{POSTGRES_DB}?sslmode=require" api = Api(app) -db.init_app(app) ma.init_app(app) mqtt.init_app(app) -health = HealthCheck() - -with app.app_context(): - db.create_all() - @mqtt.on_log() def handle_logging(client, userdata, level, buf): @@ -80,15 +68,9 @@ def handle_status_message_proxy(*args, **kwargs): handle_status_message(*args, **kwargs) -api.add_resource(AllDevicesResource, "/devices") api.add_resource(AllDevicesOfflineResource, "/devices/offline") api.add_resource(AllDevicesOnlineResource, "/devices/online") -api.add_resource(DeviceResource, "/devices/{deviceid}") api.add_resource(DeviceOfflineResrouce, "/devices/{deviceid}/offline") api.add_resource(DeviceOnlineResrouce, "/devices/{deviceid}/online") -api.add_resource(SensorResource, "/devices/{deviceid}/{sensorid}") api.add_resource(SensorOfflineResource, "/devices/{deviceid}/{sensorid}/offline") api.add_resource(SensorOnlineResource, "/devices/{deviceid}/{sensorid}/online") - -health.add_check(health_database_status) -app.add_url_rule("/healthz", "healthcheck", view_func=lambda: health.run()) diff --git a/src/config.py b/src/config.py index 762c396..6edb314 100644 --- a/src/config.py +++ b/src/config.py @@ -20,11 +20,6 @@ SENTRY_DSN = os.environ.get("SENTRY_DSN") RELEASE_ID = os.environ.get("RELEASE_ID", "test") RELEASEMODE = os.environ.get("CNC_SERVICE_RELEASEMODE", "dev") -POSTGRES_HOSTNAME = os.getenv("CNC_POSTGRES_HOSTNAME", "localhost") -POSTGRES_USERNAME = os.getenv("CNC_POSTGRES_USERNAME", "cnc-service") -POSTGRES_PASSWORD = os.getenv("CNC_POSTGRES_PASSWORD", "cnc-service") -POSTGRES_DB = os.getenv("CNC_POSTGRES_DB", "cnc-service") - MQTT_HOSTNAME = os.getenv("CNC_MQTT_HOSTNAME", "localhost") MQTT_PORT = os.getenv("CNC_MQTT_PORT", "1883") MQTT_USERNAME = os.getenv("CNC_MQTT_USERNAME", "cnc-service") diff --git a/src/db.py b/src/db.py deleted file mode 100644 index e56aa1f..0000000 --- a/src/db.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 -from flask_sqlalchemy import SQLAlchemy - -""" -SQLAlchemy definition -""" - -__author__ = '@tormakris' -__copyright__ = "Copyright 2020, Birbnetes Team" -__module_name__ = "db" -__version__text__ = "1" - -db = SQLAlchemy() diff --git a/src/healthchecks.py b/src/healthchecks.py deleted file mode 100644 index e4a11e7..0000000 --- a/src/healthchecks.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 - -from db import db - -""" -Healthchek functions -""" - -__author__ = "@tormakris" -__copyright__ = "Copyright 2020, Birbnetes Team" -__module_name__ = "healthchecks" -__version__text__ = "1" - - -def health_database_status(): - is_database_working = True - output = 'database is ok' - try: - db.session.execute('SELECT 1') - except Exception as e: - output = str(e) - is_database_working = False - return is_database_working, output \ No newline at end of file diff --git a/src/marshm.py b/src/marshm.py deleted file mode 100644 index c996091..0000000 --- a/src/marshm.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -from flask_marshmallow import Marshmallow - -""" -Marshmallow definition -""" - -__author__ = '@tormakris' -__copyright__ = "Copyright 2020, Birbnetes Team" -__module_name__ = "marshm" -__version__text__ = "1" - -ma = Marshmallow() diff --git a/src/models.py b/src/models.py deleted file mode 100644 index d2ff7de..0000000 --- a/src/models.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -import enum -from sqlalchemy.sql import func -from db import db - -""" -SQLAlchemy models -""" - -__author__ = '@tormakris' -__copyright__ = "Copyright 2020, Birbnetes Team" -__module_name__ = "models" -__version__text__ = "1" - - -class DeviceStatusEnum(enum.Enum): - error = "error" - online = "online" - offline = "offline" - - -class SensorStatusEnum(enum.Enum): - unknown = "unknown" - online = "online" - offline = "offline" - - -class Device(db.Model): - __tablename__ = 'device' - id = db.Column(db.Integer, primary_key=True) - deviceid = db.Column(db.String, nullable=False) - status = db.Column(db.Enum(DeviceStatusEnum), nullable=False) - url = db.Column(db.String, nullable=False) - lastupdate = db.Column(db.TIMESTAMP, nullable=False, server_default=func.now(), onupdate=func.current_timestamp()) - sensors = db.relationship("Sensor") - - -class Sensor(db.Model): - __tablename__ = 'sensor' - id = db.Column(db.Integer, primary_key=True) - sensorid = db.Column(db.String, nullable=False) - status = db.Column(db.Enum(SensorStatusEnum), nullable=False) - device_id = db.Column(db.Integer, db.ForeignKey('device.id')) diff --git a/src/mqtt_methods.py b/src/mqtt_methods.py index 5ce017d..aa9fbc3 100644 --- a/src/mqtt_methods.py +++ b/src/mqtt_methods.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 from flask import current_app as app import config -from db import db -from schemas import DeviceSchema, SensorSchema """ MQTT Stuff @@ -13,9 +11,6 @@ __copyright__ = "Copyright 2020, Birbnetes Team" __module_name__ = "mqtt_methods" __version__text__ = "1" -deviceschema = DeviceSchema(many=False) -sensorschema = SensorSchema(many=False) - def handle_status_message(client, userdata, message): data = dict( @@ -27,18 +22,11 @@ def handle_status_message(client, userdata, message): ids = data['topic'].replace(f"{config.MQTT_STATUS_TOPIC}/", "").split("/") if len(ids) == 1: data['payload']['deviceID'] = ids[0] - status_message = deviceschema.load(data['payload'], session=db.session, transient=True).data app.logger.info(f"Recieved status message from {data['payload']['deviceID']}, persisting to db.") - db.session.merge(status_message) else: if len(ids) == 2: data['payload']['deviceID'] = ids[0] data['payload']['sensorID'] = ids[1] - status_message = sensorschema.load(data['payload'], session=db.session, transient=True).data app.logger.info(f"Recieved status message from sensor {data['payload']['sensorID']}, persisting to db.") - db.session.merge(status_message) except Exception as e: - db.session.rollback() app.logger.exception(e) - else: - db.session.commit() diff --git a/src/resources.py b/src/resources.py index 12cdb1b..da16ce3 100644 --- a/src/resources.py +++ b/src/resources.py @@ -1,10 +1,7 @@ #!/usr/bin/env python3 from flask_restful import Resource -from db import db from mqtt_flask_instance import mqtt import config -import models -import schemas """ Flask Restful endpoints @@ -16,21 +13,6 @@ __module_name__ = "resources" __version__text__ = "1" -class AllDevicesResource(Resource): - """ - Query all known devices - """ - alldeviceschema = schemas.DeviceSchema(many=True) - - def get(self): - """ - Get all stored items - :return: - """ - alldevices = models.Device.query.all() - return self.alldeviceschema.dump(list(alldevices)), 200 - - class AllDevicesOfflineResource(Resource): """ Shut down all devices @@ -55,22 +37,6 @@ class AllDevicesOnlineResource(Resource): mqtt.publish(f"{config.MQTT_COMMAND_TOPIC}/+", {"command": "online"}) -class DeviceResource(Resource): - """ - Query and control a particular device - """ - deviceschema = schemas.DeviceSchema(many=False) - - def get(self, deviceid: str): - """ - Query a device - :param deviceid: UUID of device - :return: - """ - device = models.Device.query.filter_by(id=deviceid).first_or_404() - return self.deviceschema.dump(device), 200 - - class DeviceOfflineResrouce(Resource): """ Bring a device offline @@ -78,11 +44,10 @@ class DeviceOfflineResrouce(Resource): def post(self, deviceid: str): """ Shut down a device - :param deviceid: UUID of device + :param deviceid: ID of device :return: """ - device = db.session.query(models.Device.id).filter(str(models.Device.id) == deviceid).first_or_404()[0] - mqtt.publish(f"{config.MQTT_COMMAND_TOPIC}/{device}", {"command": "offline"}) + mqtt.publish(f"{config.MQTT_COMMAND_TOPIC}/{deviceid}", {"command": "offline"}) class DeviceOnlineResrouce(Resource): @@ -92,28 +57,10 @@ class DeviceOnlineResrouce(Resource): def post(self, deviceid: str): """ Bring a device online - :param deviceid: UUID of device + :param deviceid: ID of device :return: """ - device = db.session.query(models.Device.id).filter(str(models.Device.id) == deviceid).first_or_404()[0] - mqtt.publish(f"{config.MQTT_COMMAND_TOPIC}/{device}", {"command": "online"}) - - -class SensorResource(Resource): - """ - Query and control a particular sensor of a device - """ - sensorschema = schemas.SensorSchema(many=False) - - def get(self, deviceid: str, sensorid: str): - """ - Query a sensor - :param deviceid: UUID of device - :param sensorid: UUID of sensor - :return: - """ - sensor = models.Sensor.query.filter_by(device_id=deviceid, sensorid=sensorid).first_or_404() - return self.sensorschema.dump(sensor) + mqtt.publish(f"{config.MQTT_COMMAND_TOPIC}/{deviceid}", {"command": "online"}) class SensorOfflineResource(Resource): @@ -123,13 +70,11 @@ class SensorOfflineResource(Resource): def post(self, deviceid: str, sensorid: str): """ Shut down a sensor of a device - :param deviceid: UUID of device - :param sensorid: UUID of sensor + :param deviceid: ID of device + :param sensorid: ID of sensor :return: """ - sensor = db.session.query(models.Sensor.device_id, models.Sensor.id).filter( - str(models.Sensor.device_id) == deviceid and str(models.Sensor.id) == sensorid).first_or_404() - mqtt.publish(f"{config.MQTT_COMMAND_TOPIC}/{sensor[0]}/{sensor[1]}", {"command": "offline"}) + mqtt.publish(f"{config.MQTT_COMMAND_TOPIC}/{deviceid}/{sensorid}", {"command": "offline"}) class SensorOnlineResource(Resource): @@ -139,10 +84,8 @@ class SensorOnlineResource(Resource): def post(self, deviceid: str, sensorid: str): """ Bring a sensor online - :param deviceid: UUID of device - :param sensorid: UUID of sensor + :param deviceid: ID of device + :param sensorid: ID of sensor :return: """ - sensor = db.session.query(models.Sensor.device_id, models.Sensor.id).filter( - str(models.Sensor.device_id) == deviceid and str(models.Sensor.id) == sensorid).first_or_404() - mqtt.publish(f"{config.MQTT_COMMAND_TOPIC}/{sensor[0]}/{sensor[1]}", {"command": "online"}) + mqtt.publish(f"{config.MQTT_COMMAND_TOPIC}/{deviceid}/{sensorid}", {"command": "online"}) diff --git a/src/schemas.py b/src/schemas.py deleted file mode 100644 index cb87167..0000000 --- a/src/schemas.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 -import models -from marshm import ma -from marshmallow import fields - -""" -Schemas of SQLAlchemy objects -""" - - -__author__ = "@tormakris" -__copyright__ = "Copyright 2020, Birbnetes Team" -__module_name__ = "schemas" -__version__text__ = "1" - - -class SensorSchema(ma.SQLAlchemyAutoSchema): - """ - Sensor schema autogenerated - """ - class Meta: - model = models.Sensor - include_fk = True - exclude = ('id',) - - -class DeviceSchema(ma.SQLAlchemyAutoSchema): - """ - Device schema autogenerated - """ - sensors = fields.Nested(SensorSchema, many=True) - - class Meta: - model = models.Device - include_relationships = True - exclude = ('lastupdate',)