15 Commits
v2.7 ... 3.0

Author SHA1 Message Date
root
58a04d51b3 Small bugs 2024-04-16 15:13:46 +02:00
root
e2fcc56ce0 Adding Network info and edition 2024-04-11 11:06:52 +02:00
root
843555a9d5 Adding Network and Small corrections 2024-04-07 17:42:39 +02:00
root
a3b7ef628e Adding Virtio and README info 2024-03-26 14:36:15 +01:00
root
cf6fe76352 Disk edition improve 2024-03-26 13:58:07 +01:00
root
a2683789d1 Disk Edition : Resize and Create attached disks 2024-03-25 15:49:12 +01:00
root
93f2a5379f Storage reviewed 2024-03-18 11:03:26 +01:00
root
2f113ad01b Adding VM edition and screenshot 2024-03-13 15:19:53 +01:00
root
b6ece56080 Adding edit and screenshot 2024-03-13 12:27:08 +01:00
root
e6d087f148 Terminal 2024-03-01 22:56:49 +01:00
root
8f39dc4768 README update 2024-03-01 16:58:56 +01:00
root
617a6481e9 VM snapshot add/delete 2024-02-29 16:12:31 +01:00
root
1be2e77d8a DOCU bug correct 2024-02-26 17:13:33 +01:00
root
a7906586a7 Doc and Win 2024-02-26 17:11:21 +01:00
root
ca71c554b4 Added Windows 2024-02-23 15:08:24 +01:00
30 changed files with 1270 additions and 44 deletions

113
DOCUMENTATION.md Normal file
View File

