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
15d3d135
authored
Aug 29, 2014
by
Kálmán Viktor
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into feature-template-list
parents
a304c0e4
4268ac9d
Show whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
1504 additions
and
884 deletions
+1504
-884
circle/common/models.py
+36
-4
circle/common/operations.py
+25
-8
circle/dashboard/static/dashboard/dashboard.css
+73
-0
circle/dashboard/static/dashboard/dashboard.js
+7
-2
circle/dashboard/static/dashboard/vm-create.js
+5
-0
circle/dashboard/static/dashboard/vm-list.js
+113
-79
circle/dashboard/templates/dashboard/_notifications-timeline.html
+3
-2
circle/dashboard/templates/dashboard/_vm-mass-migrate.html
+36
-0
circle/dashboard/templates/dashboard/instanceactivity_detail.html
+3
-7
circle/dashboard/templates/dashboard/lease-create.html
+1
-1
circle/dashboard/templates/dashboard/lease-edit.html
+1
-1
circle/dashboard/templates/dashboard/mass-operate.html
+38
-0
circle/dashboard/templates/dashboard/node-detail/_activity-timeline.html
+2
-4
circle/dashboard/templates/dashboard/template-list.html
+1
-1
circle/dashboard/templates/dashboard/vm-detail/_activity-timeline.html
+5
-5
circle/dashboard/templates/dashboard/vm-list.html
+30
-27
circle/dashboard/templatetags/hro.py
+12
-0
circle/dashboard/tests/test_mockedviews.py
+109
-1
circle/dashboard/tests/test_views.py
+0
-14
circle/dashboard/urls.py
+2
-5
circle/dashboard/views.py
+152
-50
circle/dashboard/vm/urls.py
+12
-4
circle/locale/hu/LC_MESSAGES/django.po
+795
-617
circle/locale/hu/LC_MESSAGES/djangojs.po
+12
-11
circle/vm/models/activity.py
+9
-6
circle/vm/models/instance.py
+7
-1
circle/vm/models/node.py
+1
-1
circle/vm/operations.py
+14
-33
No files found.
circle/common/models.py
View file @
15d3d135
...
...
@@ -27,6 +27,7 @@ from warnings import warn
from
django.contrib
import
messages
from
django.contrib.auth.models
import
User
from
django.core.cache
import
cache
from
django.core.exceptions
import
PermissionDenied
from
django.core.serializers.json
import
DjangoJSONEncoder
from
django.db.models
import
(
CharField
,
DateTimeField
,
ForeignKey
,
NullBooleanField
...
...
@@ -413,6 +414,10 @@ class HumanReadableObject(object):
self
.
_set_values
(
user_text_template
,
admin_text_template
,
params
)
def
_set_values
(
self
,
user_text_template
,
admin_text_template
,
params
):
if
isinstance
(
user_text_template
,
Promise
):
user_text_template
=
user_text_template
.
_proxy____args
[
0
]
if
isinstance
(
admin_text_template
,
Promise
):
admin_text_template
=
admin_text_template
.
_proxy____args
[
0
]
self
.
user_text_template
=
user_text_template
self
.
admin_text_template
=
admin_text_template
self
.
params
=
params
...
...
@@ -451,6 +456,12 @@ class HumanReadableObject(object):
self
.
user_text_template
,
unicode
(
self
.
params
))
return
self
.
user_text_template
def
get_text
(
self
,
user
):
if
user
and
user
.
is_superuser
:
return
self
.
get_admin_text
()
else
:
return
self
.
get_user_text
()
def
to_dict
(
self
):
return
{
"user_text_template"
:
self
.
user_text_template
,
"admin_text_template"
:
self
.
admin_text_template
,
...
...
@@ -481,13 +492,34 @@ class HumanReadableException(HumanReadableObject, Exception):
self
.
level
=
"error"
def
send_message
(
self
,
request
,
level
=
None
):
if
request
.
user
and
request
.
user
.
is_superuser
:
msg
=
self
.
get_admin_text
()
else
:
msg
=
self
.
get_user_text
()
msg
=
self
.
get_text
(
request
.
user
)
getattr
(
messages
,
level
or
self
.
level
)(
request
,
msg
)
def
fetch_human_exception
(
exception
,
user
=
None
):
"""Fetch user readable message from exception.
>>> r = humanize_exception("foo", Exception())
>>> fetch_human_exception(r, User())
u'foo'
>>> fetch_human_exception(r).get_text(User())
u'foo'
>>> fetch_human_exception(Exception(), User())
u'Unknown error'
>>> fetch_human_exception(PermissionDenied(), User())
u'Permission Denied'
"""
if
not
isinstance
(
exception
,
HumanReadableException
):
if
isinstance
(
exception
,
PermissionDenied
):
exception
=
create_readable
(
ugettext_noop
(
"Permission Denied"
))
else
:
exception
=
create_readable
(
ugettext_noop
(
"Unknown error"
),
ugettext_noop
(
"Unknown error:
%(ex)
s"
),
ex
=
unicode
(
exception
))
return
exception
.
get_text
(
user
)
if
user
else
exception
def
humanize_exception
(
message
,
exception
=
None
,
level
=
None
,
**
params
):
"""Return new dynamic-class exception which is based on
HumanReadableException and the original class with the dict of exception.
...
...
circle/common/operations.py
View file @
15d3d135
...
...
@@ -18,10 +18,10 @@
from
inspect
import
getargspec
from
logging
import
getLogger
from
.models
import
activity_context
,
has_suffix
from
django.core.exceptions
import
PermissionDenied
,
ImproperlyConfigured
from
django.utils.translation
import
ugettext_noop
from
.models
import
activity_context
,
has_suffix
,
humanize_exception
logger
=
getLogger
(
__name__
)
...
...
@@ -31,6 +31,7 @@ class Operation(object):
"""
async_queue
=
'localhost.man'
required_perms
=
None
superuser_required
=
False
do_not_call_in_templates
=
True
abortable
=
False
has_percentage
=
False
...
...
@@ -143,13 +144,26 @@ class Operation(object):
def
check_precond
(
self
):
pass
def
check_auth
(
self
,
user
):
if
self
.
required_perms
is
None
:
@classmethod
def
check_perms
(
cls
,
user
):
"""Check if user is permitted to run this operation on any instance
"""
if
cls
.
required_perms
is
None
:
raise
ImproperlyConfigured
(
"Set required_perms to () if none needed."
)
if
not
user
.
has_perms
(
self
.
required_perms
):
if
not
user
.
has_perms
(
cls
.
required_perms
):
raise
PermissionDenied
(
"
%
s doesn't have the required permissions."
%
user
)
if
cls
.
superuser_required
and
not
user
.
is_superuser
:
raise
humanize_exception
(
ugettext_noop
(
"Superuser privileges are required."
),
PermissionDenied
())
def
check_auth
(
self
,
user
):
"""Check if user is permitted to run this operation on this instance
"""
self
.
check_perms
(
user
)
def
create_activity
(
self
,
parent
,
user
,
kwargs
):
raise
NotImplementedError
...
...
@@ -185,14 +199,17 @@ class OperatedMixin(object):
def
__getattr__
(
self
,
name
):
# NOTE: __getattr__ is only called if the attribute doesn't already
# exist in your __dict__
cls
=
self
.
__class__
return
self
.
get_operation_class
(
name
)(
self
)
@classmethod
def
get_operation_class
(
cls
,
name
):
ops
=
getattr
(
cls
,
operation_registry_name
,
{})
op
=
ops
.
get
(
name
)
if
op
:
return
op
(
self
)
return
op
else
:
raise
AttributeError
(
"
%
r object has no attribute
%
r"
%
(
self
.
__class__
.
__name__
,
name
))
(
cls
.
__name__
,
name
))
def
get_available_operations
(
self
,
user
):
"""Yield Operations that match permissions of user and preconditions.
...
...
circle/dashboard/static/dashboard/dashboard.css
View file @
15d3d135
...
...
@@ -867,3 +867,76 @@ textarea[name="list-new-namelist"] {
border-bottom
:
1px
dotted
#aaa
;
padding
:
5px
0px
;
}
#vm-list-table
.migrating-icon
{
-webkit-animation
:
passing
2s
linear
infinite
;
animation
:
passing
2s
linear
infinite
;
}
@-webkit-keyframes
passing
{
0
%
{
-webkit-transform
:
translateX
(
50%
);
transform
:
translateX
(
50%
);
opacity
:
0
;
}
50
%
{
-webkit-transform
:
translateX
(
0%
);
transform
:
translateX
(
0%
);
opacity
:
1
;
}
100
%
{
-webkit-transform
:
translateX
(
-50%
);
transform
:
translateX
(
-50%
);
opacity
:
0
;
}
}
@keyframes
passing
{
0
%
{
-webkit-transform
:
translateX
(
50%
);
-ms-transform
:
translateX
(
50%
);
transform
:
translateX
(
50%
);
opacity
:
0
;
}
50
%
{
-webkit-transform
:
translateX
(
0%
);
-ms-transform
:
translateX
(
0%
);
transform
:
translateX
(
0%
);
opacity
:
1
;
}
100
%
{
-webkit-transform
:
translateX
(
-50%
);
-ms-transform
:
translateX
(
-50%
);
transform
:
translateX
(
-50%
);
opacity
:
0
;
}
}
.mass-migrate-node
{
cursor
:
pointer
;
}
.mass-op-panel
{
padding
:
6px
10px
;
}
.mass-op-panel
.check
{
color
:
#449d44
;
}
.mass-op-panel
.minus
{
color
:
#d9534f
;
}
.mass-op-panel
.status-icon
{
font-size
:
.8em
;
}
#vm-list-search
,
#vm-mass-ops
{
margin-top
:
8px
;
}
circle/dashboard/static/dashboard/dashboard.js
View file @
15d3d135
...
...
@@ -488,14 +488,19 @@ function addSliderMiscs() {
ram_fire
=
true
;
$
(
".ram-slider"
).
simpleSlider
(
"setValue"
,
parseInt
(
val
));
});
$
(
".cpu-priority-input"
).
trigger
(
"change"
);
$
(
".cpu-count-input, .ram-input"
).
trigger
(
"input"
);
setDefaultSliderValues
(
);
$
(
".cpu-priority-slider"
).
simpleSlider
(
"setDisabled"
,
$
(
".cpu-priority-input"
).
prop
(
"disabled"
));
$
(
".cpu-count-slider"
).
simpleSlider
(
"setDisabled"
,
$
(
".cpu-count-input"
).
prop
(
"disabled"
));
$
(
".ram-slider"
).
simpleSlider
(
"setDisabled"
,
$
(
".ram-input"
).
prop
(
"disabled"
));
}
function
setDefaultSliderValues
()
{
$
(
".cpu-priority-input"
).
trigger
(
"change"
);
$
(
".ram-input, .cpu-count-input"
).
trigger
(
"input"
);
}
/* deletes the VM with the pk
* if dir is true, then redirect to the dashboard landing page
...
...
circle/dashboard/static/dashboard/vm-create.js
View file @
15d3d135
...
...
@@ -28,6 +28,9 @@ function vmCreateLoaded() {
$
(
'#create-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#create-modal'
).
remove
();
});
$
(
"#create-modal"
).
on
(
"shown.bs.modal"
,
function
()
{
setDefaultSliderValues
();
});
});
return
false
;
});
...
...
@@ -217,6 +220,8 @@ function vmCustomizeLoaded() {
});
if
(
error
)
return
true
;
$
(
this
).
find
(
"i"
).
prop
(
"class"
,
"fa fa-spinner fa-spin"
);
$
.
ajax
({
url
:
'/dashboard/vm/create/'
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
...
...
circle/dashboard/static/dashboard/vm-list.js
View file @
15d3d135
...
...
@@ -14,6 +14,7 @@ $(function() {
$
(
'.vm-list-table tbody'
).
find
(
'tr'
).
mousedown
(
function
()
{
var
retval
=
true
;
if
(
!
$
(
this
).
data
(
"vm-pk"
))
return
;
if
(
ctrlDown
)
{
setRowColor
(
$
(
this
));
if
(
!
$
(
this
).
hasClass
(
'vm-list-selected'
))
{
...
...
@@ -46,86 +47,20 @@ $(function() {
selected
=
[{
'index'
:
$
(
this
).
index
(),
'vm'
:
$
(
this
).
data
(
"vm-pk"
)}];
}
// reset btn disables
$
(
'.vm-list-table tbody tr .btn'
).
attr
(
'disabled'
,
false
);
// show/hide group controls
if
(
selected
.
length
>
0
)
{
$
(
'.vm-list-group-control a'
).
attr
(
'disabled'
,
false
);
for
(
var
i
=
0
;
i
<
selected
.
length
;
i
++
)
{
$
(
'.vm-list-table tbody tr'
).
eq
(
selected
[
i
]).
find
(
'.btn'
).
attr
(
'disabled'
,
true
);
}
$
(
'#vm-mass-ops .mass-operation'
).
attr
(
'disabled'
,
false
);
}
else
{
$
(
'
.vm-list-group-control a
'
).
attr
(
'disabled'
,
true
);
$
(
'
#vm-mass-ops .mass-operation
'
).
attr
(
'disabled'
,
true
);
}
return
retval
;
});
$
(
'#vm-list-group-migrate'
).
click
(
function
()
{
// pass?
});
$
(
'.vm-list-details'
).
popover
({
'placement'
:
'auto'
,
'html'
:
true
,
'trigger'
:
'hover'
});
$
(
'.vm-list-connect'
).
popover
({
'placement'
:
'left'
,
'html'
:
true
,
'trigger'
:
'click'
});
$
(
'tbody a'
).
mousedown
(
function
(
e
)
{
// parent tr doesn't get selected when clicked
e
.
stopPropagation
();
});
$
(
'tbody a'
).
click
(
function
(
e
)
{
// browser doesn't jump to top when clicked the buttons
if
(
!
$
(
this
).
hasClass
(
'real-link'
))
{
return
false
;
}
});
/* rename */
$
(
"#vm-list-rename-button, .vm-details-rename-button"
).
click
(
function
()
{
$
(
"#vm-list-column-name"
,
$
(
this
).
closest
(
"tr"
)).
hide
();
$
(
"#vm-list-rename"
,
$
(
this
).
closest
(
"tr"
)).
css
(
'display'
,
'inline'
);
$
(
"#vm-list-rename-name"
,
$
(
this
).
closest
(
"tr"
)).
focus
();
});
/* rename ajax */
$
(
'.vm-list-rename-submit'
).
click
(
function
()
{
var
row
=
$
(
this
).
closest
(
"tr"
)
var
name
=
$
(
'#vm-list-rename-name'
,
row
).
val
();
var
url
=
'/dashboard/vm/'
+
row
.
children
(
"td:first-child"
).
text
().
replace
(
" "
,
""
)
+
'/'
;
$
.
ajax
({
method
:
'POST'
,
url
:
url
,
data
:
{
'new_name'
:
name
},
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
data
,
textStatus
,
xhr
)
{
$
(
"#vm-list-column-name"
,
row
).
html
(
$
(
"<a/>"
,
{
'class'
:
"real-link"
,
href
:
"/dashboard/vm/"
+
data
[
'vm_pk'
]
+
"/"
,
text
:
data
[
'new_name'
]
})
).
show
();
$
(
'#vm-list-rename'
,
row
).
hide
();
// addMessage(data['message'], "success");
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
"Error during renaming!"
,
"danger"
);
}
});
return
false
;
});
/* group actions */
/* select all */
...
...
@@ -133,27 +68,69 @@ $(function() {
$
(
'.vm-list-table tbody tr'
).
each
(
function
()
{
var
index
=
$
(
this
).
index
();
var
vm
=
$
(
this
).
data
(
"vm-pk"
);
if
(
!
isAlreadySelected
(
vm
))
{
if
(
vm
&&
!
isAlreadySelected
(
vm
))
{
selected
.
push
({
'index'
:
index
,
'vm'
:
vm
});
$
(
this
).
addClass
(
'vm-list-selected'
);
}
});
if
(
selected
.
length
>
0
)
$
(
'
.vm-list-group-control a
'
).
attr
(
'disabled'
,
false
);
$
(
'
#vm-mass-ops .mass-operation
'
).
attr
(
'disabled'
,
false
);
return
false
;
});
/* mass vm delete */
$
(
'#vm-list-group-delete'
).
click
(
function
()
{
addModalConfirmation
(
massDeleteVm
,
{
'url'
:
'/dashboard/vm/mass-delete/'
,
'data'
:
{
'selected'
:
selected
,
'v'
:
collectIds
(
selected
)
/* mass operations */
$
(
"#vm-mass-ops"
).
on
(
'click'
,
'.mass-operation'
,
function
(
e
)
{
var
icon
=
$
(
this
).
children
(
"i"
).
addClass
(
'fa-spinner fa-spin'
);
params
=
"?"
+
selected
.
map
(
function
(
a
){
return
"vm="
+
a
.
vm
}).
join
(
"&"
);
$
.
ajax
({
type
:
'GET'
,
url
:
$
(
this
).
attr
(
'href'
)
+
params
,
success
:
function
(
data
)
{
icon
.
removeClass
(
"fa-spinner fa-spin"
);
$
(
'body'
).
append
(
data
);
$
(
'#confirmation-modal'
).
modal
(
'show'
);
$
(
'#confirmation-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#confirmation-modal'
).
remove
();
});
$
(
"[title]"
).
tooltip
({
'placement'
:
"left"
});
}
});
return
false
;
});
$
(
"body"
).
on
(
"click"
,
"#op-form-send"
,
function
()
{
var
url
=
$
(
this
).
closest
(
"form"
).
prop
(
"action"
);
$
(
this
).
find
(
"i"
).
prop
(
"class"
,
"fa fa-fw fa-spinner fa-spin"
);
$
.
ajax
({
url
:
url
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
type
:
'POST'
,
data
:
$
(
this
).
closest
(
'form'
).
serialize
(),
success
:
function
(
data
,
textStatus
,
xhr
)
{
/* hide the modal we just submitted */
$
(
'#confirmation-modal'
).
modal
(
"hide"
);
updateStatuses
(
1
);
/* if there are messages display them */
if
(
data
.
messages
&&
data
.
messages
.
length
>
0
)
{
addMessage
(
data
.
messages
.
join
(
"<br />"
),
"danger"
);
}
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
$
(
'#confirmation-modal'
).
modal
(
"hide"
);
if
(
xhr
.
status
==
500
)
{
addMessage
(
"500 Internal Server Error"
,
"danger"
);
}
else
{
addMessage
(
xhr
.
status
+
" "
+
xhr
.
statusText
,
"danger"
);
}
);
}
});
return
false
;
});
...
...
@@ -181,8 +158,65 @@ $(function() {
$
(
".vm-list-table th a"
).
on
(
"click"
,
function
(
event
)
{
event
.
preventDefault
();
});
$
(
document
).
on
(
"click"
,
".mass-migrate-node"
,
function
()
{
$
(
this
).
find
(
'input[type="radio"]'
).
prop
(
"checked"
,
true
);
});
if
(
checkStatusUpdate
())
{
updateStatuses
(
1
);
}
});
function
checkStatusUpdate
()
{
icons
=
$
(
"#vm-list-table tbody td.state i"
);
if
(
icons
.
hasClass
(
"fa-spin"
)
||
icons
.
hasClass
(
"migrating-icon"
))
{
return
true
;
}
}
function
updateStatuses
(
runs
)
{
$
.
get
(
"/dashboard/vm/list/?compact"
,
function
(
result
)
{
$
(
"#vm-list-table tbody tr"
).
each
(
function
()
{
vm
=
$
(
this
).
data
(
"vm-pk"
);
status_td
=
$
(
this
).
find
(
"td.state"
);
status_icon
=
status_td
.
find
(
"i"
);
status_text
=
status_td
.
find
(
"span"
);
if
(
vm
in
result
)
{
if
(
result
[
vm
].
in_status_change
)
{
if
(
!
status_icon
.
hasClass
(
"fa-spin"
))
{
status_icon
.
prop
(
"class"
,
"fa fa-fw fa-spinner fa-spin"
);
}
}
else
if
(
result
[
vm
].
status
==
"MIGRATING"
)
{
if
(
!
status_icon
.
hasClass
(
"migrating-icon"
))
{
status_icon
.
prop
(
"class"
,
"fa fa-fw "
+
result
[
vm
].
icon
+
" migrating-icon"
);
}
}
else
{
status_icon
.
prop
(
"class"
,
"fa fa-fw "
+
result
[
vm
].
icon
);
}
status_text
.
text
(
result
[
vm
].
status
);
if
(
"node"
in
result
[
vm
])
{
$
(
this
).
find
(
".node"
).
text
(
result
[
vm
].
node
);
}
}
else
{
$
(
this
).
remove
();
}
});
if
(
checkStatusUpdate
())
{
setTimeout
(
function
()
{
updateStatuses
(
runs
+
1
)},
1000
+
Math
.
exp
(
runs
*
0.05
)
);
}
});
}
function
isAlreadySelected
(
vm
)
{
for
(
var
i
=
0
;
i
<
selected
.
length
;
i
++
)
if
(
selected
[
i
].
vm
==
vm
)
...
...
circle/dashboard/templates/dashboard/_notifications-timeline.html
View file @
15d3d135
{% load i18n %}
{% load hro %}
{% for n in notifications %}
<li
class=
"notification-message"
id=
"msg-{{n.id}}"
>
<span
class=
"notification-message-subject"
>
{% if n.status == "new" %}
<i
class=
"fa fa-envelope-o"
></i>
{% endif %}
{{ n.subject
.get_user_text
}}
{{ n.subject
|get_text:user
}}
</span>
<span
class=
"notification-message-date pull-right"
title=
"{{n.created}}"
>
{{ n.created|timesince }}
</span>
<div
style=
"clear: both;"
></div>
<div
class=
"notification-message-text"
>
{{ n.message
.get_user_text
|safe }}
{{ n.message
|get_text:user
|safe }}
</div>
</li>
{% empty %}
...
...
circle/dashboard/templates/dashboard/_vm-mass-migrate.html
0 → 100644
View file @
15d3d135
{% extends "dashboard/mass-operate.html" %}
{% load i18n %}
{% load sizefieldtags %}
{% block formfields %}
<hr
/>
<ul
id=
"vm-migrate-node-list"
class=
"list-unstyled"
>
<li
class=
"panel panel-default panel-primary mass-migrate-node"
>
<div
class=
"panel-body"
>
<label
for=
"migrate-to-none"
>
<strong>
{% trans "Reschedule" %}
</strong>
</label>
<input
id=
"migrate-to-none"
type=
"radio"
name=
"node"
value=
""
style=
"float: right;"
checked=
"checked"
>
<span
class=
"vm-migrate-node-property"
>
{% trans "This option will reschedule each virtual machine to the optimal node." %}
</span>
<div
style=
"clear: both;"
></div>
</div>
</li>
{% for n in nodes %}
<li
class=
"panel panel-default mass-migrate-node"
>
<div
class=
"panel-body"
>
<label
for=
"migrate-to-{{n.pk}}"
>
<strong>
{{ n }}
</strong>
</label>
<input
id=
"migrate-to-{{n.pk}}"
type=
"radio"
name=
"node"
value=
"{{ n.pk }}"
style=
"float: right;"
/>
<span
class=
"vm-migrate-node-property"
>
{% trans "CPU load" %}: {{ n.cpu_usage }}
</span>
<span
class=
"vm-migrate-node-property"
>
{% trans "RAM usage" %}: {{ n.byte_ram_usage|filesize }}/{{ n.ram_size|filesize }}
</span>
<div
style=
"clear: both;"
></div>
</div>
</li>
{% endfor %}
</ul>
<hr
/>
{% endblock %}
circle/dashboard/templates/dashboard/instanceactivity_detail.html
View file @
15d3d135
{% extends "dashboard/base.html" %}
{% load i18n %}
{% load hro %}
{% block content %}
<div
class=
"body-content"
>
<div
class=
"page-header"
>
<h1><i
class=
"fa fa-{{icon}}"
></i>
{{ object.instance.name }}:
{% if user.is_superuser %}
{{object.readable_name.get_admin_text}}
{% else %}
{{object.readable_name.get_user_text}}
{% endif %}
{{ object.instance.name }}: {{object.readable_name|get_text:user}}
</h1>
</div>
<div
class=
"row"
>
...
...
@@ -58,7 +54,7 @@
<dt>
{% trans "result" %}
</dt>
<dd><textarea
class=
"form-control"
>
{
% if user.is_superuser %}{{object.result.get_admin_text}}{% else %}{{object.result.get_user_text}}{% endif %
}
</textarea></dd>
<dd><textarea
class=
"form-control"
>
{
{object.result|get_text:user}
}
</textarea></dd>
<dt>
{% trans "resultant state" %}
</dt>
<dd>
{{object.resultant_state|default:'n/a'}}
</dd>
...
...
circle/dashboard/templates/dashboard/lease-create.html
View file @
15d3d135
...
...
@@ -11,7 +11,7 @@
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<a
class=
"pull-right btn btn-default btn-xs"
href=
"{% url "
dashboard
.
views
.
template-list
"
%}"
>
{% trans "Back" %}
</a>
<h3
class=
"no-margin"
><i
class=
"fa fa-
time
"
></i>
{% trans "Create lease" %}
</h3>
<h3
class=
"no-margin"
><i
class=
"fa fa-
clock-o
"
></i>
{% trans "Create lease" %}
</h3>
</div>
<div
class=
"panel-body"
>
{% with form=form %}
...
...
circle/dashboard/templates/dashboard/lease-edit.html
View file @
15d3d135
...
...
@@ -10,7 +10,7 @@
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<a
class=
"pull-right btn btn-default btn-xs"
href=
"{% url "
dashboard
.
views
.
template-list
"
%}"
>
{% trans "Back" %}
</a>
<h3
class=
"no-margin"
><i
class=
"fa fa-
time
"
></i>
{% trans "Edit lease" %}
</h3>
<h3
class=
"no-margin"
><i
class=
"fa fa-
clock-o
"
></i>
{% trans "Edit lease" %}
</h3>
</div>
<div
class=
"panel-body"
>
{% with form=form %}
...
...
circle/dashboard/templates/dashboard/mass-operate.html
0 → 100644
View file @
15d3d135
{% load i18n %}
{% load crispy_forms_tags %}
{% block question %}
<p>
{% blocktrans with op=op.name count count=vm_count %}
Do you want to perform the
<strong>
{{op}}
</strong>
operation on the following instance?
{% plural %}
Do you want to perform the
<strong>
{{op}}
</strong>
operation on the following {{ count }} instances?
{% endblocktrans %}
</p>
<p
class=
"text-info"
>
{{op.description}}
</p>
{% endblock %}
<form
method=
"POST"
action=
"{{url}}"
>
{% csrf_token %}
{% block formfields %}{% endblock %}
{% for i in instances %}
<div
class=
"panel panel-default mass-op-panel"
>
<i
class=
"fa {{ i.get_status_icon }} fa-fw"
></i>
{{ i.name }} ({{ i.pk }})
<div
style=
"float: right;"
title=
"{{ i.disabled }}"
class=
"status-icon"
>
<span
class=
"fa-stack"
>
<i
class=
"fa fa-stack-2x fa-square {{ i.disabled|yesno:"
minus
,
check
"
}}"
></i>
<i
class=
"fa fa-stack-1x fa-inverse fa-{% if i.disabled %}{{i.disabled_icon|default:"
minus
"}}{%
else
%}
check
{%
endif
%}"
></i>
</span>
</div>
</div>
<input
type=
"checkbox"
name=
"vm"
value=
"{{ i.pk }}"
{%
if
not
i
.
disabled
%}
checked
{%
endif
%}
style=
"display: none;"
/>
{% endfor %}
<div
class=
"pull-right"
>
<a
class=
"btn btn-default"
href=
"{% url "
dashboard
.
views
.
vm-list
"
%}"
data-dismiss=
"modal"
>
{% trans "Cancel" %}
</a>
<button
class=
"btn btn-{{ opview.effect }}"
type=
"submit"
id=
"op-form-send"
>
{% if opview.icon %}
<i
class=
"fa fa-fw fa-{{opview.icon}}"
></i>
{% endif %}{{ opview.name|capfirst }}
</button>
</div>
</form>
circle/dashboard/templates/dashboard/node-detail/_activity-timeline.html
View file @
15d3d135
{% load i18n %}
{% load hro %}
<div
id=
"activity-timeline"
class=
"timeline"
>
{% for a in activities %}
<div
class=
"activity"
data-activity-id=
"{{ a.pk }}"
>
...
...
@@ -16,10 +17,7 @@
<div
data-activity-id=
"{{ s.pk }}"
class=
"sub-activity{% if s.has_failed %} sub-activity-failed{% endif %}"
>
{% if user.is_superuser %}
{{ s.readable_name.get_admin_text }}
{% else %}
{{ s.readable_name.get_user_text }}{% endif %}
{{ s.readable_name|get_text:user }}
–
{% if s.finished %}
{{ s.finished|time:"H:i:s" }}
...
...
circle/dashboard/templates/dashboard/template-list.html
View file @
15d3d135
...
...
@@ -31,7 +31,7 @@
<i
class=
"fa fa-plus"
></i>
{% trans "new lease" %}
</a>
{% endif %}
<h3
class=
"no-margin"
><i
class=
"fa fa-
time
"
></i>
{% trans "Leases" %}
</h3>
<h3
class=
"no-margin"
><i
class=
"fa fa-
clock-o
"
></i>
{% trans "Leases" %}
</h3>
</div>
<div
class=
"panel-body"
>
<div
class=
""
style=
"max-width: 600px;"
>
...
...
circle/dashboard/templates/dashboard/vm-detail/_activity-timeline.html
View file @
15d3d135
{% load i18n %}
{% load hro %}
<div
id=
"activity-timeline"
class=
"timeline"
>
{% for a in activities %}
...
...
@@ -7,10 +7,10 @@
<span
class=
"timeline-icon{% if a.has_failed %} timeline-icon-failed{% endif %}"
>
<i
class=
"fa {% if not a.finished %}fa-refresh fa-spin {% else %}fa-{{a.icon}}{% endif %}"
></i>
</span>
<strong
{%
if
a
.
result
%}
title=
"{{ a.result
.get_user_text
}}"
{%
endif
%}
>
<strong
{%
if
a
.
result
%}
title=
"{{ a.result
|get_text:user
}}"
{%
endif
%}
>
<a
href=
"{{ a.get_absolute_url }}"
>
{% if a.times > 1 %}({{ a.times }}x){% endif %}
{{ a.readable_name
.get_user_text
|capfirst }}
</a>
{{ a.readable_name
|get_text:user
|capfirst }}
</a>
{% if a.has_percent %}
- {{ a.percentage }}%
...
...
@@ -33,9 +33,9 @@
<div
class=
"sub-timeline"
>
{% for s in a.children.all %}
<div
data-activity-id=
"{{ s.pk }}"
class=
"sub-activity{% if s.has_failed %} sub-activity-failed{% endif %}{% if s.pk == active.pk %} sub-activity-active{% endif %}"
>
<span
{%
if
s
.
result
%}
title=
"{{ s.result
.get_user_text
}}"
{%
endif
%}
>
<span
{%
if
s
.
result
%}
title=
"{{ s.result
|get_text:user
}}"
{%
endif
%}
>
<a
href=
"{{ s.get_absolute_url }}"
>
{{ s.readable_name
.get_user_text
|capfirst }}
</a></span>
–
{{ s.readable_name
|get_text:user
|capfirst }}
</a></span>
–
{% if s.finished %}
{{ s.finished|time:"H:i:s" }}
{% else %}
...
...
circle/dashboard/templates/dashboard/vm-list.html
View file @
15d3d135
...
...
@@ -15,7 +15,19 @@
</div>
<h3
class=
"no-margin"
><i
class=
"fa fa-desktop"
></i>
{% trans "Virtual machines" %}
</h3>
</div>
<div
class=
"pull-right"
style=
"max-width: 300px; margin-top: 15px; margin-right: 15px;"
>
<div
class=
"panel-body"
>
<div
class=
"row"
>
<div
class=
"col-md-8 vm-list-group-control"
id=
"vm-mass-ops"
>
<strong>
{% trans "Group actions" %}
</strong>
<button
id=
"vm-list-group-select-all"
class=
"btn btn-info btn-xs"
>
{% trans "Select all" %}
</button>
{% for o in ops %}
<a
href=
"{{ o.get_url }}"
class=
"btn btn-xs btn-{{ o.effect }} mass-operation"
title=
"{{ o.name|capfirst }}"
disabled
>
<i
class=
"fa fa-{{ o.icon }}"
></i>
</a>
{% endfor %}
</div>
<!-- .vm-list-group-control -->
<div
class=
"col-md-4"
id=
"vm-list-search"
>
<form
action=
""
method=
"GET"
>
<div
class=
"input-group"
>
{{ search_form.s }}