Commit ee069050 authored by Samarth Joshi's avatar Samarth Joshi

Initial commit

parents
export FLASK_APP = monitor.py
monitor: monitor.py
flask run
client: client.py
python3 client.py
import requests
import threading
started = False
ips = [ "http://192.168.122.100" ]
threads = []
def send_request(ip):
while started:
r = requests.get(ip)
def start(n):
global started
started = True
for i in range(n):
for ip in ips:
x = threading.Thread(target=send_request, args=(ip,), daemon=True)
threads.append(x)
x.start()
def stop():
global threads
global started
started = False
for t in threads:
t.join()
threads = []
if __name__ == "__main__":
while True:
action = input("client> ")
action = action.split(" ")
cmd = action[0]
if cmd.lower() == "start":
if started:
print("Already started! Restarting...")
stop()
n = 1
if len(action)>1:
if action[1] == "high":
n = 5
elif action[1] == "low":
n = 1
else:
n = int(action[1])
print(f"Starting load generation with {n} threads")
start(n)
elif cmd.lower() == "stop":
stop()
elif cmd.lower() == "exit":
break
from __future__ import print_function
import sys
import libvirt
import threading
import time
from flask import Flask
app = Flask(__name__)
from flask import render_template
started = False
threads = []
usage_lock = threading.Lock()
usage_count = dict()
total_window = 25
unread = list()
read = list()
def master():
threshold = 90.0 #percent
window = 8
global usage_lock
global usage_count
assert window <= total_window
while True:
with usage_lock:
u = usage_count.copy()
for key in u:
if key == "loadbalancer":
continue
avg = 0.0
for i in range(-1,-1-window,-1):
avg += u[key][i]
avg /= window
if avg > threshold:
conn = libvirt.open('qemu:///system')
dom = conn.lookupByName("server2")
state, reason = dom.state()
if state == libvirt.VIR_DOMAIN_RUNNING:
continue
unread.append("Overload Detected! Spawning new VM.")
if dom.create() < 0:
print('Can not boot guest domain.', file=sys.stderr)
exit(1)
conn.close()
time.sleep(60)
def calculate_usage(dom):
global usage_count
old_time = 0
delay = 1 # seconds
while started:
state, reason = dom.state()
if state != libvirt.VIR_DOMAIN_RUNNING:
if dom.name() not in usage_count:
usage_count[dom.name()] = [0] * total_window
else:
usage_count[dom.name()].pop(0)
usage_count[dom.name()].append(0)
time.sleep(delay)
continue
stats = dom.getCPUStats(True)
new_time = stats[0]['cpu_time']
if old_time>0:
usage = 100 * (new_time - old_time) / (delay * 1000 * 1000 * 1000)
usage = round(usage, 2)
with usage_lock:
if dom.name() not in usage_count:
usage_count[dom.name()] = [0] * total_window
else:
usage_count[dom.name()].pop(0)
usage_count[dom.name()].append(usage)
old_time = new_time
time.sleep(delay)
@app.route('/status')
def getStatus():
global usage_count
return usage_count
def start_monitoring():
global started
if started:
return
started = True
conn = libvirt.open('qemu:///system')
if conn == None:
print('Failed to open connection to qemu:///system', file=sys.stderr)
exit(1)
domains = conn.listAllDomains(0)
if len(domains) != 0:
for domain in domains:
print(f"Monitoring {domain.name()}")
x = threading.Thread(target=calculate_usage, args=(domain,), daemon=True)
x.start()
threads.append(x)
y = threading.Thread(target=master, daemon=True)
y.start()
else:
print(' None')
conn.close()
@app.route('/notifications')
def get_notifications():
global unread
global read
return_obj = { "unread":unread.copy(), "read":read.copy() }
while unread:
read.append(unread.pop())
return return_obj
@app.route('/stop_monitoring')
def stop_monitoring():
global started
started = False
for t in threads:
t.join()
@app.route('/')
def hello_world():
start_monitoring()
return render_template('index.html')
This diff is collapsed.
<!DOCTYPE html>
<html>
<head>
<title>VM Management</title>
<style>
body {
margin: 0px;
}
#graphs {
display: flex;
flex-direction: column;
width: 800px;
margin: auto;
padding: 20px;
}
.container {
width: 800px;
margin: auto;
display: flex;
justify-content: space-between;
}
.server {
display: flex;
flex-direction: row;
width: 100%;
height: 170px;
border: solid 1px #eee;
margin-bottom: 30px;
box-shadow: #eee 5px 5px 15px;
border-bottom-left-radius: 85px;
border-top-left-radius: 85px;
border-bottom-right-radius: 25px;
border-top-right-radius: 25px;
}
.unit {
font-size: 1.2em;
}
.speedometer {
display: flex;
flex-direction: column;
align-items:center;
justify-content:center;
width: 170px;
height: 170px;
box-shadow: #eee 2px 0px 10px;
border-radius: 50%;
}
.linechart {
display: flex;
flex-direction: row;
width: 550px;
height: 130px;
margin: 20px;
}
.bar {
width: 20px;
background: #00BCD4;
position: relative;
border-radius: 6px;
margin-left: 4px;
box-shadow: #eee 1px 1px 5px;
}
nav {
height: 50px;
line-height: 50px;
border-bottom: solid 1px #ccc;
}
.nav_btn {
cursor: pointer;
display: flex;
justify-content:center;
align-items:center;
width: 50px;
height: 50px;
text-align: center;
transition: background-color 0.2s ease;
background-color: #eee;
color:white;
}
.nav_btn:hover {
background-color: #00EDF8;
color:black;
}
#notification_panel {
display: none;
position: fixed;
top: 100px;
left: calc(50vw - 200px);
width: 400px;
height: 400px;
background: white;
border: solid 1px #aaa;
border-radius: 10px;
z-index:1000;
box-shadow: #aaa 2px 10px 20px;
padding: 0px;
overflow:hidden;
}
#notifications {
padding: 0px;
margin-top: 0px;
}
#notifications > li {
padding: 20px;
font-size: 1.2em;
list-style: none;
border-bottom: solid 1px #aaa;
}
#dimscreen {
display: none;
position: fixed;
top:0;
left:0;
width: 100vw;
height: 100vh;
opacity:0.2;
background-color:black;
}
.noti_header {
border-bottom: solid 2px #303F9F;
background-color: #3F51B5;
color:white;
display: flex;
justify-content: space-between;
}
.noti_header > h4 {
padding-left: 20px;
font-size: 1.5em;
margin-top: 10px;
margin-bottom: 10px;
}
.noti_header > button {
background-color: #303F9F;
color: white;
font-size: 1.5em;
border: none;
text-align:center;
width: 60px;
cursor: pointer;
}
</style>
</head>
<body>
<nav>
<div class="container">
<div>CS 695</div>
<a class="nav_btn" onclick="show_noti()"><img height="24px" src="{{url_for('static', filename='images/notify.png')}}"></a>
</div>
</nav>
<section id="graphs">
</section>
<div id="dimscreen"></div>
<section id="notification_panel">
<div class="noti_header">
<h4>Notifications: </h4>
<button onclick="close_noti()">X</button>
</div>
<ul id="notifications">
</ul>
</section>
<script src="{{url_for('static', filename='js/jquery-3.6.0.min.js')}}"></script>
<script type="text/javascript">
function get_status(){
$.ajax({
url: "status",
type: "GET",
success: function(data){
$("#graphs").empty();
jQuery.each(data, function(name, val) {
const server = document.createElement("div");
const speedometer = document.createElement("div");
const speed = document.createElement("div");
var metric = val[val.length-1]
if (val[val.length-1]>=100) {
metric = 100
} else {
metric = val[val.length-1];
}
const usage = document.createTextNode(metric+"%");
speed.appendChild(usage);
speed.setAttribute("class", "unit")
const servername = document.createElement("div");
const servernametext = document.createTextNode(name);
servername.appendChild(servernametext);
speedometer.appendChild(speed);
speedometer.appendChild(servername);
speedometer.setAttribute("class", "speedometer")
if (val[val.length-1]==0) {
speedometer.setAttribute("style", "background-image: url('{{url_for('static', filename='images/speeds/')}}z.png')");
} else if (val[val.length-1]>=100) {
speedometer.setAttribute("style", "background-image: url('{{url_for('static', filename='images/speeds/')}}100.png')");
} else {
speedometer.setAttribute("style", "background-image: url('{{url_for('static', filename='images/speeds/')}}"+parseInt(val[val.length-1]/10)+"0.png')");
}
const linechart = document.createElement("div");
linechart.setAttribute("class", "linechart")
for(var i=0; i<val.length; i++) {
const bar = document.createElement("div");
bar.setAttribute("class", "bar");
if(val[i]>=100) {
bar.setAttribute("style", "height:100%; position:relative");
} else {
bar.setAttribute("style", "height:"+val[i]+"%; position:relative; top:"+(100-val[i])+"%;");
}
linechart.appendChild(bar);
}
//server.appendChild(servername);
server.appendChild(speedometer);
server.appendChild(linechart);
server.setAttribute("class", "server")
$("#graphs").append(server);
});
}
});
}
function check_notifications () {
$.ajax({
url: "notifications",
type: "GET",
success: function(data){
for(var i=0; i<data.unread.length; i++) {
var ul = document.getElementById("notifications");
const list_item = document.createElement("li");
const list_item_text = document.createTextNode(data.unread[i]);
list_item.appendChild(list_item_text);
ul.appendChild(list_item);
alert(data.unread[i]);
}
}
});
}
function show_noti() {
document.getElementById("notification_panel").style = "display:block;";
document.getElementById("dimscreen").style = "display:block;";
}
function close_noti() {
document.getElementById("notification_panel").style = "display:none;";
document.getElementById("dimscreen").style = "display:none;";
}
const interval = setInterval(function() {
get_status()
}, 1000);
const interval2 = setInterval(function() {
check_notifications()
}, 2000);
</script>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment