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
7fed7d2c
authored
Jan 01, 2015
by
Bach Dániel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature-remove-garbage'
parents
32e83265
f876f926
Hide whitespace changes
Inline
Side-by-side
Showing
39 changed files
with
477 additions
and
1369 deletions
+477
-1369
circle/circle/settings/base.py
+1
-0
circle/common/operations.py
+2
-0
circle/dashboard/forms.py
+31
-1
circle/dashboard/static/dashboard/activity.js
+116
-4
circle/dashboard/static/dashboard/dashboard.js
+27
-171
circle/dashboard/static/dashboard/group-details.js
+0
-36
circle/dashboard/static/dashboard/group-list.js
+3
-3
circle/dashboard/static/dashboard/node-create.js
+0
-10
circle/dashboard/static/dashboard/node-details.js
+0
-33
circle/dashboard/static/dashboard/node-list.js
+0
-45
circle/dashboard/static/dashboard/template-list.js
+0
-86
circle/dashboard/static/dashboard/vm-create.js
+16
-18
circle/dashboard/static/dashboard/vm-details.js
+1
-219
circle/dashboard/templates/dashboard/confirm/ajax-delete.html
+14
-8
circle/dashboard/templates/dashboard/confirm/ajax-node-status.html
+0
-27
circle/dashboard/templates/dashboard/confirm/ajax-remove.html
+0
-22
circle/dashboard/templates/dashboard/confirm/base-delete.html
+10
-4
circle/dashboard/templates/dashboard/confirm/base-remove.html
+0
-14
circle/dashboard/templates/dashboard/confirm/base-renew.html
+0
-32
circle/dashboard/templates/dashboard/confirm/mass-delete.html
+0
-18
circle/dashboard/templates/dashboard/confirm/node-flush.html
+0
-33
circle/dashboard/templates/dashboard/confirm/node-status.html
+0
-35
circle/dashboard/templates/dashboard/group-list/column-name.html
+1
-1
circle/dashboard/templates/dashboard/index-templates.html
+1
-1
circle/dashboard/templates/dashboard/modal-wrapper.html
+0
-19
circle/dashboard/templates/dashboard/node-detail.html
+2
-1
circle/dashboard/templates/dashboard/node-detail/_activity-timeline.html
+30
-29
circle/dashboard/templates/dashboard/node-detail/activity.html
+1
-1
circle/dashboard/templates/dashboard/vm-detail.html
+2
-1
circle/dashboard/templates/dashboard/vm-detail/network.html
+14
-7
circle/dashboard/tests/test_views.py
+22
-96
circle/dashboard/urls.py
+7
-8
circle/dashboard/views/group.py
+20
-78
circle/dashboard/views/node.py
+28
-87
circle/dashboard/views/template.py
+19
-95
circle/dashboard/views/user.py
+10
-48
circle/dashboard/views/util.py
+43
-1
circle/dashboard/views/vm.py
+50
-73
circle/vm/models/instance.py
+6
-4
No files found.
circle/circle/settings/base.py
View file @
7fed7d2c
...
...
@@ -198,6 +198,7 @@ PIPELINE_JS = {
"jquery-knob/dist/jquery.knob.min.js"
,
"jquery-simple-slider/js/simple-slider.js"
,
"dashboard/dashboard.js"
,
"dashboard/activity.js"
,
"dashboard/group-details.js"
,
"dashboard/group-list.js"
,
"dashboard/js/stupidtable.min.js"
,
# no bower file
...
...
circle/common/operations.py
View file @
7fed7d2c
...
...
@@ -282,6 +282,8 @@ def register_operation(op_cls, op_id=None, target_cls=None):
"in the 'target_cls' parameter to this "
"call."
)
assert
not
hasattr
(
target_cls
,
op_id
),
(
"target class already has an attribute with this id"
)
if
not
issubclass
(
target_cls
,
OperatedMixin
):
raise
TypeError
(
"
%
r is not a subclass of
%
r"
%
(
target_cls
.
__name__
,
OperatedMixin
.
__name__
))
...
...
circle/dashboard/forms.py
View file @
7fed7d2c
...
...
@@ -898,7 +898,7 @@ class VmDownloadDiskForm(OperationForm):
def
clean
(
self
):
cleaned_data
=
super
(
VmDownloadDiskForm
,
self
)
.
clean
()
if
not
cleaned_data
[
'name'
]:
if
cleaned_data
[
'url'
]
:
if
cleaned_data
.
get
(
'url'
)
:
cleaned_data
[
'name'
]
=
urlparse
(
cleaned_data
[
'url'
])
.
path
.
split
(
'/'
)[
-
1
]
if
not
cleaned_data
[
'name'
]:
...
...
@@ -908,6 +908,36 @@ class VmDownloadDiskForm(OperationForm):
return
cleaned_data
class
VmRemoveInterfaceForm
(
OperationForm
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
choices
=
kwargs
.
pop
(
'choices'
)
self
.
interface
=
kwargs
.
pop
(
'default'
)
super
(
VmRemoveInterfaceForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
fields
.
insert
(
0
,
'interface'
,
forms
.
ModelChoiceField
(
queryset
=
choices
,
initial
=
self
.
interface
,
required
=
True
,
empty_label
=
None
,
label
=
_
(
'Interface'
)))
if
self
.
interface
:
self
.
fields
[
'interface'
]
.
widget
=
HiddenInput
()
@property
def
helper
(
self
):
helper
=
super
(
VmRemoveInterfaceForm
,
self
)
.
helper
if
self
.
interface
:
helper
.
layout
=
Layout
(
AnyTag
(
"div"
,
HTML
(
format_html
(
_
(
"<label>Vlan:</label> {0}"
),
self
.
interface
.
vlan
)),
css_class
=
"form-group"
,
),
Field
(
"interface"
),
)
return
helper
class
VmAddInterfaceForm
(
OperationForm
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
choices
=
kwargs
.
pop
(
'choices'
)
...
...
circle/dashboard/static/dashboard/
vm-common
.js
→
circle/dashboard/static/dashboard/
activity
.js
View file @
7fed7d2c
/* for functions in both vm list and vm detail */
$
(
function
()
{
var
in_progress
=
false
;
var
activity_hash
=
5
;
var
show_all
=
false
;
var
reload_vm_detail
=
false
;
/* do we need to check for new activities */
if
(
decideActivityRefresh
())
{
if
(
!
in_progress
)
{
checkNewActivity
(
1
);
in_progress
=
true
;
}
}
/* vm operations */
$
(
'a[href="#activity"]'
).
click
(
function
(){
$
(
'a[href="#activity"] i'
).
addClass
(
'fa-spin'
);
if
(
!
in_progress
)
{
checkNewActivity
(
1
);
in_progress
=
true
;
}
});
$
(
"#activity-refresh"
).
on
(
"click"
,
"#show-all-activities"
,
function
()
{
$
(
this
).
find
(
"i"
).
addClass
(
"fa-spinner fa-spin"
);
show_all
=
!
show_all
;
$
(
'a[href="#activity"]'
).
trigger
(
"click"
);
return
false
;
});
/* operations */
$
(
'#ops, #vm-details-resources-disk, #vm-details-renew-op, #vm-details-pw-reset, #vm-details-add-interface, .operation-wrapper'
).
on
(
'click'
,
'.operation'
,
function
(
e
)
{
var
icon
=
$
(
this
).
children
(
"i"
).
addClass
(
'fa-spinner fa-spin'
);
...
...
@@ -23,7 +48,7 @@ $(function() {
});
/* if the operation fails show the modal again */
$
(
"body"
).
on
(
"click"
,
"#op-form-send"
,
function
()
{
$
(
"body"
).
on
(
"click"
,
"#
confirmation-modal #
op-form-send"
,
function
()
{
var
url
=
$
(
this
).
closest
(
"form"
).
prop
(
"action"
);
$
.
ajax
({
...
...
@@ -77,4 +102,91 @@ $(function() {
return
false
;
});
function
decideActivityRefresh
()
{
var
check
=
false
;
/* if something is still spinning */
if
(
$
(
'.timeline .activity i'
).
hasClass
(
'fa-spin'
))
check
=
true
;
return
check
;
}
function
checkNewActivity
(
runs
)
{
$
.
ajax
({
type
:
'GET'
,
url
:
$
(
'a[href="#activity"]'
).
attr
(
'data-activity-url'
),
data
:
{
'show_all'
:
show_all
},
success
:
function
(
data
)
{
var
new_activity_hash
=
(
data
.
activities
+
""
).
hashCode
();
if
(
new_activity_hash
!=
activity_hash
)
{
$
(
"#activity-refresh"
).
html
(
data
.
activities
);
}
activity_hash
=
new_activity_hash
;
$
(
"#ops"
).
html
(
data
.
ops
);
$
(
"#disk-ops"
).
html
(
data
.
disk_ops
);
$
(
"[title]"
).
tooltip
();
/* changing the status text */
var
icon
=
$
(
"#vm-details-state i"
);
if
(
data
.
is_new_state
)
{
if
(
!
icon
.
hasClass
(
"fa-spin"
))
icon
.
prop
(
"class"
,
"fa fa-spinner fa-spin"
);
}
else
{
icon
.
prop
(
"class"
,
"fa "
+
data
.
icon
);
}
var
vm_state
=
$
(
"#vm-details-state"
);
if
(
vm_state
.
length
)
{
vm_state
.
data
(
"status"
,
data
[
'status'
]);
$
(
"#vm-details-state span"
).
html
(
data
[
'human_readable_status'
].
toUpperCase
());
}
if
(
data
[
'status'
]
==
"RUNNING"
)
{
if
(
data
[
'connect_uri'
])
{
$
(
"#dashboard-vm-details-connect-button"
).
removeClass
(
'disabled'
);
}
$
(
"[data-target=#_console]"
).
attr
(
"data-toggle"
,
"pill"
).
attr
(
"href"
,
"#console"
).
parent
(
"li"
).
removeClass
(
"disabled"
);
}
else
{
if
(
data
[
'connect_uri'
])
{
$
(
"#dashboard-vm-details-connect-button"
).
addClass
(
'disabled'
);
}
$
(
"[data-target=#_console]"
).
attr
(
"data-toggle"
,
"_pill"
).
attr
(
"href"
,
"#"
).
parent
(
"li"
).
addClass
(
"disabled"
);
}
if
(
data
.
status
==
"STOPPED"
||
data
.
status
==
"PENDING"
)
{
$
(
".change-resources-button"
).
prop
(
"disabled"
,
false
);
$
(
".change-resources-help"
).
hide
();
}
else
{
$
(
".change-resources-button"
).
prop
(
"disabled"
,
true
);
$
(
".change-resources-help"
).
show
();
}
if
(
runs
>
0
&&
decideActivityRefresh
())
{
setTimeout
(
function
()
{
checkNewActivity
(
runs
+
1
);},
1000
+
Math
.
exp
(
runs
*
0.05
)
);
}
else
{
in_progress
=
false
;
if
(
reload_vm_detail
)
location
.
reload
();
}
$
(
'a[href="#activity"] i'
).
removeClass
(
'fa-spin'
);
},
error
:
function
()
{
in_progress
=
false
;
}
});
}
});
String
.
prototype
.
hashCode
=
function
()
{
var
hash
=
0
,
i
,
chr
,
len
;
if
(
this
.
length
==
0
)
return
hash
;
for
(
i
=
0
,
len
=
this
.
length
;
i
<
len
;
i
++
)
{
chr
=
this
.
charCodeAt
(
i
);
hash
=
((
hash
<<
5
)
-
hash
)
+
chr
;
hash
|=
0
;
// Convert to 32bit integer
}
return
hash
;
};
circle/dashboard/static/dashboard/dashboard.js
View file @
7fed7d2c
...
...
@@ -5,63 +5,39 @@ $(function () {
var
template
=
$
(
this
).
data
(
"template"
);
$
.
ajax
({
type
:
'GET'
,
url
:
'/dashboard/vm/create/'
+
(
typeof
template
===
"undefined"
?
''
:
'?template='
+
template
),
url
:
$
(
this
).
attr
(
'href'
),
success
:
function
(
data
)
{
$
(
'body'
).
append
(
data
);
vmCreateLoaded
();
addSliderMiscs
();
$
(
'#create-modal'
).
modal
(
'show'
);
$
(
'#create-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#create-modal'
).
remove
();
var
modal
=
$
(
'#confirmation-modal'
);
modal
.
modal
(
'show'
);
modal
.
on
(
'hidden.bs.modal'
,
function
()
{
modal
.
remove
();
});
}
});
return
false
;
});
$
(
'.
node-create
'
).
click
(
function
(
e
)
{
$
(
'.
group-create, .node-create, .tx-tpl-ownership, .group-delete, .node-delete, .disk-remove, .template-delete, .delete-from-group
'
).
click
(
function
(
e
)
{
$
.
ajax
({
type
:
'GET'
,
url
:
'/dashboard/node/create/'
,
url
:
$
(
this
).
prop
(
'href'
)
,
success
:
function
(
data
)
{
$
(
'body'
).
append
(
data
);
nodeCreateLoaded
();
addSliderMiscs
();
$
(
'#create-modal'
).
modal
(
'show'
);
$
(
'#create-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#create-modal'
).
remove
();
});
}
});
return
false
;
});
$
(
'.group-create'
).
click
(
function
(
e
)
{
$
.
ajax
({
type
:
'GET'
,
url
:
'/dashboard/group/create/'
,
success
:
function
(
data
)
{
$
(
'body'
).
append
(
data
);
addSliderMiscs
();
$
(
'#create-modal'
).
modal
(
'show'
);
$
(
'#create-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#create-modal'
).
remove
();
});
}
});
return
false
;
});
$
(
'.tx-tpl-ownership'
).
click
(
function
(
e
)
{
$
.
ajax
({
type
:
'GET'
,
url
:
$
(
'.tx-tpl-ownership'
).
attr
(
'href'
),
success
:
function
(
data
)
{
$
(
'body'
).
append
(
data
);
$
(
'#confirmation-modal'
).
modal
(
'show'
);
$
(
'#confirmation-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#confirmation-modal'
).
remove
();
var
modal
=
$
(
'#confirmation-modal'
);
modal
.
modal
(
'show'
);
modal
.
on
(
'hidden.bs.modal'
,
function
()
{
modal
.
remove
();
});
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
if
(
xhr
.
status
===
403
)
{
addMessage
(
gettext
(
"Only the owners can delete the selected object."
),
"warning"
);
}
else
{
addMessage
(
gettext
(
"An error occurred. ("
)
+
xhr
.
status
+
")"
,
'danger'
)
}
}
});
return
false
;
...
...
@@ -70,12 +46,13 @@ $(function () {
$
(
'.template-choose'
).
click
(
function
(
e
)
{
$
.
ajax
({
type
:
'GET'
,
url
:
'/dashboard/template/choose/'
,
url
:
$
(
this
).
prop
(
'href'
)
,
success
:
function
(
data
)
{
$
(
'body'
).
append
(
data
);
$
(
'#create-modal'
).
modal
(
'show'
);
$
(
'#create-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#create-modal'
).
remove
();
var
modal
=
$
(
'#confirmation-modal'
);
modal
.
modal
(
'show'
);
modal
.
on
(
'hidden.bs.modal'
,
function
()
{
modal
.
remove
();
});
// check if user selected anything
$
(
"#template-choose-next-button"
).
click
(
function
()
{
...
...
@@ -101,6 +78,7 @@ $(function () {
e
.
stopImmediatePropagation
();
return
false
;
});
$
(
'[href=#index-list-view]'
).
click
(
function
(
e
)
{
var
box
=
$
(
this
).
data
(
'index-box'
);
$
(
'#'
+
box
+
'-graph-view'
).
hide
();
...
...
@@ -110,9 +88,10 @@ $(function () {
e
.
stopImmediatePropagation
();
return
false
;
});
$
(
'body [title]:not(.title-favourite)'
).
tooltip
();
$
(
'body .title-favourite'
).
tooltip
({
'placement'
:
'right'
});
$
(
'body :input[title]'
).
tooltip
({
trigger
:
'focus'
,
placement
:
'auto right'
});
$
(
'body [title]'
).
tooltip
();
$
(
".knob"
).
knob
();
$
(
'[data-toggle="pill"]'
).
click
(
function
()
{
...
...
@@ -167,74 +146,6 @@ $(function () {
addSliderMiscs
();
/* for VM removes buttons */
$
(
'.vm-delete'
).
click
(
function
()
{
var
vm_pk
=
$
(
this
).
data
(
'vm-pk'
);
var
dir
=
window
.
location
.
pathname
.
indexOf
(
'list'
)
==
-
1
;
addModalConfirmation
(
deleteObject
,
{
'url'
:
'/dashboard/vm/delete/'
+
vm_pk
+
'/'
,
'data'
:
[],
'pk'
:
vm_pk
,
'type'
:
"vm"
,
'redirect'
:
dir
});
return
false
;
});
/* for disk remove buttons */
$
(
'.disk-remove'
).
click
(
function
()
{
var
disk_pk
=
$
(
this
).
data
(
'disk-pk'
);
addModalConfirmation
(
deleteObject
,
{
'url'
:
'/dashboard/disk/'
+
disk_pk
+
'/remove/'
,
'data'
:
[],
'pk'
:
disk_pk
,
'type'
:
"disk"
,
});
return
false
;
});
/* for Node removes buttons */
$
(
'.node-delete'
).
click
(
function
()
{
var
node_pk
=
$
(
this
).
data
(
'node-pk'
);
var
dir
=
window
.
location
.
pathname
.
indexOf
(
'list'
)
==
-
1
;
addModalConfirmation
(
deleteObject
,
{
'url'
:
'/dashboard/node/delete/'
+
node_pk
+
'/'
,
'data'
:
[],
'pk'
:
node_pk
,
'type'
:
"node"
,
'redirect'
:
dir
});
return
false
;
});
/* for Node flush buttons */
$
(
'.node-flush'
).
click
(
function
()
{
var
node_pk
=
$
(
this
).
data
(
'node-pk'
);
var
postto
=
$
(
this
).
attr
(
'href'
);
var
dir
=
window
.
location
.
pathname
.
indexOf
(
'list'
)
==
-
1
;
addModalConfirmation
(
function
(){},
{
'url'
:
postto
,
'data'
:
[],
'pk'
:
node_pk
,
'type'
:
"node"
,
'redirect'
:
dir
});
return
false
;
});
/* for Group removes buttons */
$
(
'.group-delete'
).
click
(
function
()
{
var
group_pk
=
$
(
this
).
data
(
'group-pk'
);
var
dir
=
window
.
location
.
pathname
.
indexOf
(
'list'
)
==
-
1
;
addModalConfirmation
(
deleteObject
,
{
'url'
:
'/dashboard/group/delete/'
+
group_pk
+
'/'
,
'data'
:
[],
'type'
:
"group"
,
'pk'
:
group_pk
,
'redirect'
:
dir
});
return
false
;
});
/* search for vms */
var
my_vms
=
[];
$
(
"#dashboard-vm-search-input"
).
keyup
(
function
(
e
)
{
...
...
@@ -558,62 +469,6 @@ function setDefaultSliderValues() {
}
/* deletes the VM with the pk
* if dir is true, then redirect to the dashboard landing page
* else it adds a success message */
function
deleteObject
(
data
)
{
$
.
ajax
({
type
:
'POST'
,
data
:
{
'redirect'
:
data
.
redirect
},
url
:
data
.
url
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
re
,
textStatus
,
xhr
)
{
if
(
!
data
.
redirect
)
{
selected
=
[];
addMessage
(
re
.
message
,
'success'
);
if
(
data
.
type
===
"disk"
)
{
// no need to remove them from DOM
$
(
'a[data-disk-pk="'
+
data
.
pk
+
'"]'
).
parent
(
"li"
).
fadeOut
();
$
(
'a[data-disk-pk="'
+
data
.
pk
+
'"]'
).
parent
(
"h4"
).
fadeOut
();
}
else
{
$
(
'a[data-'
+
data
.
type
+
'-pk="'
+
data
.
pk
+
'"]'
).
closest
(
'tr'
).
fadeOut
(
function
()
{
$
(
this
).
remove
();
});
}
}
else
{
window
.
location
.
replace
(
'/dashboard'
);
}
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
'Uh oh :('
,
'danger'
);
}
});
}
function
massDeleteVm
(
data
)
{
f
=
function
()
{
selected
=
[];
// reset group buttons
$
(
'.vm-list-group-control a'
).
attr
(
'disabled'
,
true
);
$
(
this
).
remove
();
};
$
.
ajax
({
traditional
:
true
,
url
:
data
.
url
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
type
:
'POST'
,
data
:
{
'vms'
:
data
.
data
.
v
},
success
:
function
(
re
,
textStatus
,
xhr
)
{
for
(
var
i
=
0
;
i
<
data
.
data
.
v
.
length
;
i
++
)
$
(
'.vm-list-table tbody tr[data-vm-pk="'
+
data
.
data
.
v
[
i
]
+
'"]'
).
fadeOut
(
500
,
f
);
addMessage
(
re
.
message
,
'success'
);
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
// TODO this
}
});
}
function
addMessage
(
text
,
type
)
{
...
...
@@ -701,6 +556,7 @@ function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return
(
/^
(
GET|HEAD|OPTIONS|TRACE
)
$/
.
test
(
method
));
}
$
.
ajaxSetup
({
beforeSend
:
function
(
xhr
,
settings
)
{
if
(
!
csrfSafeMethod
(
settings
.
type
)
&&
!
this
.
crossDomain
)
{
...
...
circle/dashboard/static/dashboard/group-details.js
View file @
7fed7d2c
...
...
@@ -28,39 +28,3 @@
$
(
".group-details-help-button"
).
click
(
function
()
{
$
(
".group-details-help"
).
stop
().
slideToggle
();
});
/* for Node removes buttons */
$
(
'.delete-from-group'
).
click
(
function
()
{
var
href
=
$
(
this
).
attr
(
'href'
);
var
tr
=
$
(
this
).
closest
(
'tr'
);
var
group
=
$
(
this
).
data
(
'group_pk'
);
var
member
=
$
(
this
).
data
(
'member_pk'
);
var
dir
=
window
.
location
.
pathname
.
indexOf
(
'list'
)
==
-
1
;
addModalConfirmation
(
removeMember
,
{
'url'
:
href
,
'data'
:
[],
'tr'
:
tr
,
'group_pk'
:
group
,
'member_pk'
:
member
,
'type'
:
"user"
,
'redirect'
:
dir
});
return
false
;
});
function
removeMember
(
data
)
{
$
.
ajax
({
type
:
'POST'
,
url
:
data
.
url
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
re
,
textStatus
,
xhr
)
{
data
.
tr
.
fadeOut
(
function
()
{
$
(
this
).
remove
();});
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
'Uh oh :('
,
'danger'
);
}
});
}
circle/dashboard/static/dashboard/group-list.js
View file @
7fed7d2c
$
(
function
()
{
/* rename */
$
(
"#group-list-rename-button, .group-details-rename-button"
).
click
(
function
()
{
$
(
"
#
group-list-column-name"
,
$
(
this
).
closest
(
"tr"
)).
hide
();
$
(
"
.
group-list-column-name"
,
$
(
this
).
closest
(
"tr"
)).
hide
();
$
(
"#group-list-rename"
,
$
(
this
).
closest
(
"tr"
)).
css
(
'display'
,
'inline'
);
$
(
"#group-list-rename"
).
find
(
"input"
).
select
();
});
...
...
@@ -10,7 +10,7 @@ $(function() {
$
(
'.group-list-rename-submit'
).
click
(
function
()
{
var
row
=
$
(
this
).
closest
(
"tr"
);
var
name
=
$
(
'#group-list-rename-name'
,
row
).
val
();
var
url
=
'/dashboard/group/'
+
row
.
children
(
"td:first-child"
).
text
().
replace
(
" "
,
""
)
+
'/'
;
var
url
=
row
.
find
(
".group-list-column-name a"
).
prop
(
"href"
)
;
$
.
ajax
({
method
:
'POST'
,
url
:
url
,
...
...
@@ -18,7 +18,7 @@ $(function() {
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
data
,
textStatus
,
xhr
)
{
$
(
"
#
group-list-column-name"
,
row
).
html
(
$
(
"
.
group-list-column-name"
,
row
).
html
(
$
(
"<a/>"
,
{
'class'
:
"real-link"
,
href
:
"/dashboard/group/"
+
data
.
group_pk
+
"/"
,
...
...
circle/dashboard/static/dashboard/node-create.js
deleted
100644 → 0
View file @
32e83265
$
(
function
()
{
nodeCreateLoaded
();
});
function
nodeCreateLoaded
()
{
/* no js compatibility */
$
(
'.no-js-hidden'
).
show
();
$
(
'.js-hidden'
).
hide
();
}
circle/dashboard/static/dashboard/node-details.js
View file @
7fed7d2c
...
...
@@ -30,20 +30,6 @@ $(function() {
$
(
".node-details-help"
).
stop
().
slideToggle
();
});
/* for Node removes buttons */
$
(
'.node-enable'
).
click
(
function
()
{
var
node_pk
=
$
(
this
).
data
(
'node-pk'
);
var
dir
=
window
.
location
.
pathname
.
indexOf
(
'list'
)
==
-
1
;
addModalConfirmation
(
changeNodeStatus
,
{
'url'
:
'/dashboard/node/status/'
+
node_pk
+
'/'
,
'data'
:
[],
'pk'
:
node_pk
,
'type'
:
"node"
,
'redirect'
:
dir
});
return
false
;
});
// remove trait
$
(
'.node-details-remove-trait'
).
click
(
function
()
{
var
to_remove
=
$
(
this
).
data
(
"trait-pk"
);
...
...
@@ -69,22 +55,3 @@ $(function() {
});
});
function
changeNodeStatus
(
data
)
{
$
.
ajax
({
type
:
'POST'
,
url
:
data
.
url
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
re
,
textStatus
,
xhr
)
{
if
(
!
data
.
redirect
)
{
selected
=
[];
addMessage
(
re
.
message
,
'success'
);
}
else
{
window
.
location
.
replace
(
'/dashboard'
);
}
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
'Uh oh :('
,
'danger'
);
}
});
}
circle/dashboard/static/dashboard/node-list.js
View file @
7fed7d2c
...
...
@@ -9,49 +9,4 @@ $(function() {
$
(
'.false'
).
closest
(
"tr"
).
addClass
(
'danger'
);
$
(
'.true'
).
closest
(
"tr"
).
removeClass
(
'danger'
);
}
function
statuschangeSuccess
(
tr
){
var
tspan
=
tr
.
children
(
'.enabled'
).
children
();
var
buttons
=
tr
.
children
(
'.actions'
).
children
(
'.btn-group'
).
children
(
'.dropdown-menu'
).
children
(
'li'
).
children
(
'.node-enable'
);
buttons
.
each
(
function
(
index
){
if
(
$
(
this
).
css
(
"display"
)
==
"block"
){
$
(
this
).
css
(
"display"
,
"none"
);
}
else
{
$
(
this
).
css
(
"display"
,
"block"
);
}
});
if
(
tspan
.
hasClass
(
"false"
)){
tspan
.
removeClass
(
"false"
);
tspan
.
addClass
(
"true"
);
tspan
.
text
(
"✔"
);
}
else
{
tspan
.
removeClass
(
"true"
);
tspan
.
addClass
(
"false"
);
tspan
.
text
(
"✘"
);
}
colortable
();
}
$
(
'#table_container'
).
on
(
'click'
,
'.node-enable'
,
function
()
{
var
tr
=
$
(
this
).
closest
(
"tr"
);
var
pk
=
$
(
this
).
attr
(
'data-node-pk'
);
var
url
=
$
(
this
).
attr
(
'href'
);
$
.
ajax
({
method
:
'POST'
,
url
:
url
,
data
:
{
'change_status'
:
''
},
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
data
,
textStatus
,
xhr
)
{
statuschangeSuccess
(
tr
);
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
"Error!"
,
"danger"
);
}
});
return
false
;
});
});
circle/dashboard/static/dashboard/template-list.js
View file @
7fed7d2c
$
(
function
()
{
/* for template removes buttons */
$
(
'.template-delete'
).
click
(
function
()
{
var
template_pk
=
$
(
this
).
data
(
'template-pk'
);
addModalConfirmationOrDisplayMessage
(
deleteTemplate
,
{
'url'
:
'/dashboard/template/delete/'
+
template_pk
+
'/'
,
'data'
:
[],
'template_pk'
:
template_pk
,
});
return
false
;
});
/* for lease removes buttons */
$
(
'.lease-delete'
).
click
(
function
()
{
var
lease_pk
=
$
(
this
).
data
(
'lease-pk'
);
addModalConfirmationOrDisplayMessage
(
deleteLease
,
{
'url'
:
'/dashboard/lease/delete/'
+
lease_pk
+
'/'
,
'data'
:
[],
'lease_pk'
:
lease_pk
,
});
return
false
;
});
/* template table sort */
var
ttable
=
$
(
".template-list-table"
).
stupidtable
();
...
...
@@ -43,67 +21,3 @@ $(function() {
event
.
preventDefault
();
});
});
// send POST request then delete the row in table
function
deleteTemplate
(
data
)
{
$
.
ajax
({
type
:
'POST'
,
url
:
data
.
url
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
re
,
textStatus
,
xhr
)
{
addMessage
(
re
.
message
,
'success'
);
$
(
'a[data-template-pk="'
+
data
.
template_pk
+
'"]'
).
closest
(
'tr'
).
fadeOut
(
function
()
{
$
(
this
).
remove
();
});
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
'Uh oh :('
,
'danger'
);
}
});
}
// send POST request then delete the row in table
function
deleteLease
(
data
)
{
$
.
ajax
({
type
:
'POST'
,
url
:
data
.
url
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
re
,
textStatus
,
xhr
)
{
addMessage
(
re
.
message
,
'success'
);
$
(
'a[data-lease-pk="'
+
data
.
lease_pk
+
'"]'
).
closest
(
'tr'
).
fadeOut
(
function
()
{
$
(
this
).
remove
();
});
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
'Uh oh :('
,
'danger'
);
}
});
}
function
addModalConfirmationOrDisplayMessage
(
func
,
data
)
{
$
.
ajax
({
type
:
'GET'
,
url
:
data
[
'url'
],
data
:
jQuery
.
param
(
data
[
'data'
]),
success
:
function
(
result
)
{
$
(
'body'
).
append
(
result
);
$
(
'#confirmation-modal'
).
modal
(
'show'
);
$
(
'#confirmation-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#confirmation-modal'
).
remove
();
});
$
(
'#confirmation-modal-button'
).
click
(
function
()
{
func
(
data
);
$
(
'#confirmation-modal'
).
modal
(
'hide'
);
});
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
if
(
xhr
.
status
===
403
)
{
addMessage
(
gettext
(
"Only the owners can delete the selected object."
),
"warning"
);
}
else
{
addMessage
(
gettext
(
"An error occurred. ("
)
+
xhr
.
status
+
")"
,
'danger'
)
}
}
});
}
circle/dashboard/static/dashboard/vm-create.js
View file @
7fed7d2c
...
...
@@ -20,15 +20,15 @@ function vmCreateLoaded() {
var
template
=
$
(
this
).
data
(
"template-pk"
);
$
.
get
(
"/dashboard/vm/create/?template="
+
template
,
function
(
data
)
{
var
r
=
$
(
'#c
reate
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
var
r
=
$
(
'#c
onfirmation
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
$
(
'body'
).
append
(
data
);
vmCreateLoaded
();
addSliderMiscs
();
$
(
'#c
reate
-modal'
).
modal
(
'show'
);
$
(
'#c
reate
-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#c
reate
-modal'
).
remove
();
$
(
'#c
onfirmation
-modal'
).
modal
(
'show'
);
$
(
'#c
onfirmation
-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#c
onfirmation
-modal'
).
remove
();
});
$
(
"#c
reate
-modal"
).
on
(
"shown.bs.modal"
,
function
()
{
$
(
"#c
onfirmation
-modal"
).
on
(
"shown.bs.modal"
,
function
()
{
setDefaultSliderValues
();
});
});
...
...
@@ -48,18 +48,18 @@ function vmCreateLoaded() {
window
.
location
.
replace
(
data
.
redirect
+
'#activity'
);
}
else
{
var
r
=
$
(
'#c
reate
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
var
r
=
$
(
'#c
onfirmation
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
$
(
'body'
).
append
(
data
);
vmCreateLoaded
();
addSliderMiscs
();
$
(
'#c
reate
-modal'
).
modal
(
'show'
);
$
(
'#c
reate
-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#c
reate
-modal'
).
remove
();
$
(
'#c
onfirmation
-modal'
).
modal
(
'show'
);
$
(
'#c
onfirmation
-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#c
onfirmation
-modal'
).
remove
();
});
}
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
var
r
=
$
(
'#c
reate
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
var
r
=
$
(
'#c
onfirmation
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
if
(
xhr
.
status
==
500
)
{
addMessage
(
"500 Internal Server Error"
,
"danger"
);
...
...
@@ -211,7 +211,7 @@ function vmCustomizeLoaded() {
});
/* start vm button clicks */
$
(
'#vm-create-customized-start'
).
click
(
function
()
{
$
(
'#
confirmation-modal #
vm-create-customized-start'
).
click
(
function
()
{
var
error
=
false
;
$
(
".cpu-count-input, .ram-input, #id_name, #id_amount "
).
each
(
function
()
{
if
(
!
$
(
this
)[
0
].
checkValidity
())
{
...
...
@@ -222,8 +222,6 @@ function vmCustomizeLoaded() {
$
(
this
).
find
(
"i"
).
prop
(
"class"
,
"fa fa-spinner fa-spin"
);
if
(
$
(
"#create-modal"
))
return
true
;
$
.
ajax
({
url
:
'/dashboard/vm/create/'
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
...
...
@@ -238,18 +236,18 @@ function vmCustomizeLoaded() {
window
.
location
.
href
=
data
.
redirect
+
'#activity'
;
}
else
{
var
r
=
$
(
'#c
reate
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
var
r
=
$
(
'#c
onfirmation
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
$
(
'body'
).
append
(
data
);
vmCreateLoaded
();
addSliderMiscs
();
$
(
'#c
reate
-modal'
).
modal
(
'show'
);
$
(
'#c
reate
-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#c
reate
-modal'
).
remove
();
$
(
'#c
onfirmation
-modal'
).
modal
(
'show'
);
$
(
'#c
onfirmation
-modal'
).
on
(
'hidden.bs.modal'
,
function
()
{
$
(
'#c
onfirmation
-modal'
).
remove
();
});
}
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
var
r
=
$
(
'#c
reate
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
var
r
=
$
(
'#c
onfirmation
-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
if
(
xhr
.
status
==
500
)
{
addMessage
(
"500 Internal Server Error"
,
"danger"
);
...
...
circle/dashboard/static/dashboard/vm-details.js
View file @
7fed7d2c
var
show_all
=
false
;
var
in_progress
=
false
;
var
activity_hash
=
5
;
var
Websock_native
;
// not sure
var
reload_vm_detail
=
false
;
$
(
function
()
{
/* do we need to check for new activities */
if
(
decideActivityRefresh
())
{
if
(
!
in_progress
)
{
checkNewActivity
(
1
);
in_progress
=
true
;
}
}
$
(
'a[href="#activity"]'
).
click
(
function
(){
$
(
'a[href="#activity"] i'
).
addClass
(
'fa-spin'
);
if
(
!
in_progress
)
{
checkNewActivity
(
1
);
in_progress
=
true
;
}
});
$
(
"#activity-refresh"
).
on
(
"click"
,
"#show-all-activities"
,
function
()
{
$
(
this
).
find
(
"i"
).
addClass
(
"fa-spinner fa-spin"
);
show_all
=
!
show_all
;
$
(
'a[href="#activity"]'
).
trigger
(
"click"
);
return
false
;
});
/* save resources */
$
(
'#vm-details-resources-save'
).
click
(
function
(
e
)
{
var
error
=
false
;
...
...
@@ -43,7 +16,7 @@ $(function() {
var
vm
=
$
(
this
).
data
(
"vm"
);
$
.
ajax
({
type
:
'POST'
,
url
:
"/dashboard/vm/"
+
vm
+
"/op/resources_change/"
,
url
:
$
(
this
).
parent
(
"form"
).
prop
(
'action'
)
,
data
:
$
(
'#vm-details-resources-form'
).
serialize
(),
success
:
function
(
data
,
textStatus
,
xhr
)
{
if
(
data
.
success
)
{
...
...
@@ -89,17 +62,6 @@ $(function() {
return
false
;
});
/* remove port */
$
(
'.vm-details-remove-port'
).
click
(
function
()
{
addModalConfirmation
(
removePort
,
{
'url'
:
$
(
this
).
prop
(
"href"
),
'data'
:
[],
'rule'
:
$
(
this
).
data
(
"rule"
)
});
return
false
;
});
/* for js fallback */
$
(
"#vm-details-pw-show"
).
parent
(
"div"
).
children
(
"input"
).
prop
(
"type"
,
"password"
);
...
...
@@ -123,80 +85,6 @@ $(function() {
span
.
tooltip
();
});
/* change password confirmation */
$
(
"#vm-details-pw-change"
).
click
(
function
()
{
$
(
"#vm-details-pw-confirm"
).
fadeIn
();
return
false
;
});
/* change password */
$
(
".vm-details-pw-confirm-choice"
).
click
(
function
()
{
choice
=
$
(
this
).
data
(
"choice"
);
if
(
choice
)
{
pk
=
$
(
this
).
data
(
"vm"
);
$
.
ajax
({
type
:
'POST'
,
url
:
"/dashboard/vm/"
+
pk
+
"/"
,
data
:
{
'change_password'
:
'true'
},
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
re
,
textStatus
,
xhr
)
{
location
.
reload
();
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
if
(
xhr
.
status
==
500
)
{
addMessage
(
"Internal Server Error"
,
"danger"
);
}
else
{
addMessage
(
xhr
.
status
+
" Unknown Error"
,
"danger"
);
}
}
});
}
else
{
$
(
"#vm-details-pw-confirm"
).
fadeOut
();
}
return
false
;
});
/* add network button */
$
(
"#vm-details-network-add"
).
click
(
function
()
{
$
(
"#vm-details-network-add-form"
).
toggle
();
return
false
;
});
/* add disk button */
$
(
"#vm-details-disk-add"
).
click
(
function
()
{
$
(
"#vm-details-disk-add-for-form"
).
html
(
$
(
"#vm-details-disk-add-form"
).
html
());
return
false
;
});
/* for interface remove buttons */
$
(
'.interface-remove'
).
click
(
function
()
{
var
interface_pk
=
$
(
this
).
data
(
'interface-pk'
);
addModalConfirmation
(
removeInterface
,
{
'url'
:
'/dashboard/interface/'
+
interface_pk
+
'/delete/'
,
'data'
:
[],
'pk'
:
interface_pk
,
'type'
:
"interface"
,
});
return
false
;
});
/* removing interface post */
function
removeInterface
(
data
)
{
$
.
ajax
({
type
:
'POST'
,
url
:
data
.
url
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
re
,
textStatus
,
xhr
)
{
/* remove the html element */
$
(
'a[data-interface-pk="'
+
data
.
pk
+
'"]'
).
closest
(
"div"
).
fadeOut
();
location
.
reload
();
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
'Uh oh :('
,
'danger'
);
}
});
}
/* rename */
$
(
"#vm-details-h1-name, .vm-details-rename-button"
).
click
(
function
()
{
$
(
"#vm-details-h1-name"
).
hide
();
...
...
@@ -336,109 +224,3 @@ $(function() {
});
});
function
removePort
(
data
)
{
$
.
ajax
({
type
:
'POST'
,
url
:
data
.
url
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
re
,
textStatus
,
xhr
)
{
$
(
"a[data-rule="
+
data
.
rule
+
"]"
).
each
(
function
()
{
$
(
this
).
closest
(
"tr"
).
fadeOut
(
500
,
function
()
{
$
(
this
).
remove
();
});
});
addMessage
(
re
.
message
,
"success"
);
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
}
});
}
function
decideActivityRefresh
()
{
var
check
=
false
;
/* if something is still spinning */
if
(
$
(
'.timeline .activity i'
).
hasClass
(
'fa-spin'
))
check
=
true
;
return
check
;
}
function
checkNewActivity
(
runs
)
{
var
instance
=
location
.
href
.
split
(
'/'
);
instance
=
instance
[
instance
.
length
-
2
];
$
.
ajax
({
type
:
'GET'
,
url
:
'/dashboard/vm/'
+
instance
+
'/activity/'
,
data
:
{
'show_all'
:
show_all
},
success
:
function
(
data
)
{
var
new_activity_hash
=
(
data
.
activities
+
""
).
hashCode
();
if
(
new_activity_hash
!=
activity_hash
)
{
$
(
"#activity-refresh"
).
html
(
data
.
activities
);
}
activity_hash
=
new_activity_hash
;
$
(
"#ops"
).
html
(
data
.
ops
);
$
(
"#disk-ops"
).
html
(
data
.
disk_ops
);
$
(
"[title]"
).
tooltip
();
/* changing the status text */
var
icon
=
$
(
"#vm-details-state i"
);
if
(
data
.
is_new_state
)
{
if
(
!
icon
.
hasClass
(
"fa-spin"
))
icon
.
prop
(
"class"
,
"fa fa-spinner fa-spin"
);
}
else
{
icon
.
prop
(
"class"
,
"fa "
+
data
.
icon
);
}
$
(
"#vm-details-state"
).
data
(
"status"
,
data
[
'status'
]);
$
(
"#vm-details-state span"
).
html
(
data
[
'human_readable_status'
].
toUpperCase
());
if
(
data
[
'status'
]
==
"RUNNING"
)
{
if
(
data
[
'connect_uri'
])
{
$
(
"#dashboard-vm-details-connect-button"
).
removeClass
(
'disabled'
);
}
$
(
"[data-target=#_console]"
).
attr
(
"data-toggle"
,
"pill"
).
attr
(
"href"
,
"#console"
).
parent
(
"li"
).
removeClass
(
"disabled"
);
}
else
{
if
(
data
[
'connect_uri'
])
{
$
(
"#dashboard-vm-details-connect-button"
).
addClass
(
'disabled'
);
}
$
(
"[data-target=#_console]"
).
attr
(
"data-toggle"
,
"_pill"
).
attr
(
"href"
,
"#"
).
parent
(
"li"
).
addClass
(
"disabled"
);
}
if
(
data
.
status
==
"STOPPED"
||
data
.
status
==
"PENDING"
)
{
$
(
".change-resources-button"
).
prop
(
"disabled"
,
false
);
$
(
".change-resources-help"
).
hide
();
}
else
{
$
(
".change-resources-button"
).
prop
(
"disabled"
,
true
);
$
(
".change-resources-help"
).
show
();
}
if
(
runs
>
0
&&
decideActivityRefresh
())
{
setTimeout
(
function
()
{
checkNewActivity
(
runs
+
1
);},
1000
+
Math
.
exp
(
runs
*
0.05
)
);
}
else
{
in_progress
=
false
;
if
(
reload_vm_detail
)
location
.
reload
();
}
$
(
'a[href="#activity"] i'
).
removeClass
(
'fa-spin'
);
},
error
:
function
()
{
in_progress
=
false
;
}
});
}
String
.
prototype
.
hashCode
=
function
()
{
var
hash
=
0
,
i
,
chr
,
len
;
if
(
this
.
length
===
0
)
return
hash
;
for
(
i
=
0
,
len
=
this
.
length
;
i
<
len
;
i
++
)
{
chr
=
this
.
charCodeAt
(
i
);
hash
=
((
hash
<<
5
)
-
hash
)
+
chr
;
hash
|=
0
;
// Convert to 32bit integer
}
return
hash
;
};
circle/dashboard/templates/dashboard/confirm/ajax-delete.html
View file @
7fed7d2c
...
...
@@ -3,19 +3,25 @@
<div
class=
"modal-dialog"
>
<div
class=
"modal-content"
>
<div
class=
"modal-body"
>
{% if text %}
{{ text|safe }}
{% if member %}
{% blocktrans with group=object member=member %}
Do you really want to remove
<strong>
{{ member }}
</strong>
from {{ group }}?
{% endblocktrans %}
{% else %}
{%
blocktrans with object=object
%}
{%
blocktrans with object=object
%}
Are you sure you want to delete
<strong>
{{ object }}
</strong>
?
{%
endblocktrans
%}
{%
endblocktrans
%}
{% endif %}
<br
/>
<div
class=
"pull-right"
style=
"margin-top: 15px;"
>
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
>
{% trans "Cancel" %}
</button>
<button
id=
"confirmation-modal-button"
type=
"button"
class=
"btn btn-danger"
{%
if
disable_submit
%}
disabled
{%
endif
%}
>
{% trans "Delete" %}
</button>
<form
action=
"{{ request.path }}"
method=
"POST"
>
{% csrf_token %}
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
>
{% trans "Cancel" %}
</button>
<input
type=
"hidden"
name=
"next"
value=
"{{ request.GET.next }}"
/>
<button
class=
"btn btn-danger"
{%
if
disable_submit
%}
disabled
{%
endif
%}
>
{% trans "Delete" %}
</button>
</form>
</div>
<div
class=
"clearfix"
></div>
</div>
...
...
circle/dashboard/templates/dashboard/confirm/ajax-node-status.html
deleted
100644 → 0
View file @
32e83265
{% 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 change
<strong>
{{ object }}
</strong>
status?
{%endblocktrans%}
{% endif %}
<div
class=
"pull-right"
>
<form
action=
"{% url "
dashboard
.
views
.
status-node
"
pk=
object.pk
%}"
method=
"POST"
>
{% csrf_token %}
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
>
{% trans "Cancel" %}
</button>
<input
type=
"hidden"
name=
"change_status"
value=
""
/>
<button
class=
"btn btn-warning"
>
{% blocktrans with status=status %}Yes, {{status}}{% endblocktrans %}
</button>
</form>
</div>
<div
class=
"clearfix"
></div>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
circle/dashboard/templates/dashboard/confirm/ajax-remove.html
deleted
100644 → 0
View file @
32e83265
{% 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 remove
<strong>
{{ member }}
</strong>
from
<strong>
{{ object }}
</strong>
?
{%endblocktrans%}
{% endif %}
<br
/>
<div
class=
"pull-right"
style=
"margin-top: 15px;"
>
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
>
Cancel
</button>
<button
id=
"confirmation-modal-button"
type=
"button"
class=
"btn btn-warning"
>
Remove
</button>
</div>
<div
class=
"clearfix"
></div>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
circle/dashboard/templates/dashboard/confirm/base-delete.html
View file @
7fed7d2c
...
...
@@ -17,12 +17,18 @@
{% if text %}
{{ text|safe }}
{% else %}
{%blocktrans with object=object%}
Are you sure you want to delete
<strong>
{{ object }}
</strong>
?
{%endblocktrans%}
{% if member %}
{% blocktrans with group=object member=member %}
Do you really want to remove
<strong>
{{ member }}
</strong>
from {{ group }}?
{% endblocktrans %}
{% else %}
{% blocktrans with object=object %}
Are you sure you want to delete
<strong>
{{ object }}
</strong>
?
{% endblocktrans %}
{% endif %}
{% endif %}
<div
class=
"pull-right"
>
<form
action=
""
method=
"POST"
>
<form
action=
"
{{ request.path }}
"
method=
"POST"
>
{% csrf_token %}
<a
class=
"btn btn-default"
>
{% trans "Cancel" %}
</a>
<input
type=
"hidden"
name=
"next"
value=
"{{ request.GET.next }}"
/>
...
...
circle/dashboard/templates/dashboard/confirm/base-remove.html
deleted
100644 → 0
View file @
32e83265
{% extends "base.html" %}
{% load i18n %}
{% block title-site %}Dashboard | CIRCLE{% endblock %}
{% block content %}
{% blocktrans with group=object member=member %}
Do you really want to remove {{member}} from {{group}}?
{% endblocktrans %}
<form
action=
""
method=
"POST"
>
{% csrf_token %}
<input
type=
"submit"
value=
"{% trans "
Remove
"
%}"
/>
</form>
{% endblock %}
circle/dashboard/templates/dashboard/confirm/base-renew.html
deleted
100644 → 0
View file @
32e83265
{% extends "dashboard/base.html" %}
{% load i18n %}
{% block content %}
<div
class=
"body-content"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
>
{%blocktrans with instance=instance.name%}
Renewing
<em>
{{instance}}
</em>
{%endblocktrans%}
</h3>
</div>
<div
class=
"panel-body"
>
{%blocktrans with object=instance.name%}
Do you want to renew
<strong>
{{ object }}
</strong>
?
{%endblocktrans%}
{%blocktrans with suspend=time_of_suspend delete=time_of_delete|default:"n/a" %}
The instance will be suspended at
<em>
{{suspend}}
</em>
and removed at
<em>
{{delete}}
</em>
if you renew it now.
{%endblocktrans%}
<div
class=
"pull-right"
>
<form
action=
""
method=
"POST"
>
{% csrf_token %}
<a
class=
"btn btn-default"
href=
"{{instance.get_absolute_path}}"
>
{% trans "Back" %}
</a>
<button
class=
"btn btn-danger"
>
{% trans "Renew" %}
</button>
</form>
</div>
</div>
</div>
{% endblock %}
circle/dashboard/templates/dashboard/confirm/mass-delete.html
deleted
100644 → 0
View file @
32e83265
{% 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"
>
{% trans "Are you sure you want to delete the following objects?" %}
<br
/>
{% for o in objects %}
<strong>
{{ o }}
</strong>
{% if not forloop.last %},{% endif %}
{% endfor %}
<div
class=
"pull-right"
style=
"margin-top: 40px;"
>
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
>
{% trans "Cancel" %}
</button>
<button
id=
"confirmation-modal-button"
type=
"button"
class=
"btn btn-danger"
>
{% trans "Delete" %}
</button>
</div>
<div
class=
"clearfix"
></div>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
circle/dashboard/templates/dashboard/confirm/node-flush.html
deleted
100644 → 0
View file @
32e83265
{% extends "dashboard/base.html" %}
{% load i18n %}
{% block content %}
<div
class=
"body-content"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
>
{% if title %}
{{ title }}
{% else %}
Flush confirmation
{% endif %}
</h3>
</div>
<div
class=
"panel-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=
""
method=
"POST"
>
{% csrf_token %}
<a
class=
"btn btn-default"
>
{% trans "Back" %}
</a>
<input
type=
"hidden"
name=
"flush"
value=
""
/>
<button
class=
"btn btn-warning"
>
{% trans "Yes" %}
</button>
</form>
</div>
</div>
</div>
{% endblock %}
circle/dashboard/templates/dashboard/confirm/node-status.html
deleted
100644 → 0
View file @
32e83265
{% extends "dashboard/base.html" %}
{% load i18n %}
{% block content %}
<div
class=
"body-content"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
>
{% if title %}
{{ title }}
{% else %}
{% trans "Status changing confirmation" %}
{% endif %}
</h3>
</div>
<div
class=
"panel-body"
>
{% if text %}
{{ text }}
{% else %}
{%blocktrans with object=object%}
Are you sure you want to change
<strong>
{{ object }}
</strong>
status?
{%endblocktrans%}
{% endif %}
<div
class=
"pull-right"
>
<form
action=
""
method=
"POST"
>
{% csrf_token %}
<a
class=
"btn btn-default"
>
{% trans "Cancel" %}
</a>
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
></button>
<input
type=
"hidden"
name=
"change_status"
value=
""
/>
<button
class=
"btn btn-warning"
>
{% blocktrans with status=status %}Yes, {{status}}{% endblocktrans %}
</button>
</form>
</div>
</div>
</div>
{% endblock %}
circle/dashboard/templates/dashboard/group-list/column-name.html
View file @
7fed7d2c
...
...
@@ -7,6 +7,6 @@
<button
type=
"submit"
class=
"group-list-rename-submit btn btn-sm"
>
{% trans "Rename" %}
</button>
</form>
</div>
<div
id
=
"group-list-column-name"
>
<div
class
=
"group-list-column-name"
>
<a
class=
"real-link"
href=
"{% url "
dashboard
.
views
.
group-detail
"
pk=
record.pk
%}"
>
{{ record.name }}
</a>
</div>
circle/dashboard/templates/dashboard/index-templates.html
View file @
7fed7d2c
...
...
@@ -16,7 +16,7 @@
<i
class=
"fa fa-{{ t.os_type }}"
></i>
{{ t.name }}
</span>
<small
class=
"text-muted index-template-list-system"
>
{{ t.system }}
</small>
<div
class=
"pull-right vm-create"
data-template=
"{{ t.pk }}
"
>
<div
href=
"{% url "
dashboard
.
views
.
vm-create
"
%}?
template=
{{
t
.
pk
}}"
class=
"pull-right vm-create
"
>
<i
data-container=
"body"
title=
"{% trans "
Start
VM
instance
"
%}"
class=
"fa fa-play"
></i>
</div>
...
...
circle/dashboard/templates/dashboard/modal-wrapper.html
deleted
100644 → 0
View file @
32e83265
<div
class=
"modal fade"
id=
"create-modal"
tabindex=
"-1"
role=
"dialog"
>
<div
class=
"modal-dialog"
>
<div
class=
"modal-content"
>
{% if box_title and ajax_title %}
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
data-dismiss=
"modal"
aria-hidden=
"true"
>
×
</button>
<h4
class=
"modal-title"
>
{{ box_title }}
</h4>
</div>
{% endif %}
<div
class=
"modal-body"
>
{% include template %}
</div>
<!--<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>-->
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
circle/dashboard/templates/dashboard/node-detail.html
View file @
7fed7d2c
...
...
@@ -80,7 +80,8 @@
</a>
</li>
<li>
<a
href=
"#activity"
data-toggle=
"pill"
class=
"text-center"
>
<a
href=
"#activity"
data-toggle=
"pill"
class=
"text-center"
data-activity-url=
"{% url "
dashboard
.
views
.
node-activity-list
"
node
.
pk
%}"
>
<i
class=
"fa fa-clock-o fa-2x"
></i><br>
{% trans "Activity" %}
</a>
...
...
circle/dashboard/templates/dashboard/node-detail/_activity-timeline.html
View file @
7fed7d2c
{% load i18n %}
{% load hro %}
<div
id=
"activity-timeline"
class=
"timeline"
>
{% for a in activities %}
<div
class=
"activity"
data-activity-id=
"{{ a.pk }}"
>
<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-plus{% endif %}"
></i>
</span>
<strong
title=
"{{ a.result.get_admin_text }}"
>
{{ a.readable_name.get_admin_text|capfirst }}
</strong>
{% for a in activities %}
<div
class=
"activity"
data-activity-id=
"{{ a.pk }}"
>
<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-plus{% endif %}"
></i>
</span>
<strong
title=
"{{ a.result.get_admin_text }}"
>
{{ a.readable_name.get_admin_text|capfirst }}
</strong>
{{ a.started|date:"Y-m-d H:i" }}, {{ a.user }}
{% if a.children.count > 0 %}
<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 %}"
>
{{ s.readable_name|get_text:user }}
–
{% if s.finished %}
{{ s.finished|time:"H:i:s" }}
{% else %}
<i
class=
"fa fa-refresh fa-spin"
class=
"sub-activity-loading-icon"
></i>
{% endif %}
{% if s.has_failed %}
<div
title=
"{{ s.result.get_admin_text }}"
class=
"label label-danger"
>
{% trans "failed" %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
{{ a.started|date:"Y-m-d H:i" }}{% if a.user %}, {{ a.user }}{% endif %}
{% if a.children.count > 0 %}
<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 %}"
>
<span
title=
"{{ s.result.get_admin_text }}"
>
{{ s.readable_name|get_text:user }}
</span>
–
{% if s.finished %}
{{ s.finished|time:"H:i:s" }}
{% else %}
<i
class=
"fa fa-refresh fa-spin"
class=
"sub-activity-loading-icon"
></i>
{% endif %}
{% if s.has_failed %}
<div
class=
"label label-danger"
>
{% trans "failed" %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
circle/dashboard/templates/dashboard/node-detail/activity.html
View file @
7fed7d2c
...
...
@@ -2,6 +2,6 @@
<h3>
{% trans "Activity" %}
</h3>
<div
id=
"activity-
timeline-wrapper
"
>
<div
id=
"activity-
refresh
"
>
{% include "dashboard/node-detail/_activity-timeline.html" %}
</div>
circle/dashboard/templates/dashboard/vm-detail.html
View file @
7fed7d2c
...
...
@@ -207,7 +207,8 @@
{% trans "Network" %}
</a>
</li>
<li>
<a
href=
"#activity"
data-toggle=
"pill"
data-target=
"#_activity"
class=
"text-center"
>
<a
href=
"#activity"
data-toggle=
"pill"
data-target=
"#_activity"
class=
"text-center"
data-activity-url=
"{% url "
dashboard
.
views
.
vm-activity-list
"
instance
.
pk
%}"
>
<i
class=
"fa fa-clock-o fa-2x"
></i><br>
{% trans "Activity" %}
</a>
</li>
...
...
circle/dashboard/templates/dashboard/vm-detail/network.html
View file @
7fed7d2c
...
...
@@ -21,13 +21,14 @@
<a
href=
"{{ i.host.get_absolute_url }}"
class=
"btn btn-default btn-xs"
>
{% trans "edit" %}
</a>
{% endif %}
{%
if is_owner
%}
<a
href=
"{% url "
dashboard
.
views
.
interface-delete
"
pk=
i.pk
%}?
next=
{{
request
.
path
}}"
class=
"btn btn-danger btn-xs interface-remove
"
data-interface-pk=
"{{ i.pk }}"
>
{% trans "remove" %}
{%
with op=op.remove_interface %}{% if op
%}
<span
class=
"operation-wrapper"
>
<a
href=
"{{op.get_url}}?interface={{ i.pk }}
"
class=
"btn btn-{{op.effect}} btn-xs operation interface-remove"
{%
if
op
.
disabled
%}
disabled
{%
endif
%}
>
{% trans "remove" %}
</a>
{% endif %}
</span>
{% endif %}{% endwith %}
</h3>
{% if i.host %}
<div
class=
"row"
>
...
...
@@ -78,7 +79,13 @@
{{ l.private }}/{{ l.proto }}
</td>
<td>
<a
href=
"{{ op.remove_port.get_url }}?rule={{ l.ipv4.pk }}"
class=
"btn btn-link btn-xs vm-details-remove-port"
data-rule=
"{{ l.ipv4.pk }}"
title=
"{% trans "
Remove
"
%}"
><i
class=
"fa fa-times"
><span
class=
"sr-only"
>
{% trans "Remove" %}
</span></i></a>
<span
class=
"operation-wrapper"
>
<a
href=
"{{ op.remove_port.get_url }}?rule={{ l.ipv4.pk }}"
class=
"btn btn-link btn-xs operation"
title=
"{% trans "
Remove
"
%}"
>
<i
class=
"fa fa-times"
><span
class=
"sr-only"
>
{% trans "Remove" %}
</span></i>
</a>
</span>
</td>
</tr>
{% endif %}
...
...
circle/dashboard/tests/test_views.py
View file @
7fed7d2c
...
...
@@ -27,7 +27,7 @@ from django.contrib.auth import authenticate
from
dashboard.views
import
VmAddInterfaceView
from
vm.models
import
Instance
,
InstanceTemplate
,
Lease
,
Node
,
Trait
from
vm.operations
import
(
WakeUpOperation
,
AddInterfaceOperation
,
AddPortOperation
)
AddPortOperation
,
RemoveInterfaceOperation
)
from
..models
import
Profile
from
firewall.models
import
Vlan
,
Host
,
VlanGroup
from
mock
import
Mock
,
patch
...
...
@@ -169,26 +169,12 @@ class VmDetailTest(LoginMixin, TestCase):
inst
.
save
()
iface_count
=
inst
.
interface_set
.
count
()
c
.
post
(
"/dashboard/interface/1/delete/"
)
self
.
assertEqual
(
inst
.
interface_set
.
count
(),
iface_count
-
1
)
def
test_permitted_network_delete_w_ajax
(
self
):
c
=
Client
()
self
.
login
(
c
,
"user1"
)
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
inst
.
set_level
(
self
.
u1
,
'owner'
)
vlan
=
Vlan
.
objects
.
get
(
pk
=
1
)
inst
.
add_interface
(
vlan
=
vlan
,
user
=
self
.
us
)
inst
.
status
=
'RUNNING'
inst
.
save
()
iface_count
=
inst
.
interface_set
.
count
()
response
=
c
.
post
(
"/dashboard/interface/1/delete/"
,
HTTP_X_REQUESTED_WITH
=
'XMLHttpRequest'
)
removed_network
=
json
.
loads
(
response
.
content
)[
'removed_network'
]
self
.
assertEqual
(
removed_network
[
'vlan'
],
vlan
.
name
)
self
.
assertEqual
(
removed_network
[
'vlan_pk'
],
vlan
.
pk
)
self
.
assertEqual
(
removed_network
[
'managed'
],
vlan
.
managed
)
with
patch
.
object
(
RemoveInterfaceOperation
,
'async'
)
as
mock_method
:
mock_method
.
side_effect
=
inst
.
remove_interface
response
=
c
.
post
(
"/dashboard/vm/1/op/remove_interface/"
,
{
'interface'
:
1
})
self
.
assertEqual
(
response
.
status_code
,
302
)
assert
mock_method
.
called
self
.
assertEqual
(
inst
.
interface_set
.
count
(),
iface_count
-
1
)
def
test_unpermitted_network_delete
(
self
):
...
...
@@ -199,7 +185,10 @@ class VmDetailTest(LoginMixin, TestCase):
inst
.
add_interface
(
vlan
=
Vlan
.
objects
.
get
(
pk
=
1
),
user
=
self
.
us
)
iface_count
=
inst
.
interface_set
.
count
()
response
=
c
.
post
(
"/dashboard/interface/1/delete/"
)
with
patch
.
object
(
RemoveInterfaceOperation
,
'async'
)
as
mock_method
:
mock_method
.
side_effect
=
inst
.
remove_interface
response
=
c
.
post
(
"/dashboard/vm/1/op/remove_interface/"
,
{
'interface'
:
1
})
self
.
assertEqual
(
iface_count
,
inst
.
interface_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
403
)
...
...
@@ -766,42 +755,6 @@ class NodeDetailTest(LoginMixin, TestCase):
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
len
(
Node
.
objects
.
get
(
pk
=
1
)
.
traits
.
all
()),
trait_count
)
def
test_anon_change_node_status
(
self
):
c
=
Client
()
node
=
Node
.
objects
.
get
(
pk
=
1
)
node_enabled
=
node
.
enabled
response
=
c
.
post
(
"/dashboard/node/1/"
,
{
'change_status'
:
''
})
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
node_enabled
,
Node
.
objects
.
get
(
pk
=
1
)
.
enabled
)
def
test_unpermitted_change_node_status
(
self
):
c
=
Client
()
self
.
login
(
c
,
"user2"
)
node
=
Node
.
objects
.
get
(
pk
=
1
)
node_enabled
=
node
.
enabled
response
=
c
.
post
(
"/dashboard/node/status/1/"
,
{
'change_status'
:
''
})
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
node_enabled
,
Node
.
objects
.
get
(
pk
=
1
)
.
enabled
)
def
test_permitted_change_node_status
(
self
):
c
=
Client
()
self
.
login
(
c
,
"superuser"
)
node
=
Node
.
objects
.
get
(
pk
=
1
)
node_enabled
=
node
.
enabled
response
=
c
.
post
(
"/dashboard/node/status/1/"
,
{
'change_status'
:
''
})
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
node_enabled
,
not
Node
.
objects
.
get
(
pk
=
1
)
.
enabled
)
def
test_permitted_change_node_status_w_ajax
(
self
):
c
=
Client
()
self
.
login
(
c
,
"superuser"
)
node
=
Node
.
objects
.
get
(
pk
=
1
)
node_enabled
=
node
.
enabled
response
=
c
.
post
(
"/dashboard/node/status/1/"
,
{
'change_status'
:
''
},
HTTP_X_REQUESTED_WITH
=
'XMLHttpRequest'
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
node_enabled
,
not
Node
.
objects
.
get
(
pk
=
1
)
.
enabled
)
class
GroupCreateTest
(
LoginMixin
,
TestCase
):
fixtures
=
[
'test-vm-fixture.json'
,
'node.json'
]
...
...
@@ -949,21 +902,26 @@ class GroupDeleteTest(LoginMixin, TestCase):
def
test_permitted_group_page
(
self
):
c
=
Client
()
self
.
login
(
c
,
'user0'
)
response
=
c
.
get
(
'/dashboard/group/delete/'
+
str
(
self
.
g1
.
pk
)
+
'/'
)
with
patch
(
'dashboard.views.util.messages'
)
as
msg
:
response
=
c
.
get
(
'/dashboard/group/delete/
%
d/'
%
self
.
g1
.
pk
)
assert
not
msg
.
error
.
called
and
not
msg
.
warning
.
called
self
.
assertEqual
(
response
.
status_code
,
200
)
def
test_unpermitted_group_page
(
self
):
c
=
Client
()
self
.
login
(
c
,
'user1'
)
response
=
c
.
get
(
'/dashboard/group/delete/'
+
str
(
self
.
g1
.
pk
)
+
'/'
)
self
.
assertEqual
(
response
.
status_code
,
403
)
with
patch
(
'dashboard.views.util.messages'
)
as
msg
:
response
=
c
.
get
(
'/dashboard/group/delete/
%
d/'
%
self
.
g1
.
pk
)
assert
msg
.
error
.
called
or
msg
.
warning
.
called
self
.
assertEqual
(
response
.
status_code
,
302
)
def
test_anon_group_delete
(
self
):
c
=
Client
()
groupnum
=
Group
.
objects
.
count
()
response
=
c
.
post
(
'/dashboard/group/delete/'
+
str
(
self
.
g1
.
pk
)
+
'/'
)
response
=
c
.
get
(
'/dashboard/group/delete/
%
d/'
%
self
.
g1
.
pk
)
self
.
assertRedirects
(
response
,
'/accounts/login/?next=/dashboard/group/delete/5/'
,
status_code
=
302
)
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
Group
.
objects
.
count
(),
groupnum
)
def
test_unpermitted_group_delete
(
self
):
c
=
Client
()
...
...
@@ -1484,38 +1442,6 @@ class TransferOwnershipViewTest(LoginMixin, TestCase):
response
=
c
.
post
(
'/dashboard/vm/1/tx/'
,
{
'name'
:
'user2'
})
self
.
assertEqual
(
self
.
u2
.
notification_set
.
count
(),
c2
+
1
)
def
test_transfer
(
self
):
self
.
skipTest
(
"How did this ever pass?"
)
c
=
Client
()
self
.
login
(
c
,
'user1'
)
response
=
c
.
post
(
'/dashboard/vm/1/tx/'
,
{
'name'
:
'user2'
})
url
=
response
.
context
[
'token'
]
c
=
Client
()
self
.
login
(
c
,
'user2'
)
response
=
c
.
post
(
url
)
self
.
assertEquals
(
Instance
.
objects
.
get
(
pk
=
1
)
.
owner
.
pk
,
self
.
u2
.
pk
)
def
test_transfer_token_used_by_others
(
self
):
self
.
skipTest
(
"How did this ever pass?"
)
c
=
Client
()
self
.
login
(
c
,
'user1'
)
response
=
c
.
post
(
'/dashboard/vm/1/tx/'
,
{
'name'
:
'user2'
})
url
=
response
.
context
[
'token'
]
response
=
c
.
post
(
url
)
# token is for user2
assert
response
.
status_code
==
403
self
.
assertEquals
(
Instance
.
objects
.
get
(
pk
=
1
)
.
owner
.
pk
,
self
.
u1
.
pk
)
def
test_transfer_by_superuser
(
self
):
self
.
skipTest
(
"How did this ever pass?"
)
c
=
Client
()
self
.
login
(
c
,
'superuser'
)
response
=
c
.
post
(
'/dashboard/vm/1/tx/'
,
{
'name'
:
'user2'
})
url
=
response
.
context
[
'token'
]
c
=
Client
()
self
.
login
(
c
,
'user2'
)
response
=
c
.
post
(
url
)
self
.
assertEquals
(
Instance
.
objects
.
get
(
pk
=
1
)
.
owner
.
pk
,
self
.
u2
.
pk
)
class
IndexViewTest
(
LoginMixin
,
TestCase
):
fixtures
=
[
'test-vm-fixture.json'
,
'node.json'
]
...
...
circle/dashboard/urls.py
View file @
7fed7d2c
...
...
@@ -25,12 +25,12 @@ from .views import (
GroupDetailView
,
GroupList
,
IndexView
,
InstanceActivityDetail
,
LeaseCreate
,
LeaseDelete
,
LeaseDetail
,
MyPreferencesView
,
NodeAddTraitView
,
NodeCreate
,
NodeDelete
,
NodeDetailView
,
NodeList
,
NodeStatus
,
NodeDetailView
,
NodeList
,
NotificationView
,
TemplateAclUpdateView
,
TemplateCreate
,
TemplateDelete
,
TemplateDetail
,
TemplateList
,
vm_activity
,
VmCreate
,
VmDetailView
,
VmDetailVncTokenView
,
VmList
,
DiskRemoveView
,
get_disk_download_status
,
InterfaceDeleteView
,
DiskRemoveView
,
get_disk_download_status
,
GroupRemoveUserView
,
GroupRemoveFutureUserView
,
GroupCreate
,
GroupProfileUpdate
,
...
...
@@ -51,6 +51,7 @@ from .views import (
TransferInstanceOwnershipView
,
TransferInstanceOwnershipConfirmView
,
TransferTemplateOwnershipView
,
TransferTemplateOwnershipConfirmView
,
OpenSearchDescriptionView
,
NodeActivityView
,
)
from
.views.vm
import
vm_ops
,
vm_mass_ops
from
.views.node
import
node_ops
...
...
@@ -94,7 +95,8 @@ urlpatterns = patterns(
url
(
r'^vm/list/$'
,
VmList
.
as_view
(),
name
=
'dashboard.views.vm-list'
),
url
(
r'^vm/create/$'
,
VmCreate
.
as_view
(),
name
=
'dashboard.views.vm-create'
),
url
(
r'^vm/(?P<pk>\d+)/activity/$'
,
vm_activity
),
url
(
r'^vm/(?P<pk>\d+)/activity/$'
,
vm_activity
,
name
=
'dashboard.views.vm-activity-list'
),
url
(
r'^vm/activity/(?P<pk>\d+)/$'
,
InstanceActivityDetail
.
as_view
(),
name
=
'dashboard.views.vm-activity'
),
url
(
r'^vm/(?P<pk>\d+)/screenshot/$'
,
get_vm_screenshot
,
...
...
@@ -119,8 +121,8 @@ urlpatterns = patterns(
name
=
'dashboard.views.template-transfer-ownership-confirm'
),
url
(
r'^node/delete/(?P<pk>\d+)/$'
,
NodeDelete
.
as_view
(),
name
=
"dashboard.views.delete-node"
),
url
(
r'^node/
status/(?P<pk>\d+)/$'
,
NodeStatus
.
as_view
(),
name
=
"dashboard.views.status-node"
),
url
(
r'^node/
(?P<pk>\d+)/activity/$'
,
NodeActivityView
.
as_view
(),
name
=
'dashboard.views.node-activity-list'
),
url
(
r'^node/create/$'
,
NodeCreate
.
as_view
(),
name
=
'dashboard.views.node-create'
),
...
...
@@ -156,9 +158,6 @@ urlpatterns = patterns(
url
(
r'^disk/(?P<pk>\d+)/status/$'
,
get_disk_download_status
,
name
=
"dashboard.views.disk-status"
),
url
(
r'^interface/(?P<pk>\d+)/delete/$'
,
InterfaceDeleteView
.
as_view
(),
name
=
"dashboard.views.interface-delete"
),
url
(
r'^profile/$'
,
MyPreferencesView
.
as_view
(),
name
=
"dashboard.views.profile-preferences"
),
url
(
r'^subscribe/(?P<token>.*)/$'
,
UnsubscribeFormView
.
as_view
(),
...
...
circle/dashboard/views/group.py
View file @
7fed7d2c
...
...
@@ -29,7 +29,7 @@ from django.core.urlresolvers import reverse, reverse_lazy
from
django.http
import
HttpResponse
,
Http404
from
django.shortcuts
import
redirect
from
django.utils.translation
import
ugettext
as
_
from
django.views.generic
import
UpdateView
,
DeleteView
,
TemplateView
from
django.views.generic
import
UpdateView
,
TemplateView
from
braces.views
import
SuperuserRequiredMixin
,
LoginRequiredMixin
from
django_tables2
import
SingleTableView
...
...
@@ -41,7 +41,8 @@ from ..forms import (
from
..models
import
FutureMember
,
GroupProfile
from
vm.models
import
Instance
,
InstanceTemplate
from
..tables
import
GroupListTable
from
.util
import
CheckedDetailView
,
AclUpdateView
,
search_user
,
saml_available
from
.util
import
(
CheckedDetailView
,
AclUpdateView
,
search_user
,
saml_available
,
DeleteViewBase
)
logger
=
logging
.
getLogger
(
__name__
)
...
...
@@ -224,15 +225,18 @@ class GroupList(LoginRequiredMixin, SingleTableView):
return
groups
class
GroupRemoveUserView
(
CheckedDetailView
,
DeleteView
):
class
GroupRemoveUserView
(
DeleteViewBase
):
model
=
Group
slug_field
=
'pk'
slug_url_kwarg
=
'group_pk'
read_
level
=
'operator'
level
=
'operator'
member_key
=
'member_pk'
success_message
=
_
(
"Member successfully removed from group."
)
def
get_has_level
(
self
):
return
self
.
object
.
profile
.
has_level
def
check_auth
(
self
):
if
not
self
.
get_object
()
.
profile
.
has_level
(
self
.
request
.
user
,
self
.
level
):
raise
PermissionDenied
()
def
get_context_data
(
self
,
**
kwargs
):
context
=
super
(
GroupRemoveUserView
,
self
)
.
get_context_data
(
**
kwargs
)
...
...
@@ -243,50 +247,24 @@ class GroupRemoveUserView(CheckedDetailView, DeleteView):
return
context
def
get_success_url
(
self
):
next
=
self
.
request
.
POST
.
get
(
'next'
)
if
next
:
return
next
else
:
return
reverse_lazy
(
"dashboard.views.group-detail"
,
kwargs
=
{
'pk'
:
self
.
get_object
()
.
pk
})
return
reverse_lazy
(
"dashboard.views.group-detail"
,
kwargs
=
{
'pk'
:
self
.
get_object
()
.
pk
})
def
get
(
self
,
request
,
member_pk
,
*
args
,
**
kwargs
):
self
.
member_pk
=
member_pk
return
super
(
GroupRemoveUserView
,
self
)
.
get
(
request
,
*
args
,
**
kwargs
)
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-remove.html'
]
else
:
return
[
'dashboard/confirm/base-remove.html'
]
def
remove_member
(
self
,
pk
):
container
=
self
.
get_object
()
container
.
user_set
.
remove
(
User
.
objects
.
get
(
pk
=
pk
))
def
get_success_message
(
self
):
return
_
(
"Member successfully removed from group."
)
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
object
=
self
.
get_object
()
if
not
object
.
profile
.
has_level
(
request
.
user
,
'operator'
):
raise
PermissionDenied
()
def
delete_obj
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
remove_member
(
kwargs
[
self
.
member_key
])
success_url
=
self
.
get_success_url
()
success_message
=
self
.
get_success_message
()
if
request
.
is_ajax
():
return
HttpResponse
(
json
.
dumps
({
'message'
:
success_message
}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
success_message
)
return
redirect
(
success_url
)
class
GroupRemoveFutureUserView
(
GroupRemoveUserView
):
member_key
=
'member_org_id'
success_message
=
_
(
"Future user successfully removed from group."
)
def
get
(
self
,
request
,
member_org_id
,
*
args
,
**
kwargs
):
self
.
member_org_id
=
member_org_id
...
...
@@ -305,53 +283,17 @@ class GroupRemoveFutureUserView(GroupRemoveUserView):
FutureMember
.
objects
.
filter
(
org_id
=
org_id
,
group
=
self
.
get_object
())
.
delete
()
def
get_success_message
(
self
):
return
_
(
"Future user successfully removed from group."
)
class
GroupDelete
(
CheckedDetailView
,
DeleteView
):
"""This stuff deletes the group.
"""
class
GroupDelete
(
DeleteViewBase
):
model
=
Group
template_name
=
"dashboard/confirm/base-delete.html"
read_level
=
'operator'
def
get_has_level
(
self
):
return
self
.
object
.
profile
.
has_level
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-delete.html'
]
else
:
return
[
'dashboard/confirm/base-delete.html'
]
success_message
=
_
(
"Group successfully deleted."
)
# github.com/django/django/blob/master/django/views/generic/edit.py#L245
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
object
=
self
.
get_object
()
if
not
object
.
profile
.
has_level
(
request
.
user
,
'owner'
):
def
check_auth
(
self
):
if
not
self
.
get_object
()
.
profile
.
has_level
(
self
.
request
.
user
,
'owner'
):
raise
PermissionDenied
()
object
.
delete
()
success_url
=
self
.
get_success_url
()
success_message
=
_
(
"Group successfully deleted."
)
if
request
.
is_ajax
():
if
request
.
POST
.
get
(
'redirect'
)
.
lower
()
==
"true"
:
messages
.
success
(
request
,
success_message
)
return
HttpResponse
(
json
.
dumps
({
'message'
:
success_message
}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
success_message
)
return
redirect
(
success_url
)
def
get_success_url
(
self
):
next
=
self
.
request
.
POST
.
get
(
'next'
)
if
next
:
return
next
else
:
return
reverse_lazy
(
'dashboard.index'
)
return
reverse_lazy
(
'dashboard.views.group-list'
)
class
GroupCreate
(
GroupCodeMixin
,
LoginRequiredMixin
,
TemplateView
):
...
...
@@ -360,7 +302,7 @@ class GroupCreate(GroupCodeMixin, LoginRequiredMixin, TemplateView):
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/
modal-wrapper
.html'
]
return
[
'dashboard/
_modal
.html'
]
else
:
return
[
'dashboard/nojs-wrapper.html'
]
...
...
circle/dashboard/views/node.py
View file @
7fed7d2c
...
...
@@ -27,8 +27,10 @@ from django.db.models import Count
from
django.forms.models
import
inlineformset_factory
from
django.http
import
HttpResponse
from
django.shortcuts
import
redirect
from
django.template
import
RequestContext
from
django.template.loader
import
render_to_string
from
django.utils.translation
import
ugettext
as
_
from
django.views.generic
import
DetailView
,
TemplateView
,
Delete
View
from
django.views.generic
import
DetailView
,
TemplateView
,
View
from
braces.views
import
LoginRequiredMixin
,
SuperuserRequiredMixin
from
django_tables2
import
SingleTableView
...
...
@@ -38,7 +40,7 @@ from vm.models import Node, NodeActivity, Trait
from
..forms
import
TraitForm
,
HostForm
,
NodeForm
from
..tables
import
NodeListTable
from
.util
import
AjaxOperationMixin
,
OperationView
,
GraphMixin
from
.util
import
AjaxOperationMixin
,
OperationView
,
GraphMixin
,
DeleteViewBase
def
get_operations
(
instance
,
user
):
...
...
@@ -59,6 +61,8 @@ class NodeOperationView(AjaxOperationMixin, OperationView):
model
=
Node
context_object_name
=
'node'
# much simpler to mock object
with_reload
=
True
wait_for_result
=
1
node_ops
=
OrderedDict
([
...
...
@@ -193,7 +197,7 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/
modal-wrapper
.html'
]
return
[
'dashboard/
_modal
.html'
]
else
:
return
[
'dashboard/nojs-wrapper.html'
]
...
...
@@ -205,7 +209,7 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
context
=
self
.
get_context_data
(
**
kwargs
)
context
.
update
({
'template'
:
'dashboard/node-create.html'
,
'box_title'
:
'Create a Node'
,
'box_title'
:
_
(
'Create a node'
)
,
'hostform'
:
hostform
,
'formset'
:
formset
,
...
...
@@ -238,44 +242,16 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
return
redirect
(
path
)
class
NodeDelete
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
DeleteView
):
"""This stuff deletes the node.
"""
class
NodeDelete
(
SuperuserRequiredMixin
,
DeleteViewBase
):
model
=
Node
template_name
=
"dashboard/confirm/base-delete.html"
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-delete.html'
]
else
:
return
[
'dashboard/confirm/base-delete.html'
]
# github.com/django/django/blob/master/django/views/generic/edit.py#L245
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
object
=
self
.
get_object
()
success_message
=
_
(
"Node successfully deleted."
)
object
.
delete
()
success_url
=
self
.
get_success_url
()
success_message
=
_
(
"Node successfully deleted."
)
if
request
.
is_ajax
():
if
request
.
POST
.
get
(
'redirect'
)
.
lower
()
==
"true"
:
messages
.
success
(
request
,
success_message
)
return
HttpResponse
(
json
.
dumps
({
'message'
:
success_message
}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
success_message
)
return
redirect
(
success_url
)
def
check_auth
(
self
):
# SuperuserRequiredMixin
pass
def
get_success_url
(
self
):
next
=
self
.
request
.
POST
.
get
(
'next'
)
if
next
:
return
next
else
:
return
reverse_lazy
(
'dashboard.index'
)
return
reverse_lazy
(
'dashboard.views.node-list'
)
class
NodeAddTraitView
(
SuperuserRequiredMixin
,
DetailView
):
...
...
@@ -311,55 +287,20 @@ class NodeAddTraitView(SuperuserRequiredMixin, DetailView):
return
self
.
get
(
self
,
request
,
pk
,
*
args
,
**
kwargs
)
class
NodeStatus
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
DetailView
):
template_name
=
"dashboard/confirm/node-status.html"
model
=
Node
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-node-status.html'
]
else
:
return
[
'dashboard/confirm/node-status.html'
]
def
get_success_url
(
self
):
next
=
self
.
request
.
GET
.
get
(
'next'
)
if
next
:
return
next
else
:
return
reverse_lazy
(
"dashboard.views.node-detail"
,
kwargs
=
{
'pk'
:
self
.
object
.
pk
})
def
get_context_data
(
self
,
**
kwargs
):
context
=
super
(
NodeStatus
,
self
)
.
get_context_data
(
**
kwargs
)
if
self
.
object
.
enabled
:
context
[
'status'
]
=
"disable"
else
:
context
[
'status'
]
=
"enable"
return
context
class
NodeActivityView
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
View
):
def
get
(
self
,
request
,
pk
):
node
=
Node
.
objects
.
get
(
pk
=
pk
)
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
if
request
.
POST
.
get
(
'change_status'
)
is
not
None
:
return
self
.
__set_status
(
request
)
return
redirect
(
reverse_lazy
(
"dashboard.views.node-detail"
,
kwargs
=
{
'pk'
:
self
.
get_object
()
.
pk
}))
activities
=
NodeActivity
.
objects
.
filter
(
node
=
node
,
parent
=
None
)
.
order_by
(
'-started'
)
.
select_related
()
def
__set_status
(
self
,
request
):
self
.
object
=
self
.
get_object
()
if
not
self
.
object
.
enabled
:
self
.
object
.
enable
(
user
=
request
.
user
)
else
:
self
.
object
.
disable
(
user
=
request
.
user
)
success_message
=
_
(
"Node successfully changed status."
)
response
=
{
'activities'
:
render_to_string
(
"dashboard/node-detail/_activity-timeline.html"
,
RequestContext
(
request
,
{
'activities'
:
activities
}))
}
if
request
.
is_ajax
():
response
=
{
'message'
:
success_message
,
'node_pk'
:
self
.
object
.
pk
}
return
HttpResponse
(
json
.
dumps
(
response
),
content_type
=
"application/json"
)
else
:
messages
.
success
(
request
,
success_message
)
return
redirect
(
self
.
get_success_url
())
return
HttpResponse
(
json
.
dumps
(
response
),
content_type
=
"application/json"
)
circle/dashboard/views/template.py
View file @
7fed7d2c
...
...
@@ -28,7 +28,7 @@ from django.http import HttpResponse, HttpResponseRedirect
from
django.shortcuts
import
redirect
,
get_object_or_404
from
django.utils.translation
import
ugettext
as
_
,
ugettext_noop
from
django.views.generic
import
(
TemplateView
,
CreateView
,
DeleteView
,
UpdateView
,
TemplateView
,
CreateView
,
UpdateView
,
)
from
braces.views
import
(
...
...
@@ -47,6 +47,7 @@ from ..tables import TemplateListTable, LeaseListTable
from
.util
import
(
AclUpdateView
,
FilterMixin
,
TransferOwnershipConfirmView
,
TransferOwnershipView
,
DeleteViewBase
)
logger
=
logging
.
getLogger
(
__name__
)
...
...
@@ -56,7 +57,7 @@ class TemplateChoose(LoginRequiredMixin, TemplateView):
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/
modal-wrapper
.html'
]
return
[
'dashboard/
_modal
.html'
]
else
:
return
[
'dashboard/nojs-wrapper.html'
]
...
...
@@ -231,46 +232,17 @@ class TemplateList(LoginRequiredMixin, FilterMixin, SingleTableView):
return
qs
.
select_related
(
"lease"
,
"owner"
,
"owner__profile"
)
class
TemplateDelete
(
LoginRequiredMixin
,
DeleteView
):
class
TemplateDelete
(
DeleteViewBase
):
model
=
InstanceTemplate
success_message
=
_
(
"Template successfully deleted."
)
def
get_success_url
(
self
):
return
reverse
(
"dashboard.views.template-list"
)
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-delete.html'
]
else
:
return
[
'dashboard/confirm/base-delete.html'
]
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
if
not
self
.
get_object
()
.
has_level
(
request
.
user
,
"owner"
):
message
=
_
(
"Only the owners can delete the selected template."
)
if
request
.
is_ajax
():
raise
PermissionDenied
()
else
:
messages
.
warning
(
request
,
message
)
return
redirect
(
self
.
get_success_url
())
return
super
(
TemplateDelete
,
self
)
.
get
(
request
,
*
args
,
**
kwargs
)
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
def
delete_obj
(
self
,
request
,
*
args
,
**
kwargs
):
object
=
self
.
get_object
()
if
not
object
.
has_level
(
request
.
user
,
'owner'
):
raise
PermissionDenied
()
object
.
destroy_disks
()
object
.
delete
()
success_url
=
self
.
get_success_url
()
success_message
=
_
(
"Template successfully deleted."
)
if
request
.
is_ajax
():
return
HttpResponse
(
json
.
dumps
({
'message'
:
success_message
}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
success_message
)
return
HttpResponseRedirect
(
success_url
)
class
TemplateDetail
(
LoginRequiredMixin
,
SuccessMessageMixin
,
UpdateView
):
...
...
@@ -333,25 +305,24 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
return
kwargs
class
DiskRemoveView
(
DeleteView
):
class
DiskRemoveView
(
DeleteView
Base
):
model
=
Disk
success_message
=
_
(
"Disk successfully removed."
)
def
get_queryset
(
self
):
qs
=
super
(
DiskRemoveView
,
self
)
.
get_queryset
()
return
qs
.
exclude
(
template_set
=
None
)
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-delete.html'
]
else
:
return
[
'dashboard/confirm/base-delete.html'
]
def
get_context_data
(
self
,
**
kwargs
):
context
=
super
(
DiskRemoveView
,
self
)
.
get_context_data
(
**
kwargs
)
def
check_auth
(
self
):
disk
=
self
.
get_object
()
template
=
disk
.
template_set
.
get
()
if
not
template
.
has_level
(
self
.
request
.
user
,
'owner'
):
raise
PermissionDenied
()
def
get_context_data
(
self
,
**
kwargs
):
disk
=
self
.
get_object
()
template
=
disk
.
template_set
.
get
()
context
=
super
(
DiskRemoveView
,
self
)
.
get_context_data
(
**
kwargs
)
context
[
'title'
]
=
_
(
"Disk remove confirmation"
)
context
[
'text'
]
=
_
(
"Are you sure you want to remove "
"<strong>
%(disk)
s</strong> from "
...
...
@@ -360,29 +331,12 @@ class DiskRemoveView(DeleteView):
)
return
context
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
def
delete
_obj
(
self
,
request
,
*
args
,
**
kwargs
):
disk
=
self
.
get_object
()
template
=
disk
.
template_set
.
get
()
if
not
template
.
has_level
(
request
.
user
,
'owner'
):
raise
PermissionDenied
()
template
.
remove_disk
(
disk
=
disk
,
user
=
request
.
user
)
template
.
remove_disk
(
disk
)
disk
.
destroy
()
next_url
=
request
.
POST
.
get
(
"next"
)
success_url
=
next_url
if
next_url
else
template
.
get_absolute_url
()
success_message
=
_
(
"Disk successfully removed."
)
if
request
.
is_ajax
():
return
HttpResponse
(
json
.
dumps
({
'message'
:
success_message
}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
success_message
)
return
HttpResponseRedirect
(
"
%
s#resources"
%
success_url
)
class
LeaseCreate
(
LoginRequiredMixin
,
PermissionRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
...
...
@@ -435,18 +389,13 @@ class LeaseDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
return
super
(
LeaseDetail
,
self
)
.
post
(
request
,
*
args
,
**
kwargs
)
class
LeaseDelete
(
LoginRequiredMixin
,
DeleteView
):
class
LeaseDelete
(
DeleteViewBase
):
model
=
Lease
success_message
=
_
(
"Lease successfully deleted."
)
def
get_success_url
(
self
):
return
reverse
(
"dashboard.views.template-list"
)
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-delete.html'
]
else
:
return
[
'dashboard/confirm/base-delete.html'
]
def
get_context_data
(
self
,
*
args
,
**
kwargs
):
c
=
super
(
LeaseDelete
,
self
)
.
get_context_data
(
*
args
,
**
kwargs
)
lease
=
self
.
get_object
()
...
...
@@ -461,36 +410,11 @@ class LeaseDelete(LoginRequiredMixin, DeleteView):
c
[
'disable_submit'
]
=
True
return
c
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
if
not
self
.
get_object
()
.
has_level
(
request
.
user
,
"owner"
):
message
=
_
(
"Only the owners can delete the selected lease."
)
if
request
.
is_ajax
():
raise
PermissionDenied
()
else
:
messages
.
warning
(
request
,
message
)
return
redirect
(
self
.
get_success_url
())
return
super
(
LeaseDelete
,
self
)
.
get
(
request
,
*
args
,
**
kwargs
)
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
def
delete_obj
(
self
,
request
,
*
args
,
**
kwargs
):
object
=
self
.
get_object
()
if
not
object
.
has_level
(
request
.
user
,
"owner"
):
raise
PermissionDenied
()
if
object
.
instancetemplate_set
.
count
()
>
0
:
raise
SuspiciousOperation
()
object
.
delete
()
success_url
=
self
.
get_success_url
()
success_message
=
_
(
"Lease successfully deleted."
)
if
request
.
is_ajax
():
return
HttpResponse
(
json
.
dumps
({
'message'
:
success_message
}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
success_message
)
return
HttpResponseRedirect
(
success_url
)
class
TransferTemplateOwnershipConfirmView
(
TransferOwnershipConfirmView
):
...
...
circle/dashboard/views/user.py
View file @
7fed7d2c
...
...
@@ -35,7 +35,7 @@ from django.shortcuts import redirect, get_object_or_404
from
django.utils.translation
import
ugettext
as
_
from
django.views.decorators.http
import
require_POST
from
django.views.generic
import
(
TemplateView
,
DetailView
,
View
,
DeleteView
,
UpdateView
,
CreateView
,
TemplateView
,
DetailView
,
View
,
UpdateView
,
CreateView
,
)
from
django_sshkey.models
import
UserKey
...
...
@@ -50,7 +50,7 @@ from ..forms import (
from
..models
import
Profile
,
GroupProfile
,
ConnectCommand
,
create_profile
from
..tables
import
UserKeyListTable
,
ConnectCommandListTable
from
.util
import
saml_available
from
.util
import
saml_available
,
DeleteViewBase
logger
=
logging
.
getLogger
(
__name__
)
...
...
@@ -385,36 +385,17 @@ class UserKeyDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
return
super
(
UserKeyDetail
,
self
)
.
post
(
self
,
request
,
args
,
kwargs
)
class
UserKeyDelete
(
LoginRequiredMixin
,
DeleteView
):
class
UserKeyDelete
(
DeleteViewBase
):
model
=
UserKey
success_message
=
_
(
"SSH key successfully deleted."
)
def
get_success_url
(
self
):
return
reverse
(
"dashboard.views.profile-preferences"
)
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-delete.html'
]
else
:
return
[
'dashboard/confirm/base-delete.html'
]
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
object
=
self
.
get_object
()
if
object
.
user
!=
request
.
user
:
def
check_auth
(
self
):
if
self
.
get_object
()
.
user
!=
self
.
request
.
user
:
raise
PermissionDenied
()
object
.
delete
()
success_url
=
self
.
get_success_url
()
success_message
=
_
(
"SSH key successfully deleted."
)
if
request
.
is_ajax
():
return
HttpResponse
(
json
.
dumps
({
'message'
:
success_message
}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
success_message
)
return
HttpResponseRedirect
(
success_url
)
class
UserKeyCreate
(
LoginRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
model
=
UserKey
...
...
@@ -460,36 +441,17 @@ class ConnectCommandDetail(LoginRequiredMixin, SuccessMessageMixin,
return
kwargs
class
ConnectCommandDelete
(
LoginRequiredMixin
,
DeleteView
):
class
ConnectCommandDelete
(
DeleteViewBase
):
model
=
ConnectCommand
success_message
=
_
(
"Command template successfully deleted."
)
def
get_success_url
(
self
):
return
reverse
(
"dashboard.views.profile-preferences"
)
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-delete.html'
]
else
:
return
[
'dashboard/confirm/base-delete.html'
]
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
object
=
self
.
get_object
()
if
object
.
user
!=
request
.
user
:
def
check_auth
(
self
):
if
self
.
get_object
()
.
user
!=
self
.
request
.
user
:
raise
PermissionDenied
()
object
.
delete
()
success_url
=
self
.
get_success_url
()
success_message
=
_
(
"Command template successfully deleted."
)
if
request
.
is_ajax
():
return
HttpResponse
(
json
.
dumps
({
'message'
:
success_message
}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
success_message
)
return
HttpResponseRedirect
(
success_url
)
class
ConnectCommandCreate
(
LoginRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
...
...
circle/dashboard/views/util.py
View file @
7fed7d2c
...
...
@@ -33,7 +33,7 @@ from django.db.models import Q
from
django.http
import
HttpResponse
,
Http404
,
HttpResponseRedirect
from
django.shortcuts
import
redirect
,
render
from
django.utils.translation
import
ugettext_lazy
as
_
,
ugettext_noop
from
django.views.generic
import
DetailView
,
View
from
django.views.generic
import
DetailView
,
View
,
DeleteView
from
django.views.generic.detail
import
SingleObjectMixin
from
braces.views
import
LoginRequiredMixin
...
...
@@ -694,3 +694,45 @@ class TransferOwnershipConfirmView(LoginRequiredMixin, View):
unicode
(
user
),
user
.
pk
,
new_owner
,
key
)
raise
PermissionDenied
()
return
(
instance
,
new_owner
)
class
DeleteViewBase
(
LoginRequiredMixin
,
DeleteView
):
level
=
'owner'
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-delete.html'
]
else
:
return
[
'dashboard/confirm/base-delete.html'
]
def
check_auth
(
self
):
if
not
self
.
get_object
()
.
has_level
(
self
.
request
.
user
,
self
.
level
):
raise
PermissionDenied
()
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
try
:
self
.
check_auth
()
except
PermissionDenied
:
message
=
_
(
"Only the owners can delete the selected object."
)
if
request
.
is_ajax
():
raise
PermissionDenied
()
else
:
messages
.
warning
(
request
,
message
)
return
redirect
(
self
.
get_success_url
())
return
super
(
DeleteViewBase
,
self
)
.
get
(
request
,
*
args
,
**
kwargs
)
def
delete_obj
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
get_object
()
.
delete
()
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
check_auth
()
self
.
delete_obj
(
request
,
*
args
,
**
kwargs
)
if
request
.
is_ajax
():
return
HttpResponse
(
json
.
dumps
({
'message'
:
self
.
success_message
}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
self
.
success_message
)
return
HttpResponseRedirect
(
self
.
get_success_url
())
circle/dashboard/views/vm.py
View file @
7fed7d2c
...
...
@@ -37,7 +37,7 @@ from django.utils.translation import (
)
from
django.views.decorators.http
import
require_GET
from
django.views.generic
import
(
UpdateView
,
ListView
,
TemplateView
,
DeleteView
UpdateView
,
ListView
,
TemplateView
)
from
braces.views
import
SuperuserRequiredMixin
,
LoginRequiredMixin
...
...
@@ -64,6 +64,7 @@ from ..forms import (
VmDiskResizeForm
,
RedeployForm
,
VmDiskRemoveForm
,
VmMigrateForm
,
VmDeployForm
,
VmPortRemoveForm
,
VmPortAddForm
,
VmRemoveInterfaceForm
,
)
from
..models
import
Favourite
...
...
@@ -324,6 +325,32 @@ def get_operations(instance, user):
return
ops
class
VmRemoveInterfaceView
(
FormOperationMixin
,
VmOperationView
):
op
=
'remove_interface'
form_class
=
VmRemoveInterfaceForm
show_in_toolbar
=
False
wait_for_result
=
0.5
icon
=
'times'
effect
=
"danger"
with_reload
=
True
def
get_form_kwargs
(
self
):
instance
=
self
.
get_op
()
.
instance
choices
=
instance
.
interface_set
.
all
()
interface_pk
=
self
.
request
.
GET
.
get
(
'interface'
)
if
interface_pk
:
try
:
default
=
choices
.
get
(
pk
=
interface_pk
)
except
(
ValueError
,
Interface
.
DoesNotExist
):
raise
Http404
()
else
:
default
=
None
val
=
super
(
VmRemoveInterfaceView
,
self
)
.
get_form_kwargs
()
val
.
update
({
'choices'
:
choices
,
'default'
:
default
})
return
val
class
VmAddInterfaceView
(
FormOperationMixin
,
VmOperationView
):
op
=
'add_interface'
...
...
@@ -707,6 +734,7 @@ vm_ops = OrderedDict([
op
=
'remove_disk'
,
form_class
=
VmDiskRemoveForm
,
icon
=
'times'
,
effect
=
"danger"
)),
(
'add_interface'
,
VmAddInterfaceView
),
(
'remove_interface'
,
VmRemoveInterfaceView
),
(
'remove_port'
,
VmPortRemoveView
),
(
'add_port'
,
VmPortAddView
),
(
'renew'
,
VmRenewView
),
...
...
@@ -951,10 +979,21 @@ class VmCreate(LoginRequiredMixin, TemplateView):
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/
modal-wrapper
.html'
]
return
[
'dashboard/
_modal
.html'
]
else
:
return
[
'dashboard/nojs-wrapper.html'
]
def
get_template
(
self
,
request
,
pk
):
try
:
template
=
InstanceTemplate
.
objects
.
get
(
pk
=
int
(
pk
))
except
(
ValueError
,
InstanceTemplate
.
DoesNotExist
):
raise
Http404
()
if
not
template
.
has_level
(
request
.
user
,
'user'
):
raise
PermissionDenied
()
return
template
def
get
(
self
,
request
,
form
=
None
,
*
args
,
**
kwargs
):
if
not
request
.
user
.
has_perm
(
'vm.create_vm'
):
raise
PermissionDenied
()
...
...
@@ -965,9 +1004,7 @@ class VmCreate(LoginRequiredMixin, TemplateView):
template_pk
=
form
.
template
.
pk
if
template_pk
:
template
=
get_object_or_404
(
InstanceTemplate
,
pk
=
template_pk
)
if
not
template
.
has_level
(
request
.
user
,
'user'
):
raise
PermissionDenied
()
template
=
self
.
get_template
(
request
,
template_pk
)
if
form
is
None
:
form
=
self
.
form_class
(
user
=
request
.
user
,
template
=
template
)
else
:
...
...
@@ -992,33 +1029,21 @@ class VmCreate(LoginRequiredMixin, TemplateView):
})
return
self
.
render_to_response
(
context
)
def
__create_normal
(
self
,
request
,
*
args
,
**
kwargs
):
user
=
request
.
user
template
=
InstanceTemplate
.
objects
.
get
(
pk
=
request
.
POST
.
get
(
"template"
))
# permission check
if
not
template
.
has_level
(
request
.
user
,
'user'
):
raise
PermissionDenied
()
args
=
{
"template"
:
template
,
"owner"
:
user
}
instances
=
[
Instance
.
create_from_template
(
**
args
)]
def
__create_normal
(
self
,
request
,
template
,
*
args
,
**
kwargs
):
instances
=
[
Instance
.
create_from_template
(
template
=
template
,
owner
=
request
.
user
)]
return
self
.
__deploy
(
request
,
instances
)
def
__create_customized
(
self
,
request
,
*
args
,
**
kwargs
):
def
__create_customized
(
self
,
request
,
template
,
*
args
,
**
kwargs
):
user
=
request
.
user
# no form yet, using POST directly:
template
=
get_object_or_404
(
InstanceTemplate
,
pk
=
request
.
POST
.
get
(
"template"
))
form
=
self
.
form_class
(
request
.
POST
,
user
=
request
.
user
,
template
=
template
)
if
not
form
.
is_valid
():
return
self
.
get
(
request
,
form
,
*
args
,
**
kwargs
)
post
=
form
.
cleaned_data
if
not
template
.
has_level
(
user
,
'user'
):
raise
PermissionDenied
()
ikwargs
=
{
'name'
:
post
[
'name'
],
'template'
:
template
,
...
...
@@ -1071,6 +1096,8 @@ class VmCreate(LoginRequiredMixin, TemplateView):
if
not
request
.
user
.
has_perm
(
'vm.create_vm'
):
raise
PermissionDenied
()
template
=
self
.
get_template
(
request
,
request
.
POST
.
get
(
"template"
))
# limit chekcs
try
:
limit
=
user
.
profile
.
instance_limit
...
...
@@ -1096,7 +1123,7 @@ class VmCreate(LoginRequiredMixin, TemplateView):
request
.
POST
.
get
(
"customized"
)
is
None
else
self
.
__create_customized
)
return
create_func
(
request
,
*
args
,
**
kwargs
)
return
create_func
(
request
,
template
,
*
args
,
**
kwargs
)
@require_GET
...
...
@@ -1111,56 +1138,6 @@ def get_vm_screenshot(request, pk):
return
HttpResponse
(
image
,
mimetype
=
"image/png"
)
class
InterfaceDeleteView
(
DeleteView
):
model
=
Interface
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/confirm/ajax-delete.html'
]
else
:
return
[
'dashboard/confirm/base-delete.html'
]
def
get_context_data
(
self
,
**
kwargs
):
context
=
super
(
InterfaceDeleteView
,
self
)
.
get_context_data
(
**
kwargs
)
interface
=
self
.
get_object
()
context
[
'text'
]
=
_
(
"Are you sure you want to remove this interface "
"from <strong>
%(vm)
s</strong>?"
%
{
'vm'
:
interface
.
instance
.
name
})
return
context
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
object
=
self
.
get_object
()
instance
=
self
.
object
.
instance
if
not
instance
.
has_level
(
request
.
user
,
"owner"
):
raise
PermissionDenied
()
instance
.
remove_interface
(
interface
=
self
.
object
,
user
=
request
.
user
)
success_url
=
self
.
get_success_url
()
success_message
=
_
(
"Interface successfully deleted."
)
if
request
.
is_ajax
():
return
HttpResponse
(
json
.
dumps
(
{
'message'
:
success_message
,
'removed_network'
:
{
'vlan'
:
self
.
object
.
vlan
.
name
,
'vlan_pk'
:
self
.
object
.
vlan
.
pk
,
'managed'
:
self
.
object
.
host
is
not
None
,
}}),
content_type
=
"application/json"
,
)
else
:
messages
.
success
(
request
,
success_message
)
return
HttpResponseRedirect
(
"
%
s#network"
%
success_url
)
def
get_success_url
(
self
):
redirect
=
self
.
request
.
POST
.
get
(
"next"
)
if
redirect
:
return
redirect
self
.
object
.
instance
.
get_absolute_url
()
class
InstanceActivityDetail
(
CheckedDetailView
):
model
=
InstanceActivity
context_object_name
=
'instanceactivity'
# much simpler to mock object
...
...
circle/vm/models/instance.py
View file @
7fed7d2c
...
...
@@ -354,6 +354,12 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
def
create
(
cls
,
params
,
disks
,
networks
,
req_traits
,
tags
):
""" Create new Instance object.
"""
# permission check
for
network
in
networks
:
if
not
network
.
vlan
.
has_level
(
params
[
'owner'
],
'user'
):
raise
PermissionDenied
()
# create instance and do additional setup
inst
=
cls
(
**
params
)
...
...
@@ -408,10 +414,6 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
networks
=
(
template
.
interface_set
.
all
()
if
networks
is
None
else
networks
)
for
network
in
networks
:
if
not
network
.
vlan
.
has_level
(
owner
,
'user'
):
raise
PermissionDenied
()
req_traits
=
(
template
.
req_traits
.
all
()
if
req_traits
is
None
else
req_traits
)
...
...
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