Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.py[co]
__pycache__/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, slohni .gitignore z fmfi-svt/gate, vyhod __pycache__ a pridaj .* a !.git*.

venv/
config.py
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,18 @@ Setup
- fish: `. venv/bin/activate.fish`
- csh, tcsh: `source venv/bin/activate.csh`
3. Install dependencies if necessary: `pip install -r requirements.txt`
4. configure: `cp config.py{.example,}; $EDITOR config.py`
5. bootstrap (create DB tables and such): `./bootstrap.py`
6. run with `./run.py` (or `while true; do sleep 0.1; ./run.py; done`, as the
server will stop when the code changes => auto-restart on save ^_^)

The rest doesn't exist yet.

Next to do:
-----------

- tests
- listen on UDP socket
- wrap/unwrap NaCl
- fix DB singleton (who wants a singleton?!) -- inversion of control
- CI
19 changes: 19 additions & 0 deletions bootstrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3

import config
from gateserver import db

def db_create_tables():
print('Creating DB tables...', end='')
with open('./tables.sql', 'r') as f:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, od suboru s koncovkou .sql by som cakal korektne sql. Tu su to sql fragmenty s # komentarmi (as opposed to --). Trochu zvlastne.
(Preco je to vlastne v sql subore a nie v python kode?)

if not db.conn: db.connect(config.db_url)
for line in f:
line = line.split('#')[0].strip()
if line: db.exec_sql('CREATE TABLE IF NOT EXISTS ' + line)
print(' OK')

def all():
db_create_tables()

if __name__ == '__main__':
all()
11 changes: 11 additions & 0 deletions config.py.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""The server configuration."""

http_api = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Co ja viem ci dicty. Co takto rovno http_port = 5047 a davat vsetkym cely config modul? Lahsie sa budu robit spolocne nastavenia ak nejake casom budu.

'port': 5047,
}

controller_api = {
'port': 5042,
}

db_url = 'postgresql://user:password@host/dbname'
Empty file added gateserver/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions gateserver/controller_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""The UDP server that provides the API for the controllers."""

def serve(config):
# TODO
print('controller_api would start serving')
21 changes: 21 additions & 0 deletions gateserver/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Holds the (global) connection to the DB."""
# TODO maybe use a connection pool one beautiful day

import config

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ak je aj config globalny, ako budu vyzerat testy? (coskoro uvidim)
Vlastne tu je ten import asi zbytocny?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jop, je, sorry.

import psycopg2

conn = None

def connect(db_url):
global conn
conn = psycopg2.connect(db_url)
conn.autocommit=True

def exec_sql(query, args=(), ret=False):
"""Execute the query, returning the result as a list if `ret` is set."""
with conn.cursor() as cur:
cur.execute(query, args)
if ret: return cur.fetchall()

# thrown when constraints aren't satisfied
IntegrityError = psycopg2.IntegrityError
81 changes: 81 additions & 0 deletions gateserver/http_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""Defines the REST API for CRUD and management."""

from . import db
import nacl.raw as nacl
import cherrypy

class MountPoint:
"""Represents a mount point, or path prefix, for attaching resources to."""
pass

class Resource(MountPoint):
"""Represents a REST resource."""
exposed = True

class CRUDResource(Resource):
"""Represents a REST resource that exposes a DB table's CRUD methods."""
def __init__(self, tbl, put_columns, get_columns, on_save=lambda x: x):
assert(tbl.isidentifier())
self.table = tbl
self.put_columns = put_columns
self.get_columns = get_columns
self.on_save = on_save

@cherrypy.tools.json_out()
def GET(self, id=None):
cols = list(self.get_columns)
q = 'SELECT {} FROM {}'.format(','.join(cols), self.table)
if id: q += ' WHERE id = %s'
rs = [ dict(zip(cols, r)) for r in db.exec_sql(q, (id,), ret=True) ]
if id:
if len(rs) < 1: raise cherrypy.HTTPError('404 Not Found')
return rs[0]
else: return rs

@cherrypy.tools.json_in()
@cherrypy.tools.json_out()
def PUT(self, id):
json = self.on_save(dict(cherrypy.request.json, id=id))
print(json)
cols, values, ps = [], [], []
for c in self.put_columns:
cols.append(c)
values.append(json.get(c))
ps.append('%s')
q = 'INSERT INTO controller ({}) VALUES ({})'.format(','.join(cols),
','.join(ps))
try:
db.exec_sql(q, values)
except db.IntegrityError as e:
raise cherrypy.HTTPError('400 Bad Request', e.pgerror)
return { 'url': cherrypy.url() }

# TODO POST

def DELETE(self, id):
db.exec_sql('DELETE FROM {} WHERE id = %s'.format(self.table), (id,))

################################################################################

api_root = MountPoint()
api_root.controller = CRUDResource('controller',
get_columns={'id', 'ip', 'name'},
put_columns={'id', 'ip', 'key', 'name'},
on_save=lambda ctrl:
dict(ctrl, key=nacl.randombytes(nacl.crypto_secretbox_KEYBYTES)))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, detaily tabuliek by som cakal v inom subore nez vseobecne triedy vyssie.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Toto chce byť o tom, že "čo vidno v HTTP API". (Skutočný obsah tabuliek sú nadmnožiny toho, čo je tuto.) V jednom súbore je to preto, že všetko (vrátane tých všeobecných tried) to definuje, ako vyzerá HTTP API. Ale som ukecateľná na rozdelenie. A vlastne to aj tak idem celé prepísať... :D


################################################################################

cherrypy_conf = {
'/': {
'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
}
}
cherrypy.tree.mount(api_root, '/', cherrypy_conf)

def serve(config):
cherrypy.config.update({'server.socket_port': config['port']})
cherrypy.engine.start()

def stop():
cherrypy.engine.exit()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
CherryPy==3.6.0
psycopg2==2.5.4
https://github.com/warner/python-tweetnacl/tarball/b48a25a33f
13 changes: 13 additions & 0 deletions run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Gate server runner."""

from gateserver import db, controller_api, http_api
import config

def serve():
db.connect(config.db_url)
controller_api.serve(config.controller_api)
http_api.serve(config.http_api)

if __name__ == '__main__':
serve()
1 change: 1 addition & 0 deletions tables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
controller (id macaddr PRIMARY KEY, ip inet UNIQUE NOT NULL, key bytea NOT NULL, name text)