Revision 3838ca66
Added by David Sorber over 7 years ago
| software/hbkif/conf/apache/hbkif.conf | ||
|---|---|---|
|
WSGIScriptAlias /hbkif /home/dsorber/Documents/repo/software/hbkif/hbkif.wsgi
|
||
|
<Directory /home/dsorber/Documents/repo/software/hbkif/>
|
||
|
WSGIApplicationGroup %{GLOBAL}
|
||
|
WSGIPassAuthorization On
|
||
|
Require all granted
|
||
|
</Directory>
|
||
|
|
||
|
#Alias "/docs/uspto/claims" "/ryftone/regression/uspto/claims/"
|
||
|
#<Directory /ryftone/regression/uspto/claims/>
|
||
|
# Options +Indexes
|
||
|
# Require all granted
|
||
|
#</Directory>
|
||
|
|
||
|
#Alias "/docs/uspto/parsed" "/ryftone/regression/uspto/split-parsed/"
|
||
|
#<Directory /ryftone/regression/uspto/split-parsed/>
|
||
|
# Options +Indexes
|
||
|
# Require all granted
|
||
|
#</Directory>
|
||
|
|
||
| software/hbkif/hbkif.wsgi | ||
|---|---|---|
|
import re
|
||
|
|
||
|
from hbkif.controllers.status import StatusController
|
||
|
from hbkif.controllers.version import VersionController
|
||
|
|
||
|
class Delegator(object):
|
||
|
|
||
|
def __init__(self):
|
||
|
pass
|
||
|
|
||
|
def __call__(self, environ, start_response):
|
||
|
path = environ['PATH_INFO']
|
||
|
|
||
|
if re.match('^/status(/)?$', path):
|
||
|
# Authenticate user
|
||
|
controller = StatusController(environ)
|
||
|
status, headers, body = controller.generate_response()
|
||
|
elif re.match('^/version(/)?$', path):
|
||
|
# All bookmarks page
|
||
|
controller = VersionController(environ)
|
||
|
status, headers, body = controller.generate_response()
|
||
|
else:
|
||
|
# User supplied an invalid URL
|
||
|
status = '200 OK'
|
||
|
body = 'hbkif is working!\n\nInvalid URL "{:s}"'.format(environ['PATH_INFO'])
|
||
|
body = body.encode()
|
||
|
headers = [('Content-Type', 'text/plain'),
|
||
|
('Content-Length', str(len(body)))]
|
||
|
|
||
|
# Send the status and response headers to the server
|
||
|
start_response(status, headers)
|
||
|
# Return the response body. (Notice it is wrapped in a list although it
|
||
|
# could be any iterable.)
|
||
|
return [body]
|
||
|
|
||
|
|
||
|
# Set the callable object
|
||
|
application = Delegator()
|
||
| software/hbkif/hbkif/config.py | ||
|---|---|---|
|
import logging
|
||
|
|
||
|
# Version information
|
||
|
VERSION_MAJOR = 0
|
||
|
VERSION_MINOR = 1
|
||
|
VERSION_BUGFIX = 0
|
||
|
|
||
|
VERSION_STR = '{:d}.{:d}.{:d}'.format(VERSION_MAJOR, VERSION_MINOR,
|
||
|
VERSION_BUGFIX)
|
||
|
|
||
|
# Logfile information
|
||
|
LOGFILE_PATH = '/var/log/hbkif/hbkif.log'
|
||
|
LOG_FORMAT_STR = '%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s|%(message)s'
|
||
|
LOG_DATE_FORMAT_STR = '%a %b %d %H:%M:%S %Y'
|
||
|
DEFAULT_LOG_LEVEL = logging.DEBUG
|
||
|
|
||
|
|
||
| software/hbkif/hbkif/controllers/base.py | ||
|---|---|---|
|
import logging
|
||
|
|
||
|
from hbkif.config import LOGFILE_PATH, LOG_FORMAT_STR, LOG_DATE_FORMAT_STR, \
|
||
|
DEFAULT_LOG_LEVEL
|
||
|
|
||
|
|
||
|
def auth_with_jwt(func):
|
||
|
""" Method decorator for setting up JWT authorization for controllers that
|
||
|
require it.
|
||
|
"""
|
||
|
def func_wrapper(self, *args, **kwargs):
|
||
|
self.status = '403 Forbidden'
|
||
|
self.content = b''
|
||
|
|
||
|
http_auth = self.environ.get('HTTP_AUTHORIZATION', '')
|
||
|
logging.debug('HTTP_AUTHORIZATION: {:s}'.format(http_auth))
|
||
|
if http_auth.startswith('Bearer '):
|
||
|
logging.debug('Attempting to authorize JWT')
|
||
|
self.token_valid, self.token_expired, self.user_id = \
|
||
|
authorize_jwt(http_auth[7:])
|
||
|
|
||
|
if not self.token_expired:
|
||
|
# The token is not expired, so decide what to do next...
|
||
|
# For now we'll fully authorize if the token is valid (this
|
||
|
# can be changed in the future if fine-grained permissions are
|
||
|
# needed)
|
||
|
self.authorized = self.token_valid
|
||
|
else:
|
||
|
# TODO: handle expired...
|
||
|
logging.debug('JWT is expired')
|
||
|
return func(self, *args, **kwargs)
|
||
|
|
||
|
return func_wrapper
|
||
|
|
||
|
|
||
|
class BaseController(object):
|
||
|
|
||
|
def __init__(self, environ):
|
||
|
|
||
|
logging.basicConfig(format=LOG_FORMAT_STR,
|
||
|
datefmt=LOG_DATE_FORMAT_STR,
|
||
|
filename=LOGFILE_PATH, level=DEFAULT_LOG_LEVEL)
|
||
|
|
||
|
self.environ = environ
|
||
|
self.content = ''
|
||
|
|
||
|
# Override this as necessary
|
||
|
self.content_type = 'text/html; charset=utf-8'
|
||
|
|
||
|
# Unless overridden status should be 200 for success
|
||
|
self.status = '200 OK'
|
||
|
|
||
|
# Add custom headers as needed (formatted as tuple of: name, value)
|
||
|
self.custom_headers = []
|
||
|
|
||
|
# Used for token authorization
|
||
|
self.authorized = False
|
||
|
self.token_valid = False
|
||
|
self.token_expired = False
|
||
|
self.user_id = None
|
||
|
|
||
|
def build_content(self):
|
||
|
""" This method is used to build the content that is used to generate
|
||
|
the response. This method should populate the self.content dict.
|
||
|
|
||
|
Override this method with your own to make this controller actually
|
||
|
do stuff!
|
||
|
"""
|
||
|
return self.content.encode()
|
||
|
|
||
|
def generate_response(self):
|
||
|
""" This function generates the actual response which is made up of two
|
||
|
parts, the response headers and the response body. It then returns the
|
||
|
status along with the headers and body.
|
||
|
"""
|
||
|
body = self.build_content()
|
||
|
|
||
|
# Set the appropriate response headers
|
||
|
headers = [('Content-Type', self.content_type),
|
||
|
('Content-Length', str(len(body)))]
|
||
|
|
||
|
# Include any custom headers
|
||
|
if self.custom_headers:
|
||
|
headers.extend(self.custom_headers)
|
||
|
|
||
|
return self.status, headers, body
|
||
| software/hbkif/hbkif/controllers/status.py | ||
|---|---|---|
|
import datetime
|
||
|
import json
|
||
|
import logging
|
||
|
import subprocess
|
||
|
|
||
|
from hbkif.controllers.base import BaseController
|
||
|
from hbkif.config import VERSION_STR
|
||
|
|
||
|
pwd = 'admin'
|
||
|
|
||
|
class StatusController(BaseController):
|
||
|
|
||
|
def __init__(self, environ):
|
||
|
super(StatusController, self).__init__(environ)
|
||
|
|
||
|
# We return JSON
|
||
|
self.content_type = 'application/json'
|
||
|
|
||
|
def build_content(self):
|
||
|
logging.info('Handling /status request')
|
||
|
|
||
|
# Build command
|
||
|
cmd = 'ipmitool -I lanplus -U admin -P {:s} -H brutus-ipmi power status'
|
||
|
cmd = cmd.format(pwd)
|
||
|
proc = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE,
|
||
|
stderr=subprocess.STDOUT)
|
||
|
|
||
|
version_dict = {'returncode': proc.returncode,
|
||
|
'status': proc.stdout.decode('utf-8').strip()}
|
||
|
self.content = json.dumps(version_dict).encode()
|
||
|
return self.content
|
||
| software/hbkif/hbkif/controllers/version.py | ||
|---|---|---|
|
import datetime
|
||
|
import json
|
||
|
import logging
|
||
|
|
||
|
from hbkif.controllers.base import BaseController
|
||
|
from hbkif.config import VERSION_STR
|
||
|
|
||
|
class VersionController(BaseController):
|
||
|
|
||
|
def __init__(self, environ):
|
||
|
super(VersionController, self).__init__(environ)
|
||
|
|
||
|
# We return JSON
|
||
|
self.content_type = 'application/json'
|
||
|
|
||
|
def build_content(self):
|
||
|
logging.info('Handling /version request')
|
||
|
now = datetime.datetime.now()
|
||
|
version_dict = {'version': VERSION_STR,
|
||
|
'timestamp': now.strftime('%Y-%m-%d %H:%M:%S')}
|
||
|
self.content = json.dumps(version_dict).encode()
|
||
|
return self.content
|
||
| software/hbkif/index.html | ||
|---|---|---|
|
<html>
|
||
|
<!--
|
||
|
Note: This requires recent JQuery and JQuery UI (for the dope ass progress
|
||
|
bar).
|
||
|
-->
|
||
|
|
||
|
<head>
|
||
|
<title>hbkif</title>
|
||
|
|
||
|
<style type="text/css">
|
||
|
@font-face {
|
||
|
font-family: Roboto;
|
||
|
src: url(htdocs/Roboto-Regular.ttf);
|
||
|
}
|
||
|
|
||
|
body {
|
||
|
background-color: #EEE;
|
||
|
font-family: Roboto, sans-serif, Arial;
|
||
|
font-size 18px;
|
||
|
}
|
||
|
|
||
|
div.queue_item {
|
||
|
width: 70%;
|
||
|
background-color: #FFF;
|
||
|
border: 1px solid #000;
|
||
|
padding: 0.4em;
|
||
|
margin-left: auto;
|
||
|
margin-right: auto;
|
||
|
margin-bottom: 1em;
|
||
|
}
|
||
|
|
||
|
div.position {
|
||
|
display: inline-block;
|
||
|
width: 10%;
|
||
|
font-size: 48px;
|
||
|
padding: 1%;
|
||
|
vertical-align: top;
|
||
|
text-align: center;
|
||
|
}
|
||
|
|
||
|
div.side {
|
||
|
display: inline-block;
|
||
|
width: 80%;
|
||
|
}
|
||
|
|
||
|
div.title {
|
||
|
margin-top: 1em;
|
||
|
}
|
||
|
|
||
|
div.particulars {
|
||
|
font-size: 12px;
|
||
|
}
|
||
|
|
||
|
.bold {
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
|
||
|
div#pbar {
|
||
|
height: 8px;
|
||
|
margin-top: 0.6em;
|
||
|
margin-bottom: 0.6em;
|
||
|
}
|
||
|
|
||
|
</style>
|
||
|
<link rel="stylesheet" href="htdocs/js/jquery-ui/jquery-ui.css">
|
||
|
|
||
|
<script type="text/javascript" src="htdocs/js/jquery-3.3.1.min.js"></script>
|
||
|
<script src="htdocs/js/jquery-ui/jquery-ui.js"></script>
|
||
|
|
||
|
<script type="text/javascript" language="javascript">
|
||
|
function format_size(bytes)
|
||
|
{
|
||
|
var tera = 1024 * 1024 * 1024 * 1024;
|
||
|
var giga = 1024 * 1024 * 1024;
|
||
|
var mega = 1024 * 1024;
|
||
|
var kilo = 1024;
|
||
|
|
||
|
var output;
|
||
|
if (bytes >= tera)
|
||
|
{
|
||
|
output = (bytes / tera).toFixed(2) + ' TB';
|
||
|
}
|
||
|
else if (bytes >= giga)
|
||
|
{
|
||
|
output = (bytes / giga).toFixed(2) + ' GB';
|
||
|
}
|
||
|
else if (bytes >= mega)
|
||
|
{
|
||
|
output = (bytes / mega).toFixed(2) + ' MB';
|
||
|
}
|
||
|
else if (bytes >= kilo)
|
||
|
{
|
||
|
output = (bytes / kil).toFixed(2) + ' KB';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
output = bytes + ' bytes';
|
||
|
}
|
||
|
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
// Load JSON and display it
|
||
|
function getAndDisplay(event) {
|
||
|
$.getJSON("http://brutus:8081/queue", function(data) {
|
||
|
// Clear the stage DIV
|
||
|
$("#stage").empty();
|
||
|
|
||
|
var progress_value = 0.0;
|
||
|
|
||
|
// Iterate over the contents of the queue
|
||
|
var queue = data["queue"]
|
||
|
for (var idx = 0; idx < queue.length; idx++)
|
||
|
{
|
||
|
// For the first entry parse out the progress value
|
||
|
if (idx == 0)
|
||
|
{
|
||
|
progress_value = parseFloat(queue[idx]["status"].split(" ")[0]);
|
||
|
$("#stage").append('<div class="queue_item">' +
|
||
|
' <div class="position">' + queue[idx]["position"] + '</div>' +
|
||
|
' <div class="side">' +
|
||
|
' <div class="title">' + queue[idx]["path"] + '</div>' +
|
||
|
' <div id="pbar"></div>' +
|
||
|
' <div class="particulars">' +
|
||
|
' <ul>' +
|
||
|
' <li><span class="bold">Status:</span> ' + queue[idx]["status"] + '</li>' +
|
||
|
' <li><span class="bold">Details:</span> ' + queue[idx]["width"] + 'x' + queue[idx]["height"] + ' @ ' + queue[idx]["framerate"] + 'fps</li>' +
|
||
|
' <li><span class="bold">Size:</span> ' + format_size(queue[idx]["initial_size"]) + '</li>' +
|
||
|
' <li><span class="bold">Added:</span> ' + queue[idx]["time_added"] + '</li>' +
|
||
|
' <li><span class="bold">Started:</span> ' + queue[idx]["encode_started"] + '</li>' +
|
||
|
' </ul>' +
|
||
|
' </div>' +
|
||
|
' </div>' +
|
||
|
'</div>');
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There's got to be a better way...
|
||
|
$("#stage").append('<div class="queue_item">' +
|
||
|
' <div class="position">' + queue[idx]["position"] + '</div>' +
|
||
|
' <div class="side">' +
|
||
|
' <div class="title">' + queue[idx]["path"] + '</div>' +
|
||
|
' <div class="particulars">' +
|
||
|
' <ul>' +
|
||
|
' <li><span class="bold">Status:</span> ' + queue[idx]["status"] + '</li>' +
|
||
|
' <li><span class="bold">Details:</span> ' + queue[idx]["width"] + 'x' + queue[idx]["height"] + ' @ ' + queue[idx]["framerate"] + 'fps</li>' +
|
||
|
' <li><span class="bold">Size:</span> ' + format_size(queue[idx]["initial_size"]) + '</li>' +
|
||
|
' <li><span class="bold">Added:</span> ' + queue[idx]["time_added"] + '</li>' +
|
||
|
' <li><span class="bold">Started:</span> ' + queue[idx]["encode_started"] + '</li>' +
|
||
|
' </ul>' +
|
||
|
' </div>' +
|
||
|
' </div>' +
|
||
|
'</div>');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$("#pbar").progressbar({
|
||
|
value: progress_value
|
||
|
});
|
||
|
|
||
|
// The div we're looking for gets dynamically added so we can't
|
||
|
// make this part of the regular CSS.
|
||
|
$("#pbar").find(".ui-progressbar-value").css({
|
||
|
'background-color': '#f60'
|
||
|
});
|
||
|
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Call the getAndDisplay function once the page loads
|
||
|
$(document).ready(function() {
|
||
|
getAndDisplay("");
|
||
|
//$("#driver").click(getAndDisplay);
|
||
|
});
|
||
|
|
||
|
// Call the getAndDisplay function every 2 sec
|
||
|
window.setInterval(function() {
|
||
|
getAndDisplay("");
|
||
|
}, 2000);
|
||
|
</script>
|
||
|
</head>
|
||
|
|
||
|
<body>
|
||
|
<p>Encode status:</p>
|
||
|
|
||
|
<div id="stage">STAGE</div>
|
||
|
|
||
|
</body>
|
||
|
</html>
|
||
| software/hbkif/setup.py | ||
|---|---|---|
|
from setuptools import setup, find_packages
|
||
|
|
||
|
# Use implicit ryft namespace package to keep things organized
|
||
|
setup(
|
||
|
name='hbkif',
|
||
|
version='0.1.0',
|
||
|
description='Handbrake web interface',
|
||
|
packages=['hbkif', 'hbkif.controllers'],
|
||
|
# ~ packages=find_packages('hbkif'),
|
||
|
# ~ entry_points = {
|
||
|
# ~ 'console_scripts': [
|
||
|
# ~ 'docsim_daemon = ryft.xrest.docsim_daemon:main',
|
||
|
# ~ ]
|
||
|
# ~ },
|
||
|
zip_safe=False,
|
||
|
)
|
||
Thanks to hbkcd, the interface portion can now just be some javascript
(and CSS and HTML).