Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Gelencsér Szabolcs
/
cloud
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
f2577fb3
authored
Apr 19, 2013
by
Őry Máté
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
one: enforce pep8 and others
parent
1b2fb7a6
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
408 additions
and
251 deletions
+408
-251
one/admin.py
+39
-18
one/models.py
+145
-95
one/tasks.py
+6
-1
one/util.py
+2
-1
one/views.py
+216
-136
No files found.
one/admin.py
View file @
f2577fb3
from
django.contrib
import
messages
from
django.core.exceptions
import
ValidationError
from
django
import
contrib
from
django
import
contrib
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
one
import
models
from
one
import
models
import
string
import
school.models
def
owner_person
(
obj
):
def
owner_person
(
obj
):
p
=
obj
.
owner
p
=
obj
.
owner
return
"
%
s
%
s (
%
s)"
%
(
p
.
last_name
,
p
.
first_name
,
p
.
username
)
return
"
%
s
%
s (
%
s)"
%
(
p
.
last_name
,
p
.
first_name
,
p
.
username
)
owner_person
.
short_description
=
_
(
'owner'
)
owner_person
.
short_description
=
_
(
'owner'
)
class
PersonInline
(
contrib
.
admin
.
StackedInline
):
class
PersonInline
(
contrib
.
admin
.
StackedInline
):
model
=
models
.
Person
model
=
school
.
models
.
Person
max_num
=
1
max_num
=
1
can_delete
=
False
can_delete
=
False
class
SshKeyInline
(
contrib
.
admin
.
TabularInline
):
class
SshKeyInline
(
contrib
.
admin
.
TabularInline
):
model
=
models
.
SshKey
model
=
models
.
SshKey
extra
=
2
extra
=
2
class
DetailsInline
(
contrib
.
admin
.
StackedInline
):
class
DetailsInline
(
contrib
.
admin
.
StackedInline
):
model
=
models
.
UserCloudDetails
model
=
models
.
UserCloudDetails
max_num
=
1
max_num
=
1
can_delete
=
False
can_delete
=
False
class
MyUserAdmin
(
contrib
.
auth
.
admin
.
UserAdmin
):
list_display
=
(
'username'
,
'full_name'
,
'email'
,
'date_joined'
,
'instance_count'
,
'course_groups'
)
list_filter
=
(
'is_superuser'
,
'is_active'
,
'groups'
,
'person__course_groups'
,
)
class
MyUserAdmin
(
contrib
.
auth
.
admin
.
UserAdmin
):
list_display
=
(
'username'
,
'full_name'
,
'email'
,
'date_joined'
,
'instance_count'
,
'course_groups'
)
list_filter
=
(
'is_superuser'
,
'is_active'
,
'groups'
,
'person__course_groups'
,
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
MyUserAdmin
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
add
=
[
PersonInline
,
SshKeyInline
,
DetailsInline
,
]
try
:
try
:
inlines
=
inlines
+
(
PersonInline
,
SshKeyInline
,
DetailsInline
)
inlines
=
self
.
inlines
except
NameError
:
except
NameError
:
inlines
=
(
PersonInline
,
SshKeyInline
,
DetailsInline
)
inlines
=
list
()
self
.
inlines
=
inlines
+
add
def
instance_count
(
self
,
obj
):
def
instance_count
(
self
,
obj
):
return
_
(
"
%(sum)
d (
%(active)
d active)"
)
%
{
'sum'
:
obj
.
instance_set
.
count
(),
return
_
(
"
%(sum)
d (
%(active)
d active)"
)
%
{
'active'
:
obj
.
instance_set
.
filter
(
state
=
'ACTIVE'
)
.
count
(),
}
'sum'
:
obj
.
instance_set
.
count
(),
'active'
:
obj
.
instance_set
.
filter
(
state
=
'ACTIVE'
)
.
count
(),
}
def
course_groups
(
self
,
obj
):
def
course_groups
(
self
,
obj
):
try
:
try
:
...
@@ -48,31 +61,35 @@ class MyUserAdmin(contrib.auth.admin.UserAdmin):
...
@@ -48,31 +61,35 @@ class MyUserAdmin(contrib.auth.admin.UserAdmin):
ordering
=
[
"-date_joined"
]
ordering
=
[
"-date_joined"
]
contrib
.
admin
.
site
.
unregister
(
contrib
.
auth
.
models
.
User
)
contrib
.
admin
.
site
.
unregister
(
contrib
.
auth
.
models
.
User
)
contrib
.
admin
.
site
.
register
(
contrib
.
auth
.
models
.
User
,
MyUserAdmin
)
contrib
.
admin
.
site
.
register
(
contrib
.
auth
.
models
.
User
,
MyUserAdmin
)
def
update_state
(
modeladmin
,
request
,
queryset
):
def
update_state
(
modeladmin
,
request
,
queryset
):
for
i
in
queryset
.
all
():
for
i
in
queryset
.
all
():
i
.
update_state
()
i
.
update_state
()
update_state
.
short_description
=
_
(
'Update status'
)
update_state
.
short_description
=
_
(
'Update status'
)
def
submit_vm
(
modeladmin
,
request
,
queryset
):
def
submit_vm
(
modeladmin
,
request
,
queryset
):
for
i
in
queryset
.
all
():
for
i
in
queryset
.
all
():
i
.
submit
(
request
.
user
)
i
.
submit
(
request
.
user
)
i
.
update_state
()
i
.
update_state
()
submit_vm
.
short_description
=
_
(
'Submit VM'
)
submit_vm
.
short_description
=
_
(
'Submit VM'
)
def
delete_vm
(
modeladmin
,
request
,
queryset
):
def
delete_vm
(
modeladmin
,
request
,
queryset
):
for
i
in
queryset
.
exclude
(
state
=
'DONE'
)
.
all
():
for
i
in
queryset
.
exclude
(
state
=
'DONE'
)
.
all
():
i
.
one_delete
()
i
.
one_delete
()
delete_vm
.
short_description
=
_
(
'Delete VM'
)
delete_vm
.
short_description
=
_
(
'Delete VM'
)
def
suspend_vm
(
modeladmin
,
request
,
queryset
):
def
suspend_vm
(
modeladmin
,
request
,
queryset
):
for
i
in
queryset
.
filter
(
state
=
'ACTIVE'
)
.
all
():
for
i
in
queryset
.
filter
(
state
=
'ACTIVE'
)
.
all
():
i
.
stop
()
i
.
stop
()
suspend_vm
.
short_description
=
_
(
'Suspend VM'
)
suspend_vm
.
short_description
=
_
(
'Suspend VM'
)
def
resume_vm
(
modeladmin
,
request
,
queryset
):
def
resume_vm
(
modeladmin
,
request
,
queryset
):
for
i
in
queryset
.
filter
(
state__in
=
(
'STOPPED'
,
'SUSPENDED'
))
.
all
():
for
i
in
queryset
.
filter
(
state__in
=
(
'STOPPED'
,
'SUSPENDED'
))
.
all
():
i
.
resume
()
i
.
resume
()
...
@@ -80,21 +97,24 @@ resume_vm.short_description = _('Resume VM')
...
@@ -80,21 +97,24 @@ resume_vm.short_description = _('Resume VM')
class
TemplateAdmin
(
contrib
.
admin
.
ModelAdmin
):
class
TemplateAdmin
(
contrib
.
admin
.
ModelAdmin
):
model
=
models
.
Template
model
=
models
.
Template
list_display
=
(
'name'
,
'state'
,
owner_person
,
'system'
,
'public'
)
list_display
=
(
'name'
,
'state'
,
owner_person
,
'system'
,
'public'
)
list_filter
=
(
'owner'
,
'public'
)
list_filter
=
(
'owner'
,
'public'
)
class
InstanceAdmin
(
contrib
.
admin
.
ModelAdmin
):
class
InstanceAdmin
(
contrib
.
admin
.
ModelAdmin
):
model
=
models
.
Instance
model
=
models
.
Instance
actions
=
[
update_state
,
submit_vm
,
delete_vm
,
suspend_vm
,
resume_vm
]
actions
=
[
update_state
,
submit_vm
,
delete_vm
,
suspend_vm
,
resume_vm
]
list_display
=
(
'id'
,
'name'
,
owner_person
,
'state'
)
list_display
=
(
'id'
,
'name'
,
owner_person
,
'state'
)
readonly_fields
=
(
'ip'
,
'active_since'
,
'pw'
,
'template'
)
readonly_fields
=
(
'ip'
,
'active_since'
,
'pw'
,
'template'
)
list_filter
=
(
'owner'
,
'template'
,
'state'
)
list_filter
=
(
'owner'
,
'template'
,
'state'
)
def
queryset
(
self
,
request
):
def
queryset
(
self
,
request
):
return
super
(
InstanceAdmin
,
self
)
.
queryset
(
request
)
return
super
(
InstanceAdmin
,
self
)
.
queryset
(
request
)
class
DiskAdmin
(
contrib
.
admin
.
ModelAdmin
):
class
DiskAdmin
(
contrib
.
admin
.
ModelAdmin
):
model
=
models
.
Disk
model
=
models
.
Disk
list_display
=
(
'name'
,
'used_by'
)
list_display
=
(
'name'
,
'used_by'
)
def
used_by
(
self
,
obj
):
def
used_by
(
self
,
obj
):
...
@@ -104,12 +124,14 @@ class DiskAdmin(contrib.admin.ModelAdmin):
...
@@ -104,12 +124,14 @@ class DiskAdmin(contrib.admin.ModelAdmin):
return
None
return
None
used_by
.
verbose_name
=
_
(
'used by'
)
used_by
.
verbose_name
=
_
(
'used by'
)
class
NetworkAdmin
(
contrib
.
admin
.
ModelAdmin
):
class
NetworkAdmin
(
contrib
.
admin
.
ModelAdmin
):
model
=
models
.
Network
model
=
models
.
Network
list_display
=
(
'name'
,
'nat'
,
'public'
,
'get_vlan'
)
list_display
=
(
'name'
,
'nat'
,
'public'
,
'get_vlan'
)
class
ShareAdmin
(
contrib
.
admin
.
ModelAdmin
):
class
ShareAdmin
(
contrib
.
admin
.
ModelAdmin
):
model
=
models
.
Network
model
=
models
.
Network
list_filter
=
(
'group'
,
'template'
,
)
list_filter
=
(
'group'
,
'template'
,
)
list_display
=
(
'name'
,
owner_person
,
'template'
,
'group'
,
)
list_display
=
(
'name'
,
owner_person
,
'template'
,
'group'
,
)
...
@@ -119,4 +141,3 @@ contrib.admin.site.register(models.Network, NetworkAdmin)
...
@@ -119,4 +141,3 @@ contrib.admin.site.register(models.Network, NetworkAdmin)
contrib
.
admin
.
site
.
register
(
models
.
Disk
,
DiskAdmin
)
contrib
.
admin
.
site
.
register
(
models
.
Disk
,
DiskAdmin
)
contrib
.
admin
.
site
.
register
(
models
.
Share
,
ShareAdmin
)
contrib
.
admin
.
site
.
register
(
models
.
Share
,
ShareAdmin
)
contrib
.
admin
.
site
.
register
(
models
.
InstanceType
)
contrib
.
admin
.
site
.
register
(
models
.
InstanceType
)
one/models.py
View file @
f2577fb3
...
@@ -2,20 +2,21 @@
...
@@ -2,20 +2,21 @@
from
datetime
import
datetime
from
datetime
import
datetime
from
datetime
import
timedelta
as
td
from
datetime
import
timedelta
as
td
import
subprocess
,
tempfile
,
os
,
stat
,
re
,
base64
,
struct
,
logging
import
base64
import
struct
import
logging
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.core.exceptions
import
ValidationError
from
django.core.exceptions
import
ValidationError
from
django.core
import
signing
from
django.core
import
signing
from
django.db
import
models
from
django.db
import
models
from
django.db
import
transaction
from
django.db
import
transaction
from
django.db.models.signals
import
post_delete
,
pre_delete
from
django.db.models.signals
import
post_save
,
pre_delete
from
django.db.models.signals
import
post_save
from
django.template.defaultfilters
import
escape
from
django
import
forms
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
firewall.models
import
Host
,
Rule
,
Vlan
,
Record
from
firewall.models
import
Host
,
Vlan
from
school.models
import
Person
,
Group
from
school.models
import
Group
from
store.api
import
StoreApi
from
store.api
import
StoreApi
from
.util
import
keygen
from
.util
import
keygen
import
django.conf
import
django.conf
...
@@ -25,6 +26,7 @@ CLOUD_URL = django.conf.settings.CLOUD_URL
...
@@ -25,6 +26,7 @@ CLOUD_URL = django.conf.settings.CLOUD_URL
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
pwgen
=
User
.
objects
.
make_random_password
pwgen
=
User
.
objects
.
make_random_password
def
create_user_profile
(
sender
,
instance
,
created
,
**
kwargs
):
def
create_user_profile
(
sender
,
instance
,
created
,
**
kwargs
):
"""User creation hook: create cloud details object"""
"""User creation hook: create cloud details object"""
if
created
:
if
created
:
...
@@ -34,27 +36,33 @@ def create_user_profile(sender, instance, created, **kwargs):
...
@@ -34,27 +36,33 @@ def create_user_profile(sender, instance, created, **kwargs):
post_save
.
connect
(
create_user_profile
,
sender
=
User
)
post_save
.
connect
(
create_user_profile
,
sender
=
User
)
class
UserCloudDetails
(
models
.
Model
):
class
UserCloudDetails
(
models
.
Model
):
"""Cloud related details of a user."""
"""Cloud related details of a user."""
user
=
models
.
OneToOneField
(
User
,
verbose_name
=
_
(
'user'
),
user
=
models
.
OneToOneField
(
User
,
verbose_name
=
_
(
'user'
),
related_name
=
'cloud_details'
)
related_name
=
'cloud_details'
)
smb_password
=
models
.
CharField
(
max_length
=
20
,
smb_password
=
models
.
CharField
(
max_length
=
20
,
verbose_name
=
_
(
'Samba password'
),
verbose_name
=
_
(
'Samba password'
),
help_text
=
_
(
'Generated password for accessing store from '
help_text
=
_
(
'Generated password for '
'accessing store from '
'Windows.'
))
'Windows.'
))
ssh_key
=
models
.
ForeignKey
(
'SshKey'
,
verbose_name
=
_
(
'SSH key (public)'
),
ssh_key
=
models
.
ForeignKey
(
'SshKey'
,
verbose_name
=
_
(
'SSH key (public)'
),
null
=
True
,
blank
=
True
,
related_name
=
'userclouddetails_set'
,
null
=
True
,
blank
=
True
,
help_text
=
_
(
'Generated SSH public key for accessing store from '
related_name
=
'userclouddetails_set'
,
'Linux.'
))
help_text
=
_
(
'Generated SSH public key for '
ssh_private_key
=
models
.
TextField
(
verbose_name
=
_
(
'SSH key (private)'
),
blank
=
True
,
help_text
=
_
(
'Generated SSH private key for '
'accessing store from Linux.'
))
'accessing store from Linux.'
))
ssh_private_key
=
models
.
TextField
(
verbose_name
=
_
(
'SSH key (private)'
),
blank
=
True
,
help_text
=
_
(
'Generated SSH private key '
'for accessing store from '
'Linux.'
))
share_quota
=
models
.
IntegerField
(
verbose_name
=
_
(
'share quota'
),
share_quota
=
models
.
IntegerField
(
verbose_name
=
_
(
'share quota'
),
default
=
0
)
default
=
0
)
instance_quota
=
models
.
IntegerField
(
verbose_name
=
_
(
'instance quota'
),
instance_quota
=
models
.
IntegerField
(
verbose_name
=
_
(
'instance quota'
),
default
=
20
)
default
=
20
)
disk_quota
=
models
.
IntegerField
(
verbose_name
=
_
(
'disk quota'
),
disk_quota
=
models
.
IntegerField
(
verbose_name
=
_
(
'disk quota'
),
default
=
2048
,
help_text
=
_
(
'Disk quota in mebibytes.'
))
default
=
2048
,
help_text
=
_
(
'Disk quota in mebibytes.'
))
def
reset_keys
(
self
):
def
reset_keys
(
self
):
"""Delete old SSH key pair and generate new one."""
"""Delete old SSH key pair and generate new one."""
...
@@ -79,16 +87,20 @@ class UserCloudDetails(models.Model):
...
@@ -79,16 +87,20 @@ class UserCloudDetails(models.Model):
if
i
.
state
in
(
'ACTIVE'
,
'PENDING'
,
):
if
i
.
state
in
(
'ACTIVE'
,
'PENDING'
,
):
c
=
c
+
i
.
template
.
instance_type
.
credit
c
=
c
+
i
.
template
.
instance_type
.
credit
return
c
return
c
def
get_instance_pc
(
self
):
def
get_instance_pc
(
self
):
return
100
*
self
.
get_weighted_instance_count
()
/
self
.
instance_quota
return
100
*
self
.
get_weighted_instance_count
()
/
self
.
instance_quota
def
get_weighted_share_count
(
self
):
def
get_weighted_share_count
(
self
):
c
=
0
c
=
0
for
i
in
Share
.
objects
.
filter
(
owner
=
self
.
user
)
.
all
():
for
i
in
Share
.
objects
.
filter
(
owner
=
self
.
user
)
.
all
():
c
=
c
+
i
.
template
.
instance_type
.
credit
*
i
.
instance_limit
c
=
c
+
i
.
template
.
instance_type
.
credit
*
i
.
instance_limit
return
c
return
c
def
get_share_pc
(
self
):
def
get_share_pc
(
self
):
return
100
*
self
.
get_weighted_share_count
()
/
self
.
share_quota
return
100
*
self
.
get_weighted_share_count
()
/
self
.
share_quota
def
set_quota
(
sender
,
instance
,
created
,
**
kwargs
):
def
set_quota
(
sender
,
instance
,
created
,
**
kwargs
):
if
not
StoreApi
.
userexist
(
instance
.
user
.
username
):
if
not
StoreApi
.
userexist
(
instance
.
user
.
username
):
try
:
try
:
...
@@ -108,6 +120,7 @@ def set_quota(sender, instance, created, **kwargs):
...
@@ -108,6 +120,7 @@ def set_quota(sender, instance, created, **kwargs):
instance
.
disk_quota
*
1024
)
instance
.
disk_quota
*
1024
)
post_save
.
connect
(
set_quota
,
sender
=
UserCloudDetails
)
post_save
.
connect
(
set_quota
,
sender
=
UserCloudDetails
)
def
reset_keys
(
sender
,
instance
,
created
,
**
kwargs
):
def
reset_keys
(
sender
,
instance
,
created
,
**
kwargs
):
if
created
:
if
created
:
instance
.
reset_smb
()
instance
.
reset_smb
()
...
@@ -115,6 +128,7 @@ def reset_keys(sender, instance, created, **kwargs):
...
@@ -115,6 +128,7 @@ def reset_keys(sender, instance, created, **kwargs):
post_save
.
connect
(
reset_keys
,
sender
=
UserCloudDetails
)
post_save
.
connect
(
reset_keys
,
sender
=
UserCloudDetails
)
class
OpenSshKeyValidator
(
object
):
class
OpenSshKeyValidator
(
object
):
"""Validate OpenSSH keys (length and type)."""
"""Validate OpenSSH keys (length and type)."""
valid_types
=
[
'ssh-rsa'
,
'ssh-dsa'
]
valid_types
=
[
'ssh-rsa'
,
'ssh-dsa'
]
...
@@ -133,21 +147,23 @@ class OpenSshKeyValidator(object):
...
@@ -133,21 +147,23 @@ class OpenSshKeyValidator(object):
data
=
base64
.
decodestring
(
key_string
)
data
=
base64
.
decodestring
(
key_string
)
int_len
=
4
int_len
=
4
str_len
=
struct
.
unpack
(
'>I'
,
data
[:
int_len
])[
0
]
str_len
=
struct
.
unpack
(
'>I'
,
data
[:
int_len
])[
0
]
if
not
data
[
int_len
:
int_len
+
str_len
]
==
type
:
if
not
data
[
int_len
:
(
int_len
+
str_len
)
]
==
type
:
raise
raise
except
ValidationError
:
except
ValidationError
:
raise
raise
except
:
except
:
raise
ValidationError
(
_
(
'Invalid OpenSSH public key.'
))
raise
ValidationError
(
_
(
'Invalid OpenSSH public key.'
))
class
SshKey
(
models
.
Model
):
class
SshKey
(
models
.
Model
):
"""SSH public key (in OpenSSH format)."""
"""SSH public key (in OpenSSH format)."""
user
=
models
.
ForeignKey
(
User
,
related_name
=
'sshkey_set'
)
user
=
models
.
ForeignKey
(
User
,
related_name
=
'sshkey_set'
)
key
=
models
.
TextField
(
verbose_name
=
_
(
'SSH key'
),
key
=
models
.
TextField
(
verbose_name
=
_
(
'SSH key'
),
help_text
=
_
(
'<a href="/info/ssh/">SSH public key in OpenSSH '
help_text
=
_
(
'<a href="/info/ssh/">SSH public key '
'format</a> used for shell and store login '
'in OpenSSH format</a> used for shell '
'(2048+ bit RSA preferred). Example: '
'and store login (2048+ bit RSA '
'<code>ssh-rsa AAAAB...QtQ== john</code>.'
),
'preferred). Example: <code>ssh-rsa '
'AAAAB...QtQ== john</code>.'
),
validators
=
[
OpenSshKeyValidator
()])
validators
=
[
OpenSshKeyValidator
()])
def
__unicode__
(
self
):
def
__unicode__
(
self
):
...
@@ -164,7 +180,7 @@ TYPES = {"LAB": {"verbose_name": _('lab'), "id": "LAB",
...
@@ -164,7 +180,7 @@ TYPES = {"LAB": {"verbose_name": _('lab'), "id": "LAB",
"suspend"
:
td
(
hours
=
5
),
"delete"
:
td
(
days
=
15
),
"suspend"
:
td
(
hours
=
5
),
"delete"
:
td
(
days
=
15
),
"help_text"
:
_
(
'For lab or homework with short lifetime.'
)},
"help_text"
:
_
(
'For lab or homework with short lifetime.'
)},
"PROJECT"
:
{
"verbose_name"
:
_
(
'project'
),
"id"
:
"PROJECT"
,
"PROJECT"
:
{
"verbose_name"
:
_
(
'project'
),
"id"
:
"PROJECT"
,
"suspend"
:
td
(
weeks
=
5
),
"delete"
:
td
(
days
=
366
/
2
),
"suspend"
:
td
(
weeks
=
5
),
"delete"
:
td
(
days
=
366
/
2
),
"help_text"
:
_
(
'For project work.'
)},
"help_text"
:
_
(
'For project work.'
)},
"SERVER"
:
{
"verbose_name"
:
_
(
'server'
),
"id"
:
"SERVER"
,
"SERVER"
:
{
"verbose_name"
:
_
(
'server'
),
"id"
:
"SERVER"
,
"suspend"
:
td
(
days
=
365
),
"delete"
:
None
,
"suspend"
:
td
(
days
=
365
),
"delete"
:
None
,
...
@@ -174,6 +190,7 @@ DEFAULT_TYPE = TYPES['LAB']
...
@@ -174,6 +190,7 @@ DEFAULT_TYPE = TYPES['LAB']
TYPES_L
=
sorted
(
TYPES
.
values
(),
key
=
lambda
m
:
m
[
"suspend"
])
TYPES_L
=
sorted
(
TYPES
.
values
(),
key
=
lambda
m
:
m
[
"suspend"
])
TYPES_C
=
tuple
([(
i
[
0
],
i
[
1
][
"verbose_name"
])
for
i
in
TYPES
.
items
()])
TYPES_C
=
tuple
([(
i
[
0
],
i
[
1
][
"verbose_name"
])
for
i
in
TYPES
.
items
()])
class
Share
(
models
.
Model
):
class
Share
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
100
,
verbose_name
=
_
(
'name'
))
name
=
models
.
CharField
(
max_length
=
100
,
verbose_name
=
_
(
'name'
))
description
=
models
.
TextField
(
verbose_name
=
_
(
'description'
))
description
=
models
.
TextField
(
verbose_name
=
_
(
'description'
))
...
@@ -183,12 +200,15 @@ class Share(models.Model):
...
@@ -183,12 +200,15 @@ class Share(models.Model):
verbose_name
=
_
(
'created at'
))
verbose_name
=
_
(
'created at'
))
type
=
models
.
CharField
(
choices
=
TYPES_C
,
max_length
=
10
)
type
=
models
.
CharField
(
choices
=
TYPES_C
,
max_length
=
10
)
instance_limit
=
models
.
IntegerField
(
verbose_name
=
_
(
'instance limit'
),
instance_limit
=
models
.
IntegerField
(
verbose_name
=
_
(
'instance limit'
),
help_text
=
_
(
'Maximal count of instances launchable for this '
help_text
=
_
(
'Maximal count of '
'share.'
))
'instances launchable '
'for this share.'
))
per_user_limit
=
models
.
IntegerField
(
verbose_name
=
_
(
'per user limit'
),
per_user_limit
=
models
.
IntegerField
(
verbose_name
=
_
(
'per user limit'
),
help_text
=
_
(
'Maximal count of instances launchable by a single '
help_text
=
_
(
'Maximal count of '
'user.'
))
'instances launchable by '
owner
=
models
.
ForeignKey
(
User
,
null
=
True
,
blank
=
True
,
related_name
=
'share_set'
)
'a single user.'
))
owner
=
models
.
ForeignKey
(
User
,
null
=
True
,
blank
=
True
,
related_name
=
'share_set'
)
class
Meta
:
class
Meta
:
ordering
=
[
'group'
,
'template'
,
'owner'
,
]
ordering
=
[
'group'
,
'template'
,
'owner'
,
]
...
@@ -233,6 +253,7 @@ class Share(models.Model):
...
@@ -233,6 +253,7 @@ class Share(models.Model):
def
get_used_quota
(
self
):
def
get_used_quota
(
self
):
return
self
.
template
.
get_credits_per_instance
()
*
self
.
instance_limit
return
self
.
template
.
get_credits_per_instance
()
*
self
.
instance_limit
class
Disk
(
models
.
Model
):
class
Disk
(
models
.
Model
):
"""Virtual disks automatically synchronized with OpenNebula."""
"""Virtual disks automatically synchronized with OpenNebula."""
name
=
models
.
CharField
(
max_length
=
100
,
unique
=
True
,
name
=
models
.
CharField
(
max_length
=
100
,
unique
=
True
,
...
@@ -270,21 +291,23 @@ class Disk(models.Model):
...
@@ -270,21 +291,23 @@ class Disk(models.Model):
if
delete
:
if
delete
:
Disk
.
objects
.
exclude
(
id__in
=
l
)
.
delete
()
Disk
.
objects
.
exclude
(
id__in
=
l
)
.
delete
()
class
Network
(
models
.
Model
):
class
Network
(
models
.
Model
):
"""Virtual networks automatically synchronized with OpenNebula."""
"""Virtual networks automatically synchronized with OpenNebula."""
name
=
models
.
CharField
(
max_length
=
100
,
unique
=
True
,
name
=
models
.
CharField
(
max_length
=
100
,
unique
=
True
,
verbose_name
=
_
(
'name'
))
verbose_name
=
_
(
'name'
))
nat
=
models
.
BooleanField
(
verbose_name
=
_
(
'NAT'
),
nat
=
models
.
BooleanField
(
verbose_name
=
_
(
'NAT'
),
help_text
=
_
(
'If network address translation is done.'
))
help_text
=
_
(
'If network address translation is '
'done.'
))
public
=
models
.
BooleanField
(
verbose_name
=
_
(
'public'
),
public
=
models
.
BooleanField
(
verbose_name
=
_
(
'public'
),
help_text
=
_
(
'If internet gateway is available.'
))
help_text
=
_
(
'If internet gateway is '
'available.'
))
class
Meta
:
class
Meta
:
ordering
=
[
'name'
]
ordering
=
[
'name'
]
verbose_name
=
_
(
'network'
)
verbose_name
=
_
(
'network'
)
verbose_name_plural
=
_
(
'networks'
)
verbose_name_plural
=
_
(
'networks'
)
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
self
.
name
return
self
.
name
...
@@ -310,6 +333,7 @@ class Network(models.Model):
...
@@ -310,6 +333,7 @@ class Network(models.Model):
pass
pass
l
.
append
(
id
)
l
.
append
(
id
)
Network
.
objects
.
exclude
(
id__in
=
l
)
.
delete
()
Network
.
objects
.
exclude
(
id__in
=
l
)
.
delete
()
def
get_vlan
(
self
):
def
get_vlan
(
self
):
return
Vlan
.
objects
.
get
(
vid
=
self
.
id
)
return
Vlan
.
objects
.
get
(
vid
=
self
.
id
)
...
@@ -333,15 +357,20 @@ class InstanceType(models.Model):
...
@@ -333,15 +357,20 @@ class InstanceType(models.Model):
TEMPLATE_STATES
=
((
'NEW'
,
_
(
'new'
)),
(
'SAVING'
,
_
(
'saving'
)),
TEMPLATE_STATES
=
((
'NEW'
,
_
(
'new'
)),
(
'SAVING'
,
_
(
'saving'
)),
(
'READY'
,
_
(
'ready'
)),
)
(
'READY'
,
_
(
'ready'
)),
)
class
Template
(
models
.
Model
):
class
Template
(
models
.
Model
):
"""Virtual machine template specifying OS, disk, type and network."""
"""Virtual machine template specifying OS, disk, type and network."""
name
=
models
.
CharField
(
max_length
=
100
,
unique
=
True
,
name
=
models
.
CharField
(
max_length
=
100
,
unique
=
True
,
verbose_name
=
_
(
'name'
))
verbose_name
=
_
(
'name'
))
access_type
=
models
.
CharField
(
max_length
=
10
,
access_type
=
models
.
CharField
(
max_length
=
10
,
choices
=
[(
'rdp'
,
'rdp'
),
(
'nx'
,
'nx'
),
(
'ssh'
,
'ssh'
)],
choices
=
[(
'rdp'
,
'rdp'
),
(
'nx'
,
'nx'
),
(
'ssh'
,
'ssh'
)],
verbose_name
=
_
(
'access method'
))
verbose_name
=
_
(
'access method'
))
disk
=
models
.
ForeignKey
(
Disk
,
verbose_name
=
_
(
'disk'
),
related_name
=
'template_set'
)
disk
=
models
.
ForeignKey
(
Disk
,
verbose_name
=
_
(
instance_type
=
models
.
ForeignKey
(
InstanceType
,
related_name
=
'template_set'
,
'disk'
),
related_name
=
'template_set'
)
instance_type
=
models
.
ForeignKey
(
InstanceType
,
related_name
=
'template_set'
,
verbose_name
=
_
(
'instance type'
))
verbose_name
=
_
(
'instance type'
))
network
=
models
.
ForeignKey
(
Network
,
verbose_name
=
_
(
'network'
),
network
=
models
.
ForeignKey
(
Network
,
verbose_name
=
_
(
'network'
),
related_name
=
'template_set'
)
related_name
=
'template_set'
)
...
@@ -352,10 +381,12 @@ class Template(models.Model):
...
@@ -352,10 +381,12 @@ class Template(models.Model):
state
=
models
.
CharField
(
max_length
=
10
,
choices
=
TEMPLATE_STATES
,
state
=
models
.
CharField
(
max_length
=
10
,
choices
=
TEMPLATE_STATES
,
default
=
'NEW'
)
default
=
'NEW'
)
public
=
models
.
BooleanField
(
verbose_name
=
_
(
'public'
),
default
=
False
,
public
=
models
.
BooleanField
(
verbose_name
=
_
(
'public'
),
default
=
False
,
help_text
=
_
(
'If other users can derive templates of this one.'
))
help_text
=
_
(
'If other users can derive '
'templates of this one.'
))
description
=
models
.
TextField
(
verbose_name
=
_
(
'description'
),
blank
=
True
)
description
=
models
.
TextField
(
verbose_name
=
_
(
'description'
),
blank
=
True
)
system
=
models
.
TextField
(
verbose_name
=
_
(
'operating system'
),
blank
=
True
,
system
=
models
.
TextField
(
verbose_name
=
_
(
'operating system'
),
blank
=
True
,
help_text
=
(
_
(
'Name of operating system in format like "
%
s".'
)
%
help_text
=
(
_
(
'Name of operating system in '
'format like "
%
s".'
)
%
"Ubuntu 12.04 LTS Desktop amd64"
))
"Ubuntu 12.04 LTS Desktop amd64"
))
class
Meta
:
class
Meta
:
...
@@ -363,7 +394,6 @@ class Template(models.Model):
...
@@ -363,7 +394,6 @@ class Template(models.Model):
verbose_name_plural
=
_
(
'templates'
)
verbose_name_plural
=
_
(
'templates'
)
ordering
=
[
'name'
,
]
ordering
=
[
'name'
,
]
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
self
.
name
return
self
.
name
...
@@ -396,20 +426,19 @@ class Template(models.Model):
...
@@ -396,20 +426,19 @@ class Template(models.Model):
else
:
else
:
return
shares
return
shares
def
get_share_quota_usage_for
(
self
,
user
=
None
):
def
get_share_quota_usage_for
(
self
,
user
=
None
,
type
=
None
):
shares
=
self
.
get_shares_for
(
user
)
if
type
is
None
:
usage
=
0
c
=
self
.
get_credits_per_instance
()
for
share
in
shares
:
else
:
usage
+=
share
.
instance_limit
*
self
.
get_credits_per_instance
()
c
=
type
.
credit
return
usage
def
get_share_quota_usage_for_user_with_type
(
self
,
type
,
user
=
None
):
shares
=
self
.
get_shares_for
(
user
)
shares
=
self
.
get_shares_for
(
user
)
usage
=
0
usage
=
0
for
share
in
shares
:
for
share
in
shares
:
usage
+=
share
.
instance_limit
*
type
.
credit
usage
+=
share
.
instance_limit
*
c
return
usage
return
usage
class
Instance
(
models
.
Model
):
class
Instance
(
models
.
Model
):
"""Virtual machine instance."""
"""Virtual machine instance."""
name
=
models
.
CharField
(
max_length
=
100
,
name
=
models
.
CharField
(
max_length
=
100
,
...
@@ -433,19 +462,24 @@ class Instance(models.Model):
...
@@ -433,19 +462,24 @@ class Instance(models.Model):
default
=
'DEPLOYABLE'
)
default
=
'DEPLOYABLE'
)
active_since
=
models
.
DateTimeField
(
null
=
True
,
blank
=
True
,
active_since
=
models
.
DateTimeField
(
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'active since'
),
verbose_name
=
_
(
'active since'
),
help_text
=
_
(
'Time stamp of successful boot report.'
))
help_text
=
_
(
'Time stamp of successful '
'boot report.'
))
firewall_host
=
models
.
ForeignKey
(
Host
,
blank
=
True
,
null
=
True
,
firewall_host
=
models
.
ForeignKey
(
Host
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'host in firewall'
),
related_name
=
'instance_set'
)
verbose_name
=
_
(
'host in firewall'
),
related_name
=
'instance_set'
)
pw
=
models
.
CharField
(
max_length
=
20
,
verbose_name
=
_
(
'password'
),
pw
=
models
.
CharField
(
max_length
=
20
,
verbose_name
=
_
(
'password'
),
help_text
=
_
(
'Original password of instance'
))
help_text
=
_
(
'Original password of instance'
))
one_id
=
models
.
IntegerField
(
unique
=
True
,
blank
=
True
,
null
=
True
,
one_id
=
models
.
IntegerField
(
unique
=
True
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'OpenNebula ID'
))
verbose_name
=
_
(
'OpenNebula ID'
))
share
=
models
.
ForeignKey
(
'Share'
,
blank
=
True
,
null
=
True
,
share
=
models
.
ForeignKey
(
'Share'
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'share'
),
related_name
=
'instance_set'
)
verbose_name
=
_
(
'share'
),
related_name
=
'instance_set'
)
time_of_suspend
=
models
.
DateTimeField
(
default
=
None
,
time_of_suspend
=
models
.
DateTimeField
(
default
=
None
,
verbose_name
=
_
(
'time of suspend'
),
null
=
True
,
blank
=
True
)
verbose_name
=
_
(
'time of suspend'
),
null
=
True
,
blank
=
True
)
time_of_delete
=
models
.
DateTimeField
(
default
=
None
,
time_of_delete
=
models
.
DateTimeField
(
default
=
None
,
verbose_name
=
_
(
'time of delete'
),
null
=
True
,
blank
=
True
)
verbose_name
=
_
(
'time of delete'
),
null
=
True
,
blank
=
True
)
waiting
=
models
.
BooleanField
(
default
=
False
)
waiting
=
models
.
BooleanField
(
default
=
False
)
class
Meta
:
class
Meta
:
...
@@ -458,13 +492,15 @@ class Instance(models.Model):
...
@@ -458,13 +492,15 @@ class Instance(models.Model):
@models.permalink
@models.permalink
def
get_absolute_url
(
self
):
def
get_absolute_url
(
self
):
return
(
'one.views.vm_show'
,
None
,
{
'iid'
:
self
.
id
})
return
(
'one.views.vm_show'
,
None
,
{
'iid'
:
self
.
id
})
def
get_port
(
self
,
use_ipv6
=
False
):
def
get_port
(
self
,
use_ipv6
=
False
):
"""Get public port number for default access method."""
"""Get public port number for default access method."""
proto
=
self
.
template
.
access_type
proto
=
self
.
template
.
access_type
if
self
.
nat
and
not
use_ipv6
:
if
self
.
nat
and
not
use_ipv6
:
return
{
"rdp"
:
23000
,
"nx"
:
22000
,
"ssh"
:
22000
}[
proto
]
+
int
(
self
.
ip
.
split
(
'.'
)[
2
])
*
256
+
int
(
self
.
ip
.
split
(
'.'
)[
3
])
return
({
"rdp"
:
23000
,
"nx"
:
22000
,
"ssh"
:
22000
}[
proto
]
+
int
(
self
.
ip
.
split
(
'.'
)[
2
])
*
256
+
int
(
self
.
ip
.
split
(
'.'
)[
3
]))
else
:
else
:
return
{
"rdp"
:
3389
,
"nx"
:
22
,
"ssh"
:
22
}[
proto
]
return
{
"rdp"
:
3389
,
"nx"
:
22
,
"ssh"
:
22
}[
proto
]
...
@@ -510,21 +546,10 @@ class Instance(models.Model):
...
@@ -510,21 +546,10 @@ class Instance(models.Model):
pass
pass
return
age
return
age
@classmethod
@staticmethod
def
submit
(
cls
,
template
,
owner
,
extra
=
""
,
share
=
None
):
def
_create_context
(
pw
,
hostname
,
smb_password
,
ssh_private_key
,
owner
,
"""Submit a new instance to OpenNebula."""
token
,
extra
):
from
django.template.defaultfilters
import
escape
"""Return XML context configuration with given parameters."""
inst
=
Instance
(
pw
=
pwgen
(),
template
=
template
,
owner
=
owner
,
share
=
share
,
state
=
'PENDING'
)
inst
.
save
()
hostname
=
u"
%
d"
%
(
inst
.
id
,
)
token
=
signing
.
dumps
(
inst
.
id
,
salt
=
'activate'
)
try
:
details
=
owner
.
cloud_details
except
:
details
=
UserCloudDetails
(
user
=
owner
)
details
.
save
()
ctx
=
u'''
ctx
=
u'''
<SOURCE>web</SOURCE>
<SOURCE>web</SOURCE>
<HOSTNAME>
%(hostname)
s</HOSTNAME>
<HOSTNAME>
%(hostname)
s</HOSTNAME>
...
@@ -536,14 +561,64 @@ class Instance(models.Model):
...
@@ -536,14 +561,64 @@ class Instance(models.Model):
<SERVER>store.cloud.ik.bme.hu</SERVER>
<SERVER>store.cloud.ik.bme.hu</SERVER>
%(extra)
s
%(extra)
s
'''
%
{
'''
%
{
"pw"
:
escape
(
inst
.
pw
),
"pw"
:
escape
(
pw
),
"hostname"
:
escape
(
hostname
),
"hostname"
:
escape
(
hostname
),
"smbpw"
:
escape
(
details
.
smb_password
),
"smbpw"
:
escape
(
smb_password
),
"sshkey"
:
escape
(
details
.
ssh_private_key
),
"sshkey"
:
escape
(
ssh_private_key
),
"neptun"
:
escape
(
owner
.
username
),
"neptun"
:
escape
(
owner
),
"booturl"
:
"
%
sb/
%
s/"
%
(
CLOUD_URL
,
token
),
"booturl"
:
"
%
sb/
%
s/"
%
(
CLOUD_URL
,
token
),
"extra"
:
extra
"extra"
:
extra
}
}
return
ctx
def
_create_host
(
self
,
hostname
,
occi_result
):
"""Create firewall host for recently submitted Instance."""
host
=
Host
(
vlan
=
Vlan
.
objects
.
get
(
name
=
self
.
template
.
network
.
name
),
owner
=
self
.
owner
,
hostname
=
hostname
,
mac
=
occi_result
[
'interfaces'
][
0
][
'mac'
],
ipv4
=
occi_result
[
'interfaces'
][
0
][
'ip'
],
ipv6
=
'auto'
,
)
if
self
.
template
.
network
.
nat
:
host
.
pub_ipv4
=
Vlan
.
objects
.
get
(
name
=
self
.
template
.
network
.
name
)
.
snat_ip
host
.
shared_ip
=
True
try
:
host
.
save
()
except
:
for
i
in
Host
.
objects
.
filter
(
ipv4
=
host
.
ipv4
)
.
all
():
logger
.
warning
(
'Delete orphan fw host (
%
s) of
%
s.'
%
(
i
,
self
))
i
.
delete
()
for
i
in
Host
.
objects
.
filter
(
mac
=
host
.
mac
)
.
all
():
logger
.
warning
(
'Delete orphan fw host (
%
s) of
%
s.'
%
(
i
,
self
))
i
.
delete
()
host
.
save
()
host
.
enable_net
()
port
=
{
"rdp"
:
3389
,
"nx"
:
22
,
"ssh"
:
22
}[
self
.
template
.
access_type
]
host
.
add_port
(
"tcp"
,
self
.
get_port
(),
port
)
self
.
firewall_host
=
host
self
.
save
()
@classmethod
def
submit
(
cls
,
template
,
owner
,
extra
=
""
,
share
=
None
):
"""Submit a new instance to OpenNebula."""
inst
=
Instance
(
pw
=
pwgen
(),
template
=
template
,
owner
=
owner
,
share
=
share
,
state
=
'PENDING'
,
waiting
=
True
)
inst
.
save
()
hostname
=
u"
%
d"
%
(
inst
.
id
,
)
token
=
signing
.
dumps
(
inst
.
id
,
salt
=
'activate'
)
try
:
details
=
owner
.
cloud_details
except
:
details
=
UserCloudDetails
(
user
=
owner
)
details
.
save
()
ctx
=
cls
.
_create_context
(
inst
.
pw
,
hostname
,
details
.
smb_password
,
details
.
ssh_private_key
,
owner
.
username
,
token
,
extra
)
try
:
try
:
from
.tasks
import
CreateInstanceTask
from
.tasks
import
CreateInstanceTask
x
=
CreateInstanceTask
.
delay
(
x
=
CreateInstanceTask
.
delay
(
...
@@ -566,33 +641,7 @@ class Instance(models.Model):
...
@@ -566,33 +641,7 @@ class Instance(models.Model):
'id'
:
inst
.
one_id
})
'id'
:
inst
.
one_id
})
inst
.
save
()
inst
.
save
()
host
=
Host
(
inst
.
_create_host
(
hostname
)
vlan
=
Vlan
.
objects
.
get
(
name
=
template
.
network
.
name
),
owner
=
owner
,
hostname
=
hostname
,
mac
=
res
[
'interfaces'
][
0
][
'mac'
],
ipv4
=
res
[
'interfaces'
][
0
][
'ip'
],
ipv6
=
'auto'
,
)
if
inst
.
template
.
network
.
nat
:
host
.
pub_ipv4
=
Vlan
.
objects
.
get
(
name
=
template
.
network
.
name
)
.
snat_ip
host
.
shared_ip
=
True
try
:
host
.
save
()
except
:
for
i
in
Host
.
objects
.
filter
(
ipv4
=
host
.
ipv4
)
.
all
():
logger
.
warning
(
'Delete orphan fw host (
%
s) of
%
s.'
%
(
i
,
inst
))
i
.
delete
()
for
i
in
Host
.
objects
.
filter
(
mac
=
host
.
mac
)
.
all
():
logger
.
warning
(
'Delete orphan fw host (
%
s) of
%
s.'
%
(
i
,
inst
))
i
.
delete
()
host
.
save
()
host
.
enable_net
()
host
.
add_port
(
"tcp"
,
inst
.
get_port
(),
{
"rdp"
:
3389
,
"nx"
:
22
,
"ssh"
:
22
}[
inst
.
template
.
access_type
])
inst
.
firewall_host
=
host
inst
.
save
()
return
inst
return
inst
def
one_delete
(
self
):
def
one_delete
(
self
):
...
@@ -678,6 +727,7 @@ class Instance(models.Model):
...
@@ -678,6 +727,7 @@ class Instance(models.Model):
self
.
firewall_host_delete
()
self
.
firewall_host_delete
()
return
True
return
True
def
delete_instance_pre
(
sender
,
instance
,
using
,
**
kwargs
):
def
delete_instance_pre
(
sender
,
instance
,
using
,
**
kwargs
):
if
instance
.
state
!=
'DONE'
:
if
instance
.
state
!=
'DONE'
:
instance
.
one_delete
()
instance
.
one_delete
()
...
...
one/tasks.py
View file @
f2577fb3
...
@@ -37,27 +37,32 @@ class UpdateInstanceStateTask(Task):
...
@@ -37,27 +37,32 @@ class UpdateInstanceStateTask(Task):
# ezek csak azert vannak felveve, hogy szepen meg lehessen hivni oket
# ezek csak azert vannak felveve, hogy szepen meg lehessen hivni oket
# ezeket a fejgepen futo celery futtatja
# ezeket a fejgepen futo celery futtatja
class
CreateInstanceTask
(
Task
):
class
CreateInstanceTask
(
Task
):
def
run
(
self
,
name
,
instance_type
,
disk_id
,
network_id
,
ctx
):
def
run
(
self
,
name
,
instance_type
,
disk_id
,
network_id
,
ctx
):
pass
pass
class
DeleteInstanceTask
(
Task
):
class
DeleteInstanceTask
(
Task
):
def
run
(
self
,
one_id
):
def
run
(
self
,
one_id
):
pass
pass
class
ChangeInstanceStateTask
(
Task
):
class
ChangeInstanceStateTask
(
Task
):
def
run
(
self
,
one_id
,
new_state
):
def
run
(
self
,
one_id
,
new_state
):
pass
pass
class
SaveAsTask
(
Task
):
class
SaveAsTask
(
Task
):
def
run
(
self
,
one_id
,
new_img
):
def
run
(
self
,
one_id
,
new_img
):
pass
pass
class
UpdateDiskTask
(
Task
):
class
UpdateDiskTask
(
Task
):
def
run
(
self
):
def
run
(
self
):
pass
pass
class
UpdateNetworkTask
(
Task
):
class
UpdateNetworkTask
(
Task
):
def
run
(
self
):
def
run
(
self
):
pass
pass
one/util.py
View file @
f2577fb3
def
keygen
(
length
=
1024
):
def
keygen
(
length
=
1024
):
import
os
,
base64
import
os
import
base64
from
datetime
import
date
from
datetime
import
date
from
Crypto.PublicKey
import
RSA
from
Crypto.PublicKey
import
RSA
...
...
one/views.py
View file @
f2577fb3
...
@@ -3,26 +3,21 @@ from datetime import datetime
...
@@ -3,26 +3,21 @@ from datetime import datetime
from
django.conf
import
settings
from
django.conf
import
settings
from
datetime
import
timedelta
as
td
from
datetime
import
timedelta
as
td
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.models
import
User
from
django.contrib
import
messages
from
django.contrib
import
messages
from
django.core.exceptions
import
PermissionDenied
from
django.core.exceptions
import
PermissionDenied
,
ValidationError
from
django.core
import
signing
,
urlresolvers
from
django.core
import
signing
from
django.core.mail
import
mail_managers
,
send_mail
from
django.shortcuts
import
render_to_response
,
get_object_or_404
,
redirect
from
django.db
import
transaction
from
django.forms
import
ModelForm
,
Textarea
from
django.http
import
Http404
from
django.shortcuts
import
render
,
render_to_response
,
get_object_or_404
,
redirect
from
django.template
import
RequestContext
from
django.template
import
RequestContext
from
django.
template.loader
import
render_to_string
from
django.
utils.translation
import
ugettext_lazy
as
_
,
ugettext
from
django.
utils.decorators
import
method_decorator
from
django.
views.decorators.http
import
require_GET
,
require_POST
from
django.
utils.translation
import
get_language
as
lang
from
django.
views.decorators.http
import
require_safe
from
django.
utils.translation
import
ugettext_lazy
as
_
from
django.
http
import
HttpResponse
from
django.views.
decorators.http
import
*
from
django.views.
generic
import
View
from
django.views.generic
import
*
# from firewall.tasks
import *
from
firewall.tasks
import
*
from
one.models
import
Instance
,
UserCloudDetails
,
Template
,
Share
from
one.models
import
*
from
one.models
import
InstanceType
,
TYPES_L
,
TYPES
,
SshKey
from
school.models
import
*
from
school.models
import
Semester
,
Group
import
django.contrib.auth
as
auth
from
store.api
import
StoreApi
import
json
import
json
import
logging
import
logging
import
subprocess
import
subprocess
...
@@ -30,14 +25,18 @@ import subprocess
...
@@ -30,14 +25,18 @@ import subprocess
store_settings
=
settings
.
STORE_SETTINGS
store_settings
=
settings
.
STORE_SETTINGS
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
def
_list_instances
(
request
):
def
_list_instances
(
request
):
instances
=
Instance
.
objects
.
exclude
(
state
=
'DONE'
)
.
filter
(
owner
=
request
.
user
)
instances
=
Instance
.
objects
.
exclude
(
state
=
'DONE'
)
.
filter
(
owner
=
request
.
user
)
instances
=
instances
.
exclude
(
state
=
'DONE'
)
instances
=
instances
.
exclude
(
state
=
'DONE'
)
return
instances
return
instances
def
info
(
request
):
def
info
(
request
):
return
render_to_response
(
"info.html"
,
RequestContext
(
request
,
{}))
return
render_to_response
(
"info.html"
,
RequestContext
(
request
,
{}))
def
index
(
request
):
def
index
(
request
):
if
request
.
user
.
is_authenticated
():
if
request
.
user
.
is_authenticated
():
return
redirect
(
home
)
return
redirect
(
home
)
...
@@ -51,7 +50,8 @@ def home(request):
...
@@ -51,7 +50,8 @@ def home(request):
instances
=
_list_instances
(
request
)
instances
=
_list_instances
(
request
)
shares
=
[
s
for
s
in
request
.
user
.
person_set
.
all
()[
0
]
.
get_shares
()]
shares
=
[
s
for
s
in
request
.
user
.
person_set
.
all
()[
0
]
.
get_shares
()]
for
i
,
s
in
enumerate
(
shares
):
for
i
,
s
in
enumerate
(
shares
):
s
.
running_shared
=
s
.
instance_set
.
all
()
.
exclude
(
state
=
"DONE"
)
.
filter
(
owner
=
request
.
user
)
.
count
()
s
.
running_shared
=
s
.
instance_set
.
all
()
.
exclude
(
state
=
"DONE"
)
.
filter
(
owner
=
request
.
user
)
.
count
()
shares
[
i
]
=
s
shares
[
i
]
=
s
try
:
try
:
details
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
details
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
...
@@ -74,6 +74,7 @@ def home(request):
...
@@ -74,6 +74,7 @@ def home(request):
'storeserv'
:
store_settings
[
'store_public'
],
'storeserv'
:
store_settings
[
'store_public'
],
}))
}))
@login_required
@login_required
def
ajax_template_delete
(
request
):
def
ajax_template_delete
(
request
):
try
:
try
:
...
@@ -82,16 +83,21 @@ def ajax_template_delete(request):
...
@@ -82,16 +83,21 @@ def ajax_template_delete(request):
return
HttpResponse
(
unicode
(
_
(
"Invalid template ID."
)),
status
=
404
)
return
HttpResponse
(
unicode
(
_
(
"Invalid template ID."
)),
status
=
404
)
template
=
get_object_or_404
(
Template
,
id
=
template_id
)
template
=
get_object_or_404
(
Template
,
id
=
template_id
)
if
template
.
running_instances
()
>
0
:
if
template
.
running_instances
()
>
0
:
return
HttpResponse
(
unicode
(
_
(
"There are running instances of this template."
)),
status
=
404
)
return
HttpResponse
(
unicode
(
_
(
'There are running instances of '
'this template.'
)),
status
=
404
)
elif
template
.
share_set
.
exists
():
elif
template
.
share_set
.
exists
():
return
HttpResponse
(
unicode
(
_
(
"Template is still shared."
)),
status
=
404
)
return
HttpResponse
(
unicode
(
_
(
"Template is still shared."
)),
status
=
404
)
elif
template
.
owner
!=
request
.
user
:
elif
template
.
owner
!=
request
.
user
:
return
HttpResponse
(
unicode
(
_
(
"You don't have permission to delete this template."
)),
status
=
404
)
return
HttpResponse
(
unicode
(
_
(
"You don't have permission to delete "
"this template."
)),
status
=
404
)
else
:
else
:
if
template
.
safe_delete
():
if
template
.
safe_delete
():
return
HttpResponse
(
unicode
(
_
(
"Template successfully deleted."
)))
return
HttpResponse
(
unicode
(
_
(
"Template successfully deleted."
)))
else
:
else
:
return
HttpResponse
(
unicode
(
_
(
"Unexpected error happened."
)),
status
=
404
)
return
HttpResponse
(
unicode
(
_
(
"Unexpected error happened."
)),
status
=
404
)
def
ajax_template_name_unique
(
request
):
def
ajax_template_name_unique
(
request
):
name
=
request
.
GET
[
'name'
]
name
=
request
.
GET
[
'name'
]
...
@@ -100,6 +106,7 @@ def ajax_template_name_unique(request):
...
@@ -100,6 +106,7 @@ def ajax_template_name_unique(request):
s
=
"False"
s
=
"False"
return
HttpResponse
(
s
)
return
HttpResponse
(
s
)
@login_required
@login_required
def
vm_credentials
(
request
,
iid
):
def
vm_credentials
(
request
,
iid
):
try
:
try
:
...
@@ -110,54 +117,70 @@ def vm_credentials(request, iid):
...
@@ -110,54 +117,70 @@ def vm_credentials(request, iid):
vm
.
is_ipv6
=
is_ipv6
vm
.
is_ipv6
=
is_ipv6
vm
.
port_v4
=
vm
.
get_port
(
use_ipv6
=
False
)
vm
.
port_v4
=
vm
.
get_port
(
use_ipv6
=
False
)
vm
.
port_v6
=
vm
.
get_port
(
use_ipv6
=
True
)
vm
.
port_v6
=
vm
.
get_port
(
use_ipv6
=
True
)
return
render_to_response
(
'vm-credentials.html'
,
RequestContext
(
request
,
{
'i'
:
vm
}))
return
render_to_response
(
'vm-credentials.html'
,
RequestContext
(
request
,
{
'i'
:
vm
}))
except
:
except
:
return
HttpResponse
(
_
(
"Could not get Virtual Machine credentials."
),
status
=
404
)
return
HttpResponse
(
_
(
"Could not get Virtual Machine credentials."
),
status
=
404
)
messages
.
error
(
request
,
_
(
'Failed to power off virtual machine.'
))
messages
.
error
(
request
,
_
(
'Failed to power off virtual machine.'
))
class
AjaxTemplateWizard
(
View
):
class
AjaxTemplateWizard
(
View
):
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
return
render_to_response
(
'new-template-flow-1.html'
,
RequestContext
(
request
,
{
templates
=
([
t
for
t
in
Template
.
objects
.
filter
(
public
=
True
)
.
all
()]
+
'templates'
:
[
t
for
t
in
Template
.
objects
.
filter
(
public
=
True
)
.
all
()]
+
[
t
for
t
[
t
for
t
in
Template
.
objects
.
filter
(
owner
=
request
.
user
)
.
all
()],
in
Template
.
objects
.
filter
(
owner
=
request
.
user
)
.
all
()])
}))
ctx
=
RequestContext
(
request
,
{
'templates'
:
templates
})
return
render_to_response
(
'new-template-flow-1.html'
,
ctx
)
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
base
=
get_object_or_404
(
Template
,
id
=
request
.
POST
[
'base'
])
base
=
get_object_or_404
(
Template
,
id
=
request
.
POST
[
'base'
])
if
base
.
owner
!=
request
.
user
and
not
base
.
public
and
not
request
.
user
.
is_superuser
:
if
base
.
owner
!=
request
.
user
and
(
not
base
.
public
and
not
request
.
user
.
is_superuser
):
raise
PermissionDenied
()
raise
PermissionDenied
()
try
:
try
:
maxshare
=
Template
.
objects
.
order_by
(
'-pk'
)[
0
]
.
pk
+
1
maxshare
=
Template
.
objects
.
order_by
(
'-pk'
)[
0
]
.
pk
+
1
except
:
except
:
maxshare
=
1
maxshare
=
1
return
render_to_response
(
'new-template-flow.html'
,
RequestContext
(
request
,
{
ctx
=
{
'sizes'
:
InstanceType
.
objects
.
all
(),
'sizes'
:
InstanceType
.
objects
.
all
(),
'base'
:
base
,
'base'
:
base
,
'maxshare'
:
maxshare
,
'maxshare'
:
maxshare
,
}))
}
return
render_to_response
(
'new-template-flow.html'
,
RequestContext
(
request
,
ctx
))
ajax_template_wizard
=
login_required
(
AjaxTemplateWizard
.
as_view
())
ajax_template_wizard
=
login_required
(
AjaxTemplateWizard
.
as_view
())
class
AjaxTemplateEditWizard
(
View
):
class
AjaxTemplateEditWizard
(
View
):
def
get
(
self
,
request
,
id
,
*
args
,
**
kwargs
):
def
get
(
self
,
request
,
id
,
*
args
,
**
kwargs
):
template
=
get_object_or_404
(
Template
,
id
=
id
)
template
=
get_object_or_404
(
Template
,
id
=
id
)
if
template
.
owner
!=
request
.
user
and
not
template
.
public
and
not
request
.
user
.
is_superuser
:
if
template
.
owner
!=
request
.
user
and
(
not
template
.
public
and
not
request
.
user
.
is_superuser
):
raise
PermissionDenied
()
raise
PermissionDenied
()
return
render_to_response
(
'edit-template-flow.html'
,
RequestContext
(
request
,
{
ctx
=
{
'sizes'
:
InstanceType
.
objects
.
all
(),
'sizes'
:
InstanceType
.
objects
.
all
(),
'template'
:
template
,
'template'
:
template
,
}))
}
return
render_to_response
(
'edit-template-flow.html'
,
RequestContext
(
request
,
ctx
))
def
post
(
self
,
request
,
id
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
id
,
*
args
,
**
kwargs
):
template
=
get_object_or_404
(
Template
,
id
=
id
)
template
=
get_object_or_404
(
Template
,
id
=
id
)
user_details
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
user_details
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
if
template
.
owner
!=
request
.
user
and
not
template
.
public
and
not
request
.
user
.
is_superuser
:
if
template
.
owner
!=
request
.
user
and
(
not
template
.
public
and
not
request
.
user
.
is_superuser
):
raise
PermissionDenied
()
raise
PermissionDenied
()
instance_type
=
get_object_or_404
(
InstanceType
,
id
=
request
.
POST
[
'size'
])
instance_type
=
get_object_or_404
(
current_used_share_quota
=
user_details
.
get_weighted_share_count
()
InstanceType
,
id
=
request
.
POST
[
'size'
])
current_used_share_quota_without_template
=
current_used_share_quota
-
template
.
get_share_quota_usage_for
(
request
.
user
)
new_quota_for_current_template
=
template
.
get_share_quota_usage_for_user_with_type
(
instance_type
,
request
.
user
)
# calculate quota need with changed template type
new_used_share_quota
=
current_used_share_quota_without_template
+
new_quota_for_current_template
q
=
user_details
.
get_weighted_share_count
()
allow_type_modify
=
True
if
new_used_share_quota
<=
user_details
.
share_quota
else
False
q
-=
template
.
get_share_quota_usage_for
(
request
.
user
)
if
not
allow_type_modify
:
q
+=
template
.
get_share_quota_usage_for
(
request
.
user
,
messages
.
error
(
request
,
_
(
'You do not have enough free share quota.'
))
type
=
instance_type
)
if
q
>
user_details
.
share_quota
:
messages
.
error
(
request
,
_
(
'You do not have enough free share quota.'
))
return
redirect
(
home
)
return
redirect
(
home
)
template
.
instance_type
=
instance_type
template
.
instance_type
=
instance_type
template
.
description
=
request
.
POST
[
'description'
]
template
.
description
=
request
.
POST
[
'description'
]
...
@@ -172,12 +195,15 @@ class AjaxShareWizard(View):
...
@@ -172,12 +195,15 @@ class AjaxShareWizard(View):
def
get
(
self
,
request
,
id
,
gid
=
None
,
*
args
,
**
kwargs
):
def
get
(
self
,
request
,
id
,
gid
=
None
,
*
args
,
**
kwargs
):
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
if
det
.
get_weighted_share_count
()
>=
det
.
share_quota
:
if
det
.
get_weighted_share_count
()
>=
det
.
share_quota
:
return
HttpResponse
(
unicode
(
_
(
'You do not have any free share quota.'
)))
msg
=
ugettext
(
'You do not have any free share quota.'
)
return
HttpResponse
(
msg
)
types
=
TYPES_L
types
=
TYPES_L
types
[
0
][
'default'
]
=
True
types
[
0
][
'default'
]
=
True
for
i
,
t
in
enumerate
(
types
):
for
i
,
t
in
enumerate
(
types
):
t
[
'deletex'
]
=
datetime
.
now
()
+
td
(
seconds
=
1
)
+
t
[
'delete'
]
if
t
[
'delete'
]
else
None
t
[
'deletex'
]
=
datetime
.
now
()
+
td
(
t
[
'suspendx'
]
=
datetime
.
now
()
+
td
(
seconds
=
1
)
+
t
[
'suspend'
]
if
t
[
'suspend'
]
else
None
seconds
=
1
)
+
t
[
'delete'
]
if
t
[
'delete'
]
else
None
t
[
'suspendx'
]
=
datetime
.
now
()
+
td
(
seconds
=
1
)
+
t
[
'suspend'
]
if
t
[
'suspend'
]
else
None
types
[
i
]
=
t
types
[
i
]
=
t
if
gid
:
if
gid
:
gid
=
get_object_or_404
(
Group
,
id
=
gid
)
gid
=
get_object_or_404
(
Group
,
id
=
gid
)
...
@@ -188,10 +214,12 @@ class AjaxShareWizard(View):
...
@@ -188,10 +214,12 @@ class AjaxShareWizard(View):
'types'
:
types
,
'types'
:
types
,
'group'
:
gid
,
'group'
:
gid
,
}))
}))
def
post
(
self
,
request
,
id
,
gid
=
None
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
id
,
gid
=
None
,
*
args
,
**
kwargs
):
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
base
=
get_object_or_404
(
Template
,
id
=
id
)
base
=
get_object_or_404
(
Template
,
id
=
id
)
if
base
.
owner
!=
request
.
user
and
not
base
.
public
and
not
request
.
user
.
is_superuser
:
if
base
.
owner
!=
request
.
user
and
(
not
base
.
public
and
not
request
.
user
.
is_superuser
):
raise
PermissionDenied
()
raise
PermissionDenied
()
group
=
None
group
=
None
if
gid
:
if
gid
:
...
@@ -205,35 +233,45 @@ class AjaxShareWizard(View):
...
@@ -205,35 +233,45 @@ class AjaxShareWizard(View):
if
not
stype
in
TYPES
.
keys
():
if
not
stype
in
TYPES
.
keys
():
raise
PermissionDenied
()
raise
PermissionDenied
()
il
=
request
.
POST
[
'instance_limit'
]
il
=
request
.
POST
[
'instance_limit'
]
if
det
.
get_weighted_share_count
()
+
int
(
il
)
*
base
.
instance_type
.
credit
>
det
.
share_quota
:
if
det
.
share_quota
<
(
det
.
get_weighted_share_count
()
+
messages
.
error
(
request
,
_
(
'You do not have enough free share quota.'
))
int
(
il
)
*
base
.
instance_type
.
credit
):
messages
.
error
(
request
,
_
(
'You do not have enough free share quota.'
))
return
redirect
(
'/'
)
return
redirect
(
'/'
)
s
=
Share
.
objects
.
create
(
name
=
request
.
POST
[
'name'
],
description
=
request
.
POST
[
'description'
],
Share
.
objects
.
create
(
type
=
stype
,
instance_limit
=
il
,
per_user_limit
=
request
.
POST
[
'per_user_limit'
],
name
=
request
.
POST
[
'name'
],
description
=
request
.
POST
[
'description'
],
type
=
stype
,
instance_limit
=
il
,
per_user_limit
=
request
.
POST
[
'per_user_limit'
],
group
=
group
,
template
=
base
,
owner
=
request
.
user
)
group
=
group
,
template
=
base
,
owner
=
request
.
user
)
messages
.
success
(
request
,
_
(
'Successfully shared
%
s.'
)
%
base
)
messages
.
success
(
request
,
_
(
'Successfully shared
%
s.'
)
%
base
)
return
redirect
(
group
)
return
redirect
(
group
)
ajax_share_wizard
=
login_required
(
AjaxShareWizard
.
as_view
())
ajax_share_wizard
=
login_required
(
AjaxShareWizard
.
as_view
())
class
AjaxShareEditWizard
(
View
):
class
AjaxShareEditWizard
(
View
):
def
get
(
self
,
request
,
id
,
*
args
,
**
kwargs
):
def
get
(
self
,
request
,
id
,
*
args
,
**
kwargs
):
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
if
det
.
get_weighted_share_count
()
>
det
.
share_quota
:
if
det
.
get_weighted_share_count
()
>
det
.
share_quota
:
logger
.
warning
(
'[one] User
%
s ha more used share quota, than its limit, how is that possible? (
%
d >
%
d)'
,
logger
.
warning
(
str
(
request
.
user
),
'[one] User
%
s ha more used share quota, than its limit, how '
'is that possible? (
%
d >
%
d)'
,
str
(
request
.
user
),
det
.
get_weighted_share_count
(),
det
.
get_weighted_share_count
(),
det
.
share_quota
)
det
.
share_quota
)
return
HttpResponse
(
unicode
(
_
(
'You do not have any free share quota.'
)))
msg
=
ugettext
(
'You do not have any free share quota.'
)
return
HttpResponse
(
msg
)
types
=
TYPES_L
types
=
TYPES_L
for
i
,
t
in
enumerate
(
types
):
for
i
,
t
in
enumerate
(
types
):
t
[
'deletex'
]
=
datetime
.
now
()
+
td
(
seconds
=
1
)
+
t
[
'delete'
]
if
t
[
'delete'
]
else
None
t
[
'deletex'
]
=
datetime
.
now
()
+
td
(
t
[
'suspendx'
]
=
datetime
.
now
()
+
td
(
seconds
=
1
)
+
t
[
'suspend'
]
if
t
[
'suspend'
]
else
None
seconds
=
1
)
+
t
[
'delete'
]
if
t
[
'delete'
]
else
None
t
[
'suspendx'
]
=
datetime
.
now
()
+
td
(
seconds
=
1
)
+
t
[
'suspend'
]
if
t
[
'suspend'
]
else
None
types
[
i
]
=
t
types
[
i
]
=
t
share
=
get_object_or_404
(
Share
,
id
=
id
)
share
=
get_object_or_404
(
Share
,
id
=
id
)
return
render_to_response
(
'edit-share.html'
,
RequestContext
(
request
,
{
return
render_to_response
(
'edit-share.html'
,
RequestContext
(
request
,
{
'share'
:
share
,
'share'
:
share
,
'types'
:
types
,
'types'
:
types
,
}))
}))
def
post
(
self
,
request
,
id
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
id
,
*
args
,
**
kwargs
):
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
share
=
get_object_or_404
(
Share
,
id
=
id
)
share
=
get_object_or_404
(
Share
,
id
=
id
)
...
@@ -243,13 +281,15 @@ class AjaxShareEditWizard(View):
...
@@ -243,13 +281,15 @@ class AjaxShareEditWizard(View):
if
not
stype
in
TYPES
.
keys
():
if
not
stype
in
TYPES
.
keys
():
raise
PermissionDenied
()
raise
PermissionDenied
()
instance_limit
=
int
(
request
.
POST
[
'instance_limit'
])
instance_limit
=
int
(
request
.
POST
[
'instance_limit'
])
current_used_share_quota
=
det
.
get_weighted_share_count
()
current_used_share_quota_without_current_share
=
current_used_share_quota
-
share
.
get_used_quota
()
# calculate quota need with changed share
new_quota_for_current_share
=
instance_limit
*
share
.
template
.
get_credits_per_instance
()
q
=
det
.
get_weighted_share_count
()
new_used_share_quota
=
current_used_share_quota_without_current_share
+
new_quota_for_current_share
q
-=
share
.
get_used_quota
()
allow_stype_modify
=
True
if
new_used_share_quota
<=
det
.
share_quota
else
False
q
+=
instance_limit
*
share
.
template
.
get_credits_per_instance
()
if
not
allow_stype_modify
:
messages
.
error
(
request
,
_
(
'You do not have enough free share quota.'
))
if
det
.
share_quota
<
q
:
messages
.
error
(
request
,
_
(
'You do not have enough free share quota.'
))
return
redirect
(
share
.
group
)
return
redirect
(
share
.
group
)
share
.
name
=
request
.
POST
[
'name'
]
share
.
name
=
request
.
POST
[
'name'
]
share
.
description
=
request
.
POST
[
'description'
]
share
.
description
=
request
.
POST
[
'description'
]
...
@@ -273,9 +313,11 @@ def vm_saveas(request, vmid):
...
@@ -273,9 +313,11 @@ def vm_saveas(request, vmid):
messages
.
success
(
request
,
_
(
"Template is being saved..."
))
messages
.
success
(
request
,
_
(
"Template is being saved..."
))
return
redirect
(
inst
)
return
redirect
(
inst
)
def
vm_new_ajax
(
request
,
template
):
def
vm_new_ajax
(
request
,
template
):
return
vm_new
(
request
,
template
,
redir
=
False
)
return
vm_new
(
request
,
template
,
redir
=
False
)
def
_redirect_or_201
(
path
,
redir
):
def
_redirect_or_201
(
path
,
redir
):
if
redir
:
if
redir
:
return
redirect
(
path
)
return
redirect
(
path
)
...
@@ -284,38 +326,80 @@ def _redirect_or_201(path, redir):
...
@@ -284,38 +326,80 @@ def _redirect_or_201(path, redir):
response
[
'Location'
]
=
path
response
[
'Location'
]
=
path
return
response
return
response
def
_template_for_save
(
base
,
request
):
def
_template_for_save
(
base
,
request
):
if
base
.
owner
!=
request
.
user
and
not
base
.
public
and
not
request
.
user
.
is_superuser
:
if
base
.
owner
!=
request
.
user
and
(
not
base
.
public
and
not
request
.
user
.
is_superuser
):
raise
PermissionDenied
()
raise
PermissionDenied
()
name
=
request
.
POST
[
'name'
]
name
=
request
.
POST
[
'name'
]
t
=
Template
.
objects
.
create
(
name
=
name
,
disk
=
base
.
disk
,
instance_type_id
=
request
.
POST
[
'size'
],
network
=
base
.
network
,
owner
=
request
.
user
)
t
=
Template
.
objects
.
create
(
name
=
name
,
disk
=
base
.
disk
,
instance_type_id
=
request
.
POST
[
'size'
],
network
=
base
.
network
,
owner
=
request
.
user
)
t
.
access_type
=
base
.
access_type
t
.
access_type
=
base
.
access_type
t
.
description
=
request
.
POST
[
'description'
]
t
.
description
=
request
.
POST
[
'description'
]
t
.
system
=
base
.
system
t
.
system
=
base
.
system
t
.
save
()
t
.
save
()
return
t
return
t
def
_check_quota
(
request
,
template
,
share
):
def
_check_quota
(
request
,
template
,
share
):
"""
"""
Returns if the given request is permitted to run the new vm.
Returns if the given request is permitted to run the new vm.
"""
"""
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
user
=
request
.
user
if
det
.
get_weighted_instance_count
()
+
template
.
instance_type
.
credit
>
det
.
instance_quota
:
det
=
UserCloudDetails
.
objects
.
get
(
user
=
user
)
messages
.
error
(
request
,
_
(
'You do not have any free quota. You can not launch this until you stop an other instance.'
))
q
=
det
.
get_weighted_instance_count
()
+
template
.
instance_type
.
credit
if
q
>
det
.
instance_quota
:
messages
.
error
(
request
,
_
(
'You do not have any free quota. You can not launch '
'this until you stop an other instance.'
))
return
False
return
False
if
share
:
if
share
:
if
share
.
get_running
()
+
1
>
share
.
instance_limit
:
if
share
.
instance_limit
<
share
.
get_running
()
+
1
:
messages
.
error
(
request
,
_
(
'The share does not have any free quota. You can not launch this until someone stops an instance.'
))
msg
=
_
(
'The share does not have any free quota. You can '
'not launch this until someone stops an instance.'
)
messages
.
error
(
request
,
msg
)
return
False
return
False
elif
share
.
get_running_or_stopped
(
request
.
user
)
+
1
>
share
.
per_user_limit
:
messages
.
error
(
request
,
_
(
'You do not have any free quota for this share. You can not launch this until you stop an other instance.'
))
if
share
.
per_user_limit
<
share
.
get_running_or_stopped
(
user
)
+
1
:
msg
=
_
(
'You do not have any free quota for this share. You can '
'not launch this until you stop an other instance.'
)
messages
.
error
(
request
,
msg
)
return
False
return
False
if
not
share
.
group
.
members
.
filter
(
user
=
request
.
user
)
and
not
share
.
group
.
owners
.
filter
(
user
=
request
.
user
):
messages
.
error
(
request
,
_
(
'You are not a member of the share group.'
))
if
not
share
.
group
.
members
.
filter
(
user
=
request
.
user
)
and
\
not
share
.
group
.
owners
.
filter
(
user
=
request
.
user
):
msg
=
_
(
'You are not a member of the share group.'
)
messages
.
error
(
request
,
msg
)
return
False
return
False
return
True
return
True
@require_POST
def
_check_permission
(
request
,
template
,
share
):
"""
Returns if the given request is permitted to try the new vm.
"""
if
not
share
and
not
template
.
public
and
template
.
owner
!=
request
.
user
:
msg
=
_
(
'You have no permission to try this instance without a share. '
'Launch a new instance through a share.'
)
messages
.
error
(
request
,
msg
)
return
False
else
:
return
True
def
_try_submit
(
base
,
request
,
extra
,
share
):
try
:
inst
=
Instance
.
submit
(
base
,
request
.
user
,
extra
=
extra
,
share
=
share
)
except
Exception
as
e
:
logger
.
error
(
'Failed to create virtual machine.'
+
unicode
(
e
))
messages
.
error
(
request
,
_
(
'Failed to create virtual machine.'
))
else
:
inst
.
renew
()
return
inst
@login_required
@login_required
def
vm_new
(
request
,
template
=
None
,
share
=
None
,
redir
=
True
):
def
vm_new
(
request
,
template
=
None
,
share
=
None
,
redir
=
True
):
base
=
None
base
=
None
...
@@ -334,44 +418,14 @@ def vm_new(request, template=None, share=None, redir=True):
...
@@ -334,44 +418,14 @@ def vm_new(request, template=None, share=None, redir=True):
except
:
except
:
messages
.
error
(
request
,
_
(
'Can not create template.'
))
messages
.
error
(
request
,
_
(
'Can not create template.'
))
go
=
False
go
=
False
go
=
go
and
_check_quota
(
request
,
base
,
share
)
go
=
go
and
(
_check_quota
(
request
,
base
,
share
)
and
_check_permission
(
request
,
base
,
share
))
if
not
share
and
not
base
.
public
and
base
.
owner
!=
request
.
user
:
messages
.
error
(
request
,
_
(
'You have no permission to try this instance without a share. Launch a new instance through a share.'
))
go
=
False
type
=
share
.
type
if
share
else
'LAB'
TYPES
[
type
][
'suspend'
]
time_of_suspend
=
TYPES
[
type
][
'suspend'
]
+
datetime
.
now
()
if
TYPES
[
type
][
'delete'
]:
time_of_delete
=
TYPES
[
type
][
'delete'
]
+
datetime
.
now
()
else
:
time_of_delete
=
None
inst
=
None
if
go
:
if
go
:
try
:
inst
=
_try_submit
(
base
,
request
,
extra
,
share
)
inst
=
Instance
.
submit
(
base
,
request
.
user
,
extra
=
extra
,
share
=
share
)
elif
extra
and
base
:
# clean up new template object
except
Exception
as
e
:
logger
.
error
(
'Failed to create virtual machine.'
+
unicode
(
e
))
messages
.
error
(
request
,
_
(
'Failed to create virtual machine.'
))
inst
=
None
if
inst
:
inst
.
waiting
=
True
inst
.
time_of_suspend
=
time_of_suspend
inst
.
time_of_delete
=
time_of_delete
inst
.
save
()
elif
extra
and
base
:
base
.
delete
()
base
.
delete
()
return
_redirect_or_201
(
inst
.
get_absolute_url
()
if
inst
else
'/'
,
redir
)
return
_redirect_or_201
(
inst
.
get_absolute_url
()
if
inst
else
'/'
,
redir
)
class
VmListView
(
ListView
):
context_object_name
=
'instances'
template_name
=
'list.html'
def
get_queryset
(
self
):
self
.
profile
=
request
.
user
return
Instance
.
objects
.
filter
(
owner
=
self
.
profile
)
vm_list
=
login_required
(
VmListView
.
as_view
())
@require_safe
@require_safe
@login_required
@login_required
...
@@ -392,7 +446,7 @@ def vm_show(request, iid):
...
@@ -392,7 +446,7 @@ def vm_show(request, iid):
inst
.
hostname_v6
=
inst
.
get_connect_host
(
use_ipv6
=
True
)
inst
.
hostname_v6
=
inst
.
get_connect_host
(
use_ipv6
=
True
)
inst
.
port_v4
=
inst
.
get_port
(
use_ipv6
=
False
)
inst
.
port_v4
=
inst
.
get_port
(
use_ipv6
=
False
)
inst
.
port_v6
=
inst
.
get_port
(
use_ipv6
=
True
)
inst
.
port_v6
=
inst
.
get_port
(
use_ipv6
=
True
)
return
render_to_response
(
"show.html"
,
RequestContext
(
request
,{
return
render_to_response
(
"show.html"
,
RequestContext
(
request
,
{
'uri'
:
inst
.
get_connect_uri
(),
'uri'
:
inst
.
get_connect_uri
(),
'state'
:
inst
.
state
,
'state'
:
inst
.
state
,
'name'
:
inst
.
name
,
'name'
:
inst
.
name
,
...
@@ -400,11 +454,12 @@ def vm_show(request, iid):
...
@@ -400,11 +454,12 @@ def vm_show(request, iid):
'age'
:
inst
.
get_age
(),
'age'
:
inst
.
get_age
(),
'instances'
:
_list_instances
(
request
),
'instances'
:
_list_instances
(
request
),
'i'
:
inst
,
'i'
:
inst
,
'booting'
:
not
inst
.
active_since
,
'booting'
:
not
inst
.
active_since
,
'ports'
:
ports
,
'ports'
:
ports
,
'userdetails'
:
details
'userdetails'
:
details
}))
}))
@require_safe
@require_safe
@login_required
@login_required
def
vm_ajax_instance_status
(
request
,
iid
):
def
vm_ajax_instance_status
(
request
,
iid
):
...
@@ -417,6 +472,7 @@ def vm_ajax_instance_status(request, iid):
...
@@ -417,6 +472,7 @@ def vm_ajax_instance_status(request, iid):
'state'
:
inst
.
template
.
state
'state'
:
inst
.
template
.
state
}}))
}}))
@login_required
@login_required
def
vm_ajax_rename
(
request
,
iid
):
def
vm_ajax_rename
(
request
,
iid
):
inst
=
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
inst
=
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
...
@@ -424,6 +480,7 @@ def vm_ajax_rename(request, iid):
...
@@ -424,6 +480,7 @@ def vm_ajax_rename(request, iid):
inst
.
save
()
inst
.
save
()
return
HttpResponse
(
json
.
dumps
({
'name'
:
inst
.
name
}))
return
HttpResponse
(
json
.
dumps
({
'name'
:
inst
.
name
}))
def
boot_token
(
request
,
token
):
def
boot_token
(
request
,
token
):
try
:
try
:
id
=
signing
.
loads
(
token
,
salt
=
'activate'
)
id
=
signing
.
loads
(
token
,
salt
=
'activate'
)
...
@@ -437,6 +494,7 @@ def boot_token(request, token):
...
@@ -437,6 +494,7 @@ def boot_token(request, token):
inst
.
save
()
inst
.
save
()
return
HttpResponse
(
"KTHXBYE"
)
return
HttpResponse
(
"KTHXBYE"
)
class
VmPortAddView
(
View
):
class
VmPortAddView
(
View
):
def
post
(
self
,
request
,
iid
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
iid
,
*
args
,
**
kwargs
):
try
:
try
:
...
@@ -460,6 +518,7 @@ class VmPortAddView(View):
...
@@ -460,6 +518,7 @@ class VmPortAddView(View):
vm_port_add
=
login_required
(
VmPortAddView
.
as_view
())
vm_port_add
=
login_required
(
VmPortAddView
.
as_view
())
@require_safe
@require_safe
@login_required
@login_required
@require_GET
@require_GET
...
@@ -473,14 +532,17 @@ def vm_port_del(request, iid, proto, private):
...
@@ -473,14 +532,17 @@ def vm_port_del(request, iid, proto, private):
messages
.
error
(
request
,
_
(
u"Removing port failed."
))
messages
.
error
(
request
,
_
(
u"Removing port failed."
))
return
redirect
(
'/vm/show/
%
d/'
%
int
(
iid
))
return
redirect
(
'/vm/show/
%
d/'
%
int
(
iid
))
class
VmDeleteView
(
View
):
class
VmDeleteView
(
View
):
def
post
(
self
,
request
,
iid
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
iid
,
*
args
,
**
kwargs
):
try
:
try
:
inst
=
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
inst
=
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
if
inst
.
template
.
state
!=
'READY'
and
inst
.
template
.
owner
==
request
.
user
:
if
inst
.
template
.
state
!=
'READY'
and
\
inst
.
template
.
owner
==
request
.
user
:
inst
.
template
.
delete
()
inst
.
template
.
delete
()
inst
.
one_delete
()
inst
.
one_delete
()
messages
.
success
(
request
,
_
(
'Virtual machine is successfully deleted.'
))
messages
.
success
(
request
,
_
(
'Virtual machine is successfully deleted.'
))
except
:
except
:
messages
.
error
(
request
,
_
(
'Failed to delete virtual machine.'
))
messages
.
error
(
request
,
_
(
'Failed to delete virtual machine.'
))
if
request
.
is_ajax
():
if
request
.
is_ajax
():
...
@@ -490,13 +552,14 @@ class VmDeleteView(View):
...
@@ -490,13 +552,14 @@ class VmDeleteView(View):
def
get
(
self
,
request
,
iid
,
*
args
,
**
kwargs
):
def
get
(
self
,
request
,
iid
,
*
args
,
**
kwargs
):
i
=
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
i
=
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
return
render_to_response
(
"confirm_delete.html"
,
RequestContext
(
request
,
{
ctx
=
RequestContext
(
request
,
{
'i'
:
i
,
})
'i'
:
i
})
)
return
render_to_response
(
"confirm_delete.html"
,
ctx
)
vm_delete
=
login_required
(
VmDeleteView
.
as_view
())
vm_delete
=
login_required
(
VmDeleteView
.
as_view
())
@login_required
@login_required
#@require_POST
#
@require_POST
def
vm_unshare
(
request
,
id
,
*
args
,
**
kwargs
):
def
vm_unshare
(
request
,
id
,
*
args
,
**
kwargs
):
s
=
get_object_or_404
(
Share
,
id
=
id
)
s
=
get_object_or_404
(
Share
,
id
=
id
)
g
=
s
.
group
g
=
s
.
group
...
@@ -504,7 +567,8 @@ def vm_unshare(request, id, *args, **kwargs):
...
@@ -504,7 +567,8 @@ def vm_unshare(request, id, *args, **kwargs):
raise
PermissionDenied
()
raise
PermissionDenied
()
try
:
try
:
if
s
.
get_running_or_stopped
()
>
0
:
if
s
.
get_running_or_stopped
()
>
0
:
messages
.
error
(
request
,
_
(
'There are machines running of this share.'
))
messages
.
error
(
request
,
_
(
'There are machines running of this share.'
))
else
:
else
:
s
.
delete
()
s
.
delete
()
messages
.
success
(
request
,
_
(
'Share is successfully removed.'
))
messages
.
success
(
request
,
_
(
'Share is successfully removed.'
))
...
@@ -512,23 +576,27 @@ def vm_unshare(request, id, *args, **kwargs):
...
@@ -512,23 +576,27 @@ def vm_unshare(request, id, *args, **kwargs):
messages
.
error
(
request
,
_
(
'Failed to remove share.'
))
messages
.
error
(
request
,
_
(
'Failed to remove share.'
))
return
redirect
(
g
)
return
redirect
(
g
)
@login_required
@login_required
@require_POST
@require_POST
def
vm_stop
(
request
,
iid
,
*
args
,
**
kwargs
):
def
vm_stop
(
request
,
iid
,
*
args
,
**
kwargs
):
try
:
try
:
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
.
stop
()
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
.
stop
()
messages
.
success
(
request
,
_
(
'Virtual machine is successfully stopped.'
))
messages
.
success
(
request
,
_
(
'Virtual machine is successfully stopped.'
))
except
:
except
:
messages
.
error
(
request
,
_
(
'Failed to stop virtual machine.'
))
messages
.
error
(
request
,
_
(
'Failed to stop virtual machine.'
))
return
redirect
(
'/'
)
return
redirect
(
'/'
)
@login_required
@login_required
@require_POST
@require_POST
def
vm_resume
(
request
,
iid
,
*
args
,
**
kwargs
):
def
vm_resume
(
request
,
iid
,
*
args
,
**
kwargs
):
try
:
try
:
obj
=
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
obj
=
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
obj
.
resume
()
obj
.
resume
()
messages
.
success
(
request
,
_
(
'Virtual machine is successfully resumed.'
))
messages
.
success
(
request
,
_
(
'Virtual machine is successfully resumed.'
))
except
:
except
:
messages
.
error
(
request
,
_
(
'Failed to resume virtual machine.'
))
messages
.
error
(
request
,
_
(
'Failed to resume virtual machine.'
))
try
:
try
:
...
@@ -537,6 +605,7 @@ def vm_resume(request, iid, *args, **kwargs):
...
@@ -537,6 +605,7 @@ def vm_resume(request, iid, *args, **kwargs):
pass
pass
return
redirect
(
'/'
)
return
redirect
(
'/'
)
@login_required
@login_required
@require_POST
@require_POST
def
vm_renew
(
request
,
which
,
iid
,
*
args
,
**
kwargs
):
def
vm_renew
(
request
,
which
,
iid
,
*
args
,
**
kwargs
):
...
@@ -550,33 +619,38 @@ def vm_renew(request, which, iid, *args, **kwargs):
...
@@ -550,33 +619,38 @@ def vm_renew(request, which, iid, *args, **kwargs):
'vm'
:
vm
'vm'
:
vm
}))
}))
@login_required
@login_required
@require_POST
@require_POST
def
vm_power_off
(
request
,
iid
,
*
args
,
**
kwargs
):
def
vm_power_off
(
request
,
iid
,
*
args
,
**
kwargs
):
try
:
try
:
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
.
poweroff
()
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
.
poweroff
()
messages
.
success
(
request
,
_
(
'Virtual machine is successfully powered off.'
))
messages
.
success
(
request
,
_
(
'Virtual machine is successfully powered off.'
))
except
:
except
:
messages
.
error
(
request
,
_
(
'Failed to power off virtual machine.'
))
messages
.
error
(
request
,
_
(
'Failed to power off virtual machine.'
))
return
redirect
(
'/'
)
return
redirect
(
'/'
)
@login_required
@login_required
@require_POST
@require_POST
def
vm_restart
(
request
,
iid
,
*
args
,
**
kwargs
):
def
vm_restart
(
request
,
iid
,
*
args
,
**
kwargs
):
try
:
try
:
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
.
restart
()
get_object_or_404
(
Instance
,
id
=
iid
,
owner
=
request
.
user
)
.
restart
()
messages
.
success
(
request
,
_
(
'Virtual machine is successfully restarted.'
))
messages
.
success
(
request
,
_
(
'Virtual machine is successfully restarted.'
))
except
:
except
:
messages
.
error
(
request
,
_
(
'Failed to restart virtual machine.'
))
messages
.
error
(
request
,
_
(
'Failed to restart virtual machine.'
))
return
redirect
(
'/'
)
return
redirect
(
'/'
)
@login_required
@login_required
@require_POST
@require_POST
def
key_add
(
request
):
def
key_add
(
request
):
try
:
try
:
key
=
SshKey
()
key
=
SshKey
()
key
.
key
=
request
.
POST
[
'key'
]
key
.
key
=
request
.
POST
[
'key'
]
key
.
user
=
request
.
user
key
.
user
=
request
.
user
key
.
full_clean
()
key
.
full_clean
()
key
.
save
()
key
.
save
()
_update_keys
(
request
.
user
)
_update_keys
(
request
.
user
)
...
@@ -590,22 +664,25 @@ def key_add(request):
...
@@ -590,22 +664,25 @@ def key_add(request):
messages
.
success
(
request
,
_
(
'Public key successfully added.'
))
messages
.
success
(
request
,
_
(
'Public key successfully added.'
))
return
redirect
(
'/'
)
return
redirect
(
'/'
)
@login_required
@login_required
@require_POST
@require_POST
def
key_ajax_delete
(
request
):
def
key_ajax_delete
(
request
):
try
:
try
:
key
=
get_object_or_404
(
SshKey
,
id
=
request
.
POST
[
'id'
],
user
=
request
.
user
)
key
=
get_object_or_404
(
SshKey
,
id
=
request
.
POST
[
'id'
],
user
=
request
.
user
)
key
.
delete
()
key
.
delete
()
_update_keys
(
request
.
user
)
_update_keys
(
request
.
user
)
except
:
except
:
messages
.
error
(
request
,
_
(
'Failed to delete public key'
))
messages
.
error
(
request
,
_
(
'Failed to delete public key'
))
return
HttpResponse
(
'OK'
)
return
HttpResponse
(
'OK'
)
@login_required
@login_required
@require_POST
@require_POST
def
key_ajax_reset
(
request
):
def
key_ajax_reset
(
request
):
try
:
try
:
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
det
=
UserCloudDetails
.
objects
.
get
(
user
=
request
.
user
)
det
.
reset_smb
()
det
.
reset_smb
()
det
.
reset_keys
()
det
.
reset_keys
()
_update_keys
(
request
.
user
)
_update_keys
(
request
.
user
)
...
@@ -613,6 +690,7 @@ def key_ajax_reset(request):
...
@@ -613,6 +690,7 @@ def key_ajax_reset(request):
messages
.
error
(
request
,
_
(
'Failed to reset keys'
))
messages
.
error
(
request
,
_
(
'Failed to reset keys'
))
return
HttpResponse
(
'OK'
)
return
HttpResponse
(
'OK'
)
def
_update_keys
(
user
):
def
_update_keys
(
user
):
details
=
user
.
cloud_details
details
=
user
.
cloud_details
password
=
details
.
smb_password
password
=
details
.
smb_password
...
@@ -622,22 +700,24 @@ def _update_keys(user):
...
@@ -622,22 +700,24 @@ def _update_keys(user):
user
=
user
.
username
user
=
user
.
username
StoreApi
.
updateauthorizationinfo
(
user
,
password
,
key_list
)
StoreApi
.
updateauthorizationinfo
(
user
,
password
,
key_list
)
def
stat
(
request
):
def
stat
(
request
):
values
=
subprocess
.
check_output
([
'/opt/webadmin/cloud/miscellaneous/stat/stat_wrap.sh'
])
values
=
subprocess
.
check_output
(
[
'/opt/webadmin/cloud/miscellaneous/stat/stat_wrap.sh'
])
# values = '''
# values = '''
# {"CPU": {"USED_CPU": 2, "ALLOC_CPU": 0,
# {"CPU": {"USED_CPU": 2, "ALLOC_CPU": 0,
# "FREE_CPU": 98}, "MEM": {"FREE_MEM": 1685432, "ALLOC_MEM":0,
# "FREE_CPU": 98}, "MEM": {"FREE_MEM": 1685432, "ALLOC_MEM":0,
# "USED_MEM": 366284}}'''
# "USED_MEM": 366284}}'''
stat_dict
=
json
.
loads
(
values
)
stat_dict
=
json
.
loads
(
values
)
return
HttpResponse
(
render_to_response
(
"stat.html"
,
RequestContext
(
return
HttpResponse
(
render_to_response
(
"stat.html"
,
RequestContext
(
request
,
{
request
,
{
'STAT'
:
stat_dict
,
}
'STAT'
:
stat_dict
,
}
)))
)))
def
sites
(
request
,
site
):
def
sites
(
request
,
site
):
if
site
in
[
"legal"
,
"policy"
,
"help"
,
"support"
,
"changelog"
,
]:
if
site
in
[
"legal"
,
"policy"
,
"help"
,
"support"
,
"changelog"
,
]:
return
render_to_response
(
"sites/
%
s.html"
%
site
,
RequestContext
(
request
,
{}))
ctx
=
RequestContext
(
request
,
{})
return
render_to_response
(
"sites/
%
s.html"
%
site
,
ctx
)
else
:
else
:
return
redirect
(
home
)
return
redirect
(
home
)
...
...
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