Disk Edition : Resize and Create attached disks

main
root 2024-03-25 15:49:12 +01:00
parent 93f2a5379f
commit a2683789d1
22 changed files with 331 additions and 33 deletions

74
app.py
View File

@ -17,6 +17,7 @@ from functions.fvnc import *
from functions.fbackup import *
from functions.fpool import *
from functions.fedit import *
from functions.fdisks import *
app = Flask(__name__,static_url_path='',static_folder='static',template_folder='templates')
@ -107,12 +108,13 @@ def logout():
@app.route('/',methods=['GET'])
@login_required
def get_home():
vlxc, vlibvirt, vhype = get_version()
lxc_up = len(get_lxc_activ())
lxc_down = len(get_lxc_inactiv())
vm_up = len(get_vm_activ())
vm_down = len(get_vm_inactiv())
full = get_host_full()
return render_template('index.html', host=Host(), full = full, lxc_up=lxc_up, lxc_down=lxc_down, vm_up=vm_up, vm_down=vm_down)
return render_template('index.html', host=Host(), full = full, lxc_up=lxc_up, lxc_down=lxc_down, vm_up=vm_up, vm_down=vm_down,vlxc=vlxc, vlibvirt=vlibvirt, vhype=vhype)
def get_ressources():
while True:
@ -181,6 +183,53 @@ def delvol():
flash('Error on deleting '+volume_name+':'+str(e), category='danger')
return redirect(url_for('get_pool'))
############################
## DISKS ##
############################
@app.route('/editdisks',methods=['POST'])
@login_required
def editdisks():
disk_id = request.form['disk_id']
disk_file = request.form['diskfile']
disk_name = os.path.basename(disk_file)
actual_size = request.form['actual_size']
new_size = request.form['new_size']
vm_name = request.form['vm_name']
try:
resize_disk(disk_file,actual_size,new_size,vm_name,disk_id)
flash(disk_name+' had been resized to '+str(new_size)+'G', category='success')
except Exception as e:
flash('Error resizing '+disk_name+':'+str(e), category='danger')
return redirect(url_for('state'))
@app.route('/adddisk',methods=['POST'])
@login_required
def adddisk():
disk_name = request.form['disk_name']
vm_name = request.form['vm_name']
disk_size = request.form['disk_size']
disk_id = request.form['disk_id']
try:
create_attached_disk(vm_name,disk_name,disk_size,disk_id)
flash(disk_name+' had been created and attached', category='success')
except Exception as e:
flash('Error creating '+disk_name+':'+str(e), category='danger')
return redirect(url_for('state'))
@app.route('/detachdisk',methods=['POST'])
@login_required
def detachdisk():
diskfile = request.form['diskfile']
disk_name = os.path.basename(diskfile)
vm_name = request.form['vm_name']
try:
detach_disk(vm_name,diskfile)
flash(disk_name+' had been dettached', category='success')
except Exception as e:
flash('Error Detaching '+disk_name+' on '+vm_name+':'+str(e), category='danger')
return redirect(url_for('state'))
############################
## VNC ##
############################
@ -291,24 +340,27 @@ def edit():
vm_name = request.form['edit']
state, maxmem, mem, cpus, cput = get_info_vm(vm_name)
#Memory in MB
return render_template('edit.html', vm_name=vm_name, max_Mermory=conn.getInfo()[1],max_vCPU= conn.getMaxVcpus(None),actual_vCPU=cpus, actual_ram=int(mem/1024),screen_64=get_screenshot(vm_name).decode('ASCII'),actual_autostart=get_autostart(vm_name))
return render_template('edit.html', vm_name=vm_name, max_Mermory=conn.getInfo()[1],max_vCPU= conn.getMaxVcpus(None),actual_vCPU=cpus, actual_ram=int(mem/1024),screen_64=get_screenshot(vm_name).decode('ASCII'),actual_autostart=get_autostart(vm_name),disks=get_disks_info(vm_name))
@app.route('/editressources',methods=['POST'])
@login_required
def editressources():
vm_name = request.form['vm_name']
state, maxmem, mem, cpus, cput = get_info_vm(vm_name)
new_ram = int(request.form['new_mem'])
new_cpu = int(request.form['new_cpu'])
new_autostart = request.form.get('new_autostart')
try:
set_memory(vm_name,int(new_ram*1024))
except Exception as e:
flash('Error editing Memory for '+vm_name+': '+str(e), category='danger')
try:
set_vcpu(vm_name,new_cpu)
except Exception as e:
flash('Error editing CPU for '+vm_name+': '+str(e), category='danger')
new_cpu = int(request.form['new_cpu'])
if new_cpu != cpus:
try:
set_vcpu(vm_name,new_cpu)
except Exception as e:
flash('Error editing CPU for '+vm_name+': '+str(e), category='danger')
if new_ram != int(mem/1024):
try:
set_memory(vm_name,int(new_ram*1024))
except Exception as e:
flash('Error editing Memory for '+vm_name+': '+str(e), category='danger')
#Autostart
if request.form.get('new_autostart')=='auto_check':
try:

