Skip to content

Commit 5498f07

Browse files
committed
Server: target and runner management
1 parent 8e622ed commit 5498f07

File tree

9 files changed

+338
-6
lines changed

9 files changed

+338
-6
lines changed

hwut_server/api/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
from hwut_server.api.about import mod as mod_about
22
from hwut_server.api.execute import mod as mod_execute
3+
from hwut_server.api.runners import mod as mod_runners
4+
from hwut_server.api.targets import mod as mod_targets
35

46

57
def register(app):
68
"""
79
:param flask.Flask app: a Flask app
810
"""
911

10-
app.register_blueprint(mod_execute)
1112
app.register_blueprint(mod_about)
13+
app.register_blueprint(mod_execute)
14+
app.register_blueprint(mod_runners)
15+
app.register_blueprint(mod_targets)

hwut_server/api/runners.py

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from flask import Blueprint, jsonify, request, abort, redirect
2+
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
3+
4+
from hwut_server.models import Boards, Microcontrollers
5+
from hwut_server.models.runners import Runners
6+
from hwut_server.decorators import requires_authentication, requires_superuser
7+
from hwut_server.utils import dict_list_extended_if_authentication
8+
from hwut_server.database import db
9+
10+
mod = Blueprint('runners', __name__, url_prefix='/runners')
11+
12+
13+
@mod.route('/*', methods=['GET'], defaults={'filter_type': 'any', 'filter1': None, 'filter2': None})
14+
@mod.route('/<string:filter_type>/<string:filter1>', methods=['GET'], defaults={'filter2': None})
15+
@mod.route('/<string:filter_type>/<string:filter1>/<string:filter2>', methods=['GET'])
16+
def runners_list(filter_type, filter1, filter2):
17+
runner_list = list()
18+
if filter_type == 'any':
19+
runner_list = Runners.query.all()
20+
elif (filter_type == 'by_board') and (filter1 is not None) and (filter2 is None):
21+
runner_list = Runners.query.filter(Runners.target_board == filter1).all()
22+
elif (filter_type == 'by_microcontroller') and (filter1 is not None) and (filter2 is None):
23+
runner_list = Runners.query.filter(Runners.target_microcontroller == filter1).all()
24+
elif (filter_type == 'by_board_and_microcontroller') and (filter1 is not None) and (filter2 is not None):
25+
runner_list = Runners.query.filter(Runners.target_microcontroller == filter1) \
26+
.filter(Runners.target_board == filter1).all()
27+
else:
28+
abort(400) # Bad request
29+
return jsonify(dict_list_extended_if_authentication(request, runner_list))
30+
31+
32+
@mod.route('/<int:id>', methods=['GET'])
33+
def targets_boards_get(id):
34+
try:
35+
runner = Runners.query.filter(Runners.id == id).one()
36+
if True: # FIXME: if user is owner os superuser
37+
return jsonify(runner.to_dict_long())
38+
else:
39+
return jsonify(runner.to_dict_short())
40+
except:
41+
abort(404)
42+
43+
44+
@mod.route('/add', methods=['PUT'])
45+
@requires_authentication
46+
def runner_create():
47+
if (request.args.get('board') is None) or (request.args.get('microcontroller') is None):
48+
abort(400)
49+
50+
# FIXME: get user
51+
user = request.authorization.username
52+
53+
try:
54+
board = Boards.query.filter(Boards.name == request.args.get('board')).one()
55+
except MultipleResultsFound:
56+
abort(500, 'board "{}" exists multiple time in database'.format(request.args.get('board')))
57+
return
58+
except NoResultFound:
59+
abort(410, 'board "{}" does not exist'.format(request.args.get('board')))
60+
return
61+
try:
62+
microcontroller = Microcontrollers.query\
63+
.filter(Microcontrollers.name == request.args.get('microcontroller')).one()
64+
except MultipleResultsFound:
65+
abort(500, 'microcontroller exists multiple time in database')
66+
return
67+
except NoResultFound:
68+
abort(410, 'microcontroller does not exist')
69+
return
70+
try:
71+
runner = Runners(user, board.name, microcontroller.name)
72+
db.session.add(runner)
73+
db.session.commit()
74+
except:
75+
abort(500, 'unable to create runner')
76+
return
77+
return redirect(mod.url_prefix + '/' + str(runner.id), 201)
78+
79+
80+
@mod.route('/<int:id>', methods=['DELETE'])
81+
@requires_superuser
82+
def runner_delete(id):
83+
try:
84+
runner = Runners.query.filter(Runners.id == id).one()
85+
db.session.delete(runner)
86+
db.session.commit()
87+
return '', 204
88+
except MultipleResultsFound:
89+
abort(500, 'runner exists multiple time in database')
90+
except NoResultFound:
91+
abort(410, 'runner does not exist')

