Commit 32e83265 by Bach Dániel

Merge branch 'feature-node-salt'

parents c711f3c1 8a293708
......@@ -19,7 +19,8 @@ from __future__ import absolute_import
from django.contrib.auth.models import Group, User
from django_tables2 import Table, A
from django_tables2.columns import TemplateColumn, Column, LinkColumn
from django_tables2.columns import (TemplateColumn, Column, LinkColumn,
from vm.models import Node, InstanceTemplate, Lease
from django.utils.translation import ugettext_lazy as _
......@@ -67,12 +68,18 @@ class NodeListTable(Table):
minion_online = BooleanColumn(
verbose_name=_("Minion online"),
attrs={'th': {'class': 'node-list-table-thin'}},
class Meta:
model = Node
attrs = {'class': ('table table-bordered table-striped table-hover '
fields = ('pk', 'name', 'host', 'get_status_display', 'priority',
'overcommit', 'number_of_VMs', )
'minion_online', 'overcommit', 'number_of_VMs', )
class GroupListTable(Table):
......@@ -7,8 +7,9 @@
<dt>{% trans "RAM size" %}:</dt> <dd>{% widthratio 1048576 1 %} MiB</dd>
<dt>{% trans "Architecture" %}:</dt><dd>{{ }}</dd>
<dt>{% trans "Host IP" %}:</dt><dd>{{ }}</dd>
<dt>{% trans "Enabled" %}:</dt><dd>{{ node.enabled }}</dd>
<dt>{% trans "Host online" %}:</dt><dd> {{ }}</dd>
<dt>{% trans "Enabled" %}:</dt><dd>{{ node.enabled|yesno }}</dd>
<dt>{% trans "Host online" %}:</dt><dd> {{|yesno }}</dd>
<dt>{% trans "Minion online" %}:</dt><dd> {{ node.minion_online|yesno }}</dd>
<dt>{% trans "Priority" %}:</dt><dd>{{ node.priority }}</dd>
<dt>{% trans "Driver Version:" %}</dt>
......@@ -68,6 +68,8 @@ node_ops = OrderedDict([
op='passivate', icon='play-circle-o', effect='info')),
('disable', NodeOperationView.factory(
op='disable', icon='times-circle-o', effect='danger')),
('update_node', NodeOperationView.factory(
op='update_node', icon='refresh', effect='warning')),
('reset', NodeOperationView.factory(
op='reset', icon='stethoscope', effect='danger')),
('flush', NodeOperationView.factory(
......@@ -17,9 +17,15 @@
from __future__ import absolute_import, unicode_literals
from functools import update_wrapper
from glob import glob
from logging import getLogger
import os.path
from warnings import warn
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.db.models import (
......@@ -44,6 +50,56 @@ from .common import Trait
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,
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:
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)
# 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)
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
if time() > timeout_at:'jid %s minions %s did not return in time',
jid, (minions - found))
return ret
def node_available(function):
"""Decorate methods to ignore disabled Nodes.
......@@ -111,6 +167,18 @@ class Node(OperatedMixin, TimeStampedModel):
online = property(get_online)
def get_minion_online(self):
name =
client = MyLocalClient()
client.opts['timeout'] = 0.2
return bool(client.cmd(name, '')[name])
except (KeyError, SaltClientError):
return False
minion_online = property(get_minion_online)
def get_info(self):
......@@ -26,6 +26,7 @@ from StringIO import StringIO
from tarfile import TarFile, TarInfo
import time
from urlparse import urlsplit
from salt.client import LocalClient
from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.utils import timezone
......@@ -1192,6 +1193,70 @@ class DisableOperation(NodeOperation):
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 = ""
def minion_cmd(self, module, params, timeout=3600):
name =
client = LocalClient()
data = client.cmd(
name, module, params, timeout=timeout)
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(
readable_name=ugettext_noop('upgrade packages')) as sa:
data = self.minion_cmd('pkg.upgrade', [])
upgraded = len(filter(lambda x: x['old'] and x['new'],
installed = len(filter(lambda x: not x['old'] and x['new'],
removed = len(filter(lambda x: x['old'] and not x['new'],
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"])
if not v["result"]:
failed += 1
if failed:
raise HumanReadableException.create(ugettext_noop(
"Failed: %(failed)s"), failed=failed)
class ScreenshotOperation(RemoteInstanceOperation):
id = 'screenshot'
name = _("screenshot")
......@@ -28,6 +28,7 @@ pylibmc==1.3.0
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment