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
c08afb10
authored
Nov 25, 2024
by
Szeberényi Imre
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Form batman:
two-factor fix vm-details fix storage model (duisk types?)
parent
2a3a6018
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
141 additions
and
47 deletions
+141
-47
circle/circle/settings/base.py
+38
-3
circle/dashboard/forms.py
+6
-3
circle/dashboard/templates/dashboard/enable-two-factor.html
+5
-2
circle/dashboard/templates/dashboard/vm-detail.html
+1
-3
circle/dashboard/urls.py
+5
-2
circle/dashboard/views/user.py
+0
-1
circle/dashboard/views/vm.py
+47
-1
circle/storage/models.py
+2
-1
circle/vm/operations.py
+32
-28
circle/vm/tasks/local_periodic_tasks.py
+5
-3
No files found.
circle/circle/settings/base.py
View file @
c08afb10
...
...
@@ -333,7 +333,7 @@ DJANGO_APPS = (
'django.contrib.auth'
,
'django.contrib.contenttypes'
,
'django.contrib.sessions'
,
'django.contrib.sites'
,
#
'django.contrib.sites',
'django.contrib.messages'
,
'django.contrib.staticfiles'
,
...
...
@@ -357,6 +357,7 @@ THIRD_PARTY_APPS = (
'statici18n'
,
'django_sshkey'
,
'pipeline'
,
'qrcode2'
,
)
...
...
@@ -413,7 +414,7 @@ LOGGING = {
'address'
:
'/dev/log'
,
# 'socktype': SOCK_STREAM,
# 'address': ('host', '514'),
}
}
,
},
'loggers'
:
{
'django.request'
:
{
...
...
@@ -531,6 +532,7 @@ LOGIN_REDIRECT_URL = "/"
AGENT_DIR
=
get_env_variable
(
'DJANGO_AGENT_DIR'
,
join
(
unicode
(
expanduser
(
"~"
)),
'agent'
))
# AGENT_DIR is the root directory for the agent.
# The directory structure SHOULD be:
# /home/username/agent
...
...
@@ -542,12 +544,45 @@ AGENT_DIR = get_env_variable(
#
try
:
git_env
=
{
'GIT_DIR'
:
join
(
join
(
AGENT_DIR
,
"agent
-linux"
),
'.git'
)}
git_env
=
{
'GIT_DIR'
:
join
(
join
(
AGENT_DIR
,
"agent
"
),
".git"
)}
AGENT_VERSION
=
check_output
(
(
'git'
,
'log'
,
'-1'
,
r'--pretty=format:
%
h'
,
'HEAD'
),
env
=
git_env
)
except
:
AGENT_VERSION
=
None
print
(
"LEGACY VERSION:
%
s ------"
%
AGENT_VERSION
)
#### NEW ####
####
# AGENT_VERSIONS is a dict eg: { "Linux" : "vers1" , "Windows" : "vers2", .... }
# Normally it is fetched from Jason fromatted AGENT_DIR/wersions.txt file
#
# The dir namings ha changed:
# The same, but the dir names are lowercase and generated like this:
# agent-(agent_system)-(version). Eg: agent-linux-vers1, agent-window-vers2
try
:
with
open
(
"
%
s/versions.txt"
%
AGENT_DIR
,
"r"
)
as
f
:
AGENT_VERSIONS
=
loads
(
f
.
read
())
print
(
"-----KONWN VERSIONS-----"
)
print
(
AGENT_VERSIONS
)
except
:
print
(
"Format ERROR in versions.txt !!!! "
)
# TODO more error reposrting
AGENT_VERSIONS
=
None
## PUBLIC function for getting the latest version ad the DIR
def
GET_AGENT_VERSION_BY_SYSTEM
(
agent_system
):
if
agent_system
is
None
:
return
None
,
None
if
type
(
AGENT_VERSIONS
)
is
not
dict
:
# legacy naming
return
AGENT_VERSION
,
AGENT_DIR
+
"/agent-"
+
agent_system
.
lower
()
ret
=
AGENT_VERSIONS
.
get
(
agent_system
)
if
ret
is
None
:
return
None
,
None
return
ret
,
AGENT_DIR
+
"/agent-"
+
agent_system
.
lower
()
+
"-"
+
ret
LOCALE_PATHS
=
(
join
(
SITE_ROOT
,
'locale'
),
)
COMPANY_NAME
=
get_env_variable
(
"COMPANY_NAME"
,
"BME IK 2015"
)
...
...
circle/dashboard/forms.py
View file @
c08afb10
...
...
@@ -543,7 +543,7 @@ class TemplateForm(forms.ModelForm):
else
:
self
.
allowed_fields
=
(
'name'
,
'access_method'
,
'description'
,
'system'
,
'tags'
,
'arch'
,
'lease'
,
'has_agent'
)
'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
())
-
...
...
@@ -972,11 +972,14 @@ class VmImportDiskForm(OperationForm):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
user
=
kwargs
.
pop
(
'user'
)
super
(
VmImportDiskForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
allowed
=
()
allowed
=
allowed
+
Disk
.
EXPORT_FORMATS
print
(
allowed
)
disk_paths
=
Store
(
self
.
user
)
.
get_files_with_exts
(
[
f
[
0
]
for
f
in
Disk
.
EXPORT_FORMATS
]
[
f
[
0
]
for
f
in
allowed
]
)
disk_filenames
=
[
os
.
path
.
basename
(
item
)
for
item
in
disk_paths
]
#raise Error(disk_filenames)
self
.
choices
=
zip
(
disk_paths
,
disk_filenames
)
self
.
fields
[
'name'
]
=
forms
.
CharField
(
max_length
=
100
,
label
=
_
(
'Name'
))
...
...
circle/dashboard/templates/dashboard/enable-two-factor.html
View file @
c08afb10
{% extends "dashboard/base.html" %}
{% load i18n %}
{% load qrcode2 %}
{% block content %}
<div
class=
"row"
>
...
...
@@ -27,8 +29,9 @@
Your secret key is:
<strong>
{{ secret }}
</strong>
{% endblocktrans %}
</span>
<img
src=
"//chart.googleapis.com/chart?chs=255x255&chld=L|0&cht=qr&chl={{ uri }}"
/>
<small><a
href=
"{{ uri }}"
>
{{ uri }}
</a></small>
{#
<img
src=
"//chart.googleapis.com/chart?chs=255x255&chld=L|0&cht=qr&chl={{ uri }}"
/>
#}
<img
src=
"{{ uri | qrcode_src }}"
with=
"300"
i
height=
300
alt=
"{{ uri }}"
>
{#
<small><a
href=
"{{ uri }}"
>
{{ uri }}
</a></small>
#}
</div>
<hr
/>
<div
id=
"two-factor-confirm"
>
...
...
circle/dashboard/templates/dashboard/vm-detail.html
View file @
c08afb10
...
...
@@ -112,15 +112,13 @@
<dd>
{{ instance.access_method|upper }}
</dd>
<dt>
{% trans "Host" %}
</dt>
<dd>
{% if instance.get_connect_port %}
{{ instance.get_connect_host }}:
<strong>
{{ instance.get_connect_port }}
</strong>
{% if instance.get_connect_port %}{{ instance.get_connect_host }}:
<strong>
{{ instance.get_connect_port }}
</strong>
{% elif instance.interface_set.count
<
1
%}
<
strong
>
{% trans "The VM doesn't have any network interface." %}
</strong>
{% else %}
<strong>
{% trans "The required port for this protocol is not forwarded." %}
</strong>
{% endif %}
</dd>
{% if instance.ipv6 and instance.get_connect_port %}
<dt>
{% trans "Host (IPv6)" %}
</dt>
<dd>
{{ ipv6_host }}:
<strong>
{{ ipv6_port }}
</strong></dd>
...
...
circle/dashboard/urls.py
View file @
c08afb10
...
...
@@ -58,7 +58,8 @@ from .views import (
MessageList
,
MessageDetail
,
MessageCreate
,
MessageDelete
,
EnableTwoFactorView
,
DisableTwoFactorView
,
AclUserGroupAutocomplete
,
AclUserAutocomplete
,
RescheduleView
,
GroupImportView
,
GroupExportView
RescheduleView
,
GroupImportView
,
GroupExportView
,
start_instance
,
shutdown_instance
,
get_instance
)
from
.views.node
import
node_ops
from
.views.vm
import
vm_ops
,
vm_mass_ops
...
...
@@ -78,7 +79,9 @@ urlpatterns = [
name
=
"dashboard.views.lease-delete"
),
url
(
r'^lease/(?P<pk>\d+)/acl/$'
,
LeaseAclUpdateView
.
as_view
(),
name
=
"dashboard.views.lease-acl"
),
url
(
r'^vmstart/(?P<pk>\d+)'
,
start_instance
),
url
(
r'^vmshutdown/(?P<pk>\d+)'
,
shutdown_instance
),
url
(
r'^vmget/(?P<pk>\d+)'
,
get_instance
),
url
(
r'^template/create/$'
,
TemplateCreate
.
as_view
(),
name
=
"dashboard.views.template-create"
),
url
(
r'^template/choose/$'
,
TemplateChoose
.
as_view
(),
...
...
circle/dashboard/views/user.py
View file @
c08afb10
...
...
@@ -723,7 +723,6 @@ if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
# authenticate the remote user
session_info
=
response
.
session_info
()
if
callable
(
attribute_mapping
):
attribute_mapping
=
attribute_mapping
()
if
callable
(
create_unknown_user
):
...
...
circle/dashboard/views/vm.py
View file @
c08afb10
...
...
@@ -36,7 +36,7 @@ from django.template.loader import render_to_string
from
django.utils.translation
import
(
ugettext
as
_
,
ugettext_noop
,
ungettext_lazy
,
)
from
django.views.decorators.http
import
require_GET
from
django.views.decorators.http
import
require_GET
,
require_POST
from
django.views.generic
import
(
UpdateView
,
ListView
,
TemplateView
)
...
...
@@ -70,6 +70,7 @@ from ..forms import (
VmRemoveInterfaceForm
,
VmRenameForm
,
)
from
vm.operations
import
DeployOperation
,
ShutdownOperation
from
request.models
import
TemplateAccessType
,
LeaseType
from
request.forms
import
LeaseRequestForm
,
TemplateRequestForm
from
..models
import
Favourite
...
...
@@ -1266,6 +1267,51 @@ def get_disk_download_status(request, pk):
content_type
=
"application/json"
,
)
from
django.views.decorators.csrf
import
csrf_exempt
@csrf_exempt
@require_POST
def
start_instance
(
request
,
pk
):
instance
=
Instance
.
objects
.
get
(
pk
=
pk
)
DeployOperation
(
instance
)
.
call
(
node
=
None
,
user
=
request
.
user
)
return
HttpResponse
(
json
.
dumps
({
'id'
:
pk
,
'op'
:
'deploy'
}),
content_type
=
"application/json"
,
)
@csrf_exempt
@require_POST
def
shutdown_instance
(
request
,
pk
):
instance
=
Instance
.
objects
.
get
(
pk
=
pk
)
ShutdownOperation
(
instance
)
.
call
(
user
=
request
.
user
)
return
HttpResponse
(
json
.
dumps
({
'id'
:
pk
,
'op'
:
'shutdown'
}),
content_type
=
"application/json"
,
)
@require_GET
def
get_instance
(
request
,
pk
):
instance
=
Instance
.
objects
.
get
(
pk
=
pk
)
if
instance
.
owner
!=
request
.
user
:
raise
PermissionDenied
()
return
HttpResponse
(
json
.
dumps
({
'id'
:
pk
,
'name'
:
instance
.
name
,
'status'
:
instance
.
status
,
'pw'
:
instance
.
pw
,
'ipv4'
:
str
(
instance
.
ipv4
),
'hostipv4'
:
instance
.
get_connect_host
(
use_ipv6
=
False
),
'sshport'
:
instance
.
get_connect_port
(
use_ipv6
=
False
)
}),
content_type
=
"application/json"
,
)
class
ClientCheck
(
LoginRequiredMixin
,
TemplateView
):
...
...
circle/storage/models.py
View file @
c08afb10
...
...
@@ -126,7 +126,8 @@ class Disk(TimeStampedModel):
EXPORT_FORMATS
=
((
'qcow2'
,
_
(
'QEMU disk image'
)),
(
'vmdk'
,
_
(
'VMware disk image'
)),
(
'vdi'
,
_
(
'VirtualBox disk image'
)),
(
'vpc'
,
_
(
'HyperV disk image'
)))
(
'vpc'
,
_
(
'HyperV disk image'
)),
(
'iso'
,
'ISO image'
))
name
=
CharField
(
blank
=
True
,
max_length
=
100
,
verbose_name
=
_
(
"name"
))
filename
=
CharField
(
max_length
=
256
,
unique
=
True
,
verbose_name
=
_
(
"filename"
))
...
...
circle/vm/operations.py
View file @
c08afb10
...
...
@@ -873,7 +873,7 @@ class ShutdownOperation(AbortableRemoteOperationMixin,
remote_queue
=
(
"vm"
,
"slow"
)
remote_timeout
=
180
def
_operation
(
self
,
task
):
def
_operation
(
self
,
task
=
vm_tasks
.
shutdown
):
super
(
ShutdownOperation
,
self
)
.
_operation
(
task
=
task
)
self
.
instance
.
yield_node
()
...
...
@@ -1575,19 +1575,6 @@ class AgentStartedOperation(InstanceOperation):
self
.
instance
.
_change_ip
(
parent_activity
=
activity
)
self
.
instance
.
_restart_networking
(
parent_activity
=
activity
)
new_version
=
settings
.
AGENT_VERSION
if
new_version
and
old_version
and
new_version
!=
old_version
:
try
:
self
.
instance
.
update_agent
(
parent_activity
=
activity
,
agent_system
=
agent_system
)
except
TimeoutError
:
pass
else
:
activity
.
sub_activity
(
'agent_wait'
,
readable_name
=
ugettext_noop
(
"wait agent restarting"
),
interruptible
=
True
)
return
# agent is going to restart
if
not
self
.
initialized
:
try
:
self
.
measure_boot_time
()
...
...
@@ -1600,6 +1587,20 @@ class AgentStartedOperation(InstanceOperation):
self
.
instance
.
_set_time
(
parent_activity
=
activity
)
self
.
instance
.
_set_hostname
(
parent_activity
=
activity
)
new_version
=
settings
.
GET_AGENT_VERSION_BY_SYSTEM
(
agent_system
)[
0
]
# if agent_system and new_version and (old_version is None or ("NOAGENTUPDATE" not in old_version and new_version != old_version)) :
if
agent_system
and
new_version
and
old_version
and
"NOAGENTUPDATE"
not
in
old_version
and
new_version
!=
old_version
:
try
:
self
.
instance
.
update_agent
(
parent_activity
=
activity
,
agent_system
=
agent_system
)
except
TimeoutError
:
pass
else
:
activity
.
sub_activity
(
'agent_wait'
,
readable_name
=
ugettext_noop
(
"wait agent restarting"
),
interruptible
=
True
)
return
# agent is going to restart
@register_operation
class
CleanupOperation
(
SubOperationMixin
,
RemoteAgentOperation
):
id
=
'_cleanup'
...
...
@@ -1658,11 +1659,12 @@ class UpdateAgentOperation(RemoteAgentOperation):
def
get_activity_name
(
self
,
kwargs
):
return
create_readable
(
ugettext_noop
(
'update agent to
%(version)
s'
),
version
=
settings
.
AGENT_VERSION
)
ugettext_noop
(
'update agent'
))
# ugettext_noop('update agent to %(version)s'),
# version=settings.GET_AGENT_VERSION_BY_SYSTEM(agent_system)[0])
@staticmethod
def
create_linux_tar
():
def
create_linux_tar
(
agent_system
):
def
exclude
(
tarinfo
):
ignored
=
(
'./.'
,
'./misc'
,
'./windows'
)
if
any
(
tarinfo
.
name
.
startswith
(
x
)
for
x
in
ignored
):
...
...
@@ -1670,13 +1672,14 @@ class UpdateAgentOperation(RemoteAgentOperation):
else
:
return
tarinfo
_vers
,
_dir
=
settings
.
GET_AGENT_VERSION_BY_SYSTEM
(
agent_system
)
f
=
StringIO
()
with
TarFile
.
open
(
fileobj
=
f
,
mode
=
'w:gz'
)
as
tar
:
agent_path
=
os
.
path
.
join
(
settings
.
AGENT_DIR
,
"agent-linux"
)
agent_path
=
_dir
tar
.
add
(
agent_path
,
arcname
=
'.'
,
filter
=
exclude
)
version_fileobj
=
StringIO
(
settings
.
AGENT_VERSION
)
version_fileobj
=
StringIO
(
_vers
)
version_info
=
TarInfo
(
name
=
'version.txt'
)
version_info
.
size
=
len
(
version_fileobj
.
buf
)
tar
.
addfile
(
version_info
,
version_fileobj
)
...
...
@@ -1684,14 +1687,15 @@ class UpdateAgentOperation(RemoteAgentOperation):
return
encodestring
(
f
.
getvalue
())
.
replace
(
'
\n
'
,
''
)
@staticmethod
def
create_windows_tar
():
def
create_windows_tar
(
agent_system
):
_vers
,
_dir
=
settings
.
GET_AGENT_VERSION_BY_SYSTEM
(
agent_system
)
f
=
StringIO
()
agent_path
=
os
.
path
.
join
(
settings
.
AGENT_DIR
,
"agent-win"
)
agent_path
=
_dir
with
TarFile
.
open
(
fileobj
=
f
,
mode
=
'w|gz'
)
as
tar
:
tar
.
add
(
agent_path
,
arcname
=
'.'
)
version_fileobj
=
StringIO
(
settings
.
AGENT_VERSION
)
version_fileobj
=
StringIO
(
_vers
)
version_info
=
TarInfo
(
name
=
'version.txt'
)
version_info
.
size
=
len
(
version_fileobj
.
buf
)
tar
.
addfile
(
version_info
,
version_fileobj
)
...
...
@@ -1699,15 +1703,15 @@ class UpdateAgentOperation(RemoteAgentOperation):
return
encodestring
(
f
.
getvalue
())
.
replace
(
'
\n
'
,
''
)
def
_operation
(
self
,
user
,
activity
,
agent_system
):
_vers
,
_dir
=
settings
.
GET_AGENT_VERSION_BY_SYSTEM
(
agent_system
)
queue
=
self
.
_get_remote_queue
()
instance
=
self
.
instance
if
agent_system
==
"Windows"
:
executable
=
os
.
listdir
(
os
.
path
.
join
(
settings
.
AGENT_DIR
,
"agent-win"
))[
0
]
data
=
self
.
create_windows_tar
()
executable
=
sorted
(
os
.
listdir
(
_dir
))[
0
]
data
=
self
.
create_windows_tar
(
agent_system
)
elif
agent_system
==
"Linux"
:
executable
=
""
data
=
self
.
create_linux_tar
()
data
=
self
.
create_linux_tar
(
agent_system
)
else
:
# Legacy update method
executable
=
""
...
...
@@ -1720,7 +1724,7 @@ class UpdateAgentOperation(RemoteAgentOperation):
chunk_size
=
1024
*
1024
chunk_number
=
0
index
=
0
filename
=
settings
.
AGENT_VERSION
+
".tar"
filename
=
_vers
+
".tar"
while
True
:
chunk
=
data
[
index
:
index
+
chunk_size
]
if
chunk
:
...
...
@@ -1734,7 +1738,7 @@ class UpdateAgentOperation(RemoteAgentOperation):
agent_tasks
.
update
.
apply_async
(
queue
=
queue
,
args
=
(
instance
.
vm_name
,
filename
,
executable
,
checksum
)
)
.
get
(
timeout
=
6
0
)
)
.
get
(
timeout
=
12
0
)
break
...
...
circle/vm/tasks/local_periodic_tasks.py
View file @
c08afb10
...
...
@@ -50,17 +50,19 @@ def garbage_collector(offset=timezone.timedelta(seconds=20)):
for
i
in
Instance
.
objects
.
filter
(
destroyed_at
=
None
)
.
all
():
logger
.
debug
(
"Garbage_collector work_package:
%
d
%
s:
%
s:"
,
work_package
,
i
.
pk
,
now
>
i
.
time_of_delete
)
if
i
.
time_of_delete
and
now
>
i
.
time_of_delete
+
grace_period
and
work_package
>
0
:
logger
.
debug
(
"Garbage_collector delete"
)
work_package
-=
1
i
.
destroy
.
async
(
system
=
True
)
logger
.
debug
(
"Garbage_collector delete"
)
logger
.
info
(
"Expired instance
%
d destroyed."
,
i
.
pk
)
try
:
i
.
destroy
.
async
(
system
=
True
)
i
.
owner
.
profile
.
notify
(
ugettext_noop
(
'
%(instance)
s destroyed'
),
ugettext_noop
(
'Your instance <a href="
%(url)
s">
%(instance)
s</a> '
'has been destroyed due to expiration.'
),
instance
=
i
.
name
,
url
=
i
.
get_absolute_url
())
except
ActivityInProgressError
:
logger
.
error
(
"Expired instance
%
d can't be destroyed due the AtctivityInPorgressError."
,
i
.
pk
)
except
Exception
as
e
:
logger
.
debug
(
'Could not notify owner of instance
%
d .
%
s'
,
i
.
pk
,
unicode
(
e
))
...
...
@@ -78,7 +80,7 @@ def garbage_collector(offset=timezone.timedelta(seconds=20)):
'You can resume or destroy it.'
),
instance
=
i
.
name
,
url
=
i
.
get_absolute_url
())
except
ActivityInProgressError
:
logger
.
error
(
"Expired instance
%
d can't be
destroy
ed due the AtctivityInPorgressError."
,
i
.
pk
)
logger
.
error
(
"Expired instance
%
d can't be
suspend
ed due the AtctivityInPorgressError."
,
i
.
pk
)
except
Exception
as
e
:
logger
.
info
(
'Could not notify owner of instance
%
d .
%
s'
,
i
.
pk
,
unicode
(
e
))
...
...
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