Basic scheduling working

This commit is contained in:
Fergal Moran
2020-09-04 22:30:04 +01:00
parent ec2746de18
commit 9da2dd1c72
14 changed files with 264 additions and 7 deletions

View File

@@ -0,0 +1,6 @@
server {
location / {
root /var/www/html;
try_files $uri $uri/ /index.html;
}
}

23
client/.nginx/nginx.conf Normal file
View File

@@ -0,0 +1,23 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile off;
keepalive_timeout 60;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

View File

@@ -2,26 +2,31 @@ import logging
import os
from logging.handlers import RotatingFileHandler
from celery import Celery
from flask import Flask
from flask_cors import CORS
from flask_jwt_extended import JWTManager, verify_jwt_in_request_optional, get_jwt_identity
from flask_jwt_extended import JWTManager
from flask_mail import Mail
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from app.utils.encoders import IPAddressFieldEncoder
from app.config import Config
from app.utils.encoders import IPAddressFieldEncoder
logger = logging.getLogger(__name__)
db = SQLAlchemy()
jwt = JWTManager()
mail = Mail()
migrate = Migrate()
CELERY_TASK_LIST = [
'app.tasks.hosts',
]
def create_app(app_name='bitchmin', config_class=Config):
logger.info('Creating app {}'.format(app_name))
app = Flask(app_name)
app.config.from_object(config_class)
@@ -31,6 +36,7 @@ def create_app(app_name='bitchmin', config_class=Config):
jwt.init_app(app)
logger.info('Performing migrations')
migrate.init_app(app, db)
mail.init_app(app)
# cors = CORS(app, resources={r"/*": {"origins": "*"}})
CORS(app, supports_credentials=True)
@@ -66,3 +72,28 @@ def create_app(app_name='bitchmin', config_class=Config):
db.init_app(app)
return app
def create_celery_app(app=None):
app = app or create_app()
celery = Celery(
app.import_name,
broker=app.config['CELERY_BROKER_URL'],
include=CELERY_TASK_LIST)
celery.conf.update(app.config)
TaskBase = celery.Task
class ContextTask(TaskBase):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return TaskBase.__call__(self, *args, **kwargs)
celery.Task = ContextTask
return celery
celery = create_celery_app()

View File

@@ -1,4 +1,6 @@
import os
from datetime import timedelta
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
@@ -17,6 +19,23 @@ class Config(object):
ADMINS = ['Ferg@lMoran.me']
LANGUAGES = ['en', 'ie']
CELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL')
CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND')
MAIL_SERVER = os.getenv('MAIL_SERVER')
MAIL_PORT = os.getenv('MAIL_PORT')
MAIL_USE_TLS = os.getenv('MAIL_USE_TLS')
MAIL_USE_SSL = os.getenv('MAIL_USE_SSL')
MAIL_USERNAME = os.getenv('MAIL_USERNAME')
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD')
CELERYBEAT_SCHEDULE = {
'add-every-15-minutes': {
'task': 'app.tasks.hosts.check_host_records',
'schedule': timedelta(minutes=15)
}
}
class ProductionConfig(Config):
DEBUG = False

View File

@@ -1,2 +1,3 @@
from .dnsupdate import DnsUpdate
from .user import User
from .bindstate import BindState

View File

@@ -0,0 +1,18 @@
from sqlalchemy_utils import IPAddressType
from app import db
from app.models._basemodel import _BaseModelMixin
"""
This is the state (as best we can determine)
of the BIND server. Most important is to store the IP for ns1.bitchmints.com
and check if this has changed on every run
"""
class BindState(db.Model, _BaseModelMixin):
__tablename__ = 'bind_state'
nameserver_1_ip = db.Column(IPAddressType(255), nullable=False)
nameserver_1_host = db.Column(db.String(255), unique=True, nullable=False)

View File

@@ -19,7 +19,7 @@ class DnsUpdate(db.Model, _BaseModelMixin):
id = db.Column(db.Integer, primary_key=True)
host = db.Column(db.String(120), unique=True, nullable=False)
host = db.Column(db.String(255), unique=True, nullable=False)
ip = db.Column(IPAddressType(255), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))

View File

@@ -0,0 +1 @@
from .hosts import check_host_records

88
server/app/tasks/hosts.py Normal file
View File

