예제 #1
0
def connect(module, debug=False):
    ''' Connects to CVP device using user provided credentials from playbook.
    :param module: Ansible module with parameters and client connection.
    :return: CvpClient object with connection instantiated.
    '''
    client = CvpClient()
    connection = Connection(module._socket_path)
    host = connection.get_option("host")
    port = connection.get_option("port")
    user = connection.get_option("remote_user")
    pswd = connection.get_option("password")
    if debug:
        logging.debug('*** Connecting to CVP')
    try:
        client.connect([host],
                       user,
                       pswd,
                       protocol="https",
                       port=port,
                       )
    except CvpLoginError as e:
        module.fail_json(msg=str(e))

    if debug:
        logging.debug('*** Connected to CVP')

    return client
예제 #2
0
def connect(module):
    """
    Connects to CVP device using user provided credentials from playbook.

    Parameters
    ----------
    module : AnsibleModule
        Ansible module.

    Returns
    -------
    CvpClient
        CvpClient object with connection instantiated.
    """
    client = CvpClient()
    connection = Connection(module._socket_path)
    host = connection.get_option("host")
    port = connection.get_option("port")
    user = connection.get_option("remote_user")
    pswd = connection.get_option("password")
    try:
        client.connect(
            [host],
            user,
            pswd,
            protocol="https",
            port=port,
        )
    except CvpLoginError as e:
        module.fail_json(msg=str(e))
    return client
예제 #3
0
def connect(module):
    """
    Create a connection to CVP server to use API

    Parameters
    ----------
    module : AnsibleModule
        Object representing Ansible module structure with a CvpClient connection

    Returns
    -------
    CvpClient
        CvpClient object to manager API calls.
    """
    client = CvpClient()
    connection = Connection(module._socket_path)
    host = connection.get_option("host")
    port = connection.get_option("port")
    user = connection.get_option("remote_user")
    pswd = connection.get_option("password")
    try:
        client.connect(
            [host],
            user,
            pswd,
            protocol="https",
            port=port,
        )
    except CvpLoginError as e:
        module.fail_json(msg=str(e))

    return client
예제 #4
0
def create_ssh_connection(module):
    connection = Connection(module._socket_path)

    global _DEVICE_ZTP
    if not _DEVICE_ZTP:
        # For zeroize devices, configure authentication
        connect_ztp_device(module, connection.get_option('host'),
                           connection.get_option('remote_user'),
                           connection.get_option('password'))
        _DEVICE_ZTP = True

    return connection
예제 #5
0
파일: net_get.py 프로젝트: zealot00/ansible
    def run(self, tmp=None, task_vars=None):
        socket_path = None
        self._get_network_os(task_vars)
        persistent_connection = self._play_context.connection.split('.')[-1]

        result = super(ActionModule, self).run(task_vars=task_vars)

        if persistent_connection != 'network_cli':
            # It is supported only with network_cli
            result['failed'] = True
            result['msg'] = ('connection type %s is not valid for net_get module,'
                             ' please use fully qualified name of network_cli connection type' % self._play_context.connection)
            return result

        try:
            src = self._task.args['src']
        except KeyError as exc:
            return {'failed': True, 'msg': 'missing required argument: %s' % exc}

        # Get destination file if specified
        dest = self._task.args.get('dest')

        if dest is None:
            dest = self._get_default_dest(src)
        else:
            dest = self._handle_dest_path(dest)

        # Get proto
        proto = self._task.args.get('protocol')
        if proto is None:
            proto = 'scp'

        if socket_path is None:
            socket_path = self._connection.socket_path

        conn = Connection(socket_path)
        sock_timeout = conn.get_option('persistent_command_timeout')

        try:
            changed = self._handle_existing_file(conn, src, dest, proto, sock_timeout)
            if changed is False:
                result['changed'] = changed
                result['destination'] = dest
                return result
        except Exception as exc:
            result['msg'] = ('Warning: %s idempotency check failed. Check dest' % exc)

        try:
            conn.get_file(
                source=src, destination=dest,
                proto=proto, timeout=sock_timeout
            )
        except Exception as exc:
            result['failed'] = True
            result['msg'] = 'Exception received: %s' % exc

        result['changed'] = changed
        result['destination'] = dest
        return result
예제 #6
0
    def run(self, tmp=None, task_vars=None):
        socket_path = None
        network_os = self._get_network_os(task_vars).split('.')[-1]
        persistent_connection = self._play_context.connection.split('.')[-1]

        result = super(ActionModule, self).run(task_vars=task_vars)

        if persistent_connection != 'network_cli':
            # It is supported only with network_cli
            result['failed'] = True
            result['msg'] = (
                'connection type %s is not valid for net_put module,'
                ' please use fully qualified name of network_cli connection type'
                % self._play_context.connection)
            return result

        try:
            src = self._task.args['src']
        except KeyError as exc:
            return {
                'failed': True,
                'msg': 'missing required argument: %s' % exc
            }

        src_file_path_name = src

        # Get destination file if specified
        dest = self._task.args.get('dest')

        # Get proto
        proto = self._task.args.get('protocol')
        if proto is None:
            proto = 'scp'

        # Get mode if set
        mode = self._task.args.get('mode')
        if mode is None:
            mode = 'binary'

        if mode == 'text':
            try:
                self._handle_template(convert_data=False)
            except ValueError as exc:
                return dict(failed=True, msg=to_text(exc))

            # Now src has resolved file write to disk in current diectory for scp
            src = self._task.args.get('src')
            filename = str(uuid.uuid4())
            cwd = self._loader.get_basedir()
            output_file = os.path.join(cwd, filename)
            try:
                with open(output_file, 'wb') as f:
                    f.write(to_bytes(src, encoding='utf-8'))
            except Exception:
                os.remove(output_file)
                raise
        else:
            try:
                output_file = self._get_binary_src_file(src)
            except ValueError as exc:
                return dict(failed=True, msg=to_text(exc))

        if socket_path is None:
            socket_path = self._connection.socket_path

        conn = Connection(socket_path)
        sock_timeout = conn.get_option('persistent_command_timeout')

        if dest is None:
            dest = src_file_path_name

        try:
            changed = self._handle_existing_file(conn, output_file, dest,
                                                 proto, sock_timeout)
            if changed is False:
                result['changed'] = changed
                result['destination'] = dest
                return result
        except Exception as exc:
            result['msg'] = (
                'Warning: %s idempotency check failed. Check dest' % exc)

        try:
            conn.copy_file(source=output_file,
                           destination=dest,
                           proto=proto,
                           timeout=sock_timeout)
        except Exception as exc:
            if to_text(exc) == "No response from server":
                if network_os == 'iosxr':
                    # IOSXR sometimes closes socket prematurely after completion
                    # of file transfer
                    result[
                        'msg'] = 'Warning: iosxr scp server pre close issue. Please check dest'
            else:
                result['failed'] = True
                result['msg'] = 'Exception received: %s' % exc

        if mode == 'text':
            # Cleanup tmp file expanded wih ansible vars
            os.remove(output_file)

        result['changed'] = changed
        result['destination'] = dest
        return result
