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
Show whitespace changes
Inline
Side-by-side
Showing
72 changed files
with
1692 additions
and
735 deletions
+1692
-735
circle/circle/settings/base.py
+11
-0
circle/circle/settings/production.py
+5
-11
circle/circle/settings/test.py
+6
-0
circle/common/models.py
+55
-20
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
+2
-0
circle/dashboard/migrations/0011_auto__add_field_notification_subject_data__add_field_notification_mess.py
+2
-0
circle/dashboard/migrations/0012_migrate_messages.py
+2
-0
circle/dashboard/migrations/0013_auto__del_field_notification_message__del_field_notification_subject.py
+2
-0
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
+64
-18
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
+27
-53
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
+5
-7
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
+15
-31
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:
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
=
{
from
urlparse
import
urlsplit
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
,
}
'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
:
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
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"
),
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
)
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
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):
},
u'dashboard.profile'
:
{
'Meta'
:
{
'object_name'
:
'Profile'
},
'disk_quota'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'2048'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'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'
}),
'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'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
},
...
...
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):
},
u'dashboard.profile'
:
{
'Meta'
:
{
'object_name'
:
'Profile'
},
'disk_quota'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'2048'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'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'
}),
'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'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
},
...
...
circle/dashboard/migrations/0012_migrate_messages.py
View file @
b226eeef
...
...
@@ -93,11 +93,13 @@ class Migration(DataMigration):
},
u'dashboard.profile'
:
{
'Meta'
:
{
'object_name'
:
'Profile'
},
'disk_quota'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'2048'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'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'
}),
'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'
}),
'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):
},
u'dashboard.profile'
:
{
'Meta'
:
{
'object_name'
:
'Profile'
},
'disk_quota'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'2048'
}),
'email_notifications'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
u'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'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'
}),
'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'
}),
'user'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
u"orm['auth.User']"
,
'unique'
:
'True'
})
},
...
...
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 (
Model
,
ForeignKey
,
OneToOneField
,
CharField
,
IntegerField
,
TextField
,
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.utils.translation
import
ugettext_lazy
as
_
from
django_sshkey.models
import
UserKey
from
django.core.exceptions
import
ObjectDoesNotExist
from
sizefield.models
import
FileSizeField
from
jsonfield
import
JSONField
from
model_utils.models
import
TimeStampedModel
...
...
@@ -44,8 +47,12 @@ from common.models import HumanReadableObject, create_readable, Encoder
from
vm.tasks.agent_tasks
import
add_keys
,
del_keys
from
.store_api
import
Store
,
NoStoreException
,
NotOkException
logger
=
getLogger
(
__name__
)
pwgen
=
User
.
objects
.
make_random_password
class
Favourite
(
Model
):
instance
=
ForeignKey
(
"vm.Instance"
)
...
...
@@ -109,6 +116,18 @@ class Profile(Model):
email_notifications
=
BooleanField
(
verbose_name
=
_
(
"Email notifications"
),
default
=
True
,
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
,
**
kwargs
):
...
...
@@ -201,6 +220,11 @@ def create_profile(sender, user, request, **kwargs):
if
not
user
.
pk
:
return
False
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
user_logged_in
.
connect
(
create_profile
)
...
...
@@ -268,6 +292,44 @@ else:
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
):
from
vm.models
import
Instance
...
...
circle/dashboard/static/dashboard/dashboard.css
View file @
b226eeef
...
...
@@ -186,42 +186,6 @@ html {
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
)
{
text-align
:
right
;
}
...
...
@@ -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
{
margin
:
2px
0
;
padding
:
2px
3px
;
...
...
@@ -752,6 +792,74 @@ textarea[name="list-new-namelist"] {
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
{
margin
:
20px
0
0
10px
;
}
#vm-details-resources-form
{
margin-top
:
15px
;
}
#vm-details-resources-form
.row
,
.resources-sliders
.row
{
margin-bottom
:
15px
;
}
#vm-create-disk-add-form
{
max-width
:
450px
;
margin-top
:
15px
;
}
.vm-create-template
{
max-width
:
800px
;
border
:
1px
solid
black
;
border-bottom
:
none
;
}
.vm-create-template-list
.vm-create-template
:last-child
{
border-bottom
:
1px
solid
black
;
}
.vm-create-template-summary
{
padding
:
15px
;
cursor
:
pointer
;
}
.vm-create-template
:nth-child
(
odd
)
.vm-create-template-summary
{
background
:
#F5F5F5
;
}
.vm-create-template-list
.vm-create-template-summary
:hover
{
background
:
#D2D2D2
;
}
.vm-create-template-details
{
border-top
:
1px
dashed
#D3D3D3
;
padding
:
15px
;
}
.vm-create-template-details
ul
{
list-style
:
none
;
padding
:
0
15px
;
}
.vm-create-template-details
li
{
border-bottom
:
1px
dotted
#aaa
;
padding
:
5px
0px
;
}
circle/dashboard/static/dashboard/dashboard.js
View file @
b226eeef
...
...
@@ -112,6 +112,7 @@ $(function () {
/* no js compatibility */
noJS
();
$
(
'.no-js-hidden'
).
show
();
$
(
'.js-hidden'
).
hide
();
...
...
@@ -349,9 +350,9 @@ $(function () {
}
}
for
(
var
i
=
0
;
i
<
5
&&
i
<
search_result
.
length
;
i
++
)
html
+=
generateGroupHTML
(
search_result
[
i
].
url
,
search_result
[
i
].
name
);
html
+=
generateGroupHTML
(
search_result
[
i
].
url
,
search_result
[
i
].
name
,
search_result
.
length
<
5
);
if
(
search_result
.
length
==
0
)
html
+=
'<div class="list-group-item">No result</div>'
;
html
+=
'<div class="list-group-item
list-group-item-last
">No result</div>'
;
$
(
"#dashboard-group-list"
).
html
(
html
);
// if there is only one result and ENTER is pressed redirect
...
...
@@ -370,7 +371,10 @@ $(function () {
});
/* don't close notifications window on missclick */
$
(
document
).
on
(
"click"
,
".notification-messages"
,
function
()
{
$
(
document
).
on
(
"click"
,
".notification-messages"
,
function
(
e
)
{
if
(
$
(
e
.
target
).
closest
(
"a"
).
length
)
return
true
else
return
false
;
});
...
...
@@ -395,8 +399,8 @@ function generateVmHTML(pk, name, host, icon, _status, fav, is_last) {
'</a>'
;
}
function
generateGroupHTML
(
url
,
name
)
{
return
'<a href="'
+
url
+
'" class="list-group-item real-link">'
+
function
generateGroupHTML
(
url
,
name
,
is_last
)
{
return
'<a href="'
+
url
+
'" class="list-group-item real-link
'
+
(
is_last
?
" list-group-item-last"
:
""
)
+
'
">'
+
'<i class="fa fa-users"></i> '
+
name
+
'</a>'
;
}
...
...
@@ -431,28 +435,63 @@ function compareVmByFav(a, b) {
return
a
.
pk
<
b
.
pk
?
-
1
:
1
;
}
$
(
document
).
on
(
'shown.bs.tab'
,
'a[href="#resources"]'
,
function
(
e
)
{
$
(
".cpu-priority-input"
).
trigger
(
"change"
);
$
(
".cpu-count-input, .ram-input"
).
trigger
(
"input"
);
})
function
addSliderMiscs
()
{
$
(
'.vm-slider'
).
each
(
function
()
{
$
(
"<span>"
).
addClass
(
"output"
).
html
(
$
(
this
).
val
()).
insertAfter
(
$
(
this
));
// set max values based on inputs
var
cpu_count_range
=
"0, "
+
$
(
".cpu-count-input"
).
prop
(
"max"
);
var
ram_range
=
"0, "
+
$
(
".ram-input"
).
prop
(
"max"
);
$
(
".cpu-count-slider"
).
data
(
"slider-range"
,
cpu_count_range
);
$
(
".ram-slider"
).
data
(
"slider-range"
,
ram_range
);
$
(
".vm-slider"
).
simpleSlider
();
$
(
".cpu-priority-slider"
).
bind
(
"slider:changed"
,
function
(
event
,
data
)
{
var
value
=
data
.
value
+
0
;
$
(
'.cpu-priority-input option[value="'
+
value
+
'"]'
).
attr
(
"selected"
,
"selected"
);
});
$
(
'.vm-slider'
).
slider
()
.
on
(
'slide'
,
function
(
e
)
{
$
(
this
).
val
(
e
.
value
);
$
(
this
).
parent
(
'div'
).
nextAll
(
"span"
).
html
(
e
.
value
)
$
(
".cpu-priority-input"
).
change
(
function
()
{
var
val
=
$
(
":selected"
,
$
(
this
)).
val
();
$
(
".cpu-priority-slider"
).
simpleSlider
(
"setValue"
,
val
);
});
refreshSliders
();
}
$
(
".cpu-count-slider"
).
bind
(
"slider:changed"
,
function
(
event
,
data
)
{
var
value
=
data
.
value
+
0
;
$
(
".cpu-count-input"
).
val
(
parseInt
(
value
));
});
// ehhh
function
refreshSliders
()
{
$
(
'.vm-slider'
).
each
(
function
()
{
$
(
this
).
val
(
$
(
this
).
slider
().
data
(
'slider'
).
getValue
());
$
(
this
).
parent
(
'div'
).
nextAll
(
"span"
).
html
(
$
(
this
).
val
());
$
(
".cpu-count-input"
).
bind
(
"input"
,
function
()
{
var
val
=
parseInt
(
$
(
this
).
val
());
if
(
!
val
)
return
;
$
(
".cpu-count-slider"
).
simpleSlider
(
"setValue"
,
val
);
});
var
ram_fire
=
false
;
$
(
".ram-slider"
).
bind
(
"slider:changed"
,
function
(
event
,
data
)
{
if
(
ram_fire
)
{
ram_fire
=
false
;
return
;
}
var
value
=
data
.
value
+
0
;
$
(
".ram-input"
).
val
(
value
);
});
$
(
".ram-input"
).
bind
(
"input"
,
function
()
{
var
val
=
$
(
this
).
val
();
ram_fire
=
true
;
$
(
".ram-slider"
).
simpleSlider
(
"setValue"
,
parseInt
(
val
));
});
$
(
".cpu-priority-input"
).
trigger
(
"change"
);
$
(
".cpu-count-input, .ram-input"
).
trigger
(
"input"
);
}