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
744c1885
authored
Jan 08, 2026
by
Your Name
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
storage + UI fix
parent
9b3516eb
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
181 additions
and
189 deletions
+181
-189
circle/dashboard/forms.py
+58
-8
circle/dashboard/static/dashboard/activity.js
+35
-31
circle/dashboard/static/dashboard/dashboard.js
+33
-33
circle/dashboard/templates/base.html
+0
-95
circle/dashboard/templates/dashboard/base.html
+2
-1
circle/dashboard/templates/dashboard/storage/detail.html
+13
-4
circle/dashboard/views/storage.py
+30
-8
circle/manager/scheduler.py
+3
-3
circle/storage/tasks/periodic_tasks.py
+7
-6
No files found.
circle/dashboard/forms.py
View file @
744c1885
...
@@ -24,7 +24,7 @@ import pyotp
...
@@ -24,7 +24,7 @@ import pyotp
from
crispy_forms.bootstrap
import
FormActions
from
crispy_forms.bootstrap
import
FormActions
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.layout
import
(
from
crispy_forms.layout
import
(
Layout
,
Div
,
BaseInput
,
Field
,
HTML
,
Submit
,
TEMPLATE_PACK
,
Fieldset
Layout
,
Div
,
BaseInput
,
Field
,
HTML
,
Submit
,
TEMPLATE_PACK
,
Fieldset
,
)
)
from
crispy_forms.utils
import
render_field
from
crispy_forms.utils
import
render_field
from
dal
import
autocomplete
from
dal
import
autocomplete
...
@@ -1735,24 +1735,74 @@ class DataStoreForm(ModelForm):
...
@@ -1735,24 +1735,74 @@ class DataStoreForm(ModelForm):
fields
=
(
"name"
,
"path"
,
"hostname"
)
fields
=
(
"name"
,
"path"
,
"hostname"
)
#class DiskForm(ModelForm):
# created = forms.DateTimeField()
# modified = forms.DateTimeField()
#
# def __init__(self, *args, **kwargs):
# super(DiskForm, self).__init__(*args, **kwargs)
#
# for k, v in self.fields.iteritems():
# v.widget.attrs['readonly'] = True
# self.fields['created'].initial = self.instance.created
# self.fields['modified'].initial = self.instance.modified
#
# class Meta:
# model = Disk
# fields = ("name", "filename", "datastore", "type", "bus", "size",
# "base", "dev_num", "destroyed", "is_ready",)
#
class
DiskForm
(
ModelForm
):
class
DiskForm
(
ModelForm
):
created
=
forms
.
DateTimeField
()
created
=
forms
.
DateTimeField
(
required
=
False
)
modified
=
forms
.
DateTimeField
()
modified
=
forms
.
DateTimeField
(
required
=
False
)
@property
def
helper
(
self
):
helper
=
FormHelper
()
helper
.
form_method
=
"post"
helper
.
layout
=
Layout
(
Fieldset
(
''
,
'name'
,
'filename'
,
'datastore'
,
'type'
,
'bus'
,
'size'
,
'base'
,
'dev_num'
,
'destroyed'
,
'is_ready'
,
'created'
,
'modified'
,
),
FormActions
(
Submit
(
'submit'
,
'Save'
),
)
)
return
helper
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
DiskForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
DiskForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
for
k
,
v
in
self
.
fields
.
iteritems
():
# Make all fields non-editable except 'datastore', and 'filename'.
v
.
widget
.
attrs
[
'readonly'
]
=
True
for
name
,
field
in
self
.
fields
.
iteritems
():
self
.
fields
[
'created'
]
.
initial
=
self
.
instance
.
created
if
name
==
'datastore'
or
name
==
'filename'
:
self
.
fields
[
'modified'
]
.
initial
=
self
.
instance
.
modified
continue
# field.widget.attrs['disabled'] = 'disabled'
# Show timestamps (read-only display fields)
self
.
fields
[
'created'
]
.
initial
=
getattr
(
self
.
instance
,
'created'
,
None
)
self
.
fields
[
'modified'
]
.
initial
=
getattr
(
self
.
instance
,
'modified'
,
None
)
# self.fields['created'].widget.attrs['disabled'] = 'disabled'
# self.fields['modified'].widget.attrs['disabled'] = 'disabled'
class
Meta
:
class
Meta
:
model
=
Disk
model
=
Disk
fields
=
(
"name"
,
"filename"
,
"datastore"
,
"type"
,
"bus"
,
"size"
,
fields
=
(
"name"
,
"filename"
,
"datastore"
,
"type"
,
"bus"
,
"size"
,
"base"
,
"dev_num"
,
"destroyed"
,
"is_ready"
,)
"base"
,
"dev_num"
,
"destroyed"
,
"is_ready"
,)
class
MessageForm
(
ModelForm
):
class
MessageForm
(
ModelForm
):
class
Meta
:
class
Meta
:
model
=
Message
model
=
Message
...
...
circle/dashboard/static/dashboard/activity.js
View file @
744c1885
...
@@ -27,39 +27,43 @@ $(function() {
...
@@ -27,39 +27,43 @@ $(function() {
return
false
;
return
false
;
});
});
function
initAutocompleteSelect2
(
$root
)
{
function
spinDisks
()
{
$root
.
find
(
'select[data-autocomplete-light-function="select2"]'
).
each
(
function
()
{
$
(
'#disks-spinner'
).
show
().
addClass
(
'fa-spin'
);
var
$el
=
$
(
this
);
// már initelve?
if
(
$el
.
hasClass
(
'select2-hidden-accessible'
))
return
;
var
url
=
$el
.
data
(
'autocomplete-light-url'
);
var
placeholder
=
$el
.
data
(
'placeholder'
)
||
''
;
$el
.
select2
({
dropdownParent
:
$root
,
// bootstrap modal fókusz miatt
width
:
'resolve'
,
placeholder
:
placeholder
,
allowClear
:
true
,
ajax
:
{
url
:
url
,
dataType
:
'json'
,
delay
:
250
,
data
:
function
(
params
)
{
return
{
q
:
params
.
term
,
page
:
params
.
page
||
1
};
},
processResults
:
function
(
data
)
{
// DAL autocomplete view tipikusan már {results: [...], pagination: {more: ...}}
return
data
;
}
},
// data-html="true" miatt: hagyjuk az HTML-t renderelődni
escapeMarkup
:
function
(
m
)
{
return
m
;
}
});
});
}
}
$
(
function
()
{
$
(
'#storage-link'
).
on
(
'click'
,
function
()
{
$
(
'#storage-link-spinner'
)
.
show
()
.
addClass
(
'fa-spin'
);
});
});
// Datastore autosubmit spinner
$
(
'select[data-autosubmit="1"]'
).
on
(
'change'
,
function
()
{
$
(
'#ds-spinner'
)
.
show
()
.
addClass
(
'fa-spin'
);
});
// Filter clicks
$
(
'.storage-filter'
).
on
(
'click'
,
function
()
{
spinDisks
();
});
// Search submit
$
(
'#network-host-list-form'
).
on
(
'submit'
,
function
()
{
spinDisks
();
});
$
(
function
()
{
$
(
'.nav-spinner'
).
on
(
'click'
,
function
()
{
$
(
this
).
find
(
'.fa-spinner'
)
.
show
()
.
addClass
(
'fa-spin'
);
});
});
function
showConfirmationModal
(
data
)
{
function
showConfirmationModal
(
data
)
{
// ha valamiért bent maradt egy régi modal, takarítsuk (örökölt kódnál előfordul)
// ha valamiért bent maradt egy régi modal, takarítsuk (örökölt kódnál előfordul)
$
(
'#confirmation-modal'
).
remove
();
$
(
'#confirmation-modal'
).
remove
();
...
...
circle/dashboard/static/dashboard/dashboard.js
View file @
744c1885
...
@@ -29,39 +29,6 @@ $(function () {
...
@@ -29,39 +29,6 @@ $(function () {
return
false
;
return
false
;
});
});
function
initAutocompleteSelect2
(
$root
)
{
$root
.
find
(
'select[data-autocomplete-light-function="select2"]'
).
each
(
function
()
{
var
$el
=
$
(
this
);
// már initelve?
if
(
$el
.
hasClass
(
'select2-hidden-accessible'
))
return
;
var
url
=
$el
.
data
(
'autocomplete-light-url'
);
var
placeholder
=
$el
.
data
(
'placeholder'
)
||
''
;
$el
.
select2
({
dropdownParent
:
$root
,
// bootstrap modal fókusz miatt
width
:
'resolve'
,
placeholder
:
placeholder
,
allowClear
:
true
,
ajax
:
{
url
:
url
,
dataType
:
'json'
,
delay
:
250
,
data
:
function
(
params
)
{
return
{
q
:
params
.
term
,
page
:
params
.
page
||
1
};
},
processResults
:
function
(
data
)
{
// DAL autocomplete view tipikusan már {results: [...], pagination: {more: ...}}
return
data
;
}
},
// data-html="true" miatt: hagyjuk az HTML-t renderelődni
escapeMarkup
:
function
(
m
)
{
return
m
;
}
});
});
}
$
(
'.group-create, .group-import, .group-export, .node-create, .tx-tpl-ownership, .group-delete, .node-delete, .disk-remove, .template-delete, .delete-from-group, .group-remove-all-btn, .lease-delete'
).
click
(
function
(
e
)
{
$
(
'.group-create, .group-import, .group-export, .node-create, .tx-tpl-ownership, .group-delete, .node-delete, .disk-remove, .template-delete, .delete-from-group, .group-remove-all-btn, .lease-delete'
).
click
(
function
(
e
)
{
$
.
ajax
({
$
.
ajax
({
type
:
'GET'
,
type
:
'GET'
,
...
@@ -317,6 +284,39 @@ $(function () {
...
@@ -317,6 +284,39 @@ $(function () {
});
});
});
});
function
initAutocompleteSelect2
(
$root
)
{
$root
.
find
(
'select[data-autocomplete-light-function="select2"]'
).
each
(
function
()
{
var
$el
=
$
(
this
);
// már initelve?
if
(
$el
.
hasClass
(
'select2-hidden-accessible'
))
return
;
var
url
=
$el
.
data
(
'autocomplete-light-url'
);
var
placeholder
=
$el
.
data
(
'placeholder'
)
||
''
;
$el
.
select2
({
dropdownParent
:
$root
,
// bootstrap modal fókusz miatt
width
:
'resolve'
,
placeholder
:
placeholder
,
allowClear
:
true
,
ajax
:
{
url
:
url
,
dataType
:
'json'
,
delay
:
250
,
data
:
function
(
params
)
{
return
{
q
:
params
.
term
,
page
:
params
.
page
||
1
};
},
processResults
:
function
(
data
)
{
// DAL autocomplete view tipikusan már {results: [...], pagination: {more: ...}}
return
data
;
}
},
// data-html="true" miatt: hagyjuk az HTML-t renderelődni
escapeMarkup
:
function
(
m
)
{
return
m
;
}
});
});
}
function
generateVmHTML
(
data
,
is_last
)
{
function
generateVmHTML
(
data
,
is_last
)
{
return
'<a href="'
+
data
.
url
+
'" class="list-group-item'
+
return
'<a href="'
+
data
.
url
+
'" class="list-group-item'
+
(
is_last
?
' list-group-item-last'
:
''
)
+
'">'
+
(
is_last
?
' list-group-item-last'
:
''
)
+
'">'
+
...
...
circle/dashboard/templates/base.html
View file @
744c1885
...
@@ -99,100 +99,5 @@
...
@@ -99,100 +99,5 @@
{% block extra_etc %}
{% block extra_etc %}
{% endblock %}
{% endblock %}
<style>
/* Full-page loading overlay */
#loading-overlay
{
position
:
fixed
;
top
:
0
;
left
:
0
;
width
:
100%
;
height
:
100%
;
background
:
rgba
(
255
,
255
,
255
,
0.85
);
z-index
:
9999
;
display
:
none
;
}
#loading-overlay
.spinner
{
position
:
absolute
;
top
:
50%
;
left
:
50%
;
transform
:
translate
(
-50%
,
-50%
);
text-align
:
center
;
color
:
#555
;
}
</style>
<div
id=
"loading-overlay"
>
<div
class=
"spinner"
>
<i
class=
"fa fa-spinner fa-spin fa-3x"
></i>
<p>
{% trans "Loading..." %}
</p>
</div>
</div>
//
<script>
// (function () {
// // Robust loader without beforeunload/select-change pitfalls.
// var overlay = document.getElementById("loading-overlay");
// if (!overlay) return;
//
// var timer = null;
//
// function showLoaderDelayed() {
// // Avoid stacking timers.
// if (timer) return;
//
// timer = setTimeout(function () {
// overlay.style.display = "block";
// }, 300); // show only if navigation takes noticeable time
// }
//
// function cancelLoader() {
// if (timer) {
// clearTimeout(timer);
// timer = null;
// }
// // In case it was shown and the browser restored from BFCache.
// overlay.style.display = "none";
// }
//
// // Cancel on load/pageshow (covers bfcache restore too).
// window.addEventListener("load", cancelLoader);
// window.addEventListener("pageshow", cancelLoader);
//
// // Show on form submit (POST or GET).
// var forms = document.getElementsByTagName("form");
// for (var i = 0; i
<
forms
.
length
;
i
++
)
{
// forms[i].addEventListener("submit", function () {
//// showLoaderDelayed();
// });
// }
// // Show on autosubmit select change (programmatic form.submit() does not trigger submit events).
// var autos = document.querySelectorAll('select[data-autosubmit="1"]');
// for (var k = 0; k
<
autos
.
length
;
k
++
)
{
// autos[k].addEventListener("change", function () {
// showLoaderDelayed();
// });
// }
//
// // Show on same-tab link clicks.
// var links = document.getElementsByTagName("a");
// for (var j = 0; j
<
links
.
length
;
j
++
)
{
// (function (a) {
// a.addEventListener("click", function (e) {
// // Ignore modified clicks (new tab/window, etc.)
// if (e.ctrlKey || e.shiftKey || e.metaKey || e.altKey) return;
// if (a.target && a.target !== "_self") return;
// if (!a.href) return;
//
// // Ignore hash-only navigation on the same page.
// var href = a.getAttribute("href");
// if (href && href.charAt(0) === "#") return;
//
//// showLoaderDelayed();
// });
// })(links[j]);
// }
// })();
//
</script>
</body>
</body>
</html>
</html>
circle/dashboard/templates/dashboard/base.html
View file @
744c1885
...
@@ -38,9 +38,10 @@
...
@@ -38,9 +38,10 @@
</a>
</a>
</li>
</li>
<li>
<li>
<a
href=
"{% url "
dashboard
.
views
.
storage
"
%}"
>
<a
href=
"{% url "
dashboard
.
views
.
storage
"
%}"
class=
"nav-spinner"
>
<i
class=
"fa fa-database"
></i>
<i
class=
"fa fa-database"
></i>
<span
class=
"hidden-sm"
>
{% trans "Storage" %}
</span>
<span
class=
"hidden-sm"
>
{% trans "Storage" %}
</span>
<i
class=
"fa fa-spinner"
style=
"display:none; margin-left:4px;"
></i>
</a>
</a>
</li>
</li>
<li>
<li>
...
...
circle/dashboard/templates/dashboard/storage/detail.html
View file @
744c1885
...
@@ -13,7 +13,11 @@
...
@@ -13,7 +13,11 @@
<div
class=
"col-md-5"
>
<div
class=
"col-md-5"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
><i
class=
"fa fa-database"
></i>
{% trans "Datastore" %}
</h3>
<h3
class=
"no-margin"
>
<i
class=
"fa fa-database"
></i>
i
{% trans "Datastore" %}
<i
id=
"ds-spinner"
class=
"fa fa-spinner"
style=
"display:none;"
></i>
</h3>
</div>
</div>
<div
class=
"panel-body"
>
<div
class=
"panel-body"
>
<form
method=
"get"
action=
""
>
<form
method=
"get"
action=
""
>
...
@@ -111,7 +115,9 @@
...
@@ -111,7 +115,9 @@
{% if stats %}
{% if stats %}
<div
class=
"panel panel-default"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
><i
class=
"fa fa-file"
></i>
{% trans "Disks" %}
</h3>
<h3
class=
"no-margin"
><i
class=
"fa fa-file"
></i>
{% trans "Disks" %}
<i
id=
"disks-spinner"
class=
"fa fa-spinner"
style=
"display:none;"
></i>
</h3>
</div>
</div>
<div
class=
"panel-body"
>
<div
class=
"panel-body"
>
<div
class=
"row"
>
<div
class=
"row"
>
...
@@ -119,11 +125,13 @@
...
@@ -119,11 +125,13 @@
<ul
class=
"nav nav-pills"
style=
"margin: 5px 0 20px 0;"
>
<ul
class=
"nav nav-pills"
style=
"margin: 5px 0 20px 0;"
>
<li
class=
"disabled"
><a
href=
"#"
>
{% trans "Filter by type" %}
</a></li>
<li
class=
"disabled"
><a
href=
"#"
>
{% trans "Filter by type" %}
</a></li>
<li
{%
if
not
request
.
GET
.
filter
%}
class=
"active"
{%
endif
%}
>
<li
{%
if
not
request
.
GET
.
filter
%}
class=
"active"
{%
endif
%}
>
<a
href=
"{{ request.path }}?s={{ request.GET.s }}"
>
{% trans "ALL" %}
</a>
<a
href=
"{{ request.path }}?ds={{ ds_selected }}&s={{ request.GET.s }}"
class=
"storage-filter"
>
{% trans "ALL" %}
</a>
</li>
</li>
{% for f in filter_names %}
{% for f in filter_names %}
<li
{%
if
request
.
GET
.
filter =
=
f
.
0
%}
class=
"active"
{%
endif
%}
>
<li
{%
if
request
.
GET
.
filter =
=
f
.
0
%}
class=
"active"
{%
endif
%}
>
<a
href=
"?
filter={{ f.0 }}&s={{ request.GET.s }}
"
>
{{ f.1|capfirst }}
</a>
<a
href=
"?
ds={{ ds_selected }}&filter={{ f.0 }}&s={{ request.GET.s }}"
class=
"storage-filter
"
>
{{ f.1|capfirst }}
</a>
</li>
</li>
{% endfor %}
{% endfor %}
</ul>
</ul>
...
@@ -134,6 +142,7 @@
...
@@ -134,6 +142,7 @@
<input
type=
"text"
name=
"s"
class=
"form-control"
<input
type=
"text"
name=
"s"
class=
"form-control"
value=
"{{ request.GET.s }}"
placeholder=
"{% trans "
Search
..."
%}"
/>
value=
"{{ request.GET.s }}"
placeholder=
"{% trans "
Search
..."
%}"
/>
<input
type=
"hidden"
name=
"filter"
value=
"{{ request.GET.filter }}"
/>
<input
type=
"hidden"
name=
"filter"
value=
"{{ request.GET.filter }}"
/>
<input
type=
"hidden"
name=
"ds"
value=
"{{ ds_selected }}"
/>
<span
class=
"input-group-btn"
>
<span
class=
"input-group-btn"
>
<button
class=
"btn btn-primary"
><i
class=
"fa fa-search"
></i></button>
<button
class=
"btn btn-primary"
><i
class=
"fa fa-search"
></i></button>
</span>
</span>
...
...
circle/dashboard/views/storage.py
View file @
744c1885
...
@@ -17,6 +17,7 @@
...
@@ -17,6 +17,7 @@
from
__future__
import
unicode_literals
,
absolute_import
from
__future__
import
unicode_literals
,
absolute_import
import
errno
import
errno
import
logging
from
django.contrib
import
messages
from
django.contrib
import
messages
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
...
@@ -35,6 +36,8 @@ from ..forms import DataStoreForm, DiskForm
...
@@ -35,6 +36,8 @@ from ..forms import DataStoreForm, DiskForm
from
django.shortcuts
import
get_object_or_404
,
redirect
from
django.shortcuts
import
get_object_or_404
,
redirect
from
django.db
import
IntegrityError
from
django.db
import
IntegrityError
logger
=
logging
.
getLogger
(
__name__
)
class
StorageDetail
(
SuperuserRequiredMixin
,
TemplateView
):
class
StorageDetail
(
SuperuserRequiredMixin
,
TemplateView
):
template_name
=
"dashboard/storage/detail.html"
template_name
=
"dashboard/storage/detail.html"
...
@@ -147,7 +150,7 @@ class StorageDetail(SuperuserRequiredMixin, TemplateView):
...
@@ -147,7 +150,7 @@ class StorageDetail(SuperuserRequiredMixin, TemplateView):
messages
.
success
(
request
,
_
(
"Datastore updated."
))
messages
.
success
(
request
,
_
(
"Datastore updated."
))
return
self
.
_redirect_with_ds
(
ds
.
pk
)
return
self
.
_redirect_with_ds
(
ds
.
pk
)
def
get_table_data
(
self
,
ds
):
def
get_table_data
(
self
,
ds
,
missing
):
if
ds
is
None
:
if
ds
is
None
:
return
Disk
.
objects
.
none
()
return
Disk
.
objects
.
none
()
...
@@ -163,12 +166,18 @@ class StorageDetail(SuperuserRequiredMixin, TemplateView):
...
@@ -163,12 +166,18 @@ class StorageDetail(SuperuserRequiredMixin, TemplateView):
}
}
if
filter_name
:
if
filter_name
:
qs
=
qs
.
filter
(
**
filter_queries
.
get
(
filter_name
,
{}))
if
filter_name
==
'missing'
:
qs
=
missing
else
:
qs
=
qs
.
filter
(
**
filter_queries
.
get
(
filter_name
,
{}))
if
search
:
if
search
:
search
=
search
.
strip
()
qs
=
qs
.
filter
(
qs
=
qs
.
filter
(
Q
(
name__icontains
=
search
)
|
Q
(
name__icontains
=
search
)
|
Q
(
filename__icontains
=
search
))
Q
(
filename__icontains
=
search
)
|
Q
(
instance_set__name__icontains
=
search
)
|
Q
(
template_set__name__icontains
=
search
)
)
.
distinct
()
return
qs
return
qs
...
@@ -235,7 +244,8 @@ class StorageDetail(SuperuserRequiredMixin, TemplateView):
...
@@ -235,7 +244,8 @@ class StorageDetail(SuperuserRequiredMixin, TemplateView):
context
[
"orphan_disks"
]
=
None
context
[
"orphan_disks"
]
=
None
context
[
"disk_table"
]
=
DiskListTable
(
context
[
"disk_table"
]
=
DiskListTable
(
self
.
get_table_data
(
ds
),
request
=
self
.
request
,
self
.
get_table_data
(
ds
,
missing
=
context
[
"missing_disks"
]),
request
=
self
.
request
,
template
=
"django_tables2/with_pagination.html"
template
=
"django_tables2/with_pagination.html"
)
)
...
@@ -243,15 +253,27 @@ class StorageDetail(SuperuserRequiredMixin, TemplateView):
...
@@ -243,15 +253,27 @@ class StorageDetail(SuperuserRequiredMixin, TemplateView):
(
'vm'
,
_
(
"virtual machine"
)),
(
'vm'
,
_
(
"virtual machine"
)),
(
'template'
,
_
(
"template"
)),
(
'template'
,
_
(
"template"
)),
(
'none'
,
_
(
"none"
)),
(
'none'
,
_
(
"none"
)),
(
'missing'
,
_
(
"missing"
)),
)
)
return
context
return
context
class
DiskDetail
(
SuperuserRequiredMixin
,
UpdateView
):
class
DiskDetail
(
SuperuserRequiredMixin
,
UpdateView
):
model
=
Disk
model
=
Disk
form_class
=
DiskForm
form_class
=
DiskForm
template_name
=
"dashboard/storage/disk.html"
template_name
=
"dashboard/storage/disk.html"
def
form_valid
(
self
,
form
):
def
form_valid
(
self
,
form
):
pass
# Save only allowed edits (datastore) and redirect back.
self
.
object
=
form
.
save
()
messages
.
success
(
self
.
request
,
_
(
"Disk updated."
))
return
redirect
(
self
.
request
.
path
)
#class DiskDetail(SuperuserRequiredMixin, UpdateView):
# model = Disk
# form_class = DiskForm
# template_name = "dashboard/storage/disk.html"
#
# def form_valid(self, form):
# pass
#
circle/manager/scheduler.py
View file @
744c1885
...
@@ -62,10 +62,10 @@ def common_select(instance, nodes):
...
@@ -62,10 +62,10 @@ def common_select(instance, nodes):
nodes
=
[
n
for
n
in
nodes
nodes
=
[
n
for
n
in
nodes
if
n
.
schedule_enabled
and
n
.
online
and
if
n
.
schedule_enabled
and
n
.
online
and
has_traits
(
instance
.
req_traits
.
all
(),
n
)]
has_traits
(
instance
.
req_traits
.
all
(),
n
)]
logger
.
error
(
'capab:
%
s 0
selected_nodes:
%
s'
,
instance
.
capability_group
,
nodes
)
logger
.
debug
(
'capab:
%
s
selected_nodes:
%
s'
,
instance
.
capability_group
,
nodes
)
if
instance
.
capability_group
:
if
instance
.
capability_group
:
nodes
=
[
n
for
n
in
nodes
if
n
.
capability
==
instance
.
capability_group
]
nodes
=
[
n
for
n
in
nodes
if
n
ot
n
.
capability
or
n
.
capability
==
instance
.
capability_group
]
logger
.
error
(
'capab:
%
s selected_nodes:
%
s'
,
instance
.
capability_group
,
nodes
)
logger
.
debug
(
'capab:
%
s selected_nodes:
%
s'
,
instance
.
capability_group
,
nodes
)
if
not
nodes
:
if
not
nodes
:
logger
.
warning
(
'select_node: no usable node for
%
s'
,
unicode
(
instance
))
logger
.
warning
(
'select_node: no usable node for
%
s'
,
unicode
(
instance
))
raise
TraitsUnsatisfiableException
()
raise
TraitsUnsatisfiableException
()
...
...
circle/storage/tasks/periodic_tasks.py
View file @
744c1885
...
@@ -39,16 +39,17 @@ def garbage_collector(timeout=15):
...
@@ -39,16 +39,17 @@ def garbage_collector(timeout=15):
args
=
[
ds
.
path
],
queue
=
queue_name
)
.
get
(
timeout
=
timeout
))
args
=
[
ds
.
path
],
queue
=
queue_name
)
.
get
(
timeout
=
timeout
))
disks
=
set
(
ds
.
get_deletable_disks
())
disks
=
set
(
ds
.
get_deletable_disks
())
queue_name
=
ds
.
get_remote_queue_name
(
'storage'
,
priority
=
'slow'
)
queue_name
=
ds
.
get_remote_queue_name
(
'storage'
,
priority
=
'slow'
)
for
i
in
disks
&
files
:
logger
.
info
(
"Image:
%
s at Datastore:
%
s moved to trash folder."
%
(
i
,
ds
.
path
))
storage_tasks
.
move_to_trash
.
apply_async
(
args
=
[
ds
.
path
,
i
],
queue
=
queue_name
)
.
get
(
timeout
=
timeout
)
try
:
try
:
for
i
in
disks
&
files
:
logger
.
error
(
"Image:
%
s at Datastore:
%
s moved to trash folder."
%
(
i
,
ds
.
path
))
storage_tasks
.
move_to_trash
.
apply_async
(
args
=
[
ds
.
path
,
i
],
queue
=
queue_name
)
.
get
(
timeout
=
timeout
)
storage_tasks
.
make_free_space
.
apply_async
(
storage_tasks
.
make_free_space
.
apply_async
(
args
=
[
ds
.
path
],
queue
=
queue_name
)
.
get
(
timeout
=
timeout
)
args
=
[
ds
.
path
],
queue
=
queue_name
)
.
get
(
timeout
=
timeout
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
warning
(
str
(
e
))
logger
.
error
(
str
(
e
))
@celery.task
@celery.task
...
...
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