예제 #7
0
    def run(self, tmp=None, task_vars=None):
        changed = False
        socket_path = None
        network_os = self._get_network_os(task_vars).split(".")[-1]
        persistent_connection = self._play_context.connection.split(".")[-1]

        result = super(ActionModule, self).run(task_vars=task_vars)

        if persistent_connection != "network_cli":
            # It is supported only with network_cli
            result["failed"] = True
            result["msg"] = (
                "connection type %s is not valid for net_put module,"
                " please use fully qualified name of network_cli connection type"
                % self._play_context.connection
            )
            return result

        try:
            src = self._task.args["src"]
        except KeyError as exc:
            return {
                "failed": True,
                "msg": "missing required argument: %s" % exc,
            }

        src_file_path_name = src

        # Get destination file if specified
        dest = self._task.args.get("dest")

        # Get proto
        proto = self._task.args.get("protocol")
        if proto is None:
            proto = "scp"

        # Get mode if set
        mode = self._task.args.get("mode")
        if mode is None:
            mode = "binary"

        if mode == "text":
            try:
                self._handle_src_option(convert_data=False)
            except AnsibleError as exc:
                return dict(failed=True, msg=to_text(exc))

            # Now src has resolved file write to disk in current diectory for scp
            src = self._task.args.get("src")
            filename = str(uuid.uuid4())
            cwd = self._loader.get_basedir()
            output_file = os.path.join(cwd, filename)
            try:
                with open(output_file, "wb") as f:
                    f.write(to_bytes(src, encoding="utf-8"))
            except Exception:
                os.remove(output_file)
                raise
        else:
            try:
                output_file = self._get_binary_src_file(src)
            except ValueError as exc:
                return dict(failed=True, msg=to_text(exc))

        if socket_path is None:
            socket_path = self._connection.socket_path

        conn = Connection(socket_path)
        sock_timeout = conn.get_option("persistent_command_timeout")

        if dest is None:
            dest = src_file_path_name
        try:
            changed = self._handle_existing_file(
                conn, output_file, dest, proto, sock_timeout
            )
            if changed is False:
                result["changed"] = changed
                result["destination"] = dest
                if mode == "text":
                    # Cleanup tmp file expanded wih ansible vars
                    os.remove(output_file)
                return result
        except Exception as exc:
            result["msg"] = (
                "Warning: %s idempotency check failed. Check dest" % exc
            )

        try:
            conn.copy_file(
                source=output_file,
                destination=dest,
                proto=proto,
                timeout=sock_timeout,
            )
        except Exception as exc:
            if to_text(exc) == "No response from server":
                if network_os == "iosxr":
                    # IOSXR sometimes closes socket prematurely after completion
                    # of file transfer
                    result[
                        "msg"
                    ] = "Warning: iosxr scp server pre close issue. Please check dest"
            else:
                result["failed"] = True
                result["msg"] = "Exception received: %s" % exc

        if mode == "text":
            # Cleanup tmp file expanded wih ansible vars
            os.remove(output_file)

        result["changed"] = changed
        result["destination"] = dest
        return result
    def run(self, tmp=None, task_vars=None):
        del tmp  # tmp no longer has any effect

        module_name = self._task.action.split('.')[-1]
        self._config_module = True if module_name == 'nxos_config' else False
        persistent_connection = self._play_context.connection.split('.')[-1]
        warnings = []

        if (self._play_context.connection in ('httpapi', 'local') or self._task.args.get('provider', {}).get('transport') == 'nxapi') \
                and module_name in ('nxos_file_copy', 'nxos_nxapi'):
            return {
                'failed':
                True,
                'msg':
                "Transport type 'nxapi' is not valid for '%s' module." %
                (module_name)
            }

        if module_name == 'nxos_file_copy':
            self._task.args['host'] = self._play_context.remote_addr
            self._task.args['password'] = self._play_context.password
            if self._play_context.connection == 'network_cli':
                self._task.args['username'] = self._play_context.remote_user
            elif self._play_context.connection == 'local':
                self._task.args[
                    'username'] = self._play_context.connection_user

        if module_name == 'nxos_install_os':
            connection = self._connection
            if connection.transport == 'local':
                persistent_command_timeout = C.PERSISTENT_COMMAND_TIMEOUT
                persistent_connect_timeout = C.PERSISTENT_CONNECT_TIMEOUT
            else:
                persistent_command_timeout = connection.get_option(
                    'persistent_command_timeout')
                persistent_connect_timeout = connection.get_option(
                    'persistent_connect_timeout')

            display.vvvv(
                'PERSISTENT_COMMAND_TIMEOUT is %s' %
                str(persistent_command_timeout),
                self._play_context.remote_addr)
            display.vvvv(
                'PERSISTENT_CONNECT_TIMEOUT is %s' %
                str(persistent_connect_timeout),
                self._play_context.remote_addr)
            if persistent_command_timeout < 600 or persistent_connect_timeout < 600:
                msg = 'PERSISTENT_COMMAND_TIMEOUT and PERSISTENT_CONNECT_TIMEOUT'
                msg += ' must be set to 600 seconds or higher when using nxos_install_os module.'
                msg += ' Current persistent_command_timeout setting:' + str(
                    persistent_command_timeout)
                msg += ' Current persistent_connect_timeout setting:' + str(
                    persistent_connect_timeout)
                return {'failed': True, 'msg': msg}

        if persistent_connection in ('network_cli', 'httpapi'):
            provider = self._task.args.get('provider', {})
            if any(provider.values()):
                display.warning(
                    'provider is unnecessary when using %s and will be ignored'
                    % self._play_context.connection)
                del self._task.args['provider']
            if self._task.args.get('transport'):
                display.warning(
                    'transport is unnecessary when using %s and will be ignored'
                    % self._play_context.connection)
                del self._task.args['transport']

            if module_name == 'nxos_gir':
                conn = Connection(self._connection.socket_path)
                persistent_command_timeout = conn.get_option(
                    'persistent_command_timeout')
                gir_timeout = 200
                if persistent_command_timeout < gir_timeout:
                    conn.set_option('persistent_command_timeout', gir_timeout)
                    msg = "timeout value extended to %ss for nxos_gir" % gir_timeout
                    display.warning(msg)

        elif self._play_context.connection == 'local':
            provider = load_provider(nxos_provider_spec, self._task.args)
            transport = provider['transport'] or 'cli'

            display.vvvv('connection transport is %s' % transport,
                         self._play_context.remote_addr)

            if transport == 'cli':
                pc = copy.deepcopy(self._play_context)
                pc.connection = 'ansible.netcommon.network_cli'
                pc.network_os = 'cisco.nxos.nxos'
                pc.remote_addr = provider[
                    'host'] or self._play_context.remote_addr
                pc.port = int(provider['port'] or self._play_context.port
                              or 22)
                pc.remote_user = provider[
                    'username'] or self._play_context.connection_user
                pc.password = provider[
                    'password'] or self._play_context.password
                pc.private_key_file = provider[
                    'ssh_keyfile'] or self._play_context.private_key_file
                pc.become = provider['authorize'] or False
                if pc.become:
                    pc.become_method = 'enable'
                pc.become_pass = provider['auth_pass']

                connection = self._shared_loader_obj.connection_loader.get(
                    'ansible.netcommon.persistent',
                    pc,
                    sys.stdin,
                    task_uuid=self._task._uuid)

                # TODO: Remove below code after ansible minimal is cut out
                if connection is None:
                    pc.connection = 'network_cli'
                    pc.network_os = 'nxos'
                    connection = self._shared_loader_obj.connection_loader.get(
                        'persistent',
                        pc,
                        sys.stdin,
                        task_uuid=self._task._uuid)

                display.vvv(
                    'using connection plugin %s (was local)' % pc.connection,
                    pc.remote_addr)

                command_timeout = int(
                    provider['timeout']
                ) if provider['timeout'] else connection.get_option(
                    'persistent_command_timeout')
                connection.set_options(
                    direct={'persistent_command_timeout': command_timeout})

                socket_path = connection.run()
                display.vvvv('socket_path: %s' % socket_path, pc.remote_addr)
                if not socket_path:
                    return {
                        'failed':
                        True,
                        'msg':
                        'unable to open shell. Please see: ' +
                        'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell'
                    }

                task_vars['ansible_socket'] = socket_path

            else:
                self._task.args[
                    'provider'] = ActionModule.nxapi_implementation(
                        provider, self._play_context)
                warnings.append([
                    'connection local support for this module is deprecated and will be removed in version 2.14,'
                    ' use connection either httpapi or ansible.netcommon.httpapi (whichever is applicable)'
                ])
        else:
            return {
                'failed':
                True,
                'msg':
                'Connection type %s is not valid for this module' %
                self._play_context.connection
            }

        result = super(ActionModule, self).run(task_vars=task_vars)
        if warnings:
            if 'warnings' in result:
                result['warnings'].extend(warnings)
            else:
                result['warnings'] = warnings
        return result
