Merge pull request #280 from FirmlyReality/networkbillings

Networkbillings
This commit is contained in:
Yujian Zhu 2017-12-05 17:54:19 +08:00 committed by GitHub
commit 3838a99c93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 230 additions and 13 deletions

View File

@ -520,6 +520,10 @@ def user_quotainfo_monitor(user, beans, form, issue):
logger.info("handle request: monitor/user/createdvnodes/") logger.info("handle request: monitor/user/createdvnodes/")
res = G_historymgr.getCreatedVNodes(user) res = G_historymgr.getCreatedVNodes(user)
return json.dumps({'success':'true', 'createdvnodes':res}) return json.dumps({'success':'true', 'createdvnodes':res})
elif issue == 'net_stats':
logger.info("handle request: monitor/user/net_stats/")
res = G_historymgr.get_user_net_stats(user)
return json.dumps({'success':'true', 'net_stats':res})
else: else:
return json.dumps({'success':'false', 'message':"Unspported Method!"}) return json.dumps({'success':'false', 'message':"Unspported Method!"})

View File

@ -74,6 +74,11 @@ workerinfo = {}
# has the second keys same as the third keys in monitor_vnodes. # has the second keys same as the third keys in monitor_vnodes.
workercinfo = {} workercinfo = {}
# store the network statistics of users' gateways on current Worker.
# key is username
# bytes_sent and bytes_recv are the second keys
gateways_stats = {}
# only use on worker # only use on worker
containerpids = [] containerpids = []
pid2name = {} pid2name = {}
@ -218,6 +223,7 @@ class Container_Collector(threading.Thread):
nowbillingval = workercinfo[vnode_name]['basic_info']['billing'] nowbillingval = workercinfo[vnode_name]['basic_info']['billing']
nowbillingval += billingval nowbillingval += billingval
workercinfo[vnode_name]['basic_info']['billing'] = nowbillingval workercinfo[vnode_name]['basic_info']['billing'] = nowbillingval
workercinfo[vnode_name]['basic_info']['billing_history'] = get_billing_history(vnode_name)
workercinfo[vnode_name]['basic_info']['billing_history']['cpu'] += billing['cpu'] workercinfo[vnode_name]['basic_info']['billing_history']['cpu'] += billing['cpu']
workercinfo[vnode_name]['basic_info']['billing_history']['mem'] += billing['mem'] workercinfo[vnode_name]['basic_info']['billing_history']['mem'] += billing['mem']
workercinfo[vnode_name]['basic_info']['billing_history']['disk'] += billing['disk'] workercinfo[vnode_name]['basic_info']['billing_history']['disk'] += billing['disk']
@ -266,6 +272,12 @@ class Container_Collector(threading.Thread):
self.net_stats[key]['errout'] = int(raw_stats[key].errin) self.net_stats[key]['errout'] = int(raw_stats[key].errin)
self.net_stats[key]['dropin'] = int(raw_stats[key].dropout) self.net_stats[key]['dropin'] = int(raw_stats[key].dropout)
self.net_stats[key]['dropout'] = int(raw_stats[key].dropin) self.net_stats[key]['dropout'] = int(raw_stats[key].dropin)
else:
if key not in gateways_stats.keys():
gateways_stats[key] = {}
gateways_stats[key]['bytes_recv'] = int(raw_stats[key].bytes_sent)
gateways_stats[key]['bytes_sent'] = int(raw_stats[key].bytes_recv)
gateways_stats[key]['bytes_total'] = gateways_stats[key]['bytes_recv'] + gateways_stats[key]['bytes_sent']
#logger.info(self.net_stats) #logger.info(self.net_stats)
# the main function to collect monitoring data of a container # the main function to collect monitoring data of a container
@ -565,10 +577,11 @@ class Collector(threading.Thread):
def workerFetchInfo(master_ip): def workerFetchInfo(master_ip):
global workerinfo global workerinfo
global workercinfo global workercinfo
global gateways_stats
global G_masterip global G_masterip
# tell the worker the ip address of the master # tell the worker the ip address of the master
G_masterip = master_ip G_masterip = master_ip
return str([workerinfo, workercinfo]) return str([workerinfo, workercinfo, gateways_stats])
# get owner name of a container # get owner name of a container
def get_owner(container_name): def get_owner(container_name):
@ -649,8 +662,27 @@ class Master_Collector(threading.Thread):
self.thread_stop = False self.thread_stop = False
self.nodemgr = nodemgr self.nodemgr = nodemgr
self.master_ip = master_ip self.master_ip = master_ip
self.net_lastbillings = {}
self.bytes_per_beans = 1000000000
return return
def net_billings(self, username, now_bytes_total):
global monitor_vnodes
if not username in self.net_lastbillings.keys():
self.net_lastbillings[username] = 0
elif int(now_bytes_total/self.bytes_per_beans) < self.net_lastbillings[username]:
self.net_lastbillings[username] = 0
diff = int(now_bytes_total/self.bytes_per_beans) - self.net_lastbillings[username]
if diff > 0:
auth_key = env.getenv('AUTH_KEY')
data = {"owner_name":username,"billing":diff, "auth_key":auth_key}
header = {'Content-Type':'application/x-www-form-urlencoded'}
http = Http()
[resp,content] = http.request("http://"+self.master_ip+"/billing/beans/","POST",urlencode(data),headers = header)
logger.info("response from master:"+content.decode('utf-8'))
self.net_lastbillings[username] += diff
monitor_vnodes[username]['net_stats']['net_billings'] = self.net_lastbillings[username]
def run(self): def run(self):
global monitor_hosts global monitor_hosts
global monitor_vnodes global monitor_vnodes
@ -672,6 +704,12 @@ class Master_Collector(threading.Thread):
if not owner in monitor_vnodes.keys(): if not owner in monitor_vnodes.keys():
monitor_vnodes[owner] = {} monitor_vnodes[owner] = {}
monitor_vnodes[owner][container] = info[1][container] monitor_vnodes[owner][container] = info[1][container]
for user in info[2].keys():
if not user in monitor_vnodes.keys():
continue
else:
monitor_vnodes[user]['net_stats'] = info[2][user]
self.net_billings(user, info[2][user]['bytes_total'])
except Exception as err: except Exception as err:
logger.warning(traceback.format_exc()) logger.warning(traceback.format_exc())
logger.warning(err) logger.warning(err)
@ -912,3 +950,14 @@ class History_Manager:
tmp = {"name":vnode.name,"billing":vnode.billing} tmp = {"name":vnode.name,"billing":vnode.billing}
res.append(tmp) res.append(tmp)
return res return res
# get users' net_stats
def get_user_net_stats(self,owner):
global monitor_vnodes
try:
res = monitor_vnodes[owner]['net_stats']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res