Binary file not shown.

View File

@ -55,3 +55,11 @@ def del_snap_vm(vm_name,item):
dom=conn.lookupByName(vm_name)
snap_del=dom.snapshotLookupByName(item)
snap_del.delete()
def rest_snap_vm(vm_name,item):
dom=conn.lookupByName(vm_name)
snaps = dom.listAllSnapshots()
for snap in snaps:
if snap.getName() == item:
dom.revertToSnapshot(snap)

View File

@ -0,0 +1,81 @@
import os
from config import *
from xml.dom import minidom
import subprocess
from functions.fpool import *
def get_volume_disk(disk_path):
refresh_pool()
vol_disk = conn.storageVolLookupByPath(disk_path)
vol_size = vol_disk.info()[1]
vol_size_G = vol_size / (1024*1024*1024)
return vol_size_G
def get_disks_info(vm_name):
disks = []
dom = conn.lookupByName(vm_name)
raw_xml = dom.XMLDesc(0)
xml = minidom.parseString(raw_xml)
diskTypes = xml.getElementsByTagName('disk')
for diskType in diskTypes:
if diskType.getAttribute('device') == 'disk':
disk_unit = []
disk_id = 'Unknown'
diskNodes = diskType.childNodes
for diskNode in diskNodes:
if diskNode.nodeName == 'target':
for attr in diskNode.attributes.keys():
if diskNode.attributes[attr].name == 'dev':
disk_id = diskNode.attributes[attr].value
if diskNode.nodeName == 'source':
for attr in diskNode.attributes.keys():
if diskNode.attributes[attr].name == 'file':
blkinf = dom.blockInfo(diskNode.attributes[attr].value)
vol_size = os.path.getsize(diskNode.attributes[attr].value)
diskname = os.path.basename(diskNode.attributes[attr].value)
volsize = get_volume_disk(diskNode.attributes[attr].value)
disksize = round(blkinf[0] / 1024**3)
diskused = round(blkinf[1] / 1024**3)
if disksize > 0:
diskpercent = (diskused*100 / disksize)
else:
diskpercent = 0
disk_unit = [diskNode.attributes[attr].value,disksize,diskused,diskpercent,diskname,int(volsize)]
if diskNode.nodeName == 'target':
for attr in diskNode.attributes.keys():
if diskNode.attributes[attr].name == 'dev':
disk_id = diskNode.attributes[attr].value
disk_unit.append(disk_id)
disks.append(disk_unit)
return disks
def create_attached_disk(vm_name,disk_name,disk_size,disk_id):
cmd = "qemu-img create -f qcow2 "+str(disk_path)+str(disk_name)+".qcow2 "+str(disk_size)+"G"
subprocess.call(cmd, shell=True)
dom = conn.lookupByName(vm_name)
if not dom.isActive():
dom.create()
disk_full = disk_path+disk_name+".qcow2"
cmd = "virsh attach-disk --domain "+vm_name+" "+disk_full+" --target "+disk_id+" --persistent --config --live"
subprocess.call(cmd, shell=True)
refresh_pool()
def resize_disk(disk_file,actual_size,new_size,vm_name,disk_id):
cmd = "virsh detach-disk '"+vm_name+"' "+disk_file+" --persistent --config --live"
subprocess.call(cmd, shell=True)
if int(actual_size) < int(new_size):
cmd = "qemu-img resize -f qcow2 "+str(disk_file)+" "+str(new_size)+"G"
subprocess.call(cmd, shell=True)
else:
cmd = "qemu-img resize -f qcow2 --shrink "+str(disk_file)+" "+str(new_size)+"G"
subprocess.call(cmd, shell=True)
cmd = "virsh attach-disk '"+str(vm_name)+"' "+str(disk_file)+" "+str(disk_id)+" --cache none --persistent --config --live"
subprocess.call(cmd, shell=True)
refresh_pool()
def detach_disk(vm_name,diskfile):
cmd = "virsh detach-disk '"+str(vm_name)+"' "+str(diskfile)+" --persistent --config --live"
subprocess.call(cmd, shell=True)
refresh_pool()