예제 #9
0
class ActionModule(ActionBase):
    def process_playbook_values(self):
        ''' Get playbook values and perform input validation '''
        argument_spec = dict(
            vrf=dict(type='str', default='management'),
            connect_ssh_port=dict(type='int', default=22),
            file_system=dict(type='str', default='bootflash:'),
            file_pull=dict(type='bool', default=False),
            file_pull_timeout=dict(type='int', default=300),
            file_pull_compact=dict(type='bool', default=False),
            file_pull_kstack=dict(type='bool', default=False),
            local_file=dict(type='str'),
            local_file_directory=dict(type='str'),
            remote_file=dict(type='str'),
            remote_scp_server=dict(type='str'),
            remote_scp_server_user=dict(type='str'),
            remote_scp_server_password=dict(no_log=True),
        )

        playvals = {}
        # Process key value pairs from playbook task
        for key in argument_spec.keys():
            playvals[key] = self._task.args.get(
                key, argument_spec[key].get('default'))
            if playvals[key] is None:
                continue
            if argument_spec[key].get('type') is None:
                argument_spec[key]['type'] = 'str'
            type_ok = False
            type = argument_spec[key]['type']
            if type == 'str':
                if isinstance(playvals[key], six.string_types):
                    type_ok = True
            elif type == 'int':
                if isinstance(playvals[key], int):
                    type_ok = True
            elif type == 'bool':
                if isinstance(playvals[key], bool):
                    type_ok = True
            else:
                raise AnsibleError(
                    'Unrecognized type <{0}> for playbook parameter <{1}>'.
                    format(type, key))

            if not type_ok:
                raise AnsibleError(
                    'Playbook parameter <{0}> value should be of type <{1}>'.
                    format(key, type))

        # Validate playbook dependencies
        if playvals['file_pull']:
            if playvals.get('remote_file') is None:
                raise AnsibleError(
                    'Playbook parameter <remote_file> required when <file_pull> is True'
                )
            if playvals.get('remote_scp_server') is None:
                raise AnsibleError(
                    'Playbook parameter <remote_scp_server> required when <file_pull> is True'
                )

        if playvals['remote_scp_server'] or \
           playvals['remote_scp_server_user'] or \
           playvals['remote_scp_server_password']:

            if None in (playvals['remote_scp_server'],
                        playvals['remote_scp_server_user'],
                        playvals['remote_scp_server_password']):
                params = '<remote_scp_server>, <remote_scp_server_user>, ,remote_scp_server_password>'
                raise AnsibleError(
                    'Playbook parameters {0} must all be set together'.format(
                        params))

        return playvals

    def check_library_dependencies(self, file_pull):
        if file_pull:
            if not HAS_PEXPECT:
                msg = 'library pexpect is required when file_pull is True but does not appear to be '
                msg += 'installed. It can be installed using `pip install pexpect`'
                raise AnsibleError(msg)
        else:
            if paramiko is None:
                msg = 'library paramiko is required when file_pull is False but does not appear to be '
                msg += 'installed. It can be installed using `pip install paramiko`'
                raise AnsibleError(msg)

            if not HAS_SCP:
                msg = 'library scp is required when file_pull is False but does not appear to be '
                msg += 'installed. It can be installed using `pip install scp`'
                raise AnsibleError(msg)

    def md5sum_check(self, dst, file_system):
        command = 'show file {0}{1} md5sum'.format(file_system, dst)
        remote_filehash = self.conn.exec_command(command)
        remote_filehash = to_bytes(remote_filehash,
                                   errors='surrogate_or_strict')

        local_file = self.playvals['local_file']
        try:
            with open(local_file, 'rb') as f:
                filecontent = f.read()
        except (OSError, IOError) as exc:
            raise AnsibleError('Error reading the file: {0}'.format(
                to_text(exc)))

        filecontent = to_bytes(filecontent, errors='surrogate_or_strict')
        local_filehash = hashlib.md5(filecontent).hexdigest()

        if local_filehash == remote_filehash:
            return True
        else:
            return False

    def remote_file_exists(self, remote_file, file_system):
        command = 'dir {0}/{1}'.format(file_system, remote_file)
        body = self.conn.exec_command(command)
        if 'No such file' in body:
            return False
        else:
            return self.md5sum_check(remote_file, file_system)

    def verify_remote_file_exists(self, dst, file_system):
        command = 'dir {0}/{1}'.format(file_system, dst)
        body = self.conn.exec_command(command)
        if 'No such file' in body:
            return 0
        return body.split()[0].strip()

    def local_file_exists(self, file):
        return os.path.isfile(file)

    def get_flash_size(self, file_system):
        command = 'dir {0}'.format(file_system)
        body = self.conn.exec_command(command)

        match = re.search(r'(\d+) bytes free', body)
        if match:
            bytes_free = match.group(1)
            return int(bytes_free)

        match = re.search(r'No such file or directory', body)
        if match:
            raise AnsibleError(
                'Invalid nxos filesystem {0}'.format(file_system))
        else:
            raise AnsibleError(
                'Unable to determine size of filesystem {0}'.format(
                    file_system))

    def enough_space(self, file, file_system):
        flash_size = self.get_flash_size(file_system)
        file_size = os.path.getsize(file)
        if file_size > flash_size:
            return False

        return True

    def transfer_file_to_device(self, remote_file):
        timeout = self.socket_timeout
        local_file = self.playvals['local_file']
        file_system = self.playvals['file_system']
        file_size = os.path.getsize(local_file)

        if not self.enough_space(local_file, file_system):
            raise AnsibleError(
                'Could not transfer file. Not enough space on device.')

        # frp = full_remote_path, flp = full_local_path
        frp = '{0}{1}'.format(file_system, remote_file)
        flp = os.path.join(os.path.abspath(local_file))
        try:
            self.conn.copy_file(source=flp,
                                destination=frp,
                                proto='scp',
                                timeout=timeout)
        except Exception as exc:
            self.results['failed'] = True
            self.results['msg'] = ('Exception received : %s' % exc)

    def file_push(self):
        local_file = self.playvals['local_file']
        remote_file = self.playvals['remote_file'] or os.path.basename(
            local_file)
        file_system = self.playvals['file_system']

        if not self.local_file_exists(local_file):
            raise AnsibleError('Local file {0} not found'.format(local_file))

        remote_file = remote_file or os.path.basename(local_file)
        remote_exists = self.remote_file_exists(remote_file, file_system)

        if not remote_exists:
            self.results['changed'] = True
            file_exists = False
        else:
            self.results[
                'transfer_status'] = 'No Transfer: File already copied to remote device.'
            file_exists = True

        if not self.play_context.check_mode and not file_exists:
            self.transfer_file_to_device(remote_file)
            self.results[
                'transfer_status'] = 'Sent: File copied to remote device.'

        self.results['local_file'] = local_file
        if remote_file is None:
            remote_file = os.path.basename(local_file)
        self.results['remote_file'] = remote_file

    def copy_file_from_remote(self, local, local_file_directory, file_system):
        self.results['failed'] = False
        nxos_hostname = self.play_context.remote_addr
        nxos_username = self.play_context.remote_user
        nxos_password = self.play_context.password
        port = self.playvals['connect_ssh_port']

        # Build copy command components that will be used to initiate copy from the nxos device.
        cmdroot = 'copy scp://'
        ruser = self.playvals['remote_scp_server_user'] + '@'
        rserver = self.playvals['remote_scp_server']
        rfile = self.playvals['remote_file'] + ' '
        vrf = ' vrf ' + self.playvals['vrf']
        local_dir_root = '/'
        if self.playvals['file_pull_compact']:
            compact = ' compact '
        else:
            compact = ''
        if self.playvals['file_pull_kstack']:
            kstack = ' use-kstack '
        else:
            kstack = ''

        def process_outcomes(session, timeout=None):
            if timeout is None:
                timeout = 10
            outcome = {}
            outcome['user_response_required'] = False
            outcome['password_prompt_detected'] = False
            outcome['existing_file_with_same_name'] = False
            outcome['final_prompt_detected'] = False
            outcome['copy_complete'] = False
            outcome['expect_timeout'] = False
            outcome['error'] = False
            outcome['error_data'] = None

            # Possible outcomes key:
            # 0) - Are you sure you want to continue connecting (yes/no)
            # 1) - Password: or @servers's password:
            # 2) - Warning: There is already a file existing with this name. Do you want to overwrite (y/n)?[n]
            # 3) - Timeout conditions
            # 4) - No space on nxos device file_system
            # 5) - Username/Password or file permission issues
            # 6) - File does not exist on remote scp server
            # 7) - invalid nxos command
            # 8) - compact option not supported
            # 9) - compaction attempt failed
            # 10) - other failures like attempting to compact non image file
            # 11) - failure to resolve hostname
            # 12) - Too many authentication failures
            # 13) - Copy to / from this server not permitted
            # 14) - Copy completed without issues
            # 15) - nxos_router_prompt#
            # 16) - pexpect timeout
            possible_outcomes = [
                'yes', '(?i)Password', 'file existing with this name',
                'timed out', '(?i)No space.*#', '(?i)Permission denied.*#',
                '(?i)No such file.*#', '.*Invalid command.*#',
                'Compaction is not supported on this platform.*#',
                'Compact of.*failed.*#', '(?i)Failed.*#',
                '(?i)Could not resolve hostname',
                '(?i)Too many authentication failures',
                r'(?i)Copying to\/from this server name is not permitted',
                '(?i)Copy complete', r'#\s', pexpect.TIMEOUT
            ]
            index = session.expect(possible_outcomes, timeout=timeout)
            # Each index maps to items in possible_outcomes
            if index == 0:
                outcome['user_response_required'] = True
                return outcome
            elif index == 1:
                outcome['password_prompt_detected'] = True
                return outcome
            elif index == 2:
                outcome['existing_file_with_same_name'] = True
                return outcome
            elif index in [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]:
                before = session.before.strip().replace(' \x08', '')
                after = session.after.strip().replace(' \x08', '')
                outcome['error'] = True
                outcome['error_data'] = 'COMMAND {0} ERROR {1}'.format(
                    before, after)
                return outcome
            elif index == 14:
                outcome['copy_complete'] = True
                return outcome
            elif index == 15:
                outcome['final_prompt_detected'] = True
                return outcome
            elif index == 16:
                # The before property will contain all text up to the expected string pattern.
                # The after string will contain the text that was matched by the expected pattern.
                outcome['expect_timeout'] = True
                outcome[
                    'error_data'] = 'Expect Timeout error occured: BEFORE {0} AFTER {1}'.format(
                        session.before, session.after)
                return outcome
            else:
                outcome['error'] = True
                outcome[
                    'error_data'] = 'Unrecognized error occured: BEFORE {0} AFTER {1}'.format(
                        session.before, session.after)
                return outcome

            return outcome

        # Spawn pexpect connection to NX-OS device.
        nxos_session = pexpect.spawn('ssh ' + nxos_username + '@' +
                                     nxos_hostname + ' -p' + str(port))
        # There might be multiple user_response_required prompts or intermittent timeouts
        # spawning the expect session so loop up to 5 times during the spwan process.
        for connect_attempt in range(6):
            outcome = process_outcomes(nxos_session)
            if outcome['user_response_required']:
                nxos_session.sendline('yes')
                continue
            if outcome['password_prompt_detected']:
                nxos_session.sendline(nxos_password)
                continue
            if outcome['final_prompt_detected']:
                break
            if outcome['error'] or outcome['expect_timeout']:
                self.results['failed'] = True
                self.results[
                    'error_data'] = 'Failed to spawn expect session! ' + outcome[
                        'error_data']
                return
        else:
            # The before property will contain all text up to the expected string pattern.
            # The after string will contain the text that was matched by the expected pattern.
            msg = 'After {0} attempts, failed to spawn pexpect session to {1}'
            msg += 'BEFORE: {2}, AFTER: {3}'
            raise AnsibleError(
                msg.format(connect_attempt, nxos_hostname, nxos_session.before,
                           nxos_session.before))

        # Create local file directory under NX-OS filesystem if
        # local_file_directory playbook parameter is set.
        if local_file_directory:
            dir_array = local_file_directory.split('/')
            for each in dir_array:
                if each:
                    mkdir_cmd = 'mkdir ' + local_dir_root + each
                    nxos_session.sendline(mkdir_cmd)
                    outcome = process_outcomes(nxos_session)
                    if outcome['error'] or outcome['expect_timeout']:
                        self.results['mkdir_cmd'] = mkdir_cmd
                        self.results['failed'] = True
                        self.results['error_data'] = outcome['error_data']
                        return
                    local_dir_root += each + '/'

        # Initiate file copy
        copy_cmd = (cmdroot + ruser + rserver + rfile + file_system +
                    local_dir_root + local + compact + vrf + kstack)
        self.results['copy_cmd'] = copy_cmd
        nxos_session.sendline(copy_cmd)
        for copy_attempt in range(6):
            outcome = process_outcomes(nxos_session,
                                       self.playvals['file_pull_timeout'])
            if outcome['user_response_required']:
                nxos_session.sendline('yes')
                continue
            if outcome['password_prompt_detected']:
                nxos_session.sendline(
                    self.playvals['remote_scp_server_password'])
                continue
            if outcome['existing_file_with_same_name']:
                nxos_session.sendline('y')
                continue
            if outcome['copy_complete']:
                self.results[
                    'transfer_status'] = 'Received: File copied/pulled to nxos device from remote scp server.'
                break
            if outcome['error'] or outcome['expect_timeout']:
                self.results['failed'] = True
                self.results['error_data'] = outcome['error_data']
                return
        else:
            # The before property will contain all text up to the expected string pattern.
            # The after string will contain the text that was matched by the expected pattern.
            msg = 'After {0} attempts, failed to copy file to {1}'
            msg += 'BEFORE: {2}, AFTER: {3}, CMD: {4}'
            raise AnsibleError(
                msg.format(copy_attempt, nxos_hostname, nxos_session.before,
                           nxos_session.before, copy_cmd))

    def file_pull(self):
        local_file = self.playvals['local_file']
        remote_file = self.playvals['remote_file']
        file_system = self.playvals['file_system']
        # Note: This is the local file directory on the remote nxos device.
        local_file_dir = self.playvals['local_file_directory']

        local_file = local_file or self.playvals['remote_file'].split('/')[-1]

        if not self.play_context.check_mode:
            self.copy_file_from_remote(local_file, local_file_dir, file_system)

        if not self.results['failed']:
            self.results['changed'] = True
            self.results['remote_file'] = remote_file
            if local_file_dir:
                dir = local_file_dir
            else:
                dir = ''
            self.results['local_file'] = file_system + dir + '/' + local_file
            self.results['remote_scp_server'] = self.playvals[
                'remote_scp_server']

    # This is the main run method for the action plugin to copy files
    def run(self, tmp=None, task_vars=None):
        socket_path = None
        self.play_context = copy.deepcopy(self._play_context)
        self.results = super(ActionModule, self).run(task_vars=task_vars)

        if self.play_context.connection != 'network_cli':
            # Plugin is supported only with network_cli
            self.results['failed'] = True
            self.results['msg'] = ('Connection type must be <network_cli>')
            return self.results

        # Get playbook values
        self.playvals = self.process_playbook_values()

        file_pull = self.playvals['file_pull']
        self.check_library_dependencies(file_pull)

        if socket_path is None:
            socket_path = self._connection.socket_path
        self.conn = Connection(socket_path)
        self.socket_timeout = self.conn.get_option(
            'persistent_command_timeout')

        # This action plugin support two modes of operation.
        # - file_pull is False - Push files from the ansible controller to nxos switch.
        # - file_pull is True - Initiate copy from the device to pull files to the nxos switch.
        self.results['transfer_status'] = 'No Transfer'
        self.results['file_system'] = self.playvals['file_system']
        if file_pull:
            self.file_pull()
        else:
            self.file_push()

        return self.results
