mirror of
https://github.com/ComputerScienceHouse/proxstar.git
synced 2025-02-14 22:11:51 +00:00
improved some ui stuff, added basic usage limit checking, and added ability to change vm status
This commit is contained in:
parent
6d494a59a5
commit
071e841cb3
5 changed files with 148 additions and 40 deletions
43
app.py
43
app.py
|
@ -31,10 +31,6 @@ def list_vms():
|
||||||
for vm in vms:
|
for vm in vms:
|
||||||
if 'name' not in vm:
|
if 'name' not in vm:
|
||||||
vms.remove(vm)
|
vms.remove(vm)
|
||||||
else:
|
|
||||||
vm['config'] = get_vm_config(proxmox, vm['vmid'])
|
|
||||||
vm['disk_size'] = get_vm_disk_size(
|
|
||||||
proxmox, vm['vmid'], config=vm['config'])
|
|
||||||
vms = sorted(vms, key=lambda k: k['name'])
|
vms = sorted(vms, key=lambda k: k['name'])
|
||||||
return render_template('list_vms.html', username='com6056', vms=vms)
|
return render_template('list_vms.html', username='com6056', vms=vms)
|
||||||
|
|
||||||
|
@ -50,23 +46,34 @@ def vm_details(vmid):
|
||||||
return render_template('vm_details.html', username='com6056', vm=vm)
|
return render_template('vm_details.html', username='com6056', vm=vm)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/create")
|
@app.route("/vm_status/<string:vmid>", methods=['POST'])
|
||||||
def create():
|
def vm_status(vmid):
|
||||||
return render_template('create.html', username='com6056')
|
action = request.form['action']
|
||||||
|
change_vm_status(proxmox, vmid, action)
|
||||||
|
|
||||||
@app.route("/get_create", methods=['POST'])
|
|
||||||
def get_create():
|
|
||||||
name = request.form['name']
|
|
||||||
cores = request.form['cores']
|
|
||||||
memory = request.form['memory']
|
|
||||||
disk = request.form['disk']
|
|
||||||
vmid, mac = create_vm(proxmox, starrs, user, name, cores, memory, disk)
|
|
||||||
register_starrs(starrs, name, user, mac,
|
|
||||||
get_next_ip(starrs, '49net Public Fixed')[0][0])
|
|
||||||
return redirect("/proxstar/vm/{}".format(vmid))
|
return redirect("/proxstar/vm/{}".format(vmid))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/create", methods=['GET', 'POST'])
|
||||||
|
def create():
|
||||||
|
if request.method == 'GET':
|
||||||
|
usage = get_user_usage(proxmox, 'proxstar')
|
||||||
|
limits = get_user_usage_limits(user)
|
||||||
|
return render_template('create.html', username='com6056', usage=usage, limits=limits)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
name = request.form['name']
|
||||||
|
cores = request.form['cores']
|
||||||
|
memory = request.form['memory']
|
||||||
|
disk = request.form['disk']
|
||||||
|
usage_check = check_user_usage(proxmox, user, cores, memory, disk)
|
||||||
|
if usage_check:
|
||||||
|
return usage_check
|
||||||
|
else:
|
||||||
|
vmid, mac = create_vm(proxmox, starrs, user, name, cores, memory, disk)
|
||||||
|
register_starrs(starrs, name, user, mac,
|
||||||
|
get_next_ip(starrs, '49net Public Fixed')[0][0])
|
||||||
|
return redirect("/proxstar/vm/{}".format(vmid))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/delete", methods=['POST'])
|
@app.route("/delete", methods=['POST'])
|
||||||
def delete():
|
def delete():
|
||||||
vmid = request.form['delete']
|
vmid = request.form['delete']
|
||||||
|
|
67
proxmox.py
67
proxmox.py
|
@ -66,6 +66,7 @@ def get_vm_interfaces(proxmox, vmid, config=None):
|
||||||
else:
|
else:
|
||||||
mac = mac[1].split('=')[1]
|
mac = mac[1].split('=')[1]
|
||||||
interfaces.append([key, mac])
|
interfaces.append([key, mac])
|
||||||
|
interfaces = sorted(interfaces, key=lambda x: x[0])
|
||||||
return interfaces
|
return interfaces
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,15 +88,51 @@ def get_vm_disks(proxmox, vmid, config=None):
|
||||||
for key, val in config.items():
|
for key, val in config.items():
|
||||||
valid_disk_types = ['virtio', 'ide', 'sata', 'scsi']
|
valid_disk_types = ['virtio', 'ide', 'sata', 'scsi']
|
||||||
if any(disk_type in key for disk_type in valid_disk_types):
|
if any(disk_type in key for disk_type in valid_disk_types):
|
||||||
disk_size = val.split(',')
|
if 'cdrom' not in val:
|
||||||
if 'size' in disk_size[0]:
|
disk_size = val.split(',')
|
||||||
disk_size = disk_size[0].split('=')[1]
|
if 'size' in disk_size[0]:
|
||||||
else:
|
disk_size = disk_size[0].split('=')[1]
|
||||||
disk_size = disk_size[1].split('=')[1]
|
else:
|
||||||
disks.append([key, disk_size])
|
disk_size = disk_size[1].split('=')[1]
|
||||||
|
disks.append([key, disk_size])
|
||||||
|
disks = sorted(disks, key=lambda x: x[0])
|
||||||
return disks
|
return disks
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_usage_limits(user):
|
||||||
|
limits = dict()
|
||||||
|
limits['cpu'] = 4
|
||||||
|
limits['mem'] = 4
|
||||||
|
limits['disk'] = 100
|
||||||
|
return limits
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_usage(proxmox, user):
|
||||||
|
usage = dict()
|
||||||
|
usage['cpu'] = 0
|
||||||
|
usage['mem'] = 0
|
||||||
|
usage['disk'] = 0
|
||||||
|
vms = get_vms_for_user(proxmox, user)
|
||||||
|
for vm in vms:
|
||||||
|
config = get_vm_config(proxmox, vm['vmid'])
|
||||||
|
usage['cpu'] += int(config['cores'] * config.get('sockets', 1))
|
||||||
|
usage['mem'] += (int(config['memory']) // 1024)
|
||||||
|
for disk in get_vm_disks(proxmox, vm['vmid'], config):
|
||||||
|
usage['disk'] += int(disk[1][:-1])
|
||||||
|
return usage
|
||||||
|
|
||||||
|
|
||||||
|
def check_user_usage(proxmox, user, vm_cpu, vm_mem, vm_disk):
|
||||||
|
limits = get_user_usage_limits(user)
|
||||||
|
cur_usage = get_user_usage(proxmox, user)
|
||||||
|
if int(cur_usage['cpu']) + int(vm_cpu) > int(limits['cpu']):
|
||||||
|
return 'Exceeds CPU limit!'
|
||||||
|
elif int(cur_usage['mem']) + (int(vm_mem) / 1024) > int(limits['mem']):
|
||||||
|
return 'Exceeds memory limit!'
|
||||||
|
elif int(cur_usage['disk']) + int(vm_disk) > int(limits['disk']):
|
||||||
|
return 'Exceeds disk limit!'
|
||||||
|
|
||||||
|
|
||||||
def create_vm(proxmox, starrs, user, name, cores, memory, disk):
|
def create_vm(proxmox, starrs, user, name, cores, memory, disk):
|
||||||
node = proxmox.nodes(get_node_least_mem(proxmox))
|
node = proxmox.nodes(get_node_least_mem(proxmox))
|
||||||
vmid = get_free_vmid(proxmox)
|
vmid = get_free_vmid(proxmox)
|
||||||
|
@ -114,7 +151,21 @@ def create_vm(proxmox, starrs, user, name, cores, memory, disk):
|
||||||
|
|
||||||
|
|
||||||
def delete_vm(proxmox, starrs, vmid):
|
def delete_vm(proxmox, starrs, vmid):
|
||||||
print(vmid)
|
|
||||||
print(get_vm_node(proxmox, vmid))
|
|
||||||
node = proxmox.nodes(get_vm_node(proxmox, vmid))
|
node = proxmox.nodes(get_vm_node(proxmox, vmid))
|
||||||
node.qemu(vmid).delete()
|
node.qemu(vmid).delete()
|
||||||
|
|
||||||
|
|
||||||
|
def change_vm_status(proxmox, vmid, action):
|
||||||
|
node = proxmox.nodes(get_vm_node(proxmox, vmid))
|
||||||
|
if action == 'start':
|
||||||
|
node.qemu(vmid).status.start.post()
|
||||||
|
elif action == 'stop':
|
||||||
|
node.qemu(vmid).status.stop.post()
|
||||||
|
elif action == 'shutdown':
|
||||||
|
node.qemu(vmid).status.shutdown.post()
|
||||||
|
elif action == 'reset':
|
||||||
|
node.qemu(vmid).status.reset.post()
|
||||||
|
elif action == 'suspend':
|
||||||
|
node.qemu(vmid).status.suspend.post()
|
||||||
|
elif action == 'resume':
|
||||||
|
node.qemu(vmid).status.resume.post()
|
||||||
|
|
|
@ -16,7 +16,7 @@ table, th, td {
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-body {
|
.panel-body {
|
||||||
padding: 25px 15px 15px 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel p {
|
.panel p {
|
||||||
|
@ -43,4 +43,26 @@ table, th, td {
|
||||||
|
|
||||||
.dl-horizontal dd {
|
.dl-horizontal dd {
|
||||||
margin-left: 180px;
|
margin-left: 180px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-list {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-list .nav-header {
|
||||||
|
padding: 3px 15px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 18px;
|
||||||
|
color: #999999;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-left: -15px;
|
||||||
|
margin-right: -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proxstar-actionbtn {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<h3 class="panel-title">Create VM</h3>
|
<h3 class="panel-title">Create VM</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<form action="get_create" method="post">
|
<form action="create" method="post">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name">VM Name</label>
|
<label for="name">VM Name</label>
|
||||||
<input type="text" name="name" class="form-control">
|
<input type="text" name="name" class="form-control">
|
||||||
|
@ -17,24 +17,24 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="cores">Cores</label>
|
<label for="cores">Cores</label>
|
||||||
<select name="cores" class="form-control">
|
<select name="cores" class="form-control">
|
||||||
<option value="1">1</option>
|
{% for i in range(1, 4 - usage['cpu'] + 1) %}
|
||||||
<option value="2">2</option>
|
<option value="{{ i }}">{{ i }}</option>
|
||||||
<option value="4">4</option>
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="memory">Memory</label>
|
<label for="memory">Memory</label>
|
||||||
<select name="memory" class="form-control">
|
<select name="memory" class="form-control">
|
||||||
<option value="1024">1GB</option>
|
{% for i in range(1, 4 - usage['mem'] + 1) %}
|
||||||
<option value="2048">2GB</option>
|
<option value="{{ i * 1024 }}">{{ i }}GB</option>
|
||||||
<option value="4096">4GB</option>
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="disk">Disk</label>
|
<label for="disk">Disk</label>
|
||||||
<input type="text" name="disk" class="form-control">
|
<input type="text" name="disk" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<input type="submit">
|
<button class="btn btn-success" type="submit" value="Create">Create</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -42,10 +42,12 @@
|
||||||
<div class="col-md-3 col-sm-12">
|
<div class="col-md-3 col-sm-12">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">Help</h3>
|
<h3 class="panel-title">Current Usage</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<p>Some help text.</p>
|
<p>CPU: {{ usage['cpu'] }}/{{ limits['cpu'] }} Cores</p>
|
||||||
|
<p>Memory: {{ usage['mem'] }}/{{ limits['mem'] }} GB</p>
|
||||||
|
<p>Disk: {{ usage['disk'] }}/{{ limits['disk'] }} GB</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,8 +9,34 @@
|
||||||
<h3 class="panel-title">Actions</h3>
|
<h3 class="panel-title">Actions</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
{% if vm['qmpstatus'] == 'stopped' %}
|
||||||
|
<form action="/proxstar/vm_status/{{ vm['vmid'] }}" method="post">
|
||||||
|
<button class="btn btn-success proxstar-actionbtn" type="submit" name="action" value="start">START</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% if vm['qmpstatus'] == 'paused' %}
|
||||||
|
<form action="/proxstar/vm_status/{{ vm['vmid'] }}" method="post">
|
||||||
|
<button class="btn btn-success proxstar-actionbtn" type="submit" name="action" value="resume">RESUME</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% if vm['qmpstatus'] == 'running' or vm['qmpstatus'] == 'paused' %}
|
||||||
|
<form action="/proxstar/vm_status/{{ vm['vmid'] }}" method="post">
|
||||||
|
<button class="btn btn-info proxstar-actionbtn" type="submit" name="action" value="shutdown">SHUTDOWN</button>
|
||||||
|
</form>
|
||||||
|
{% if vm['qmpstatus'] == 'running' %}
|
||||||
|
<form action="/proxstar/vm_status/{{ vm['vmid'] }}" method="post">
|
||||||
|
<button class="btn btn-info proxstar-actionbtn" type="submit" name="action" value="suspend">SUSPEND</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
<form action="/proxstar/vm_status/{{ vm['vmid'] }}" method="post">
|
||||||
|
<button class="btn btn-warning proxstar-actionbtn" type="submit" name="action" value="stop">STOP</button>
|
||||||
|
</form>
|
||||||
|
<form action="/proxstar/vm_status/{{ vm['vmid'] }}" method="post">
|
||||||
|
<button class="btn btn-warning proxstar-actionbtn" type="submit" name="action" value="reset">RESET</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
<form action="/proxstar/delete" method="post">
|
<form action="/proxstar/delete" method="post">
|
||||||
<button class="btn btn-danger" type="submit" name="delete" value="{{ vm['vmid'] }}">DELETE</button>
|
<button class="btn btn-danger proxstar-actionbtn" type="submit" name="delete" value="{{ vm['vmid'] }}">DELETE</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,9 +72,9 @@
|
||||||
<dt>ID</dt>
|
<dt>ID</dt>
|
||||||
<dd>{{ vm['vmid'] }}</dd>
|
<dd>{{ vm['vmid'] }}</dd>
|
||||||
<dt>Status</dt>
|
<dt>Status</dt>
|
||||||
<dd>{{ vm['status'] }}</dd>
|
<dd>{{ vm['qmpstatus'] }}</dd>
|
||||||
<dt>Cores</dt>
|
<dt>Cores</dt>
|
||||||
<dd>{{ vm['config']['cores'] * vm['config'].get(sockets, 1) }}</dd>
|
<dd>{{ vm['config']['cores'] * vm['config'].get('sockets', 1) }}</dd>
|
||||||
<dt>Memory</dt>
|
<dt>Memory</dt>
|
||||||
<dd>{{ vm['config']['memory'] }} MB</dd>
|
<dd>{{ vm['config']['memory'] }} MB</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
Loading…
Reference in a new issue