Commit dec67193 by Őry Máté

Merge branch 'master' into feature-resize_disk

Conflicts:
	circle/vm/operations.py
parents a27aafa6 026e7b20
......@@ -273,3 +273,4 @@ def register_operation(op_cls, op_id=None, target_cls=None):
setattr(target_cls, operation_registry_name, dict())
getattr(target_cls, operation_registry_name)[op_id] = op_cls
return op_cls
......@@ -524,11 +524,7 @@ class TemplateForm(forms.ModelForm):
value = field.widget.value_from_datadict(
self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, forms.FileField):
initial = self.initial.get(name, field.initial)
value = field.clean(value, initial)
else:
value = field.clean(value)
value = field.clean(value)
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
......@@ -544,13 +540,14 @@ class TemplateForm(forms.ModelForm):
else:
self.cleaned_data[name] = getattr(old, name)
if "req_traits" not in self.allowed_fields:
self.cleaned_data['req_traits'] = self.instance.req_traits.all()
def save(self, commit=True):
data = self.cleaned_data
self.instance.max_ram_size = data.get('ram_size')
instance = super(TemplateForm, self).save(commit=False)
if commit:
instance.save()
instance = super(TemplateForm, self).save(commit=True)
# create and/or delete InterfaceTemplates
networks = InterfaceTemplate.objects.filter(
......
......@@ -973,3 +973,33 @@ textarea[name="new_members"] {
.hilight .autocomplete-hl {
color: orange;
}
.node-list-table tbody>tr>td, .node-list-table thead>tr>th {
vertical-align: middle;
}
.node-list-table thead>tr>th,
.node-list-table .enabled, .node-list-table .priority,
.node-list-table .overcommit, .node-list-table .number_of_VMs {
text-align: center;
}
.node-list-table-thin {
width: 10px;
}
.node-list-table-monitor {
width: 250px;
}
.graph-images img {
max-width: 100%;
}
#vm-list-table tbody td:nth-child(3) {
white-space: nowrap;
}
#vm-list-table td {
vertical-align: middle;
}
......@@ -397,6 +397,20 @@ $(function () {
clientInstalledAction(connectUri);
return false;
});
/* change graphs */
$(".graph-buttons a").click(function() {
var time = $(this).data("graph-time");
$(".graph-images img").each(function() {
var src = $(this).prop("src");
var new_src = src.substring(0, src.lastIndexOf("/") + 1) + time;
$(this).prop("src", new_src);
});
// change the buttons too
$(".graph-buttons a").removeClass("btn-primary").addClass("btn-default");
$(this).removeClass("btn-default").addClass("btn-primary");
return false;
});
});
function generateVmHTML(pk, name, host, icon, _status, fav, is_last) {
......
......@@ -19,8 +19,7 @@ from __future__ import absolute_import
from django.contrib.auth.models import Group, User
from django_tables2 import Table, A
from django_tables2.columns import (TemplateColumn, Column, BooleanColumn,
LinkColumn)
from django_tables2.columns import TemplateColumn, Column, LinkColumn
from vm.models import Node, InstanceTemplate, Lease
from django.utils.translation import ugettext_lazy as _
......@@ -40,8 +39,10 @@ class NodeListTable(Table):
attrs={'th': {'class': 'node-list-table-thin'}},
)
enabled = BooleanColumn(
get_status_display = Column(
verbose_name=_("Status"),
attrs={'th': {'class': 'node-list-table-thin'}},
order_by=("enabled", "schedule_enabled"),
)
name = TemplateColumn(
......@@ -66,20 +67,12 @@ class NodeListTable(Table):
orderable=False,
)
actions = TemplateColumn(
verbose_name=_("Actions"),
attrs={'th': {'class': 'node-list-table-thin'}},
template_code=('{% include "dashboard/node-list/column-'
'actions.html" with btn_size="btn-xs" %}'),
orderable=False,
)
class Meta:
model = Node
attrs = {'class': ('table table-bordered table-striped table-hover '
'node-list-table')}
fields = ('pk', 'name', 'host', 'enabled', 'priority', 'overcommit',
'number_of_VMs', )
fields = ('pk', 'name', 'host', 'get_status_display', 'priority',
'overcommit', 'number_of_VMs', )
class GroupListTable(Table):
......
{% for o in graph_time_options %}
<a class="btn btn-xs
btn-{% if graph_time == o.time %}primary{% else %}default{% endif %}"
href="?graph_time={{ o.time }}"
data-graph-time="{{ o.time }}">
{{ o.name }}
</a>
{% endfor %}
......@@ -18,6 +18,8 @@ Choose a compute node to migrate {{obj}} to.
<li class="panel panel-default"><div class="panel-body">
<label for="migrate-to-{{n.pk}}">
<strong>{{ n }}</strong>
<div class="label label-primary"><i class="fa {{n.get_status_icon}}"></i>
{{n.get_status_display}}</div>
{% if current == n.pk %}<div class="label label-info">{% trans "current" %}</div>{% endif %}
{% if selected == n.pk %}<div class="label label-success">{% trans "recommended" %}</div>{% endif %}
</label>
......
{% load i18n %}
<div class="modal fade" id="confirmation-modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
{% if text %}
{{ text }}
{% else %}
{%blocktrans with object=object%}
Are you sure you want to flush <strong>{{ object }}</strong>?
{%endblocktrans%}
{% endif %}
<div class="pull-right">
<form action="{% url "dashboard.views.flush-node" pk=node.pk %}?next={{next}}" method="POST">
{% csrf_token %}
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Cancel" %}</button>
<input type="hidden" name="flush" value=""/>
<button class="btn btn-warning">{% trans "Yes" %}</button>
</form>
</div>
<div class="clearfix"></div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div>
......@@ -6,13 +6,12 @@
{% block content %}
<div class="body-content">
<div class="page-header">
<div class="pull-right" id="ops">
{% include "dashboard/vm-detail/_operations.html" %}
</div>
<div class="pull-right" style="padding-top: 15px;">
<a title="{% trans "Rename" %}" href="#" class="btn btn-default btn-xs node-details-rename-button"><i class="fa fa-pencil"></i></a>
<a title="{% trans "Flush" %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-flush" href="{% url "dashboard.views.flush-node" pk=node.pk %}"><i class="fa fa-cloud-upload"></i></a>
<a title="{% trans "Enable" %}" style="display:{% if node.enabled %}none{% else %}inline-block{% endif %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-enable" href="{% url "dashboard.views.status-node" pk=node.pk %}?next={{ request.path }}"><i class="fa fa-check"></i></a>
<a title="{% trans "Disable" %}" style="display:{% if not node.enabled %}none{% else %}inline-block{% endif %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-enable" href="{% url "dashboard.views.status-node" pk=node.pk %}?next={{ request.path }}"><i class="fa fa-ban"></i></a>
<a title="{% trans "Delete" %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-delete" href="{% url "dashboard.views.delete-node" pk=node.pk %}"><i class="fa fa-trash-o"></i></a>
<a title="{% trans "Help" %}" href="#" class="btn btn-default btn-xs node-details-help-button"><i class="fa fa-question"></i></a>
</div>
<h1>
<div id="node-details-rename">
......@@ -26,42 +25,34 @@
{{ node.name }}
</div>
</h1>
<div class="node-details-help js-hidden">
<ul style="list-style: none;">
<li>
<strong>{% trans "Rename" %}:</strong>
{% trans "Change the name of the node." %}
</li>
<li>
<strong>{% trans "Flush" %}:</strong>
{% trans "Disable node and move all instances to other one." %}
</li>
<li>
<strong>{% trans "Enable" %}:</strong>
{% trans "Enables node." %}
</li>
<li>
<strong>{% trans "Disable" %}:</strong>
{% trans "Disables node." %}
</li>
<li>
<strong>{% trans "Delete" %}:</strong>
{% trans "Remove node and it's host." %}
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-md-2" id="node-info-pane">
<div id="node-info-data" class="big">
<span id="node-details-state" class="label
{% if node.state == 'ONLINE' %}label-success
{% elif node.state == 'MISSING' %}label-danger
{% elif node.state == 'DISABLED' %}label-warning
{% elif node.state == 'OFFLINE' %}label-warning{% endif %}">
{% if node.state == 'ACTIVE' %}label-success
{% elif node.state == 'PASSIVE' %}label-warning
{% else %}label-danger{% endif %}">
<i class="fa {{ node.get_status_icon }}"></i> {{ node.get_status_display|upper }}
</span>
</div>
<div>
{% if node.enabled %}
<span class="label label-success">{% trans "Enabled" %}</span>
{% if node.schedule_enabled %}
<span class="label label-success">{% trans "Schedule enabled" %}</span>
{% else %}
<span class="label label-warning">{% trans "Schedule disabled" %}</span>
{% endif %}
{% else %}
<span class="label label-warning">{% trans "Disabled" %}</span>
{% endif %}
{% if node.online %}
<span class="label label-success">{% trans "Online" %}</span>
{% else %}
<span class="label label-warning">{% trans "Offline" %}</span>
{% endif %}
</div>
</div>
<div class="col-md-10" id="node-detail-pane">
<div class="panel panel-default" id="node-detail-panel">
......
......@@ -30,15 +30,22 @@
</div>
<div class="col-md-8">
{% if graphite_enabled %}
<img src="{% url "dashboard.views.node-graph" node.pk "cpu" "6h" %}" style="width:100%"/>
<img src="{% url "dashboard.views.node-graph" node.pk "memory" "6h" %}" style="width:100%"/>
<img src="{% url "dashboard.views.node-graph" node.pk "network" "6h" %}" style="width:100%"/>
<div class="text-center graph-buttons">
{% include "dashboard/_graph-time-buttons.html" %}
</div>
<div class="graph-images text-center">
<img src="{% url "dashboard.views.node-graph" node.pk "cpu" graph_time %}"/>
<img src="{% url "dashboard.views.node-graph" node.pk "memory" graph_time %}"/>
<img src="{% url "dashboard.views.node-graph" node.pk "network" graph_time %}"/>
<img src="{% url "dashboard.views.node-graph" node.pk "vm" graph_time %}"/>
<img src="{% url "dashboard.views.node-graph" node.pk "alloc" graph_time %}"/>
</div>
{% endif %}
</div>
</div>
<style>
.form-group {
margin: 0px;
}
</div>
</style>
<style>
.form-group {
margin: 0px;
}
</style>
......@@ -21,25 +21,23 @@
</div>
</div>
<style>
.node-list-table tbody>tr>td, .node-list-table thead>tr>th {
vertical-align: middle;
}
.node-list-table thead>tr>th,
.node-list-table .enabled, .node-list-table .priority,
.node-list-table .overcommit, .node-list-table .number_of_VMs {
text-align: center;
}
.node-list-table-thin {
width: 10px;
}
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<div class="pull-right graph-buttons">
{% include "dashboard/_graph-time-buttons.html" %}
</div>
<h3 class="no-margin"><i class="fa fa-area-chart"></i> {% trans "Graphs" %}</h3>
</div>
<div class="text-center graph-images">
<img src="{% url "dashboard.views.node-list-graph" "alloc" graph_time %}"/>
<img src="{% url "dashboard.views.node-list-graph" "vm" graph_time %}"/>
</div>
</div>
</div><!-- -col-md-12 -->
</div><!-- .row -->
.node-list-table-monitor {
width: 250px;
}
</style>
{% endblock %}
{% block extra_js %}
......
{% load i18n %}
<div class="btn-group">
<button type="button" class="btn {{ btn_size }} btn-warning nojs-dropdown-toogle dropdown-toggle" data-toggle="dropdown">Action
<i class="fa fa-caret-down"></i>
</button>
<ul class="dropdown-menu nojs-dropdown-toogle" role="menu">
<li>
<a href="#" class="node-details-rename-button">
<i class="fa fa-pencil"></i> {% trans "Rename" %}
</a>
</li>
<li>
<a data-node-pk="{{ record.pk }}" class="real-link node-flush" href="{% url "dashboard.views.flush-node" pk=record.pk %}">
<i class="fa fa-cloud-upload"></i> {% trans "Flush" %}
</a>
</li>
<li>
<a style={% if record.enabled %}"display:none"{% else %}"display:block"{% endif %} data-node-pk="{{ record.pk }}" class="real-link node-enable" href="{% url "dashboard.views.status-node" pk=record.pk %}?next={{ request.path }}">
<i class="fa fa-check"></i> {% trans "Enable" %}
</a>
</li>
<li>
<a style={% if record.enabled %}"display:block"{% else %}"display:none"{% endif %} data-node-pk="{{ record.pk }}" class="real-link node-enable" href="{% url "dashboard.views.status-node" pk=record.pk %}?next={{ request.path }}">
<i class="fa fa-times"></i> {% trans "Disable" %}
</a>
</li>
<li>
<a data-node-pk="{{ record.pk }}" class="real-link node-delete" href="{% url "dashboard.views.delete-node" pk=record.pk %}?next={{ request.path }}">
<i class="fa fa-trash-o"></i> {% trans "Delete" %}
</a>
</li>
</ul>
</div>
......@@ -23,18 +23,15 @@
{% csrf_token %}
{{ form.name|as_crispy_field }}
<a {% if form.parent.value %}
href="{% url "dashboard.views.template-detail" pk=form.parent.value %}"
{% else %}
disabled %}
{% endif %}
class="btn btn-default pull-right" style="margin-top: 24px;">
{% trans "Visit" %}
<i class="fa fa-arrow-circle-right"></i>
</a>
<div style="width: 80%;">
{{ form.parent|as_crispy_field }}
</div>
<strong>{% trans "Parent template" %}:</strong>
{% if parent %}
<a href="{% url "dashboard.views.template-detail" pk=parent.pk %}">
{{ parent.name }}
</a>
{% else %}
-
{% endif %}
<fieldset class="resources-sliders">
<legend>{% trans "Resource configuration" %}</legend>
......
......@@ -123,9 +123,14 @@
</div>
<div class="col-md-8">
{% if graphite_enabled %}
<img src="{% url "dashboard.views.vm-graph" instance.pk "cpu" "6h" %}" style="width:100%"/>
<img src="{% url "dashboard.views.vm-graph" instance.pk "memory" "6h" %}" style="width:100%"/>
<img src="{% url "dashboard.views.vm-graph" instance.pk "network" "6h" %}" style="width:100%"/>
<div class="text-center graph-buttons">
{% include "dashboard/_graph-time-buttons.html" %}
</div>
<div class="graph-images text-center">
<img src="{% url "dashboard.views.vm-graph" instance.pk "cpu" graph_time %}"/>
<img src="{% url "dashboard.views.vm-graph" instance.pk "memory" graph_time %}"/>
<img src="{% url "dashboard.views.vm-graph" instance.pk "network" graph_time %}"/>
</div>
{% endif %}
</div>
</div>
......@@ -25,11 +25,11 @@ from .views import (
GroupDetailView, GroupList, IndexView,
InstanceActivityDetail, LeaseCreate, LeaseDelete, LeaseDetail,
MyPreferencesView, NodeAddTraitView, NodeCreate, NodeDelete,
NodeDetailView, NodeFlushView, NodeGraphView, NodeList, NodeStatus,
NodeDetailView, NodeList, NodeStatus,
NotificationView, PortDelete, TemplateAclUpdateView, TemplateCreate,
TemplateDelete, TemplateDetail, TemplateList, TransferOwnershipConfirmView,
TransferOwnershipView, vm_activity, VmCreate, VmDetailView,
VmDetailVncTokenView, VmGraphView, VmList,
VmDetailVncTokenView, VmList,
DiskRemoveView, get_disk_download_status, InterfaceDeleteView,
GroupRemoveUserView,
GroupRemoveFutureUserView,
......@@ -46,7 +46,10 @@ from .views import (
GroupPermissionsView,
LeaseAclUpdateView,
ClientCheck, TokenLogin,
VmGraphView, NodeGraphView, NodeListGraphView,
)
from .views.vm import vm_ops, vm_mass_ops
from .views.node import node_ops
autocomplete_light.autodiscover()
......@@ -74,8 +77,6 @@ urlpatterns = patterns(
name="dashboard.views.template-list"),
url(r"^template/delete/(?P<pk>\d+)/$", TemplateDelete.as_view(),
name="dashboard.views.template-delete"),
url(r'^vm/', include('dashboard.vm.urls')),
url(r'^vm/(?P<pk>\d+)/remove_port/(?P<rule>\d+)/$', PortDelete.as_view(),
name='dashboard.views.remove-port'),
url(r'^vm/(?P<pk>\d+)/$', VmDetailView.as_view(),
......@@ -110,8 +111,6 @@ urlpatterns = patterns(
name="dashboard.views.delete-node"),
url(r'^node/status/(?P<pk>\d+)/$', NodeStatus.as_view(),
name="dashboard.views.status-node"),
url(r'^node/flush/(?P<pk>\d+)/$', NodeFlushView.as_view(),
name="dashboard.views.flush-node"),
url(r'^node/create/$', NodeCreate.as_view(),
name='dashboard.views.node-create'),
......@@ -121,14 +120,18 @@ urlpatterns = patterns(
name="dashboard.views.delete-group"),
url(r'^group/list/$', GroupList.as_view(),
name='dashboard.views.group-list'),
url((r'^vm/(?P<pk>\d+)/graph/(?P<metric>cpu|memory|network)/'
url((r'^vm/(?P<pk>\d+)/graph/(?P<metric>[a-z]+)/'
r'(?P<time>[0-9]{1,2}[hdwy])$'),
VmGraphView.as_view(),
name='dashboard.views.vm-graph'),
url((r'^node/(?P<pk>\d+)/graph/(?P<metric>cpu|memory|network)/'
url((r'^node/(?P<pk>\d+)/graph/(?P<metric>[a-z]+)/'
r'(?P<time>[0-9]{1,2}[hdwy])$'),
NodeGraphView.as_view(),
name='dashboard.views.node-graph'),
url((r'^node/graph/(?P<metric>[a-z]+)/'
r'(?P<time>[0-9]{1,2}[hdwy])$'),
NodeListGraphView.as_view(),
name='dashboard.views.node-list-graph'),
url(r'^group/(?P<pk>\d+)/$', GroupDetailView.as_view(),
name='dashboard.views.group-detail'),
url(r'^group/(?P<pk>\d+)/update/$', GroupProfileUpdate.as_view(),
......@@ -210,3 +213,21 @@ urlpatterns = patterns(
url(r'^token-login/(?P<token>.*)/$', TokenLogin.as_view(),
name="dashboard.views.token-login"),
)
urlpatterns += patterns(
'',
*(url(r'^vm/(?P<pk>\d+)/op/%s/$' % op, v.as_view(), name=v.get_urlname())
for op, v in vm_ops.iteritems())
)
urlpatterns += patterns(
'',
*(url(r'^vm/mass_op/%s/$' % op, v.as_view(), name=v.get_urlname())
for op, v in vm_mass_ops.iteritems())
)
urlpatterns += patterns(
'',
*(url(r'^node/(?P<pk>\d+)/op/%s/$' % op, v.as_view(), name=v.get_urlname())
for op, v in node_ops.iteritems())
)
......@@ -11,3 +11,4 @@ from template import *
from user import *
from util import *
from vm import *
from graph import *
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from __future__ import absolute_import, unicode_literals
import logging
import requests
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse, Http404
from django.utils.translation import ugettext_lazy as _
from django.views.generic import View
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
from vm.models import Instance, Node
logger = logging.getLogger(__name__)
def register_graph(metric_cls, graph_name, graphview_cls):
if not hasattr(graphview_cls, 'metrics'):
graphview_cls.metrics = {}
graphview_cls.metrics[graph_name] = metric_cls
class GraphViewBase(LoginRequiredMixin, View):
def create_class(self, cls):
return type(str(cls.__name__ + 'Metric'), (cls, self.base), {})
def get(self, request, pk, metric, time, *args, **kwargs):
graphite_url = settings.GRAPHITE_URL
if graphite_url is None:
raise Http404()
try:
metric = self.metrics[metric]
except KeyError:
raise Http404()
try:
instance = self.get_object(request, pk)
except self.model.DoesNotExist:
raise Http404()
metric = self.create_class(metric)(instance)
return HttpResponse(metric.get_graph(graphite_url, time),
mimetype="image/png")
def get_object(self, request, pk):
instance = self.model.objects.get(id=pk)
if not instance.has_level(request.user, 'user'):
raise PermissionDenied()
return instance
class Metric(object):
cacti_style = True
derivative = False
scale_to_seconds = None
metric_name = None
title = None
label = None
def __init__(self, obj, metric_name=None):
self.obj = obj
self.metric_name = (
metric_name or self.metric_name or self.__class__.__name__.lower())
def get_metric_name(self):
return self.metric_name
def get_label(self):
return self.label or self.get_metric_name()
def get_title(self):
return self.title or self.get_metric_name()
def get_minmax(self):
return (None, None)
def get_target(self):
target = '%s.%s' % (self.obj.metric_prefix, self.get_metric_name())
if self.derivative:
target = 'nonNegativeDerivative(%s)' % target
if self.scale_to_seconds:
target = 'scaleToSeconds(%s, %d)' % (target, self.scale_to_seconds)
target = 'alias(%s, "%s")' % (target, self.get_label())
if self.cacti_style:
target = 'cactiStyle(%s)' % target
return target
def get_graph(self, graphite_url, time, width=500, height=200):
params = {'target': self.get_target(),
'from': '-%s' % time,
'title': self.get_title().encode('UTF-8'),
'width': width,
'height': height}
ymin, ymax = self.get_minmax()
if ymin is not None:
params['yMin'] = ymin
if ymax is not None:
params['yMax'] = ymax
logger.debug('%s %s', graphite_url, params)
response = requests.get('%s/render/' % graphite_url, params=params)
return response.content
class VmMetric(Metric):
def get_title(self):
title = super(VmMetric, self).get_title()
return '%s (%s) - %s' % (self.obj.name, self.obj.vm_name, title)
class NodeMetric(Metric):
def get_title(self):
title = super(NodeMetric, self).get_title()
return '%s (%s) - %s' % (self.obj.name, self.obj.host.hostname, title)
class VmGraphView(GraphViewBase):
model = Instance
base = VmMetric
class NodeGraphView(SuperuserRequiredMixin, GraphViewBase):
model = Node
base = NodeMetric
def get_object(self, request, pk):
return self.model.objects.get(id=pk)
class NodeListGraphView(SuperuserRequiredMixin, GraphViewBase):
model = Node
base = Metric
def get_object(self, request, pk):
return Node.objects.filter(enabled=True)
def get(self, request, metric, time, *args, **kwargs):
return super(NodeListGraphView, self).get(request, None, metric, time)
class Ram(object):
metric_name = "memory.usage"
title = _("RAM usage (%)")
label = _("RAM usage (%)")
def get_minmax(self):
return (0, 105)
register_graph(Ram, 'memory', VmGraphView)
register_graph(Ram, 'memory', NodeGraphView)
class Cpu(object):
metric_name = "cpu.percent"
title = _("CPU usage (%)")
label = _("CPU usage (%)")
def get_minmax(self):
if isinstance(self.obj, Node):
return (0, 105)
else:
return (0, self.obj.num_cores * 100 + 5)
register_graph(Cpu, 'cpu', VmGraphView)
register_graph(Cpu, 'cpu', NodeGraphView)
class VmNetwork(object):
title = _("Network")
def get_minmax(self):
return (0, None)
def get_target(self):
metrics = []
for n in self.obj.interface_set.all():
params = (self.obj.metric_prefix, n.vlan.vid, n.vlan.name)
metrics.append(
'alias(scaleToSeconds(nonNegativeDerivative('
'%s.network.bytes_recv-%s), 10), "out - %s (bits/s)")' % (
params))
metrics.append(
'alias(scaleToSeconds(nonNegativeDerivative('
'%s.network.bytes_sent-%s), 10), "in - %s (bits/s)")' % (
params))
return 'group(%s)' % ','.join(metrics)
register_graph(VmNetwork, 'network', VmGraphView)