View File

@ -1,6 +1,10 @@
var mem_usedp = 0; var mem_usedp = 0;
var cpu_usedp = 0; var cpu_usedp = 0;
var is_running = true; var is_running = true;
var ingress_rate = 0;
var egress_rate = 0;
var ingress_rate_limit = 0;
var egress_rate_limit = 0;
function processMemData(data) function processMemData(data)
{ {
@ -50,7 +54,20 @@ function getCpuY()
return cpu_usedp*100; return cpu_usedp*100;
} }
function plot_graph(container,url,processData,getY) { function processRate(data)
{
}
function getIngressRateP()
{
//alert(ingress_rate*8 / 1000.0);
return ingress_rate * 8 / 1000.0;
}
function getEgressRateP()
{
return egress_rate * 8 / 1000.0;
}
function plot_graph(container,url,processData,getY,fetchdata=true, maxy=110) {
//var container = $("#flot-line-chart-moving"); //var container = $("#flot-line-chart-moving");
@ -88,9 +105,10 @@ function plot_graph(container,url,processData,getY) {
} }
if (data.length < maximum) { if (data.length < maximum) {
$.post(url,{user:"root",key:"root"},processData,"json"); if(fetchdata)
var y = getY(); $.post(url,{},processData,"json");
data.push(y < 0 ? 0 : y > 100 ? 100 : y); var y = getY();
data.push(y < 0 ? 0 : y > maxy ? maxy : y);
} }
// zip the generated y values with the x values // zip the generated y values with the x values
@ -152,7 +170,7 @@ function plot_graph(container,url,processData,getY) {
}, },
yaxis: { yaxis: {
min: 0, min: 0,
max: 110 max: maxy
}, },
legend: { legend: {
show: true show: true
@ -227,12 +245,14 @@ function processBasicInfo()
var secs = Math.floor(total % 3600 % 60); var secs = Math.floor(total % 3600 % 60);
$("#con_time").html(hour+"h "+min+"m "+secs+"s"); $("#con_time").html(hour+"h "+min+"m "+secs+"s");
$("#con_billing").html("<a target='_blank' title='How to figure out it?' href='https://unias.github.io/docklet/book/en/billing/billing.html'>"+basic_info.billing+" <img src='/static/img/bean.png' /></a>"); $("#con_billing").html("<a target='_blank' title='How to figure out it?' href='https://unias.github.io/docklet/book/en/billing/billing.html'>"+basic_info.billing+" <img src='/static/img/bean.png' /></a>");
$("#con_billingthishour").html("<a target='_blank' title='How to figure out it?' href='https://unias.github.io/docklet/book/en/billing/billing.html'>"+basic_info.billing_this_hour+" <img src='/static/img/bean.png' /></a>"); $("#con_billingthishour").html("<a target='_blank' title='How to figure out it?' href='https://unias.github.io/docklet/book/en/billing/billing.html'>"+basic_info.billing_this_hour.total+" <img src='/static/img/bean.png' /></a>");
},"json"); },"json");
$.post(url+"/net_stats/",{},function(data){ $.post(url+"/net_stats/",{},function(data){
var net_stats = data.monitor.net_stats; var net_stats = data.monitor.net_stats;
var in_rate = parseInt(net_stats.bytes_recv_per_sec); var in_rate = parseInt(net_stats.bytes_recv_per_sec);
var out_rate = parseInt(net_stats.bytes_sent_per_sec); var out_rate = parseInt(net_stats.bytes_sent_per_sec);
ingress_rate = in_rate;
egress_rate = out_rate;
$("#net_in_rate").html(num2human(in_rate)+"Bps"); $("#net_in_rate").html(num2human(in_rate)+"Bps");
$("#net_out_rate").html(num2human(out_rate)+"Bps"); $("#net_out_rate").html(num2human(out_rate)+"Bps");
$("#net_in_bytes").html(num2human(net_stats.bytes_recv)+"B"); $("#net_in_bytes").html(num2human(net_stats.bytes_recv)+"B");
@ -245,6 +265,20 @@ function processBasicInfo()
$("#net_out_drop").html(net_stats.dropin); $("#net_out_drop").html(net_stats.dropin);
},"json"); },"json");
} }
function plot_net(host,monitorurl)
{
var url = "http://" + host + "/user/selfQuery/";
$.post(url,{},function(data){
ingress_rate_limit = parseInt(data.groupinfo.input_rate_limit);
egress_rate_limit = parseInt(data.groupinfo.output_rate_limit);
plot_graph($("#ingress-chart"), monitorurl, processRate, getIngressRateP,false,ingress_rate_limit);
plot_graph($("#egress-chart"), monitorurl, processRate, getEgressRateP,false,egress_rate_limit*1.5);
},"json");
}
setInterval(processBasicInfo,1000); setInterval(processBasicInfo,1000);
plot_graph($("#mem-chart"),url + "/mem_use/",processMemData,getMemY); plot_graph($("#mem-chart"),url + "/mem_use/",processMemData,getMemY);
plot_graph($("#cpu-chart"),url + "/cpu_use/",processCpuData,getCpuY); plot_graph($("#cpu-chart"),url + "/cpu_use/",processCpuData,getCpuY);
plot_net(host, url + "/net_stats/");

