django-orchestra/orchestra/contrib/orchestration/methods.py

177 lines
6.1 KiB
Python
Raw Normal View History

2014-05-08 16:59:35 +00:00
import hashlib
import json
2014-10-04 09:29:18 +00:00
import logging
2014-05-08 16:59:35 +00:00
import os
import socket
import sys
import select
import paramiko
from celery.datastructures import ExceptionInfo
2014-10-27 15:15:22 +00:00
from django.conf import settings as djsettings
2014-05-08 16:59:35 +00:00
from orchestra.utils.sys import sshrun
from orchestra.utils.python import CaptureStdout, import_class
2015-03-23 15:36:51 +00:00
2014-05-08 16:59:35 +00:00
from . import settings
2014-10-04 09:29:18 +00:00
logger = logging.getLogger(__name__)
paramiko_connections = {}
2014-10-04 13:23:04 +00:00
2014-10-04 09:29:18 +00:00
def Paramiko(backend, log, server, cmds, async=False):
"""
Executes cmds to remote server using Pramaiko
"""
script = '\n'.join(cmds)
2014-05-08 16:59:35 +00:00
script = script.replace('\r', '')
2015-05-06 14:39:25 +00:00
log.state = log.STARTED
log.script = script
2015-05-06 14:39:25 +00:00
log.save(update_fields=('script', 'state'))
if not cmds:
return
2014-10-15 12:47:28 +00:00
channel = None
ssh = None
2014-05-08 16:59:35 +00:00
try:
addr = server.get_address()
# ssh connection
ssh = paramiko_connections.get(addr)
if not ssh:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
key = settings.ORCHESTRATION_SSH_KEY_PATH
try:
ssh.connect(addr, username='root', key_filename=key)
except socket.error as e:
logger.error('%s timed out on %s' % (backend, addr))
log.state = log.TIMEOUT
log.stderr = str(e)
log.save(update_fields=['state', 'stderr'])
return
paramiko_connections[addr] = ssh
2014-05-08 16:59:35 +00:00
transport = ssh.get_transport()
2014-10-02 15:58:27 +00:00
channel = transport.open_session()
channel.exec_command(backend.script_executable)
channel.sendall(script)
channel.shutdown_write()
2014-10-04 13:23:04 +00:00
# Log results
2014-10-04 09:29:18 +00:00
logger.debug('%s running on %s' % (backend, server))
if async:
2015-04-03 10:14:45 +00:00
second = False
2014-05-08 16:59:35 +00:00
while True:
# Non-blocking is the secret ingridient in the async sauce
select.select([channel], [], [])
if channel.recv_ready():
2015-04-03 10:14:45 +00:00
part = channel.recv(1024).decode('utf-8')
while part:
log.stdout += part
2015-04-03 10:14:45 +00:00
part = channel.recv(1024).decode('utf-8')
2014-05-08 16:59:35 +00:00
if channel.recv_stderr_ready():
2015-04-03 10:14:45 +00:00
part = channel.recv_stderr(1024).decode('utf-8')
while part:
log.stderr += part
2015-04-03 10:14:45 +00:00
part = channel.recv_stderr(1024).decode('utf-8')
log.save(update_fields=['stdout', 'stderr'])
2014-05-08 16:59:35 +00:00
if channel.exit_status_ready():
2015-04-03 10:14:45 +00:00
if second:
break
second = True
else:
log.stdout += channel.makefile('rb', -1).read().decode('utf-8')
log.stderr += channel.makefile_stderr('rb', -1).read().decode('utf-8')
log.exit_code = channel.recv_exit_status()
log.state = log.SUCCESS if log.exit_code == 0 else log.FAILURE
2014-10-04 09:29:18 +00:00
logger.debug('%s execution state on %s is %s' % (backend, server, log.state))
2014-05-08 16:59:35 +00:00
log.save()
except:
log.state = log.ERROR
2014-05-08 16:59:35 +00:00
log.traceback = ExceptionInfo(sys.exc_info()).traceback
2014-10-04 13:23:04 +00:00
logger.error('Exception while executing %s on %s' % (backend, server))
logger.debug(log.traceback)
2014-05-08 16:59:35 +00:00
log.save()
2014-10-04 13:23:04 +00:00
finally:
if log.state == log.STARTED:
log.state = log.ABORTED
log.save(update_fields=['state'])
2014-10-15 12:47:28 +00:00
if channel is not None:
channel.close()
def OpenSSH(backend, log, server, cmds, async=False):
"""
Executes cmds to remote server using SSH with connection resuse for maximum performance
"""
script = '\n'.join(cmds)
script = script.replace('\r', '')
log.state = log.STARTED
log.script = script
log.save(update_fields=('script', 'state'))
if not cmds:
return
channel = None
ssh = None
try:
ssh = sshrun(server.get_address(), script, executable=backend.script_executable,
persist=True, async=async)
logger.debug('%s running on %s' % (backend, server))
if async:
second = False
for state in ssh:
log.stdout += state.stdout.decode('utf8')
log.stderr += state.stderr.decode('utf8')
log.save()
log.exit_code = state.exit_code
else:
log.stdout = ssh.stdout
log.stderr = ssh.stderr
log.exit_code = ssh.exit_code
log.state = log.SUCCESS if log.exit_code == 0 else log.FAILURE
logger.debug('%s execution state on %s is %s' % (backend, server, log.state))
log.save()
except:
log.state = log.ERROR
log.traceback = ExceptionInfo(sys.exc_info()).traceback
logger.error('Exception while executing %s on %s' % (backend, server))
logger.debug(log.traceback)
log.save()
finally:
if log.state == log.STARTED:
log.state = log.ABORTED
log.save(update_fields=['state'])
def SSH(*args, **kwargs):
""" facade function enabling to chose between multiple SSH backends"""
method = import_class(settings.ORCHESTRATION_SSH_METHOD_BACKEND)
return method(*args, **kwargs)
2014-05-08 16:59:35 +00:00
def Python(backend, log, server, cmds, async=False):
# TODO collect stdout?
2015-04-02 16:14:55 +00:00
script = [ str(cmd.func.__name__) + str(cmd.args) for cmd in cmds ]
2014-05-08 16:59:35 +00:00
script = json.dumps(script, indent=4).replace('"', '')
2015-05-06 14:39:25 +00:00
log.state = log.STARTED
2014-05-08 16:59:35 +00:00
log.script = '\n'.join([log.script, script])
2015-05-06 14:39:25 +00:00
log.save(update_fields=('script', 'state'))
2014-05-08 16:59:35 +00:00
try:
for cmd in cmds:
2015-03-23 15:36:51 +00:00
with CaptureStdout() as stdout:
result = cmd(server)
for line in stdout:
2015-04-02 16:14:55 +00:00
log.stdout += line + '\n'
if async:
log.save(update_fields=['stdout'])
2014-05-08 16:59:35 +00:00
except:
log.exit_code = 1
log.state = log.FAILURE
2014-05-08 16:59:35 +00:00
log.traceback = ExceptionInfo(sys.exc_info()).traceback
2015-03-23 15:36:51 +00:00
logger.error('Exception while executing %s on %s' % (backend, server))
2014-05-08 16:59:35 +00:00
else:
log.exit_code = 0
log.state = log.SUCCESS
2015-03-23 15:36:51 +00:00
logger.debug('%s execution state on %s is %s' % (backend, server, log.state))
2014-05-08 16:59:35 +00:00
log.save()