mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-05-03 15:51:30 -07:00
This completes the refactor of the nxos modules to use the persistent connection. It also updates all of the nxos modules to use the new connection module and preserves use of nxapi as well.
253 lines
7.4 KiB
Python
253 lines
7.4 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# 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/>.
|
|
#
|
|
|
|
ANSIBLE_METADATA = {'status': ['preview'],
|
|
'supported_by': 'community',
|
|
'version': '1.0'}
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: nxos_file_copy
|
|
version_added: "2.2"
|
|
short_description: Copy a file to a remote NXOS device over SCP.
|
|
description:
|
|
- Copy a file to the flash (or bootflash) remote network device
|
|
on NXOS devices.
|
|
author:
|
|
- Jason Edelman (@jedelman8)
|
|
- Gabriele Gerbino (@GGabriele)
|
|
notes:
|
|
- The feature must be enabled with feature scp-server.
|
|
- If the file is already present (md5 sums match), no transfer will
|
|
take place.
|
|
- Check mode will tell you if the file would be copied.
|
|
options:
|
|
local_file:
|
|
description:
|
|
- Path to local file. Local directory must exist.
|
|
required: true
|
|
remote_file:
|
|
description:
|
|
- Remote file path of the copy. Remote directories must exist.
|
|
If omitted, the name of the local file will be used.
|
|
required: false
|
|
default: null
|
|
file_system:
|
|
description:
|
|
- The remote file system of the device. If omitted,
|
|
devices that support a file_system parameter will use
|
|
their default values.
|
|
required: false
|
|
default: null
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
- nxos_file_copy:
|
|
local_file: "./test_file.txt"
|
|
username: "{{ un }}"
|
|
password: "{{ pwd }}"
|
|
host: "{{ inventory_hostname }}"
|
|
'''
|
|
|
|
RETURN = '''
|
|
transfer_status:
|
|
description: Whether a file was transferred. "No Transfer" or "Sent".
|
|
returned: success
|
|
type: string
|
|
sample: 'Sent'
|
|
local_file:
|
|
description: The path of the local file.
|
|
returned: success
|
|
type: string
|
|
sample: '/path/to/local/file'
|
|
remote_file:
|
|
description: The path of the remote file.
|
|
returned: success
|
|
type: string
|
|
sample: '/path/to/remote/file'
|
|
'''
|
|
import os
|
|
import re
|
|
import time
|
|
|
|
import paramiko
|
|
|
|
from ansible.module_utils.nxos import run_commands
|
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
try:
|
|
from scp import SCPClient
|
|
HAS_SCP = True
|
|
except ImportError:
|
|
HAS_SCP = False
|
|
|
|
def execute_show_command(command, module, command_type='cli_show'):
|
|
if module.params['transport'] == 'cli':
|
|
cmds = [command]
|
|
body = run_commands(module, cmds)
|
|
elif module.params['transport'] == 'nxapi':
|
|
cmds = [command]
|
|
body = run_commands(module, cmds)
|
|
|
|
return body
|
|
|
|
|
|
def remote_file_exists(module, dst, file_system='bootflash:'):
|
|
command = 'dir {0}/{1}'.format(file_system, dst)
|
|
body = execute_show_command(command, module, command_type='cli_show_ascii')
|
|
if 'No such file' in body[0]:
|
|
return False
|
|
return True
|
|
|
|
|
|
def verify_remote_file_exists(module, dst, file_system='bootflash:'):
|
|
command = 'dir {0}/{1}'.format(file_system, dst)
|
|
body = execute_show_command(command, module, command_type='cli_show_ascii')
|
|
if 'No such file' in body[0]:
|
|
return 0
|
|
return body[0].split()[0].strip()
|
|
|
|
|
|
def local_file_exists(module):
|
|
return os.path.isfile(module.params['local_file'])
|
|
|
|
|
|
def get_flash_size(module):
|
|
command = 'dir {}'.format(module.params['file_system'])
|
|
body = execute_show_command(command, module, command_type='cli_show_ascii')
|
|
|
|
match = re.search(r'(\d+) bytes free', body[0])
|
|
bytes_free = match.group(1)
|
|
|
|
return int(bytes_free)
|
|
|
|
|
|
def enough_space(module):
|
|
flash_size = get_flash_size(module)
|
|
file_size = os.path.getsize(module.params['local_file'])
|
|
if file_size > flash_size:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def transfer_file(module, dest):
|
|
file_size = os.path.getsize(module.params['local_file'])
|
|
|
|
if not local_file_exists(module):
|
|
module.fail_json(msg='Could not transfer file. Local file doesn\'t exist.')
|
|
|
|
if not enough_space(module):
|
|
module.fail_json(msg='Could not transfer file. Not enough space on device.')
|
|
|
|
hostname = module.params['host']
|
|
username = module.params['username']
|
|
password = module.params['password']
|
|
|
|
ssh = paramiko.SSHClient()
|
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
ssh.connect(
|
|
hostname=hostname,
|
|
username=username,
|
|
password=password)
|
|
|
|
full_remote_path = '{}{}'.format(module.params['file_system'], dest)
|
|
scp = SCPClient(ssh.get_transport())
|
|
try:
|
|
scp.put(module.params['local_file'], full_remote_path)
|
|
except:
|
|
time.sleep(10)
|
|
temp_size = verify_remote_file_exists(
|
|
module, dest, file_system=module.params['file_system'])
|
|
if int(temp_size) == int(file_size):
|
|
pass
|
|
else:
|
|
module.fail_json(msg='Could not transfer file. There was an error '
|
|
'during transfer. Please make sure remote '
|
|
'permissions are set.', temp_size=temp_size,
|
|
file_size=file_size)
|
|
scp.close()
|
|
return True
|
|
|
|
|
|
def main():
|
|
argument_spec = dict(
|
|
local_file=dict(required=True),
|
|
remote_file=dict(required=False),
|
|
file_system=dict(required=False, default='bootflash:'),
|
|
include_defaults=dict(default=True),
|
|
config=dict(),
|
|
save=dict(type='bool', default=False)
|
|
)
|
|
|
|
argument_spec.update(nxos_argument_spec)
|
|
|
|
module = AnsibleModule(argument_spec=argument_spec,
|
|
supports_check_mode=True)
|
|
|
|
if not HAS_SCP:
|
|
module.fail_json(
|
|
msg='library scp is required but does not appear to be '
|
|
'installed. It can be installed using `pip install scp`'
|
|
)
|
|
|
|
warnings = list()
|
|
check_args(module, warnings)
|
|
|
|
local_file = module.params['local_file']
|
|
remote_file = module.params['remote_file']
|
|
file_system = module.params['file_system']
|
|
|
|
changed = False
|
|
transfer_status = 'No Transfer'
|
|
|
|
if not os.path.isfile(local_file):
|
|
module.fail_json(msg="Local file {} not found".format(local_file))
|
|
|
|
dest = remote_file or os.path.basename(local_file)
|
|
remote_exists = remote_file_exists(module, dest, file_system=file_system)
|
|
|
|
if not remote_exists:
|
|
changed = True
|
|
file_exists = False
|
|
else:
|
|
file_exists = True
|
|
|
|
if not module.check_mode and not file_exists:
|
|
try:
|
|
transfer_file(module, dest)
|
|
transfer_status = 'Sent'
|
|
except ShellError:
|
|
clie = get_exception()
|
|
module.fail_json(msg=str(clie))
|
|
|
|
if remote_file is None:
|
|
remote_file = os.path.basename(local_file)
|
|
|
|
module.exit_json(changed=changed,
|
|
transfer_status=transfer_status,
|
|
local_file=local_file,
|
|
remote_file=remote_file,
|
|
warnings=warnings,
|
|
file_system=file_system)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|