96 lines
2.9 KiB
Python
96 lines
2.9 KiB
Python
"""
|
|
Defines distributed server functionality.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
import threading
|
|
from tempfile import NamedTemporaryFile
|
|
|
|
from fabric import Connection
|
|
from invoke import UnexpectedExit
|
|
|
|
from core.errors import CoreCommandError
|
|
|
|
LOCK = threading.Lock()
|
|
|
|
|
|
class DistributedServer(object):
|
|
"""
|
|
Provides distributed server interactions.
|
|
"""
|
|
|
|
def __init__(self, name, host):
|
|
"""
|
|
Create a DistributedServer instance.
|
|
|
|
:param str name: convenience name to associate with host
|
|
:param str host: host to connect to
|
|
"""
|
|
self.name = name
|
|
self.host = host
|
|
self.conn = Connection(host, user="root")
|
|
self.lock = threading.Lock()
|
|
|
|
def remote_cmd(self, cmd, env=None, cwd=None, wait=True):
|
|
"""
|
|
Run command remotely using server connection.
|
|
|
|
:param str cmd: command to run
|
|
:param dict env: environment for remote command, default is None
|
|
:param str cwd: directory to run command in, defaults to None, which is the
|
|
user's home directory
|
|
:param bool wait: True to wait for status, False to background process
|
|
:return: stdout when success
|
|
:rtype: str
|
|
:raises CoreCommandError: when a non-zero exit status occurs
|
|
"""
|
|
|
|
replace_env = env is not None
|
|
if not wait:
|
|
cmd += " &"
|
|
logging.info(
|
|
"remote cmd server(%s) cwd(%s) wait(%s): %s", self.host, cwd, wait, cmd
|
|
)
|
|
try:
|
|
with self.lock:
|
|
if cwd is None:
|
|
result = self.conn.run(
|
|
cmd, hide=False, env=env, replace_env=replace_env
|
|
)
|
|
else:
|
|
with self.conn.cd(cwd):
|
|
result = self.conn.run(
|
|
cmd, hide=False, env=env, replace_env=replace_env
|
|
)
|
|
return result.stdout.strip()
|
|
except UnexpectedExit as e:
|
|
stdout, stderr = e.streams_for_display()
|
|
raise CoreCommandError(e.result.exited, cmd, stdout, stderr)
|
|
|
|
def remote_put(self, source, destination):
|
|
"""
|
|
Push file to remote server.
|
|
|
|
:param str source: source file to push
|
|
:param str destination: destination file location
|
|
:return: nothing
|
|
"""
|
|
with self.lock:
|
|
self.conn.put(source, destination)
|
|
|
|
def remote_put_temp(self, destination, data):
|
|
"""
|
|
Remote push file contents to a remote server, using a temp file as an
|
|
intermediate step.
|
|
|
|
:param str destination: file destination for data
|
|
:param str data: data to store in remote file
|
|
:return: nothing
|
|
"""
|
|
with self.lock:
|
|
temp = NamedTemporaryFile(delete=False)
|
|
temp.write(data.encode("utf-8"))
|
|
temp.close()
|
|
self.conn.put(temp.name, destination)
|
|
os.unlink(temp.name)
|