View File

@ -73,6 +73,41 @@
</div> </div>
{% for master in allcontainers %} {% for master in allcontainers %}
<div class="row">
<div class="col-md-12">
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">Total Network Statistics @ {{master.split("@")[1]}}</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i>
</button>
<button type="button" class="btn btn-box-tool" data-widget="remove"><i class="fa fa-times"></i></button>
</div>
</div>
<div class="box-body table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th>Total Bytes Sent</th>
<th>Total Bytes Received</th>
<th>Total Bytes Transefer</th>
<th>Network Billings</th>
</tr>
</thead>
<tbody>
<tr>
<td id='{{master.split("@")[1]}}_bytes_sent'>--</td>
<td id='{{master.split("@")[1]}}_bytes_recv'>--</td>
<td id='{{master.split("@")[1]}}_bytes_total'>--</td>
<td id='{{master.split("@")[1]}}_net_billings'>--</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
{% for clustername, clusterinfo in allcontainers[master].items() %} {% for clustername, clusterinfo in allcontainers[master].items() %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
@ -243,6 +278,31 @@
<script src="http://cdn.bootcss.com/datatables-tabletools/2.1.5/js/TableTools.min.js"></script> <script src="http://cdn.bootcss.com/datatables-tabletools/2.1.5/js/TableTools.min.js"></script>
<script type='text/javascript'> <script type='text/javascript'>
function num2human(data)
{
units=['','K','M','G','T'];
tempdata = data/1.0;
//return tempdata;
for(var i = 1; i < units.length; ++i)
{
if( tempdata / 1000.0 > 1)
tempdata = tempdata/1000.0;
else
return tempdata.toFixed(2) + units[i-1];
}
return tempdata.toFixed(2) + units[4];
}
function update_net_stats(url,index)
{
$.post(url,{},function(data){
var bytes_sent = parseInt(data.net_stats.bytes_sent);
var bytes_recv = parseInt(data.net_stats.bytes_recv);
$("#"+index+"_bytes_sent").html(num2human(bytes_sent)+"B");
$("#"+index+"_bytes_recv").html(num2human(bytes_recv)+"B");
$("#"+index+"_bytes_total").html(num2human(bytes_sent+bytes_recv)+"B");
$("#"+index+"_net_billings").html(data.net_stats.net_billings);
},"json");
}
function update(url,index) function update(url,index)
{ {
@ -275,10 +335,20 @@
$("#"+index+"_billing_port").html(data.monitor.basic_info.billing_this_hour.port) $("#"+index+"_billing_port").html(data.monitor.basic_info.billing_this_hour.port)
$("#"+index+"_billing_port_use").html(data.monitor.basic_info.billing_this_hour.port_use) $("#"+index+"_billing_port_use").html(data.monitor.basic_info.billing_this_hour.port_use)
} }
$("#"+index+"_billing_history_cpu").html(data.monitor.basic_info.billing_history.cpu) if(!!data.monitor.basic_info.billing_history)
$("#"+index+"_billing_history_mem").html(data.monitor.basic_info.billing_history.mem) {
$("#"+index+"_billing_history_disk").html(data.monitor.basic_info.billing_history.disk) $("#"+index+"_billing_history_cpu").html(data.monitor.basic_info.billing_history.cpu);
$("#"+index+"_billing_history_port").html(data.monitor.basic_info.billing_history.port) $("#"+index+"_billing_history_mem").html(data.monitor.basic_info.billing_history.mem);
$("#"+index+"_billing_history_disk").html(data.monitor.basic_info.billing_history.disk);
$("#"+index+"_billing_history_port").html(data.monitor.basic_info.billing_history.port);
}
else
{
$("#"+index+"_billing_history_cpu").html(0);
$("#"+index+"_billing_history_mem").html(0);
$("#"+index+"_billing_history_disk").html(0);
$("#"+index+"_billing_history_port").html(0);
}
$("#"+index+"_billing_history_total").html(data.monitor.basic_info.billing) $("#"+index+"_billing_history_total").html(data.monitor.basic_info.billing)
$("#"+index+"_billing_cpu_a").html(data.monitor.basic_info.a_cpu) $("#"+index+"_billing_cpu_a").html(data.monitor.basic_info.a_cpu)
@ -338,6 +408,8 @@
//var url0 = "http://" + host + "/monitor/vnodes/"; //var url0 = "http://" + host + "/monitor/vnodes/";
{% for master in allcontainers %} {% for master in allcontainers %}
url = "http://" + host + "/monitor/" + '{{master.split("@")[0]}}' + "/user/net_stats/";
update_net_stats(url,'{{master.split("@")[1]}}')
{% for clustername, clusterinfo in allcontainers[master].items() %} {% for clustername, clusterinfo in allcontainers[master].items() %}
{% for container in clusterinfo['containers'] %} {% for container in clusterinfo['containers'] %}
//url = url0 + '{{ container['containername'] }}'; //url = url0 + '{{ container['containername'] }}';

View File

@ -63,8 +63,8 @@
<td id='con_cpu'>--</td> <td id='con_cpu'>--</td>
<td id='con_mem'>--</td> <td id='con_mem'>--</td>
<td id='con_disk'>--</td> <td id='con_disk'>--</td>
<td id='con_billing'><a target='_blank' title='How to figure out it?' href='https://unias.github.io/docklet/book/en/billing/billing.html'>{{ container['billing'] }} <img src='/static/img/bean.png' /></a></td> <td id='con_billing'>--</td>
<td id='con_billingthishour'><a target='_blank' title='How to figure out it?' href='https://unias.github.io/docklet/book/en/billing/billing.html'>{{ container['billing_this_hour'] }} <img src='/static/img/bean.png' /></a></td> <td id='con_billingthishour'>--</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -164,6 +164,46 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-lg-6">
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">Ingress Rate(kbps):</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i>
</button>
<button type="button" class="btn btn-box-tool" data-widget="remove"><i class="fa fa-times"></i></button>
</div>
</div>
<div class="box-body">
<div class="flot-chart">
<div class="flot-chart-content" id="ingress-chart"></div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">Egress Rate(kbps):</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i>
</button>
<button type="button" class="btn btn-box-tool" data-widget="remove"><i class="fa fa-times"></i></button>
</div>
</div>
<div class="box-body">
<div class="flot-chart">
<div class="flot-chart-content" id="egress-chart"></div>
</div>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}
{% block script_src %} {% block script_src %}