View File

@ -1,20 +1,71 @@
from config import *
import base64
import time
import lxc
def get_version():
vlxc = lxc.version
vlibvirt = str(libvirt.getVersion()/1000000) #1000000 * major + 1000 * minor + release
vhype = hype_version
return vlxc, vlibvirt, vhype
#memory = Memory in Mo
def set_memory(vm_name,memory_new):
dom = conn.lookupByName(vm_name)
dom.setMemory(memory_new)
dom.shutdown()
time.sleep(3)
alive=0
while alive < 5:
if dom.isActive():
time.sleep(3)
alive+=1
else:
alive=6
if dom.isActive():
dom.destroy()
dom.setMaxMemory(memory_new)
dom.setMemoryFlags(memory_new)
dom.create()
time.sleep(3)
alive=0
while alive < 3:
if dom.isActive():
alive=4
else:
time.sleep(3)
alive+=1
def set_vcpu(vm_name,vcpu_new):
dom = conn.lookupByName(vm_name)
dom.setVcpus(vcpu_new)
dom.shutdown()
time.sleep(3)
alive=0
while alive < 5:
if dom.isActive():
time.sleep(3)
alive+=1
else:
alive=6
if dom.isActive():
dom.destroy()
dom.setVcpusFlags(vcpu_new,libvirt.VIR_DOMAIN_AFFECT_CONFIG | libvirt.VIR_DOMAIN_VCPU_MAXIMUM)
dom.setVcpusFlags(vcpu_new,libvirt.VIR_DOMAIN_AFFECT_CONFIG | libvirt.VIR_DOMAIN_VCPU_CURRENT)
dom.create()
time.sleep(3)
alive=0
while alive < 3:
if dom.isActive():
alive=4
else:
time.sleep(3)
alive+=1
def get_info_vm(vm_name):
dom = conn.lookupByName(vm_name)
state, maxmem, mem, cpus, cput = dom.info()
return state, maxmem, mem, cpus, cput
#Screenshot
def get_screenshot(vm_name):
dom = conn.lookupByName(vm_name)
stream = conn.newStream()
@ -31,3 +82,18 @@ def get_screenshot(vm_name):
data = base64.b64encode(f.read())
os.remove(file)
return data
#Autostart
def get_autostart(vm_name):
dom = conn.lookupByName(vm_name)
return dom.autostart()
def set_autostart(vm_name):
dom = conn.lookupByName(vm_name)
dom.setAutostart(1)
def unset_autostart(vm_name):
dom = conn.lookupByName(vm_name)
dom.setAutostart(0)

View File

@ -29,7 +29,6 @@ def get_pool_info(pool_name):
pool_info.append(pool.UUIDString())
pool_info.append(str(pool.isActive()))
pool_info.append(str(human_size(pool.info()[1])))
pool_info.append(str(human_size(pool.info()[2])))
pool_info.append(str(human_size(pool.info()[3])))
if pool.info()[1]==0:

View File

@ -66,5 +66,10 @@
</div>
</div>
</div>
<script>
var myDropzone = new Dropzone("#file-dropzone", {
maxFilesize: 10240, // 10Go in Mo
timeout: 0,
});
</script>
{% endblock %}

View File