class ActionModule(ActionBase):
    def process_playbook_values(self):
        """ Get playbook values and perform input validation """
        argument_spec = dict(
            vrf=dict(type="str", default="management"),
            connect_ssh_port=dict(type="int", default=22),
            file_system=dict(type="str", default="bootflash:"),
            file_pull=dict(type="bool", default=False),
            file_pull_timeout=dict(type="int", default=300),
            file_pull_protocol=dict(
                type="str",
                default="scp",
                choices=["scp", "sftp", "http", "https", "tftp", "ftp"],
            ),
            file_pull_compact=dict(type="bool", default=False),
            file_pull_kstack=dict(type="bool", default=False),
            local_file=dict(type="path"),
            local_file_directory=dict(type="path"),
            remote_file=dict(type="path"),
            remote_scp_server=dict(type="str"),
            remote_scp_server_user=dict(type="str"),
            remote_scp_server_password=dict(no_log=True),
        )

        playvals = {}
        # Process key value pairs from playbook task
        for key in argument_spec.keys():
            playvals[key] = self._task.args.get(
                key, argument_spec[key].get("default"))
            if playvals[key] is None:
                continue

            option_type = argument_spec[key].get("type", "str")
            try:
                if option_type == "str":
                    playvals[key] = validation.check_type_str(playvals[key])
                elif option_type == "int":
                    playvals[key] = validation.check_type_int(playvals[key])
                elif option_type == "bool":
                    playvals[key] = validation.check_type_bool(playvals[key])
                elif option_type == "path":
                    playvals[key] = validation.check_type_path(playvals[key])
                else:
                    raise AnsibleError(
                        "Unrecognized type <{0}> for playbook parameter <{1}>".
                        format(option_type, key))

            except (TypeError, ValueError) as e:
                raise AnsibleError(
                    "argument %s is of type %s and we were unable to convert to %s: %s"
                    % (key, type(playvals[key]), option_type, to_native(e)))

            if "choices" in argument_spec[key] and playvals[
                    key] not in argument_spec[key].get("choices"):
                raise AnsibleError(
                    "argument {0} with value {1} is not valid. Allowed values are {2}"
                    .format(
                        key,
                        playvals[key],
                        ", ".join(argument_spec[key].get("choices")),
                    ))

        # Validate playbook dependencies
        if playvals["file_pull"]:
            if playvals.get("remote_file") is None:
                raise AnsibleError(
                    "Playbook parameter <remote_file> required when <file_pull> is True"
                )
            if playvals.get("remote_scp_server") is None:
                raise AnsibleError(
                    "Playbook parameter <remote_scp_server> required when <file_pull> is True"
                )

        if playvals["remote_scp_server"] or playvals["remote_scp_server_user"]:

            if None in (
                    playvals["remote_scp_server"],
                    playvals["remote_scp_server_user"],
            ):
                params = "<remote_scp_server>, <remote_scp_server_user>"
                raise AnsibleError(
                    "Playbook parameters {0} must be set together".format(
                        params))

        return playvals

    def check_library_dependencies(self, file_pull):
        if file_pull:
            if not HAS_PEXPECT:
                msg = "library pexpect is required when file_pull is True but does not appear to be "
                msg += "installed. It can be installed using `pip install pexpect`"
                raise AnsibleError(msg)
        else:
            if paramiko is None:
                msg = "library paramiko is required when file_pull is False but does not appear to be "
                msg += "installed. It can be installed using `pip install paramiko`"
                raise AnsibleError(msg)

            if not HAS_SCP:
                msg = "library scp is required when file_pull is False but does not appear to be "
                msg += "installed. It can be installed using `pip install scp`"
                raise AnsibleError(msg)

    def md5sum_check(self, dst, file_system):
        command = "show file {0}{1} md5sum".format(file_system, dst)
        remote_filehash = self.conn.exec_command(command)
        remote_filehash = to_bytes(remote_filehash,
                                   errors="surrogate_or_strict")

        local_file = self.playvals["local_file"]
        try:
            with open(local_file, "rb") as f:
                filecontent = f.read()
        except (OSError, IOError) as exc:
            raise AnsibleError("Error reading the file: {0}".format(
                to_text(exc)))

        filecontent = to_bytes(filecontent, errors="surrogate_or_strict")
        local_filehash = hashlib.md5(filecontent).hexdigest()

        decoded_rhash = remote_filehash.decode("UTF-8")

        if local_filehash == decoded_rhash:
            return True
        else:
            return False

    def remote_file_exists(self, remote_file, file_system):
        command = "dir {0}/{1}".format(file_system, remote_file)
        body = self.conn.exec_command(command)

        if "No such file" in body:
            return False
        else:
            return self.md5sum_check(remote_file, file_system)

    def verify_remote_file_exists(self, dst, file_system):
        command = "dir {0}/{1}".format(file_system, dst)
        body = self.conn.exec_command(command)
        if "No such file" in body:
            return 0
        return body.split()[0].strip()

    def local_file_exists(self, file):
        return os.path.isfile(file)

    def get_flash_size(self, file_system):
        command = "dir {0}".format(file_system)
        body = self.conn.exec_command(command)

        match = re.search(r"(\d+) bytes free", body)
        if match:
            bytes_free = match.group(1)
            return int(bytes_free)

        match = re.search(r"No such file or directory", body)
        if match:
            raise AnsibleError(
                "Invalid nxos filesystem {0}".format(file_system))
        else:
            raise AnsibleError(
                "Unable to determine size of filesystem {0}".format(
                    file_system))

    def enough_space(self, file, file_system):
        flash_size = self.get_flash_size(file_system)
        file_size = os.path.getsize(file)
        if file_size > flash_size:
            return False

        return True

    def transfer_file_to_device(self, remote_file):
        timeout = self.socket_timeout
        local_file = self.playvals["local_file"]
        file_system = self.playvals["file_system"]

        if not self.enough_space(local_file, file_system):
            raise AnsibleError(
                "Could not transfer file. Not enough space on device.")

        # frp = full_remote_path, flp = full_local_path
        frp = "{0}{1}".format(file_system, remote_file)
        flp = os.path.join(os.path.abspath(local_file))
        try:
            self.conn.copy_file(source=flp,
                                destination=frp,
                                proto="scp",
                                timeout=timeout)
        except Exception as exc:
            self.results["failed"] = True
            self.results["msg"] = "Exception received : %s" % exc

    def file_push(self):
        local_file = self.playvals["local_file"]
        remote_file = self.playvals["remote_file"] or os.path.basename(
            local_file)
        file_system = self.playvals["file_system"]

        if not self.local_file_exists(local_file):
            raise AnsibleError("Local file {0} not found".format(local_file))

        remote_file = remote_file or os.path.basename(local_file)
        remote_exists = self.remote_file_exists(remote_file, file_system)

        if not remote_exists:
            self.results["changed"] = True
            file_exists = False
        else:
            self.results[
                "transfer_status"] = "No Transfer: File already copied to remote device."
            file_exists = True

        if not self.play_context.check_mode and not file_exists:
            self.transfer_file_to_device(remote_file)
            self.results[
                "transfer_status"] = "Sent: File copied to remote device."

        self.results["local_file"] = local_file
        if remote_file is None:
            remote_file = os.path.basename(local_file)
        self.results["remote_file"] = remote_file

    def copy_file_from_remote(self, local, local_file_directory, file_system):
        self.results["failed"] = False
        nxos_hostname = self.play_context.remote_addr
        nxos_username = self.play_context.remote_user
        nxos_password = self.play_context.password or ""
        port = self.playvals["connect_ssh_port"]

        # Build copy command components that will be used to initiate copy from the nxos device.
        cmdroot = "copy " + self.playvals["file_pull_protocol"] + "://"
        ruser = self.playvals["remote_scp_server_user"] + "@"
        rserver = self.playvals["remote_scp_server"]
        rfile = self.playvals["remote_file"] + " "
        vrf = " vrf " + self.playvals["vrf"]
        local_dir_root = "/"
        if self.playvals["file_pull_compact"]:
            compact = " compact "
        else:
            compact = ""
        if self.playvals["file_pull_kstack"]:
            kstack = " use-kstack "
        else:
            kstack = ""

        def process_outcomes(session, timeout=None):
            if timeout is None:
                timeout = 10
            outcome = {}
            outcome["user_response_required"] = False
            outcome["password_prompt_detected"] = False
            outcome["existing_file_with_same_name"] = False
            outcome["final_prompt_detected"] = False
            outcome["copy_complete"] = False
            outcome["expect_timeout"] = False
            outcome["error"] = False
            outcome["error_data"] = None

            # Possible outcomes key:
            # 0) - Are you sure you want to continue connecting (yes/no)
            # 1) - Password: or @servers's password:
            # 2) - Warning: There is already a file existing with this name. Do you want to overwrite (y/n)?[n]
            # 3) - Timeout conditions
            # 4) - No space on nxos device file_system
            # 5) - Username/Password or file permission issues
            # 6) - File does not exist on remote scp server
            # 7) - invalid nxos command
            # 8) - compact option not supported
            # 9) - compaction attempt failed
            # 10) - other failures like attempting to compact non image file
            # 11) - failure to resolve hostname
            # 12) - Too many authentication failures
            # 13) - Copy to / from this server not permitted
            # 14) - Copy completed without issues
            # 15) - nxos_router_prompt#
            # 16) - pexpect timeout
            possible_outcomes = [
                r"sure you want to continue connecting \(yes/no\)\? ",
                "(?i)Password: "******"file existing with this name",
                "timed out",
                "(?i)No space.*#",
                "(?i)Permission denied.*#",
                "(?i)No such file.*#",
                ".*Invalid command.*#",
                "Compaction is not supported on this platform.*#",
                "Compact of.*failed.*#",
                "(?i)Failed.*#",
                "(?i)Could not resolve hostname",
                "(?i)Too many authentication failures",
                r"(?i)Copying to\/from this server name is not permitted",
                "(?i)Copy complete",
                r"#\s",
                pexpect.TIMEOUT,
            ]
            index = session.expect(possible_outcomes, timeout=timeout)
            # Each index maps to items in possible_outcomes
            if index == 0:
                outcome["user_response_required"] = True
                return outcome
            elif index == 1:
                outcome["password_prompt_detected"] = True
                return outcome
            elif index == 2:
                outcome["existing_file_with_same_name"] = True
                return outcome
            elif index in [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]:
                decoded_before = session.before.decode("UTF-8")
                decoded_after = session.after.decode("UTF-8")
                before = decoded_before.strip().replace(" \x08", "")
                after = decoded_after.strip().replace(" \x08", "")
                outcome["error"] = True
                outcome["error_data"] = "COMMAND {0} ERROR {1}".format(
                    before, after)
                return outcome
            elif index == 14:
                outcome["copy_complete"] = True
                return outcome
            elif index == 15:
                outcome["final_prompt_detected"] = True
                return outcome
            elif index == 16:
                # The before property will contain all text up to the expected string pattern.
                # The after string will contain the text that was matched by the expected pattern.
                outcome["expect_timeout"] = True
                outcome[
                    "error_data"] = "Expect Timeout error occurred: BEFORE {0} AFTER {1}".format(
                        session.before, session.after)
                return outcome
            else:
                outcome["error"] = True
                outcome[
                    "error_data"] = "Unrecognized error occurred: BEFORE {0} AFTER {1}".format(
                        session.before, session.after)
                return outcome

            return outcome

        # Spawn pexpect connection to NX-OS device.
        nxos_session = pexpect.spawn("ssh " + nxos_username + "@" +
                                     nxos_hostname + " -p" + str(port))
        # There might be multiple user_response_required prompts or intermittent timeouts
        # spawning the expect session so loop up to 24 times during the spawn process.
        max_attempts = 24
        for connect_attempt in range(max_attempts):
            outcome = process_outcomes(nxos_session)
            if outcome["user_response_required"]:
                nxos_session.sendline("yes")
                continue
            if outcome["password_prompt_detected"]:
                time.sleep(3)
                nxos_session.sendline(nxos_password)
                continue
            if outcome["final_prompt_detected"]:
                break
            if outcome["error"] or outcome["expect_timeout"]:
                # Error encountered, try to spawn expect session n more times up to max_attempts - 1
                if connect_attempt < max_attempts:
                    outcome["error"] = False
                    outcome["expect_timeout"] = False
                    nxos_session.close()
                    nxos_session = pexpect.spawn("ssh " + nxos_username + "@" +
                                                 nxos_hostname + " -p" +
                                                 str(port))
                    continue
                self.results["failed"] = True
                outcome["error_data"] = re.sub(nxos_password, "",
                                               outcome["error_data"])
                self.results["error_data"] = (
                    "Failed to spawn expect session! " + outcome["error_data"])
                nxos_session.close()
                return
        else:
            # The before property will contain all text up to the expected string pattern.
            # The after string will contain the text that was matched by the expected pattern.
            msg = "After {0} attempts, failed to spawn pexpect session to {1}"
            msg += "BEFORE: {2}, AFTER: {3}"
            error_msg = msg.format(
                connect_attempt,
                nxos_hostname,
                nxos_session.before,
                nxos_session.after,
            )
            re.sub(nxos_password, "", error_msg)
            nxos_session.close()
            raise AnsibleError(error_msg)

        # Create local file directory under NX-OS filesystem if
        # local_file_directory playbook parameter is set.
        if local_file_directory:
            dir_array = local_file_directory.split("/")
            for each in dir_array:
                if each:
                    mkdir_cmd = "mkdir " + local_dir_root + each
                    nxos_session.sendline(mkdir_cmd)
                    outcome = process_outcomes(nxos_session)
                    if outcome["error"] or outcome["expect_timeout"]:
                        self.results["mkdir_cmd"] = mkdir_cmd
                        self.results["failed"] = True
                        outcome["error_data"] = re.sub(nxos_password, "",
                                                       outcome["error_data"])
                        self.results["error_data"] = outcome["error_data"]
                        return
                    local_dir_root += each + "/"

        # Initiate file copy
        copy_cmd = (cmdroot + ruser + rserver + rfile + file_system +
                    local_dir_root + local + compact + vrf + kstack)
        self.results["copy_cmd"] = copy_cmd
        nxos_session.sendline(copy_cmd)
        for copy_attempt in range(6):
            outcome = process_outcomes(nxos_session,
                                       self.playvals["file_pull_timeout"])
            if outcome["user_response_required"]:
                nxos_session.sendline("yes")
                continue
            if outcome["password_prompt_detected"]:
                if self.playvals.get("remote_scp_server_password"):
                    nxos_session.sendline(
                        self.playvals["remote_scp_server_password"])
                else:
                    err_msg = "Remote scp server {0} requires a password.".format(
                        rserver)
                    err_msg += " Set the <remote_scp_server_password> playbook parameter or configure nxos device for passwordless scp"
                    raise AnsibleError(err_msg)
                continue
            if outcome["existing_file_with_same_name"]:
                nxos_session.sendline("y")
                continue
            if outcome["copy_complete"]:
                self.results[
                    "transfer_status"] = "Received: File copied/pulled to nxos device from remote scp server."
                break
            if outcome["error"] or outcome["expect_timeout"]:
                self.results["failed"] = True
                outcome["error_data"] = re.sub(nxos_password, "",
                                               outcome["error_data"])
                if self.playvals.get("remote_scp_server_password"):
                    outcome["error_data"] = re.sub(
                        self.playvals["remote_scp_server_password"],
                        "",
                        outcome["error_data"],
                    )
                self.results["error_data"] = outcome["error_data"]
                nxos_session.close()
                return
        else:
            # The before property will contain all text up to the expected string pattern.
            # The after string will contain the text that was matched by the expected pattern.
            msg = "After {0} attempts, failed to copy file to {1}"
            msg += "BEFORE: {2}, AFTER: {3}, CMD: {4}"
            error_msg = msg.format(
                copy_attempt,
                nxos_hostname,
                nxos_session.before,
                nxos_session.before,
                copy_cmd,
            )
            re.sub(nxos_password, "", error_msg)
            if self.playvals.get("remote_scp_server_password"):
                re.sub(self.playvals["remote_scp_server_password"], "",
                       error_msg)
            nxos_session.close()
            raise AnsibleError(error_msg)

        nxos_session.close()

    def file_pull(self):
        local_file = self.playvals["local_file"]
        remote_file = self.playvals["remote_file"]
        file_system = self.playvals["file_system"]
        # Note: This is the local file directory on the remote nxos device.
        local_file_dir = self.playvals["local_file_directory"]

        local_file = local_file or self.playvals["remote_file"].split("/")[-1]

        if not self.play_context.check_mode:
            self.copy_file_from_remote(local_file, local_file_dir, file_system)

        if not self.results["failed"]:
            self.results["changed"] = True
            self.results["remote_file"] = remote_file
            if local_file_dir:
                dir = local_file_dir
            else:
                dir = ""
            self.results["local_file"] = file_system + dir + "/" + local_file
            self.results["remote_scp_server"] = self.playvals[
                "remote_scp_server"]

    # This is the main run method for the action plugin to copy files
    def run(self, tmp=None, task_vars=None):
        socket_path = None
        self.play_context = copy.deepcopy(self._play_context)
        self.results = super(ActionModule, self).run(task_vars=task_vars)

        if self.play_context.connection.split(".")[-1] != "network_cli":
            # Plugin is supported only with network_cli
            self.results["failed"] = True
            self.results["msg"] = (
                "Connection type must be fully qualified name for network_cli connection type, got %s"
                % self.play_context.connection)
            return self.results

        # Get playbook values
        self.playvals = self.process_playbook_values()

        file_pull = self.playvals["file_pull"]
        self.check_library_dependencies(file_pull)

        if socket_path is None:
            socket_path = self._connection.socket_path
        self.conn = Connection(socket_path)

        # Call get_capabilities() to start the connection to the device.
        self.conn.get_capabilities()

        self.socket_timeout = self.conn.get_option(
            "persistent_command_timeout")

        # This action plugin support two modes of operation.
        # - file_pull is False - Push files from the ansible controller to nxos switch.
        # - file_pull is True - Initiate copy from the device to pull files to the nxos switch.
        self.results["transfer_status"] = "No Transfer"
        self.results["file_system"] = self.playvals["file_system"]
        if file_pull:
            self.file_pull()
        else:
            self.file_push()

        return self.results
