Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Gelencsér Szabolcs
/
circlestack
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
32e83265
authored
Jan 01, 2015
by
Bach Dániel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature-node-salt'
parents
c711f3c1
8a293708
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
148 additions
and
4 deletions
+148
-4
circle/dashboard/tables.py
+9
-2
circle/dashboard/templates/dashboard/node-detail/resources.html
+3
-2
circle/dashboard/views/node.py
+2
-0
circle/vm/models/node.py
+68
-0
circle/vm/operations.py
+65
-0
requirements/base.txt
+1
-0
No files found.
circle/dashboard/tables.py
View file @
32e83265
...
@@ -19,7 +19,8 @@ from __future__ import absolute_import
...
@@ -19,7 +19,8 @@ from __future__ import absolute_import
from
django.contrib.auth.models
import
Group
,
User
from
django.contrib.auth.models
import
Group
,
User
from
django_tables2
import
Table
,
A
from
django_tables2
import
Table
,
A
from
django_tables2.columns
import
TemplateColumn
,
Column
,
LinkColumn
from
django_tables2.columns
import
(
TemplateColumn
,
Column
,
LinkColumn
,
BooleanColumn
)
from
vm.models
import
Node
,
InstanceTemplate
,
Lease
from
vm.models
import
Node
,
InstanceTemplate
,
Lease
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
...
@@ -67,12 +68,18 @@ class NodeListTable(Table):
...
@@ -67,12 +68,18 @@ class NodeListTable(Table):
orderable
=
False
,
orderable
=
False
,
)
)
minion_online
=
BooleanColumn
(
verbose_name
=
_
(
"Minion online"
),
attrs
=
{
'th'
:
{
'class'
:
'node-list-table-thin'
}},
orderable
=
False
,
)
class
Meta
:
class
Meta
:
model
=
Node
model
=
Node
attrs
=
{
'class'
:
(
'table table-bordered table-striped table-hover '
attrs
=
{
'class'
:
(
'table table-bordered table-striped table-hover '
'node-list-table'
)}
'node-list-table'
)}
fields
=
(
'pk'
,
'name'
,
'host'
,
'get_status_display'
,
'priority'
,
fields
=
(
'pk'
,
'name'
,
'host'
,
'get_status_display'
,
'priority'
,
'overcommit'
,
'number_of_VMs'
,
)
'
minion_online'
,
'
overcommit'
,
'number_of_VMs'
,
)
class
GroupListTable
(
Table
):
class
GroupListTable
(
Table
):
...
...
circle/dashboard/templates/dashboard/node-detail/resources.html
View file @
32e83265
...
@@ -7,8 +7,9 @@
...
@@ -7,8 +7,9 @@
<dt>
{% trans "RAM size" %}:
</dt>
<dd>
{% widthratio node.info.ram_size 1048576 1 %} MiB
</dd>
<dt>
{% trans "RAM size" %}:
</dt>
<dd>
{% widthratio node.info.ram_size 1048576 1 %} MiB
</dd>
<dt>
{% trans "Architecture" %}:
</dt><dd>
{{ node.info.architecture }}
</dd>
<dt>
{% trans "Architecture" %}:
</dt><dd>
{{ node.info.architecture }}
</dd>
<dt>
{% trans "Host IP" %}:
</dt><dd>
{{ node.host.ipv4 }}
</dd>
<dt>
{% trans "Host IP" %}:
</dt><dd>
{{ node.host.ipv4 }}
</dd>
<dt>
{% trans "Enabled" %}:
</dt><dd>
{{ node.enabled }}
</dd>
<dt>
{% trans "Enabled" %}:
</dt><dd>
{{ node.enabled|yesno }}
</dd>
<dt>
{% trans "Host online" %}:
</dt><dd>
{{ node.online }}
</dd>
<dt>
{% trans "Host online" %}:
</dt><dd>
{{ node.online|yesno }}
</dd>
<dt>
{% trans "Minion online" %}:
</dt><dd>
{{ node.minion_online|yesno }}
</dd>
<dt>
{% trans "Priority" %}:
</dt><dd>
{{ node.priority }}
</dd>
<dt>
{% trans "Priority" %}:
</dt><dd>
{{ node.priority }}
</dd>
<dt>
{% trans "Driver Version:" %}
</dt>
<dt>
{% trans "Driver Version:" %}
</dt>
<dd>
<dd>
...
...
circle/dashboard/views/node.py
View file @
32e83265
...
@@ -68,6 +68,8 @@ node_ops = OrderedDict([
...
@@ -68,6 +68,8 @@ node_ops = OrderedDict([
op
=
'passivate'
,
icon
=
'play-circle-o'
,
effect
=
'info'
)),
op
=
'passivate'
,
icon
=
'play-circle-o'
,
effect
=
'info'
)),
(
'disable'
,
NodeOperationView
.
factory
(
(
'disable'
,
NodeOperationView
.
factory
(
op
=
'disable'
,
icon
=
'times-circle-o'
,
effect
=
'danger'
)),
op
=
'disable'
,
icon
=
'times-circle-o'
,
effect
=
'danger'
)),
(
'update_node'
,
NodeOperationView
.
factory
(
op
=
'update_node'
,
icon
=
'refresh'
,
effect
=
'warning'
)),
(
'reset'
,
NodeOperationView
.
factory
(
(
'reset'
,
NodeOperationView
.
factory
(
op
=
'reset'
,
icon
=
'stethoscope'
,
effect
=
'danger'
)),
op
=
'reset'
,
icon
=
'stethoscope'
,
effect
=
'danger'
)),
(
'flush'
,
NodeOperationView
.
factory
(
(
'flush'
,
NodeOperationView
.
factory
(
...
...
circle/vm/models/node.py
View file @
32e83265
...
@@ -17,9 +17,15 @@
...
@@ -17,9 +17,15 @@
from
__future__
import
absolute_import
,
unicode_literals
from
__future__
import
absolute_import
,
unicode_literals
from
functools
import
update_wrapper
from
functools
import
update_wrapper
from
glob
import
glob
from
logging
import
getLogger
from
logging
import
getLogger
import
os.path
from
warnings
import
warn
from
warnings
import
warn
import
requests
import
requests
from
salt.client
import
LocalClient
from
salt.exceptions
import
SaltClientError
import
salt.utils
from
time
import
time
,
sleep
from
django.conf
import
settings
from
django.conf
import
settings
from
django.db.models
import
(
from
django.db.models
import
(
...
@@ -44,6 +50,56 @@ from .common import Trait
...
@@ -44,6 +50,56 @@ from .common import Trait
logger
=
getLogger
(
__name__
)
logger
=
getLogger
(
__name__
)
class
MyLocalClient
(
LocalClient
):
def
get_returns
(
self
,
jid
,
minions
,
timeout
=
None
):
'''
Get the returns for the command line interface via the event system
'''
minions
=
set
(
minions
)
if
timeout
is
None
:
timeout
=
self
.
opts
[
'timeout'
]
jid_dir
=
salt
.
utils
.
jid_dir
(
jid
,
self
.
opts
[
'cachedir'
],
self
.
opts
[
'hash_type'
])
start
=
time
()
timeout_at
=
start
+
timeout
found
=
set
()
ret
=
{}
wtag
=
os
.
path
.
join
(
jid_dir
,
'wtag*'
)
# Check to see if the jid is real, if not return the empty dict
if
not
os
.
path
.
isdir
(
jid_dir
):
logger
.
warning
(
"jid_dir (
%
s) does not exist"
,
jid_dir
)
return
ret
# Wait for the hosts to check in
while
True
:
time_left
=
timeout_at
-
time
()
raw
=
self
.
event
.
get_event
(
time_left
,
jid
)
if
raw
is
not
None
and
'return'
in
raw
:
found
.
add
(
raw
[
'id'
])
ret
[
raw
[
'id'
]]
=
raw
[
'return'
]
if
len
(
found
.
intersection
(
minions
))
>=
len
(
minions
):
# All minions have returned, break out of the loop
logger
.
debug
(
"jid
%
s found all minions"
,
jid
)
break
continue
# Then event system timeout was reached and nothing was returned
if
len
(
found
.
intersection
(
minions
))
>=
len
(
minions
):
# All minions have returned, break out of the loop
logger
.
debug
(
"jid
%
s found all minions"
,
jid
)
break
if
glob
(
wtag
)
and
time
()
<=
timeout_at
+
1
:
# The timeout +1 has not been reached and there is still a
# write tag for the syndic
continue
if
time
()
>
timeout_at
:
logger
.
info
(
'jid
%
s minions
%
s did not return in time'
,
jid
,
(
minions
-
found
))
break
sleep
(
0.01
)
return
ret
def
node_available
(
function
):
def
node_available
(
function
):
"""Decorate methods to ignore disabled Nodes.
"""Decorate methods to ignore disabled Nodes.
"""
"""
...
@@ -111,6 +167,18 @@ class Node(OperatedMixin, TimeStampedModel):
...
@@ -111,6 +167,18 @@ class Node(OperatedMixin, TimeStampedModel):
online
=
property
(
get_online
)
online
=
property
(
get_online
)
@method_cache
(
20
)
def
get_minion_online
(
self
):
name
=
self
.
host
.
hostname
try
:
client
=
MyLocalClient
()
client
.
opts
[
'timeout'
]
=
0.2
return
bool
(
client
.
cmd
(
name
,
'test.ping'
)[
name
])
except
(
KeyError
,
SaltClientError
):
return
False
minion_online
=
property
(
get_minion_online
)
@node_available
@node_available
@method_cache
(
300
)
@method_cache
(
300
)
def
get_info
(
self
):
def
get_info
(
self
):
...
...
circle/vm/operations.py
View file @
32e83265
...
@@ -26,6 +26,7 @@ from StringIO import StringIO
...
@@ -26,6 +26,7 @@ from StringIO import StringIO
from
tarfile
import
TarFile
,
TarInfo
from
tarfile
import
TarFile
,
TarInfo
import
time
import
time
from
urlparse
import
urlsplit
from
urlparse
import
urlsplit
from
salt.client
import
LocalClient
from
django.core.exceptions
import
PermissionDenied
,
SuspiciousOperation
from
django.core.exceptions
import
PermissionDenied
,
SuspiciousOperation
from
django.utils
import
timezone
from
django.utils
import
timezone
...
@@ -1192,6 +1193,70 @@ class DisableOperation(NodeOperation):
...
@@ -1192,6 +1193,70 @@ class DisableOperation(NodeOperation):
@register_operation
@register_operation
class
UpdateNodeOperation
(
NodeOperation
):
id
=
'update_node'
name
=
_
(
"update node"
)
description
=
_
(
"Upgrade or install node software (vmdriver, agentdriver, "
"monitor-client) with Salt."
)
required_perms
=
()
online_required
=
False
async_queue
=
"localhost.man.slow"
def
minion_cmd
(
self
,
module
,
params
,
timeout
=
3600
):
name
=
self
.
node
.
host
.
hostname
client
=
LocalClient
()
data
=
client
.
cmd
(
name
,
module
,
params
,
timeout
=
timeout
)
try
:
data
=
data
[
name
]
except
KeyError
:
raise
HumanReadableException
.
create
(
ugettext_noop
(
"No minions matched the target."
))
if
not
isinstance
(
data
,
dict
):
raise
HumanReadableException
.
create
(
ugettext_noop
(
"Unhandled exception:
%(msg)
s"
),
msg
=
unicode
(
data
))
return
data
def
_operation
(
self
,
activity
):
with
activity
.
sub_activity
(
'upgrade_packages'
,
readable_name
=
ugettext_noop
(
'upgrade packages'
))
as
sa
:
data
=
self
.
minion_cmd
(
'pkg.upgrade'
,
[])
upgraded
=
len
(
filter
(
lambda
x
:
x
[
'old'
]
and
x
[
'new'
],
data
.
values
()))
installed
=
len
(
filter
(
lambda
x
:
not
x
[
'old'
]
and
x
[
'new'
],
data
.
values
()))
removed
=
len
(
filter
(
lambda
x
:
x
[
'old'
]
and
not
x
[
'new'
],
data
.
values
()))
sa
.
result
=
create_readable
(
ugettext_noop
(
"Upgraded:
%(upgraded)
s, Installed:
%(installed)
s, "
"Removed:
%(removed)
s"
),
upgraded
=
upgraded
,
installed
=
installed
,
removed
=
removed
)
data
=
self
.
minion_cmd
(
'state.sls'
,
[
'node'
,
'nfs-client'
])
failed
=
0
for
k
,
v
in
data
.
iteritems
():
logger
.
debug
(
'salt state
%
s
%
s'
,
k
,
v
)
act_name
=
': '
.
join
(
k
.
split
(
'_|-'
)[:
2
])
if
not
v
[
"result"
]
or
v
[
"changes"
]:
act
=
activity
.
create_sub
(
act_name
[:
70
],
readable_name
=
act_name
)
act
.
result
=
create_readable
(
ugettext_noop
(
"Changes:
%(changes)
s Comment:
%(comment)
s"
),
changes
=
v
[
"changes"
],
comment
=
v
[
"comment"
])
act
.
finish
(
v
[
"result"
])
if
not
v
[
"result"
]:
failed
+=
1
if
failed
:
raise
HumanReadableException
.
create
(
ugettext_noop
(
"Failed:
%(failed)
s"
),
failed
=
failed
)
@register_operation
class
ScreenshotOperation
(
RemoteInstanceOperation
):
class
ScreenshotOperation
(
RemoteInstanceOperation
):
id
=
'screenshot'
id
=
'screenshot'
name
=
_
(
"screenshot"
)
name
=
_
(
"screenshot"
)
...
...
requirements/base.txt
View file @
32e83265
...
@@ -28,6 +28,7 @@ pylibmc==1.3.0
...
@@ -28,6 +28,7 @@ pylibmc==1.3.0
python-dateutil==2.2
python-dateutil==2.2
pytz==2014.2
pytz==2014.2
requests==2.2.1
requests==2.2.1
salt==2014.1.0
simplejson==3.4.0
simplejson==3.4.0
six==1.6.1
six==1.6.1
South==0.8.4
South==0.8.4
...
...
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