View File

@ -360,6 +360,18 @@ def monitor_request(comid,infotype,masterip):
logger.debug("monitor" + str(type(result))) logger.debug("monitor" + str(type(result)))
return json.dumps(result) return json.dumps(result)
@app.route("/monitor/<masterip>/user/<issue>/", methods=['POST'])
@login_required
def monitor_user_request(issue,masterip):
data = {
"user": session['username']
}
path = "/monitor/user/" + str(issue) + "/"
logger.debug(path + "_____" + masterip)
result = dockletRequest.post(path, data, masterip)
logger.debug("monitor" + str(type(result)))
return json.dumps(result)
@app.route("/beans/application/", methods=['GET']) @app.route("/beans/application/", methods=['GET'])
@login_required @login_required
def beansapplication(): def beansapplication():
@ -479,6 +491,12 @@ def groupdel(groupname):
def userinfo(): def userinfo():
return userinfoView.as_view() return userinfoView.as_view()
@app.route("/user/selfQuery/", methods=['GET', 'POST'])
@login_required
def userselfQuery():
result = dockletRequest.post('/user/selfQuery/')
return json.dumps(result['data'])
@app.route("/user/query/", methods=['GET', 'POST']) @app.route("/user/query/", methods=['GET', 'POST'])
@administration_required @administration_required
def userquery(): def userquery():