Initial commit
commit
0c238bdb65
|
@ -0,0 +1,3 @@
|
|||
*.egg-info/
|
||||
__pycache__/
|
||||
*.py[co]
|
|
@ -0,0 +1,19 @@
|
|||
from werkzeug import serving
|
||||
import dyns
|
||||
import os.path
|
||||
|
||||
|
||||
app = dyns.make_app()
|
||||
|
||||
|
||||
try:
|
||||
serving.run_simple(
|
||||
hostname='::',
|
||||
port=8080,
|
||||
application=app,
|
||||
static_files={
|
||||
'/static': os.path.join(os.path.dirname(__file__), 'static'),
|
||||
},
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print('Exiting...')
|
|
@ -0,0 +1,16 @@
|
|||
from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name='DyNS',
|
||||
version='0.1',
|
||||
description='Dynamic Nameserver Configuration UI',
|
||||
author='Dustin C. Hatch',
|
||||
author_email='dustin@hatch.name',
|
||||
url='https://bitbucket.org/AdmiralNemo/dyns',
|
||||
license='GPL-3',
|
||||
packages=find_packages('src'),
|
||||
package_dir={'': 'src'},
|
||||
install_requires=[
|
||||
'Milla',
|
||||
],
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
import sqlalchemy
|
||||
import milla
|
||||
import milla.util
|
||||
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
'sqlalchemy.url': 'postgresql:///named',
|
||||
}
|
||||
|
||||
|
||||
def make_app(filename=None):
|
||||
from . import routes, model
|
||||
|
||||
app = milla.Application(routes.router)
|
||||
app.config.update(DEFAULT_CONFIG)
|
||||
if filename:
|
||||
app.config.update(milla.util.read_config(filename))
|
||||
|
||||
engine = sqlalchemy.engine_from_config(app.config, 'sqlalchemy.')
|
||||
model.Session.configure(bind=engine)
|
||||
|
||||
return app
|
|
@ -0,0 +1,89 @@
|
|||
from . import model
|
||||
import json
|
||||
import milla
|
||||
|
||||
|
||||
@milla.allow('GET', 'HEAD', 'POST')
|
||||
def all_zones(request):
|
||||
response = request.ResponseClass()
|
||||
response.content_type = 'application/json'
|
||||
|
||||
session = model.Session()
|
||||
if request.method == 'GET':
|
||||
zones = map(model.Zone.as_dict, session.query(model.Zone))
|
||||
json.dump(list(zones), response.body_file)
|
||||
session.rollback()
|
||||
elif request.method == 'POST':
|
||||
data = json.loads(request.text)
|
||||
zone = model.Zone(**data)
|
||||
session.add(zone)
|
||||
session.commit()
|
||||
response.status_int = 201
|
||||
return response
|
||||
|
||||
|
||||
@milla.allow('GET', 'HEAD', 'POST', 'PUT', 'DELETE')
|
||||
def zone(request, name):
|
||||
response = request.ResponseClass()
|
||||
response.content_type = 'application/json'
|
||||
|
||||
session = model.Session()
|
||||
zone = session.query(model.Zone).get(name)
|
||||
if not zone:
|
||||
raise milla.HTTPNotFound
|
||||
|
||||
if request.method == 'GET':
|
||||
zone_d = zone.as_dict()
|
||||
zone_d['records'] = list(map(model.Record.as_dict, zone.records))
|
||||
json.dump(zone_d, response.body_file)
|
||||
session.rollback()
|
||||
elif request.method == 'PUT':
|
||||
data = json.loads(request.text)
|
||||
for k, v in data.items():
|
||||
assert k != 'records'
|
||||
assert hasattr(zone, k)
|
||||
setattr(zone, k, v)
|
||||
session.commit()
|
||||
elif request.method == 'POST':
|
||||
data = json.loads(request.text)
|
||||
zone_name = data.pop('zone', zone.name)
|
||||
assert zone_name == zone.name
|
||||
record = model.Record(zone=zone_name, **data)
|
||||
session.add(record)
|
||||
session.commit()
|
||||
response.status_int = 201
|
||||
response.location = request.create_href_full(
|
||||
'/records/{}'.format(record.id)
|
||||
)
|
||||
elif request.method == 'DELETE':
|
||||
session.delete(zone)
|
||||
session.commit()
|
||||
else:
|
||||
session.rollback()
|
||||
return response
|
||||
|
||||
@milla.allow('GET', 'HEAD', 'PUT', 'DELETE')
|
||||
def record(request, id):
|
||||
response = request.ResponseClass()
|
||||
response.content_type = 'application/json'
|
||||
|
||||
session = model.Session()
|
||||
record = session.query(model.Record).get(id)
|
||||
if not record:
|
||||
raise milla.HTTPNotFound
|
||||
|
||||
if request.method == 'GET':
|
||||
json.dump(record.as_dict(), response.body_file)
|
||||
session.rollback()
|
||||
elif request.method == 'DELETE':
|
||||
session.delete(record)
|
||||
session.commit()
|
||||
elif request.method == 'PUT':
|
||||
data =json.loads(request.text)
|
||||
for k, v in data.items():
|
||||
assert hasattr(record, k)
|
||||
setattr(record, k, v)
|
||||
session.commit()
|
||||
else:
|
||||
session.rollback()
|
||||
return response
|
|
@ -0,0 +1,52 @@
|
|||
from __future__ import unicode_literals
|
||||
from sqlalchemy import schema, types, orm
|
||||
from sqlalchemy.ext import declarative
|
||||
|
||||
|
||||
Base = declarative.declarative_base()
|
||||
|
||||
Session = orm.session.sessionmaker()
|
||||
|
||||
NoResultFound = orm.exc.NoResultFound
|
||||
|
||||
|
||||
class Serializable(object):
|
||||
|
||||
def as_dict(self):
|
||||
def serialize(obj):
|
||||
for col in obj.__table__.columns:
|
||||
value = getattr(obj, col.name)
|
||||
if isinstance(value, Serializable):
|
||||
value = dict(serialize(value))
|
||||
yield (col.name, value)
|
||||
return dict(serialize(self))
|
||||
|
||||
|
||||
class Zone(Base, Serializable):
|
||||
|
||||
__tablename__ = 'zones'
|
||||
|
||||
name = schema.Column(types.Unicode(254), primary_key=True)
|
||||
ttl = schema.Column(types.Integer, nullable=False, default=3600)
|
||||
source = schema.Column(types.Unicode(254), nullable=False)
|
||||
contact = schema.Column(types.Unicode(254), nullable=False)
|
||||
serial = schema.Column(types.Integer, nullable=False, default=1024)
|
||||
refresh = schema.Column(types.Integer, nullable=False, default=900)
|
||||
retry = schema.Column(types.Integer, nullable=False, default=600)
|
||||
expire = schema.Column(types.Integer, nullable=False, default=86400)
|
||||
minimum = schema.Column(types.Integer, nullable=False, default=3600)
|
||||
records = orm.relationship('Record', cascade='delete')
|
||||
|
||||
|
||||
class Record(Base, Serializable):
|
||||
|
||||
__tablename__ = 'records'
|
||||
|
||||
id = schema.Column(types.Integer, autoincrement=True, primary_key=True)
|
||||
zone = schema.Column(types.Unicode(254), schema.ForeignKey(Zone.name),
|
||||
nullable=False)
|
||||
host = schema.Column(types.Unicode(254), nullable=False)
|
||||
ttl = schema.Column(types.Integer, nullable=False, default=3600)
|
||||
rdtype = schema.Column(types.Unicode(9), nullable=False)
|
||||
mx_prio = schema.Column(types.Integer)
|
||||
data = schema.Column(types.UnicodeText)
|
|
@ -0,0 +1,8 @@
|
|||
from . import controllers
|
||||
from milla.dispatch import routing
|
||||
|
||||
router = routing.Router()
|
||||
|
||||
router.add_route('/zones/', controllers.all_zones)
|
||||
router.add_route('/zones/{name}', controllers.zone)
|
||||
router.add_route('/records/{id}', controllers.record)
|
Reference in New Issue