Added orchestration.Server remote status retrieval

This commit is contained in:
Marc Aymerich 2016-02-04 12:09:09 +00:00
parent e74964417a
commit 7578c47c9a
7 changed files with 113 additions and 17 deletions

26
TODO.md
View file

@ -460,3 +460,29 @@ mkhomedir_helper or create ssh homes with bash.rc and such
# POSTFIX web traffic monitor '": uid=" from=<%(user)s>'
# orchestra.server PING/SSH+uptime status
class ServerState(models.Model):
server = models.OneToOneField(Server)
ping = models.CharField(max_length=256)
uptime = models.CharField(max_length=256)
from orchestra.contrib.orchestration.models import Server
from orchestra.utils.sys import run, sshrun, joinall
def retrieve_state(servers):
uptimes = []
pings = []
for server in servers:
address = server.get_address()
ping = run('ping -c 1 %s' % address, async=True)
pings.append(ping)
uptime = sshrun(address, 'uptime', persist=True, async=True)
uptimes.append(uptime)
pings = joinall(pings, silent=True)
uptimes = joinall(uptimes, silent=True)
for ping in pings:
print(ping.stdout.splitlines()[-1])
for uptime in uptimes:
print(uptime.stdout)
retrieve_state(Server.objects.all())

View file

@ -11,6 +11,7 @@ from . import settings, helpers
from .backends import ServiceBackend
from .forms import RouteForm
from .models import Server, Route, BackendLog, BackendOperation
from .utils import retrieve_state
from .widgets import RouteBackendSelect
@ -167,9 +168,25 @@ class BackendLogAdmin(admin.ModelAdmin):
class ServerAdmin(admin.ModelAdmin):
list_display = ('name', 'address', 'os')
list_display = ('name', 'address', 'os', 'display_ping', 'display_uptime')
list_filter = ('os',)
def display_ping(self, instance):
return self._remote_state[instance.pk][0]
display_ping.short_description = _("Ping")
display_ping.allow_tags = True
def display_uptime(self, instance):
return self._remote_state[instance.pk][1]
display_uptime.short_description = _("Uptime")
display_uptime.allow_tags = True
def get_queryset(self, request):
""" Order by structured name and imporve performance """
qs = super(ServerAdmin, self).get_queryset(request)
if request.method == 'GET' and request.resolver_match.func.__name__ == 'changelist_view':
self._remote_state = retrieve_state(qs)
return qs
admin.site.register(Server, ServerAdmin)
admin.site.register(BackendLog, BackendLogAdmin)

View file

@ -28,7 +28,7 @@ class Server(models.Model):
validators=[OrValidator(validate_ip_address, validate_hostname)],
null=True, unique=True, help_text=_(
"Optional IP address or domain name. Name field will be used if not provided.<br>"
"If the IP address never change you can set this field and save DNS requests."))
"If the IP address never changes you can set this field and save DNS requests."))
description = models.TextField(_("description"), blank=True)
os = models.CharField(_("operative system"), max_length=32,
choices=settings.ORCHESTRATION_OS_CHOICES,

View file

@ -0,0 +1,31 @@
from orchestra.utils.sys import run, sshrun, join
def retrieve_state(servers):
uptimes = []
pings = []
for server in servers:
address = server.get_address()
ping = run('ping -c 1 -w 1 %s' % address, async=True)
pings.append(ping)
uptime = sshrun(address, 'uptime', persist=True, async=True, options={'ConnectTimeout': 1})
uptimes.append(uptime)
state = {}
for server, ping, uptime in zip(servers, pings, uptimes):
ping = join(ping, silent=True)
ping = ping.stdout.splitlines()[-1].decode()
if ping.startswith('rtt'):
ping = '%s ms' % ping.split('/')[4]
else:
ping = '<span style="color:red"><b>offline<b></span>'
uptime = join(uptime, silent=True)
uptime = uptime.stdout.decode().split()
if uptime:
uptime = 'Up %s %s load %s %s %s' % (uptime[2], uptime[3], uptime[-3], uptime[-2], uptime[-1])
else:
uptime = '<span style="color:red"><b>Timeout<b></span>'
state[server.pk] = (ping, uptime)
return state

View file

@ -348,6 +348,17 @@ class PHPUploadMaxFileSize(PHPAppOption):
regex = r'^[0-9]{1,3}M$'
class PHPUploadTmpDir(PHPAppOption):
name = 'upload_tmp_dir'
verbose_name = _("Upload tmp dir")
help_text = _("The temporary directory used for storing files when doing file upload. "
"Must be writable by whatever user PHP is running as. "
"If not specified PHP will use the system's default.<br>"
"If the directory specified here is not writable, PHP falls back to the "
"system default temporary directory. If open_basedir is on, then the system "
"default directory must be allowed for an upload to succeed.")
regex = r'.*$'
class PHPZendExtension(PHPAppOption):
name = 'zend_extension'
verbose_name = _("Zend extension")

View file

@ -251,6 +251,7 @@ WEBAPPS_ENABLED_OPTIONS = Setting('WEBAPPS_ENABLED_OPTIONS', (
'orchestra.contrib.webapps.options.PHPSuhosinSimulation',
'orchestra.contrib.webapps.options.PHPSuhosinExecutorIncludeWhitelist',
'orchestra.contrib.webapps.options.PHPUploadMaxFileSize',
'orchestra.contrib.webapps.options.PHPUploadTmpDir',
'orchestra.contrib.webapps.options.PHPZendExtension',
),
# lazy loading

View file

@ -143,6 +143,14 @@ def join(iterator, display=False, silent=False, valid_codes=(0,)):
return out
def joinall(iterators, **kwargs):
results = []
for iterator in iterators:
out = join(iterator, **kwargs)
results.append(out)
return results
def run(command, display=False, valid_codes=(0,), silent=False, stdin=b'', async=False):
iterator = runiterator(command, display, stdin)
next(iterator)
@ -151,22 +159,24 @@ def run(command, display=False, valid_codes=(0,), silent=False, stdin=b'', async
return join(iterator, display=display, silent=silent, valid_codes=valid_codes)
def sshrun(addr, command, *args, executable='bash', persist=False, **kwargs):
from .. import settings
options = [
'stricthostkeychecking=no',
'BatchMode=yes',
'EscapeChar=none',
]
def sshrun(addr, command, *args, executable='bash', persist=False, options=None, **kwargs):
base_options = {
'stricthostkeychecking': 'no',
'BatchMode': 'yes',
'EscapeChar': 'none',
}
if persist:
options.extend((
'ControlMaster=auto',
'ControlPersist=yes',
'ControlPath=' + settings.ORCHESTRA_SSH_CONTROL_PATH,
))
from .. import settings
base_options.update({
'ControlMaster': 'auto',
'ControlPersist': 'yes',
'ControlPath': settings.ORCHESTRA_SSH_CONTROL_PATH,
})
base_options.update(options or {})
options = ['%s=%s' % (k, v) for k, v in base_options.items()]
options = ' -o '.join(options)
cmd = 'ssh -o {options} -C root@{addr} {executable}'.format(options=options, addr=addr,
executable=executable)
cmd = 'ssh -o {options} -C root@{addr} {executable}'.format(
options=options, addr=addr, executable=executable)
return run(cmd, *args, stdin=command.encode('utf8'), **kwargs)