mirror of
https://github.com/akuker/RASCSI.git
synced 2025-04-10 04:37:11 +00:00
Merge branch 'eric/web-dayna' into develop
This commit is contained in:
commit
f0c618c3e4
5
.gitignore
vendored
5
.gitignore
vendored
@ -1 +1,6 @@
|
||||
venv
|
||||
*.pyc
|
||||
core
|
||||
.idea/
|
||||
.DS_Store
|
||||
*.swp
|
||||
|
@ -28,7 +28,7 @@ LIDO_DRIVER=~/RASCSI/lido-driver.img
|
||||
|
||||
function initialChecks() {
|
||||
currentUser=$(whoami)
|
||||
if [ "pi" != $currentUser ]; then
|
||||
if [ "pi" != "$currentUser" ]; then
|
||||
echo "You must use 'pi' user (current: $currentUser)"
|
||||
exit 1
|
||||
fi
|
||||
@ -40,10 +40,14 @@ function initialChecks() {
|
||||
fi
|
||||
}
|
||||
|
||||
function installPackages() {
|
||||
sudo apt-get update && sudo apt install git libspdlog-dev genisoimage python3 python3-venv nginx bridge-utils -y
|
||||
}
|
||||
|
||||
# install all dependency packages for RaSCSI Service
|
||||
# compile and install RaSCSI Service
|
||||
function installRaScsi() {
|
||||
installPackages
|
||||
sudo apt-get update && sudo apt-get install --yes git libspdlog-dev
|
||||
|
||||
cd ~/RASCSI/src/raspberrypi
|
||||
@ -69,7 +73,7 @@ www-data ALL=NOPASSWD: /sbin/shutdown, /sbin/reboot
|
||||
|
||||
function stopOldWebInterface() {
|
||||
APACHE_STATUS=$(sudo systemctl status apache2 &> /dev/null; echo $?)
|
||||
if [ $APACHE_STATUS -eq 0 ] ; then
|
||||
if [ "$APACHE_STATUS" -eq 0 ] ; then
|
||||
echo "Stopping old Apache2 RaSCSI Web..."
|
||||
sudo systemctl disable apache2
|
||||
sudo systemctl stop apache2
|
||||
@ -79,7 +83,7 @@ function stopOldWebInterface() {
|
||||
# install everything required to run an HTTP server (Nginx + Python Flask App)
|
||||
function installRaScsiWebInterface() {
|
||||
stopOldWebInterface
|
||||
sudo apt install genisoimage python3 python3-venv nginx -y
|
||||
installPackages
|
||||
|
||||
sudo cp -f ~/RASCSI/src/web/service-infra/nginx-default.conf /etc/nginx/sites-available/default
|
||||
sudo cp -f ~/RASCSI/src/web/service-infra/502.html /var/www/html/502.html
|
||||
@ -100,11 +104,15 @@ function installRaScsiWebInterface() {
|
||||
|
||||
function updateRaScsiGit() {
|
||||
cd ~/RASCSI
|
||||
git pull
|
||||
git fetch
|
||||
git stash
|
||||
git rebase origin/master
|
||||
git stash apply
|
||||
}
|
||||
|
||||
function updateRaScsi() {
|
||||
updateRaScsiGit
|
||||
installPackages
|
||||
sudo systemctl stop rascsi
|
||||
|
||||
cd ~/RASCSI/src/raspberrypi
|
||||
@ -241,8 +249,8 @@ function showMenu() {
|
||||
choice=-1
|
||||
|
||||
until [ $choice -ge "0" ] && [ $choice -le "7" ]; do
|
||||
echo "Enter your choice (0-7) or CTRL-C to exit"
|
||||
read choice
|
||||
echo -n "Enter your choice (0-7) or CTRL-C to exit: "
|
||||
read -r choice
|
||||
done
|
||||
|
||||
|
||||
|
@ -7,39 +7,39 @@ from settings import *
|
||||
def make_cd(file_path, file_type, file_creator):
|
||||
with open(file_path, "rb") as f:
|
||||
file_bytes = f.read()
|
||||
file_name = file_path.split('/')[-1]
|
||||
file_suffix = file_name.split('.')[-1]
|
||||
file_name = file_path.split("/")[-1]
|
||||
file_suffix = file_name.split(".")[-1]
|
||||
|
||||
if file_type is None and file_creator is None:
|
||||
if file_suffix.lower() == 'sea':
|
||||
file_type = '.sea'
|
||||
file_creator = 'APPL'
|
||||
if file_suffix.lower() == "sea":
|
||||
file_type = ".sea"
|
||||
file_creator = "APPL"
|
||||
|
||||
v = Volume()
|
||||
v.name = "TestName"
|
||||
|
||||
v['Folder'] = Folder()
|
||||
v["Folder"] = Folder()
|
||||
|
||||
v['Folder'][file_name] = File()
|
||||
v['Folder'][file_name].data = file_bytes
|
||||
v['Folder'][file_name].rsrc = b''
|
||||
v["Folder"][file_name] = File()
|
||||
v["Folder"][file_name].data = file_bytes
|
||||
v["Folder"][file_name].rsrc = b""
|
||||
if not (file_type is None and file_creator is None):
|
||||
v['Folder'][file_name].type = bytearray(file_type)
|
||||
v['Folder'][file_name].creator = bytearray(file_creator)
|
||||
v["Folder"][file_name].type = bytearray(file_type)
|
||||
v["Folder"][file_name].creator = bytearray(file_creator)
|
||||
|
||||
padding = (len(file_bytes) % 512) + (512 * 50)
|
||||
print("mod", str(len(file_bytes) % 512))
|
||||
print("padding " + str(padding))
|
||||
print("len " + str(len(file_bytes)))
|
||||
print("total " + str(len(file_bytes) + padding))
|
||||
with open(base_dir + 'test.hda', 'wb') as f:
|
||||
with open(base_dir + "test.hda", "wb") as f:
|
||||
flat = v.write(
|
||||
size=len(file_bytes) + padding,
|
||||
align=512, # Allocation block alignment modulus (2048 for CDs)
|
||||
desktopdb=True, # Create a dummy Desktop Database to prevent a rebuild on boot
|
||||
bootable=False, # This requires a folder with a ZSYS and a FNDR file
|
||||
startapp=('Folder', file_name), # Path (as tuple) to an app to open at boot
|
||||
startapp=("Folder", file_name), # Path (as tuple) to an app to open at boot
|
||||
)
|
||||
f.write(flat)
|
||||
|
||||
return base_dir + 'test.hda'
|
||||
return base_dir + "test.hda"
|
||||
|
@ -5,8 +5,9 @@ import time
|
||||
|
||||
from ractl_cmds import attach_image
|
||||
from settings import *
|
||||
valid_file_types = ['*.hda', '*.iso', '*.cdr']
|
||||
valid_file_types = r'|'.join([fnmatch.translate(x) for x in valid_file_types])
|
||||
|
||||
valid_file_types = ["*.hda", "*.iso", "*.cdr"]
|
||||
valid_file_types = r"|".join([fnmatch.translate(x) for x in valid_file_types])
|
||||
|
||||
|
||||
def create_new_image(file_name, type, size):
|
||||
@ -15,8 +16,10 @@ def create_new_image(file_name, type, size):
|
||||
else:
|
||||
file_name = file_name + "." + type
|
||||
|
||||
return subprocess.run(["dd", "if=/dev/zero", "of=" + base_dir + file_name, "bs=1M", "count=" + size],
|
||||
capture_output=True)
|
||||
return subprocess.run(
|
||||
["dd", "if=/dev/zero", "of=" + base_dir + file_name, "bs=1M", "count=" + size],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
|
||||
def delete_image(file_name):
|
||||
@ -30,18 +33,24 @@ def delete_image(file_name):
|
||||
|
||||
def unzip_file(file_name):
|
||||
import zipfile
|
||||
with zipfile.ZipFile(base_dir + file_name, 'r') as zip_ref:
|
||||
|
||||
with zipfile.ZipFile(base_dir + file_name, "r") as zip_ref:
|
||||
zip_ref.extractall(base_dir)
|
||||
return True
|
||||
|
||||
|
||||
def rascsi_service(action):
|
||||
# start/stop/restart
|
||||
return subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode == 0
|
||||
return (
|
||||
subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode
|
||||
== 0
|
||||
)
|
||||
|
||||
|
||||
def download_file_to_iso(scsi_id, url):
|
||||
import urllib.request
|
||||
file_name = url.split('/')[-1]
|
||||
|
||||
file_name = url.split("/")[-1]
|
||||
tmp_ts = int(time.time())
|
||||
tmp_dir = "/tmp/" + str(tmp_ts) + "/"
|
||||
os.mkdir(tmp_dir)
|
||||
@ -50,15 +59,18 @@ def download_file_to_iso(scsi_id, url):
|
||||
|
||||
urllib.request.urlretrieve(url, tmp_full_path)
|
||||
# iso_filename = make_cd(tmp_full_path, None, None) # not working yet
|
||||
iso_proc = subprocess.run(["genisoimage", "-hfs", "-o", iso_filename, tmp_full_path], capture_output=True)
|
||||
iso_proc = subprocess.run(
|
||||
["genisoimage", "-hfs", "-o", iso_filename, tmp_full_path], capture_output=True
|
||||
)
|
||||
if iso_proc.returncode != 0:
|
||||
return iso_proc
|
||||
return attach_image(scsi_id, iso_filename, "cd")
|
||||
return attach_image(scsi_id, iso_filename, "SCCD")
|
||||
|
||||
|
||||
def download_image(url):
|
||||
import urllib.request
|
||||
file_name = url.split('/')[-1]
|
||||
|
||||
file_name = url.split("/")[-1]
|
||||
full_path = base_dir + file_name
|
||||
|
||||
urllib.request.urlretrieve(url, full_path)
|
||||
|
12
src/web/mock/bin/brctl
Normal file
12
src/web/mock/bin/brctl
Normal file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Mock responses to rascsi-web
|
||||
case $1 in
|
||||
"show")
|
||||
echo "rascsi_bridge 8000.dca632b05dd1 no eth0"
|
||||
;;
|
||||
|
||||
**)
|
||||
echo "default"
|
||||
;;
|
||||
esac
|
12
src/web/mock/bin/ip
Normal file
12
src/web/mock/bin/ip
Normal file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Mock responses to rascsi-web
|
||||
case $1 in
|
||||
"link")
|
||||
echo "link here"
|
||||
;;
|
||||
|
||||
**)
|
||||
echo "default"
|
||||
;;
|
||||
esac
|
@ -3,7 +3,10 @@ import subprocess
|
||||
|
||||
def rascsi_service(action):
|
||||
# start/stop/restart
|
||||
return subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode == 0
|
||||
return (
|
||||
subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode
|
||||
== 0
|
||||
)
|
||||
|
||||
|
||||
def reboot_pi():
|
||||
@ -15,6 +18,14 @@ def shutdown_pi():
|
||||
|
||||
|
||||
def running_version():
|
||||
ra_web_version = subprocess.run(["git", "rev-parse", "HEAD"], capture_output=True).stdout.decode("utf-8").strip()
|
||||
pi_version = subprocess.run(["uname", "-a"], capture_output=True).stdout.decode("utf-8").strip()
|
||||
ra_web_version = (
|
||||
subprocess.run(["git", "rev-parse", "HEAD"], capture_output=True)
|
||||
.stdout.decode("utf-8")
|
||||
.strip()
|
||||
)
|
||||
pi_version = (
|
||||
subprocess.run(["uname", "-a"], capture_output=True)
|
||||
.stdout.decode("utf-8")
|
||||
.strip()
|
||||
)
|
||||
return ra_web_version + " " + pi_version
|
||||
|
@ -4,8 +4,9 @@ import subprocess
|
||||
import re
|
||||
|
||||
from settings import *
|
||||
valid_file_types = ['*.hda', '*.iso', '*.cdr', '*.zip']
|
||||
valid_file_types = r'|'.join([fnmatch.translate(x) for x in valid_file_types])
|
||||
|
||||
valid_file_types = ["*.hda", "*.iso", "*.cdr", "*.zip"]
|
||||
valid_file_types = r"|".join([fnmatch.translate(x) for x in valid_file_types])
|
||||
# List of SCSI ID's you'd like to exclude - eg if you are on a Mac, the System is usually 7
|
||||
EXCLUDE_SCSI_IDS = [7]
|
||||
|
||||
@ -20,19 +21,36 @@ def list_files():
|
||||
for path, dirs, files in os.walk(base_dir):
|
||||
# Only list valid file types
|
||||
files = [f for f in files if re.match(valid_file_types, f)]
|
||||
files_list.extend([
|
||||
(os.path.join(path, file),
|
||||
# TODO: move formatting to template
|
||||
'{:,.0f}'.format(os.path.getsize(os.path.join(path, file)) / float(1 << 20)) + " MB")
|
||||
for file in files])
|
||||
files_list.extend(
|
||||
[
|
||||
(
|
||||
os.path.join(path, file),
|
||||
# TODO: move formatting to template
|
||||
"{:,.0f}".format(
|
||||
os.path.getsize(os.path.join(path, file)) / float(1 << 20)
|
||||
)
|
||||
+ " MB",
|
||||
)
|
||||
for file in files
|
||||
]
|
||||
)
|
||||
return files_list
|
||||
|
||||
|
||||
def list_config_files():
|
||||
files_list = []
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
for file in files:
|
||||
if file.endswith(".csv"):
|
||||
files_list.append(file)
|
||||
return files_list
|
||||
|
||||
|
||||
def get_valid_scsi_ids(devices):
|
||||
invalid_list = EXCLUDE_SCSI_IDS.copy()
|
||||
for device in devices:
|
||||
if device['file'] != "NO MEDIA" and device['file'] != "-":
|
||||
invalid_list.append(int(device['id']))
|
||||
if device["file"] != "NO MEDIA" and device["file"] != "-":
|
||||
invalid_list.append(int(device["id"]))
|
||||
|
||||
valid_list = list(range(8))
|
||||
for id in invalid_list:
|
||||
@ -46,19 +64,33 @@ def get_type(scsi_id):
|
||||
return list_devices()[int(scsi_id)]["type"]
|
||||
|
||||
|
||||
def attach_image(scsi_id, image, type):
|
||||
if type == "cd" and get_type(scsi_id) == "SCCD":
|
||||
def attach_image(scsi_id, image, device_type):
|
||||
if device_type == "SCCD" and get_type(scsi_id) == "SCCD":
|
||||
return insert(scsi_id, image)
|
||||
elif device_type == "SCDP":
|
||||
attach_daynaport(scsi_id)
|
||||
else:
|
||||
return subprocess.run(["rasctl", "-c", "attach", "-t", type, "-i", scsi_id, "-f", image], capture_output=True)
|
||||
if device_type == "SCCD":
|
||||
device_type = "cd"
|
||||
return subprocess.run(
|
||||
["rasctl", "-c", "attach", "-t", device_type, "-i", scsi_id, "-f", image],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
|
||||
def detach_by_id(scsi_id):
|
||||
return subprocess.run(["rasctl", "-c" "detach", "-i", scsi_id], capture_output=True)
|
||||
|
||||
|
||||
def detach_all():
|
||||
for scsi_id in range(0, 7):
|
||||
subprocess.run(["rasctl", "-c" "detach", "-i", str(scsi_id)])
|
||||
|
||||
|
||||
def disconnect_by_id(scsi_id):
|
||||
return subprocess.run(["rasctl", "-c", "disconnect", "-i", scsi_id], capture_output=True)
|
||||
return subprocess.run(
|
||||
["rasctl", "-c", "disconnect", "-i", scsi_id], capture_output=True
|
||||
)
|
||||
|
||||
|
||||
def eject_by_id(scsi_id):
|
||||
@ -66,33 +98,67 @@ def eject_by_id(scsi_id):
|
||||
|
||||
|
||||
def insert(scsi_id, image):
|
||||
return subprocess.run(["rasctl", "-i", scsi_id, "-c", "insert", "-f", image], capture_output=True)
|
||||
return subprocess.run(
|
||||
["rasctl", "-i", scsi_id, "-c", "insert", "-f", image], capture_output=True
|
||||
)
|
||||
|
||||
|
||||
def attach_daynaport(scsi_id):
|
||||
return subprocess.run(
|
||||
["rasctl", "-i", scsi_id, "-c", "attach", "-t", "daynaport"],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
|
||||
def is_bridge_setup(interface):
|
||||
process = subprocess.run(["brctl", "show"], capture_output=True)
|
||||
output = process.stdout.decode("utf-8")
|
||||
if "rascsi_bridge" in output and interface in output:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def daynaport_setup_bridge(interface):
|
||||
return subprocess.run(
|
||||
[f"{base_dir}../RASCSI/src/raspberrypi/setup_bridge.sh", interface],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
|
||||
def rascsi_service(action):
|
||||
# start/stop/restart
|
||||
return subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode == 0
|
||||
return (
|
||||
subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode
|
||||
== 0
|
||||
)
|
||||
|
||||
|
||||
def list_devices():
|
||||
device_list = []
|
||||
for id in range(8):
|
||||
device_list.append({"id": str(id), "un": "-", "type": "-", "file": "-"})
|
||||
output = subprocess.run(["rasctl", "-l"], capture_output=True).stdout.decode("utf-8")
|
||||
output = subprocess.run(["rasctl", "-l"], capture_output=True).stdout.decode(
|
||||
"utf-8"
|
||||
)
|
||||
for line in output.splitlines():
|
||||
# Valid line to process, continue
|
||||
if not line.startswith("+") and \
|
||||
not line.startswith("| ID |") and \
|
||||
(not line.startswith("No device is installed.") or line.startswith("No images currently attached.")) \
|
||||
and len(line) > 0:
|
||||
if (
|
||||
not line.startswith("+")
|
||||
and not line.startswith("| ID |")
|
||||
and (
|
||||
not line.startswith("No device is installed.")
|
||||
or line.startswith("No images currently attached.")
|
||||
)
|
||||
and len(line) > 0
|
||||
):
|
||||
line.rstrip()
|
||||
device = {}
|
||||
segments = line.split("|")
|
||||
if len(segments) > 4:
|
||||
idx = int(segments[1].strip())
|
||||
device_list[idx]["id"] = str(idx)
|
||||
device_list[idx]['un'] = segments[2].strip()
|
||||
device_list[idx]['type'] = segments[3].strip()
|
||||
device_list[idx]['file'] = segments[4].strip()
|
||||
device_list[idx]["un"] = segments[2].strip()
|
||||
device_list[idx]["type"] = segments[3].strip()
|
||||
device_list[idx]["file"] = segments[4].strip()
|
||||
|
||||
return device_list
|
||||
|
@ -1,4 +1,4 @@
|
||||
import os
|
||||
|
||||
base_dir = os.getenv('BASE_DIR', "/home/pi/images/")
|
||||
MAX_FILE_SIZE = os.getenv('MAX_FILE_SIZE', 1024 * 1024 * 1024 * 2) # 2gb
|
||||
base_dir = os.getenv("BASE_DIR", "/home/pi/images/")
|
||||
MAX_FILE_SIZE = os.getenv("MAX_FILE_SIZE", 1024 * 1024 * 1024 * 2) # 2gb
|
||||
|
@ -17,10 +17,26 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Current RaSCSI Configuration</h2>
|
||||
<form action="/config/load" method="post">
|
||||
<select name="name" >
|
||||
{% for config in config_files %}
|
||||
<option value="{{config}}">{{config.replace(".csv", '')}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Load" />
|
||||
</form>
|
||||
<form action="/config/save" method="post">
|
||||
<input name="name" placeholder="default">
|
||||
<input type="submit" value="Save" />
|
||||
</form>
|
||||
<form action="/scsi/detach_all" method="post" onsubmit="return confirm('Detach all SCSI Devices?')">
|
||||
<input type="submit" value="Detach All" />
|
||||
</form>
|
||||
|
||||
<table cellpadding="3" border="black">
|
||||
<tbody>
|
||||
<tr>
|
||||
@ -103,7 +119,35 @@
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
<h2>Attach Ethernet Adapter</h2>
|
||||
<p>Emulates a SCSI DaynaPORT Ethernet Adapter. Host drivers required.</p>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/daynaport/attach" method="post">
|
||||
<select name="scsi_id">
|
||||
{% for id in scsi_ids %}
|
||||
<option value="{{id}}">{{id}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Attach" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
{% if bridge_configured %}
|
||||
<small>Bridge is currently configured!</small>
|
||||
{% else %}
|
||||
<form action="/daynaport/setup" method="post">
|
||||
<input type="submit" value="Create Bridge" />
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
<h2>Upload File</h2>
|
||||
<p>Uploads file to <tt>{{base_dir}}</tt>. Max file size is set to {{max_file_size / 1024 /1024 }}MB</p>
|
||||
<table style="border: none">
|
||||
@ -183,7 +227,6 @@
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Raspberry Pi Operations</h2>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
|
288
src/web/web.py
288
src/web/web.py
@ -1,206 +1,298 @@
|
||||
import os
|
||||
|
||||
from flask import Flask, render_template, request, flash, url_for, redirect, send_file
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from file_cmds import create_new_image, download_file_to_iso, delete_image, unzip_file, download_image
|
||||
from file_cmds import (
|
||||
create_new_image,
|
||||
download_file_to_iso,
|
||||
delete_image,
|
||||
unzip_file,
|
||||
download_image,
|
||||
)
|
||||
from pi_cmds import shutdown_pi, reboot_pi, running_version, rascsi_service
|
||||
from ractl_cmds import attach_image, list_devices, is_active, list_files, detach_by_id, eject_by_id, get_valid_scsi_ids
|
||||
from ractl_cmds import (
|
||||
attach_image,
|
||||
list_devices,
|
||||
is_active,
|
||||
list_files,
|
||||
detach_by_id,
|
||||
eject_by_id,
|
||||
get_valid_scsi_ids,
|
||||
attach_daynaport,
|
||||
is_bridge_setup,
|
||||
daynaport_setup_bridge,
|
||||
list_config_files,
|
||||
detach_all,
|
||||
)
|
||||
from settings import *
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
devices = list_devices()
|
||||
scsi_ids = get_valid_scsi_ids(devices)
|
||||
return render_template('index.html',
|
||||
devices=devices,
|
||||
active=is_active(),
|
||||
files=list_files(),
|
||||
base_dir=base_dir,
|
||||
scsi_ids=scsi_ids,
|
||||
max_file_size=MAX_FILE_SIZE,
|
||||
version=running_version())
|
||||
return render_template(
|
||||
"index.html",
|
||||
bridge_configured=is_bridge_setup("eth0"),
|
||||
devices=devices,
|
||||
active=is_active(),
|
||||
files=list_files(),
|
||||
config_files=list_config_files(),
|
||||
base_dir=base_dir,
|
||||
scsi_ids=scsi_ids,
|
||||
max_file_size=MAX_FILE_SIZE,
|
||||
version=running_version(),
|
||||
)
|
||||
|
||||
@app.route('/logs')
|
||||
|
||||
@app.route("/config/save", methods=["POST"])
|
||||
def config_save():
|
||||
file_name = request.form.get("name") or "default"
|
||||
file_name = f"{base_dir}{file_name}.csv"
|
||||
import csv
|
||||
|
||||
with open(file_name, "w") as csv_file:
|
||||
writer = csv.writer(csv_file)
|
||||
for device in list_devices():
|
||||
if device["type"] is not "-":
|
||||
writer.writerow(device.values())
|
||||
flash(f"Saved config to {file_name}!")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/config/load", methods=["POST"])
|
||||
def config_load():
|
||||
file_name = request.form.get("name") or "default.csv"
|
||||
file_name = f"{base_dir}{file_name}"
|
||||
detach_all()
|
||||
import csv
|
||||
|
||||
with open(file_name) as csv_file:
|
||||
config_reader = csv.reader(csv_file)
|
||||
for row in config_reader:
|
||||
image_name = row[3].replace("(WRITEPROTECT)", "")
|
||||
attach_image(row[0], image_name, row[2])
|
||||
flash(f"Loaded config from {file_name}!")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/logs")
|
||||
def logs():
|
||||
import subprocess
|
||||
lines = request.args.get('lines') or "100"
|
||||
|
||||
lines = request.args.get("lines") or "100"
|
||||
process = subprocess.run(["journalctl", "-n", lines], capture_output=True)
|
||||
|
||||
if process.returncode == 0:
|
||||
headers = { 'content-type':'text/plain' }
|
||||
headers = {"content-type": "text/plain"}
|
||||
return process.stdout.decode("utf-8"), 200, headers
|
||||
else:
|
||||
flash(u'Failed to get logs')
|
||||
flash(process.stdout.decode("utf-8"), 'stdout')
|
||||
flash(process.stderr.decode("utf-8"), 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
flash("Failed to get logs")
|
||||
flash(process.stdout.decode("utf-8"), "stdout")
|
||||
flash(process.stderr.decode("utf-8"), "stderr")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/scsi/attach', methods=['POST'])
|
||||
@app.route("/daynaport/attach", methods=["POST"])
|
||||
def daynaport_attach():
|
||||
scsi_id = request.form.get("scsi_id")
|
||||
process = attach_daynaport(scsi_id)
|
||||
if process.returncode == 0:
|
||||
flash(f"Attached DaynaPORT to SCSI id {scsi_id}!")
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
flash(f"Failed to attach DaynaPORT to SCSI id {scsi_id}!", "error")
|
||||
flash(process.stdout.decode("utf-8"), "stdout")
|
||||
flash(process.stderr.decode("utf-8"), "stderr")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/daynaport/setup", methods=["POST"])
|
||||
def daynaport_setup():
|
||||
# Future use for wifi
|
||||
interface = request.form.get("interface") or "eth0"
|
||||
process = daynaport_setup_bridge(interface)
|
||||
if process.returncode == 0:
|
||||
flash(f"Configured DaynaPORT bridge on {interface}!")
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
flash(f"Failed to configure DaynaPORT bridge on {interface}!", "error")
|
||||
flash(process.stdout.decode("utf-8"), "stdout")
|
||||
flash(process.stderr.decode("utf-8"), "stderr")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/scsi/attach", methods=["POST"])
|
||||
def attach():
|
||||
file_name = request.form.get('file_name')
|
||||
scsi_id = request.form.get('scsi_id')
|
||||
file_name = request.form.get("file_name")
|
||||
scsi_id = request.form.get("scsi_id")
|
||||
|
||||
# Validate image type by suffix
|
||||
if file_name.lower().endswith('.iso') or file_name.lower().endswith('iso'):
|
||||
if file_name.lower().endswith(".iso") or file_name.lower().endswith("iso"):
|
||||
image_type = "cd"
|
||||
elif file_name.lower().endswith('.hda'):
|
||||
elif file_name.lower().endswith(".hda"):
|
||||
image_type = "hd"
|
||||
else:
|
||||
flash(u'Unknown file type. Valid files are .iso, .hda, .cdr', 'error')
|
||||
return redirect(url_for('index'))
|
||||
flash("Unknown file type. Valid files are .iso, .hda, .cdr", "error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
process = attach_image(scsi_id, file_name, image_type)
|
||||
if process.returncode == 0:
|
||||
flash('Attached '+ file_name + " to scsi id " + scsi_id + "!")
|
||||
return redirect(url_for('index'))
|
||||
flash(f"Attached {file_name} to SCSI id {scsi_id}!")
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
flash(u'Failed to attach '+ file_name + " to scsi id " + scsi_id + "!", 'error')
|
||||
flash(process.stdout.decode("utf-8"), 'stdout')
|
||||
flash(process.stderr.decode("utf-8"), 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
flash(f"Failed to attach {file_name} to SCSI id {scsi_id}!", "error")
|
||||
flash(process.stdout.decode("utf-8"), "stdout")
|
||||
flash(process.stderr.decode("utf-8"), "stderr")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/scsi/detach', methods=['POST'])
|
||||
@app.route("/scsi/detach_all", methods=["POST"])
|
||||
def detach_all_devices():
|
||||
detach_all()
|
||||
flash("Detached all SCSI devices!")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/scsi/detach", methods=["POST"])
|
||||
def detach():
|
||||
scsi_id = request.form.get('scsi_id')
|
||||
scsi_id = request.form.get("scsi_id")
|
||||
process = detach_by_id(scsi_id)
|
||||
if process.returncode == 0:
|
||||
flash("Detached scsi id " + scsi_id + "!")
|
||||
return redirect(url_for('index'))
|
||||
flash("Detached SCSI id " + scsi_id + "!")
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
flash(u"Failed to detach scsi id " + scsi_id + "!", 'error')
|
||||
flash(process.stdout, 'stdout')
|
||||
flash(process.stderr, 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
flash("Failed to detach SCSI id " + scsi_id + "!", "error")
|
||||
flash(process.stdout, "stdout")
|
||||
flash(process.stderr, "stderr")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/scsi/eject', methods=['POST'])
|
||||
@app.route("/scsi/eject", methods=["POST"])
|
||||
def eject():
|
||||
scsi_id = request.form.get('scsi_id')
|
||||
scsi_id = request.form.get("scsi_id")
|
||||
process = eject_by_id(scsi_id)
|
||||
if process.returncode == 0:
|
||||
flash("Ejected scsi id " + scsi_id + "!")
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
flash(u"Failed to eject scsi id " + scsi_id + "!", 'error')
|
||||
flash(process.stdout, 'stdout')
|
||||
flash(process.stderr, 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
flash("Failed to eject SCSI id " + scsi_id + "!", "error")
|
||||
flash(process.stdout, "stdout")
|
||||
flash(process.stderr, "stderr")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/pi/reboot', methods=['POST'])
|
||||
@app.route("/pi/reboot", methods=["POST"])
|
||||
def restart():
|
||||
reboot_pi()
|
||||
flash("Restarting...")
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/rascsi/restart', methods=['POST'])
|
||||
@app.route("/rascsi/restart", methods=["POST"])
|
||||
def rascsi_restart():
|
||||
rascsi_service("restart")
|
||||
flash("Restarting RaSCSI Service...")
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/pi/shutdown', methods=['POST'])
|
||||
@app.route("/pi/shutdown", methods=["POST"])
|
||||
def shutdown():
|
||||
shutdown_pi()
|
||||
flash("Shutting down...")
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/files/download_to_iso', methods=['POST'])
|
||||
@app.route("/files/download_to_iso", methods=["POST"])
|
||||
def download_file():
|
||||
scsi_id = request.form.get('scsi_id')
|
||||
url = request.form.get('url')
|
||||
scsi_id = request.form.get("scsi_id")
|
||||
url = request.form.get("url")
|
||||
process = download_file_to_iso(scsi_id, url)
|
||||
if process.returncode == 0:
|
||||
flash("File Downloaded")
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
flash(u"Failed to download file", 'error')
|
||||
flash(process.stdout, 'stdout')
|
||||
flash(process.stderr, 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
flash("Failed to download file", "error")
|
||||
flash(process.stdout, "stdout")
|
||||
flash(process.stderr, "stderr")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/files/download_image', methods=['POST'])
|
||||
@app.route("/files/download_image", methods=["POST"])
|
||||
def download_img():
|
||||
url = request.form.get('url')
|
||||
url = request.form.get("url")
|
||||
# TODO: error handling
|
||||
download_image(url)
|
||||
flash("File Downloaded")
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/files/upload', methods=['POST'])
|
||||
@app.route("/files/upload", methods=["POST"])
|
||||
def upload_file():
|
||||
if 'file' not in request.files:
|
||||
flash('No file part', 'error')
|
||||
return redirect(url_for('index'))
|
||||
file = request.files['file']
|
||||
if "file" not in request.files:
|
||||
flash("No file part", "error")
|
||||
return redirect(url_for("index"))
|
||||
file = request.files["file"]
|
||||
if file:
|
||||
filename = secure_filename(file.filename)
|
||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
||||
return redirect(url_for('index', filename=filename))
|
||||
file.save(os.path.join(app.config["UPLOAD_FOLDER"], filename))
|
||||
return redirect(url_for("index", filename=filename))
|
||||
|
||||
|
||||
@app.route('/files/create', methods=['POST'])
|
||||
@app.route("/files/create", methods=["POST"])
|
||||
def create_file():
|
||||
file_name = request.form.get('file_name')
|
||||
size = request.form.get('size')
|
||||
type = request.form.get('type')
|
||||
file_name = request.form.get("file_name")
|
||||
size = request.form.get("size")
|
||||
type = request.form.get("type")
|
||||
|
||||
process = create_new_image(file_name, type, size)
|
||||
if process.returncode == 0:
|
||||
flash("Drive created")
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
flash(u"Failed to create file", 'error')
|
||||
flash(process.stdout, 'stdout')
|
||||
flash(process.stderr, 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
flash("Failed to create file", "error")
|
||||
flash(process.stdout, "stdout")
|
||||
flash(process.stderr, "stderr")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/files/download', methods=['POST'])
|
||||
@app.route("/files/download", methods=["POST"])
|
||||
def download():
|
||||
image = request.form.get('image')
|
||||
image = request.form.get("image")
|
||||
return send_file(base_dir + image, as_attachment=True)
|
||||
|
||||
|
||||
@app.route('/files/delete', methods=['POST'])
|
||||
@app.route("/files/delete", methods=["POST"])
|
||||
def delete():
|
||||
image = request.form.get('image')
|
||||
image = request.form.get("image")
|
||||
if delete_image(image):
|
||||
flash("File " + image + " deleted")
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
flash(u"Failed to Delete " + image, 'error')
|
||||
return redirect(url_for('index'))
|
||||
flash("Failed to Delete " + image, "error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route('/files/unzip', methods=['POST'])
|
||||
@app.route("/files/unzip", methods=["POST"])
|
||||
def unzip():
|
||||
image = request.form.get('image')
|
||||
image = request.form.get("image")
|
||||
|
||||
if unzip_file(image):
|
||||
flash("Unzipped file " + image)
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
flash(u"Failed to unzip " + image, 'error')
|
||||
return redirect(url_for('index'))
|
||||
flash("Failed to unzip " + image, "error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.secret_key = 'rascsi_is_awesome_insecure_secret_key'
|
||||
app.config['SESSION_TYPE'] = 'filesystem'
|
||||
app.config['UPLOAD_FOLDER'] = base_dir
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
||||
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE
|
||||
app.secret_key = "rascsi_is_awesome_insecure_secret_key"
|
||||
app.config["SESSION_TYPE"] = "filesystem"
|
||||
app.config["UPLOAD_FOLDER"] = base_dir
|
||||
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
|
||||
app.config["MAX_CONTENT_LENGTH"] = MAX_FILE_SIZE
|
||||
|
||||
from waitress import serve
|
||||
|
||||
serve(app, host="0.0.0.0", port=8080)
|
||||
|
Loading…
x
Reference in New Issue
Block a user