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
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
72 changed files
with
1730 additions
and
776 deletions
+1730
-776
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
+0
-0
circle/dashboard/migrations/0010_auto__add_field_profile_smb_password__add_field_profile_disk_quota.py
+0
-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
+0
-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
-0
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
+0
-0
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
+0
-0
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
+0
-0
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:
...
@@ -447,5 +447,16 @@ if graphite_host and graphite_port:
else
:
else
:
GRAPHITE_URL
=
None
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
)
^
SESSION_COOKIE_NAME
=
"csessid
%
x"
%
(((
getnode
()
//
139
)
^
(
getnode
()
%
983
))
&
0xffff
)
(
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
...
@@ -70,20 +70,14 @@ SERVER_EMAIL = EMAIL_HOST_USER
########## CACHE CONFIGURATION
########## CACHE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
try
:
from
urlparse
import
urlsplit
CACHES
=
{
'default'
:
{
CACHES
=
{
'BACKEND'
:
'django.core.cache.backends.memcached.MemcachedCache'
,
'default'
:
{
'LOCATION'
:
get_env_variable
(
'DJANGO_MEMCACHED'
),
'BACKEND'
:
'django.core.cache.backends.memcached.MemcachedCache'
,
}
'LOCATION'
:
urlsplit
(
get_env_variable
(
'CACHE_URI'
))
.
netloc
,
}
except
ImproperlyConfigured
:
CACHES
=
{
'default'
:
{
'BACKEND'
:
'django.core.cache.backends.locmem.LocMemCache'
,
'LOCATION'
:
SITE_NAME
,
}
}
}
}
########## END CACHE CONFIGURATION
########## END CACHE CONFIGURATION
...
...
circle/circle/settings/test.py
View file @
b226eeef
...
@@ -35,7 +35,11 @@ SOUTH_TESTS_MIGRATE = False
...
@@ -35,7 +35,11 @@ SOUTH_TESTS_MIGRATE = False
INSTALLED_APPS
+=
(
INSTALLED_APPS
+=
(
'acl.tests'
,
'acl.tests'
,
'django_nose'
,
)
)
TEST_RUNNER
=
'django_nose.NoseTestSuiteRunner'
NOSE_ARGS
=
[
'--with-doctest'
]
PASSWORD_HASHERS
=
[
'django.contrib.auth.hashers.MD5PasswordHasher'
]
CACHES
=
{
CACHES
=
{
'default'
:
{
'default'
:
{
...
@@ -52,3 +56,5 @@ LOGGING['handlers']['console'] = {'level': level,
...
@@ -52,3 +56,5 @@ LOGGING['handlers']['console'] = {'level': level,
'formatter'
:
'simple'
}
'formatter'
:
'simple'
}
for
i
in
LOCAL_APPS
:
for
i
in
LOCAL_APPS
:
LOGGING
[
'loggers'
][
i
]
=
{
'handlers'
:
[
'console'
],
'level'
:
level
}
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
...
@@ -23,6 +23,7 @@ from logging import getLogger
from
time
import
time
from
time
import
time
from
warnings
import
warn
from
warnings
import
warn
from
django.contrib
import
messages
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
django.core.serializers.json
import
DjangoJSONEncoder
from
django.core.serializers.json
import
DjangoJSONEncoder
...
@@ -46,17 +47,24 @@ class WorkerNotFound(Exception):
...
@@ -46,17 +47,24 @@ class WorkerNotFound(Exception):
def
activitycontextimpl
(
act
,
on_abort
=
None
,
on_commit
=
None
):
def
activitycontextimpl
(
act
,
on_abort
=
None
,
on_commit
=
None
):
try
:
try
:
yield
act
try
:
except
BaseException
as
e
:
yield
act
# BaseException is the common parent of Exception and
except
HumanReadableException
as
e
:
# system-exiting exceptions, e.g. KeyboardInterrupt
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
)
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
)
act
.
finish
(
succeeded
=
False
,
result
=
result
,
event_handler
=
handler
)
raise
e
raise
else
:
else
:
act
.
finish
(
succeeded
=
True
,
event_handler
=
on_commit
)
act
.
finish
(
succeeded
=
True
,
event_handler
=
on_commit
)
...
@@ -70,11 +78,11 @@ activity_code_separator = '.'
...
@@ -70,11 +78,11 @@ activity_code_separator = '.'
def
has_prefix
(
activity_code
,
*
prefixes
):
def
has_prefix
(
activity_code
,
*
prefixes
):
"""Determine whether the activity code has the specified prefix.
"""Determine whether the activity code has the specified prefix.
E.g.: has_prefix('foo.bar.buz', 'foo.bar') == True
>>> assert has_prefix('foo.bar.buz', 'foo.bar')
has_prefix('foo.bar.buz', 'foo', 'bar') == True
>>> assert has_prefix('foo.bar.buz', 'foo', 'bar')
has_prefix('foo.bar.buz', 'foo.bar', 'buz') == True
>>> assert has_prefix('foo.bar.buz', 'foo.bar', 'buz')
has_prefix('foo.bar.buz', 'foo', 'bar', 'buz') == True
>>> assert has_prefix('foo.bar.buz', 'foo', 'bar', 'buz')
has_prefix('foo.bar.buz', 'foo', 'buz') == False
>>> assert not has_prefix('foo.bar.buz', 'foo', 'buz')
"""
"""
equal
=
lambda
a
,
b
:
a
==
b
equal
=
lambda
a
,
b
:
a
==
b
act_code_parts
=
split_activity_code
(
activity_code
)
act_code_parts
=
split_activity_code
(
activity_code
)
...
@@ -85,11 +93,11 @@ def has_prefix(activity_code, *prefixes):
...
@@ -85,11 +93,11 @@ def has_prefix(activity_code, *prefixes):
def
has_suffix
(
activity_code
,
*
suffixes
):
def
has_suffix
(
activity_code
,
*
suffixes
):
"""Determine whether the activity code has the specified suffix.
"""Determine whether the activity code has the specified suffix.
E.g.: has_suffix('foo.bar.buz', 'bar.buz') == True
>>> assert has_suffix('foo.bar.buz', 'bar.buz')
has_suffix('foo.bar.buz', 'bar', 'buz') == True
>>> assert has_suffix('foo.bar.buz', 'bar', 'buz')
has_suffix('foo.bar.buz', 'foo.bar', 'buz') == True
>>> assert has_suffix('foo.bar.buz', 'foo.bar', 'buz')
has_suffix('foo.bar.buz', 'foo', 'bar', 'buz') == True
>>> assert has_suffix('foo.bar.buz', 'foo', 'bar', 'buz')
has_suffix('foo.bar.buz', 'foo', 'buz') == False
>>> assert not has_suffix('foo.bar.buz', 'foo', 'buz')
"""
"""
equal
=
lambda
a
,
b
:
a
==
b
equal
=
lambda
a
,
b
:
a
==
b
act_code_parts
=
split_activity_code
(
activity_code
)
act_code_parts
=
split_activity_code
(
activity_code
)
...
@@ -196,6 +204,10 @@ class ActivityModel(TimeStampedModel):
...
@@ -196,6 +204,10 @@ class ActivityModel(TimeStampedModel):
DeprecationWarning
,
stacklevel
=
2
)
DeprecationWarning
,
stacklevel
=
2
)
value
=
create_readable
(
user_text_template
=
""
,
value
=
create_readable
(
user_text_template
=
""
,
admin_text_template
=
value
)
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
()
self
.
result_data
=
None
if
value
is
None
else
value
.
to_dict
()
...
@@ -361,8 +373,9 @@ class HumanReadableObject(object):
...
@@ -361,8 +373,9 @@ class HumanReadableObject(object):
@classmethod
@classmethod
def
create
(
cls
,
user_text_template
,
admin_text_template
=
None
,
**
params
):
def
create
(
cls
,
user_text_template
,
admin_text_template
=
None
,
**
params
):
return
cls
(
user_text_template
,
return
cls
(
user_text_template
=
user_text_template
,
admin_text_template
or
user_text_template
,
params
)
admin_text_template
=
(
admin_text_template
or
user_text_template
),
params
=
params
)
def
set
(
self
,
user_text_template
,
admin_text_template
=
None
,
**
params
):
def
set
(
self
,
user_text_template
,
admin_text_template
=
None
,
**
params
):
self
.
_set_values
(
user_text_template
,
self
.
_set_values
(
user_text_template
,
...
@@ -407,10 +420,29 @@ create_readable = HumanReadableObject.create
...
@@ -407,10 +420,29 @@ create_readable = HumanReadableObject.create
class
HumanReadableException
(
HumanReadableObject
,
Exception
):
class
HumanReadableException
(
HumanReadableObject
,
Exception
):
"""HumanReadableObject that is an Exception so can used in except clause.
"""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
"""Return new dynamic-class exception which is based on
HumanReadableException and the original class with the dict of exception.
HumanReadableException and the original class with the dict of exception.
...
@@ -423,4 +455,7 @@ def humanize_exception(message, exception=None, **params):
...
@@ -423,4 +455,7 @@ def humanize_exception(message, exception=None, **params):
Ex
=
type
(
"HumanReadable"
+
type
(
exception
)
.
__name__
,
Ex
=
type
(
"HumanReadable"
+
type
(
exception
)
.
__name__
,
(
HumanReadableException
,
type
(
exception
)),
(
HumanReadableException
,
type
(
exception
)),
exception
.
__dict__
)
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 @@
...
@@ -1322,7 +1322,7 @@
"user_permissions"
:
[
"user_permissions"
:
[
115
115
],
],
"password"
:
"
pbkdf2_sha256$10000$KIoeMs78MiOj$PnVXn3YJMehbOciBO32CMzqL0ZnQrzrdb7+b5dE13os="
,
"password"
:
"
md5$qLN4mQMOrsUJ$f07129fd1a289a0afb4e09f7a6816a4f"
,
"email"
:
"test@example.org"
,
"email"
:
"test@example.org"
,
"date_joined"
:
"2013-09-04T15:29:49.914Z"
"date_joined"
:
"2013-09-04T15:29:49.914Z"
}
}
...
@@ -1382,7 +1382,7 @@
...
@@ -1382,7 +1382,7 @@
"pw"
:
"ads"
,
"pw"
:
"ads"
,
"time_of_suspend"
:
null
,
"time_of_suspend"
:
null
,
"ram_size"
:
200
,
"ram_size"
:
200
,
"priority"
:
4
,
"priority"
:
10
,
"active_since"
:
null
,
"active_since"
:
null
,
"template"
:
null
,
"template"
:
null
,
"access_method"
:
"nx"
,
"access_method"
:
"nx"
,
...
@@ -1412,7 +1412,7 @@
...
@@ -1412,7 +1412,7 @@
"pw"
:
"ads"
,
"pw"
:
"ads"
,
"time_of_suspend"
:
null
,
"time_of_suspend"
:
null
,
"ram_size"
:
200
,
"ram_size"
:
200
,
"priority"
:
4
,
"priority"
:
10
,
"active_since"
:
null
,
"active_since"
:
null
,
"template"
:
null
,
"template"
:
null
,
"access_method"
:
"nx"
,
"access_method"
:
"nx"
,
...
@@ -1518,7 +1518,7 @@
...
@@ -1518,7 +1518,7 @@
"ram_size"
:
1024
,
"ram_size"
:
1024
,
"modified"
:
"2014-01-24T00:58:19.654Z"
,
"modified"
:
"2014-01-24T00:58:19.654Z"
,
"system"
:
"bubuntu"
,
"system"
:
"bubuntu"
,
"priority"
:
2
0
,
"priority"
:
1
0
,
"access_method"
:
"ssh"
,
"access_method"
:
"ssh"
,
"raw_data"
:
""
,
"raw_data"
:
""
,
"arch"
:
"x86_64"
,
"arch"
:
"x86_64"
,
...
...
circle/dashboard/forms.py
View file @
b226eeef
This diff is collapsed.
Click to expand it.
circle/dashboard/migrations/0010_auto__add_field_profile_smb_password__add_field_profile_disk_quota.py
0 → 100644
View file @
b226eeef
This diff is collapsed.
Click to expand it.
circle/dashboard/migrations/0010_auto__add_futuremember__add_unique_futuremember_org_id_group.py
View file @
b226eeef
...
@@ -97,11 +97,13 @@ class Migration(SchemaMigration):
...
@@ -97,11 +97,13 @@ class Migration(SchemaMigration):
},
},
u'dashboard.profile'
:
{
u'dashboard.profile'
:
{
'Meta'
:
{
'object_name'
:
'Profile'
},
'Meta'
:
{
'object_name'
:
'Profile'
},
'disk_quota'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'2048'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'instance_limit'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'5'
}),
'instance_limit'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'5'
}),
'org_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'unique'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'org_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'unique'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'preferred_language'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'en'"
,
'max_length'
:
'32'
}),
'preferred_language'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'en'"
,
'max_length'
:
'32'
}),
'smb_password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'20'
}),
'use_gravatar'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'use_gravatar'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
},
},
...
@@ -269,4 +271,4 @@ class Migration(SchemaMigration):
...
@@ -269,4 +271,4 @@ class Migration(SchemaMigration):
}
}
}
}
complete_apps
=
[
'dashboard'
]
complete_apps
=
[
'dashboard'
]
\ No newline at end of file
circle/dashboard/migrations/0011_auto__add_field_notification_subject_data__add_field_notification_mess.py
View file @
b226eeef
...
@@ -98,11 +98,13 @@ class Migration(SchemaMigration):
...
@@ -98,11 +98,13 @@ class Migration(SchemaMigration):
},
},
u'dashboard.profile'
:
{
u'dashboard.profile'
:
{
'Meta'
:
{
'object_name'
:
'Profile'
},
'Meta'
:
{
'object_name'
:
'Profile'
},
'disk_quota'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'2048'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'instance_limit'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'5'
}),
'instance_limit'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'5'
}),
'org_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'unique'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'org_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'unique'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'preferred_language'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'en'"
,
'max_length'
:
'32'
}),
'preferred_language'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'en'"
,
'max_length'
:
'32'
}),
'smb_password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'20'
}),
'use_gravatar'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'use_gravatar'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
},
},
...
@@ -270,4 +272,4 @@ class Migration(SchemaMigration):
...
@@ -270,4 +272,4 @@ class Migration(SchemaMigration):
}
}
}
}
complete_apps
=
[
'dashboard'
]
complete_apps
=
[
'dashboard'
]
\ No newline at end of file
circle/dashboard/migrations/0012_migrate_messages.py
View file @
b226eeef
...
@@ -93,11 +93,13 @@ class Migration(DataMigration):
...
@@ -93,11 +93,13 @@ class Migration(DataMigration):
},
},
u'dashboard.profile'
:
{
u'dashboard.profile'
:
{
'Meta'
:
{
'object_name'
:
'Profile'
},
'Meta'
:
{
'object_name'
:
'Profile'
},
'disk_quota'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'2048'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'instance_limit'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'5'
}),
'instance_limit'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'5'
}),
'org_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'unique'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'org_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'unique'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'preferred_language'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'en'"
,
'max_length'
:
'32'
}),
'preferred_language'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'en'"
,
'max_length'
:
'32'
}),
'smb_password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'20'
}),
'use_gravatar'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'use_gravatar'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
},
},
...
...
circle/dashboard/migrations/0013_auto__del_field_notification_message__del_field_notification_subject.py
View file @
b226eeef
...
@@ -96,11 +96,13 @@ class Migration(SchemaMigration):
...
@@ -96,11 +96,13 @@ class Migration(SchemaMigration):
},
},
u'dashboard.profile'
:
{
u'dashboard.profile'
:
{
'Meta'
:
{
'object_name'
:
'Profile'
},
'Meta'
:
{
'object_name'
:
'Profile'
},
'disk_quota'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'2048'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'instance_limit'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'5'
}),
'instance_limit'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'5'
}),
'org_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'unique'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'org_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'unique'
:
'True'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'preferred_language'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'en'"
,
'max_length'
:
'32'
}),
'preferred_language'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'en'"
,
'max_length'
:
'32'
}),
'smb_password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'20'
}),
'use_gravatar'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'use_gravatar'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
},
},
...
@@ -268,4 +270,4 @@ class Migration(SchemaMigration):
...
@@ -268,4 +270,4 @@ class Migration(SchemaMigration):
}
}
}
}
complete_apps
=
[
'dashboard'
]
complete_apps
=
[
'dashboard'
]
\ No newline at end of file
circle/dashboard/migrations/0014_auto__chg_field_profile_disk_quota.py
0 → 100644
View file @
b226eeef
This diff is collapsed.
Click to expand it.
circle/dashboard/models.py
View file @
b226eeef
...
@@ -29,10 +29,13 @@ from django.db.models import (
...
@@ -29,10 +29,13 @@ from django.db.models import (
Model
,
ForeignKey
,
OneToOneField
,
CharField
,
IntegerField
,
TextField
,
Model
,
ForeignKey
,
OneToOneField
,
CharField
,
IntegerField
,
TextField
,
DateTimeField
,
permalink
,
BooleanField
DateTimeField
,
permalink
,
BooleanField
)
)
from
django.db.models.signals
import
post_save
,
pre_delete
from
django.db.models.signals
import
post_save
,
pre_delete
,
post_delete
from
django.templatetags.static
import
static
from
django.templatetags.static
import
static
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
django_sshkey.models
import
UserKey
from
django_sshkey.models
import
UserKey
from
django.core.exceptions
import
ObjectDoesNotExist
from
sizefield.models
import
FileSizeField
from
jsonfield
import
JSONField
from
jsonfield
import
JSONField
from
model_utils.models
import
TimeStampedModel
from
model_utils.models
import
TimeStampedModel
...
@@ -44,8 +47,12 @@ from common.models import HumanReadableObject, create_readable, Encoder
...
@@ -44,8 +47,12 @@ from common.models import HumanReadableObject, create_readable, Encoder
from
vm.tasks.agent_tasks
import
add_keys
,
del_keys
from
vm.tasks.agent_tasks
import
add_keys
,
del_keys
from
.store_api
import
Store
,
NoStoreException
,
NotOkException
logger
=
getLogger
(
__name__
)
logger
=
getLogger
(
__name__
)
pwgen
=
User
.
objects
.
make_random_password
class
Favourite
(
Model
):
class
Favourite
(
Model
):
instance
=
ForeignKey
(
"vm.Instance"
)
instance
=
ForeignKey
(
"vm.Instance"
)
...
@@ -109,6 +116,18 @@ class Profile(Model):
...
@@ -109,6 +116,18 @@ class Profile(Model):
email_notifications
=
BooleanField
(
email_notifications
=
BooleanField
(
verbose_name
=
_
(
"Email notifications"
),
default
=
True
,
verbose_name
=
_
(
"Email notifications"
),
default
=
True
,
help_text
=
_
(
'Whether user wants to get digested email notifications.'
))
help_text
=
_
(
'Whether user wants to get digested email notifications.'
))
smb_password
=
CharField
(
max_length
=
20
,
verbose_name
=
_
(
'Samba password'
),
help_text
=
_
(
'Generated password for accessing store from '
'virtual machines.'
),
default
=
pwgen
,
)
disk_quota
=
FileSizeField
(
verbose_name
=
_
(
'disk quota'
),
default
=
2048
*
1024
*
1024
,
help_text
=
_
(
'Disk quota in mebibytes.'
))
def
notify
(
self
,
subject
,
template
,
context
=
None
,
valid_until
=
None
,
def
notify
(
self
,
subject
,
template
,
context
=
None
,
valid_until
=
None
,
**
kwargs
):
**
kwargs
):
...
@@ -201,6 +220,11 @@ def create_profile(sender, user, request, **kwargs):
...
@@ -201,6 +220,11 @@ def create_profile(sender, user, request, **kwargs):
if
not
user
.
pk
:
if
not
user
.
pk
:
return
False
return
False
profile
,
created
=
Profile
.
objects
.
get_or_create
(
user
=
user
)
profile
,
created
=
Profile
.
objects
.
get_or_create
(
user
=
user
)
try
:
Store
(
user
)
.
create_user
(
profile
.
smb_password
,
None
,
profile
.
disk_quota
)
except
:
logger
.
exception
(
"Can't create user
%
s"
,
unicode
(
user
))
return
created
return
created
user_logged_in
.
connect
(
create_profile
)
user_logged_in
.
connect
(
create_profile
)
...
@@ -268,6 +292,44 @@ else:
...
@@ -268,6 +292,44 @@ else:
logger
.
debug
(
"Do not register save_org_id to djangosaml2 pre_user_save"
)
logger
.
debug
(
"Do not register save_org_id to djangosaml2 pre_user_save"
)
def
update_store_profile
(
sender
,
**
kwargs
):
profile
=
kwargs
.
get
(
'instance'
)
keys
=
[
i
.
key
for
i
in
profile
.
user
.
userkey_set
.
all
()]
try
:
s
=
Store
(
profile
.
user
)
s
.
create_user
(
profile
.
smb_password
,
keys
,
profile
.
disk_quota
)
except
NoStoreException
:
logger
.
debug
(
"Store is not available."
)
except
NotOkException
:
logger
.
critical
(
"Store is not accepting connections."
)
post_save
.
connect
(
update_store_profile
,
sender
=
Profile
)
def
update_store_keys
(
sender
,
**
kwargs
):
userkey
=
kwargs
.
get
(
'instance'
)
try
:
profile
=
userkey
.
user
.
profile
except
ObjectDoesNotExist
:
pass
# If there is no profile the user is deleted
else
:
keys
=
[
i
.
key
for
i
in
profile
.
user
.
userkey_set
.
all
()]
try
:
s
=
Store
(
userkey
.
user
)
s
.
create_user
(
profile
.
smb_password
,
keys
,
profile
.
disk_quota
)
except
NoStoreException
:
logger
.
debug
(
"Store is not available."
)
except
NotOkException
:
logger
.
critical
(
"Store is not accepting connections."
)
post_save
.
connect
(
update_store_keys
,
sender
=
UserKey
)
post_delete
.
connect
(
update_store_keys
,
sender
=
UserKey
)
def
add_ssh_keys
(
sender
,
**
kwargs
):
def
add_ssh_keys
(
sender
,
**
kwargs
):
from
vm.models
import
Instance
from
vm.models
import
Instance
...
...
circle/dashboard/static/dashboard/dashboard.css
View file @
b226eeef
...
@@ -186,42 +186,6 @@ html {
...
@@ -186,42 +186,6 @@ html {
text-decoration
:
none
!important
;
text-decoration
:
none
!important
;
}
}
.slider
{
display
:
inline-block
;
}
.slider
.track
{
height
:
20px
;
top
:
50%
;
}
.slider
>
.dragger
,
.slider
>
.dragger
:hover
{
border-radius
:
0px
;
-moz-border-radius
:
0px
;
-webkit-border-radius
:
0px
;
width
:
8px
;
height
:
24px
;
margin-top
:
-12px
!important
;
text-shadow
:
0
1px
0
#fff
;
background-image
:
-webkit-gradient
(
linear
,
left
0%
,
left
100%
,
from
(
#428bca
),
to
(
#3071a9
));
background-image
:
-webkit-linear-gradient
(
top
,
#428bca
,
0%
,
#3071a9
,
100%
);
background-image
:
-moz-linear-gradient
(
top
,
#428bca
0%
,
#3071a9
100%
);
background-image
:
linear-gradient
(
to
bottom
,
#428bca
0%
,
#3071a9
100%
);
background-repeat
:
repeat-x
;
border-color
:
#2d6ca2
;
filter
:
progid
:
DXImageTransform
.
Microsoft
.
gradient
(
startColorstr
=
'#ff428bca'
,
endColorstr
=
'#ff3071a9'
,
GradientType
=
0
);
}
.slider
>
.dragger
:hover
{
background-color
:
#3071a9
;
background-image
:
none
;
border-color
:
#2d6ca2
;
}
.slider
>
.highlight-track
{
height
:
20px
;
top
:
50%
;
}
.slider
+
.output
{
}
.rule-table
tr
>
:nth-child
(
1
)
{
.rule-table
tr
>
:nth-child
(
1
)
{
text-align
:
right
;
text-align
:
right
;
}
}
...
@@ -723,6 +687,82 @@ textarea[name="list-new-namelist"] {
...
@@ -723,6 +687,82 @@ textarea[name="list-new-namelist"] {
}
}
#store-list-list
{
list-style
:
none
;
}
.store-list-item
{
cursor
:
pointer
;
}
.store-list-item
:hover
{
background
:
rgba
(
0
,
0
,
0
,
0.6
);
}
.store-list-item-icon
{
width
:
20px
;
text-align
:
center
;
display
:
inline-block
;
margin-right
:
15px
;
float
:
left
;
}
.store-list-item-size
{
width
:
70px
;
text-align
:
right
;
float
:
right
;
}
.store-list-file-infos
{
padding
:
15px
;
display
:
none
;
border-left
:
1px
solid
#ddd
;
border-right
:
1px
solid
#ddd
;
position
:
relative
;
}
.store-list-item-new
{
display
:
inline-block
;
}
.store-list-item-new
.badge
{
margin-left
:
5px
;
background
:
#5bc0dc
;
}
.store-list-item-icon-directory
{
color
:
#ff8c00
;
}
.store-remove-button
{
margin-top
:
8px
;
}
#dashboard-files-toplist
div
.list-group-item
{
color
:
#555
;
}
#dashboard-files-toplist
div
.list-group-item
:hover
{
background
:
#eee
;
}
.store-list-item-name
{
max-width
:
70%
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
overflow
:
hidden
;
float
:
left
;
}
.dashboard-toplist-icon
{
float
:
left
;
padding
:
2px
5px
0
0
;
}
.no-hover
:hover
{
background
:
none
!important
;
}
#group-detail-permissions
.filtered
{
#group-detail-permissions
.filtered
{
margin
:
2px
0
;
margin
:
2px
0
;
padding
:
2px
3px
;
padding
:
2px
3px
;
...
@@ -752,6 +792,74 @@ textarea[name="list-new-namelist"] {
...
@@ -752,6 +792,74 @@ textarea[name="list-new-namelist"] {
margin-top
:
-6px
;
margin-top
:
-6px
;
}
}
.store-action-button
{
margin-left
:
5px
;
}
#progress-marker-hard
{
border-top-right-radius
:
4px
;
border-bottom-right-radius
:
4px
;
right
:
0
;
background
:
red
;
}
.progress-marker
{
width
:
6px
;
height
:
20px
;
position
:
absolute
;
}
#show-all-activities-container
{
#show-all-activities-container
{
margin
:
20px
0
0
10px
;
margin
:
20px
0
0
10px
;
}