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
a8fb5a86
authored
Jul 07, 2014
by
Bach Dániel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature-fix-acls'
Conflicts: circle/vm/operations.py
parents
f7488817
f32bb524
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
283 additions
and
72 deletions
+283
-72
circle/circle/__init__.py
+20
-0
circle/common/operations.py
+5
-2
circle/dashboard/fixtures/test-vm-fixture.json
+18
-0
circle/dashboard/forms.py
+44
-13
circle/dashboard/static/dashboard/bootstrap-slider/bootstrap-slider.js
+3
-0
circle/dashboard/static/dashboard/vm-details.js
+12
-3
circle/dashboard/templates/dashboard/_template-choose.html
+2
-0
circle/dashboard/templates/dashboard/vm-detail/console.html
+6
-0
circle/dashboard/templates/dashboard/vm-detail/resources.html
+10
-1
circle/dashboard/templates/dashboard/vm-list/column-admin.html
+1
-1
circle/dashboard/tests/test_mockedviews.py
+6
-4
circle/dashboard/tests/test_views.py
+6
-0
circle/dashboard/urls.py
+1
-3
circle/dashboard/views.py
+45
-44
circle/storage/models.py
+3
-0
circle/vm/models/instance.py
+5
-0
circle/vm/operations.py
+91
-1
circle/vm/tests/test_models.py
+5
-0
No files found.
circle/circle/__init__.py
View file @
a8fb5a86
# register a signal do update permissions every migration.
# This is based on app django_extensions update_permissions command
from
south.signals
import
post_migrate
def
update_permissions_after_migration
(
app
,
**
kwargs
):
"""
Update app permission just after every migration.
This is based on app django_extensions update_permissions
management command.
"""
from
django.conf
import
settings
from
django.db.models
import
get_app
,
get_models
from
django.contrib.auth.management
import
create_permissions
create_permissions
(
get_app
(
app
),
get_models
(),
2
if
settings
.
DEBUG
else
0
)
post_migrate
.
connect
(
update_permissions_after_migration
)
circle/common/operations.py
View file @
a8fb5a86
...
@@ -20,7 +20,7 @@ from logging import getLogger
...
@@ -20,7 +20,7 @@ from logging import getLogger
from
.models
import
activity_context
,
has_suffix
from
.models
import
activity_context
,
has_suffix
from
django.core.exceptions
import
PermissionDenied
from
django.core.exceptions
import
PermissionDenied
,
ImproperlyConfigured
logger
=
getLogger
(
__name__
)
logger
=
getLogger
(
__name__
)
...
@@ -30,7 +30,7 @@ class Operation(object):
...
@@ -30,7 +30,7 @@ class Operation(object):
"""Base class for VM operations.
"""Base class for VM operations.
"""
"""
async_queue
=
'localhost.man'
async_queue
=
'localhost.man'
required_perms
=
()
required_perms
=
None
do_not_call_in_templates
=
True
do_not_call_in_templates
=
True
abortable
=
False
abortable
=
False
has_percentage
=
False
has_percentage
=
False
...
@@ -141,6 +141,9 @@ class Operation(object):
...
@@ -141,6 +141,9 @@ class Operation(object):
pass
pass
def
check_auth
(
self
,
user
):
def
check_auth
(
self
,
user
):
if
self
.
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
(
self
.
required_perms
):
raise
PermissionDenied
(
"
%
s doesn't have the required permissions."
raise
PermissionDenied
(
"
%
s doesn't have the required permissions."
%
user
)
%
user
)
...
...
circle/dashboard/fixtures/test-vm-fixture.json
View file @
a8fb5a86
...
@@ -1240,6 +1240,24 @@
...
@@ -1240,6 +1240,24 @@
}
}
},
},
{
{
"pk"
:
1367
,
"model"
:
"auth.permission"
,
"fields"
:
{
"codename"
:
"create_vm"
,
"name"
:
"Can create a new VM."
,
"content_type"
:
28
}
},
{
"pk"
:
1368
,
"model"
:
"auth.permission"
,
"fields"
:
{
"codename"
:
"access_console"
,
"name"
:
"Can access the graphical console of a VM."
,
"content_type"
:
28
}
},
{
"pk"
:
1
,
"pk"
:
1
,
"model"
:
"auth.group"
,
"model"
:
"auth.group"
,
"fields"
:
{
"fields"
:
{
...
...
circle/dashboard/forms.py
View file @
a8fb5a86
...
@@ -25,6 +25,7 @@ from django.contrib.auth.forms import (
...
@@ -25,6 +25,7 @@ from django.contrib.auth.forms import (
)
)
from
django.contrib.auth.models
import
User
,
Group
from
django.contrib.auth.models
import
User
,
Group
from
django.core.validators
import
URLValidator
from
django.core.validators
import
URLValidator
from
django.core.exceptions
import
PermissionDenied
,
ValidationError
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.layout
import
(
from
crispy_forms.layout
import
(
...
@@ -593,6 +594,17 @@ class TemplateForm(forms.ModelForm):
...
@@ -593,6 +594,17 @@ class TemplateForm(forms.ModelForm):
n
=
self
.
instance
.
interface_set
.
values_list
(
"vlan"
,
flat
=
True
)
n
=
self
.
instance
.
interface_set
.
values_list
(
"vlan"
,
flat
=
True
)
self
.
initial
[
'networks'
]
=
n
self
.
initial
[
'networks'
]
=
n
self
.
allowed_fields
=
(
'name'
,
'access_method'
,
'description'
,
'system'
,
'tags'
)
if
self
.
user
.
has_perm
(
'vm.change_template_resources'
):
self
.
allowed_fields
+=
tuple
(
set
(
self
.
fields
.
keys
())
-
set
([
'raw_data'
]))
if
self
.
user
.
is_superuser
:
self
.
allowed_fields
+=
(
'raw_data'
,
)
for
name
,
field
in
self
.
fields
.
items
():
if
name
not
in
self
.
allowed_fields
:
field
.
widget
.
attrs
[
'disabled'
]
=
'disabled'
if
not
self
.
instance
.
pk
and
len
(
self
.
errors
)
<
1
:
if
not
self
.
instance
.
pk
and
len
(
self
.
errors
)
<
1
:
self
.
instance
.
priority
=
20
self
.
instance
.
priority
=
20
self
.
instance
.
ram_size
=
512
self
.
instance
.
ram_size
=
512
...
@@ -603,14 +615,35 @@ class TemplateForm(forms.ModelForm):
...
@@ -603,14 +615,35 @@ class TemplateForm(forms.ModelForm):
return
User
.
objects
.
get
(
pk
=
self
.
instance
.
owner
.
pk
)
return
User
.
objects
.
get
(
pk
=
self
.
instance
.
owner
.
pk
)
return
self
.
user
return
self
.
user
def
clean_raw_data
(
self
):
def
_clean_fields
(
self
):
# if raw_data has changed and the user is not superuser
try
:
if
"raw_data"
in
self
.
changed_data
and
not
self
.
user
.
is_superuser
:
old
=
InstanceTemplate
.
objects
.
get
(
pk
=
self
.
instance
.
pk
)
old_raw_data
=
InstanceTemplate
.
objects
.
get
(
except
InstanceTemplate
.
DoesNotExist
:
pk
=
self
.
instance
.
pk
)
.
raw_data
old
=
None
return
old_raw_data
for
name
,
field
in
self
.
fields
.
items
():
else
:
if
name
in
self
.
allowed_fields
:
return
self
.
cleaned_data
[
'raw_data'
]
value
=
field
.
widget
.
value_from_datadict
(
self
.
data
,
self
.
files
,
self
.
add_prefix
(
name
))
try
:
if
isinstance
(
field
,
forms
.
FileField
):
initial
=
self
.
initial
.
get
(
name
,
field
.
initial
)
value
=
field
.
clean
(
value
,
initial
)
else
:
value
=
field
.
clean
(
value
)
self
.
cleaned_data
[
name
]
=
value
if
hasattr
(
self
,
'clean_
%
s'
%
name
):
value
=
getattr
(
self
,
'clean_
%
s'
%
name
)()
self
.
cleaned_data
[
name
]
=
value
except
ValidationError
as
e
:
self
.
_errors
[
name
]
=
self
.
error_class
(
e
.
messages
)
if
name
in
self
.
cleaned_data
:
del
self
.
cleaned_data
[
name
]
elif
old
:
if
name
==
'networks'
:
self
.
cleaned_data
[
name
]
=
[
i
.
vlan
for
i
in
self
.
instance
.
interface_set
.
all
()]
else
:
self
.
cleaned_data
[
name
]
=
getattr
(
old
,
name
)
def
save
(
self
,
commit
=
True
):
def
save
(
self
,
commit
=
True
):
data
=
self
.
cleaned_data
data
=
self
.
cleaned_data
...
@@ -624,6 +657,8 @@ class TemplateForm(forms.ModelForm):
...
@@ -624,6 +657,8 @@ class TemplateForm(forms.ModelForm):
networks
=
InterfaceTemplate
.
objects
.
filter
(
networks
=
InterfaceTemplate
.
objects
.
filter
(
template
=
self
.
instance
)
.
values_list
(
"vlan"
,
flat
=
True
)
template
=
self
.
instance
)
.
values_list
(
"vlan"
,
flat
=
True
)
for
m
in
data
[
'networks'
]:
for
m
in
data
[
'networks'
]:
if
not
m
.
has_level
(
self
.
user
,
"user"
):
raise
PermissionDenied
()
if
m
.
pk
not
in
networks
:
if
m
.
pk
not
in
networks
:
InterfaceTemplate
(
vlan
=
m
,
managed
=
m
.
managed
,
InterfaceTemplate
(
vlan
=
m
,
managed
=
m
.
managed
,
template
=
self
.
instance
)
.
save
()
template
=
self
.
instance
)
.
save
()
...
@@ -635,10 +670,6 @@ class TemplateForm(forms.ModelForm):
...
@@ -635,10 +670,6 @@ class TemplateForm(forms.ModelForm):
@property
@property
def
helper
(
self
):
def
helper
(
self
):
kwargs_raw_data
=
{}
if
not
self
.
user
.
is_superuser
:
kwargs_raw_data
[
'readonly'
]
=
None
helper
=
FormHelper
()
helper
=
FormHelper
()
helper
.
layout
=
Layout
(
helper
.
layout
=
Layout
(
Field
(
"name"
),
Field
(
"name"
),
...
@@ -690,7 +721,7 @@ class TemplateForm(forms.ModelForm):
...
@@ -690,7 +721,7 @@ class TemplateForm(forms.ModelForm):
_
(
"Virtual machine settings"
),
_
(
"Virtual machine settings"
),
Field
(
'access_method'
),
Field
(
'access_method'
),
Field
(
'boot_menu'
),
Field
(
'boot_menu'
),
Field
(
'raw_data'
,
**
kwargs_raw_data
),
Field
(
'raw_data'
),
Field
(
'req_traits'
),
Field
(
'req_traits'
),
Field
(
'description'
),
Field
(
'description'
),
Field
(
"parent"
,
type
=
"hidden"
),
Field
(
"parent"
,
type
=
"hidden"
),
...
...
circle/dashboard/static/dashboard/bootstrap-slider/bootstrap-slider.js
View file @
a8fb5a86
...
@@ -192,6 +192,9 @@
...
@@ -192,6 +192,9 @@
},
},
mousedown
:
function
(
ev
)
{
mousedown
:
function
(
ev
)
{
if
(
this
.
element
[
0
].
disabled
)
{
return
false
;
}
// Touch: Get the original event:
// Touch: Get the original event:
if
(
this
.
touchCapable
&&
ev
.
type
===
'touchstart'
)
{
if
(
this
.
touchCapable
&&
ev
.
type
===
'touchstart'
)
{
...
...
circle/dashboard/static/dashboard/vm-details.js
View file @
a8fb5a86
...
@@ -11,13 +11,14 @@ $(function() {
...
@@ -11,13 +11,14 @@ $(function() {
/* save resources */
/* save resources */
$
(
'#vm-details-resources-save'
).
click
(
function
()
{
$
(
'#vm-details-resources-save'
).
click
(
function
()
{
$
(
'i.icon-save'
,
this
).
removeClass
(
"icon-save"
).
addClass
(
"icon-refresh icon-spin"
);
$
(
'i.icon-save'
,
this
).
removeClass
(
"icon-save"
).
addClass
(
"icon-refresh icon-spin"
);
var
vm
=
$
(
this
).
data
(
"vm"
);
$
.
ajax
({
$
.
ajax
({
type
:
'POST'
,
type
:
'POST'
,
url
:
location
.
href
,
url
:
"/dashboard/vm/"
+
vm
+
"/op/resources_change/"
,
data
:
$
(
'#vm-details-resources-form'
).
serialize
(),
data
:
$
(
'#vm-details-resources-form'
).
serialize
(),
success
:
function
(
data
,
textStatus
,
xhr
)
{
success
:
function
(
data
,
textStatus
,
xhr
)
{
addMessage
(
data
[
'message'
],
'success'
);
$
(
"#vm-details-resources-save i"
).
removeClass
(
'icon-refresh icon-spin'
).
addClass
(
"icon-save"
);
$
(
"#vm-details-resources-save i"
).
removeClass
(
'icon-refresh icon-spin'
).
addClass
(
"icon-save"
);
$
(
'a[href="#activity"]'
).
trigger
(
"click"
);
},
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
error
:
function
(
xhr
,
textStatus
,
error
)
{
$
(
"#vm-details-resources-save i"
).
removeClass
(
'icon-refresh icon-spin'
).
addClass
(
"icon-save"
);
$
(
"#vm-details-resources-save i"
).
removeClass
(
'icon-refresh icon-spin'
).
addClass
(
"icon-save"
);
...
@@ -330,7 +331,7 @@ function decideActivityRefresh() {
...
@@ -330,7 +331,7 @@ function decideActivityRefresh() {
/* unescapes html got via the request, also removes whitespaces and replaces all ' with " */
/* unescapes html got via the request, also removes whitespaces and replaces all ' with " */
function
unescapeHTML
(
html
)
{
function
unescapeHTML
(
html
)
{
return
html
.
replace
(
/</g
,
'<'
).
replace
(
/>/g
,
'>'
).
replace
(
/&/g
,
'&'
).
replace
(
/–/g
,
"–"
).
replace
(
/
\/
/g
,
""
).
replace
(
/'/g
,
'"'
).
replace
(
/ /g
,
''
);
return
html
.
replace
(
/</g
,
'<'
).
replace
(
/>/g
,
'>'
).
replace
(
/&/g
,
'&'
).
replace
(
/–/g
,
"–"
).
replace
(
/
\/
/g
,
""
).
replace
(
/'/g
,
'"'
).
replace
(
/
'/g
,
"'"
).
replace
(
/
/g
,
''
);
}
}
/* the html page contains some tags that were modified via js (titles for example), we delete these
/* the html page contains some tags that were modified via js (titles for example), we delete these
...
@@ -367,6 +368,14 @@ function checkNewActivity(only_status, runs) {
...
@@ -367,6 +368,14 @@ function checkNewActivity(only_status, runs) {
$
(
"[data-target=#_console]"
).
attr
(
"data-toggle"
,
"_pill"
).
attr
(
"href"
,
"#"
).
parent
(
"li"
).
addClass
(
"disabled"
);
$
(
"[data-target=#_console]"
).
attr
(
"data-toggle"
,
"_pill"
).
attr
(
"href"
,
"#"
).
parent
(
"li"
).
addClass
(
"disabled"
);
}
}
if
(
data
[
'status'
]
==
"STOPPED"
)
{
$
(
".enabled-when-stopped"
).
prop
(
"disabled"
,
false
);
$
(
".hide-when-stopped"
).
hide
();
}
else
{
$
(
".enabled-when-stopped"
).
prop
(
"disabled"
,
true
);
$
(
".hide-when-stopped"
).
show
();
}
if
(
runs
>
0
&&
decideActivityRefresh
())
{
if
(
runs
>
0
&&
decideActivityRefresh
())
{
setTimeout
(
setTimeout
(
function
()
{
checkNewActivity
(
only_status
,
runs
+
1
)},
function
()
{
checkNewActivity
(
only_status
,
runs
+
1
)},
...
...
circle/dashboard/templates/dashboard/_template-choose.html
View file @
a8fb5a86
...
@@ -16,10 +16,12 @@
...
@@ -16,10 +16,12 @@
<div
class=
"clearfix"
></div>
<div
class=
"clearfix"
></div>
</div>
</div>
{% endfor %}
{% endfor %}
{% if perms.vm.create_base_template %}
<div
class=
"panel panel-default template-choose-list-element"
>
<div
class=
"panel panel-default template-choose-list-element"
>
<input
type=
"radio"
name=
"parent"
value=
"base_vm"
/>
<input
type=
"radio"
name=
"parent"
value=
"base_vm"
/>
{% trans "Create a new base VM without disk" %}
{% trans "Create a new base VM without disk" %}
</div>
</div>
{% endif %}
<button
type=
"submit"
id=
"template-choose-next-button"
class=
"btn btn-success pull-right"
>
{% trans "Next" %}
</button>
<button
type=
"submit"
id=
"template-choose-next-button"
class=
"btn btn-success pull-right"
>
{% trans "Next" %}
</button>
<div
class=
"clearfix"
></div>
<div
class=
"clearfix"
></div>
</div>
</div>
...
...
circle/dashboard/templates/dashboard/vm-detail/console.html
View file @
a8fb5a86
{% load i18n %}
{% load i18n %}
<div
class=
"btn-toolbar"
>
<div
class=
"btn-toolbar"
>
{% if perms.vm.access_console %}
<button
id=
"sendCtrlAltDelButton"
class=
"btn btn-danger btn-sm"
>
{% trans "Send Ctrl+Alt+Del" %}
</button>
<button
id=
"sendCtrlAltDelButton"
class=
"btn btn-danger btn-sm"
>
{% trans "Send Ctrl+Alt+Del" %}
</button>
<button
id=
"sendPasswordButton"
class=
"btn btn-default btn-sm"
>
{% trans "Type password" %}
</button>
<button
id=
"sendPasswordButton"
class=
"btn btn-default btn-sm"
>
{% trans "Type password" %}
</button>
{% endif %}
<button
id=
"getScreenshotButton"
class=
"btn btn-info btn-sm pull-right"
data-vm-pk=
"{{ instance.pk }}"
><i
class=
"icon-picture"
></i>
{% trans "Screenshot" %}
</button>
<button
id=
"getScreenshotButton"
class=
"btn btn-info btn-sm pull-right"
data-vm-pk=
"{{ instance.pk }}"
><i
class=
"icon-picture"
></i>
{% trans "Screenshot" %}
</button>
</div>
</div>
{% if perms.vm.access_console %}
<div
class=
"alert alert-info"
id=
"noVNC_status"
>
<div
class=
"alert alert-info"
id=
"noVNC_status"
>
</div>
</div>
{% endif %}
<div
id=
"vm-console-screenshot"
>
<div
id=
"vm-console-screenshot"
>
<button
class=
"btn btn-danger btn-sm pull-right"
>
{% trans "Close" %}
</button>
<button
class=
"btn btn-danger btn-sm pull-right"
>
{% trans "Close" %}
</button>
...
@@ -14,6 +18,7 @@
...
@@ -14,6 +18,7 @@
<hr
/>
<hr
/>
</div>
</div>
{% if perms.vm.access_console %}
<canvas
id=
"noVNC_canvas"
width=
"640px"
height=
"20px"
>
Canvas not supported.
<canvas
id=
"noVNC_canvas"
width=
"640px"
height=
"20px"
>
Canvas not supported.
</canvas>
</canvas>
...
@@ -22,3 +27,4 @@
...
@@ -22,3 +27,4 @@
var
INCLUDE_URI
=
'{{ STATIC_URL }}dashboard/novnc/'
;
var
INCLUDE_URI
=
'{{ STATIC_URL }}dashboard/novnc/'
;
var
VNC_URL
=
"{{ vnc_url }}"
;
var
VNC_URL
=
"{{ vnc_url }}"
;
</script>
</script>
{% endif %}
circle/dashboard/templates/dashboard/vm-detail/resources.html
View file @
a8fb5a86
...
@@ -33,11 +33,20 @@
...
@@ -33,11 +33,20 @@
</div>
</div>
</p>
</p>
{% if can_change_resources %}
<p
class=
"row"
>
<p
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"col-sm-12"
>
<button
type=
"submit"
class=
"btn btn-success btn-sm"
id=
"vm-details-resources-save"
><i
class=
"icon-save"
></i>
{% trans "Save resources" %}
</button>
<button
type=
"submit"
class=
"btn btn-success btn-sm enabled-when-stopped"
id=
"vm-details-resources-save"
data-vm=
"{{ instance.pk }}"
{%
if
not
op
.
resources_change
%}
disabled
{%
endif
%}
>
<i
class=
"icon-save"
></i>
{% trans "Save resources" %}
</button>
<span
class=
"hide-when-stopped"
{%
if
op
.
resources_change
%}
style=
"display: none;"
{%
endif
%}
>
{% trans "Stop your VM to change resources." %}
</span>
</div>
</div>
</p>
</p>
{% endif %}
</form>
</form>
<hr
/>
<hr
/>
...
...
circle/dashboard/templates/dashboard/vm-list/column-admin.html
View file @
a8fb5a86
<a
href=
"{% url "
dashboard
.
v
iews
.
vm-
migrate
"
pk=
record.pk
%}"
class=
"btn btn-default btn-xs vm-migrate"
data-vm-pk=
"{{ record.pk }}"
title
data-original-title=
"Migrate"
>
<a
href=
"{% url "
dashboard
.
v
m
.
op
.
migrate
"
pk=
record.pk
%}"
class=
"btn btn-default btn-xs vm-migrate"
data-vm-pk=
"{{ record.pk }}"
title
data-original-title=
"Migrate"
>
<i
class=
"icon-truck"
></i>
<i
class=
"icon-truck"
></i>
</a>
</a>
<a
id=
"vm-list-rename-button"
class=
"btn btn-default btn-xs"
title
data-original-title=
"Rename"
>
<a
id=
"vm-list-rename-button"
class=
"btn btn-default btn-xs"
title
data-original-title=
"Rename"
>
...
...
circle/dashboard/tests/test_mockedviews.py
View file @
a8fb5a86
...
@@ -159,7 +159,7 @@ class VmOperationViewTestCase(unittest.TestCase):
...
@@ -159,7 +159,7 @@ class VmOperationViewTestCase(unittest.TestCase):
assert
not
msg
.
error
.
called
assert
not
msg
.
error
.
called
def
test_migrate_failed
(
self
):
def
test_migrate_failed
(
self
):
request
=
FakeRequestFactory
(
POST
=
{
'node'
:
1
})
request
=
FakeRequestFactory
(
POST
=
{
'node'
:
1
}
,
superuser
=
True
)
view
=
vm_ops
[
'migrate'
]
view
=
vm_ops
[
'migrate'
]
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
...
@@ -177,7 +177,7 @@ class VmOperationViewTestCase(unittest.TestCase):
...
@@ -177,7 +177,7 @@ class VmOperationViewTestCase(unittest.TestCase):
assert
msg
.
error
.
called
assert
msg
.
error
.
called
def
test_migrate_template
(
self
):
def
test_migrate_template
(
self
):
request
=
FakeRequestFactory
()
request
=
FakeRequestFactory
(
superuser
=
True
)
view
=
vm_ops
[
'migrate'
]
view
=
vm_ops
[
'migrate'
]
with
patch
.
object
(
view
,
'get_object'
)
as
go
:
with
patch
.
object
(
view
,
'get_object'
)
as
go
:
...
@@ -190,7 +190,7 @@ class VmOperationViewTestCase(unittest.TestCase):
...
@@ -190,7 +190,7 @@ class VmOperationViewTestCase(unittest.TestCase):
view
.
as_view
()(
request
,
pk
=
1234
)
.
render
()
.
status_code
,
200
)
view
.
as_view
()(
request
,
pk
=
1234
)
.
render
()
.
status_code
,
200
)
def
test_save_as_wo_name
(
self
):
def
test_save_as_wo_name
(
self
):
request
=
FakeRequestFactory
(
POST
=
{})
request
=
FakeRequestFactory
(
POST
=
{}
,
has_perms_mock
=
True
)
view
=
vm_ops
[
'save_as_template'
]
view
=
vm_ops
[
'save_as_template'
]
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
...
@@ -224,7 +224,7 @@ class VmOperationViewTestCase(unittest.TestCase):
...
@@ -224,7 +224,7 @@ class VmOperationViewTestCase(unittest.TestCase):
assert
not
msg
.
error
.
called
assert
not
msg
.
error
.
called
def
test_save_as_template
(
self
):
def
test_save_as_template
(
self
):
request
=
FakeRequestFactory
()
request
=
FakeRequestFactory
(
has_perms_mock
=
True
)
view
=
vm_ops
[
'save_as_template'
]
view
=
vm_ops
[
'save_as_template'
]
with
patch
.
object
(
view
,
'get_object'
)
as
go
:
with
patch
.
object
(
view
,
'get_object'
)
as
go
:
...
@@ -246,6 +246,8 @@ def FakeRequestFactory(*args, **kwargs):
...
@@ -246,6 +246,8 @@ def FakeRequestFactory(*args, **kwargs):
user
=
UserFactory
()
user
=
UserFactory
()
user
.
is_authenticated
=
lambda
:
kwargs
.
get
(
'authenticated'
,
True
)
user
.
is_authenticated
=
lambda
:
kwargs
.
get
(
'authenticated'
,
True
)
user
.
is_superuser
=
kwargs
.
get
(
'superuser'
,
False
)
user
.
is_superuser
=
kwargs
.
get
(
'superuser'
,
False
)
if
kwargs
.
get
(
'has_perms_mock'
,
False
):
user
.
has_perms
=
MagicMock
(
return_value
=
True
)
request
=
HttpRequest
()
request
=
HttpRequest
()
request
.
user
=
user
request
.
user
=
user
...
...
circle/dashboard/tests/test_views.py
View file @
a8fb5a86
...
@@ -63,6 +63,8 @@ class VmDetailTest(LoginMixin, TestCase):
...
@@ -63,6 +63,8 @@ class VmDetailTest(LoginMixin, TestCase):
self
.
g1
.
user_set
.
add
(
self
.
u1
)
self
.
g1
.
user_set
.
add
(
self
.
u1
)
self
.
g1
.
user_set
.
add
(
self
.
u2
)
self
.
g1
.
user_set
.
add
(
self
.
u2
)
self
.
g1
.
save
()
self
.
g1
.
save
()
self
.
u1
.
user_permissions
.
add
(
Permission
.
objects
.
get
(
codename
=
'create_vm'
))
settings
[
"default_vlangroup"
]
=
'public'
settings
[
"default_vlangroup"
]
=
'public'
VlanGroup
.
objects
.
create
(
name
=
'public'
)
VlanGroup
.
objects
.
create
(
name
=
'public'
)
...
@@ -1544,6 +1546,8 @@ class VmDetailVncTest(LoginMixin, TestCase):
...
@@ -1544,6 +1546,8 @@ class VmDetailVncTest(LoginMixin, TestCase):
inst
.
node
=
Node
.
objects
.
all
()[
0
]
inst
.
node
=
Node
.
objects
.
all
()[
0
]
inst
.
save
()
inst
.
save
()
inst
.
set_level
(
self
.
u1
,
'operator'
)
inst
.
set_level
(
self
.
u1
,
'operator'
)
self
.
u1
.
user_permissions
.
add
(
Permission
.
objects
.
get
(
codename
=
'access_console'
))
response
=
c
.
get
(
'/dashboard/vm/1/vnctoken/'
)
response
=
c
.
get
(
'/dashboard/vm/1/vnctoken/'
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
...
@@ -1554,6 +1558,8 @@ class VmDetailVncTest(LoginMixin, TestCase):
...
@@ -1554,6 +1558,8 @@ class VmDetailVncTest(LoginMixin, TestCase):
inst
.
node
=
Node
.
objects
.
all
()[
0
]
inst
.
node
=
Node
.
objects
.
all
()[
0
]
inst
.
save
()
inst
.
save
()
inst
.
set_level
(
self
.
u1
,
'user'
)
inst
.
set_level
(
self
.
u1
,
'user'
)
self
.
u1
.
user_permissions
.
add
(
Permission
.
objects
.
get
(
codename
=
'access_console'
))
response
=
c
.
get
(
'/dashboard/vm/1/vnctoken/'
)
response
=
c
.
get
(
'/dashboard/vm/1/vnctoken/'
)
self
.
assertEqual
(
response
.
status_code
,
403
)
self
.
assertEqual
(
response
.
status_code
,
403
)
...
...
circle/dashboard/urls.py
View file @
a8fb5a86
...
@@ -28,7 +28,7 @@ from .views import (
...
@@ -28,7 +28,7 @@ from .views import (
NotificationView
,
PortDelete
,
TemplateAclUpdateView
,
TemplateCreate
,
NotificationView
,
PortDelete
,
TemplateAclUpdateView
,
TemplateCreate
,
TemplateDelete
,
TemplateDetail
,
TemplateList
,
TransferOwnershipConfirmView
,
TemplateDelete
,
TemplateDetail
,
TemplateList
,
TransferOwnershipConfirmView
,
TransferOwnershipView
,
vm_activity
,
VmCreate
,
VmDelete
,
VmDetailView
,
TransferOwnershipView
,
vm_activity
,
VmCreate
,
VmDelete
,
VmDetailView
,
VmDetailVncTokenView
,
VmGraphView
,
VmList
,
VmMassDelete
,
VmMigrateView
,
VmDetailVncTokenView
,
VmGraphView
,
VmList
,
VmMassDelete
,
VmRenewView
,
DiskRemoveView
,
get_disk_download_status
,
InterfaceDeleteView
,
VmRenewView
,
DiskRemoveView
,
get_disk_download_status
,
InterfaceDeleteView
,
GroupRemoveAclUserView
,
GroupRemoveAclGroupView
,
GroupRemoveUserView
,
GroupRemoveAclUserView
,
GroupRemoveAclGroupView
,
GroupRemoveUserView
,
GroupRemoveFutureUserView
,
GroupRemoveFutureUserView
,
...
@@ -83,8 +83,6 @@ urlpatterns = patterns(
...
@@ -83,8 +83,6 @@ urlpatterns = patterns(
url
(
r'^vm/mass-delete/'
,
VmMassDelete
.
as_view
(),
url
(
r'^vm/mass-delete/'
,
VmMassDelete
.
as_view
(),
name
=
'dashboard.view.mass-delete-vm'
),
name
=
'dashboard.view.mass-delete-vm'
),
url
(
r'^vm/(?P<pk>\d+)/activity/$'
,
vm_activity
),
url
(
r'^vm/(?P<pk>\d+)/activity/$'
,
vm_activity
),
url
(
r'^vm/(?P<pk>\d+)/migrate/$'
,
VmMigrateView
.
as_view
(),
name
=
'dashboard.views.vm-migrate'
),
url
(
r'^vm/(?P<pk>\d+)/renew/((?P<key>.*)/?)$'
,
VmRenewView
.
as_view
(),
url
(
r'^vm/(?P<pk>\d+)/renew/((?P<key>.*)/?)$'
,
VmRenewView
.
as_view
(),
name
=
'dashboard.views.vm-renew'
),
name
=
'dashboard.views.vm-renew'
),
url
(
r'^vm/activity/(?P<pk>\d+)/$'
,
InstanceActivityDetail
.
as_view
(),
url
(
r'^vm/activity/(?P<pk>\d+)/$'
,
InstanceActivityDetail
.
as_view
(),
...
...
circle/dashboard/views.py
View file @
a8fb5a86
...
@@ -244,6 +244,8 @@ class VmDetailVncTokenView(CheckedDetailView):
...
@@ -244,6 +244,8 @@ class VmDetailVncTokenView(CheckedDetailView):
self
.
object
=
self
.
get_object
()
self
.
object
=
self
.
get_object
()
if
not
self
.
object
.
has_level
(
request
.
user
,
'operator'
):
if
not
self
.
object
.
has_level
(
request
.
user
,
'operator'
):
raise
PermissionDenied
()
raise
PermissionDenied
()
if
not
request
.
user
.
has_perm
(
'vm.access_console'
):
raise
PermissionDenied
()
if
self
.
object
.
node
:
if
self
.
object
.
node
:
with
instance_activity
(
code_suffix
=
'console-accessed'
,
with
instance_activity
(
code_suffix
=
'console-accessed'
,
instance
=
self
.
object
,
user
=
request
.
user
,
instance
=
self
.
object
,
user
=
request
.
user
,
...
@@ -294,13 +296,14 @@ class VmDetailView(CheckedDetailView):
...
@@ -294,13 +296,14 @@ class VmDetailView(CheckedDetailView):
if
self
.
request
.
user
.
is_superuser
:
if
self
.
request
.
user
.
is_superuser
:
context
[
'traits_form'
]
=
TraitsForm
(
instance
=
instance
)
context
[
'traits_form'
]
=
TraitsForm
(
instance
=
instance
)
context
[
'raw_data_form'
]
=
RawDataForm
(
instance
=
instance
)
context
[
'raw_data_form'
]
=
RawDataForm
(
instance
=
instance
)
# resources change perm
context
[
'can_change_resources'
]
=
self
.
request
.
user
.
has_perm
(
"vm.change_resources"
)
return
context
return
context
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
if
(
request
.
POST
.
get
(
'ram-size'
)
and
request
.
POST
.
get
(
'cpu-count'
)
and
request
.
POST
.
get
(
'cpu-priority'
)):
return
self
.
__set_resources
(
request
)
options
=
{
options
=
{
'change_password'
:
self
.
__change_password
,
'change_password'
:
self
.
__change_password
,
'new_name'
:
self
.
__set_name
,
'new_name'
:
self
.
__set_name
,
...
@@ -328,33 +331,6 @@ class VmDetailView(CheckedDetailView):
...
@@ -328,33 +331,6 @@ class VmDetailView(CheckedDetailView):
return
redirect
(
reverse_lazy
(
"dashboard.views.detail"
,
return
redirect
(
reverse_lazy
(
"dashboard.views.detail"
,
kwargs
=
{
'pk'
:
self
.
object
.
pk
}))
kwargs
=
{
'pk'
:
self
.
object
.
pk
}))
def
__set_resources
(
self
,
request
):
self
.
object
=
self
.
get_object
()
if
not
self
.
object
.
has_level
(
request
.
user
,
'owner'
):
raise
PermissionDenied
()
if
not
request
.
user
.
has_perm
(
'vm.change_resources'
):
raise
PermissionDenied
()
resources
=
{
'num_cores'
:
request
.
POST
.
get
(
'cpu-count'
),
'ram_size'
:
request
.
POST
.
get
(
'ram-size'
),
'max_ram_size'
:
request
.
POST
.
get
(
'ram-size'
),
# TODO: max_ram
'priority'
:
request
.
POST
.
get
(
'cpu-priority'
)
}
Instance
.
objects
.
filter
(
pk
=
self
.
object
.
pk
)
.
update
(
**
resources
)
success_message
=
_
(
"Resources successfully updated."
)
if
request
.
is_ajax
():
response
=
{
'message'
:
success_message
}
return
HttpResponse
(
json
.
dumps
(
response
),
content_type
=
"application/json"
)
else
:
messages
.
success
(
request
,
success_message
)
return
redirect
(
reverse_lazy
(
"dashboard.views.detail"
,
kwargs
=
{
'pk'
:
self
.
object
.
pk
}))
def
__set_name
(
self
,
request
):
def
__set_name
(
self
,
request
):
self
.
object
=
self
.
get_object
()
self
.
object
=
self
.
get_object
()
if
not
self
.
object
.
has_level
(
request
.
user
,
'owner'
):
if
not
self
.
object
.
has_level
(
request
.
user
,
'owner'
):
...
@@ -606,8 +582,9 @@ class VmOperationView(OperationView):
...
@@ -606,8 +582,9 @@ class VmOperationView(OperationView):
model
=
Instance
model
=
Instance
context_object_name
=
'instance'
# much simpler to mock object
context_object_name
=
'instance'
# much simpler to mock object
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
extra
=
None
,
*
args
,
**
kwargs
):
resp
=
super
(
VmOperationView
,
self
)
.
post
(
request
,
*
args
,
**
kwargs
)
resp
=
super
(
VmOperationView
,
self
)
.
post
(
request
,
extra
,
*
args
,
**
kwargs
)
if
request
.
is_ajax
():
if
request
.
is_ajax
():
store
=
messages
.
get_messages
(
request
)
store
=
messages
.
get_messages
(
request
)
store
.
used
=
True
store
.
used
=
True
...
@@ -699,6 +676,29 @@ class VmSaveView(FormOperationMixin, VmOperationView):
...
@@ -699,6 +676,29 @@ class VmSaveView(FormOperationMixin, VmOperationView):
effect
=
'info'
effect
=
'info'
form_class
=
VmSaveForm
form_class
=
VmSaveForm
class
VmResourcesChangeView
(
VmOperationView
):
op
=
'resources_change'
icon
=
"save"
show_in_toolbar
=
False
def
post
(
self
,
request
,
extra
=
None
,
*
args
,
**
kwargs
):
if
extra
is
None
:
extra
=
{}
resources
=
{
'num_cores'
:
"cpu-count"
,
'priority'
:
"cpu-priority"
,
'ram_size'
:
"ram-size"
,
"max_ram_size"
:
"ram-size"
,
# TODO
}
for
k
,
v
in
resources
.
iteritems
():
extra
[
k
]
=
request
.
POST
.
get
(
v
)
return
super
(
VmResourcesChangeView
,
self
)
.
post
(
request
,
extra
,
*
args
,
**
kwargs
)
vm_ops
=
OrderedDict
([
vm_ops
=
OrderedDict
([
(
'deploy'
,
VmOperationView
.
factory
(
(
'deploy'
,
VmOperationView
.
factory
(
op
=
'deploy'
,
icon
=
'play'
,
effect
=
'success'
)),
op
=
'deploy'
,
icon
=
'play'
,
effect
=
'success'
)),
...
@@ -1012,7 +1012,7 @@ class GroupAclUpdateView(AclUpdateView):
...
@@ -1012,7 +1012,7 @@ class GroupAclUpdateView(AclUpdateView):
kwargs
=
self
.
kwargs
))
kwargs
=
self
.
kwargs
))
class
TemplateChoose
(
TemplateView
):
class
TemplateChoose
(
LoginRequiredMixin
,
TemplateView
):
def
get_template_names
(
self
):
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
if
self
.
request
.
is_ajax
():
...
@@ -1045,6 +1045,9 @@ class TemplateChoose(TemplateView):
...
@@ -1045,6 +1045,9 @@ class TemplateChoose(TemplateView):
else
:
else
:
template
=
get_object_or_404
(
InstanceTemplate
,
pk
=
template
)
template
=
get_object_or_404
(
InstanceTemplate
,
pk
=
template
)
if
not
template
.
has_level
(
request
.
user
,
"user"
):
raise
PermissionDenied
()
instance
=
Instance
.
create_from_template
(
instance
=
Instance
.
create_from_template
(
template
=
template
,
owner
=
request
.
user
,
is_base
=
True
)
template
=
template
,
owner
=
request
.
user
,
is_base
=
True
)
...
@@ -1072,7 +1075,7 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
...
@@ -1072,7 +1075,7 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
return
context
return
context
def
get
(
self
,
*
args
,
**
kwargs
):
def
get
(
self
,
*
args
,
**
kwargs
):
if
not
self
.
request
.
user
.
has_perm
(
'vm.create_template'
):
if
not
self
.
request
.
user
.
has_perm
(
'vm.create_
base_
template'
):
raise
PermissionDenied
()
raise
PermissionDenied
()
return
super
(
TemplateCreate
,
self
)
.
get
(
*
args
,
**
kwargs
)
return
super
(
TemplateCreate
,
self
)
.
get
(
*
args
,
**
kwargs
)
...
@@ -1083,7 +1086,7 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
...
@@ -1083,7 +1086,7 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
return
kwargs
return
kwargs
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
if
not
self
.
request
.
user
.
has_perm
(
'vm.create_template'
):
if
not
self
.
request
.
user
.
has_perm
(
'vm.create_
base_
template'
):
raise
PermissionDenied
()
raise
PermissionDenied
()
form
=
self
.
form_class
(
request
.
POST
,
user
=
request
.
user
)
form
=
self
.
form_class
(
request
.
POST
,
user
=
request
.
user
)
...
@@ -1105,8 +1108,6 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
...
@@ -1105,8 +1108,6 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
return
redirect
(
"
%
s#resources"
%
inst
.
get_absolute_url
())
return
redirect
(
"
%
s#resources"
%
inst
.
get_absolute_url
())
return
super
(
TemplateCreate
,
self
)
.
post
(
self
,
request
,
args
,
kwargs
)
def
__create_networks
(
self
,
vlans
,
user
):
def
__create_networks
(
self
,
vlans
,
user
):
networks
=
[]
networks
=
[]
for
v
in
vlans
:
for
v
in
vlans
:
...
@@ -1167,12 +1168,6 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
...
@@ -1167,12 +1168,6 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
template
=
self
.
get_object
()
template
=
self
.
get_object
()
if
not
template
.
has_level
(
request
.
user
,
'owner'
):
if
not
template
.
has_level
(
request
.
user
,
'owner'
):
raise
PermissionDenied
()
raise
PermissionDenied
()
for
disk
in
self
.
get_object
()
.
disks
.
all
():