@@ -0,0 +1,88 @@
from ipaddress import ip_address
from flask_mail import Message
import os
from sqlalchemy.orm.exc import NoResultFound
from app import create_celery_app, mail, db
from app.models import DnsUpdate, BindState, dnsupdate
import logging
from app.utils import dnsupdate, iputils
from app.utils.twilio import send_sms
logger = logging.getLogger(__name__)
celery = create_celery_app()
@celery.task
def check_host_records():
platform_ip = iputils.get_my_external_ip()
logger.debug('Current platform ip: {}'.format(platform_ip))
try:
logger.info('Checking for existing state')
bind_state = BindState.query.one()
except NoResultFound:
bind_state = BindState(
nameserver_1_ip=platform_ip,
nameserver_1_host=os.getenv('DNS_SERVER')
)
db.session.add(bind_state)
db.session.commit()
previous_ip = bind_state.nameserver_1_ip
if ip_address(platform_ip) != previous_ip:
logger.info('External IP has changed')
bind_state.nameserver_1_ip = platform_ip
logger.info('Updating nameserver record')
dnsupdate.update_dns(
os.getenv('DNS_SERVER'),
os.getenv('DNS_ZONE'),
os.getenv('DNS_KEY'),
bind_state.nameserver_1_host,
platform_ip
)
logger.info('Checking host records')
hosts = DnsUpdate.query.filter_by(ip=previous_ip)
result = 'Here are the hosts we have\n'
for host in hosts:
result += '\tHost: {} IP: {} Date Updated: {}'.format(
host.host,
host.ip,
host.updated_on
)
dnsupdate.update_dns(
os.getenv('DNS_SERVER'),
os.getenv('DNS_ZONE'),
os.getenv('DNS_KEY'),
host.host,
platform_ip
)
host.ip = platform_ip
logger.info('Saving host details')
db.session.commit()
result = 'End of hosts'
logger.debug(result)
logger.info('Sending mail')
try:
send_sms(
os.getenv('SMS_NOTIFY_TO'),
os.getenv('SMS_NOTIFY_FROM'),
'IP address for {} changed from {} to {}'.format(
bind_state.nameserver_1_host,
previous_ip,
platform_ip
)
)
logger.info('Mail sent successfully')
except Exception as e:
logger.error('Unable to send mail report')
logger.error(e)
else:
logger.info('No IP changes detected')

View File

@@ -1,6 +1,6 @@
import ipaddress
import re
import requests
import sys
@@ -24,3 +24,8 @@ def is_valid_hostname(hostname):
hostname = hostname[:-1] # strip exactly one dot from the right, if present
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(x) for x in hostname.split("."))
def get_my_external_ip():
ip = requests.get('http://ipinfo.io/json').json()['ip']
return ip

View File

@@ -0,0 +1,16 @@
from twilio.rest import Client
import os
import logging
logger = logging.getLogger(__name__)
def send_sms(number_to, number_from, message):
client = Client(os.getenv('TWILIO_ACCOUNT_SID'), os.getenv('TWILIO_AUTH_TOKEN'))
logger.debug('Sending SMS')
message = client.messages.create(
to=number_to,
from_=number_from,
body=message
)
logger.debug(message)

View File

@@ -0,0 +1,36 @@
"""Added Bind State
Revision ID: 7de781bdfffe
Revises: e4b3d04da7fc
Create Date: 2020-09-04 21:47:57.400947
"""
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utils
# revision identifiers, used by Alembic.
revision = '7de781bdfffe'
down_revision = 'e4b3d04da7fc'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('bind_state',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_on', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True),
sa.Column('updated_on', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True),
sa.Column('nameserver_1_ip', sqlalchemy_utils.types.ip_address.IPAddressType(length=255), nullable=False),
sa.Column('nameserver_1_host', sa.String(length=255), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('nameserver_1_host')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('bind_state')
# ### end Alembic commands ###

View File

@@ -3,6 +3,7 @@ Flask-Cors
Flask-SQLAlchemy
Flask_Migrate
flask_jwt_extended
Flask-Mail
sqlalchemy_utils
phue
python-dotenv
@@ -11,6 +12,9 @@ psycopg2-binary==2.8.5
Psycopg2
pylint
werkzeug
celery
redis
flower
requests
IPy
pydig

9
server/start_with_jobs.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
# Run Celery worker
celery -A app.celery worker --loglevel=DEBUG --detach --pidfile=''
# Run Celery Beat
celery -A app.celery beat --loglevel=DEBUG --detach --pidfile=''
flask run