Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Gelencsér Szabolcs
/
circlestack
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
cfae5fec
authored
Sep 29, 2016
by
Kálmán Viktor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dashboard: add disk usage breakdown pie chart
parent
ab26a51e
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
159 additions
and
2 deletions
+159
-2
circle/bower.json
+2
-1
circle/circle/settings/base.py
+6
-0
circle/dashboard/static/dashboard/dashboard.less
+25
-0
circle/dashboard/static/dashboard/datastore-details.js
+36
-0
circle/dashboard/templates/dashboard/storage/detail.html
+56
-1
circle/dashboard/views/storage.py
+22
-0
circle/storage/models.py
+7
-0
circle/storage/tasks/storage_tasks.py
+5
-0
No files found.
circle/bower.json
View file @
cfae5fec
...
@@ -20,6 +20,7 @@
...
@@ -20,6 +20,7 @@
"bootbox"
:
"~4.3.0"
,
"bootbox"
:
"~4.3.0"
,
"intro.js"
:
"0.9.0"
,
"intro.js"
:
"0.9.0"
,
"favico.js"
:
"~0.3.5"
,
"favico.js"
:
"~0.3.5"
,
"datatables"
:
"~1.10.4"
"datatables"
:
"~1.10.4"
,
"chart.js"
:
"2.3.0"
}
}
}
}
circle/circle/settings/base.py
View file @
cfae5fec
...
@@ -245,6 +245,12 @@ PIPELINE_JS = {
...
@@ -245,6 +245,12 @@ PIPELINE_JS = {
),
),
"output_filename"
:
"vm-detail.js"
,
"output_filename"
:
"vm-detail.js"
,
},
},
"datastore"
:
{
"source_filenames"
:
(
"chart.js/dist/Chart.min.js"
,
"dashboard/datastore-details.js"
),
"output_filename"
:
"datastore.js"
,
},
}
}
...
...
circle/dashboard/static/dashboard/dashboard.less
View file @
cfae5fec
...
@@ -1563,3 +1563,28 @@ textarea[name="new_members"] {
...
@@ -1563,3 +1563,28 @@ textarea[name="new_members"] {
margin: 15px 0 2px 0;
margin: 15px 0 2px 0;
}
}
}
}
#datastore-chart-legend {
width: 350px;
margin-top: 100px;
margin-left: -120px;
/* Landscape phones and down */
@media (max-width: 992px) {
margin-left: -25px;
}
ul {
list-style: none;
}
li {
font-size: 18px;
margin-bottom: 2px;
span {
display: inline-block;
width: 30px;
height: 18px;
margin-right: 8px;
}
}
}
circle/dashboard/static/dashboard/datastore-details.js
0 → 100644
View file @
cfae5fec
$
(
function
()
{
var
data
=
JSON
.
parse
(
$
(
"#chart-data"
).
data
(
"data"
));
var
labels
=
[];
for
(
var
i
=
0
;
i
<
data
.
labels
.
length
;
i
++
)
{
labels
.
push
(
data
.
labels
[
i
]
+
" ("
+
data
.
readable_data
[
i
]
+
")"
);
}
var
pieChart
=
new
Chart
(
document
.
getElementById
(
"datastore-chart"
),
{
type
:
'pie'
,
data
:
{
labels
:
labels
,
datasets
:
[{
data
:
data
[
'data'
],
backgroundColor
:
[
"#57b257"
,
"#538ccc"
,
"#f0df24"
,
"#ff9a38"
,
"#7f7f7f"
,
]
}]
},
options
:
{
legend
:
{
display
:
false
,
},
tooltips
:
{
callbacks
:
{
label
:
function
(
item
,
chartData
)
{
return
data
[
'labels'
][
item
.
index
]
+
": "
+
data
[
'readable_data'
][
item
.
index
];
}
}
},
}
});
$
(
"#datastore-chart-legend"
).
html
(
pieChart
.
generateLegend
());
});
circle/dashboard/templates/dashboard/storage/detail.html
View file @
cfae5fec
{% extends "dashboard/base.html" %}
{% extends "dashboard/base.html" %}
{% load staticfiles %}
{% load pipeline %}
{% load sizefieldtags %}
{% load i18n %}
{% load i18n %}
{% load render_table from django_tables2 %}
{% load render_table from django_tables2 %}
{% load crispy_forms_tags %}
{% load crispy_forms_tags %}
...
@@ -105,4 +106,58 @@
...
@@ -105,4 +106,58 @@
</div>
</div>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-md-12"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
>
<i
class=
"fa fa-pie-chart"
></i>
{% trans "Disk usage breakdown" %}
</h3>
</div>
<div
class=
"panel-body"
>
<div
class=
"row"
>
<div
class=
"col-md-9"
>
<canvas
id=
"datastore-chart"
></canvas>
</div>
<div
class=
"col-md-3"
>
<div
id=
"datastore-chart-legend"
></div>
</div>
</div>
<div
id=
"chart-data"
data-data=
'{
"data": [{{stats.template_actual_size}},
{{stats.vm_actual_size}},
{{stats.dumps}},
{{stats.iso_raw}},
{{stats.trash}}],
"readable_data": ["{{stats.template_actual_size|filesize}}",
"{{stats.vm_actual_size|filesize}}",
"{{stats.dumps|filesize}}",
"{{stats.dumps|filesize}}",
"{{stats.iso_raw|filesize}}",
"{{stats.trash|filesize}}"],
"labels": ["{% trans "Templates" %}",
"{% trans "Virtual machines" %}",
"{% trans "Memory dumps" %}",
"{% trans "ISO + Raw images" %}",
"{% trans "Trash" %}"]
}
'
>
</div>
<div>
{% trans "Total virtual machine disk usage" %}:
<strong>
{{ stats.vm_actual_size|filesize }}
</strong>
<br
/>
{% trans "Actual virtual machine disk usage" %}:
<strong>
{{ stats.vm_size|filesize}}
</strong>
</div>
</div>
<!-- .panel-body -->
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
{% javascript "datastore" %}
{% endblock %}
{% endblock %}
circle/dashboard/views/storage.py
View file @
cfae5fec
...
@@ -91,6 +91,7 @@ class StorageDetail(SuperuserRequiredMixin, UpdateView):
...
@@ -91,6 +91,7 @@ class StorageDetail(SuperuserRequiredMixin, UpdateView):
return
qs
return
qs
def
_get_stats
(
self
):
def
_get_stats
(
self
):
# datastore stats
stats
=
self
.
object
.
get_statistics
()
stats
=
self
.
object
.
get_statistics
()
free_space
=
int
(
stats
[
'free_space'
])
free_space
=
int
(
stats
[
'free_space'
])
free_percent
=
float
(
stats
[
'free_percent'
])
free_percent
=
float
(
stats
[
'free_percent'
])
...
@@ -98,11 +99,32 @@ class StorageDetail(SuperuserRequiredMixin, UpdateView):
...
@@ -98,11 +99,32 @@ class StorageDetail(SuperuserRequiredMixin, UpdateView):
total_space
=
free_space
/
(
free_percent
/
100.0
)
total_space
=
free_space
/
(
free_percent
/
100.0
)
used_space
=
total_space
-
free_space
used_space
=
total_space
-
free_space
# file stats
data
=
self
.
get_object
()
.
get_file_statistics
()
dumps_size
=
sum
(
d
[
'size'
]
for
d
in
data
[
'dumps'
])
trash
=
sum
(
d
[
'size'
]
for
d
in
data
[
'trash'
])
iso_raw
=
sum
(
d
[
'size'
]
for
d
in
data
[
'disks'
]
if
d
[
'format'
]
in
(
"iso"
,
"raw"
))
vm_size
=
vm_actual_size
=
template_actual_size
=
0
for
d
in
data
[
'disks'
]:
if
d
[
'format'
]
==
"qcow2"
and
d
[
'type'
]
==
"normal"
:
template_actual_size
+=
d
[
'actual_size'
]
else
:
vm_size
+=
d
[
'size'
]
vm_actual_size
+=
d
[
'actual_size'
]
return
{
return
{
'used_percent'
:
int
(
100
-
free_percent
),
'used_percent'
:
int
(
100
-
free_percent
),
'free_space'
:
filesizeformat
(
free_space
),
'free_space'
:
filesizeformat
(
free_space
),
'used_space'
:
filesizeformat
(
used_space
),
'used_space'
:
filesizeformat
(
used_space
),
'total_space'
:
filesizeformat
(
total_space
),
'total_space'
:
filesizeformat
(
total_space
),
'dumps'
:
dumps_size
,
'trash'
:
trash
,
'iso_raw'
:
iso_raw
,
'vm_size'
:
vm_size
,
'vm_actual_size'
:
vm_actual_size
,
'template_actual_size'
:
template_actual_size
,
}
}
def
get_success_url
(
self
):
def
get_success_url
(
self
):
...
...
circle/storage/models.py
View file @
cfae5fec
...
@@ -110,6 +110,13 @@ class DataStore(Model):
...
@@ -110,6 +110,13 @@ class DataStore(Model):
disks
=
Disk
.
objects
.
filter
(
destroyed__isnull
=
True
,
is_ready
=
True
)
disks
=
Disk
.
objects
.
filter
(
destroyed__isnull
=
True
,
is_ready
=
True
)
return
disks
.
exclude
(
filename__in
=
files
)
return
disks
.
exclude
(
filename__in
=
files
)
@method_cache
(
30
)
def
get_file_statistics
(
self
,
timeout
=
15
):
queue_name
=
self
.
get_remote_queue_name
(
'storage'
,
"slow"
)
data
=
storage_tasks
.
get_file_statistics
.
apply_async
(
args
=
[
self
.
path
],
queue
=
queue_name
)
.
get
(
timeout
=
timeout
)
return
data
class
Disk
(
TimeStampedModel
):
class
Disk
(
TimeStampedModel
):
...
...
circle/storage/tasks/storage_tasks.py
View file @
cfae5fec
...
@@ -81,3 +81,8 @@ def recover_from_trash(datastore, disk_path):
...
@@ -81,3 +81,8 @@ def recover_from_trash(datastore, disk_path):
@celery.task
(
name
=
'storagedriver.get_storage_stat'
)
@celery.task
(
name
=
'storagedriver.get_storage_stat'
)
def
get_storage_stat
(
path
):
def
get_storage_stat
(
path
):
pass
pass
@celery.task
(
name
=
'storagedriver.get_file_statistics'
)
def
get_file_statistics
(
datastore
):
pass
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