예제 #11
0
    def run(self, tmp=None, task_vars=None):
        del tmp  # tmp no longer has any effect

        module_name = self._task.action.split(".")[-1]
        self._config_module = (True if module_name
                               in ["nxos_config", "config"] else False)
        persistent_connection = self._play_context.connection.split(".")[-1]

        warnings = []

        if (self._play_context.connection in ("httpapi", "local")
                or self._task.args.get("provider", {}).get("transport")
                == "nxapi") and module_name in ("nxos_file_copy",
                                                "nxos_nxapi"):
            return {
                "failed":
                True,
                "msg":
                "Transport type 'nxapi' is not valid for '%s' module." %
                (module_name),
            }

        if module_name == "nxos_file_copy":
            self._task.args["host"] = self._play_context.remote_addr
            self._task.args["password"] = self._play_context.password
            if self._play_context.connection == "network_cli":
                self._task.args["username"] = self._play_context.remote_user
            elif self._play_context.connection == "local":
                self._task.args[
                    "username"] = self._play_context.connection_user

        if module_name == "nxos_install_os":
            connection = self._connection
            if connection.transport == "local":
                persistent_command_timeout = C.PERSISTENT_COMMAND_TIMEOUT
                persistent_connect_timeout = C.PERSISTENT_CONNECT_TIMEOUT
            else:
                persistent_command_timeout = connection.get_option(
                    "persistent_command_timeout")
                persistent_connect_timeout = connection.get_option(
                    "persistent_connect_timeout")

            display.vvvv(
                "PERSISTENT_COMMAND_TIMEOUT is %s" %
                str(persistent_command_timeout),
                self._play_context.remote_addr,
            )
            display.vvvv(
                "PERSISTENT_CONNECT_TIMEOUT is %s" %
                str(persistent_connect_timeout),
                self._play_context.remote_addr,
            )
            if (persistent_command_timeout < 600
                    or persistent_connect_timeout < 600):
                msg = (
                    "PERSISTENT_COMMAND_TIMEOUT and PERSISTENT_CONNECT_TIMEOUT"
                )
                msg += " must be set to 600 seconds or higher when using nxos_install_os module."
                msg += " Current persistent_command_timeout setting:" + str(
                    persistent_command_timeout)
                msg += " Current persistent_connect_timeout setting:" + str(
                    persistent_connect_timeout)
                return {"failed": True, "msg": msg}

        if persistent_connection in ("network_cli", "httpapi"):
            provider = self._task.args.get("provider", {})
            if any(provider.values()):
                display.warning(
                    "provider is unnecessary when using %s and will be ignored"
                    % self._play_context.connection)
                del self._task.args["provider"]

            if module_name == "nxos_gir":
                conn = Connection(self._connection.socket_path)
                persistent_command_timeout = conn.get_option(
                    "persistent_command_timeout")
                gir_timeout = 200
                if persistent_command_timeout < gir_timeout:
                    conn.set_option("persistent_command_timeout", gir_timeout)
                    msg = ("timeout value extended to %ss for nxos_gir" %
                           gir_timeout)
                    display.warning(msg)

        elif self._play_context.connection == "local":
            provider = load_provider(nxos_provider_spec, self._task.args)
            transport = provider["transport"] or "cli"

            display.vvvv(
                "connection transport is %s" % transport,
                self._play_context.remote_addr,
            )

            if transport == "cli":
                pc = copy.deepcopy(self._play_context)
                pc.connection = "ansible.netcommon.network_cli"
                pc.network_os = "cisco.nxos.nxos"
                pc.remote_addr = (provider["host"]
                                  or self._play_context.remote_addr)
                pc.port = int(provider["port"] or self._play_context.port
                              or 22)
                pc.remote_user = (provider["username"]
                                  or self._play_context.connection_user)
                pc.password = (provider["password"]
                               or self._play_context.password)
                pc.private_key_file = (provider["ssh_keyfile"]
                                       or self._play_context.private_key_file)
                pc.become = provider["authorize"] or False
                if pc.become:
                    pc.become_method = "enable"
                pc.become_pass = provider["auth_pass"]

                connection = self._shared_loader_obj.connection_loader.get(
                    "ansible.netcommon.persistent",
                    pc,
                    sys.stdin,
                    task_uuid=self._task._uuid,
                )

                # TODO: Remove below code after ansible minimal is cut out
                if connection is None:
                    pc.connection = "network_cli"
                    pc.network_os = "nxos"
                    connection = self._shared_loader_obj.connection_loader.get(
                        "persistent",
                        pc,
                        sys.stdin,
                        task_uuid=self._task._uuid)

                display.vvv(
                    "using connection plugin %s (was local)" % pc.connection,
                    pc.remote_addr,
                )

                command_timeout = (
                    int(provider["timeout"]) if provider["timeout"] else
                    connection.get_option("persistent_command_timeout"))
                connection.set_options(
                    direct={"persistent_command_timeout": command_timeout})

                socket_path = connection.run()
                display.vvvv("socket_path: %s" % socket_path, pc.remote_addr)
                if not socket_path:
                    return {
                        "failed":
                        True,
                        "msg":
                        "unable to open shell. Please see: " +
                        "https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell",
                    }

                task_vars["ansible_socket"] = socket_path

            else:
                self._task.args[
                    "provider"] = ActionModule.nxapi_implementation(
                        provider, self._play_context)
                warnings.append([
                    "connection local support for this module is deprecated and will be removed in version 2.14,"
                    " use connection either httpapi or ansible.netcommon.httpapi (whichever is applicable)"
                ])
        else:
            return {
                "failed":
                True,
                "msg":
                "Connection type %s is not valid for this module" %
                self._play_context.connection,
            }

        result = super(ActionModule, self).run(task_vars=task_vars)
        if warnings:
            if "warnings" in result:
                result["warnings"].extend(warnings)
            else:
                result["warnings"] = warnings
        return result
