1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Initial commit for the "fireball2" connection plugin

Still needs:
* chunked file transfer/receive
* should probably move all send/recv operations to separate
  functions to reduce code duplication
* initial connection setup over ssh? or do we handle that in runner?
This commit is contained in:
James Cammarata 2013-08-07 11:54:53 -04:00
parent fd2aabaa27
commit acc5d09351
3 changed files with 136 additions and 2 deletions

View file

@ -132,6 +132,7 @@ ANSIBLE_NOCOWS = get_config(p, DEFAULTS, 'nocows', 'ANSIBLE_NOCO
ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None) ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None)
PARAMIKO_RECORD_HOST_KEYS = get_config(p, 'paramiko_connection', 'record_host_keys', 'ANSIBLE_PARAMIKO_RECORD_HOST_KEYS', True, boolean=True) PARAMIKO_RECORD_HOST_KEYS = get_config(p, 'paramiko_connection', 'record_host_keys', 'ANSIBLE_PARAMIKO_RECORD_HOST_KEYS', True, boolean=True)
ZEROMQ_PORT = int(get_config(p, 'fireball_connection', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099)) ZEROMQ_PORT = int(get_config(p, 'fireball_connection', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099))
FIREBALL2_PORT = int(get_config(p, 'fireball_connection', 'fireball2_port', 'ANSIBLE_FIREBALL2_PORT', 5099))
DEFAULT_UNDEFINED_VAR_BEHAVIOR = get_config(p, DEFAULTS, 'error_on_undefined_vars', 'ANSIBLE_ERROR_ON_UNDEFINED_VARS', True, boolean=True) DEFAULT_UNDEFINED_VAR_BEHAVIOR = get_config(p, DEFAULTS, 'error_on_undefined_vars', 'ANSIBLE_ERROR_ON_UNDEFINED_VARS', True, boolean=True)
HOST_KEY_CHECKING = get_config(p, DEFAULTS, 'host_key_checking', 'ANSIBLE_HOST_KEY_CHECKING', True, boolean=True) HOST_KEY_CHECKING = get_config(p, DEFAULTS, 'host_key_checking', 'ANSIBLE_HOST_KEY_CHECKING', True, boolean=True)

View file

@ -0,0 +1,129 @@
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import json
import os
import base64
import socket
from ansible.callbacks import vvv
from ansible import utils
from ansible import errors
from ansible import constants
class Connection(object):
''' raw socket accelerated connection '''
def __init__(self, runner, host, port, *args, **kwargs):
self.runner = runner
# attempt to work around shared-memory funness
if getattr(self.runner, 'aes_keys', None):
utils.AES_KEYS = self.runner.aes_keys
self.host = host
self.context = None
self.conn = None
self.cipher = AES256Cipher()
if port is None:
self.port = constants.FIREBALL2_PORT
else:
self.port = port
def connect(self):
''' activates the connection object '''
self.conn = socket.socket()
self.conn.connect((self.host,self.port))
return self
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'):
''' run a command on the remote host '''
vvv("EXEC COMMAND %s" % cmd)
data = dict(
mode='command',
cmd=cmd,
tmp_path=tmp_path,
executable=executable,
)
data = utils.jsonify(data)
data = self.cipher.encrypt(data)
if self.conn.sendall(data):
raise errors.AnisbleError("Failed to send command to %s:%s" % (self.host,self.port))
response = self.conn.recv(2048)
response = self.cipher.decrypt(response)
response = utils.parse_json(response)
return (response.get('rc',None), '', response.get('stdout',''), response.get('stderr',''))
def put_file(self, in_path, out_path):
''' transfer a file from local to remote '''
vvv("PUT %s TO %s" % (in_path, out_path), host=self.host)
if not os.path.exists(in_path):
raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path)
data = base64.file(in_path).read()
data = base64.b64encode(data)
data = dict(mode='put', data=data, out_path=out_path)
# TODO: support chunked file transfer
data = utils.jsonify(data)
data = self.cipher.encrypt(data)
if self.conn.sendall(data):
raise errors.AnsibleError("failed to send the file to %s:%s" % (self.host,self.port))
response = self.conn.recv(2048)
response = self.cipher.decrypt(response)
response = utils.parse_json(response)
# no meaningful response needed for this
def fetch_file(self, in_path, out_path):
''' save a remote file to the specified path '''
vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host)
data = dict(mode='fetch', in_path=in_path)
data = utils.jsonify(data)
data = self.cipher.encrypt(data)
if self.conn.sendall(data):
raise errors.AnsibleError("failed to initiate the file fetch with %s:%s" % (self.host,self.port))
response = self.socket.recv(2048)
response = self.cipher.decrypt(response)
response = utils.parse_json(response)
response = response['data']
response = base64.b64decode(response)
fh = open(out_path, "w")
fh.write(response)
fh.close()
def close(self):
''' terminate the connection '''
# Be a good citizen
try:
self.conn.close()
except:
pass

View file

@ -31,6 +31,7 @@ import ansible.constants as C
import time import time
import StringIO import StringIO
import stat import stat
import string
import termios import termios
import tty import tty
import pipes import pipes
@ -40,6 +41,7 @@ import warnings
import traceback import traceback
import getpass import getpass
import hmac
from Crypto.Cipher import from Crypto.Cipher import
from Crypto import Random from Crypto import Random
from Crypto.Random.random import StrongRandom from Crypto.Random.random import StrongRandom
@ -55,8 +57,10 @@ except ImportError:
try: try:
from hashlib import md5 as _md5 from hashlib import md5 as _md5
from hashlib import sha1 as _sha1
except ImportError: except ImportError:
from md5 import md5 as _md5 from md5 import md5 as _md5
from sha1 import sha1 as _sha1
PASSLIB_AVAILABLE = False PASSLIB_AVAILABLE = False
try: try:
@ -109,7 +113,7 @@ class AES256Cipher(object):
Returns true if the lifetime of the current key has Returns true if the lifetime of the current key has
exceeded the set lifetime. exceeded the set lifetime.
""" """
if ((time.time() - self.init_time) > self.lifetime): if (time.time() - self.init_time) > self.lifetime:
return True return True
else: else:
return False return False
@ -134,7 +138,7 @@ class AES256Cipher(object):
""" """
Generates an HMAC-SHA1 signature for the message Generates an HMAC-SHA1 signature for the message
""" """
return hmac.new(self.key, msg, hashlib.sha1).digest() return hmac.new(self.key, msg, _sha1).digest()
def validate_sig(self, msg, sig): def validate_sig(self, msg, sig):
""" """