1# -*- coding: utf-8 -*- 2# Based on local.py (c) 2012, Michael DeHaan <michael.dehaan@gmail.com> 3# Based on chroot.py (c) 2013, Maykel Moya <mmoya@speedyrails.com> 4# Copyright (c) 2013, Michael Scherer <misc@zarb.org> 5# Copyright (c) 2017 Ansible Project 6# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 7 8from __future__ import (absolute_import, division, print_function) 9__metaclass__ = type 10 11DOCUMENTATION = ''' 12 author: Michael Scherer (@mscherer) <misc@zarb.org> 13 name: funcd 14 short_description: Use funcd to connect to target 15 description: 16 - This transport permits you to use Ansible over Func. 17 - For people who have already setup func and that wish to play with ansible, 18 this permit to move gradually to ansible without having to redo completely the setup of the network. 19 options: 20 remote_addr: 21 description: 22 - The path of the chroot you want to access. 23 default: inventory_hostname 24 vars: 25 - name: ansible_host 26 - name: ansible_func_host 27''' 28 29HAVE_FUNC = False 30try: 31 import func.overlord.client as fc 32 HAVE_FUNC = True 33except ImportError: 34 pass 35 36import os 37import tempfile 38import shutil 39 40from ansible.errors import AnsibleError 41from ansible.plugins.connection import ConnectionBase 42from ansible.utils.display import Display 43 44display = Display() 45 46 47class Connection(ConnectionBase): 48 """ Func-based connections """ 49 50 has_pipelining = False 51 52 def __init__(self, runner, host, port, *args, **kwargs): 53 self.runner = runner 54 self.host = host 55 # port is unused, this go on func 56 self.port = port 57 self.client = None 58 59 def connect(self, port=None): 60 if not HAVE_FUNC: 61 raise AnsibleError("func is not installed") 62 63 self.client = fc.Client(self.host) 64 return self 65 66 def exec_command(self, cmd, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): 67 """ run a command on the remote minion """ 68 69 if in_data: 70 raise AnsibleError("Internal Error: this module does not support optimized module pipelining") 71 72 # totally ignores privlege escalation 73 display.vvv("EXEC %s" % cmd, host=self.host) 74 p = self.client.command.run(cmd)[self.host] 75 return p[0], p[1], p[2] 76 77 @staticmethod 78 def _normalize_path(path, prefix): 79 if not path.startswith(os.path.sep): 80 path = os.path.join(os.path.sep, path) 81 normpath = os.path.normpath(path) 82 return os.path.join(prefix, normpath[1:]) 83 84 def put_file(self, in_path, out_path): 85 """ transfer a file from local to remote """ 86 87 out_path = self._normalize_path(out_path, '/') 88 display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) 89 self.client.local.copyfile.send(in_path, out_path) 90 91 def fetch_file(self, in_path, out_path): 92 """ fetch a file from remote to local """ 93 94 in_path = self._normalize_path(in_path, '/') 95 display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) 96 # need to use a tmp dir due to difference of semantic for getfile 97 # ( who take a # directory as destination) and fetch_file, who 98 # take a file directly 99 tmpdir = tempfile.mkdtemp(prefix="func_ansible") 100 self.client.local.getfile.get(in_path, tmpdir) 101 shutil.move(os.path.join(tmpdir, self.host, os.path.basename(in_path)), out_path) 102 shutil.rmtree(tmpdir) 103 104 def close(self): 105 """ terminate the connection; nothing to do here """ 106 pass 107