@ -12,18 +12,98 @@
<img src="data:image/png;base64,{{ screen_64 }}" class="img-thumbnail">
</div>
<div class="col-sm">
Vcpu : {{ actual_vCPU }}</br><br>
Memory: {{ actual_ram }} MB</br>
<i class="fa-solid fa-microchip"></i> vCPU : {{ actual_vCPU }}</br></br>
<i class="fa-solid fa-memory"></i> Memory: {{ actual_ram }} MB</br></br>
{% if actual_autostart == 1 %}
Autostart : <input type="checkbox" class="form-check-input" checked disabled/>
{% else %}
Autostart : <input type="checkbox" class="form-check-input" disabled/>
{% endif %}
</div>
<div class="col-sm">
<form action="/editressources" method="post">
<input type="hidden" name="vm_name" value="{{ vm_name }}"></input>
<input type="text" name="new_cpu" class="form-control form-control-sm" placeholder="{{ actual_vCPU }}" value="{{ actual_vCPU }}"></input> (Max : {{ max_vCPU }})<br>
<input type="text" name="new_mem" class="form-control form-control-sm" placeholder="{{ actual_ram }}" value="{{ actual_ram }}" ></input> MB (Max : {{ max_Mermory }} MB)<br><br>
<button type="submit" class="btn btn-outline-warning btn-hype">Set New Values</button>
</form>
</div>
<table><tr><td>CPU :</td><td><input type="text" name="new_cpu" class="form-control form-control-sm" placeholder="{{ actual_vCPU }}" value="{{ actual_vCPU }}" ></input></td><td><small> (Max : {{ max_vCPU }} )</small></td></tr></table><br>
<table><tr><td>Memory :</td><td><input type="text" name="new_mem" class="form-control form-control-sm" placeholder="{{ actual_ram }}" value="{{ actual_ram }}" ></input></td><td> MB <small> (Max : {{ max_Mermory }} MB)</small></td></tr></table><br>
{% if actual_autostart == 1 %}
<input type="checkbox" name="new_autostart" class="form-check-input" value="auto_check" checked/> Autostart
{% else %}
<input type="checkbox" name="new_autostart" class="form-check-input" value="auto_check"/> Autostart
{% endif %}
<br><br>
<br><small><i class="fa-solid fa-triangle-exclamation"></i> Changing vCPU and/or Memory will restart {{ vm_name }} <i class="fa-solid fa-triangle-exclamation"></i></small>
<br><br>
<button type="submit" class="btn btn-outline-warning btn-hype"><i class="fa-solid fa-pencil"></i></button>
</form>
</div>
</div>
</div>
</div>
<br>
<div class="card">
<div class="card-header">
Disk(s) of {{ vm_name }}
</div>
<div class="card-body">
<div class="row">
<div class="col-sm">
{% for disk in disks %}
<i class="fa-solid fa-hard-drive"></i>
<b>{{ disk[4] }}</b> ({{ disk[5] }} G) <br>
<small>System disk:<br>name: {{ disk[6] }} - Size : {{ disk[1] }} G (Used : {{ disk[2] }} G)</br></small><br>
<div class="progress">
<div id="diskbar" class="progress-bar bg-info progress-bar-striped" role="progressbar" style="width: {{ disk[3] }}%;" aria-valuenow={{ disk[3] }} aria-valuemin="0" aria-valuemax="100">{{ disk[3] }} %</div>
</div><br>
{% endfor %}
</div>
<div class="col-sm">
<small><i class="fa-solid fa-triangle-exclamation"></i> Shrinking a disk size, can corrupt or delete your data <i class="fa-solid fa-triangle-exclamation"></i></small><br><br>
<table style="width:100%;">
{% for disk in disks %}
<tr><td>{{ disk[4] }} </td><td>
<form action="/editdisks" method="post">
<input type="hidden" name="vm_name" value="{{vm_name }}" />
<input type="hidden" name="disk_id" value="{{ disk[6] }}">
<input type="hidden" name="actual_size" value="{{ disk[5] }}" />
<input type="hidden" name="diskfile" value="{{ disk[0] }}" />
<input type="text" name="new_size" class="form-control form-control-sm" placeholder="{{ disk[5] }}" value="{{ new_size }}" />
</td><td> G </td><td>
<button type="submit" class="btn btn-outline-info btn-hype">Resize</button>
</form>
</td><td>
<form action="/detachdisk" method="post">
<input type="hidden" name="vm_name" value="{{vm_name }}" />
<input type="hidden" name="diskfile" value="{{ disk[0] }}" />
<button type="submit" class="btn btn-outline-danger btn-hype"><i class="fa-solid fa-link-slash"></i></button>
</form>
</td>
</tr><tr></tr><tr></tr><tr></tr><tr>
{% endfor%}
</table>
</div>
</div>
<br>
Add a disk<hr>
<div class="row">
<div class="col-sm">
<form action="/adddisk" method="post">
<input type="hidden" name="vm_name" value="{{ vm_name }}">
Name </br></br></br>Size </br></br>Disk target<br>
</div>
<div class="col-sm">
<input type="text" name="disk_name" class="form-control form-control-sm" ><br>
<input type="text" name="disk_size" class="form-control form-control-smplaceholder="10"" > G<br>
<input type="text" name="disk_id" class="form-control form-control-sm" placeholder="vda"><br>
</div>
<div class="col-sm">
</div>
<button type="submit" class="btn btn-outline-info btn-hype"><i class="fa-solid fa-plus"></i> Add</button>
</form>
</div>
</div>
</div>

View File

@ -10,7 +10,8 @@
</div>
<div class="card-body">
<h1 class="display-6">{{ host.hostname }}</h1>
Last boot : {{ host.boot_time }}
Last boot : {{ host.boot_time }}<br>
<b>LXC:</b> {{ vlxc }} - <b>Libvirt:</b> {{ vlibvirt }} - <b>Hype:</b> {{ vhype }}
</div>
</div>
</div>

View File

@ -60,4 +60,12 @@
</div>
</div>
</div>
<!-- DropZone -->
<script>
var myDropzone = new Dropzone("#file-dropzone", {
maxFilesize: 10240, // 10Go in Mo
timeout: 0,
});
</script>
{% endblock %}

View File

@ -16,7 +16,6 @@
<link href="{{url_for('static', filename = 'style.css')}}" rel="stylesheet">
<link href="{{url_for('static', filename = 'dropzone.min.css')}}" rel="stylesheet">
<script src="{{url_for('static', filename = 'popper.min.js')}}"></script>
<script src="{{url_for('static', filename = 'bootstrap.bundle.min.js')}}"></script>
<script src="{{url_for('static', filename = 'jquery.min.js')}}"></script>
@ -73,8 +72,6 @@
{% block content %}{% endblock %}
<!-- Pour le fun -->
<div style="position:fixed;bottom:0;right:0">Version Beta 2.7</div>
<!-- Alerting -->
<script>
var opacity=0;
@ -117,14 +114,14 @@ menu_btn.addEventListener("click", () => {
}
// ]]>
</script>
<!-- DropZone -->
<!-- DropZone
<script>
var myDropzone = new Dropzone("#file-dropzone", {
maxFilesize: 10240, // 10Go in Mo
timeout: 0,
});
</script>
-->
<!-- Alert JS -->
<script>
//Get the cookie related to theme

View File

@ -20,6 +20,7 @@
</td><td>
<form action="/snaplxc" method="post"><button type="submit" class="btn btn-outline-secondary btn-hype disabled" value="{{ act_lxc[0] }}" name="snap" onclick="loading();"><i class="fa-solid fa-box-archive"></i></button></form>
</td><td>
</td><td>
</td><td>
{{act_lxc[1]}}
</td></tr>
@ -42,7 +43,7 @@
<form action="/snaplxc" method="post"><button type="submit" class="btn btn-outline-secondary btn-hype" value="{{ inact_lxc }}" name="snap" onclick="loading();"><i class="fa-solid fa-box-archive"></i></button></form>
</td><td>
</td><td>
-
</td><td> -
</td></tr>
{% endfor %}
<!-- Activ VM -->

View File

@ -1,8 +1,8 @@
{% extends 'layout.html' %}
{% block content %}
{% include 'menu.html' %}
<a href="/vnc" target="_blank"><i class="fa-solid fa-expand"></i></a></br>
<iframe src="/vnc" height=768px width=1024px class="embed-responsive-item" allowfullscreen></iframe>
<center><a href="/vnc" target="_blank" class="btn btn-outline-info"> Full screen <i class="fa-solid fa-expand"></i></a></br>
<br>
<iframe src="/vnc" height=768px width=1024px class="embed-responsive-item" allowfullscreen></iframe></center>
{% endblock %}