mirror of
https://github.com/vale981/dirkules
synced 2025-03-05 09:21:38 -05:00
commit
f4cc0bc354
19 changed files with 295 additions and 180 deletions
|
@ -1,4 +1,6 @@
|
||||||
from flask_wtf import csrf, CSRFProtect
|
import datetime
|
||||||
|
|
||||||
|
from flask_wtf import CSRFProtect
|
||||||
import dirkules.config as config
|
import dirkules.config as config
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
@ -12,6 +14,7 @@ csrf.init_app(app)
|
||||||
app_version = app.config["VERSION"]
|
app_version = app.config["VERSION"]
|
||||||
|
|
||||||
import dirkules.models
|
import dirkules.models
|
||||||
|
import dirkules.samba.models
|
||||||
|
|
||||||
# create db if not exists
|
# create db if not exists
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
@ -21,3 +24,15 @@ scheduler.init_app(app)
|
||||||
scheduler.start()
|
scheduler.start()
|
||||||
# import views
|
# import views
|
||||||
import dirkules.views
|
import dirkules.views
|
||||||
|
|
||||||
|
from dirkules.samba import bp_samba as bp_samba
|
||||||
|
|
||||||
|
app.register_blueprint(bp_samba, url_prefix='/samba')
|
||||||
|
|
||||||
|
from dirkules.models import Drive
|
||||||
|
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def check_drives():
|
||||||
|
if Drive.query.first() is None:
|
||||||
|
scheduler.get_job("refresh_disks").modify(next_run_time=datetime.datetime.now())
|
||||||
|
|
|
@ -5,7 +5,7 @@ from logging.config import dictConfig
|
||||||
from apscheduler.jobstores.base import ConflictingIdError
|
from apscheduler.jobstores.base import ConflictingIdError
|
||||||
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
|
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
|
||||||
|
|
||||||
VERSION = "1.0"
|
VERSION = "1.0.1"
|
||||||
|
|
||||||
baseDir = os.path.abspath(os.path.dirname(__file__))
|
baseDir = os.path.abspath(os.path.dirname(__file__))
|
||||||
staticDir = os.path.join(baseDir, 'static')
|
staticDir = os.path.join(baseDir, 'static')
|
||||||
|
|
|
@ -66,10 +66,10 @@ def part_for_disk(device):
|
||||||
# lsblk /dev/sdd -b -o NAME,LABEL,FSTYPE,SIZE,UUID,MOUNTPOINT
|
# lsblk /dev/sdd -b -o NAME,LABEL,FSTYPE,SIZE,UUID,MOUNTPOINT
|
||||||
parts = []
|
parts = []
|
||||||
part_dict = list()
|
part_dict = list()
|
||||||
keys = ['name', 'label', 'fs', 'size', 'uuid', 'mount']
|
keys = ['size', 'name', 'label', 'fs', 'uuid', 'mount']
|
||||||
device = "/dev/" + device
|
device = "/dev/" + device
|
||||||
lsblk = subprocess.Popen(
|
lsblk = subprocess.Popen(
|
||||||
["sudo lsblk " + device + " -l -b -o NAME,LABEL,FSTYPE,SIZE,UUID,MOUNTPOINT"],
|
["sudo lsblk " + device + " -l -b -o SIZE,NAME,LABEL,FSTYPE,UUID,MOUNTPOINT"],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
shell=True,
|
shell=True,
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
|
@ -83,26 +83,24 @@ def part_for_disk(device):
|
||||||
del parts[1]
|
del parts[1]
|
||||||
element_length = list()
|
element_length = list()
|
||||||
counter = 0
|
counter = 0
|
||||||
last_letter = 0
|
|
||||||
pre_value = " "
|
pre_value = " "
|
||||||
for char in parts[0]:
|
for char in parts[0]:
|
||||||
if char != " " and pre_value == " ":
|
if char != " " and pre_value == " ":
|
||||||
element_length.append(counter)
|
if len(element_length) == 0:
|
||||||
|
element_length.append(0)
|
||||||
|
else:
|
||||||
|
element_length.append(counter)
|
||||||
counter += 1
|
counter += 1
|
||||||
pre_value = char
|
pre_value = char
|
||||||
# size ist rechtsbuendig. Extra Behandlung
|
|
||||||
# TODO: Besser machen
|
|
||||||
if char == "S" and parts[0][last_letter] == "E":
|
|
||||||
del element_length[-1]
|
|
||||||
element_length.append((last_letter + 2))
|
|
||||||
if char != " ":
|
|
||||||
last_letter = counter - 1
|
|
||||||
element_length.append(len(parts[0]))
|
element_length.append(len(parts[0]))
|
||||||
del parts[0]
|
del parts[0]
|
||||||
for part in parts:
|
for part in parts:
|
||||||
values = list()
|
values = list()
|
||||||
for start, end in zip(element_length, element_length[1:]):
|
for start, next_start in zip(element_length, element_length[1:]):
|
||||||
values.append(part[start:(end - 1)].strip())
|
if next_start == element_length[-1:][0]:
|
||||||
|
values.append(part[start:len(part)].strip())
|
||||||
|
else:
|
||||||
|
values.append(part[start:(next_start - 1)].strip())
|
||||||
part_dict.append(dict(zip(keys, values)))
|
part_dict.append(dict(zip(keys, values)))
|
||||||
|
|
||||||
return part_dict
|
return part_dict
|
||||||
|
|
|
@ -2,13 +2,10 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
# This file should read btrfs pools
|
def get_free_space(mount_point):
|
||||||
# Storage: sudo btrfs fi usage -b -T /media/data-raid
|
|
||||||
|
|
||||||
def get_free_space(name):
|
|
||||||
lines = list()
|
lines = list()
|
||||||
df = subprocess.Popen(
|
df = subprocess.Popen(
|
||||||
["df -B K /dev/" + name],
|
["df -B K " + mount_point],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
shell=True,
|
shell=True,
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
|
@ -20,7 +17,7 @@ def get_free_space(name):
|
||||||
break
|
break
|
||||||
df.stdout.close()
|
df.stdout.close()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
newLine = ' '.join(line.split())
|
new_line = ' '.join(line.split())
|
||||||
newLine = newLine.split(" ")
|
new_line = new_line.split(" ")
|
||||||
if name in newLine[0]:
|
if mount_point in new_line:
|
||||||
return int(newLine[3][:-1]) * 1024
|
return int(new_line[3][:-1]) * 1024
|
||||||
|
|
|
@ -80,6 +80,7 @@ def get_drives():
|
||||||
|
|
||||||
def pool_gen():
|
def pool_gen():
|
||||||
part_dict = dict()
|
part_dict = dict()
|
||||||
|
Pool.query.delete()
|
||||||
# creates map uuid is key, partitions are values
|
# creates map uuid is key, partitions are values
|
||||||
for part in Partitions.query.all():
|
for part in Partitions.query.all():
|
||||||
if part.uuid in part_dict:
|
if part.uuid in part_dict:
|
||||||
|
@ -97,7 +98,9 @@ def pool_gen():
|
||||||
drives = drives + str(Drive.query.get(part.drive_id)) + ","
|
drives = drives + str(Drive.query.get(part.drive_id)) + ","
|
||||||
drives = drives[:-1]
|
drives = drives[:-1]
|
||||||
value = value[0]
|
value = value[0]
|
||||||
Pool.query.delete()
|
missing = absent_drive(drives)
|
||||||
|
if missing is not None:
|
||||||
|
missing = ",".join(str(x.name) for x in missing)
|
||||||
if value.fs == "btrfs":
|
if value.fs == "btrfs":
|
||||||
if value.mountpoint:
|
if value.mountpoint:
|
||||||
memory_map = btrfsTools.get_space(value.mountpoint)
|
memory_map = btrfsTools.get_space(value.mountpoint)
|
||||||
|
@ -108,16 +111,16 @@ def pool_gen():
|
||||||
['unbekannt', '1.00', 'unbekannt', '1.00'])))
|
['unbekannt', '1.00', 'unbekannt', '1.00'])))
|
||||||
pool_obj = Pool(value.label, memory_map.get("total"), memory_map.get("free"), raid_map.get("data_raid"),
|
pool_obj = Pool(value.label, memory_map.get("total"), memory_map.get("free"), raid_map.get("data_raid"),
|
||||||
raid_map.get("data_ratio"), raid_map.get("meta_raid"), raid_map.get("meta_ratio"), value.fs,
|
raid_map.get("data_ratio"), raid_map.get("meta_raid"), raid_map.get("meta_ratio"), value.fs,
|
||||||
value.mountpoint, "not implemented", drives, get_pool_health(drives))
|
value.mountpoint, "not implemented", drives, get_pool_health(drives), missing)
|
||||||
db.session.add(pool_obj)
|
db.session.add(pool_obj)
|
||||||
|
|
||||||
elif value.fs == "ext4":
|
elif value.fs == "ext4":
|
||||||
if value.mountpoint:
|
if value.mountpoint:
|
||||||
free_space = ext4Tools.get_free_space(value.name)
|
free_space = ext4Tools.get_free_space(value.mountpoint)
|
||||||
else:
|
else:
|
||||||
free_space = 2
|
free_space = 2
|
||||||
pool_obj = Pool(value.label, value.size, free_space, raid, 1.00, raid, 1.00, value.fs, value.mountpoint,
|
pool_obj = Pool(value.label, value.size, free_space, raid, 1.00, raid, 1.00, value.fs, value.mountpoint,
|
||||||
"not implemented", drives)
|
"not implemented", drives, get_pool_health(drives), missing)
|
||||||
db.session.add(pool_obj)
|
db.session.add(pool_obj)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -137,6 +140,23 @@ def get_pool_health(drive_list):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def absent_drive(drive_list):
|
||||||
|
"""
|
||||||
|
:param drive_list: contains drives which belongs to pool
|
||||||
|
:return: List of absent drives or None
|
||||||
|
"""
|
||||||
|
missing = list()
|
||||||
|
drive_split = drive_list.split(",")
|
||||||
|
for drive in drive_split:
|
||||||
|
db_drive = db.session.query(Drive).filter(Drive.name == drive).scalar()
|
||||||
|
if db_drive.missing:
|
||||||
|
missing.append(db_drive)
|
||||||
|
if not missing:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return missing
|
||||||
|
|
||||||
|
|
||||||
def delete_drive(drive):
|
def delete_drive(drive):
|
||||||
"""
|
"""
|
||||||
removes a given drive object (including cascades) from db
|
removes a given drive object (including cascades) from db
|
||||||
|
|
|
@ -78,9 +78,10 @@ class Pool(db.Model):
|
||||||
mountopt = db.Column(db.String)
|
mountopt = db.Column(db.String)
|
||||||
drives = db.Column(db.String)
|
drives = db.Column(db.String)
|
||||||
healthy = db.Column(db.Boolean)
|
healthy = db.Column(db.Boolean)
|
||||||
|
missing = db.Column(db.String)
|
||||||
|
|
||||||
def __init__(self, label, size, free, data_raid, data_ratio, meta_raid, meta_ratio, fs, mountpoint, mountopt,
|
def __init__(self, label, size, free, data_raid, data_ratio, meta_raid, meta_ratio, fs, mountpoint, mountopt,
|
||||||
drives, healthy):
|
drives, healthy, missing):
|
||||||
self.label = label
|
self.label = label
|
||||||
self.size = size
|
self.size = size
|
||||||
self.free = free
|
self.free = free
|
||||||
|
@ -93,6 +94,7 @@ class Pool(db.Model):
|
||||||
self.mountopt = mountopt
|
self.mountopt = mountopt
|
||||||
self.drives = drives
|
self.drives = drives
|
||||||
self.healthy = healthy
|
self.healthy = healthy
|
||||||
|
self.missing = missing
|
||||||
|
|
||||||
|
|
||||||
class Time(db.Model):
|
class Time(db.Model):
|
||||||
|
@ -118,24 +120,3 @@ class Cleaning(db.Model):
|
||||||
self.path = path
|
self.path = path
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
|
|
||||||
class SambaShare(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
name = db.Column(db.String, nullable=False)
|
|
||||||
writeable = db.Column(db.Boolean)
|
|
||||||
recycling = db.Column(db.Boolean)
|
|
||||||
btrfs = db.Column(db.Boolean)
|
|
||||||
options = db.relationship('SambaOptions', order_by="SambaOptions.id", backref="samba_share", lazy="select")
|
|
||||||
|
|
||||||
def __init__(self, name, writeable=False, recycling=False, btrfs=False):
|
|
||||||
self.name = name
|
|
||||||
self.writeable = writeable
|
|
||||||
self.recycling = recycling
|
|
||||||
self.btrfs = btrfs
|
|
||||||
|
|
||||||
|
|
||||||
class SambaOptions(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
option = db.Column(db.String, nullable=False)
|
|
||||||
value = db.Column(db.String, nullable=False)
|
|
||||||
sambashare_id = db.Column(db.Integer, db.ForeignKey('samba_share.id'), nullable=False)
|
|
||||||
|
|
4
dirkules/samba/__init__.py
Normal file
4
dirkules/samba/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
bp_samba = Blueprint('samba', __name__, template_folder='templates')
|
||||||
|
from dirkules.samba import views
|
32
dirkules/samba/manager.py
Normal file
32
dirkules/samba/manager.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
from dirkules.config import staticDir
|
||||||
|
|
||||||
|
from dirkules import db, app_version
|
||||||
|
|
||||||
|
from dirkules.samba.models import SambaGlobal
|
||||||
|
|
||||||
|
|
||||||
|
def set_samba_global(workgroup, name):
|
||||||
|
SambaGlobal.query.delete()
|
||||||
|
workgroup = SambaGlobal("workgroup", workgroup)
|
||||||
|
name = SambaGlobal("server string", "%h {}".format(name))
|
||||||
|
db.session.add(workgroup)
|
||||||
|
db.session.add(name)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_smb():
|
||||||
|
if SambaGlobal.query.first() is None:
|
||||||
|
workgroup = 'WORKGROUP'
|
||||||
|
server_string = '%h dirkules'
|
||||||
|
else:
|
||||||
|
workgroup = SambaGlobal.query.get(1)
|
||||||
|
server_string = SambaGlobal.query.get(2)
|
||||||
|
f = open("smb.conf.txt", "w")
|
||||||
|
samba_global = open(staticDir + "/conf/samba_global.conf", "r")
|
||||||
|
f.write("# This file was generated by dirkules v{}".format(app_version))
|
||||||
|
f.write()
|
||||||
|
f.write("# Global Config")
|
||||||
|
f.write("server string = {}".format(server_string))
|
||||||
|
f.write("workgroup = {}".format(workgroup))
|
||||||
|
f.write(samba_global.read())
|
||||||
|
f.close()
|
34
dirkules/samba/models.py
Normal file
34
dirkules/samba/models.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from dirkules import db
|
||||||
|
|
||||||
|
|
||||||
|
class SambaGlobal(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
element = db.Column(db.String)
|
||||||
|
value = db.Column(db.String)
|
||||||
|
|
||||||
|
def __init__(self, element, value):
|
||||||
|
self.element = element
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
|
||||||
|
class SambaShare(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
name = db.Column(db.String, nullable=False)
|
||||||
|
writeable = db.Column(db.Boolean)
|
||||||
|
recycling = db.Column(db.Boolean)
|
||||||
|
btrfs = db.Column(db.Boolean)
|
||||||
|
options = db.relationship('SambaOptions', order_by="SambaOptions.id", backref="samba_share", lazy="select",
|
||||||
|
cascade="all, delete-orphan")
|
||||||
|
|
||||||
|
def __init__(self, name, writeable=False, recycling=False, btrfs=False):
|
||||||
|
self.name = name
|
||||||
|
self.writeable = writeable
|
||||||
|
self.recycling = recycling
|
||||||
|
self.btrfs = btrfs
|
||||||
|
|
||||||
|
|
||||||
|
class SambaOptions(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
option = db.Column(db.String, nullable=False)
|
||||||
|
value = db.Column(db.String, nullable=False)
|
||||||
|
sambashare_id = db.Column(db.Integer, db.ForeignKey('samba_share.id'), nullable=False)
|
|
@ -5,18 +5,19 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<t2>Feste Bestandteile der<i> Samba global conf</i></t2>
|
<t2>Feste Bestandteile der<i> Samba global conf</i></t2>
|
||||||
<div class="ui segment">
|
<div class="ui segment">
|
||||||
{% for line in conf %}
|
{% for line in conf %}
|
||||||
{{ line }}<br>
|
{{ line }}<br>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<t2>Veränderbare Bestandteile der<i> Samba global conf</i></t2>
|
<t2>Veränderbare Bestandteile der<i> Samba global conf</i></t2>
|
||||||
{% from "_formhelpers.html" import render_field %}
|
{% from "_formhelpers.html" import render_field %}
|
||||||
<form method=post class="ui form error">
|
<form method=post class="ui form error">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
{{ render_field(form.workgroup) }}
|
{{ render_field(form.workgroup) }}
|
||||||
{{ render_field(form.server_string) }}
|
{{ render_field(form.server_string) }}
|
||||||
<button class="ui button" type="submit"><i class="save icon"></i> Ändern</button>
|
{{ render_field(form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
47
dirkules/samba/templates/samba/index.html
Normal file
47
dirkules/samba/templates/samba/index.html
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Samba{% endblock %}
|
||||||
|
{% block head %}
|
||||||
|
{{ super() }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="topspacer"></div>
|
||||||
|
<div class="ui container">
|
||||||
|
<a href="{{ url_for('.config') }}">
|
||||||
|
<div class="ui primary labeled icon button">
|
||||||
|
<i class="edit icon"></i> Konfigurieren
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('.add') }}">
|
||||||
|
<div class="ui primary labeled icon button">
|
||||||
|
<i class="plus icon"></i> Freigabe hinzufügen
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('.generate') }}">
|
||||||
|
<div class="ui primary labeled icon button">
|
||||||
|
<i class="magic icon"></i> Generieren
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<table class="ui celled fixed table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Schreibzugriff</th>
|
||||||
|
<th>Papierkorb</th>
|
||||||
|
<th>BtrFS Optimierungen</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{% for share in shares %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ share.name }}</td>
|
||||||
|
<td>{{ share.writeable }}</td>
|
||||||
|
<td>{{ share.recycling }}</td>
|
||||||
|
<td>{{ share.btrfs }}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
38
dirkules/samba/validation.py
Normal file
38
dirkules/samba/validation.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, BooleanField, SelectField, IntegerField, RadioField, validators, SubmitField
|
||||||
|
|
||||||
|
|
||||||
|
class SambaConfigForm(FlaskForm):
|
||||||
|
workgroup = StringField("workgroup", [validators.required(message="Bitte Feld ausfüllen!"),
|
||||||
|
validators.Regexp('^[a-z]+$', message="Bitte nur Kleinbuchstaben eingeben."),
|
||||||
|
validators.Length(max=255, message="Eingabe zu lang")],
|
||||||
|
render_kw={"placeholder": "Nichts..."})
|
||||||
|
server_string = StringField("server string", [validators.required(message="Bitte Feld ausfüllen!"),
|
||||||
|
validators.Regexp('^[a-z]+$',
|
||||||
|
message="Bitte nur Kleinbuchstaben eingeben."),
|
||||||
|
validators.Length(max=255, message="Eingabe zu lang")],
|
||||||
|
render_kw={"placeholder": "Nichts..."})
|
||||||
|
submit = SubmitField("Speichern")
|
||||||
|
|
||||||
|
|
||||||
|
class SambaAddForm(FlaskForm):
|
||||||
|
name = StringField("Name der Freigabe", [validators.required(message="Bitte Feld ausfüllen!"),
|
||||||
|
validators.Length(max=255, message="Eingabe zu lang")],
|
||||||
|
render_kw={"placeholder": "Bilder"})
|
||||||
|
writeable = BooleanField("Schreibzugriff")
|
||||||
|
recycling = BooleanField("Papierkorb")
|
||||||
|
btrfs = BooleanField("BtrFS Optimierungen (Vorsicht!)")
|
||||||
|
# additional
|
||||||
|
path = SelectField("Pfad", choices=[("Value1", "Label1"), ("Value2", "Label2")])
|
||||||
|
user = StringField("Berechtigte Nutzer", [validators.required(message="Bitte Feld ausfüllen!"),
|
||||||
|
validators.Length(max=255, message="Eingabe zu lang")],
|
||||||
|
render_kw={"placeholder": "sambadaniel"})
|
||||||
|
create_mask = IntegerField("Dateimaske", [validators.Optional(),
|
||||||
|
validators.NumberRange(min=4, max=4,
|
||||||
|
message="Bitte 4 Zahlen eingeben!")],
|
||||||
|
render_kw={"placeholder": "0600"})
|
||||||
|
dir_mask = IntegerField("Ordnermaske", [validators.Optional(),
|
||||||
|
validators.NumberRange(min=4, max=4,
|
||||||
|
message="Bitte 4 Zahlen eingeben!")],
|
||||||
|
render_kw={"placeholder": "0700"})
|
||||||
|
submit = SubmitField("Speichern")
|
44
dirkules/samba/views.py
Normal file
44
dirkules/samba/views.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
from dirkules import db
|
||||||
|
|
||||||
|
from dirkules.config import staticDir
|
||||||
|
from flask import render_template, url_for, request, redirect
|
||||||
|
from dirkules.samba import bp_samba
|
||||||
|
from dirkules.samba.manager import set_samba_global
|
||||||
|
from dirkules.samba.models import SambaShare
|
||||||
|
from dirkules.samba.validation import SambaConfigForm, SambaAddForm
|
||||||
|
|
||||||
|
|
||||||
|
@bp_samba.route('/', methods=['GET'])
|
||||||
|
def index():
|
||||||
|
shares = SambaShare.query.order_by(db.asc(db.collate(SambaShare.name, 'NOCASE'))).all()
|
||||||
|
return render_template('samba/index.html', shares=shares)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_samba.route('/config', methods=['GET', 'POST'])
|
||||||
|
def config():
|
||||||
|
form = SambaConfigForm(request.form)
|
||||||
|
if request.method == 'POST' and form.validate():
|
||||||
|
set_samba_global(form.workgroup.data, form.server_string.data)
|
||||||
|
return redirect(url_for('.index'))
|
||||||
|
file = open(staticDir + "/conf/samba_global.conf", "r")
|
||||||
|
conf = list()
|
||||||
|
while True:
|
||||||
|
line = file.readline()
|
||||||
|
if line != '':
|
||||||
|
conf.append(line.rstrip())
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return render_template('samba/config.html', form=form, conf=conf)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_samba.route('/add', methods=['GET', 'POST'])
|
||||||
|
def add():
|
||||||
|
form = SambaAddForm(request.form)
|
||||||
|
if request.method == 'POST' and form.validate():
|
||||||
|
return redirect(url_for('.index'))
|
||||||
|
return render_template('samba/add.html', form=form)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_samba.route('/generate')
|
||||||
|
def generate():
|
||||||
|
return redirect(url_for('.index'))
|
|
@ -23,7 +23,7 @@
|
||||||
<a class="item" href="{{ url_for('drives') }}"><i class="hdd icon"></i> Festplatten</a>
|
<a class="item" href="{{ url_for('drives') }}"><i class="hdd icon"></i> Festplatten</a>
|
||||||
<a class="item" href="{{ url_for('pools') }}"><i class="server icon"></i> Pools</a>
|
<a class="item" href="{{ url_for('pools') }}"><i class="server icon"></i> Pools</a>
|
||||||
<a class="item" href="{{ url_for('cleaning') }}"><i class="trash icon"></i> Cleaning</a>
|
<a class="item" href="{{ url_for('cleaning') }}"><i class="trash icon"></i> Cleaning</a>
|
||||||
<a class="item" href="{{ url_for('samba') }}"><i class="share alternate icon"></i> Samba</a>
|
<a class="item" href="{{ url_for('samba.index') }}"><i class="share alternate icon"></i> Samba</a>
|
||||||
<div class="right menu">
|
<div class="right menu">
|
||||||
<a class="ui item" href="{{ url_for('about') }}">über Dirkules</a>
|
<a class="ui item" href="{{ url_for('about') }}">über Dirkules</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -81,7 +81,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Gesundheit</td>
|
<td>Gesundheit</td>
|
||||||
{% if pool.healthy %}
|
{% if pool.healthy and pool.missing is none %}
|
||||||
<td class="positive"><i class="icon checkmark"></i>Gesund</td>
|
<td class="positive"><i class="icon checkmark"></i>Gesund</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="negative"><i class="attention icon"></i>Kritisch</td>
|
<td class="negative"><i class="attention icon"></i>Kritisch</td>
|
||||||
|
@ -99,13 +99,14 @@
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
Die RAID Level von Data und Metadata unterscheiden sich. Normalerweise sollten beide gleich
|
Die RAID Level von Data und Metadata unterscheiden sich. Normalerweise sollten beide gleich
|
||||||
konfiguriert werden, da es sonst unter Umständen zu <b>Datenverlust</b> kommen kann, wenn ein
|
konfiguriert werden, da es sonst unter Umständen zu <b>Datenverlust</b> kommen kann, wenn
|
||||||
|
ein
|
||||||
Laufwerk ausfällt.<br>
|
Laufwerk ausfällt.<br>
|
||||||
Solltest du einen guten Grund dafür haben, ignoriere einfach diese Meldung.
|
Solltest du einen guten Grund dafür haben, ignoriere einfach diese Meldung.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if pool.healthy %}
|
{% if pool.healthy and pool.missing is none %}
|
||||||
<div class="ui positive message">
|
<div class="ui positive message">
|
||||||
<i class="close icon"></i>
|
<i class="close icon"></i>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
@ -116,16 +117,28 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="ui negative message">
|
{% if not pool.healthy %}
|
||||||
<i class="close icon"></i>
|
<div class="ui negative message">
|
||||||
<div class="header">
|
<i class="close icon"></i>
|
||||||
Kritischer Zustand
|
<div class="header">
|
||||||
|
Kritischer Zustand
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Mindetstens ein Laufwerk befindet sich in einem schlechten Zustand! Du solltest
|
||||||
|
dieses schnellstmöglich tauschen.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
{% else %}
|
||||||
Mindetstens ein Laufwerk befindet sich in einem schlechten Zustand! Du solltest
|
<div class="ui negative message">
|
||||||
dieses schnellstmöglich tauschen.
|
<i class="close icon"></i>
|
||||||
</p>
|
<div class="header">
|
||||||
</div>
|
Kritischer Zustand
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Folgende Laufwerke wurden nicht gefunden: {{ pool.missing }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% block title %}Dashboard{% endblock %}
|
|
||||||
{% block head %}
|
|
||||||
{{ super() }}
|
|
||||||
{% endblock %}
|
|
||||||
{% block body %}
|
|
||||||
<a href="{{ url_for('samba_global') }}">
|
|
||||||
<div class="ui primary labeled icon button">
|
|
||||||
<i class="plus icon"></i> Samba Global conf
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="{{ url_for('samba_add') }}">
|
|
||||||
<div class="ui primary labeled icon button">
|
|
||||||
<i class="plus icon"></i> Samba add
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<table class="ui celled fixed table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Schreibzugriff</th>
|
|
||||||
<th>Papierkorb</th>
|
|
||||||
<th>BtrFS Optimierungen</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{% for share in shares %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ share.name }}</td>
|
|
||||||
<td>{{ share.writeable }}</td>
|
|
||||||
<td>{{ share.recycling }}</td>
|
|
||||||
<td>{{ share.btrfs }}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endblock %}
|
|
|
@ -17,42 +17,6 @@ class CleaningForm(FlaskForm):
|
||||||
submit = SubmitField("Job speichern")
|
submit = SubmitField("Job speichern")
|
||||||
|
|
||||||
|
|
||||||
class SambaCleaningForm(FlaskForm):
|
|
||||||
workgroup = StringField("workgroup", [validators.required(message="Bitte Feld ausfüllen!"),
|
|
||||||
validators.Regexp('^[a-z]+$', message="Bitte nur Kleinbuchstaben eingeben."),
|
|
||||||
validators.Length(max=255, message="Eingabe zu lang")],
|
|
||||||
render_kw={"placeholder": "Nichts..."})
|
|
||||||
server_string = StringField("server string", [validators.required(message="Bitte Feld ausfüllen!"),
|
|
||||||
validators.Regexp('^[a-z]+$',
|
|
||||||
message="Bitte nur Kleinbuchstaben eingeben."),
|
|
||||||
validators.Length(max=255, message="Eingabe zu lang")],
|
|
||||||
render_kw={"placeholder": "Nichts..."})
|
|
||||||
|
|
||||||
|
|
||||||
class SambaAddForm(FlaskForm):
|
|
||||||
name = StringField("Name der Freigabe", [validators.required(message="Bitte Feld ausfüllen!"),
|
|
||||||
validators.Length(max=255, message="Eingabe zu lang")],
|
|
||||||
render_kw={"placeholder": "Bilder"})
|
|
||||||
writeable = BooleanField("Schreibzugriff")
|
|
||||||
recycling = BooleanField("Papierkorb")
|
|
||||||
btrfs = BooleanField("BtrFS Optimierungen (Vorsicht!)")
|
|
||||||
# additional
|
|
||||||
path = SelectField("Pfad", choices=[("Value1", "Label1"), ("Value2", "Label2")])
|
|
||||||
user = StringField("Berechtigte Nutzer", [validators.required(message="Bitte Feld ausfüllen!"),
|
|
||||||
validators.Length(max=255, message="Eingabe zu lang")],
|
|
||||||
render_kw={"placeholder": "sambadaniel"})
|
|
||||||
create_mask = IntegerField("Dateimaske", [validators.Optional(),
|
|
||||||
validators.NumberRange(min=4, max=4,
|
|
||||||
message="Bitte 4 Zahlen eingeben!")],
|
|
||||||
render_kw={"placeholder": "0600"})
|
|
||||||
dir_mask = IntegerField("Ordnermaske", [validators.Optional(),
|
|
||||||
validators.NumberRange(min=4, max=4,
|
|
||||||
message="Bitte 4 Zahlen eingeben!")],
|
|
||||||
render_kw={"placeholder": "0700"})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SemanticMultiSelectField(SelectField):
|
class SemanticMultiSelectField(SelectField):
|
||||||
def pre_validate(self, form):
|
def pre_validate(self, form):
|
||||||
if self.choices is not None:
|
if self.choices is not None:
|
||||||
|
|
|
@ -6,10 +6,9 @@ from dirkules import app, db, scheduler, app_version
|
||||||
import dirkules.manager.serviceManager as servMan
|
import dirkules.manager.serviceManager as servMan
|
||||||
import dirkules.manager.driveManager as driveMan
|
import dirkules.manager.driveManager as driveMan
|
||||||
import dirkules.manager.cleaning as cleaningMan
|
import dirkules.manager.cleaning as cleaningMan
|
||||||
from dirkules.models import Drive, Cleaning, SambaShare, Pool
|
from dirkules.models import Drive, Cleaning, Pool
|
||||||
import dirkules.manager.viewManager as viewManager
|
import dirkules.manager.viewManager as viewManager
|
||||||
from dirkules.validation.validators import CleaningForm, SambaCleaningForm, SambaAddForm, PoolAddForm
|
from dirkules.validation.validators import CleaningForm, PoolAddForm
|
||||||
from dirkules.config import staticDir
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
|
@ -146,36 +145,3 @@ def add_cleaning():
|
||||||
viewManager.create_cleaning_obj(form.jobname.data, form.path.data, form.active.data)
|
viewManager.create_cleaning_obj(form.jobname.data, form.path.data, form.active.data)
|
||||||
return redirect(url_for('cleaning'))
|
return redirect(url_for('cleaning'))
|
||||||
return render_template('add_cleaning.html', form=form)
|
return render_template('add_cleaning.html', form=form)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/samba', methods=['GET'])
|
|
||||||
def samba():
|
|
||||||
shares = SambaShare.query.order_by(db.asc(db.collate(SambaShare.name, 'NOCASE'))).all()
|
|
||||||
return render_template('samba.html', shares=shares)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/samba/global', methods=['GET', 'POST'])
|
|
||||||
def samba_global():
|
|
||||||
form = SambaCleaningForm(request.form)
|
|
||||||
if request.method == 'POST' and form.validate():
|
|
||||||
print("Input:")
|
|
||||||
print(form.workgroup.data)
|
|
||||||
print(form.server_string.data)
|
|
||||||
return redirect(url_for('samba_global'))
|
|
||||||
file = open(staticDir + "/conf/samba_global.conf")
|
|
||||||
conf = list()
|
|
||||||
while True:
|
|
||||||
line = file.readline()
|
|
||||||
if line != '':
|
|
||||||
conf.append(line.rstrip())
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
return render_template('samba_global.html', form=form, conf=conf)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/samba/add', methods=['GET', 'POST'])
|
|
||||||
def samba_add():
|
|
||||||
form = SambaAddForm(request.form)
|
|
||||||
if request.method == 'POST' and form.validate():
|
|
||||||
return redirect(url_for('samba'))
|
|
||||||
return render_template('samba_add.html', form=form)
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue