Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Fukász Rómeó Ervin
/
cloud
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
A prog2-höz tartozó friss repo anyagok itt elérhetőek:
https://git.iit.bme.hu/
Commit
86c16fe8
authored
Sep 15, 2014
by
Kálmán Viktor
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into feature-new-tour
parents
96fe17ce
ea3039d6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
366 additions
and
206 deletions
+366
-206
circle/common/models.py
+8
-0
circle/dashboard/autocomplete_light_registry.py
+69
-14
circle/dashboard/forms.py
+24
-4
circle/dashboard/models.py
+6
-2
circle/dashboard/static/dashboard/dashboard.css
+14
-1
circle/dashboard/templates/dashboard/_modal.html
+6
-0
circle/dashboard/templates/dashboard/group-detail.html
+2
-3
circle/dashboard/templates/dashboard/template-edit.html
+1
-0
circle/dashboard/templates/dashboard/vm-detail/access.html
+3
-1
circle/dashboard/templates/dashboard/vm-detail/tx-owner.html
+13
-22
circle/dashboard/tests/test_views.py
+10
-10
circle/dashboard/views.py
+36
-18
circle/network/forms.py
+32
-29
circle/network/templates/network/host-edit.html
+50
-46
circle/network/views.py
+36
-32
circle/storage/models.py
+2
-1
circle/vm/migrations/0027_auto__add_field_instance_has_agent__add_field_instancetemplate_has_age.py
+0
-0
circle/vm/models/activity.py
+19
-10
circle/vm/models/instance.py
+6
-1
circle/vm/operations.py
+16
-6
circle/vm/tasks/local_agent_tasks.py
+11
-6
circle/vm/tests/test_models.py
+2
-0
No files found.
circle/common/models.py
View file @
86c16fe8
...
...
@@ -214,6 +214,14 @@ class ActivityModel(TimeStampedModel):
self
.
result_data
=
None
if
value
is
None
else
value
.
to_dict
()
@classmethod
def
construct_activity_code
(
cls
,
code_suffix
,
sub_suffix
=
None
):
code
=
join_activity_code
(
cls
.
ACTIVITY_CODE_BASE
,
code_suffix
)
if
sub_suffix
:
return
join_activity_code
(
code
,
sub_suffix
)
else
:
return
code
@celery.task
()
def
compute_cached
(
method
,
instance
,
memcached_seconds
,
...
...
circle/dashboard/autocomplete_light_registry.py
View file @
86c16fe8
import
autocomplete_light
from
django.contrib.auth.models
import
User
from
django.utils.html
import
escape
from
django.utils.translation
import
ugettext
as
_
from
.views
import
AclUpdateView
from
.models
import
Profile
class
AclUserAutocomplete
(
autocomplete_light
.
AutocompleteGenericBase
):
def
highlight
(
field
,
q
,
none_wo_match
=
True
):
"""
>>> highlight('<b>Akkount Krokodil', 'kro', False)
u'<b>Akkount <span class="autocomplete-hl">Kro</span>kodil'
"""
if
not
field
:
return
None
try
:
match
=
field
.
lower
()
.
index
(
q
.
lower
())
except
ValueError
:
match
=
None
if
q
and
match
is
not
None
:
match_end
=
match
+
len
(
q
)
return
(
escape
(
field
[:
match
])
+
'<span class="autocomplete-hl">'
+
escape
(
field
[
match
:
match_end
])
+
'</span>'
+
escape
(
field
[
match_end
:]))
elif
none_wo_match
:
return
None
else
:
return
escape
(
field
)
class
AclUserGroupAutocomplete
(
autocomplete_light
.
AutocompleteGenericBase
):
search_fields
=
(
(
'
^first_name'
,
'last_name'
,
'username'
,
'^
email'
,
'profile__org_id'
),
(
'
^
name'
,
'groupprofile__org_id'
),
(
'
first_name'
,
'last_name'
,
'username'
,
'
email'
,
'profile__org_id'
),
(
'name'
,
'groupprofile__org_id'
),
)
autocomplete_js_attributes
=
{
'placeholder'
:
_
(
"Name of group or user"
)}
choice_html_format
=
u'<span data-value="
%
s"><span>
%
s</span>
%
s</span>'
choice_html_format
=
(
u'<span data-value="
%
s"><span style="display:none"'
u'>
%
s</span>
%
s</span>'
)
def
choice_html
(
self
,
choice
):
try
:
name
=
choice
.
get_full_name
()
except
AttributeError
:
name
=
_
(
'group'
)
if
name
:
name
=
u'(
%
s)'
%
name
def
choice_displayed_text
(
self
,
choice
):
q
=
unicode
(
self
.
request
.
GET
.
get
(
'q'
,
''
))
name
=
highlight
(
unicode
(
choice
),
q
,
False
)
if
isinstance
(
choice
,
User
):
extra_fields
=
[
highlight
(
choice
.
get_full_name
(),
q
,
False
),
highlight
(
choice
.
email
,
q
)]
try
:
extra_fields
.
append
(
highlight
(
choice
.
profile
.
org_id
,
q
))
except
Profile
.
DoesNotExist
:
pass
return
'
%
s (
%
s)'
%
(
name
,
', '
.
join
(
f
for
f
in
extra_fields
if
f
))
else
:
return
_
(
'
%
s (group)'
)
%
name
def
choice_html
(
self
,
choice
):
return
self
.
choice_html_format
%
(
self
.
choice_value
(
choice
),
self
.
choice_label
(
choice
),
name
)
self
.
choice_value
(
choice
),
self
.
choice_label
(
choice
),
self
.
choice_displayed_text
(
choice
))
def
choices_for_request
(
self
):
user
=
self
.
request
.
user
self
.
choices
=
(
AclUpdateView
.
get_allowed_users
(
user
),
AclUpdateView
.
get_allowed_groups
(
user
))
return
super
(
AclUserAutocomplete
,
self
)
.
choices_for_request
()
return
super
(
AclUserGroupAutocomplete
,
self
)
.
choices_for_request
()
def
autocomplete_html
(
self
):
html
=
[]
for
choice
in
self
.
choices_for_request
():
html
.
append
(
self
.
choice_html
(
choice
))
if
not
html
:
html
=
self
.
empty_html_format
%
_
(
'no matches found'
)
.
capitalize
()
return
self
.
autocomplete_html_format
%
''
.
join
(
html
)
class
AclUserAutocomplete
(
AclUserGroupAutocomplete
):
def
choices_for_request
(
self
):
user
=
self
.
request
.
user
self
.
choices
=
(
AclUpdateView
.
get_allowed_users
(
user
),
)
return
super
(
AclUserGroupAutocomplete
,
self
)
.
choices_for_request
()
autocomplete_light
.
register
(
AclUserGroupAutocomplete
)
autocomplete_light
.
register
(
AclUserAutocomplete
)
circle/dashboard/forms.py
View file @
86c16fe8
...
...
@@ -143,7 +143,7 @@ class VmCustomizeForm(forms.Form):
self
.
template
=
kwargs
.
pop
(
"template"
,
None
)
super
(
VmCustomizeForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
if
self
.
user
.
has_perm
(
"vm
_set_resou
ces"
):
if
self
.
user
.
has_perm
(
"vm
.set_resour
ces"
):
self
.
allowed_fields
=
tuple
(
self
.
fields
.
keys
())
# set displayed disk and network list
self
.
fields
[
'disks'
]
.
queryset
=
self
.
template
.
disks
.
all
()
...
...
@@ -481,7 +481,7 @@ class TemplateForm(forms.ModelForm):
else
:
self
.
allowed_fields
=
(
'name'
,
'access_method'
,
'description'
,
'system'
,
'tags'
,
'arch'
,
'lease'
)
'arch'
,
'lease'
,
'has_agent'
)
if
(
self
.
user
.
has_perm
(
'vm.change_template_resources'
)
or
not
self
.
instance
.
pk
):
self
.
allowed_fields
+=
tuple
(
set
(
self
.
fields
.
keys
())
-
...
...
@@ -1055,9 +1055,29 @@ class UserCreationForm(OrgUserCreationForm):
return
user
class
AclUserAddForm
(
forms
.
Form
):
class
AclUser
OrGroup
AddForm
(
forms
.
Form
):
name
=
forms
.
CharField
(
widget
=
autocomplete_light
.
TextWidget
(
'AclUserAutocomplete'
,
attrs
=
{
'class'
:
'form-control'
}))
'AclUserGroupAutocomplete'
,
autocomplete_js_attributes
=
{
'placeholder'
:
_
(
"Name of group or user"
)},
attrs
=
{
'class'
:
'form-control'
}))
class
TransferOwnershipForm
(
forms
.
Form
):
name
=
forms
.
CharField
(
widget
=
autocomplete_light
.
TextWidget
(
'AclUserAutocomplete'
,
autocomplete_js_attributes
=
{
"placeholder"
:
_
(
"Name of user"
)},
attrs
=
{
'class'
:
'form-control'
}),
label
=
_
(
"E-mail address or identifier of user"
))
class
AddGroupMemberForm
(
forms
.
Form
):
new_member
=
forms
.
CharField
(
widget
=
autocomplete_light
.
TextWidget
(
'AclUserAutocomplete'
,
autocomplete_js_attributes
=
{
"placeholder"
:
_
(
"Name of user"
)},
attrs
=
{
'class'
:
'form-control'
}),
label
=
_
(
"E-mail address or identifier of user"
))
class
UserKeyForm
(
forms
.
ModelForm
):
...
...
circle/dashboard/models.py
View file @
86c16fe8
...
...
@@ -261,7 +261,7 @@ def get_or_create_profile(self):
Group
.
profile
=
property
(
get_or_create_profile
)
def
create_profile
(
sender
,
user
,
request
,
**
kwargs
):
def
create_profile
(
user
):
if
not
user
.
pk
:
return
False
profile
,
created
=
Profile
.
objects
.
get_or_create
(
user
=
user
)
...
...
@@ -272,7 +272,11 @@ def create_profile(sender, user, request, **kwargs):
logger
.
exception
(
"Can't create user
%
s"
,
unicode
(
user
))
return
created
user_logged_in
.
connect
(
create_profile
)
def
create_profile_hook
(
sender
,
user
,
request
,
**
kwargs
):
return
create_profile
(
user
)
user_logged_in
.
connect
(
create_profile_hook
)
if
hasattr
(
settings
,
'SAML_ORG_ID_ATTRIBUTE'
):
logger
.
debug
(
"Register save_org_id to djangosaml2 pre_user_save"
)
...
...
circle/dashboard/static/dashboard/dashboard.css
View file @
86c16fe8
...
...
@@ -591,11 +591,15 @@ footer a, footer a:hover, footer a:visited {
width
:
100px
;
}
#group-detail-user-table
tr
:last-child
td
:nth-child
(
2
)
{
text-align
:
left
;
}
#group-detail-perm-header
{
margin-top
:
25px
;
}
textarea
[
name
=
"
list-new-namelist
"
]
{
textarea
[
name
=
"
new_members
"
]
{
max-width
:
500px
;
min-height
:
80px
;
margin-bottom
:
10px
;
...
...
@@ -968,3 +972,12 @@ textarea[name="list-new-namelist"] {
#vm-activity-state
{
margin-bottom
:
15px
;
}
.autocomplete-hl
{
color
:
#b20000
;
font-weight
:
bold
;
}
.hilight
.autocomplete-hl
{
color
:
orange
;
}
circle/dashboard/templates/dashboard/_modal.html
View file @
86c16fe8
...
...
@@ -2,6 +2,12 @@
<div
class=
"modal fade"
id=
"confirmation-modal"
tabindex=
"-1"
role=
"dialog"
>
<div
class=
"modal-dialog"
>
<div
class=
"modal-content"
>
{% if box_title and ajax_title %}
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
data-dismiss=
"modal"
aria-hidden=
"true"
>
×
</button>
<h4
class=
"modal-title"
>
{{ box_title }}
</h4>
</div>
{% endif %}
<div
class=
"modal-body"
>
{% if template %}
{% include template %}
...
...
circle/dashboard/templates/dashboard/group-detail.html
View file @
86c16fe8
...
...
@@ -89,13 +89,12 @@
<tr>
<td><i
class=
"fa fa-plus"
></i></td>
<td
colspan=
"2"
>
<input
type=
"text"
class=
"form-control"
name=
"list-new-name"
placeholder=
"{% trans "
Name
of
user
"
%}"
>
{{addmemberform.new_member}}
</td>
</tr>
</tbody>
</table>
<textarea
name=
"
list-new-namelist
"
class=
"form-control"
<textarea
name=
"
new_members
"
class=
"form-control"
placeholder=
"{% trans "
Add
multiple
users
at
once
(
one
identifier
per
line
)."
%}"
></textarea>
<div
class=
"form-actions"
>
<button
type=
"submit"
class=
"btn btn-success"
>
{% trans "Save" %}
</button>
...
...
circle/dashboard/templates/dashboard/template-edit.html
View file @
86c16fe8
...
...
@@ -51,6 +51,7 @@
{{ form.req_traits|as_crispy_field }}
{{ form.description|as_crispy_field }}
{{ form.system|as_crispy_field }}
{{ form.has_agent|as_crispy_field }}
</fieldset>
<fieldset>
<legend>
{% trans "External resources" %}
</legend>
...
...
circle/dashboard/templates/dashboard/vm-detail/access.html
View file @
86c16fe8
...
...
@@ -9,8 +9,10 @@
{% endblocktrans %}
{% endif %}
{% if user == instance.owner or user.is_superuser %}
<span
class=
"operation-wrapper"
>
<a
href=
"{% url "
dashboard
.
views
.
vm-transfer-ownership
"
instance
.
pk
%}"
class=
"btn btn-link"
>
{% trans "Transfer ownership..." %}
</a>
class=
"btn btn-link operation"
>
{% trans "Transfer ownership..." %}
</a>
</span>
{% endif %}
</p>
<h3>
{% trans "Permissions"|capfirst %}
</h3>
...
...
circle/dashboard/templates/dashboard/vm-detail/tx-owner.html
View file @
86c16fe8
{% extends "dashboard/base.html" %}
{% load i18n %}
{% block content %}
<div
class=
"body-content"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
>
{% trans "Transfer ownership" %}
</h3>
</div>
<div
class=
"panel-body"
>
<div
class=
"pull-right"
>
<form
action=
""
method=
"POST"
>
{% csrf_token %}
<label>
{% trans "E-mail address or identifier of user" %}:
<input
name=
"name"
>
</label>
<input
type=
"submit"
>
</form>
<div
class=
"pull-right"
>
<form
action=
"{% url "
dashboard
.
views
.
vm-transfer-ownership
"
pk=
instance.pk
%}"
method=
"POST"
style=
"max-width: 400px;"
>
{% csrf_token %}
<label>
{{ form.name.label }}
</label>
<div
class=
"input-group"
>
{{form.name}}
<div
class=
"input-group-btn"
>
<input
type=
"submit"
value=
"{% trans "
Save
"
%}"
class=
"btn btn-primary"
>
</div>
</div>
</div
>
{% endblock %}
</div>
</form
>
</div>
circle/dashboard/tests/test_views.py
View file @
86c16fe8
...
...
@@ -1134,7 +1134,7 @@ class GroupDetailTest(LoginMixin, TestCase):
c
=
Client
()
user_in_group
=
self
.
g1
.
user_set
.
count
()
response
=
c
.
post
(
'/dashboard/group/'
+
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
list-new-name
'
:
'user3'
})
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
new_member
'
:
'user3'
})
self
.
assertEqual
(
user_in_group
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
302
)
...
...
@@ -1144,7 +1144,7 @@ class GroupDetailTest(LoginMixin, TestCase):
self
.
login
(
c
,
'user3'
)
user_in_group
=
self
.
g1
.
user_set
.
count
()
response
=
c
.
post
(
'/dashboard/group/'
+
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
list-new-name
'
:
'user3'
})
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
new_member
'
:
'user3'
})
self
.
assertEqual
(
user_in_group
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
403
)
...
...
@@ -1153,7 +1153,7 @@ class GroupDetailTest(LoginMixin, TestCase):
self
.
login
(
c
,
'superuser'
)
user_in_group
=
self
.
g1
.
user_set
.
count
()
response
=
c
.
post
(
'/dashboard/group/'
+
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
list-new-name
'
:
'user3'
})
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
new_member
'
:
'user3'
})
self
.
assertEqual
(
user_in_group
+
1
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
302
)
...
...
@@ -1162,7 +1162,7 @@ class GroupDetailTest(LoginMixin, TestCase):
self
.
login
(
c
,
'user0'
)
user_in_group
=
self
.
g1
.
user_set
.
count
()
response
=
c
.
post
(
'/dashboard/group/'
+
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
list-new-name
'
:
'user3'
})
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
new_member
'
:
'user3'
})
self
.
assertEqual
(
user_in_group
+
1
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
302
)
...
...
@@ -1172,7 +1172,7 @@ class GroupDetailTest(LoginMixin, TestCase):
user_in_group
=
self
.
g1
.
user_set
.
count
()
response
=
c
.
post
(
'/dashboard/group/'
+
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
list-new-namelist
'
:
'user1
\r\n
user2'
})
{
'
new_members
'
:
'user1
\r\n
user2'
})
self
.
assertEqual
(
user_in_group
+
2
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
302
)
...
...
@@ -1182,7 +1182,7 @@ class GroupDetailTest(LoginMixin, TestCase):
user_in_group
=
self
.
g1
.
user_set
.
count
()
response
=
c
.
post
(
'/dashboard/group/'
+
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
list-new-namelist
'
:
'user1
\r\n
noname
\r\n
user2'
})
{
'
new_members
'
:
'user1
\r\n
noname
\r\n
user2'
})
self
.
assertEqual
(
user_in_group
+
2
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
302
)
...
...
@@ -1192,7 +1192,7 @@ class GroupDetailTest(LoginMixin, TestCase):
user_in_group
=
self
.
g1
.
user_set
.
count
()
response
=
c
.
post
(
'/dashboard/group/'
+
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
list-new-namelist
'
:
'user1
\r\n
user2'
})
{
'
new_members
'
:
'user1
\r\n
user2'
})
self
.
assertEqual
(
user_in_group
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
403
)
...
...
@@ -1201,7 +1201,7 @@ class GroupDetailTest(LoginMixin, TestCase):
user_in_group
=
self
.
g1
.
user_set
.
count
()
response
=
c
.
post
(
'/dashboard/group/'
+
str
(
self
.
g1
.
pk
)
+
'/'
,
{
'
list-new-namelist
'
:
'user1
\r\n
user2'
})
{
'
new_members
'
:
'user1
\r\n
user2'
})
self
.
assertEqual
(
user_in_group
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
302
)
...
...
@@ -1471,8 +1471,8 @@ class TransferOwnershipViewTest(LoginMixin, TestCase):
c2
=
self
.
u2
.
notification_set
.
count
()
c
=
Client
()
self
.
login
(
c
,
'user2'
)
response
=
c
.
post
(
'/dashboard/vm/1/tx/'
)
assert
response
.
status_code
==
40
0
response
=
c
.
post
(
'/dashboard/vm/1/tx/'
,
{
'name'
:
'userx'
}
)
assert
response
.
status_code
==
40
3
self
.
assertEqual
(
self
.
u2
.
notification_set
.
count
(),
c2
)
def
test_owned_offer
(
self
):
...
...
circle/dashboard/views.py
View file @
86c16fe8
...
...
@@ -70,9 +70,10 @@ from .forms import (
UserCreationForm
,
GroupProfileUpdateForm
,
UnsubscribeForm
,
VmSaveForm
,
UserKeyForm
,
VmRenewForm
,
VmStateChangeForm
,
CirclePasswordChangeForm
,
VmCreateDiskForm
,
VmDownloadDiskForm
,
TraitsForm
,
RawDataForm
,
GroupPermissionForm
,
AclUserAddForm
,
TraitsForm
,
RawDataForm
,
GroupPermissionForm
,
AclUser
OrGroup
AddForm
,
VmResourcesForm
,
VmAddInterfaceForm
,
VmListSearchForm
,
TemplateListSearchForm
,
ConnectCommandForm
TemplateListSearchForm
,
ConnectCommandForm
,
TransferOwnershipForm
,
AddGroupMemberForm
)
from
.tables
import
(
...
...
@@ -90,7 +91,7 @@ from vm.models import (
from
storage.models
import
Disk
from
firewall.models
import
Vlan
,
Host
,
Rule
from
.models
import
(
Favourite
,
Profile
,
GroupProfile
,
FutureMember
,
ConnectCommand
)
ConnectCommand
,
create_profile
)
from
.store_api
import
Store
,
NoStoreException
,
NotOkException
...
...
@@ -393,7 +394,7 @@ class VmDetailView(CheckedDetailView):
)
.
all
()
context
[
'acl'
]
=
AclUpdateView
.
get_acl_data
(
instance
,
self
.
request
.
user
,
'dashboard.views.vm-acl'
)
context
[
'aclform'
]
=
AclUserAddForm
()
context
[
'aclform'
]
=
AclUser
OrGroup
AddForm
()
context
[
'os_type_icon'
]
=
instance
.
os_type
.
replace
(
"unknown"
,
"question"
)
# ipv6 infos
...
...
@@ -1285,7 +1286,8 @@ class GroupDetailView(CheckedDetailView):
context
[
'acl'
]
=
AclUpdateView
.
get_acl_data
(
self
.
object
.
profile
,
self
.
request
.
user
,
'dashboard.views.group-acl'
)
context
[
'aclform'
]
=
AclUserAddForm
()
context
[
'aclform'
]
=
AclUserOrGroupAddForm
()
context
[
'addmemberform'
]
=
AddGroupMemberForm
()
context
[
'group_profile_form'
]
=
GroupProfileUpdate
.
get_form_object
(
self
.
request
,
self
.
object
.
profile
)
...
...
@@ -1302,17 +1304,15 @@ class GroupDetailView(CheckedDetailView):
if
request
.
POST
.
get
(
'new_name'
):
return
self
.
__set_name
(
request
)
if
request
.
POST
.
get
(
'
list-new-name
'
):
if
request
.
POST
.
get
(
'
new_member
'
):
return
self
.
__add_user
(
request
)
if
request
.
POST
.
get
(
'
list-new-namelist
'
):
if
request
.
POST
.
get
(
'
new_members
'
):
return
self
.
__add_list
(
request
)
if
(
request
.
POST
.
get
(
'list-new-name'
)
is
not
None
)
and
\
(
request
.
POST
.
get
(
'list-new-namelist'
)
is
not
None
):
return
redirect
(
reverse_lazy
(
"dashboard.views.group-detail"
,
kwargs
=
{
'pk'
:
self
.
get_object
()
.
pk
}))
return
redirect
(
reverse_lazy
(
"dashboard.views.group-detail"
,
kwargs
=
{
'pk'
:
self
.
get_object
()
.
pk
}))
def
__add_user
(
self
,
request
):
name
=
request
.
POST
[
'
list-new-name
'
]
name
=
request
.
POST
[
'
new_member
'
]
self
.
__add_username
(
request
,
name
)
return
redirect
(
reverse_lazy
(
"dashboard.views.group-detail"
,
kwargs
=
{
'pk'
:
self
.
object
.
pk
}))
...
...
@@ -1331,9 +1331,7 @@ class GroupDetailView(CheckedDetailView):
messages
.
warning
(
request
,
_
(
'User "
%
s" not found.'
)
%
name
)
def
__add_list
(
self
,
request
):
if
not
self
.
get_has_level
()(
request
.
user
,
'operator'
):
raise
PermissionDenied
()
userlist
=
request
.
POST
.
get
(
'list-new-namelist'
)
.
split
(
'
\r\n
'
)
userlist
=
request
.
POST
.
get
(
'new_members'
)
.
split
(
'
\r\n
'
)
for
line
in
userlist
:
self
.
__add_username
(
request
,
line
)
return
redirect
(
reverse_lazy
(
"dashboard.views.group-detail"
,
...
...
@@ -1720,7 +1718,7 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
obj
,
self
.
request
.
user
,
'dashboard.views.template-acl'
)
context
[
'disks'
]
=
obj
.
disks
.
all
()
context
[
'is_owner'
]
=
obj
.
has_level
(
self
.
request
.
user
,
'owner'
)
context
[
'aclform'
]
=
AclUserAddForm
()
context
[
'aclform'
]
=
AclUser
OrGroup
AddForm
()
return
context
def
get_success_url
(
self
):
...
...
@@ -2771,11 +2769,30 @@ class FavouriteView(TemplateView):
return
HttpResponse
(
"Added."
)
class
TransferOwnershipView
(
LoginRequiredMixin
,
DetailView
):
class
TransferOwnershipView
(
CheckedDetailView
,
DetailView
):
model
=
Instance
template_name
=
'dashboard/vm-detail/tx-owner.html'
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
return
[
'dashboard/_modal.html'
]
else
:
return
[
'dashboard/nojs-wrapper.html'
]
def
get_context_data
(
self
,
*
args
,
**
kwargs
):
context
=
super
(
TransferOwnershipView
,
self
)
.
get_context_data
(
*
args
,
**
kwargs
)
context
[
'form'
]
=
TransferOwnershipForm
()
context
.
update
({
'box_title'
:
_
(
"Transfer ownership"
),
'ajax_title'
:
True
,
'template'
:
"dashboard/vm-detail/tx-owner.html"
,
})
return
context
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
form
=
TransferOwnershipForm
(
request
.
POST
)
if
not
form
.
is_valid
():
return
self
.
get
(
request
)
try
:
new_owner
=
search_user
(
request
.
POST
[
'name'
])
except
User
.
DoesNotExist
:
...
...
@@ -3274,6 +3291,7 @@ class UserCreationView(LoginRequiredMixin, PermissionRequiredMixin,
self
.
get_group
(
group_pk
)
ret
=
super
(
UserCreationView
,
self
)
.
post
(
*
args
,
**
kwargs
)
if
self
.
object
:
create_profile
(
self
.
object
)
self
.
object
.
groups
.
add
(
self
.
group
)
return
redirect
(
reverse
(
'dashboard.views.group-detail'
,
args
=
[
group_pk
]))
...
...
circle/network/forms.py
View file @
86c16fe8
...
...
@@ -17,6 +17,7 @@
from
django.forms
import
ModelForm
from
django.core.urlresolvers
import
reverse_lazy
from
django.utils.translation
import
ugettext_lazy
as
_
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.layout
import
Layout
,
Fieldset
,
Div
,
Submit
,
BaseInput
...
...
@@ -56,8 +57,9 @@ class BlacklistItemForm(ModelForm):
)
),
FormActions
(
Submit
(
'submit'
,
'Save changes'
),
LinkButton
(
'back'
,
'Back'
,
reverse_lazy
(
'network.blacklist_list'
))
Submit
(
'submit'
,
_
(
'Save changes'
)),
LinkButton
(
'back'
,
_
(
"Back"
),
reverse_lazy
(
'network.blacklist_list'
))
)
)
...
...
@@ -77,8 +79,8 @@ class DomainForm(ModelForm):
),
),
FormActions
(
Submit
(
'submit'
,
'Save'
),
LinkButton
(
'back'
,
'Back'
,
reverse_lazy
(
'network.domain_list'
))
Submit
(
'submit'
,
_
(
'Save'
)
),
LinkButton
(
'back'
,
_
(
"Back"
)
,
reverse_lazy
(
'network.domain_list'
))
)
)
...
...
@@ -91,15 +93,15 @@ class GroupForm(ModelForm):
helper
.
layout
=
Layout
(
Div
(
Fieldset
(
'
Identity
'
,
''
,
'name'
,
'description'
,
'owner'
,
),
),
FormActions
(
Submit
(
'submit'
,
'Save'
),
LinkButton
(
'back'
,
'Back'
,
reverse_lazy
(
'network.group_list'
))
Submit
(
'submit'
,
_
(
'Save'
)
),
LinkButton
(
'back'
,
_
(
"Back"
)
,
reverse_lazy
(
'network.group_list'
))
)
)
...
...
@@ -112,13 +114,13 @@ class HostForm(ModelForm):
helper
.
layout
=
Layout
(
Div
(
Fieldset
(
'
Identity
'
,
''
,
'hostname'
,
'reverse'
,
'mac'
,
),
Fieldset
(
'Network'
,
_
(
'Network'
)
,
'vlan'
,
'ipv4'
,
'ipv6'
,
...
...
@@ -126,7 +128,7 @@ class HostForm(ModelForm):
'external_ipv4'
,
),
Fieldset
(
'Information'
,
_
(
'Information'
)
,
'description'
,
'location'
,
'comment'
,
...
...
@@ -134,8 +136,8 @@ class HostForm(ModelForm):
),
),
FormActions
(
Submit
(
'submit'
,
'Save'
),
LinkButton
(
'back'
,
'Back'
,
reverse_lazy
(
'network.host_list'
)))
Submit
(
'submit'
,
_
(
'Save'
)
),
LinkButton
(
'back'
,
_
(
'Back'
)
,
reverse_lazy
(
'network.host_list'
)))
)
class
Meta
:
...
...
@@ -159,8 +161,8 @@ class RecordForm(ModelForm):
)
),
FormActions
(
Submit
(
'submit'
,
'Save'
),
LinkButton
(
'back'
,
'Back'
,
reverse_lazy
(
'network.record_list'
))
Submit
(
'submit'
,
_
(
"Save"
)
),
LinkButton
(
'back'
,
_
(
"Back"
)
,
reverse_lazy
(
'network.record_list'
))
)
)
...
...
@@ -173,7 +175,7 @@ class RuleForm(ModelForm):
helper
.
layout
=
Layout
(
Div
(
Fieldset
(
'
Identity
'
,
''
,
'direction'
,
'description'
,
'foreign_network'
,
...
...
@@ -189,7 +191,7 @@ class RuleForm(ModelForm):
'nat_external_ipv4'
,
),
Fieldset
(
'External'
,
_
(
'External'
)
,
'vlan'
,
'vlangroup'
,
'host'
,
...
...
@@ -198,8 +200,8 @@ class RuleForm(ModelForm):
)
),
FormActions
(
Submit
(
'submit'
,
'Save'
),
LinkButton
(
'back'
,
'Back'
,
reverse_lazy
(
'network.rule_list'
))
Submit
(
'submit'
,
_
(
"Save"
)
),
LinkButton
(
'back'
,
_
(
"Back"
)
,
reverse_lazy
(
'network.rule_list'
))
)
)
...
...
@@ -219,8 +221,8 @@ class SwitchPortForm(ModelForm):
)
),
FormActions
(
Submit
(
'submit'
,
'Save'
),
LinkButton
(
'back'
,
'Back'
,
Submit
(
'submit'
,
_
(
"Save"
)
),
LinkButton
(
'back'
,
_
(
"Back"
)
,
reverse_lazy
(
'network.switch_port_list'
))
)
)
...
...
@@ -234,41 +236,42 @@ class VlanForm(ModelForm):
helper
.
layout
=
Layout
(
Div
(
Fieldset
(
'
Identity
'
,
''
,
'name'
,
'vid'
,
'network_type'
,
'managed'
,
),
Fieldset
(
'IPv4'
,
_
(
'IPv4'
)
,
'network4'
,
'snat_to'
,
'snat_ip'
,
'dhcp_pool'
,
),
Fieldset
(
'IPv6'
,
_
(
'IPv6'
)
,
'network6'
,
'ipv6_template'
,
'host_ipv6_prefixlen'
,
),
Fieldset
(
'Domain name service'
,
_
(
'Domain name service'
)
,
'domain'
,
'reverse_domain'
,
),
Fieldset
(
'Info'
,
_
(
'Info'
)
,
'description'
,
'comment'
,
'owner'
,
# 'created_at',
# 'modified_at',
),
),
FormActions
(
Submit
(
'submit'
,
'Save'
),
LinkButton
(
'back'
,
'Back'
,
reverse_lazy
(
'network.vlan_list'
))
Submit
(
'submit'
,
_
(
"Save"
)
),
LinkButton
(
'back'
,
_
(
"Back"
)
,
reverse_lazy
(
'network.vlan_list'
))
)
)
...
...
@@ -289,8 +292,8 @@ class VlanGroupForm(ModelForm):
)
),
FormActions
(
Submit
(
'submit'
,
'Save'
),
LinkButton
(
'back'
,
'Back'
,
reverse_lazy
(
Submit
(
'submit'
,
_
(
"Save"
)
),
LinkButton
(
'back'
,
_
(
"Back"
)
,
reverse_lazy
(
'network.vlan_group_list'
))
)
)
...
...
circle/network/templates/network/host-edit.html
View file @
86c16fe8
...
...
@@ -7,73 +7,77 @@
{% block content %}
<div
class=
"page-header"
>
<a
href=
"{% url "
network
.
host_delete
"
pk=
host_pk%}"
class=
"btn btn-danger pull-right"
><i
class=
"fa fa-times-circle"
></i>
{% trans "Delete this host" %}
</a>
<h2>
{{ form.hostname.value }}
</h2>
<a
href=
"{% url "
network
.
host_delete
"
pk=
host_pk%}"
class=
"btn btn-danger pull-right"
><i
class=
"fa fa-times-circle"
></i>
{% trans "Delete this host" %}
</a>
<h2>
{{ form.hostname.value }}
</h2>
</div>
<div
class=
"row"
>
<div
class=
"col-md-6"
>
{% crispy form %}
<div
class=
"col-md-6"
>
{% crispy form %}
</div>
<div
class=
"col-md-6"
>
<div
class=
"page-header"
>
<a
href=
"{% url "
network
.
rule_create
"
%}?
host=
{{
host_pk
}}"
class=
"btn btn-success pull-right btn-xs"
><i
class=
"fa fa-plus-circle"
></i>
{% trans "Add new rule" %}
</a>
<h3>
{% trans "Rules" %}
</h3>
</div>
<div
class=
"col-md-6"
>
<div
class=
"page-header"
>
<a
href=
"{% url "
network
.
rule_create
"
%}?
host=
{{
host_pk
}}"
class=
"btn btn-success pull-right btn-xs"
><i
class=
"fa fa-plus-circle"
></i>
{% trans "Add new rule" %}
</a>
<h3>
{% trans "Rules" %}
</h3>
</div>
{% if rule_list.data.data.count > 0 %}
{% render_table rule_list %}
{% else %}
{% trans "No rules associated with this host!" %}
{% endif %}
{% if rule_list.data.data.count > 0 %}
{% render_table rule_list %}
{% else %}
{% trans "No rules associated with this host." %}
{% endif %}
<div
class=
"page-header"
>
<h3>
{% trans "Groups" %}
</h3>
<div
class=
"page-header"
>
<h3>
{% trans "Groups" %}
</h3>
</div>
{% if group_rule_list|length > 0 %}
{% for group in group_rule_list %}
<div>
<h4
id=
"{{ group.pk }}_group_pk"
>
{{ group.name }}
<a
href=
"{% url "
network
.
remove_host_group
"
pk=
host_pk
group_pk=
group.pk
%}?
from=
{{
request
.
path
}}"
>
<i
class=
"fa fa-times"
style=
"vertical-align: middle;"
></i></a>
<a
href=
"{% url "
network
.
group
"
group
.
pk
%}"
>
<i
class=
"fa fa-pencil"
style=
"vertical-align: middle;"
></i></a>
</h4>
</div>
{% if group_rule_list|length > 0 %}
{% for group in group_rule_list %}
<div>
<h4
id=
"{{ group.pk }}_group_pk"
>
{{ group.name }}
<a
href=
"{% url "
network
.
remove_host_group
"
pk=
host_pk
group_pk=
group.pk
%}?
from=
{{
request
.
path
}}"
>
<i
class=
"fa fa-times"
style=
"vertical-align: middle;"
></i></a>
<a
href=
"{% url "
network
.
group
"
group
.
pk
%}"
>
<i
class=
"fa fa-pencil"
style=
"vertical-align: middle;"
></i></a>
</h4>
</div>
{% endfor %}
{% else %}
{% trans "This host is not added to any host groups!" %}
{% endif %}
{% endfor %}
{% else %}
{% trans "This host is not added to any host groups!" %}
{% endif %}
<div
class=
"page-header"
>
<h3>
{% trans "Add host group" %}
</h3>
</div>
{% if not_used_groups|length == 0 %}
No more groups to add!
{% trans "No more groups to add" %}
{% else %}
<form
action=
"{% url "
network
.
add_host_group
"
pk=
host_pk
%}"
method=
"POST"
>
<form
action=
"{% url "
network
.
add_host_group
"
pk=
host_pk
%}"
method=
"POST"
>
{% csrf_token %}
<div
class=
"input-group"
>
<select
name=
"group"
id=
"add_group"
class=
"form-control"
>
{% for rest in not_used_groups %}
<option
value=
"{{ rest.pk }}"
>
{{ rest }}
</option>
{% endfor %}
</select>
<div
class=
"input-group-btn"
>
<input
type=
"submit"
value=
"{% trans "
Add
group
"
%}"
class=
"btn btn-default"
></input>
</div>
</div>
<!-- input-group -->
</form>
<div
class=
"input-group"
>
<select
name=
"group"
id=
"add_group"
class=
"form-control"
>
{% for rest in not_used_groups %}
<option
value=
"{{ rest.pk }}"
>
{{ rest }}
</option>
{% endfor %}
</select>
<div
class=
"input-group-btn"
>
<input
type=
"submit"
value=
"{% trans "
Add
group
"
%}"
class=
"btn btn-default"
></input>
</div>
</div>
<!-- input-group -->
</form>
{% endif %}
<div
class=
"page-header"
>
<a
href=
"{% url "
network
.
record_create
"
%}?
host=
{{
host_pk
}}"
class=
"btn btn-xs btn-success pull-right"
>
<i
class=
"fa fa-plus-circle"
></i>
{% trans "Add new CNAME record" %}
</a>
<h3>
{% trans "Records" %}
</h3>
</div>
{% render_table records_table %}
</div>
<!-- col-sm-5 -->
</div>
<!-- col-sm-5 -->
</div>
<!-- row -->
{% endblock %}
{% block extra_etc %}
<script
src=
"{% static "
js
/
host
.
js
"
%}"
></script>
<script
src=
"{% static "
js
/
host
.
js
"
%}"
></script>
{% endblock %}
circle/network/views.py
View file @
86c16fe8
...
...
@@ -34,6 +34,7 @@ from .forms import (HostForm, VlanForm, DomainForm, GroupForm, RecordForm,
BlacklistItemForm
,
RuleForm
,
VlanGroupForm
,
SwitchPortForm
)
from
django.contrib
import
messages
from
django.contrib.messages.views
import
SuccessMessageMixin
from
django.views.generic.edit
import
FormMixin
from
django.utils.translation
import
ugettext_lazy
as
_
from
braces.views
import
LoginRequiredMixin
,
SuperuserRequiredMixin
...
...
@@ -42,25 +43,14 @@ from operator import itemgetter
from
itertools
import
chain
import
json
from
dashboard.views
import
AclUpdateView
from
dashboard.forms
import
AclUserAddForm
from
dashboard.forms
import
AclUser
OrGroup
AddForm
class
SuccessMessageMixin
(
FormMixin
):
"""
Adds a success message on successful form submission.
From django/contrib/messages/views.py@9a85ad89
"""
success_message
=
''
def
form_valid
(
self
,
form
):
response
=
super
(
SuccessMessageMixin
,
self
)
.
form_valid
(
form
)
success_message
=
self
.
get_success_message
(
form
.
cleaned_data
)
if
success_message
:
messages
.
success
(
self
.
request
,
success_message
)
return
response
def
get_success_message
(
self
,
cleaned_data
):
return
self
.
success_message
%
cleaned_data
class
InitialOwnerMixin
(
FormMixin
):
def
get_initial
(
self
):
initial
=
super
(
InitialOwnerMixin
,
self
)
.
get_initial
()
initial
[
'owner'
]
=
self
.
request
.
user
return
initial
class
IndexView
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
TemplateView
):
...
...
@@ -190,7 +180,7 @@ class DomainDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class
DomainCreate
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
SuccessMessageMixin
,
InitialOwnerMixin
,
CreateView
):
model
=
Domain
template_name
=
"network/domain-create.html"
form_class
=
DomainForm
...
...
@@ -274,7 +264,7 @@ class GroupList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
class
GroupCreate
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
SuccessMessageMixin
,
InitialOwnerMixin
,
CreateView
):
model
=
Group
template_name
=
"network/group-create.html"
form_class
=
GroupForm
...
...
@@ -413,7 +403,7 @@ class HostDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class
HostCreate
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
SuccessMessageMixin
,
InitialOwnerMixin
,
CreateView
):
model
=
Host
template_name
=
"network/host-create.html"
form_class
=
HostForm
...
...
@@ -497,7 +487,7 @@ class RecordDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class
RecordCreate
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
SuccessMessageMixin
,
InitialOwnerMixin
,
CreateView
):
model
=
Record
template_name
=
"network/record-create.html"
form_class
=
RecordForm
...
...
@@ -505,10 +495,23 @@ class RecordCreate(LoginRequiredMixin, SuperuserRequiredMixin,
success_message
=
_
(
u'Successfully created record!'
)
def
get_initial
(
self
):
return
{
# 'owner': 1,
'domain'
:
self
.
request
.
GET
.
get
(
'domain'
),
}
initial
=
super
(
RecordCreate
,
self
)
.
get_initial
()
initial
[
'domain'
]
=
self
.
request
.
GET
.
get
(
'domain'
)
host_pk
=
self
.
request
.
GET
.
get
(
"host"
)
try
:
host
=
Host
.
objects
.
get
(
pk
=
host_pk
)
except
(
Host
.
DoesNotExist
,
ValueError
):
host
=
None
if
host
:
initial
.
update
({
'type'
:
"CNAME"
,
'host'
:
host
,
'address'
:
host
.
get_fqdn
(),
})
return
initial
class
RecordDelete
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
DeleteView
):
...
...
@@ -556,18 +559,19 @@ class RuleDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class
RuleCreate
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
SuccessMessageMixin
,
InitialOwnerMixin
,
CreateView
):
model
=
Rule
template_name
=
"network/rule-create.html"
form_class
=
RuleForm
success_message
=
_
(
u'Successfully created rule!'
)
def
get_initial
(
self
):
return
{
# 'owner': 1,
initial
=
super
(
RuleCreate
,
self
)
.
get_initial
()
initial
.
update
({
'host'
:
self
.
request
.
GET
.
get
(
'host'
),
'hostgroup'
:
self
.
request
.
GET
.
get
(
'hostgroup'
)
}
})
return
initial
class
RuleDelete
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
DeleteView
):
...
...
@@ -660,14 +664,14 @@ class VlanDetail(LoginRequiredMixin, SuperuserRequiredMixin,
context
[
'vlan_vid'
]
=
self
.
kwargs
.
get
(
'vid'
)
context
[
'acl'
]
=
AclUpdateView
.
get_acl_data
(
self
.
object
,
self
.
request
.
user
,
'network.vlan-acl'
)
context
[
'aclform'
]
=
AclUserAddForm
()
context
[
'aclform'
]
=
AclUser
OrGroup
AddForm
()
return
context
success_url
=
reverse_lazy
(
'network.vlan_list'
)
class
VlanCreate
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
SuccessMessageMixin
,
InitialOwnerMixin
,
CreateView
):
model
=
Vlan
template_name
=
"network/vlan-create.html"
form_class
=
VlanForm
...
...
@@ -747,7 +751,7 @@ class VlanGroupDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class
VlanGroupCreate
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
SuccessMessageMixin
,
InitialOwnerMixin
,
CreateView
):
model
=
VlanGroup
template_name
=
"network/vlan-group-create.html"
form_class
=
VlanGroupForm
...
...
circle/storage/models.py
View file @
86c16fe8
...
...
@@ -367,7 +367,8 @@ class Disk(TimeStampedModel):
disk
=
cls
.
__create
(
user
,
params
)
disk
.
clean
()
disk
.
save
()
logger
.
debug
(
"Disk created:
%
s"
,
params
)
logger
.
debug
(
u"Disk created from:
%
s"
,
unicode
(
params
.
get
(
"base"
,
"nobase"
)))
return
disk
@classmethod
...
...
circle/vm/migrations/0027_auto__add_field_instance_has_agent__add_field_instancetemplate_has_age.py
0 → 100644
View file @
86c16fe8
This diff is collapsed.
Click to expand it.
circle/vm/models/activity.py
View file @
86c16fe8
...
...
@@ -24,7 +24,7 @@ from celery.signals import worker_ready
from
celery.contrib.abortable
import
AbortableAsyncResult
from
django.core.urlresolvers
import
reverse
from
django.db.models
import
CharField
,
ForeignKey
from
django.db.models
import
CharField
,
ForeignKey
,
BooleanField
from
django.utils
import
timezone
from
django.utils.translation
import
ugettext_lazy
as
_
,
ugettext_noop
...
...
@@ -70,6 +70,8 @@ class InstanceActivity(ActivityModel):
help_text
=
_
(
'Instance this activity works on.'
),
verbose_name
=
_
(
'instance'
))
resultant_state
=
CharField
(
blank
=
True
,
max_length
=
20
,
null
=
True
)
interruptible
=
BooleanField
(
default
=
False
,
help_text
=
_
(
'Other activities can interrupt this one.'
))
class
Meta
:
app_label
=
'vm'
...
...
@@ -91,24 +93,30 @@ class InstanceActivity(ActivityModel):
@classmethod
def
create
(
cls
,
code_suffix
,
instance
,
task_uuid
=
None
,
user
=
None
,
concurrency_check
=
True
,
readable_name
=
None
,
resultant_state
=
None
):
resultant_state
=
None
,
interruptible
=
False
):
readable_name
=
_normalize_readable_name
(
readable_name
,
code_suffix
)
# Check for concurrent activities
active_activities
=
instance
.
activity_log
.
filter
(
finished__isnull
=
True
)
if
concurrency_check
and
active_activities
.
exists
():
raise
ActivityInProgressError
.
create
(
active_activities
[
0
])
activity_code
=
join_activity_code
(
cls
.
ACTIVITY_CODE_BASE
,
code_suffix
)
for
i
in
active_activities
:
if
i
.
interruptible
:
i
.
finish
(
False
,
result
=
ugettext_noop
(
"Interrupted by other activity."
))
else
:
raise
ActivityInProgressError
.
create
(
i
)
activity_code
=
cls
.
construct_activity_code
(
code_suffix
)
act
=
cls
(
activity_code
=
activity_code
,
instance
=
instance
,
parent
=
None
,
resultant_state
=
resultant_state
,
started
=
timezone
.
now
(),
readable_name_data
=
readable_name
.
to_dict
(),
task_uuid
=
task_uuid
,
user
=
user
)
task_uuid
=
task_uuid
,
user
=
user
,
interruptible
=
interruptible
)
act
.
save
()
return
act
def
create_sub
(
self
,
code_suffix
,
task_uuid
=
None
,
concurrency_check
=
True
,
readable_name
=
None
,
resultant_state
=
None
):
readable_name
=
None
,
resultant_state
=
None
,
interruptible
=
False
):
readable_name
=
_normalize_readable_name
(
readable_name
,
code_suffix
)
# Check for concurrent activities
...
...
@@ -119,7 +127,7 @@ class InstanceActivity(ActivityModel):
act
=
InstanceActivity
(
activity_code
=
join_activity_code
(
self
.
activity_code
,
code_suffix
),
instance
=
self
.
instance
,
parent
=
self
,
resultant_state
=
resultant_state
,
resultant_state
=
resultant_state
,
interruptible
=
interruptible
,
readable_name_data
=
readable_name
.
to_dict
(),
started
=
timezone
.
now
(),
task_uuid
=
task_uuid
,
user
=
self
.
user
)
act
.
save
()
...
...
@@ -183,13 +191,14 @@ class InstanceActivity(ActivityModel):
@contextmanager
def
sub_activity
(
self
,
code_suffix
,
on_abort
=
None
,
on_commit
=
None
,
readable_name
=
None
,
task_uuid
=
None
,
concurrency_check
=
True
):
concurrency_check
=
True
,
interruptible
=
False
):
"""Create a transactional context for a nested instance activity.
"""
if
not
readable_name
:
warn
(
"Set readable_name"
,
stacklevel
=
3
)
act
=
self
.
create_sub
(
code_suffix
,
task_uuid
,
concurrency_check
,
readable_name
=
readable_name
)
readable_name
=
readable_name
,
interruptible
=
interruptible
)
return
activitycontextimpl
(
act
,
on_abort
=
on_abort
,
on_commit
=
on_commit
)
def
get_operation
(
self
):
...
...
circle/vm/models/instance.py
View file @
86c16fe8
...
...
@@ -123,6 +123,10 @@ class VirtualMachineDescModel(BaseResourceConfigModel):
'format like "
%
s".'
)
%
'Ubuntu 12.04 LTS Desktop amd64'
))
tags
=
TaggableManager
(
blank
=
True
,
verbose_name
=
_
(
"tags"
))
has_agent
=
BooleanField
(
verbose_name
=
_
(
'has agent'
),
default
=
True
,
help_text
=
_
(
'If the machine has agent installed, and '
'the manager should wait for its start.'
))
class
Meta
:
abstract
=
True
...
...
@@ -424,7 +428,8 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
# prepare parameters
common_fields
=
[
'name'
,
'description'
,
'num_cores'
,
'ram_size'
,
'max_ram_size'
,
'arch'
,
'priority'
,
'boot_menu'
,
'raw_data'
,
'lease'
,
'access_method'
,
'system'
]
'raw_data'
,
'lease'
,
'access_method'
,
'system'
,
'has_agent'
]
params
=
dict
(
template
=
template
,
owner
=
owner
,
pw
=
pwgen
())
params
.
update
([(
f
,
getattr
(
template
,
f
))
for
f
in
common_fields
])
params
.
update
(
kwargs
)
# override defaults w/ user supplied values
...
...
circle/vm/operations.py
View file @
86c16fe8
...
...
@@ -299,16 +299,20 @@ class DeployOperation(InstanceOperation):
"deploy network"
)):
self
.
instance
.
deploy_net
()
try
:
self
.
instance
.
renew
(
parent_activity
=
activity
)
except
:
pass
# Resume vm
with
activity
.
sub_activity
(
'booting'
,
readable_name
=
ugettext_noop
(
"boot virtual machine"
)):
self
.
instance
.
resume_vm
(
timeout
=
timeout
)
try
:
self
.
instance
.
renew
(
parent_activity
=
activity
)
except
:
pass
if
self
.
instance
.
has_agent
:
activity
.
sub_activity
(
'os_boot'
,
readable_name
=
ugettext_noop
(
"wait operating system loading"
),
interruptible
=
True
)
register_operation
(
DeployOperation
)
...
...
@@ -425,8 +429,11 @@ class RebootOperation(InstanceOperation):
required_perms
=
()
accept_states
=
(
'RUNNING'
,
)
def
_operation
(
self
,
timeout
=
5
):
def
_operation
(
self
,
activity
,
timeout
=
5
):
self
.
instance
.
reboot_vm
(
timeout
=
timeout
)
if
self
.
instance
.
has_agent
:
activity
.
sub_activity
(
'os_boot'
,
readable_name
=
ugettext_noop
(
"wait operating system loading"
),
interruptible
=
True
)
register_operation
(
RebootOperation
)
...
...
@@ -499,8 +506,11 @@ class ResetOperation(InstanceOperation):
required_perms
=
()
accept_states
=
(
'RUNNING'
,
)
def
_operation
(
self
,
timeout
=
5
):
def
_operation
(
self
,
activity
,
timeout
=
5
):
self
.
instance
.
reset_vm
(
timeout
=
timeout
)
if
self
.
instance
.
has_agent
:
activity
.
sub_activity
(
'os_boot'
,
readable_name
=
ugettext_noop
(
"wait operating system loading"
),
interruptible
=
True
)
register_operation
(
ResetOperation
)
...
...
circle/vm/tasks/local_agent_tasks.py
View file @
86c16fe8
...
...
@@ -85,16 +85,22 @@ def agent_started(vm, version=None):
from
vm.models
import
Instance
,
instance_activity
,
InstanceActivity
instance
=
Instance
.
objects
.
get
(
id
=
int
(
vm
.
split
(
'-'
)[
-
1
]))
queue
=
instance
.
get_remote_queue_name
(
"agent"
)
initialized
=
InstanceActivity
.
objects
.
filter
(
instance
=
instance
,
activity_code
=
'vm.Instance.agent.cleanup'
)
.
exists
()
initialized
=
instance
.
activity_log
.
filter
(
activity_code
=
'vm.Instance.agent.cleanup'
)
.
exists
()
with
instance_activity
(
code_suffix
=
'agent'
,
readable_name
=
ugettext_noop
(
'agent'
),
concurrency_check
=
False
,
instance
=
instance
)
as
act
:
with
act
.
sub_activity
(
'starting'
,
readable_name
=
ugettext_noop
(
'starting'
)):
pass
for
i
in
InstanceActivity
.
objects
.
filter
(
instance
=
instance
,
activity_code__endswith
=
'.os_boot'
,
finished__isnull
=
True
):
i
.
finish
(
True
)
if
version
and
version
!=
settings
.
AGENT_VERSION
:
try
:
update_agent
(
instance
,
act
)
...
...
@@ -108,10 +114,9 @@ def agent_started(vm, version=None):
send_init_commands
(
instance
,
act
)
send_networking_commands
(
instance
,
act
)
with
act
.
sub_activity
(
'start_access_server'
,
readable_name
=
ugettext_noop
(
'start access server'
)
):
with
act
.
sub_activity
(
'start_access_server'
,
readable_name
=
ugettext_noop
(
'start access server'
)):
start_access_server
.
apply_async
(
queue
=
queue
,
args
=
(
vm
,
))
...
...
circle/vm/tests/test_models.py
View file @
86c16fe8
...
...
@@ -217,6 +217,8 @@ class InstanceActivityTestCase(TestCase):
def
test_create_concurrency_check
(
self
):
instance
=
MagicMock
(
spec
=
Instance
)
instance
.
activity_log
.
filter
.
return_value
.
__iter__
.
return_value
=
iter
(
[
MagicMock
(
spec
=
InstanceActivity
,
interruptible
=
False
)])
instance
.
activity_log
.
filter
.
return_value
.
exists
.
return_value
=
True
with
self
.
assertRaises
(
ActivityInProgressError
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment