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
b226eeef
authored
Aug 01, 2014
by
Kálmán Viktor
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into featura-mount-store-op
parents
2c38e827
fec75c1e
Hide whitespace changes
Inline
Side-by-side
Showing
72 changed files
with
3017 additions
and
1655 deletions
+3017
-1655
circle/circle/settings/base.py
+11
-0
circle/circle/settings/production.py
+7
-13
circle/circle/settings/test.py
+6
-0
circle/common/models.py
+59
-24
circle/dashboard/fixtures/test-vm-fixture.json
+4
-4
circle/dashboard/forms.py
+136
-310
circle/dashboard/migrations/0010_auto__add_field_profile_smb_password__add_field_profile_disk_quota.py
+268
-0
circle/dashboard/migrations/0010_auto__add_futuremember__add_unique_futuremember_org_id_group.py
+3
-2
circle/dashboard/migrations/0011_auto__add_field_notification_subject_data__add_field_notification_mess.py
+3
-2
circle/dashboard/migrations/0012_migrate_messages.py
+2
-0
circle/dashboard/migrations/0013_auto__del_field_notification_message__del_field_notification_subject.py
+3
-2
circle/dashboard/migrations/0014_auto__chg_field_profile_disk_quota.py
+264
-0
circle/dashboard/models.py
+63
-1
circle/dashboard/static/dashboard/dashboard.css
+144
-36
circle/dashboard/static/dashboard/dashboard.js
+67
-21
circle/dashboard/static/dashboard/loopj-jquery-simple-slider-fa64f59/.gitignore
+0
-4
circle/dashboard/static/dashboard/loopj-jquery-simple-slider-fa64f59/README.md
+0
-28
circle/dashboard/static/dashboard/loopj-jquery-simple-slider-fa64f59/demo.html
+0
-80
circle/dashboard/static/dashboard/loopj-jquery-simple-slider-fa64f59/grunt.js
+0
-69
circle/dashboard/static/dashboard/loopj-jquery-simple-slider-fa64f59/js/simple-slider.coffee
+0
-361
circle/dashboard/static/dashboard/loopj-jquery-simple-slider-fa64f59/js/simple-slider.min.js
+0
-12
circle/dashboard/static/dashboard/loopj-jquery-simple-slider-fa64f59/package.json
+0
-33
circle/dashboard/static/dashboard/loopj-jquery-simple-slider/css/simple-slider.css
+47
-0
circle/dashboard/static/dashboard/loopj-jquery-simple-slider/js/simple-slider.js
+71
-34
circle/dashboard/static/dashboard/loopj-jquery-simple-slider/js/simple-slider.min.js
+11
-0
circle/dashboard/static/dashboard/store.js
+60
-0
circle/dashboard/static/dashboard/vm-common.js
+1
-1
circle/dashboard/static/dashboard/vm-create.js
+28
-54
circle/dashboard/static/dashboard/vm-details.js
+48
-24
circle/dashboard/static/grafikon.png
+0
-0
circle/dashboard/store_api.py
+195
-0
circle/dashboard/tables.py
+0
-37
circle/dashboard/templates/base.html
+1
-0
circle/dashboard/templates/dashboard/_resources-sliders.html
+65
-0
circle/dashboard/templates/dashboard/_template-create.html
+38
-11
circle/dashboard/templates/dashboard/_vm-create-1.html
+1
-55
circle/dashboard/templates/dashboard/_vm-create-2.html
+90
-2
circle/dashboard/templates/dashboard/base.html
+17
-19
circle/dashboard/templates/dashboard/group-create.html
+0
-5
circle/dashboard/templates/dashboard/group-detail.html
+1
-1
circle/dashboard/templates/dashboard/index.html
+3
-3
circle/dashboard/templates/dashboard/lease-edit.html
+2
-2
circle/dashboard/templates/dashboard/node-create.html
+7
-6
circle/dashboard/templates/dashboard/nojs-wrapper.html
+1
-1
circle/dashboard/templates/dashboard/profile.html
+2
-2
circle/dashboard/templates/dashboard/store/_list-box.html
+143
-0
circle/dashboard/templates/dashboard/store/index-files.html
+63
-0
circle/dashboard/templates/dashboard/store/list.html
+42
-0
circle/dashboard/templates/dashboard/store/remove.html
+42
-0
circle/dashboard/templates/dashboard/store/upload.html
+38
-0
circle/dashboard/templates/dashboard/template-edit.html
+32
-1
circle/dashboard/templates/dashboard/vm-detail.html
+9
-4
circle/dashboard/templates/dashboard/vm-detail/resources.html
+9
-38
circle/dashboard/templates/dashboard/vm-list/column-actions.html
+1
-3
circle/dashboard/tests/test_views.py
+28
-44
circle/dashboard/urls.py
+19
-3
circle/dashboard/views.py
+253
-69
circle/fabfile.py
+6
-5
circle/firewall/tasks/local_tasks.py
+1
-1
circle/locale/hu/LC_MESSAGES/circle-hu.lokalize
+6
-0
circle/locale/hu/LC_MESSAGES/django.po
+0
-0
circle/locale/hu/LC_MESSAGES/djangojs.po
+237
-23
circle/locale/hu/LC_MESSAGES/lokalize-scripts/scripts.rc
+14
-0
circle/storage/migrations/0015_set_is_ready_to_base_images.py
+57
-0
circle/storage/models.py
+85
-47
circle/vm/models/common.py
+9
-4
circle/vm/models/instance.py
+36
-25
circle/vm/operations.py
+129
-116
circle/vm/tasks/local_agent_tasks.py
+26
-11
circle/vm/tests/test_models.py
+1
-1
requirements/base.txt
+0
-1
requirements/test.txt
+2
-0
No files found.
circle/circle/settings/base.py
View file @
b226eeef
...
...
@@ -447,5 +447,16 @@ if graphite_host and graphite_port:
else
:
GRAPHITE_URL
=
None
STORE_BASIC_AUTH
=
get_env_variable
(
"STORE_BASIC_AUTH"
,
""
)
==
"True"
STORE_VERIFY_SSL
=
get_env_variable
(
"STORE_VERIFY_SSL"
,
""
)
==
"True"
STORE_SSL_AUTH
=
get_env_variable
(
"STORE_SSL_AUTH"
,
""
)
==
"True"
STORE_CLIENT_USER
=
get_env_variable
(
"STORE_CLIENT_USER"
,
""
)
STORE_CLIENT_PASSWORD
=
get_env_variable
(
"STORE_CLIENT_PASSWORD"
,
""
)
STORE_CLIENT_KEY
=
get_env_variable
(
"STORE_CLIENT_KEY"
,
""
)
STORE_CLIENT_CERT
=
get_env_variable
(
"STORE_CLIENT_CERT"
,
""
)
STORE_URL
=
get_env_variable
(
"STORE_URL"
,
""
)
SESSION_COOKIE_NAME
=
"csessid
%
x"
%
(((
getnode
()
//
139
)
^
(
getnode
()
%
983
))
&
0xffff
)
MAX_NODE_RAM
=
get_env_variable
(
"MAX_NODE_RAM"
,
1024
)
circle/circle/settings/production.py
View file @
b226eeef
...
...
@@ -70,20 +70,14 @@ SERVER_EMAIL = EMAIL_HOST_USER
########## CACHE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
try
:
CACHES
=
{
'default'
:
{
'BACKEND'
:
'django.core.cache.backends.memcached.MemcachedCache'
,
'LOCATION'
:
get_env_variable
(
'DJANGO_MEMCACHED'
),
}
}
except
ImproperlyConfigured
:
CACHES
=
{
'default'
:
{
'BACKEND'
:
'django.core.cache.backends.locmem.LocMemCache'
,
'LOCATION'
:
SITE_NAME
,
}
from
urlparse
import
urlsplit
CACHES
=
{
'default'
:
{
'BACKEND'
:
'django.core.cache.backends.memcached.MemcachedCache'
,
'LOCATION'
:
urlsplit
(
get_env_variable
(
'CACHE_URI'
))
.
netloc
,
}
}
########## END CACHE CONFIGURATION
...
...
circle/circle/settings/test.py
View file @
b226eeef
...
...
@@ -35,7 +35,11 @@ SOUTH_TESTS_MIGRATE = False
INSTALLED_APPS
+=
(
'acl.tests'
,
'django_nose'
,
)
TEST_RUNNER
=
'django_nose.NoseTestSuiteRunner'
NOSE_ARGS
=
[
'--with-doctest'
]
PASSWORD_HASHERS
=
[
'django.contrib.auth.hashers.MD5PasswordHasher'
]
CACHES
=
{
'default'
:
{
...
...
@@ -52,3 +56,5 @@ LOGGING['handlers']['console'] = {'level': level,
'formatter'
:
'simple'
}
for
i
in
LOCAL_APPS
:
LOGGING
[
'loggers'
][
i
]
=
{
'handlers'
:
[
'console'
],
'level'
:
level
}
# Forbid store usage
STORE_URL
=
""
circle/common/models.py
View file @
b226eeef
...
...
@@ -23,6 +23,7 @@ from logging import getLogger
from
time
import
time
from
warnings
import
warn
from
django.contrib
import
messages
from
django.contrib.auth.models
import
User
from
django.core.cache
import
cache
from
django.core.serializers.json
import
DjangoJSONEncoder
...
...
@@ -46,17 +47,24 @@ class WorkerNotFound(Exception):
def
activitycontextimpl
(
act
,
on_abort
=
None
,
on_commit
=
None
):
try
:
yield
act
except
BaseException
as
e
:
# BaseException is the common parent of Exception and
# system-exiting exceptions, e.g. KeyboardInterrupt
try
:
yield
act
except
HumanReadableException
as
e
:
result
=
e
raise
except
BaseException
as
e
:
# BaseException is the common parent of Exception and
# system-exiting exceptions, e.g. KeyboardInterrupt
result
=
create_readable
(
ugettext_noop
(
"Failure."
),
ugettext_noop
(
"Unhandled exception:
%(error)
s"
),
error
=
unicode
(
e
))
raise
except
:
logger
.
exception
(
"Failed activity
%
s"
%
unicode
(
act
))
handler
=
None
if
on_abort
is
None
else
lambda
a
:
on_abort
(
a
,
e
)
result
=
create_readable
(
ugettext_noop
(
"Failure."
),
ugettext_noop
(
"Unhandled exception: "
"
%(error)
s"
),
error
=
unicode
(
e
))
act
.
finish
(
succeeded
=
False
,
result
=
result
,
event_handler
=
handler
)
raise
e
raise
else
:
act
.
finish
(
succeeded
=
True
,
event_handler
=
on_commit
)
...
...
@@ -70,11 +78,11 @@ activity_code_separator = '.'
def
has_prefix
(
activity_code
,
*
prefixes
):
"""Determine whether the activity code has the specified prefix.
E.g.: has_prefix('foo.bar.buz', 'foo.bar') == True
has_prefix('foo.bar.buz', 'foo', 'bar') == True
has_prefix('foo.bar.buz', 'foo.bar', 'buz') == True
has_prefix('foo.bar.buz', 'foo', 'bar', 'buz') == True
has_prefix('foo.bar.buz', 'foo', 'buz') == False
>>> assert has_prefix('foo.bar.buz', 'foo.bar')
>>> assert has_prefix('foo.bar.buz', 'foo', 'bar')
>>> assert has_prefix('foo.bar.buz', 'foo.bar', 'buz')
>>> assert has_prefix('foo.bar.buz', 'foo', 'bar', 'buz')
>>> assert not has_prefix('foo.bar.buz', 'foo', 'buz')
"""
equal
=
lambda
a
,
b
:
a
==
b
act_code_parts
=
split_activity_code
(
activity_code
)
...
...
@@ -85,11 +93,11 @@ def has_prefix(activity_code, *prefixes):
def
has_suffix
(
activity_code
,
*
suffixes
):
"""Determine whether the activity code has the specified suffix.
E.g.: has_suffix('foo.bar.buz', 'bar.buz') == True
has_suffix('foo.bar.buz', 'bar', 'buz') == True
has_suffix('foo.bar.buz', 'foo.bar', 'buz') == True
has_suffix('foo.bar.buz', 'foo', 'bar', 'buz') == True
has_suffix('foo.bar.buz', 'foo', 'buz') == False
>>> assert has_suffix('foo.bar.buz', 'bar.buz')
>>> assert has_suffix('foo.bar.buz', 'bar', 'buz')
>>> assert has_suffix('foo.bar.buz', 'foo.bar', 'buz')
>>> assert has_suffix('foo.bar.buz', 'foo', 'bar', 'buz')
>>> assert not has_suffix('foo.bar.buz', 'foo', 'buz')
"""
equal
=
lambda
a
,
b
:
a
==
b
act_code_parts
=
split_activity_code
(
activity_code
)
...
...
@@ -196,6 +204,10 @@ class ActivityModel(TimeStampedModel):
DeprecationWarning
,
stacklevel
=
2
)
value
=
create_readable
(
user_text_template
=
""
,
admin_text_template
=
value
)
elif
not
hasattr
(
value
,
"to_dict"
):
warn
(
"Use HumanReadableObject."
,
DeprecationWarning
,
stacklevel
=
2
)
value
=
create_readable
(
user_text_template
=
""
,
admin_text_template
=
unicode
(
value
))
self
.
result_data
=
None
if
value
is
None
else
value
.
to_dict
()
...
...
@@ -361,8 +373,9 @@ class HumanReadableObject(object):
@classmethod
def
create
(
cls
,
user_text_template
,
admin_text_template
=
None
,
**
params
):
return
cls
(
user_text_template
,
admin_text_template
or
user_text_template
,
params
)
return
cls
(
user_text_template
=
user_text_template
,
admin_text_template
=
(
admin_text_template
or
user_text_template
),
params
=
params
)
def
set
(
self
,
user_text_template
,
admin_text_template
=
None
,
**
params
):
self
.
_set_values
(
user_text_template
,
...
...
@@ -407,10 +420,29 @@ create_readable = HumanReadableObject.create
class
HumanReadableException
(
HumanReadableObject
,
Exception
):
"""HumanReadableObject that is an Exception so can used in except clause.
"""
pass
def
__init__
(
self
,
level
=
None
,
*
args
,
**
kwargs
):
super
(
HumanReadableException
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
if
level
is
not
None
:
if
hasattr
(
messages
,
level
):
self
.
level
=
level
else
:
raise
ValueError
(
"Level should be the name of an attribute of django."
"contrib.messages (and it should be callable with "
"(request, message)). Like 'error', 'warning'."
)
else
:
self
.
level
=
"error"
def
send_message
(
self
,
request
,
level
=
None
):
if
request
.
user
and
request
.
user
.
is_superuser
:
msg
=
self
.
get_admin_text
()
else
:
msg
=
self
.
get_user_text
()
getattr
(
messages
,
level
or
self
.
level
)(
request
,
msg
)
def
humanize_exception
(
message
,
exception
=
None
,
**
params
):
def
humanize_exception
(
message
,
exception
=
None
,
level
=
None
,
**
params
):
"""Return new dynamic-class exception which is based on
HumanReadableException and the original class with the dict of exception.
...
...
@@ -423,4 +455,7 @@ def humanize_exception(message, exception=None, **params):
Ex
=
type
(
"HumanReadable"
+
type
(
exception
)
.
__name__
,
(
HumanReadableException
,
type
(
exception
)),
exception
.
__dict__
)
return
Ex
.
create
(
message
,
**
params
)
ex
=
Ex
.
create
(
message
,
**
params
)
if
level
:
ex
.
level
=
level
return
ex
circle/dashboard/fixtures/test-vm-fixture.json
View file @
b226eeef
...
...
@@ -1322,7 +1322,7 @@
"user_permissions"
:
[
115
],
"password"
:
"
pbkdf2_sha256$10000$KIoeMs78MiOj$PnVXn3YJMehbOciBO32CMzqL0ZnQrzrdb7+b5dE13os="
,
"password"
:
"
md5$qLN4mQMOrsUJ$f07129fd1a289a0afb4e09f7a6816a4f"
,
"email"
:
"test@example.org"
,
"date_joined"
:
"2013-09-04T15:29:49.914Z"
}
...
...
@@ -1382,7 +1382,7 @@
"pw"
:
"ads"
,
"time_of_suspend"
:
null
,
"ram_size"
:
200
,
"priority"
:
4
,
"priority"
:
10
,
"active_since"
:
null
,
"template"
:
null
,
"access_method"
:
"nx"
,
...
...
@@ -1412,7 +1412,7 @@
"pw"
:
"ads"
,
"time_of_suspend"
:
null
,
"ram_size"
:
200
,
"priority"
:
4
,
"priority"
:
10
,
"active_since"
:
null
,
"template"
:
null
,
"access_method"
:
"nx"
,
...
...
@@ -1518,7 +1518,7 @@
"ram_size"
:
1024
,
"modified"
:
"2014-01-24T00:58:19.654Z"
,
"system"
:
"bubuntu"
,
"priority"
:
2
0
,
"priority"
:
1
0
,
"access_method"
:
"ssh"
,
"raw_data"
:
""
,
"arch"
:
"x86_64"
,
...
...
circle/dashboard/forms.py
View file @
b226eeef
...
...
@@ -30,7 +30,7 @@ from django.core.exceptions import PermissionDenied, ValidationError
import
autocomplete_light
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.layout
import
(
Layout
,
Div
,
BaseInput
,
Field
,
HTML
,
Submit
,
Fieldset
,
TEMPLATE_PACK
,
Layout
,
Div
,
BaseInput
,
Field
,
HTML
,
Submit
,
TEMPLATE_PACK
,
)
from
crispy_forms.utils
import
render_field
...
...
@@ -51,7 +51,7 @@ from vm.models import (
from
django.contrib.admin.widgets
import
FilteredSelectMultiple
from
django.contrib.auth.models
import
Permission
from
.models
import
Profile
,
GroupProfile
from
circle.settings.base
import
LANGUAGES
from
circle.settings.base
import
LANGUAGES
,
MAX_NODE_RAM
from
django.utils.translation
import
string_concat
from
.virtvalidator
import
domain_validator
...
...
@@ -59,6 +59,13 @@ from .virtvalidator import domain_validator
LANGUAGES_WITH_CODE
=
((
l
[
0
],
string_concat
(
l
[
1
],
" ("
,
l
[
0
],
")"
))
for
l
in
LANGUAGES
)
priority_choices
=
(
(
10
,
_
(
"idle"
)),
(
30
,
_
(
"normal"
)),
(
80
,
_
(
"server"
)),
(
100
,
_
(
"realtime"
)),
)
class
VmSaveForm
(
forms
.
Form
):
name
=
forms
.
CharField
(
max_length
=
100
,
label
=
_
(
'Name'
),
...
...
@@ -72,19 +79,62 @@ class VmSaveForm(forms.Form):
class
VmCustomizeForm
(
forms
.
Form
):
name
=
forms
.
CharField
()
cpu_priority
=
forms
.
IntegerField
()
cpu_count
=
forms
.
IntegerField
()
ram_size
=
forms
.
IntegerField
()
amount
=
forms
.
IntegerField
(
min_value
=
0
,
initial
=
1
)
name
=
forms
.
CharField
(
widget
=
forms
.
TextInput
(
attrs
=
{
'class'
:
"form-control"
,
'style'
:
"max-width: 350px"
,
'required'
:
""
,
}))
cpu_count
=
forms
.
IntegerField
(
widget
=
forms
.
NumberInput
(
attrs
=
{
'class'
:
"form-control input-tags cpu-count-input"
,
'min'
:
1
,
'max'
:
10
,
'required'
:
""
,
}),
min_value
=
1
,
max_value
=
10
,
)
ram_size
=
forms
.
IntegerField
(
widget
=
forms
.
TextInput
(
attrs
=
{
'class'
:
"form-control input-tags ram-input"
,
'min'
:
128
,
'pattern'
:
"
\
d+"
,
'max'
:
MAX_NODE_RAM
,
'step'
:
128
,
'required'
:
""
,
}),
min_value
=
128
,
max_value
=
MAX_NODE_RAM
,
)
cpu_priority
=
forms
.
ChoiceField
(
priority_choices
,
widget
=
forms
.
Select
(
attrs
=
{
'class'
:
"form-control input-tags cpu-priority-input"
,
})
)
amount
=
forms
.
IntegerField
(
widget
=
forms
.
NumberInput
(
attrs
=
{
'class'
:
"form-control"
,
'min'
:
"1"
,
'style'
:
"max-width: 60px"
,
'required'
:
""
,
}),
initial
=
1
,
min_value
=
1
)
disks
=
forms
.
ModelMultipleChoiceField
(
queryset
=
None
,
required
=
False
)
queryset
=
None
,
required
=
False
,
widget
=
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
"form-control"
,
'id'
:
"vm-create-disk-add-form"
,
})
)
networks
=
forms
.
ModelMultipleChoiceField
(
queryset
=
None
,
required
=
False
)
queryset
=
None
,
required
=
False
,
widget
=
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
"form-control"
,
'id'
:
"vm-create-network-add-vlan"
,
})
)
template
=
forms
.
CharField
()
customized
=
forms
.
CharField
(
)
# dummy flag field
template
=
forms
.
CharField
(
widget
=
forms
.
HiddenInput
()
)
customized
=
forms
.
CharField
(
widget
=
forms
.
HiddenInput
())
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
user
=
kwargs
.
pop
(
"user"
,
None
)
...
...
@@ -111,230 +161,6 @@ class VmCustomizeForm(forms.Form):
self
.
initial
[
'template'
]
=
self
.
template
.
pk
self
.
initial
[
'customized'
]
=
self
.
template
.
pk
# set widget for amount
self
.
fields
[
'amount'
]
.
widget
=
NumberInput
()
self
.
helper
=
FormHelper
(
self
)
# don't show labels for the sliders
self
.
helper
.
form_show_labels
=
True
self
.
fields
[
'cpu_count'
]
.
label
=
""
self
.
fields
[
'ram_size'
]
.
label
=
""
self
.
fields
[
'cpu_priority'
]
.
label
=
""
self
.
helper
.
layout
=
Layout
(
Field
(
"template"
,
type
=
"hidden"
),
Field
(
"customized"
,
type
=
"hidden"
),
Div
(
Div
(
AnyTag
(
# tip: don't try to use Button class
"button"
,
AnyTag
(
"i"
,
css_class
=
"fa fa-play"
),
HTML
(
" Start"
),
css_id
=
"vm-create-customized-start"
,
css_class
=
"btn btn-success"
,
style
=
"float: right; margin-top: 24px;"
,
),
Field
(
"name"
,
style
=
"max-width: 350px;"
),
css_class
=
"col-sm-12"
,
),
css_class
=
"row"
,
),
Div
(
Div
(
Field
(
"amount"
,
min
=
"1"
,
style
=
"max-width: 60px;"
),
css_class
=
"col-sm-10"
,
),
css_class
=
"row"
,
),
Div
(
Div
(
AnyTag
(
'h2'
,
HTML
(
_
(
"Resources"
)),
),
css_class
=
"col-sm-12"
,
),
css_class
=
"row"
,
),
Div
(
# cpu priority
Div
(
HTML
(
'<label for="vm-cpu-priority-slider">'
'<i class="fa fa-trophy"></i> CPU priority'
'</label>'
),
css_class
=
"col-sm-3"
),
Div
(
Field
(
'cpu_priority'
,
id
=
"vm-cpu-priority-slider"
,
css_class
=
"vm-slider"
,
data_slider_min
=
"0"
,
data_slider_max
=
"100"
,
data_slider_step
=
"1"
,
data_slider_value
=
self
.
template
.
priority
,
data_slider_handle
=
"square"
,
data_slider_tooltip
=
"hide"
),
css_class
=
"col-sm-9"
),
css_class
=
"row"
),
Div
(
# cpu count
Div
(
HTML
(
'<label for="cpu-count-slider">'
'<i class="fa fa-cogs"></i> CPU count'
'</label>'
),
css_class
=
"col-sm-3"
),
Div
(
Field
(
'cpu_count'
,
id
=
"vm-cpu-count-slider"
,
css_class
=
"vm-slider"
,
data_slider_min
=
"1"
,
data_slider_max
=
"8"
,
data_slider_step
=
"1"
,
data_slider_value
=
self
.
template
.
num_cores
,
data_slider_handle
=
"square"
,
data_slider_tooltip
=
"hide"
),
css_class
=
"col-sm-9"
),
css_class
=
"row"
),
Div
(
# ram size
Div
(
HTML
(
'<label for="ram-slider">'
'<i class="fa fa-ticket"></i> RAM amount'
'</label>'
),
css_class
=
"col-sm-3"
),
Div
(
Field
(
'ram_size'
,
id
=
"vm-ram-size-slider"
,
css_class
=
"vm-slider"
,
data_slider_min
=
"128"
,
data_slider_max
=
"4096"
,
data_slider_step
=
"128"
,
data_slider_value
=
self
.
template
.
ram_size
,
data_slider_handle
=
"square"
,
data_slider_tooltip
=
"hide"
),
css_class
=
"col-sm-9"
),
css_class
=
"row"
),
Div
(
# disks
Div
(
AnyTag
(
"h2"
,
HTML
(
"Disks"
)
),
css_class
=
"col-sm-4"
,
),
Div
(
Div
(
Field
(
"disks"
,
css_class
=
"form-control"
,
id
=
"vm-create-disk-add-form"
),
css_class
=
"js-hidden"
,
style
=
"padding-top: 15px; max-width: 450px;"
,
),
Div
(
AnyTag
(
"h3"
,
HTML
(
_
(
"No disks are added!"
)),
css_id
=
"vm-create-disk-list"
,
),
Div
(
HTML
(
""
),
style
=
"clear: both;"
,
),
# AnyTag(
# "h3",
# Div(
# AnyTag(
# "select",
# css_class="form-control",
# css_id="vm-create-disk-add-select",
# ),
# Div(
# AnyTag(
# "a",
# AnyTag(
# "i",
# css_class="icon-plus-sign",
# ),
# href="#",
# css_id="vm-create-disk-add-button",
# css_class="btn btn-success",
# ),
# css_class="input-group-btn"
# ),
# css_class="input-group",
# style="max-width: 330px;",
# ),
# css_id="vm-create-disk-add",
# ),
css_class
=
"no-js-hidden"
,
),
css_class
=
"col-sm-8"
,
style
=
"padding-top: 3px;"
,
),
css_class
=
"row"
,
),
# end of disks
Div
(
# network
Div
(
AnyTag
(
"h2"
,
HTML
(
_
(
"Network"
)),
),
css_class
=
"col-sm-4"
,
),
Div
(
Div
(
# js-hidden
Field
(
"networks"
,
css_class
=
"form-control"
,
id
=
"vm-create-network-add-vlan"
,
),
css_class
=
"js-hidden"
,
style
=
"padding-top: 15px; max-width: 450px;"
,
),
Div
(
# no-js-hidden
AnyTag
(
"h3"
,
HTML
(
_
(
"Not added to any network!"
)),
css_id
=
"vm-create-network-list"
,
),
AnyTag
(
"h3"
,
Div
(
AnyTag
(
"select"
,
css_class
=
(
"form-control "
"font-awesome-font"
),
css_id
=
"vm-create-network-add-select"
,
),
Div
(
AnyTag
(
"a"
,
AnyTag
(
"i"
,
css_class
=
"fa fa-plus-circle"
,
),
css_id
=
(
"vm-create-network-add"
"-button"
),
css_class
=
"btn btn-success"
,
),
css_class
=
"input-group-btn"
,
),
css_class
=
"input-group"
,
style
=
"max-width: 330px;"
,
),
css_class
=
"vm-create-network-add"
),
css_class
=
"no-js-hidden"
,
),
css_class
=
"col-sm-8"
,
style
=
"padding-top: 3px;"
,
),
css_class
=
"row"
),
# end of network
)
class
GroupCreateForm
(
forms
.
ModelForm
):
...
...
@@ -581,6 +407,29 @@ class TemplateForm(forms.ModelForm):
networks
=
forms
.
ModelMultipleChoiceField
(
queryset
=
None
,
required
=
False
,
label
=
_
(
"Networks"
))
num_cores
=
forms
.
IntegerField
(
widget
=
forms
.
NumberInput
(
attrs
=
{
'class'
:
"form-control input-tags cpu-count-input"
,
'min'
:
1
,
'max'
:
10
,
'required'
:
""
,
}),
min_value
=
1
,
max_value
=
10
,
)
ram_size
=
forms
.
IntegerField
(
widget
=
forms
.
NumberInput
(
attrs
=
{
'class'
:
"form-control input-tags ram-input"
,
'min'
:
128
,
'max'
:
MAX_NODE_RAM
,
'step'
:
128
,
'required'
:
""
,
}),
min_value
=
128
,
max_value
=
MAX_NODE_RAM
,
)
priority
=
forms
.
ChoiceField
(
priority_choices
,
widget
=
forms
.
Select
(
attrs
=
{
'class'
:
"form-control input-tags cpu-priority-input"
,
}))
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
user
=
kwargs
.
pop
(
"user"
,
None
)
super
(
TemplateForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
...
...
@@ -612,18 +461,27 @@ class TemplateForm(forms.ModelForm):
field
.
widget
.
attrs
[
'disabled'
]
=
'disabled'
if
not
self
.
instance
.
pk
and
len
(
self
.
errors
)
<
1
:
self
.
instance
.
priority
=
20
self
.
instance
.
ram_size
=
512
self
.
instance
.
num_cores
=
2
self
.
initial
[
'num_cores'
]
=
1
self
.
initial
[
'priority'
]
=
10
self
.
initial
[
'ram_size'
]
=
512
self
.
initial
[
'max_ram_size'
]
=
512
lease_queryset
=
(
Lease
.
get_objects_with_level
(
"operator"
,
self
.
user
)
.
distinct
()
|
Lease
.
objects
.
filter
(
pk
=
self
.
instance
.
lease_id
)
.
distinct
())
self
.
fields
[
"lease"
]
.
queryset
=
Lease
.
get_objects_with_level
(
"operator"
,
self
.
user
)
self
.
fields
[
"lease"
]
.
queryset
=
lease_queryset
self
.
fields
[
'raw_data'
]
.
validators
.
append
(
domain_validator
)
def
clean_owner
(
self
):
if
self
.
instance
.
pk
is
not
None
:
return
User
.
objects
.
get
(
pk
=
self
.
instance
.
owner
.
pk
)
return
self
.
user
def
clean_max_ram_size
(
self
):
return
self
.
cleaned_data
.
get
(
"ram_size"
,
0
)
def
_clean_fields
(
self
):
try
:
old
=
InstanceTemplate
.
objects
.
get
(
pk
=
self
.
instance
.
pk
)
...
...
@@ -685,77 +543,14 @@ class TemplateForm(forms.ModelForm):
submit_kwargs
[
'disabled'
]
=
None
helper
=
FormHelper
()
helper
.
layout
=
Layout
(
Field
(
"name"
),
Fieldset
(
_
(
"Resource configuration"
),
Div
(
# cpu count
Div
(
Field
(
'num_cores'
,
id
=
"vm-cpu-count-slider"
,
css_class
=
"vm-slider"
,
data_slider_min
=
"1"
,
data_slider_max
=
"8"
,
data_slider_step
=
"1"
,
data_slider_value
=
self
.
instance
.
num_cores
,
data_slider_handle
=
"square"
,
data_slider_tooltip
=
"hide"
),
css_class
=
"col-sm-9"
),
css_class
=
"row"
),
Div
(
# cpu priority
Div
(
Field
(
'priority'
,
id
=
"vm-cpu-priority-slider"
,
css_class
=
"vm-slider"
,
data_slider_min
=
"0"
,
data_slider_max
=
"100"
,
data_slider_step
=
"1"
,
data_slider_value
=
self
.
instance
.
priority
,
data_slider_handle
=
"square"
,
data_slider_tooltip
=
"hide"
),
css_class
=
"col-sm-9"
),
css_class
=
"row"
),
Div
(
Div
(
Field
(
'ram_size'
,
id
=
"vm-ram-size-slider"
,
css_class
=
"vm-slider"
,
data_slider_min
=
"128"
,
data_slider_max
=
"4096"
,
data_slider_step
=
"128"
,
data_slider_value
=
self
.
instance
.
ram_size
,
data_slider_handle
=
"square"
,
data_slider_tooltip
=
"hide"
),
css_class
=
"col-sm-9"
),
css_class
=
"row"
,
),
Field
(
'max_ram_size'
,
type
=
"hidden"
,
value
=
"0"
),
Field
(
'arch'
),
),
Fieldset
(
_
(
"Virtual machine settings"
),
Field
(
'access_method'
),
Field
(
'boot_menu'
),
Field
(
'raw_data'
),
Field
(
'req_traits'
),
Field
(
'description'
),
Field
(
"parent"
,
type
=
"hidden"
),
Field
(
"system"
),
),
Fieldset
(
_
(
"External resources"
),
Field
(
"networks"
),
Field
(
"lease"
),
Field
(
"tags"
),
),
)
helper
.
add_input
(
Submit
(
'submit'
,
'Save changes'
,
**
submit_kwargs
))
return
helper
class
Meta
:
model
=
InstanceTemplate
exclude
=
(
'state'
,
'disks'
,
)
widgets
=
{
'system'
:
forms
.
TextInput
'system'
:
forms
.
TextInput
,
'max_ram_size'
:
forms
.
HiddenInput
}
...
...
@@ -902,16 +697,18 @@ class LeaseForm(forms.ModelForm):
class
VmRenewForm
(
forms
.
Form
):
force
=
forms
.
BooleanField
(
required
=
False
,
label
=
_
(
"Set expiration times even if they are shorter than "
"the current value."
))
def
__init__
(
self
,
*
args
,
**
kwargs
):
choices
=
kwargs
.
pop
(
'choices'
)
default
=
kwargs
.
pop
(
'default'
)
super
(
VmRenewForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
fields
[
'lease'
]
=
forms
.
ModelChoiceField
(
queryset
=
choices
,
initial
=
default
,
required
=
False
,
empty_label
=
None
,
label
=
_
(
'Length'
))
self
.
fields
.
insert
(
0
,
'lease'
,
forms
.
ModelChoiceField
(
queryset
=
choices
,
initial
=
default
,
required
=
False
,
empty_label
=
None
,
label
=
_
(
'Length'
)))
if
len
(
choices
)
<
2
: