Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27c3ae466f | ||
|
|
20109ff6bb | ||
|
|
eea249eabb | ||
|
|
cc44a3df4d | ||
|
|
58a04d51b3 | ||
|
|
e2fcc56ce0 | ||
|
|
843555a9d5 | ||
|
|
a3b7ef628e | ||
|
|
cf6fe76352 | ||
|
|
a2683789d1 | ||
|
|
93f2a5379f | ||
|
|
2f113ad01b | ||
|
|
b6ece56080 | ||
|
|
e6d087f148 | ||
|
|
8f39dc4768 | ||
|
|
617a6481e9 | ||
|
|
1be2e77d8a | ||
|
|
a7906586a7 | ||
|
|
ca71c554b4 |
113
DOCUMENTATION.md
Normal file
113
DOCUMENTATION.md
Normal 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()
|
||||||
74
README.md
74
README.md
@@ -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,21 +15,26 @@ Server[Server] <----> LXC((LXC))
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Install requirements
|
## Install requirements (Debian 12 example)
|
||||||
|
For your safety you can also find the packages on pip or pipx and the requirement.txt file.
|
||||||
|
|
||||||
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-openssl
|
||||||
|
|
||||||
|
apt-get install openvswitch-switch openvswitch-common
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Clone the repository :
|
Clone the repository :
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://git.nerkdesign.com/pporcheret/Hype-2.git
|
git clone https://github.com/pyhype2/Hype2.git
|
||||||
cd Hype-2
|
cd Hype2
|
||||||
|
mkdir storage/disks
|
||||||
```
|
```
|
||||||
|
|
||||||
2 - Configure Libvirt to start on boot
|
2 - Configure Libvirt to start on boot
|
||||||
@@ -46,6 +54,33 @@ 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
|
||||||
|
```
|
||||||
|
4 - Modify Qemu configuration
|
||||||
|
|
||||||
|
You will have to uncomment these lines :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
vnc_listen = "0.0.0.0"
|
||||||
|
user=root
|
||||||
|
group=root
|
||||||
|
```
|
||||||
|
|
||||||
|
This will ajust rights for Qemu to run as root and enable VNC on all address for the console.
|
||||||
|
|
||||||
## Database for users
|
## Database for users
|
||||||
|
|
||||||
@@ -57,10 +92,11 @@ 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 :
|
||||||
|
|
||||||
### Create Database for users
|
### Create Database for users (Manual creation)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sqlite3 db.db
|
sqlite3 db.db
|
||||||
@@ -167,12 +203,14 @@ 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)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NB: The first LXC creation will take time to download files. Be patient
|
||||||
|
|
||||||
## Systemd
|
## Systemd
|
||||||
|
|
||||||
You can manage hype using Systemd management by creating a file *hype.service* :
|
You can manage hype using Systemd management by creating a file *hype.service* :
|
||||||
@@ -200,9 +238,27 @@ 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.
|
||||||
|
|
||||||
|
|
||||||
|
## Windows requirement (Virtuio)
|
||||||
|
If you plan to install some Windows Virtual Machine, you will need to download the Virtuo (Windows drivers IO) and place the ISO in the *<hype_path>/storage/win* folder.
|
||||||
|
|
||||||
|
https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.229-1/
|
||||||
|
|
||||||
|
|
||||||
|
You will also need to configure the ISO name in the *configuration.py* file.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
virtuo_path= path+'/storage/win/'
|
||||||
|
virtuo_file='virtio-win-0.1.229.iso'
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
NB: Depending on the Windows version you need, you will need to use a specific Virtio version.
|
||||||
|
Lastest version will remove the old Windows version driver.
|
||||||
|
|
||||||
|
|
||||||
## 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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
__pycache__/app.cpython-311.pyc
Normal file
BIN
__pycache__/app.cpython-311.pyc
Normal file
Binary file not shown.
BIN
__pycache__/config.cpython-311.pyc
Normal file
BIN
__pycache__/config.cpython-311.pyc
Normal file
Binary file not shown.
@@ -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
functions/__pycache__/fbackup.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fbackup.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/fdisks.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fdisks.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/fedit.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fedit.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/fhost.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fhost.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/fiso.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fiso.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/flxc.cpython-311.pyc
Normal file
BIN
functions/__pycache__/flxc.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/fnet.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fnet.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/fpool.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fpool.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/fscreen.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fscreen.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/fvm.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fvm.cpython-311.pyc
Normal file
Binary file not shown.
BIN
functions/__pycache__/fvnc.cpython-311.pyc
Normal file
BIN
functions/__pycache__/fvnc.cpython-311.pyc
Normal file
Binary file not shown.
@@ -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
115
functions/fdisks.py
Normal 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()
|
||||||
|
|
||||||
125
functions/fedit.py
Normal file
125
functions/fedit.py
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
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 get_vm_infos(vm_name):
|
||||||
|
dom = conn.lookupByName(vm_name)
|
||||||
|
vm_info = dom.guestInfo()
|
||||||
|
vm_infos=[]
|
||||||
|
vm_infos=[vm_info['os.id'],vm_info['os.pretty-name'],vm_info['os.kernel-release']]
|
||||||
|
return vm_infos
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
@@ -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
125
functions/fnet.py
Normal 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 out != 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
|
||||||
@@ -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
20
functions/fscreen.py
Normal 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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from config import *
|
from config import *
|
||||||
from xml.etree import ElementTree as ET
|
from xml.etree import ElementTree as ET
|
||||||
from flask import request, Response
|
from flask import request, Response
|
||||||
|
import subprocess
|
||||||
import os
|
import os
|
||||||
|
|
||||||
def get_vnc_port(vm_name):
|
def get_vnc_port(vm_name):
|
||||||
@@ -12,14 +13,15 @@ def get_vnc_port(vm_name):
|
|||||||
return vnc_port
|
return vnc_port
|
||||||
|
|
||||||
def kill_consoles():
|
def kill_consoles():
|
||||||
os.system('kill -9 $(ps -edf | grep websockify | grep -v grep | awk \'{ print $2 }\')')
|
subprocess.run("pkill -9 -f 'websockify'", shell=True)
|
||||||
os.system('for i in $(ps -edf | grep pyxterm | grep -v grep | awk \'{ print $2 }\');do kill -9 $i;done')
|
subprocess.run("for i in $(pgrep -f 'pyxterm'); do kill -9 $i; done", shell=True)
|
||||||
|
|
||||||
def socket_connect(vm_name):
|
def socket_connect(vm_name):
|
||||||
kill_consoles()
|
kill_consoles()
|
||||||
vm_port = get_vnc_port(vm_name)
|
vm_port = get_vnc_port(vm_name)
|
||||||
os.system('websockify -D --web=/usr/share/novnc/ 6080 localhost:'+vm_port)
|
subprocess.run(['websockify', '-D', '--web=/usr/share/novnc/', '6080', 'localhost:' + vm_port])
|
||||||
|
|
||||||
def pyxterm_connect(path,lxc_name):
|
def pyxterm_connect(path,lxc_name):
|
||||||
kill_consoles()
|
kill_consoles()
|
||||||
os.system('python3 '+path+'/pyxterm.py --command \'lxc-attach\' --cmd-args \''+lxc_name+'\' &')
|
cmd = ['python3', f'{path}/pyxterm.py', '--command', 'lxc-attach', '--cmd-args', lxc_name]
|
||||||
|
pyx_process = subprocess.Popen(cmd)
|
||||||
|
|||||||
30
install.sh
Normal file
30
install.sh
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Hype² installation script
|
||||||
|
# Tested on Debian 11 and Debian 12
|
||||||
|
#
|
||||||
|
# 1.0 by Pierre Porcheret
|
||||||
|
#
|
||||||
|
#
|
||||||
|
clear
|
||||||
|
echo "Requirements packages"
|
||||||
|
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 python3-openssl
|
||||||
|
echo "Openswitch install"
|
||||||
|
apt-get install openvswitch-switch openvswitch-common
|
||||||
|
echo "Python libs install"
|
||||||
|
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
|
||||||
|
echo "Enabling Libvirt"
|
||||||
|
systemctl --quiet enable --now libvirtd
|
||||||
|
systemctl --quiet start libvirtd
|
||||||
|
echo "Bridged interface installation"
|
||||||
|
cp ./bridged.xml /usr/share/libvirt/networks/
|
||||||
|
virsh net-define bridged.xml
|
||||||
|
virsh net-start bridged
|
||||||
|
virsh net-autostart bridged
|
||||||
|
echo "Copy default database"
|
||||||
|
cp db.db.admin_example db.db
|
||||||
|
clear
|
||||||
|
echo "Installation Done"
|
||||||
|
echo "Please follow the Qemu modification according to README.md"
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
flask
|
||||||
|
flask_sqlalchemy
|
||||||
|
flask_login
|
||||||
|
pyopenssl
|
||||||
BIN
storage/win/virtio-win-0.1.229.iso
Normal file
BIN
storage/win/virtio-win-0.1.229.iso
Normal file
Binary file not shown.
@@ -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>
|
||||||
|
|||||||
@@ -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
36
templates/createnet.html
Normal 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
139
templates/edit.html
Normal 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
36
templates/edit_lxc.html
Normal 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 %}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
55
templates/menu.html.ok
Normal 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
95
templates/network.html
Normal 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 %}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
Reference in New Issue
Block a user