예제 #12
0
    def run(self, tmp=None, task_vars=None):
        changed = False
        socket_path = None
        self._get_network_os(task_vars)
        persistent_connection = self._play_context.connection.split(".")[-1]

        result = super(ActionModule, self).run(task_vars=task_vars)

        if persistent_connection != "network_cli":
            # It is supported only with network_cli
            result["failed"] = True
            result["msg"] = (
                "connection type %s is not valid for net_get module,"
                " please use fully qualified name of network_cli connection type"
                % self._play_context.connection)
            return result

        try:
            src = self._task.args["src"]
        except KeyError as exc:
            return {
                "failed": True,
                "msg": "missing required argument: %s" % exc,
            }

        # Get destination file if specified
        dest = self._task.args.get("dest")

        if dest is None:
            dest = self._get_default_dest(src)
        else:
            dest = self._handle_dest_path(dest)

        # Get proto
        proto = self._task.args.get("protocol")
        if proto is None:
            proto = "scp"

        if socket_path is None:
            socket_path = self._connection.socket_path

        conn = Connection(socket_path)
        sock_timeout = conn.get_option("persistent_command_timeout")

        try:
            changed = self._handle_existing_file(conn, src, dest, proto,
                                                 sock_timeout)
            if changed is False:
                result["changed"] = changed
                result["destination"] = dest
                return result
        except Exception as exc:
            result["msg"] = (
                "Warning: %s idempotency check failed. Check dest" % exc)

        try:
            conn.get_file(source=src,
                          destination=dest,
                          proto=proto,
                          timeout=sock_timeout)
        except Exception as exc:
            result["failed"] = True
            result["msg"] = "Exception received: %s" % exc

        result["changed"] = changed
        result["destination"] = dest
        return result
예제 #13
0
    def run(self, tmp=None, task_vars=None):
        socket_path = None
        play_context = copy.deepcopy(self._play_context)
        play_context.network_os = self._get_network_os(task_vars)

        result = super(ActionModule, self).run(task_vars=task_vars)

        if play_context.connection != 'network_cli':
            # It is supported only with network_cli
            result['failed'] = True
            result['msg'] = ('please use network_cli connection type for net_put module')
            return result

        try:
            src = self._task.args.get('src')
        except KeyError as exc:
            return {'failed': True, 'msg': 'missing required argument: %s' % exc}

        src_file_path_name = src

        # Get destination file if specified
        dest = self._task.args.get('dest')

        # Get proto
        proto = self._task.args.get('protocol')
        if proto is None:
            proto = 'scp'

        # Get mode if set
        mode = self._task.args.get('mode')
        if mode is None:
            mode = 'binary'

        if mode == 'template':
            try:
                self._handle_template()
            except ValueError as exc:
                return dict(failed=True, msg=to_text(exc))

            # Now src has resolved file write to disk in current diectory for scp
            src = self._task.args.get('src')
            filename = str(uuid.uuid4())
            cwd = self._loader.get_basedir()
            output_file = cwd + '/' + filename
            with open(output_file, 'w') as f:
                f.write(src)
        else:
            try:
                output_file = self._get_binary_src_file(src)
            except ValueError as exc:
                return dict(failed=True, msg=to_text(exc))

        if socket_path is None:
            socket_path = self._connection.socket_path

        conn = Connection(socket_path)
        sock_timeout = conn.get_option('persistent_command_timeout')

        if dest is None:
            dest = src_file_path_name
        try:
            out = conn.copy_file(
                source=output_file, destination=dest,
                proto=proto, timeout=sock_timeout
            )
        except Exception as exc:
            if to_text(exc) == "No response from server":
                if play_context.network_os == 'iosxr':
                    # IOSXR sometimes closes socket prematurely after completion
                    # of file transfer
                    result['msg'] = 'Warning: iosxr scp server pre close issue. Please check dest'
            else:
                result['failed'] = True
                result['msg'] = ('Exception received : %s' % exc)

        if mode == 'template':
            # Cleanup tmp file expanded wih ansible vars
            os.remove(output_file)

        result['changed'] = True
        return result