hwut_server/api/targets.py

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
from flask import Blueprint, jsonify, request, abort, redirect
2+
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
3+
4+
from hwut_server.models.targets import Boards, Microcontrollers
5+
from hwut_server.decorators import requires_authentication, requires_superuser
6+
from hwut_server.utils import dict_list_extended_if_authentication
7+
from hwut_server.database import db
8+
9+
mod = Blueprint('targets', __name__, url_prefix='/targets')
10+
11+
12+
@mod.route('/boards', methods=['GET'])
13+
def targets_boards():
14+
return jsonify(dict_list_extended_if_authentication(request, Boards.query.all()))
15+
16+
17+
@mod.route('/boards/<string:board_name>', methods=['GET'])
18+
def targets_boards_get(board_name):
19+
try:
20+
return jsonify(Boards.query.filter(Boards.name == board_name).one().to_dict_long())
21+
except:
22+
abort(404)
23+
24+
25+
@mod.route('/boards/<string:board_name>', methods=['PUT'])
26+
@requires_authentication
27+
def targets_boards_create(board_name):
28+
board = Boards(board_name)
29+
manufacturer = request.args.get('manufacturer')
30+
if manufacturer:
31+
board.manufacturer = manufacturer
32+
try:
33+
db.session.add(board)
34+
db.session.commit()
35+
except:
36+
return redirect(request.path, 303)
37+
return targets_boards_get(board_name), 201
38+
39+
40+
@mod.route('/boards/<string:board_name>', methods=['DELETE'])
41+
@requires_superuser
42+
def targets_boards_delete(board_name):
43+
try:
44+
board = Boards.query.filter(Boards.name == board_name).one()
45+
db.session.delete(board)
46+
db.session.commit()
47+
return '', 204
48+
except MultipleResultsFound:
49+
abort(500, 'board exists multiple time in database')
50+
except NoResultFound:
51+
abort(410, 'board does not exist')
52+
53+
54+
@mod.route('/microcontrollers', methods=['GET'])
55+
def targets_microcontrollers():
56+
return jsonify(dict_list_extended_if_authentication(request, Microcontrollers.query.all()))
57+
58+
59+
@mod.route('/microcontrollers/<string:microcontroller_name>', methods=['GET'])
60+
def targets_microcontrollers_get(microcontroller_name):
61+
try:
62+
return jsonify(Microcontrollers.query.filter(Microcontrollers.name == microcontroller_name)
63+
.one().to_dict_long())
64+
except:
65+
abort(404)
66+
67+
68+
@mod.route('/microcontrollers/<string:microcontroller_name>', methods=['PUT'])
69+
@requires_authentication
70+
def targets_microcontrollers_create(microcontroller_name):
71+
microcontroller = Microcontrollers(microcontroller_name)
72+
manufacturer = request.args.get('manufacturer')
73+
if manufacturer:
74+
microcontroller.manufacturer = manufacturer
75+
try:
76+
db.session.add(microcontroller)
77+
db.session.commit()
78+
except:
79+
return redirect(request.path, 303)
80+
return targets_microcontrollers_get(microcontroller_name), 201
81+
82+
83+
@mod.route('/microcontrollers/<string:microcontroller_name>', methods=['DELETE'])
84+
@requires_superuser
85+
def targets_microcontrollers_delete(microcontroller_name):
86+
try:
87+
microcontroller = Microcontrollers.query.filter(Microcontrollers.name == microcontroller_name).one()
88+
db.session.delete(microcontroller)
89+
db.session.commit()
90+
return '', 204
91+
except MultipleResultsFound:
92+
abort(500, 'microcontroller exists multiple time in database')
93+
except NoResultFound:
94+
abort(410, 'microcontroller does not exist')

hwut_server/models/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
from .jobs import Jobs
2+
from .runners import Runners
3+
from .targets import Boards
4+
from .targets import Microcontrollers
25
from .users import Users

hwut_server/models/jobs.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1-
from sqlalchemy.orm import relationship
1+
from enum import Enum
22

33
from hwut_server.database import db
44

55

6+
class JobStatus(Enum):
7+
WAITING = 10
8+
RUNNING = 20
9+
FINISHED = 30
10+
11+
612
class Jobs(db.Model):
713
__tablename__ = 'jobs'
814
id = db.Column('id', db.BigInteger, db.Sequence('jobs_id_seq'), primary_key=True, index=True, unique=True,
915
autoincrement=True)
1016
created = db.Column(db.TIMESTAMP, nullable=False)
1117
uploaded = db.Column(db.TIMESTAMP, nullable=False)
18+
status = db.Column(db.Enum(JobStatus))
1219
name = db.Column(db.Text)
13-
#executable_file = relationship("ExecutableFile", cascade="all,delete", backref="parent")
20+
filename_executable = db.Column(db.Text, nullable=False)
21+
filename_log = db.Column(db.Text)
22+
filename_other = db.Column(db.Text)
23+
owner = db.Column(db.Text, db.ForeignKey('users.name'), nullable=False)
1424

1525
def __init__(self, created, uploaded, name):
1626
# 'id' auto increment

