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
Commit
dec67193
authored
Sep 24, 2014
by
Őry Máté
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into feature-resize_disk
Conflicts: circle/vm/operations.py
parents
a27aafa6
026e7b20
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
735 additions
and
494 deletions
+735
-494
circle/common/operations.py
+1
-0
circle/dashboard/forms.py
+5
-8
circle/dashboard/static/dashboard/dashboard.css
+30
-0
circle/dashboard/static/dashboard/dashboard.js
+14
-0
circle/dashboard/tables.py
+6
-13
circle/dashboard/templates/dashboard/_graph-time-buttons.html
+8
-0
circle/dashboard/templates/dashboard/_vm-migrate.html
+2
-0
circle/dashboard/templates/dashboard/confirm/ajax-node-flush.html
+0
-27
circle/dashboard/templates/dashboard/node-detail.html
+23
-32
circle/dashboard/templates/dashboard/node-detail/home.html
+16
-9
circle/dashboard/templates/dashboard/node-list.html
+16
-18
circle/dashboard/templates/dashboard/node-list/column-actions.html
+0
-33
circle/dashboard/templates/dashboard/template-edit.html
+9
-12
circle/dashboard/templates/dashboard/vm-detail/home.html
+8
-3
circle/dashboard/urls.py
+29
-8
circle/dashboard/views/__init__.py
+1
-0
circle/dashboard/views/graph.py
+290
-0
circle/dashboard/views/node.py
+40
-60
circle/dashboard/views/template.py
+1
-0
circle/dashboard/views/util.py
+28
-43
circle/dashboard/views/vm.py
+3
-25
circle/dashboard/vm/__init__.py
+0
-0
circle/dashboard/vm/urls.py
+0
-33
circle/fabfile.py
+3
-1
circle/locale/hu/LC_MESSAGES/django.po
+0
-0
circle/locale/hu/LC_MESSAGES/djangojs.po
+4
-4
circle/manager/moncelery.py
+6
-0
circle/manager/scheduler.py
+2
-4
circle/monitor/tasks/local_periodic_tasks.py
+16
-0
circle/storage/models.py
+2
-1
circle/vm/migrations/0028_auto__add_field_node_schedule_enabled.py
+0
-0
circle/vm/models/instance.py
+4
-0
circle/vm/models/node.py
+46
-42
circle/vm/operations.py
+104
-77
circle/vm/tests/test_models.py
+18
-41
No files found.
circle/common/operations.py
View file @
dec67193
...
...
@@ -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
circle/dashboard/forms.py
View file @
dec67193
...
...
@@ -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
(
...
...
circle/dashboard/static/dashboard/dashboard.css
View file @
dec67193
...
...
@@ -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
;
}
circle/dashboard/static/dashboard/dashboard.js
View file @
dec67193
...
...
@@ -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
)
{
...
...
circle/dashboard/tables.py
View file @
dec67193
...
...
@@ -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
):
...
...
circle/dashboard/templates/dashboard/_graph-time-buttons.html
0 → 100644
View file @
dec67193
{% 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 %}
circle/dashboard/templates/dashboard/_vm-migrate.html
View file @
dec67193
...
...
@@ -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>
...
...
circle/dashboard/templates/dashboard/confirm/ajax-node-flush.html
deleted
100644 → 0
View file @
a27aafa6
{% 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>
circle/dashboard/templates/dashboard/node-detail.html
View file @
dec67193
...
...
@@ -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"
>
...
...
circle/dashboard/templates/dashboard/node-detail/home.html
View file @
dec67193
...
...
@@ -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>
circle/dashboard/templates/dashboard/node-list.html
View file @
dec67193
...
...
@@ -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 %}
...
...
circle/dashboard/templates/dashboard/node-list/column-actions.html
deleted
100644 → 0
View file @
a27aafa6
{% 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>
circle/dashboard/templates/dashboard/template-edit.html
View file @
dec67193
...
...
@@ -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>
...
...
circle/dashboard/templates/dashboard/vm-detail/home.html
View file @
dec67193
...
...
@@ -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>
circle/dashboard/urls.py
View file @
dec67193
...
...
@@ -25,11 +25,11 @@ from .views import (
GroupDetailView
,
GroupList
,
IndexView
,
InstanceActivityDetail
,
LeaseCreate
,
LeaseDelete
,
LeaseDetail
,
MyPreferencesView
,
NodeAddTraitView
,
NodeCreate
,
NodeDelete
,
NodeDetailView
,
Node
FlushView
,
NodeGraphView
,
Node
List
,
NodeStatus
,
NodeDetailView
,
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
,
...
...
@@ -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
())
)
circle/dashboard/views/__init__.py
View file @
dec67193
...
...
@@ -11,3 +11,4 @@ from template import *
from
user
import
*
from
util
import
*
from
vm
import
*
from
graph
import
*
circle/dashboard/views/graph.py
0 → 100644
View file @
dec67193
# 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
)