@@ -0,0 +1,113 @@
# Documentation
This is more a techincal documentation that how to use this tool.
# Install
Please refer to the README file, all install steps are described.
## Pipx
Not refered in the install, but you can pipx for you install.
# Networking
Hype use OpenvSwicth for interface and network creation. Common ovs command use in case of debgug.
DNSMask, provided by LXC, is used to create DHCP for Interface configuration.
# Storage
The server storage are in *{HYPE}/storage/*
You will find a folder *win* for the virtio image, *disks* for the VM images (qcow2) and iso for iso install images.
You can change these storage in *config.py* file.
For the *disks*,if you prefer to use the default libvirt pool storage (*/var/lib/libvirt/images*)
You can modify :
*--disk path='+str(disk_path)+str(nom)+'.qcow2,size='+str(disk)+',bus=virtio*
by
*--disk pool=default,size='+str(disk)+',bus=virtio,format=qcow2*
# Virtual Server Creation
## Linux
For the moment, no issue with tested distribution:
- Archlinux
- Debian/ubuntu
- Centos/Fedora/Redhat
- FreeBSD
- TinyCore
## Other install
Tested succeffully :
- Pfsense,Dynfi,Opnsense
## Windows
Windows OS will need some extra-drivers to run on virtualized server.
For this during the install, the VM will need a virtIO-win iso with all drivers.
The VirtIO iso is already mounted as a CD-ROM on VM creation. Load the drivers according to you OS install.
### VirtIO
VirtIO is a QEMU drivers for Windows OS (please read : https://developer.ibm.com/articles/l-virtio/)
VirtIO can be found here :
https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/?C=M;O=D
Take care of the version, some virtio are not compatible with some Windows version.
This iso is on ./storage/win/ and configured in **config.py** for upgrade.
### Tested Windows Installation (tested with Virtio 0.1.229):
Validate for :
- Windows 8, 8,1 10
- Windows Server 2019, 2022
Failed with :
- Windows 98, Windows XP, Windows 11 (UEFI access)
Never Tested :
- Win 7 and Vista
#OTHER
Please report your bugs to improve this dev.
#SOURCES
https://libvirt.gitlab.io/libvirt-appdev-guide-python/index.html
https://libvirt-python.readthedocs.io/
https://linuxcontainers.org/lxc/documentation/
# ERROR and SOLUTION
You may incounter some error in specific case, please report them if they are not in the list,
They will be consider on nexts releases.
ERROR: Error destroying Requested operation is not valid: cannot undefine domain with nvram :
SOLUTION (on CLI): virsh undefine --nvram VM_NAME
ERROR: Error stoping NOM_VM:Requested operation is not valid: domain is not running
SOLUTION 1 (on CLI): virsh reset VM_NAME
SOLUTION 2 (on CLI): virsh destroy VM_NAME (destroy the VM)
ERROR: libvirt: Storage Driver error : Requested operation is not valid: storage pool 'iso' is not active
SOLUTION (on CLI): pool = conn.storagePoolLookupByName('iso')
pool.undefine()

View File

@@ -1,7 +1,10 @@
# Hype² # Hype²
##News
- Now compatible with Windows Install, just take care to launch the driver before scanning disks.
- Screenshot and VM live edition (RAM and vCPU until Max declared) added
- Disk creation and resize
- Nework management (add,del network and interfaces)
```mermaid ```mermaid
graph LR graph LR
@@ -12,14 +15,18 @@ Server[Server] <----> LXC((LXC))
## Install requirements ## Install requirements (Debian example
For your safty you can also find the packages on pip or pipx
1 - Update and Install packages : 1 - Update and Install packages :
```sh ```sh
apt-get update -y -qq apt-get update -y -qq
apt-get install git lxc lxcfs lxc-templates qemu qemu-utils qemu-kvm virtinst bridge-utils virt-manager libvirt-daemon libvirt-daemon-system virt-viewer libvirt-clients libosinfo-bin websockify sqlite3 novnc apt-get install git lxc lxcfs lxc-templates qemu qemu-utils qemu-kvm virtinst bridge-utils virt-manager libvirt-daemon libvirt-daemon-system virt-viewer libvirt-clients libosinfo-bin websockify sqlite3 novnc
apt-get install python3 python3-flask python3-flask-login python3-flask-sqlalchemy python3-requests python3-lxc python3-libvirt python3-psutil python3-werkzeug python3-websockify python3-novnc apt-get install python3 python3-flask python3-flask-login python3-flask-sqlalchemy python3-requests python3-lxc python3-libvirt python3-psutil python3-werkzeug python3-websockify python3-novnc python3-flask-socketio python3-types-pyopenssl
apt-get install openvswitch-switch openvswitch-common
``` ```
Clone the repository : Clone the repository :
@@ -27,6 +34,7 @@ Clone the repository :
```sh ```sh
git clone https://git.nerkdesign.com/pporcheret/Hype-2.git git clone https://git.nerkdesign.com/pporcheret/Hype-2.git
cd Hype-2 cd Hype-2
mkdir storage/disks
``` ```
2 - Configure Libvirt to start on boot 2 - Configure Libvirt to start on boot
@@ -46,6 +54,23 @@ virsh net-define bridged.xml
virsh net-start bridged virsh net-start bridged
virsh net-autostart bridged virsh net-autostart bridged
``` ```
On your host, create the bridge interface "bro", according to your network with :
- @@@ The physical interface
- XXX.XXX.XXX.XXX your fixed IP (recommended)
- YYY.YYY.YYY.YYY your netmask
- ZZZ.ZZZ.ZZZ.ZZZ your gateway
```sh
auto br0
iface br0 inet static
bridge_ports @@@@
bridge_fd 0
bridge_maxwait 0
address XXX.XXX.XXX.XXX
netmask YYY.YYY.YYY.YYY
gateway ZZZ.ZZZ.ZZZ.ZZZ
```
## Database for users ## Database for users
@@ -57,6 +82,7 @@ Once connected, you will be able to create/manage users directly on software.
```sh ```sh
cp db.db.admin_example db.db cp db.db.admin_example db.db
``` ```
For security reason, you should remove **db.db.admin_example**
If you want to create this database by yourself, you can : If you want to create this database by yourself, you can :
@@ -167,7 +193,7 @@ ProxyPassReverse / https://<your_ip>:5007/
## Run and use ## Run and use
```sh ```sh
python3 app python3 app.py
and go to https://www.example.com (or https://<server_ip>:5007 without RP) with your credentials (mail and normal password or admin) and go to https://www.example.com (or https://<server_ip>:5007 without RP) with your credentials (mail and normal password or admin)
@@ -199,10 +225,9 @@ systemctl start hype.service
You can also change some configuration in the file *configuration.py* such as flask port, storage location...use it at your own risk. You can also change some configuration in the file *configuration.py* such as flask port, storage location...use it at your own risk.
## Others ## Others
You can upload Virtual Machine ISO directly from the interface. You can upload ISO directly from the interface.
If you desire to use another way such as scp or else, you can place the iso in the *<hype_path>/storage/iso/* folder If you desire to use another way such as scp or else, you can place the iso in the *<hype_path>/storage/iso/* folder

Binary file not shown.

314
app.py

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,7 @@ import os
import libvirt import libvirt
import math import math
hype_version = '2.7'
#Flask config #Flask config
class flask_config: class flask_config:
port=5007 port=5007
@@ -12,7 +13,9 @@ class flask_config:
#Path #Path
path = os.path.abspath(os.path.dirname(__file__)) path = os.path.abspath(os.path.dirname(__file__))
iso_path= path+'/storage/iso/' iso_path= path+'/storage/iso/'
disk_path = path+'/storage/disks/'
virtuo_path= path+'/storage/win/'
virtuo_file='virtio-win-0.1.229.iso'
#Qemu connection #Qemu connection
try: try:
conn = libvirt.open("qemu:///system") conn = libvirt.open("qemu:///system")

BIN
db.db Normal file

Binary file not shown.

View File

@@ -50,3 +50,16 @@ def get_snap_list_vm(vm_name):
for snap in list_snap_vm: for snap in list_snap_vm:
out.append(snap) out.append(snap)
return out return out
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)

115
functions/fdisks.py Normal file
View File

@@ -0,0 +1,115 @@
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):
pool = conn.storagePoolLookupByName('disks')
disk_full = disk_path+disk_name+".qcow2"
xml_desc = f"""
<volume>
<name>{disk_name}.qcow2</name>
<capacity unit="G">{disk_size}</capacity>
<allocation unit="G">{disk_size}</allocation>
<target>
<format type="qcow2"/>
<path>{disk_full}</path>
</target>
</volume>
"""
pool.createXML(xml_desc, 0)
dom = conn.lookupByName(vm_name)
try:
dom.resume
except:
pass
disk_full = disk_path+disk_name+".qcow2"
flags = (libvirt.VIR_DOMAIN_AFFECT_CONFIG |
libvirt.VIR_DOMAIN_AFFECT_LIVE |
libvirt.VIR_DOMAIN_AFFECT_CURRENT)
disk_xml = """
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='{source}'/>
<target dev='{target}'/>
</disk>
""".format(source=disk_full, target=disk_id)
dom.attachDeviceFlags(disk_xml, flags)
refresh_pool()
def resize_disk(disk_file,actual_size,new_size,vm_name,disk_id):
dom = conn.lookupByName(vm_name)
flags = (libvirt.VIR_DOMAIN_AFFECT_CONFIG |
libvirt.VIR_DOMAIN_AFFECT_LIVE |
libvirt.VIR_DOMAIN_AFFECT_CURRENT)
disk_xml = """
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='{source}'/>
<target dev='{target}'/>
</disk>
""".format(source=disk_file, target=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)
dom.attachDeviceFlags(disk_xml, flags)
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()

118
functions/fedit.py Normal file
View File

@@ -0,0 +1,118 @@
from config import *
from functions.fnet 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
def set_memory(vm_name,memory_new):
dom = conn.lookupByName(vm_name)
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.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
def attach_net(vm_name,net_name):
dom = conn.lookupByName(vm_name)
network_xml = f'<interface type="network"><source network="{net_name}"/><model type="virtio"/></interface>'
dom.attachDeviceFlags(network_xml, libvirt.VIR_DOMAIN_AFFECT_CONFIG | libvirt.VIR_DOMAIN_AFFECT_LIVE)
def detach_net(vm_name, net_name, net_mac,net_int):
dom = conn.lookupByName(vm_name)
# network_xml = f'<interface type="network"><mac address="{net_mac}"/><source network="{net_name}"/><model type="virtio"/></interface>'
network_xml = f'<interface type="bridge"><mac address="{net_mac}"/><source network="{net_name}" bridge="{net_int}"/></interface>'
print(network_xml)
dom.detachDeviceFlags(network_xml, libvirt.VIR_DOMAIN_AFFECT_CONFIG | libvirt.VIR_DOMAIN_AFFECT_LIVE)
def get_screenshot(vm_name):
dom = conn.lookupByName(vm_name)
stream = conn.newStream()
imageType = dom.screenshot(stream,0)
file = "tmp_screen_" + dom.name()
fileHandler = open(file, 'wb')
streamBytes = stream.recv(262120)
while streamBytes != b'':
fileHandler.write(streamBytes)
streamBytes = stream.recv(262120)
fileHandler.close()
stream.finish()
with open(file, "rb") as f:
data = base64.b64encode(f.read())
os.remove(file)
return data
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)
"""
def get_net_vm(vm_name):
netws, nu2 = get_virt_int()
vm_net_list=[]
for net in netws:
if vm_name in net[1][0]:
vm_net_list.append(net[0][0][0])
return vm_net_list
"""

View File

@@ -61,3 +61,21 @@ def destroy_lxc(lxc_name):
container=lxc.Container(lxc_name) container=lxc.Container(lxc_name)
container.destroy() container.destroy()
## RESSOURCES
def get_lxc_ressources(lxc_name):
container=lxc.Container(lxc_name)
mem_max = container.get_config_item('lxc.cgroup2.memory.max')
mem = container.get_config_item('lxc.cgroup2.memory.high')
swap_max = container.get_config_item('lxc.cgroup2.memory.swap.max')
vcpu = container.get_config_item('lxc.cgroup2.cpuset.cpus')
return vcpu, mem, mem_max, swap_max
def set_lxc_ressources(lxc_name,lxc_item,val):
container=lxc.Container(lxc_name)
container.set_config_item(lxc_item,val)
container.save_config()

125
functions/fnet.py Normal file
View File

@@ -0,0 +1,125 @@
from app import *
import ipaddress
from xml.dom import minidom
def ip_fom_mac(vm_name,net_mac):
dom=conn.lookupByName(vm_name)
mac_ip=[]
try:
ifaces = dom.interfaceAddresses(libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT)
except:
ifaces = None
result="---.---.---.---"
if ifaces is None:
result="---.---.---.---"
else:
for (name, val) in ifaces.items():
if val['addrs']:
for addr in val['addrs']:
if addr['addr']:
mac_ip.append([val['hwaddr'],addr['addr']])
break
else:
result = "---.---.---.---"
for int in mac_ip:
if int[0] == net_mac:
result=int[1]
else:
result = "---.---.---.---"
return result
def diff_net(min_dhcp,max_dhcp):
ip1 = ipaddress.IPv4Address(min_dhcp)
ip2 = ipaddress.IPv4Address(max_dhcp)
nb_ip = int(ip2) - int(ip1) + 1
return nb_ip
def get_int(net_name):
network = conn.networkLookupByName(net_name)
net_int = network.bridgeName()
return net_int
def create_vswitch_int(net_int):
cmd="ovs-vsctl add-br "+net_int
out = subprocess.call(cmd, shell=True)
if out2 != 0:
raise Exception("Error on creating interface")
def delete_network(net_name,net_int):
cmd = "ovs-vsctl del-br "+net_int
out = subprocess.call(cmd, shell=True)
if out != 0:
raise Exception("Error deleting interface")
network = conn.networkLookupByName(net_name)
network.destroy()
network.undefine()
cmd2="kill $(cat /run/dhypecp_"+net_int+".pid)"
out2 = subprocess.call(cmd2, shell=True)
if out2 != 0:
raise Exception("Error on killing DHCP process")
cmd3="rm -f /run/dhypecp_"+net_int+".pid"
out3 = subprocess.call(cmd3, shell=True)
if out3 != 0:
raise Exception("Error on Deleting DHCP process")
def create_network(net_name,net_int):
create_vswitch_int(net_int)
xml = f"""
<network>
<name>{ net_name }</name>
<forward mode='bridge'/>
<bridge name='{ net_int }'/>
<virtualport type='openvswitch'/>
</network>
"""
conn.networkDefineXML(xml)
net_use = conn.networkLookupByName(net_name)
net_use.create()
net_use.setAutostart(True)
def get_mac_net(vm_name,net_name):
dom = conn.lookupByName(vm_name)
raw_xml = dom.XMLDesc()
net_xml = minidom.parseString(raw_xml)
VM_mac = []
for device in net_xml.getElementsByTagName('devices'):
ifaces = device.getElementsByTagName('interface')
for iface in ifaces:
sources = iface.getElementsByTagName('source')
for source in sources:
if source.getAttribute('network') == net_name:
mac = iface.getElementsByTagName('mac')
target = iface.getElementsByTagName('target')
ip = ip_fom_mac(vm_name,str(mac[0].getAttribute('address')))
VM_mac.append([target[0].getAttribute('dev'),mac[0].getAttribute('address'),ip])
return VM_mac
def get_virt_int():
net_na = []
out=[]
for network in conn.listAllNetworks():
if network.isActive():
int_name=[]
int_mac = conn.interfaceLookupByName(network.bridgeName())
int_name.append([network.name(),network.bridgeName(),int_mac.MACString()])
net_detail = conn.networkLookupByName(network.name())
net_vms = net_detail.listAllPorts()
vm_name=[]
for vm in net_vms:
net_VM_info = []
raw_xml = vm.XMLDesc()
net_xml = minidom.parseString(raw_xml)
net_element = net_xml.documentElement
net_VM = net_element.getElementsByTagName("name")[0]
net_VM_info = [net_VM.firstChild.data,get_mac_net(net_VM.firstChild.data,network.name())]
if net_VM_info not in vm_name:
vm_name.append(net_VM_info)
out.append([int_name,vm_name])
else:
net_na.append(network.name())
return out, net_na

View File

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

20
functions/fscreen.py Normal file
View File

@@ -0,0 +1,20 @@
from config import *
import base64
def get_screenshot(vm_name):
dom = conn.lookupByName(vm_name)
stream = conn.newStream()
imageType = dom.screenshot(stream,0)
file = "tmp_screen_" + dom.name()
fileHandler = open(file, 'wb')
streamBytes = stream.recv(262120)
while streamBytes != b'':
fileHandler.write(streamBytes)
streamBytes = stream.recv(262120)
fileHandler.close()
print('Screenshot saved as type: ' + imageType)
stream.finish()
with open(file, "rb") as f:
data = base64.b64encode(f.read())
os.remove(file)
return data

View File

@@ -9,6 +9,14 @@ def get_vm_activ():
activ_vm.append(dom.name()) activ_vm.append(dom.name())
return activ_vm return activ_vm
#Set a VM as active
def is_active(vm_name):
dom = conn.lookupByName(vm_name)
try:
dom.resume()
except:
pass
def get_vm_inactiv(): def get_vm_inactiv():
inactiv_vm=conn.listDefinedDomains() inactiv_vm=conn.listDefinedDomains()
return inactiv_vm return inactiv_vm

4
requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
flask
flask_sqlalchemy
flask_login
pyopenssl

Binary file not shown.

View File

@@ -26,7 +26,9 @@
{{ snap_vm }}</th><th> {{ snap_vm }}</th><th>
<button type="submit" class="btn btn-outline-secondary btn-hype" value="{{ snap }}" name="start" onclick="loading();"><i class="fa-solid fa-rotate-left"></i></button> <button type="submit" class="btn btn-outline-secondary btn-hype" value="{{ snap }}" name="start" onclick="loading();"><i class="fa-solid fa-rotate-left"></i></button>
</th><th> </th><th>
<button type="submit" class="btn btn-outline-danger btn-hype" value="{{ snap }}" name="start" onclick="loading();"><i class="fa-solid fa-trash"></i></button> <form action="/del_snap_vm" method="post"><input type="hidden" name="vm_name" value="{{ vm[0] }}">
<button type="submit" class="btn btn-outline-danger btn-hype" value="{{ snap_vm }}" name="item" onclick="loading();"><i class="fa-solid fa-trash"></i></button>
</form>
</th></tr></table> </th></tr></table>
{% endfor %} {% endfor %}
</td></tr> </td></tr>

View File

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

36
templates/createnet.html Normal file
View File

@@ -0,0 +1,36 @@
{% extends 'layout.html' %}
{% block content %}
{% include 'menu.html' %}
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<i class="fa-solid fa-ethernet"></i> - Build Network
</div>
<div class="card-body">
<form action="/createint" method="post">
<label for="net_name">Network name (no space)</label>
<input type="text" id="net_name" name="net_name" class="form-control" placeholder="no_net" required><br>
<label for="net_int">Interface name (no space)</label>
<input type="text" id="net_int" name="net_int" class="form-control" placeholder="example veth0" required><br>
<label for="ip_int">Interface IP</label>
<input type="text" id="ip_int" name="ip_int" class="form-control" placeholder="example: 10.2.0.1" required><br>
<label for="mask_int">Interface mask</label>
<input type="text" id="mask_int" name="mask_int" class="form-control" placeholder="example: 255.255.255.0" required><br>
<strong>DHCP Configuration</strong>
<label for="min_dhcp">DHCP minimum IP</label>
<input type="text" id="min_dhcp" name="min_dhcp" class="form-control" placeholder="example: 10.2.0.2" required><br>
<label for="max_dhcp">DHCP maximum IP</label>
<input type="text" id="max_dhcp" name="max_dhcp" class="form-control" placeholder="example: 10.2.0.254" required><br>
<button name="creation" class="btn btn-outline-secondary btn-hype" type="submit" onclick="loading();">Create</button>
</form>
</div>
</div>
</div>
</div>
<br>
{% endblock %}

139
templates/edit.html Normal file
View File

@@ -0,0 +1,139 @@
{% extends 'layout.html' %}
{% block content %}
{% include 'menu.html' %}
<div class="container">
<div class="card">
<div class="card-header">
Ressources of {{ vm_name }}
</div>
<div class="card-body">
<div class="row">
<div class="col-sm">
<img src="data:image/png;base64,{{ screen_64 }}" class="img-thumbnail">
</div>
<div class="col-sm">
<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>
<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" onclick="loading();"><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">
Actual disks<hr>
<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>Be aware that detached disks, still exist ! You should clean unused disks on Pool tab.</small><br>
<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"><i class="fa-solid fa-up-right-and-down-left-from-center"></i></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" onclick="loading();"><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 and attach a new disk<hr>
<div class="row">
<div class="col-sm">
<form action="/adddisk" method="post">
<input type="hidden" name="vm_name" value="{{ vm_name }}">
New disk name </br></br></br>Size </br></br>Disk target (system name)<br>
</div>
<div class="col-sm">
<input type="text" name="disk_name" class="form-control form-control-sm" required><br>
<table><tr><td><input type="text" name="disk_size" class="form-control form-control-smplaceholder="10"" required></td><td> G</td></tr></table><br>
<input type="text" name="disk_id" class="form-control form-control-sm" placeholder="vda" required><br>
</div>
<div class="col-sm">
</div>
<button type="submit" class="btn btn-outline-info btn-hype" onclick="loading();"><i class="fa-solid fa-plus"></i> Add</button>
</form>
</div>
</div>
</div>
<br>
<div class="card">
<div class="card-header">
Networking for {{ vm_name }}
</div>
<div class="card-body">
Attach new network<hr>
<div class="row">
<div class="col-sm">
<form action="/addnetvm" method="post">
<input type="hidden" name="vm_name" value="{{ vm_name }}">
<label for="new_net_vm">Network</label>
<select id="new_net_vm" class="form-control" name="new_net_vm" placeholder="Profile" required>
{%for net in list_net %}
<option value={{ net }}>{{ net }}</option>
{%endfor%}
</select><br>
<button type="submit" class="btn btn-outline-info btn-hype" onclick="loading();"><i class="fa-solid fa-plus"></i> Add</button>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

36
templates/edit_lxc.html Normal file
View File

@@ -0,0 +1,36 @@
{% extends 'layout.html' %}
{% block content %}
{% include 'menu.html' %}
<div class="container">
<div class="card">
<div class="card-header">
Ressources of {{ lxc_name }}
</div>
<div class="card-body">
<div class="row">
<div class="col-sm">
<i class="fa-solid fa-microchip"></i> vCPU : {{ max_vCPU }}</br></br>
<i class="fa-solid fa-memory"></i> Memory: {{ actual_ram }}</br></br>
<i class="fa-solid fa-memory"></i> Max Memory: {{ max_Mermory }}</br></br>
<i class="fa-solid fa-memory"></i> Swap: {{ max_swap }}</br></br>
</div>
<div class="col-sm">
<form action="/editressourceslxc" method="post">
<input type="hidden" name="lxc_name" value="{{ lxc_name }}"></input>
<table><tr><td>CPU :</td><td><input type="text" name="new_cpu" class="form-control form-control-sm" placeholder="{{ max_vCPU }}" value="{{ max_vCPU }}" ></input></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></tr></table><br>
<table><tr><td>Max Memory :</td><td><input type="text" name="new_max_mem" class="form-control form-control-sm" placeholder="{{ max_Mermory }}" value="{{ max_Mermory }}" ></input></td></tr></table><br>
<table><tr><td>Swap :</td><td><input type="text" name="new_max_swap" class="form-control form-control-sm" placeholder="{{ max_swap }}" value="{{ max_swap }}" ></input></td></tr></table><br>
<br><br>
<button type="submit" class="btn btn-outline-warning btn-hype" onclick="loading();"><i class="fa-solid fa-pencil"></i></button>
</form>
</div>
</div>
</div>
</div>
<br>
</div>
{% endblock %}

View File

@@ -10,7 +10,8 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<h1 class="display-6">{{ host.hostname }}</h1> <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> </div>
</div> </div>

View File

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

View File

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

View File

@@ -22,6 +22,9 @@
<li class="nav-link"> <li class="nav-link">
<a class="nav-link text-body text-decoration-none fw-light" href="/pool"><i class="fa-solid fa-hard-drive"></i> Pools</a> <a class="nav-link text-body text-decoration-none fw-light" href="/pool"><i class="fa-solid fa-hard-drive"></i> Pools</a>
</li> </li>
<li class="nav-link">
<a class="nav-link text-body text-decoration-none fw-light" href="/network"><i class="fa-solid fa-network-wired"></i> Network</a>
</li>
</ul> </ul>
</div> </div>

55
templates/menu.html.ok Normal file
View File

@@ -0,0 +1,55 @@
<div class="side-navbar active-nav d-flex justify-content-between flex-wrap flex-column" id="sidebar">
<ul class="nav flex-column text-dark w-100 ">
<li class="nav-link">
<h1 class="display-6">Hype²</h1>
</li>
<hr class="text-body">
<li class="nav-link">
<a class="nav-link text-body text-decoration-none fw-light" href="/"><i class="fa fa-pie-chart"></i> Dashboard</a>
</li>
<li class="nav-link">
<a class="nav-link text-body text-decoration-none fw-light" href="/state"><i class="fa fa-cubes"></i> State</a>
</li>
<li class="nav-link">
<a class="nav-link text-body text-decoration-none fw-light" href="/build"><i class="fa fa-cogs"></i> Build</a>
</li>
<li class="nav-link">
<a class="nav-link text-body text-decoration-none fw-light" href="/backup"><i class="fa fa-archive"></i> Backup</a>
</li>
<li class="nav-link">
<a class="nav-link text-body text-decoration-none fw-light" href="/iso"><i class="fa-solid fa-compact-disc"></i> ISO</a>
</li>
<li class="nav-link">
<a class="nav-link text-body text-decoration-none fw-light" href="/pool"><i class="fa-solid fa-hard-drive"></i> Pools</a>
</li>
<li class="nav-link">
<a class="nav-link text-body text-decoration-none fw-light" href="/network"><i class="fa-solid fa-network-wired"></i> Network</a>
</li>
</ul>
</div>
<div class="p-1 my-container active-cont">
<nav class="navbar top-navbar px-5">
<a class="btn border-0 text-body" id="menu-btn"><i class="fa fa-bars"></i></a>
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-user"></i> {{ current_user.username }}
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownMenuLink">
<li><a class="dropdown-item" href="/logout"><i class="fa fa-sign-out"></i> Logout</a></li>
<li><a class="dropdown-item" href="/signup"><i class="fa fa-user-plus"></i> Add user</a></li>
<li><a class="dropdown-item" href="/param"><i class="fa fa-gear"></i> Parameters</a></li>
<li><a class="dropdown-item" style="cursor:pointer" id="btnSwitch"><i class="fa-solid fa-circle-half-stroke"></i> Theme</a></li>
</ul>
</nav>
</br>
<!-- Safe pour que ce soit au format html, sinon, prit comme texte -->
<div class="container">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div id="alert" class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
</br>

95
templates/network.html Normal file
View File

@@ -0,0 +1,95 @@
{% extends 'layout.html' %}
{% block content %}
{% include 'menu.html' %}
<center>
<a href="/createnet" class="btn btn-outline-danger"><i class="fa-solid fa-plus"></i> Add Network</a></br></br></br>
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
Physical interface
</div>
<div class="card-body">
{% for pint in net_hard_int %}
<i class="fa-solid fa-ethernet"></i> {{ pint }}<br>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
<center>
<br>
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
Virtual interface
</div>
<div class="card-body">
{% for noac in net_na %}
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<i class="fa-solid fa-ethernet"></i> {{ noac }} Not active
</div>
<div class="card-body">
</div></div></div></div></div>
<br><br>
{% endfor %}
{% for item in net_tree %}
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<table class="table table-sm table-borderless"><tr><td>
<i class="fa-solid fa-network-wired"></i> {{ item[0][0][0] }} - <i class="fa-solid fa-ethernet"></i> {{ item[0][0][1] }} - {{ item[0][0][2] }}
</td><td>
<form action="/delnet" method="post">
<input type="hidden" id="net_del" name="net_del" class="form-control" value="{{ item[0][0][0] }}">
<button name="delete" class="btn btn-outline-danger btn-sm" type="submit" onclick="loading();"><i class="fa fa-trash" aria-hidden="true"></i></button>
</form>
</td></tr>
</table>
</div>
<div class="card-body">
{% for sub_item in item[1] %}
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<i class="fa-solid fa-desktop"></i> {{ sub_item[0] }}<br>
</div>
<div class="card-body">
{% for pitem in sub_item[1] %}
<table class="table table-sm table-responsive table-borderless">
<tr><td>
<i class="fa-solid fa-ethernet"></i> {{ pitem[0] }} - <i class="fa-solid fa-at"></i> {{ pitem[1] }} - {{ pitem[2] }}<br>
</td><td>
<form action="/delnetvm" method="post">
<input type="hidden" id="vm_name" name="vm_name" class="form-control" value="{{ sub_item[0] }}">
<input type="hidden" id="del_net_vm" name="del_net_vm" class="form-control" value="{{ item[0][0][0] }}">
<input type="hidden" id="del_mac_vm" name="del_mac_vm" class="form-control" value="{{ pitem[1] }}">
<input type="hidden" id="del_net_int" name="del_net_int" class="form-control" value="{{ item[0][0][1] }}">
<button name="delete" class="btn btn-outline-danger btn-sm" type="submit" onclick="loading();"><i class="fa fa-trash" aria-hidden="true"></i></button>
</form>
</td></tr>
</table>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div></div></div></div></div>
{% endfor %}
{% endblock %}

View File

@@ -19,6 +19,9 @@
<form action="/destroy_lxc" method="post"><button type="submit" class="btn btn-outline-danger btn-hype disabled" value="{{ act_lxc[0] }}" name="destroy" onclick="loading();"><i class="fa fa-trash" aria-hidden="true"></i></button></form> <form action="/destroy_lxc" method="post"><button type="submit" class="btn btn-outline-danger btn-hype disabled" value="{{ act_lxc[0] }}" name="destroy" onclick="loading();"><i class="fa fa-trash" aria-hidden="true"></i></button></form>
</td><td> </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> <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>
<form action="/editlxc" method="post"><button type="submit" class="btn btn-outline-warning btn-hype" value="{{ act_lxc[0] }}" name="edit" onclick="loading();"><i class="fa-solid fa-wrench"></i></button></form>
</td><td> </td><td>
</td><td> </td><td>
{{act_lxc[1]}} {{act_lxc[1]}}
@@ -41,8 +44,9 @@
</td><td> </td><td>
<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> <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>
<form action="/editlxc" method="post"><button type="submit" class="btn btn-outline-warning btn-hype" value="{{ inact_lxc }}" name="edit" onclick="loading();"><i class="fa-solid fa-wrench"></i></button></form>
</td><td> </td><td>
- </td><td> -
</td></tr> </td></tr>
{% endfor %} {% endfor %}
<!-- Activ VM --> <!-- Activ VM -->
@@ -62,6 +66,10 @@
</td><td> </td><td>
<form action="/snapvm" method="post"><button type="submit" class="btn btn-outline-secondary btn-hype disabled" value="{{ act_vm[0] }}" name="snap" onclick="loading();"><i class="fa-solid fa-box-archive"></i></button></form> <form action="/snapvm" method="post"><button type="submit" class="btn btn-outline-secondary btn-hype disabled" value="{{ act_vm[0] }}" name="snap" onclick="loading();"><i class="fa-solid fa-box-archive"></i></button></form>
</td><td> </td><td>
<form action="/editvm" method="post"><button type="submit" class="btn btn-outline-warning btn-hype" value="{{ act_vm[0] }}" name="edit" onclick="loading();"><i class="fa-solid fa-wrench"></i></button></form>
</td><td>
{% if act_vm[1] == 1 %} {% if act_vm[1] == 1 %}
<table class="table table-borderless"><tr><th> <table class="table table-borderless"><tr><th>
<i class="fa-solid fa-compact-disc"></i> <i class="fa-solid fa-compact-disc"></i>
@@ -93,7 +101,9 @@
<form action="/destroy_vm" method="post"><button type="submit" class="btn btn-outline-danger btn-hype" value="{{ inact_vm[0] }}" name="destroy" onclick="loading();"><i class="fa fa-trash" aria-hidden="true"></i></button></form> <form action="/destroy_vm" method="post"><button type="submit" class="btn btn-outline-danger btn-hype" value="{{ inact_vm[0] }}" name="destroy" onclick="loading();"><i class="fa fa-trash" aria-hidden="true"></i></button></form>
</td><td> </td><td>
<form action="/snapvm" method="post"><button type="submit" class="btn btn-outline-secondary btn-hype" value="{{ inact_vm[0] }}" name="snap" onclick="loading();"><i class="fa-solid fa-box-archive"></i></button></form> <form action="/snapvm" method="post"><button type="submit" class="btn btn-outline-secondary btn-hype" value="{{ inact_vm[0] }}" name="snap" onclick="loading();"><i class="fa-solid fa-box-archive"></i></button></form>
</td><td>
<form action="/editvm" method="post"><button type="submit" class="btn btn-outline-warning btn-hype" value="{{ inact_vm[0] }}" name="edit" onclick="loading();"><i class="fa-solid fa-wrench"></i></button></form>
<!-- <form action="/editvm" method="post"><button type="submit" class="btn btn-outline-warning btn-hype disabled" value="{{ inact_vm[0] }}" name="edit" onclick="loading();"><i class="fa-solid fa-wrench"></i></button></form> -->
</td><td> </td><td>
{% if inact_vm[1] == 1 %} {% if inact_vm[1] == 1 %}
<table class="table table-borderless"><tr><th> <table class="table table-borderless"><tr><th>

View File

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

View File

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