hwut_server/models/runners.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from hwut_server.database import db
2+
from secrets import token_urlsafe
3+
from datetime import datetime
4+
5+
6+
class Runners(db.Model):
7+
__tablename__ = 'runners'
8+
id = db.Column('id', db.BigInteger, db.Sequence('runners_id_seq'), primary_key=True, index=True, unique=True,
9+
autoincrement=True)
10+
token = db.Column(db.Text)
11+
created = db.Column(db.DateTime, nullable=False, default=datetime.now())
12+
enabled = db.Column(db.Boolean, nullable=False)
13+
last_seen = db.Column(db.DateTime)
14+
ping_counter = db.Column(db.BigInteger, nullable=False)
15+
job_counter = db.Column(db.BigInteger, nullable=False)
16+
busy = db.Column(db.Boolean, nullable=False)
17+
owner = db.Column(db.Text, db.ForeignKey('users.name'), nullable=False)
18+
target_board = db.Column(db.Text, db.ForeignKey('boards.name'), nullable=False)
19+
target_microcontroller = db.Column(db.Text, db.ForeignKey('microcontrollers.name'), nullable=False)
20+
21+
def __init__(self, owner, board, microcontroller, enabled=True):
22+
self.token = token_urlsafe(32)
23+
self.enabled = enabled
24+
self.last_seen = None
25+
self.ping_counter = 0
26+
self.job_counter = 0
27+
self.busy = False
28+
self.owner = owner
29+
self.target_board = board
30+
self.target_microcontroller = microcontroller
31+
32+
def __repr__(self):
33+
return '<runner %d>' % self.id
34+
35+
def to_dict_short(self):
36+
return {
37+
'id': self.id,
38+
}
39+
40+
def to_dict_long(self):
41+
return {
42+
'id': self.id,
43+
'token': self.token,
44+
'created': self.created,
45+
'enabled': self.enabled,
46+
'last_seen': self.last_seen,
47+
'ping_counter': self.ping_counter,
48+
'job_counter': self.job_counter,
49+
'busy': self.busy,
50+
}
51+
52+
def ping(self):
53+
self.ping_counter = Runners.ping_counter + 1
54+
self.last_seen = datetime.now()
55+
# FIXME: Maybe do a commit here?
56+
57+
def acquire(self):
58+
self.busy = True
59+
self.job_counter = Runners.job_counter + 1
60+
# FIXME: Maybe do a commit here?
61+
62+
def release(self):
63+
self.busy = False
64+
# FIXME: Maybe do a commit here?

hwut_server/models/targets.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from hwut_server.database import db
2+
3+
from sqlalchemy.orm import relationship
4+
5+
6+
class Boards(db.Model):
7+
__tablename__ = 'boards'
8+
name = db.Column(db.Text, primary_key=True, unique=True, nullable=False)
9+
manufacturer = db.Column(db.Text)
10+
runners = relationship("Runners")
11+
12+
def __init__(self, name):
13+
self.name = name
14+
15+
def __repr__(self):
16+
return '<board %s>' % self.name
17+
18+
def to_dict_short(self):
19+
return {
20+
'name': self.name,
21+
}
22+
23+
def to_dict_long(self):
24+
return {
25+
'name': self.name,
26+
'manufacturer': self.manufacturer,
27+
'runners': self.runners,
28+
}
29+
30+
31+
class Microcontrollers(db.Model):
32+
__tablename__ = 'microcontrollers'
33+
name = db.Column(db.Text, primary_key=True, unique=True, nullable=False)
34+
manufacturer = db.Column(db.Text)
35+
runners = relationship("Runners")
36+
37+
def __init__(self, name):
38+
self.name = name
39+
40+
def __repr__(self):
41+
return '<microcontroller %s>' % self.name
42+
43+
def to_dict_short(self):
44+
return {
45+
'name': self.name,
46+
}
47+
48+
def to_dict_long(self):
49+
return {
50+
'name': self.name,
51+
'manufacturer': self.manufacturer,
52+
'runners': self.runners,
53+
}

hwut_server/models/users.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from passlib.hash import sha512_crypt
2+
from sqlalchemy.orm import relationship
23

34
from hwut_server.database import db
45

@@ -9,11 +10,12 @@ class Users(db.Model):
910
password_hash = db.Column(db.Text, nullable=False)
1011
rights = db.Column(db.BigInteger, nullable=False)
1112
enabled = db.Column(db.Boolean, nullable=False)
13+
runners = relationship("Runners")
14+
jobs = relationship("Jobs")
1215

13-
def __init__(self, name, password, rights, enabled):
14-
hash = sha512_crypt.encrypt(password)
16+
def __init__(self, name, password, rights, enabled=True):
1517
self.name = name
16-
self.password_hash = hash
18+
self.password_hash = sha512_crypt.encrypt(password)
1719
self.rights = rights
1820
self.enabled = enabled
1921

hwut_server/utils.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from hwut_server.decorators import check_authentication, authenticate
2+
3+
4+
def dict_list_extended_if_authentication(r, l):
5+
if r.args.get('extended') == '1':
6+
if not r.authorization or \
7+
not check_authentication(r.authorization.username, r.authorization.password):
8+
return authenticate()
9+
return [b.to_dict_long() for b in l]
10+
else:
11+
return [b.to_dict_short() for b in l]

0 commit comments

Comments
 (0)