Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
CIRCLE
/
cloud
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
94
Merge Requests
10
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
A prog2-höz tartozó friss repo anyagok itt elérhetőek:
https://git.iit.bme.hu/
Commit
932191a6
authored
Sep 17, 2014
by
Bach Dániel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dashboard: rewrite graph views
parent
113c28e4
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
306 additions
and
87 deletions
+306
-87
circle/dashboard/graph.py
+289
-0
circle/dashboard/urls.py
+9
-4
circle/dashboard/views.py
+0
-83
circle/vm/models/instance.py
+4
-0
circle/vm/models/node.py
+4
-0
No files found.
circle/dashboard/graph.py
0 → 100644
View file @
932191a6
# 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
,
SuspiciousOperation
from
django.http
import
HttpResponse
,
Http404
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
SuspiciousOperation
()
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
)
class
NodeNetwork
(
object
):
title
=
"Network"
def
get_minmax
(
self
):
return
(
0
,
None
)
def
get_target
(
self
):
return
(
'aliasSub(scaleToSeconds(nonNegativeDerivative(
%
s.network.b*),'
'10), ".*
\
.bytes_(sent|recv)-([a-zA-Z0-9]+).*", "
\\
2
\\
1")'
%
(
self
.
obj
.
metric_prefix
))
register_graph
(
NodeNetwork
,
'network'
,
NodeGraphView
)
class
NodeVms
(
object
):
metric_name
=
"vmcount"
title
=
"Instance count"
label
=
"instance count"
def
get_minmax
(
self
):
return
(
0
,
None
)
register_graph
(
NodeVms
,
'vm'
,
NodeGraphView
)
class
NodeAllocated
(
object
):
title
=
"Allocated memory (bytes)"
def
get_target
(
self
):
prefix
=
self
.
obj
.
metric_prefix
if
self
.
obj
.
online
:
ram_size
=
self
.
obj
.
ram_size
else
:
ram_size
=
0
used
=
'alias(
%
s.memory.used_bytes, "used")'
%
prefix
allocated
=
'alias(
%
s.memory.allocated, "allocated")'
%
prefix
max
=
'threshold(
%
d, "max")'
%
ram_size
return
'cactiStyle(group(
%
s,
%
s,
%
s))'
%
(
used
,
allocated
,
max
)
def
get_minmax
(
self
):
return
(
0
,
None
)
register_graph
(
NodeAllocated
,
'alloc'
,
NodeGraphView
)
class
NodeListAllocated
(
object
):
title
=
"Allocated memory (bytes)"
def
get_target
(
self
):
nodes
=
self
.
obj
used
=
','
.
join
(
'
%
s.memory.used_bytes'
%
n
.
metric_prefix
for
n
in
nodes
)
allocated
=
'alias(sumSeries(
%
s), "allocated")'
%
','
.
join
(
'
%
s.memory.allocated'
%
n
.
metric_prefix
for
n
in
nodes
)
max
=
'threshold(
%
d, "max")'
%
sum
(
n
.
ram_size
for
n
in
nodes
if
n
.
online
)
return
(
'group(aliasSub(aliasByNode(stacked(group(
%
s)), 1), "$",'
'" (used)"),
%
s,
%
s)'
%
(
used
,
allocated
,
max
))
def
get_minmax
(
self
):
return
(
0
,
None
)
register_graph
(
NodeListAllocated
,
'alloc'
,
NodeListGraphView
)
class
NodeListVms
(
object
):
title
=
"Instance count"
def
get_target
(
self
):
vmcount
=
','
.
join
(
'
%
s.vmcount'
%
n
.
metric_prefix
for
n
in
self
.
obj
)
return
'group(aliasByNode(stacked(group(
%
s)), 1))'
%
vmcount
def
get_minmax
(
self
):
return
(
0
,
None
)
register_graph
(
NodeListVms
,
'vm'
,
NodeListGraphView
)
circle/dashboard/urls.py
View file @
932191a6
...
...
@@ -20,16 +20,17 @@ from django.conf.urls import patterns, url, include
import
autocomplete_light
from
vm.models
import
Instance
from
.graph
import
VmGraphView
,
NodeGraphView
,
NodeListGraphView
from
.views
import
(
AclUpdateView
,
FavouriteView
,
GroupAclUpdateView
,
GroupDelete
,
GroupDetailView
,
GroupList
,
IndexView
,
InstanceActivityDetail
,
LeaseCreate
,
LeaseDelete
,
LeaseDetail
,
MyPreferencesView
,
NodeAddTraitView
,
NodeCreate
,
NodeDelete
,
NodeDetailView
,
NodeFlushView
,
Node
GraphView
,
Node
List
,
NodeStatus
,
NodeDetailView
,
NodeFlushView
,
NodeList
,
NodeStatus
,
NotificationView
,
PortDelete
,
TemplateAclUpdateView
,
TemplateCreate
,
TemplateDelete
,
TemplateDetail
,
TemplateList
,
TransferOwnershipConfirmView
,
TransferOwnershipView
,
vm_activity
,
VmCreate
,
VmDetailView
,
VmDetailVncTokenView
,
Vm
GraphView
,
Vm
List
,
VmDetailVncTokenView
,
VmList
,
DiskRemoveView
,
get_disk_download_status
,
InterfaceDeleteView
,
GroupRemoveUserView
,
GroupRemoveFutureUserView
,
...
...
@@ -121,14 +122,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
(),
...
...
circle/dashboard/views.py
View file @
932191a6
...
...
@@ -25,7 +25,6 @@ from urlparse import urljoin
import
json
import
logging
import
re
import
requests
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
,
Group
...
...
@@ -2904,88 +2903,6 @@ class TransferOwnershipConfirmView(LoginRequiredMixin, View):
return
(
instance
,
new_owner
)
class
GraphViewBase
(
LoginRequiredMixin
,
View
):
def
get
(
self
,
request
,
pk
,
metric
,
time
,
*
args
,
**
kwargs
):
graphite_url
=
settings
.
GRAPHITE_URL
if
graphite_url
is
None
:
raise
Http404
()
if
metric
not
in
self
.
metrics
.
keys
():
raise
SuspiciousOperation
()
try
:
instance
=
self
.
get_object
(
request
,
pk
)
except
self
.
model
.
DoesNotExist
:
raise
Http404
()
prefix
=
self
.
get_prefix
(
instance
)
target
=
self
.
metrics
[
metric
]
%
{
'prefix'
:
prefix
}
title
=
self
.
get_title
(
instance
,
metric
)
params
=
{
'target'
:
target
,
'from'
:
'-
%
s'
%
time
,
'title'
:
title
.
encode
(
'UTF-8'
),
'width'
:
'500'
,
'height'
:
'200'
}
logger
.
debug
(
'
%
s
%
s'
,
graphite_url
,
params
)
response
=
requests
.
get
(
'
%
s/render/'
%
graphite_url
,
params
=
params
)
return
HttpResponse
(
response
.
content
,
mimetype
=
"image/png"
)
def
get_prefix
(
self
,
instance
):
raise
NotImplementedError
(
"Subclass must implement abstract method"
)
def
get_title
(
self
,
instance
,
metric
):
raise
NotImplementedError
(
"Subclass must implement abstract method"
)
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
VmGraphView
(
GraphViewBase
):
metrics
=
{
'cpu'
:
(
'cactiStyle(alias(nonNegativeDerivative(
%(prefix)
s.cpu.usage),'
'"cpu usage (
%%
)"))'
),
'memory'
:
(
'cactiStyle(alias(
%(prefix)
s.memory.usage,'
'"memory usage (
%%
)"))'
),
'network'
:
(
'group('
'aliasSub(nonNegativeDerivative(
%(prefix)
s.network.bytes_recv*),'
' ".*-(
\
d+)
\\
)", "out (vlan
\\
1)"),'
'aliasSub(nonNegativeDerivative(
%(prefix)
s.network.bytes_sent*),'
' ".*-(
\
d+)
\\
)", "in (vlan
\\
1)"))'
),
}
model
=
Instance
def
get_prefix
(
self
,
instance
):
return
'vm.
%
s'
%
instance
.
vm_name
def
get_title
(
self
,
instance
,
metric
):
return
'
%
s (
%
s) -
%
s'
%
(
instance
.
name
,
instance
.
vm_name
,
metric
)
class
NodeGraphView
(
SuperuserRequiredMixin
,
GraphViewBase
):
metrics
=
{
'cpu'
:
(
'cactiStyle(alias(nonNegativeDerivative(
%(prefix)
s.cpu.times),'
'"cpu usage (
%%
)"))'
),
'memory'
:
(
'cactiStyle(alias(
%(prefix)
s.memory.usage,'
'"memory usage (
%%
)"))'
),
'network'
:
(
'cactiStyle(aliasByMetric('
'nonNegativeDerivative(
%(prefix)
s.network.bytes_*)))'
),
}
model
=
Node
def
get_prefix
(
self
,
instance
):
return
'circle.
%
s'
%
instance
.
host
.
hostname
def
get_title
(
self
,
instance
,
metric
):
return
'
%
s -
%
s'
%
(
instance
.
name
,
metric
)
def
get_object
(
self
,
request
,
pk
):
return
self
.
model
.
objects
.
get
(
id
=
pk
)
class
NotificationView
(
LoginRequiredMixin
,
TemplateView
):
def
get_template_names
(
self
):
...
...
circle/vm/models/instance.py
View file @
932191a6
...
...
@@ -1002,3 +1002,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
latest
=
self
.
get_latest_activity_in_progress
()
return
(
latest
and
latest
.
resultant_state
is
not
None
and
self
.
status
!=
latest
.
resultant_state
)
@property
def
metric_prefix
(
self
):
return
'vm.
%
s'
%
self
.
vm_name
circle/vm/models/node.py
View file @
932191a6
...
...
@@ -381,3 +381,7 @@ class Node(OperatedMixin, TimeStampedModel):
@permalink
def
get_absolute_url
(
self
):
return
(
'dashboard.views.node-detail'
,
None
,
{
'pk'
:
self
.
id
})
@property
def
metric_prefix
(
self
):
return
'circle.
%
s'
%
self
.
host
.
hostname
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment