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
07fb9e65
authored
Feb 07, 2014
by
Őry Máté
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into feature-activity-state
parents
71932249
f17bddde
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
282 additions
and
175 deletions
+282
-175
circle/circle/settings/base.py
+4
-0
circle/dashboard/fixtures/test-vm-fixture.json
+2
-1
circle/dashboard/forms.py
+16
-1
circle/dashboard/migrations/0002_auto__add_profile.py
+0
-0
circle/dashboard/migrations/0003_auto__add_field_profile_instance_limit.py
+0
-0
circle/dashboard/models.py
+65
-1
circle/dashboard/static/dashboard/node-details.js
+0
-129
circle/dashboard/static/dashboard/node-list.js
+2
-2
circle/dashboard/static/dashboard/vm-create.js
+4
-2
circle/dashboard/static/dashboard/vm-details.js
+4
-5
circle/dashboard/static/dashboard/vm-list.js
+1
-1
circle/dashboard/templates/dashboard/template-edit.html
+50
-2
circle/dashboard/templates/dashboard/vm-detail.html
+5
-1
circle/dashboard/templates/dashboard/vm-detail/activity.html
+5
-7
circle/dashboard/templates/dashboard/vm-detail/home.html
+3
-3
circle/dashboard/urls.py
+8
-1
circle/dashboard/views.py
+80
-17
circle/vm/migrations/0014_auto__add_field_instancetemplate_owner.py
+0
-0
circle/vm/models/instance.py
+25
-0
circle/vm/models/node.py
+3
-2
circle/vm/tasks/vm_tasks.py
+5
-0
No files found.
circle/circle/settings/base.py
View file @
07fb9e65
...
...
@@ -382,3 +382,7 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE':
'DJANGO_SAML_ATTRIBUTE_MAPPING'
,
'{"mail": ["email"], "sn": ["last_name"], '
'"uid": ["username"], "cn": ["first_name"]}'
))
SAML_CREATE_UNKNOWN_USER
=
True
if
get_env_variable
(
'DJANGO_SAML_ORG_ID_ATTRIBUTE'
,
False
)
!=
False
:
SAML_ORG_ID_ATTRIBUTE
=
get_env_variable
(
'DJANGO_SAML_ORG_ID_ATTRIBUTE'
)
circle/dashboard/fixtures/test-vm-fixture.json
View file @
07fb9e65
...
...
@@ -1467,7 +1467,8 @@
"raw_data"
:
""
,
"arch"
:
"x86_64"
,
"max_ram_size"
:
1024
,
"lease"
:
1
"lease"
:
1
,
"owner"
:
1
}
}
]
circle/dashboard/forms.py
View file @
07fb9e65
from
datetime
import
timedelta
import
uuid
from
django.contrib.auth.models
import
User
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.layout
import
(
Layout
,
Div
,
BaseInput
,
Field
,
HTML
,
Submit
,
Fieldset
,
TEMPLATE_PACK
...
...
@@ -430,8 +432,16 @@ class TemplateForm(forms.ModelForm):
def
__init__
(
self
,
*
args
,
**
kwargs
):
parent
=
kwargs
.
pop
(
"parent"
,
None
)
self
.
user
=
kwargs
.
pop
(
"user"
,
None
)
super
(
TemplateForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
fields
[
'disks'
]
=
forms
.
ModelMultipleChoiceField
(
queryset
=
DISKS
)
self
.
fields
[
'disks'
]
=
forms
.
ModelMultipleChoiceField
(
queryset
=
Disk
.
get_objects_with_level
(
'user'
,
self
.
user
)
.
exclude
(
type
=
"qcow2-snap"
)
)
data
=
self
.
data
.
copy
()
data
[
'owner'
]
=
self
.
user
.
pk
self
.
data
=
data
if
parent
is
not
None
:
template
=
InstanceTemplate
.
objects
.
get
(
pk
=
parent
)
...
...
@@ -458,6 +468,11 @@ class TemplateForm(forms.ModelForm):
self
.
instance
.
ram_size
=
512
self
.
instance
.
num_cores
=
2
def
clean_owner
(
self
):
if
self
.
instance
.
pk
is
not
None
:
return
User
.
objects
.
get
(
pk
=
self
.
instance
.
owner
.
pk
)
return
self
.
user
def
save
(
self
,
commit
=
True
):
data
=
self
.
cleaned_data
self
.
instance
.
max_ram_size
=
data
.
get
(
'ram_size'
)
...
...
circle/dashboard/migrations/0002_auto__add_profile.py
0 → 100644
View file @
07fb9e65
This diff is collapsed.
Click to expand it.
circle/dashboard/migrations/0003_auto__add_field_profile_instance_limit.py
0 → 100644
View file @
07fb9e65
This diff is collapsed.
Click to expand it.
circle/dashboard/models.py
View file @
07fb9e65
from
logging
import
getLogger
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.db.models
import
Model
,
ForeignKey
from
django.contrib.auth.signals
import
user_logged_in
from
django.db.models
import
(
Model
,
ForeignKey
,
OneToOneField
,
CharField
,
IntegerField
)
from
django.utils.translation
import
ugettext_lazy
as
_
from
vm.models
import
Instance
logger
=
getLogger
(
__name__
)
class
Favourite
(
Model
):
instance
=
ForeignKey
(
Instance
)
user
=
ForeignKey
(
User
)
class
Profile
(
Model
):
user
=
OneToOneField
(
User
)
preferred_language
=
CharField
(
verbose_name
=
_
(
'preferred language'
),
choices
=
settings
.
LANGUAGES
,
max_length
=
32
,
default
=
settings
.
LANGUAGE_CODE
,
blank
=
False
)
org_id
=
CharField
(
# may be populated from eduPersonOrgId field
unique
=
True
,
blank
=
True
,
null
=
True
,
max_length
=
64
,
help_text
=
_
(
'Unique identifier of the person, e.g. a student number.'
))
instance_limit
=
IntegerField
(
default
=
5
)
def
create_profile
(
sender
,
user
,
request
,
**
kwargs
):
if
not
user
.
pk
:
return
False
profile
,
created
=
Profile
.
objects
.
get_or_create
(
user
=
user
)
return
created
user_logged_in
.
connect
(
create_profile
)
if
hasattr
(
settings
,
'SAML_ORG_ID_ATTRIBUTE'
):
logger
.
debug
(
"Register save_org_id to djangosaml2 pre_user_save"
)
from
djangosaml2.signals
import
pre_user_save
def
save_org_id
(
sender
,
**
kwargs
):
logger
.
debug
(
"save_org_id called by
%
s"
,
sender
.
username
)
attributes
=
kwargs
.
pop
(
'attributes'
)
atr
=
settings
.
SAML_ORG_ID_ATTRIBUTE
try
:
value
=
attributes
[
atr
][
0
]
except
Exception
as
e
:
value
=
None
logger
.
info
(
"save_org_id couldn't find attribute.
%
s"
,
unicode
(
e
))
if
sender
.
pk
is
None
:
sender
.
save
()
logger
.
debug
(
"save_org_id saved user
%
s"
,
unicode
(
sender
))
profile
,
created
=
Profile
.
objects
.
get_or_create
(
user
=
sender
)
if
created
or
profile
.
org_id
!=
value
:
logger
.
info
(
"org_id of
%
s added to user
%
s's profile"
,
value
,
sender
.
username
)
profile
.
org_id
=
value
profile
.
save
()
else
:
logger
.
debug
(
"org_id of
%
s already added to user
%
s's profile"
,
value
,
sender
.
username
)
return
False
pre_user_save
.
connect
(
save_org_id
)
else
:
logger
.
debug
(
"Do not register save_org_id to djangosaml2 pre_user_save"
)
circle/dashboard/static/dashboard/node-details.js
deleted
100644 → 0
View file @
71932249
$
(
function
()
{
if
(
$
(
'.timeline .activity:first i:first'
).
hasClass
(
'icon-spin'
))
checkNewActivity
();
/* save resources */
$
(
'#vm-details-resources-save'
).
click
(
function
()
{
$
(
'i.icon-save'
,
this
).
removeClass
(
"icon-save"
).
addClass
(
"icon-refresh icon-spin"
);
$
.
ajax
({
type
:
'POST'
,
url
:
location
.
href
,
data
:
$
(
'#vm-details-resources-form'
).
serialize
(),
success
:
function
(
data
,
textStatus
,
xhr
)
{
addMessage
(
data
[
'message'
],
'success'
);
$
(
"#vm-details-resources-save i"
).
removeClass
(
'icon-refresh icon-spin'
).
addClass
(
"icon-save"
);
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
$
(
"#vm-details-resources-save i"
).
removeClass
(
'icon-refresh icon-spin'
).
addClass
(
"icon-save"
);
addMessage
(
"Eww, something is wrong"
,
'danger'
);
if
(
xhr
.
status
==
500
)
{
// alert("uhuhuhuhuhuh");
}
else
{
// alert("unknown error");
}
}
});
return
false
;
});
/* rename */
$
(
"#vm-details-h1-name, .vm-details-rename-button"
).
click
(
function
()
{
$
(
"#vm-details-h1-name"
).
hide
();
$
(
"#vm-details-rename"
).
css
(
'display'
,
'inline'
);
});
/* rename ajax */
$
(
'#vm-details-rename-submit'
).
click
(
function
()
{
var
name
=
$
(
'#vm-details-rename-name'
).
val
();
$
.
ajax
({
method
:
'POST'
,
url
:
location
.
href
,
data
:
{
'new_name'
:
name
},
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
success
:
function
(
data
,
textStatus
,
xhr
)
{
$
(
"#vm-details-h1-name"
).
html
(
data
[
'new_name'
]).
show
();
$
(
'#vm-details-rename'
).
hide
();
// addMessage(data['message'], "success");
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
"uhoh"
,
"danger"
);
}
});
return
false
;
});
/* remove tag */
$
(
'.vm-details-remove-tag'
).
click
(
function
()
{
var
to_remove
=
$
.
trim
(
$
(
this
).
parent
(
'div'
).
text
());
var
clicked
=
$
(
this
);
$
.
ajax
({
type
:
'POST'
,
url
:
location
.
href
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
data
:
{
'to_remove'
:
to_remove
},
success
:
function
(
re
)
{
if
(
re
[
'message'
].
toLowerCase
()
==
"success"
)
{
$
(
clicked
).
closest
(
".label"
).
fadeOut
(
500
,
function
()
{
$
(
this
).
remove
();
});
}
},
error
:
function
()
{
addMessage
(
re
[
'message'
],
'danger'
);
}
});
return
false
;
});
});
function
checkNewActivity
()
{
var
latest
=
$
(
'.activity:first'
).
data
(
'activity-id'
);
var
latest_sub
=
$
(
'div[data-activity-id="'
+
latest
+
'"] .sub-timeline .sub-activity:first'
).
data
(
'activity-id'
);
var
instance
=
location
.
href
.
split
(
'/'
);
instance
=
instance
[
instance
.
length
-
2
];
$
.
ajax
({
type
:
'POST'
,
url
:
'/dashboard/vm/'
+
instance
+
'/activity/'
,
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)},
data
:
{
'latest'
:
latest
,
'latest_sub'
:
latest_sub
},
success
:
function
(
data
)
{
if
(
data
[
'new_sub_activities'
].
length
>
0
)
{
d
=
data
[
'new_sub_activities'
];
html
=
""
for
(
var
i
=
0
;
i
<
d
.
length
;
i
++
)
{
html
+=
'<div data-activity-id="'
+
d
[
i
].
id
+
'" class="sub-activity">'
+
d
[
i
].
name
+
' - '
;
if
(
d
[
i
].
finished
!=
null
)
{
html
+=
d
[
i
].
finished
}
else
{
html
+=
'<i class="icon-refresh icon-spin" class="sub-activity-loading-icon"></i>'
;
}
html
+=
'</div>'
;
}
$
(
'div[data-activity-id="'
+
latest_sub
+
'"] .sub-activity .sub-activity-loading-icon'
).
remove
();
$
(
'div[data-activity-id="'
+
latest
+
'"] .sub-timeline'
).
prepend
(
html
);
}
if
(
data
[
'is_parent_finished'
])
{
var
c
=
"icon-plus"
$
(
'div[data-activity-id="'
+
latest
+
'"] .icon-refresh.icon-spin:first'
).
removeClass
(
'icon-refresh'
).
removeClass
(
'icon-spin'
).
addClass
(
c
);
}
if
(
data
[
'latest_sub_finished'
]
!=
null
)
{
s
=
$
(
'div[data-activity-id="'
+
latest_sub
+
'"]'
)
$
(
'.icon-refresh.icon-spin'
,
s
).
remove
();
$
(
s
).
append
(
data
[
'latest_sub_finished'
]);
}
if
(
data
[
'is_parent_finished'
])
return
;
else
setTimeout
(
checkNewActivity
,
1000
);
},
error
:
function
()
{
}
});
}
circle/dashboard/static/dashboard/node-list.js
View file @
07fb9e65
...
...
@@ -126,7 +126,7 @@ $(function() {
// addMessage(data['message'], "success");
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
"
uhoh
"
,
"danger"
);
addMessage
(
"
Error during renaming!
"
,
"danger"
);
}
});
return
false
;
...
...
@@ -191,7 +191,7 @@ $(function() {
onsuccess
(
params
);
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
"
uhoh
"
,
"danger"
);
addMessage
(
"
Error!
"
,
"danger"
);
}
});
return
false
;
...
...
circle/dashboard/static/dashboard/vm-create.js
View file @
07fb9e65
...
...
@@ -202,10 +202,12 @@ function vmCreateLoaded() {
}
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
var
r
=
$
(
'#create-modal'
);
r
.
next
(
'div'
).
remove
();
r
.
remove
();
if
(
xhr
.
status
==
500
)
{
a
lert
(
"uhuhuhuhuhuh
"
);
a
ddMessage
(
"500 Internal Server Error"
,
"danger
"
);
}
else
{
a
lert
(
"unknown erro
r"
);
a
ddMessage
(
xhr
.
status
+
" Unknown Error"
,
"dange
r"
);
}
}
});
...
...
circle/dashboard/static/dashboard/vm-details.js
View file @
07fb9e65
...
...
@@ -15,12 +15,11 @@ $(function() {
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
$
(
"#vm-details-resources-save i"
).
removeClass
(
'icon-refresh icon-spin'
).
addClass
(
"icon-save"
);
addMessage
(
"Eww, something is wrong"
,
'danger'
);
if
(
xhr
.
status
==
500
)
{
// alert("uhuhuhuhuhuh
");
addMessage
(
"500 Internal Server Error"
,
"danger
"
);
}
else
{
// alert("unknown erro
r");
}
addMessage
(
xhr
.
status
+
" Unknown Error"
,
"dange
r"
);
}
}
});
return
false
;
...
...
@@ -47,7 +46,7 @@ $(function() {
// addMessage(data['message'], "success");
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
"
uhoh
"
,
"danger"
);
addMessage
(
"
Error during renaming!
"
,
"danger"
);
}
});
return
false
;
...
...
circle/dashboard/static/dashboard/vm-list.js
View file @
07fb9e65
...
...
@@ -118,7 +118,7 @@ $(function() {
// addMessage(data['message'], "success");
},
error
:
function
(
xhr
,
textStatus
,
error
)
{
addMessage
(
"
uhoh
"
,
"danger"
);
addMessage
(
"
Error during renaming!
"
,
"danger"
);
}
});
return
false
;
...
...
circle/dashboard/templates/dashboard/template-edit.html
View file @
07fb9e65
...
...
@@ -4,11 +4,11 @@
{% block content %}
<div
class=
"row"
>
<div
class=
"col-md-
12
"
>
<div
class=
"col-md-
8
"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<a
class=
"pull-right btn btn-default btn-xs"
href=
"{% url "
dashboard
.
views
.
template-list
"
%}"
>
Back
</a>
<h3
class=
"no-margin"
><i
class=
"icon-desktop"
></i>
Edit template
</h3>
<h3
class=
"no-margin"
><i
class=
"icon-desktop"
></i>
{% trans "Edit template" %}
</h3>
</div>
<div
class=
"panel-body"
>
{% with form=form %}
...
...
@@ -18,8 +18,56 @@
</div>
</div>
</div>
<div
class=
"col-md-4"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
><i
class=
"icon-group"
></i>
{% trans "Manage access" %}
</h3>
</div>
<div
class=
"panel-body"
>
<form
action=
"{% url "
dashboard
.
views
.
template-acl
"
pk=
object.pk
%}"
method=
"post"
>
{% csrf_token %}
<table
class=
"table table-striped table-with-form-fields"
>
<thead><tr><th></th><th>
{% trans "Who" %}
</th><th>
{% trans "What" %}
</th><th></th></tr></thead>
<tbody>
{% for i in acl.users %}
<tr><td><i
class=
"icon-user"
></i></td><td>
{{i.user}}
</td>
<td><select
class=
"form-control"
name=
"perm-u-{{i.user.id}}"
>
{% for id, name in acl.levels %}
<option
{%
if
id =
i.level%}
selected=
"selected"
{%
endif
%}
value=
"{{id}}"
>
{{name}}
</option>
{% endfor %}
</select></td>
<td><a
href=
"#"
class=
"btn btn-link btn-xs"
><i
class=
"icon-remove"
><span
class=
"sr-only"
>
{% trans "remove" %}
</span></i></a></td></tr>
{% endfor %}
{% for i in acl.groups %}
<tr><td><i
class=
"icon-group"
></i></td><td>
{{i.group}}
</td>
<td><select
class=
"form-control"
name=
"perm-g-{{i.group.id}}"
>
{% for id, name in acl.levels %}
<option
{%
if
id =
i.level%}
selected=
"selected"
{%
endif
%}
value=
"{{id}}"
>
{{name}}
</option>
{% endfor %}
</select></td>
<td><a
href=
"#"
class=
"btn btn-link btn-xs"
><i
class=
"icon-remove"
><span
class=
"sr-only"
>
{% trans "remove" %}
</span></i></a></td></tr>
{% endfor %}
<tr><td><i
class=
"icon-plus"
></i></td>
<td><input
type=
"text"
class=
"form-control"
name=
"perm-new-name"
placeholder=
"{% trans "
Name
of
group
or
user
"
%}"
></td>
<td><select
class=
"form-control"
name=
"perm-new"
>
{% for id, name in acl.levels %}
<option
value=
"{{id}}"
>
{{name}}
</option>
{% endfor %}
</select></td><td></td>
</tr>
</tbody>
</table>
<div
class=
"form-actions"
>
<button
type=
"submit"
class=
"btn btn-success"
>
{% trans "Save" %}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<style>
fieldset
{
margin-top
:
40px
;
...
...
circle/dashboard/templates/dashboard/vm-detail.html
View file @
07fb9e65
...
...
@@ -13,7 +13,11 @@
<a
title=
"Start"
href=
"#"
class=
"btn btn-default btn-xs"
><i
class=
"icon-play"
></i></a>
<a
title=
"Wake up"
href=
"#"
class=
"btn btn-default btn-xs"
><i
class=
"icon-sun"
></i></a>
{% endif %}
<a
title=
"Shut down"
href=
"#"
class=
"btn btn-default btn-xs"
><i
class=
"icon-off"
></i></a>
<form
style=
"display: inline;"
method=
"POST"
action=
"{% url "
dashboard
.
views
.
detail
"
pk=
instance.pk
%}"
>
{% csrf_token %}
<input
type=
"hidden"
name=
"shut_down"
value=
"dummy"
/>
<button
title=
"Shut down"
class=
"btn btn-default btn-xs"
type=
"submit"
><i
class=
"icon-off"
></i></button>
</form>
<a
title=
"Migrate"
href=
"#"
class=
"btn btn-default btn-xs"
><i
class=
"icon-truck"
></i></a>
<form
style=
"display: inline;"
method=
"POST"
action=
"{% url "
dashboard
.
views
.
detail
"
pk=
instance.pk
%}"
>
{% csrf_token %}
...
...
circle/dashboard/templates/dashboard/vm-detail/activity.html
View file @
07fb9e65
...
...
@@ -15,7 +15,7 @@
<i
class=
"{% if not a.finished %} icon-refresh icon-spin {% else %}icon-plus{% endif %}"
></i>
</span>
<strong>
{{ a.get_readable_name }}
</strong>
{{ a.started|date:"Y-m-d
.
H:i" }}, {{ a.user }}
{{ a.started|date:"Y-m-d H:i" }}, {{ a.user }}
{% if a.children.count > 0 %}
<div
class=
"sub-timeline"
>
{% for s in a.children.all %}
...
...
@@ -32,12 +32,10 @@
{% endif %}
</div>
{% endfor %}
<div><span
class=
"timeline-icon timeline-warning"
><i
class=
"icon-remove"
></i></span>
<strong>
Removing
</strong>
2013-11-21 15:32
</div>
<div><span
class=
"timeline-icon timeline-warning"
><i
class=
"icon-pause"
></i></span>
<strong>
Suspending
</strong>
2013-09-21 15:32
</div>
<div><span
class=
"timeline-icon"
><i
class=
"icon-ellipsis-vertical"
></i></span>
<strong>
(now)
</strong></div>
<div><span
class=
"timeline-icon"
><i
class=
"icon-truck"
></i></span>
<strong>
Migrated to mega5
</strong>
2013-04-21 15:32, ABC123
</div>
<div><span
class=
"timeline-icon"
><i
class=
"icon-refresh"
></i></span>
<strong>
Forced reboot
</strong>
2013-04-21 15:32, ABC123
</div>
<div><span
class=
"timeline-icon"
><i
class=
"icon-plus"
></i></span>
<strong>
Created
</strong>
2013-04-21 15:32, ABC123
</div>
<div>
<span
class=
"timeline-icon"
><i
class=
"icon-plus"
></i></span>
<strong>
{% trans "Created" %}
</strong>
{{ instance.created|date:"Y-m-d H:i" }}, {{ instance.owner }}
</div>
</div>
{% block extra_js %}
...
...
circle/dashboard/templates/dashboard/vm-detail/home.html
View file @
07fb9e65
...
...
@@ -3,9 +3,9 @@
<div
class=
"col-md-4"
>
<dl>
<dt>
System:
</dt>
<dd><i
class=
"icon-
linux"
></i>
Uhu Binux Optikai Rendszer
</dd>
<dt>
Description:
</dt>
<dd><small>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc est libero, hendrerit at posuere sed, molestie congue quam.
</small></dd>
<dd><i
class=
"icon-
{{ os_type_icon }}"
></i>
{{ instance.system }}
</dd>
<dt
style=
"margin-top: 5px;"
>
Description:
</dt>
<dd><small>
{{ instance.description }}
</small></dd>
</dl>
<div
style=
"font-weight: bold;"
>
{% trans "Tags" %}
</div>
...
...
circle/dashboard/urls.py
View file @
07fb9e65
...
...
@@ -7,7 +7,7 @@ from .views import (
TransferOwnershipView
,
TransferOwnershipConfirmView
,
NodeDelete
,
TemplateList
,
LeaseDetail
,
NodeCreate
,
LeaseCreate
,
TemplateCreate
,
FavouriteView
,
NodeStatus
,
GroupList
,
TemplateDelete
,
LeaseDelete
,
VmGraphView
,
VmGraphView
,
TemplateAclUpdateView
)
urlpatterns
=
patterns
(
...
...
@@ -19,14 +19,18 @@ urlpatterns = patterns(
name
=
"dashboard.views.lease-create"
),
url
(
r'^lease/delete/(?P<pk>\d+)/$'
,
LeaseDelete
.
as_view
(),
name
=
"dashboard.views.lease-delete"
),
url
(
r'^template/create/$'
,
TemplateCreate
.
as_view
(),
name
=
"dashboard.views.template-create"
),
url
(
r'template/(?P<pk>\d+)/acl/$'
,
TemplateAclUpdateView
.
as_view
(),
name
=
'dashboard.views.template-acl'
),
url
(
r'^template/(?P<pk>\d+)/$'
,
TemplateDetail
.
as_view
(),
name
=
'dashboard.views.template-detail'
),
url
(
r"^template/list/$"
,
TemplateList
.
as_view
(),
name
=
"dashboard.views.template-list"
),
url
(
r"^template/delete/(?P<pk>\d+)/$"
,
TemplateDelete
.
as_view
(),
name
=
"dashboard.views.template-delete"
),
url
(
r'^vm/(?P<pk>\d+)/remove_port/(?P<rule>\d+)/$'
,
PortDelete
.
as_view
(),
name
=
'dashboard.views.remove-port'
),
url
(
r'^vm/(?P<pk>\d+)/$'
,
VmDetailView
.
as_view
(),
...
...
@@ -43,6 +47,7 @@ urlpatterns = patterns(
url
(
r'^vm/mass-delete/'
,
VmMassDelete
.
as_view
(),
name
=
'dashboard.view.mass-delete-vm'
),
url
(
r'^vm/(?P<pk>\d+)/activity/$'
,
vm_activity
),
url
(
r'^node/list/$'
,
NodeList
.
as_view
(),
name
=
'dashboard.views.node-list'
),
url
(
r'^node/(?P<pk>\d+)/$'
,
NodeDetailView
.
as_view
(),
name
=
'dashboard.views.node-detail'
),
...
...
@@ -54,8 +59,10 @@ urlpatterns = patterns(
name
=
"dashboard.views.status-node"
),
url
(
r'^node/create/$'
,
NodeCreate
.
as_view
(),
name
=
'dashboard.views.node-create'
),
url
(
r'^favourite/$'
,
FavouriteView
.
as_view
(),
name
=
'dashboard.views.favourite'
),
url
(
r'^group/list/$'
,
GroupList
.
as_view
(),
name
=
'dashboard.views.group-list'
),
url
((
r'^vm/(?P<pk>\d+)/graph/(?P<metric>cpu|memory|network)/'
...
...
circle/dashboard/views.py
View file @
07fb9e65
...
...
@@ -157,6 +157,8 @@ class VmDetailView(CheckedDetailView):
context
[
'forms'
]
=
{
'disk_add_form'
:
DiskAddForm
(
prefix
=
"disk"
),
}
context
[
'os_type_icon'
]
=
instance
.
os_type
.
replace
(
"unknown"
,
"question"
)
return
context
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
...
...
@@ -173,6 +175,7 @@ class VmDetailView(CheckedDetailView):
'new_network_vlan'
:
self
.
__new_network
,
'save_as'
:
self
.
__save_as
,
'disk-name'
:
self
.
__add_disk
,
'shut_down'
:
self
.
__shut_down
,
}
for
k
,
v
in
options
.
iteritems
():
...
...
@@ -359,6 +362,15 @@ class VmDetailView(CheckedDetailView):
return
redirect
(
"
%
s#resources"
%
reverse_lazy
(
"dashboard.views.detail"
,
kwargs
=
{
'pk'
:
self
.
object
.
pk
}))
def
__shut_down
(
self
,
request
):
self
.
object
=
self
.
get_object
()
if
not
self
.
object
.
has_level
(
request
.
user
,
'owner'
):
raise
PermissionDenied
()
self
.
object
.
shutdown_async
(
request
.
user
)
return
redirect
(
"
%
s#activity"
%
reverse_lazy
(
"dashboard.views.detail"
,
kwargs
=
{
'pk'
:
self
.
object
.
pk
}))
class
NodeDetailView
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
DetailView
):
template_name
=
"dashboard/node-detail.html"
...
...
@@ -446,7 +458,7 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin):
if
m
:
typ
,
id
=
m
.
groups
()
entity
=
{
'u'
:
User
,
'g'
:
Group
}[
typ
]
.
objects
.
get
(
id
=
id
)
if
instance
.
owner
==
entity
:
if
getattr
(
instance
,
"owner"
,
None
)
==
entity
:
logger
.
info
(
"Tried to set owner's acl level for
%
s by
%
s."
,
unicode
(
instance
),
unicode
(
request
.
user
))
continue
...
...
@@ -476,6 +488,29 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin):
value
,
unicode
(
request
.
user
))
class
TemplateAclUpdateView
(
AclUpdateView
):
model
=
InstanceTemplate
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
template
=
self
.
get_object
()
if
not
(
template
.
has_level
(
request
.
user
,
"owner"
)
or
getattr
(
template
,
'owner'
,
None
)
==
request
.
user
):
logger
.
warning
(
'Tried to set permissions of
%
s by non-owner
%
s.'
,
unicode
(
template
),
unicode
(
request
.
user
))
raise
PermissionDenied
()
self
.
set_levels
(
request
,
template
)
self
.
add_levels
(
request
,
template
)
post_for_disk
=
request
.
POST
.
copy
()
post_for_disk
[
'perm-new'
]
=
'user'
request
.
POST
=
post_for_disk
for
d
in
template
.
disks
.
all
():
self
.
add_levels
(
request
,
d
)
return
redirect
(
reverse
(
"dashboard.views.template-detail"
,
kwargs
=
self
.
kwargs
))
class
TemplateCreate
(
SuccessMessageMixin
,
CreateView
):
model
=
InstanceTemplate
form_class
=
TemplateForm
...
...
@@ -485,27 +520,28 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
def
get
(
self
,
*
args
,
**
kwargs
):
if
not
self
.
request
.
user
.
has_perm
(
'vm.create_template'
):
raise
PermissionDenied
()
form
=
self
.
form_class
()
form
.
fields
[
'disks'
]
.
queryset
=
Disk
.
get_objects_with_level
(
'user'
,
self
.
request
.
user
)
.
exclude
(
type
=
"qcow2-snap"
)
self
.
parent
=
self
.
request
.
GET
.
get
(
"parent"
)
return
super
(
TemplateCreate
,
self
)
.
get
(
*
args
,
**
kwargs
)
def
get_form_kwargs
(
self
):
kwargs
=
super
(
TemplateCreate
,
self
)
.
get_form_kwargs
()
kwargs
[
'parent'
]
=
getattr
(
self
,
"parent"
,
None
)
kwargs
[
'user'
]
=
self
.
request
.
user
return
kwargs
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
if
not
self
.
request
.
user
.
has_perm
(
'vm.create_template'
):
raise
PermissionDenied
()
form
=
self
.
form_class
(
request
.
POST
)
form
=
self
.
form_class
(
request
.
POST
,
user
=
request
.
user
)
if
not
form
.
is_valid
():
return
self
.
get
(
request
,
form
,
*
args
,
**
kwargs
)
post
=
form
.
cleaned_data
for
disk
in
post
[
'disks'
]:
if
not
disk
.
has_level
(
request
.
user
,
'user'
):
raise
PermissionDenied
()
return
super
(
TemplateCreate
,
self
)
.
post
(
self
,
request
,
args
,
kwargs
)
def
get_success_url
(
self
):
...
...
@@ -545,6 +581,11 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
else
:
return
super
(
TemplateDetail
,
self
)
.
get
(
request
,
*
args
,
**
kwargs
)
def
get_context_data
(
self
,
**
kwargs
):
context
=
super
(
TemplateDetail
,
self
)
.
get_context_data
(
**
kwargs
)
context
[
'acl'
]
=
get_acl_data
(
self
.
get_object
())
return
context
def
get_success_url
(
self
):
return
reverse_lazy
(
"dashboard.views.template-detail"
,
kwargs
=
self
.
kwargs
)
...
...
@@ -558,6 +599,11 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
raise
PermissionDenied
()
return
super
(
TemplateDetail
,
self
)
.
post
(
self
,
request
,
args
,
kwargs
)
def
get_form_kwargs
(
self
):
kwargs
=
super
(
TemplateDetail
,
self
)
.
get_form_kwargs
()
kwargs
[
'user'
]
=
self
.
request
.
user
return
kwargs
class
TemplateList
(
LoginRequiredMixin
,
SingleTableView
):
template_name
=
"dashboard/template-list.html"
...
...
@@ -684,7 +730,6 @@ class VmCreate(LoginRequiredMixin, TemplateView):
})
return
self
.
render_to_response
(
context
)
# TODO handle not ajax posts
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
form
=
self
.
form_class
(
request
.
POST
)
if
not
form
.
is_valid
():
...
...
@@ -692,6 +737,22 @@ class VmCreate(LoginRequiredMixin, TemplateView):
post
=
form
.
cleaned_data
user
=
request
.
user
try
:
limit
=
user
.
profile
.
instance_limit
except
Exception
as
e
:
logger
.
debug
(
'No profile or instance limit:
%
s'
,
e
)
else
:
current
=
Instance
.
active
.
filter
(
owner
=
user
)
.
count
()
logger
.
debug
(
'current use:
%
d, limit:
%
d'
,
current
,
limit
)
if
limit
<
current
:
messages
.
error
(
request
,
_
(
'Instance limit (
%
d) exceeded.'
)
%
limit
)
if
request
.
is_ajax
():
return
HttpResponse
(
json
.
dumps
({
'redirect'
:
'/'
}),
content_type
=
"application/json"
)
else
:
return
redirect
(
'/'
)
template
=
post
[
'template'
]
if
not
template
.
has_level
(
request
.
user
,
'user'
):
raise
PermissionDenied
()
...
...
@@ -1241,19 +1302,21 @@ class VmGraphView(LoginRequiredMixin, View):
if
not
instance
.
has_level
(
request
.
user
,
'user'
):
raise
PermissionDenied
()
prefix
=
'vm.
%
s'
%
instance
.
vm_name
if
metric
==
'cpu'
:
target
=
(
'cactiStyle(alias(derivative(
%
s.cpu.usage),'
'"cpu usage (
%%
)"))'
)
%
prefix
elif
metric
==
'memory'
:
target
=
(
'cactiStyle(alias(
%
s.memory.usage,'
'"memory usage (
%%
)"))'
)
%
prefix
elif
metric
==
'network'
:
target
=
(
'cactiStyle(aliasByMetric('
'derivative(
%
s.network.bytes_*)))'
)
%
prefix
else
:
targets
=
{
'cpu'
:
(
'cactiStyle(alias(derivative(
%
s.cpu.usage),'
'"cpu usage (
%%
)"))'
),
'memory'
:
(
'cactiStyle(alias(
%
s.memory.usage,'
'"memory usage (
%%
)"))'
),
'network'
:
(
'cactiStyle(aliasByMetric('
'derivative(
%
s.network.bytes_*)))'
),
}
if
metric
not
in
targets
.
keys
():
raise
SuspiciousOperation
()
prefix
=
'vm.
%
s'
%
instance
.
vm_name
target
=
targets
[
metric
]
%
prefix
title
=
'
%
s (
%
s) -
%
s'
%
(
instance
.
name
,
instance
.
vm_name
,
metric
)
params
=
urlencode
({
'target'
:
target
,
...
...
circle/vm/migrations/0014_auto__add_field_instancetemplate_owner.py
0 → 100644
View file @
07fb9e65
This diff is collapsed.
Click to expand it.
circle/vm/models/instance.py
View file @
07fb9e65
...
...
@@ -119,6 +119,7 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
disks
=
ManyToManyField
(
Disk
,
verbose_name
=
_
(
'disks'
),
related_name
=
'template_set'
,
help_text
=
_
(
'Disks which are to be mounted.'
))
owner
=
ForeignKey
(
User
)
class
Meta
:
app_label
=
'vm'
...
...
@@ -148,6 +149,12 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
else
:
return
'linux'
def
save
(
self
,
*
args
,
**
kwargs
):
is_new
=
getattr
(
self
,
"pk"
,
None
)
is
None
super
(
InstanceTemplate
,
self
)
.
save
(
*
args
,
**
kwargs
)
if
is_new
:
self
.
set_level
(
self
.
owner
,
'owner'
)
class
Instance
(
AclBase
,
VirtualMachineDescModel
,
TimeStampedModel
):
...
...
@@ -419,6 +426,24 @@ class Instance(AclBase, VirtualMachineDescModel, TimeStampedModel):
else
:
return
timedelta
()
# zero
@property
def
os_type
(
self
):
"""Get the type of the instance's operating system.
"""
if
self
.
template
is
None
: