Skip to content

Commit

Permalink
network_put and network_get modules (ansible#39592)
Browse files Browse the repository at this point in the history
* Initial commit

* Socket Timeout and dest file handler

* sftp handling

* module name change as per review

* multiple thread tmp file overwite problem

* Integration test suite for network_put

* add additional testcase for dest argument

* fix pylint/pep8/modules warnings

* add socket timeout for get_file

* network_get module

* pep8 issue on network_get

* Review comments
  • Loading branch information
gdpak committed May 16, 2018
1 parent 05c4f59 commit 86c945a
Show file tree
Hide file tree
Showing 12 changed files with 543 additions and 5 deletions.
Empty file.
70 changes: 70 additions & 0 deletions lib/ansible/modules/network/files/network_get.py
@@ -0,0 +1,70 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# (c) 2018, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type


ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}


DOCUMENTATION = """
---
module: network_get
version_added: "2.6"
author: "Deepak Agrawal (@dagrawal)"
short_description: Copy files from a network device to Ansible Controller
description:
- This module provides functionlity to copy file from network device to
ansible controller.
options:
src:
description:
- Specifies the source file. The path to the source file can either be
the full path on the network device or a relative path as per path
supported by destination network device.
required: true
protocol:
description:
- Protocol used to transfer file.
default: scp
choices: ['scp', 'sftp']
dest:
description:
- Specifies the destination file. The path to the destination file can
either be the full path on the Ansible control host or a relative
path from the playbook or role root directory.
default:
- Same filename as specified in src. The path will be playbook root
or role root directory if playbook is part of a role.
requirements:
- "scp"
notes:
- Some devices need specific configurations to be enabled before scp can work
These configuration should be pre-configued before using this module
e.g ios - C(ip scp server enable)
- User privileage to do scp on network device should be pre-configured
e.g. ios - need user privileage 15 by default for allowing scp
- Default destination of source file
"""

EXAMPLES = """
- name: copy file from the network device to ansible controller
network_get:
src: running_cfg_ios1.txt
- name: copy file from ios to common location at /tmp
network_put:
src: running_cfg_sw1.txt
dest : /tmp/ios1.txt
"""

RETURN = """
"""
71 changes: 71 additions & 0 deletions lib/ansible/modules/network/files/network_put.py
@@ -0,0 +1,71 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# (c) 2018, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type


ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}


DOCUMENTATION = """
---
module: network_put
version_added: "2.6"
author: "Deepak Agrawal (@dagrawal)"
short_description: Copy files from Ansibe controller to a network device
description:
- This module provides functionlity to copy file from Ansible controller to
network devices.
options:
src:
description:
- Specifies the source file. The path to the source file can either be
the full path on the Ansible control host or a relative path from the
playbook or role root directory.
required: true
protocol:
description:
- Protocol used to transfer file.
default: scp
choices: ['scp', 'sftp']
dest:
description:
- Specifies the destination file. The path to destination file can
either be the full path or relative path as supported by network_os.
default:
- Filename from src and at default directory of user shell on
network_os.
required: no
requirements:
- "scp"
notes:
- Some devices need specific configurations to be enabled before scp can work
These configuration should be pre-configued before using this module
e.g ios - C(ip scp server enable).
- User privileage to do scp on network device should be pre-configured
e.g. ios - need user privileage 15 by default for allowing scp.
- Default destination of source file.
"""

EXAMPLES = """
- name: copy file from ansible controller to a network device
network_put:
src: running_cfg_ios1.txt
- name: copy file at root dir of flash in slot 3 of sw1(ios)
network_put:
src: running_cfg_sw1.txt
protocol: sftp
dest : flash3:/running_cfg_sw1.txt
"""

RETURN = """
"""
130 changes: 130 additions & 0 deletions lib/ansible/plugins/action/network_get.py
@@ -0,0 +1,130 @@
# (c) 2018, Ansible Inc,
#
# 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/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import copy
import os
import time
import re

from ansible.module_utils._text import to_text
from ansible.module_utils.connection import Connection
from ansible.errors import AnsibleError
from ansible.plugins.action import ActionBase
from ansible.module_utils.six.moves.urllib.parse import urlsplit

try:
from __main__ import display
except ImportError:
from ansible.utils.display import Display
display = Display()


class ActionModule(ActionBase):

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 network_get module')
return result

try:
src = self._task.args.get('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'

sock_timeout = play_context.timeout

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

conn = Connection(socket_path)

try:
out = 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'] = True
result['destination'] = dest
return result

def _handle_dest_path(self, dest):
working_path = self._get_working_path()

if os.path.isabs(dest) or urlsplit('dest').scheme:
dst = dest
else:
dst = self._loader.path_dwim_relative(working_path, '', dest)

return dst

def _get_src_filename_from_path(self, src_path):
filename_list = re.split('/|:', src_path)
return filename_list[-1]

def _get_working_path(self):
cwd = self._loader.get_basedir()
if self._task._role is not None:
cwd = self._task._role._role_path
return cwd

def _get_default_dest(self, src_path):
dest_path = self._get_working_path()
src_fname = self._get_src_filename_from_path(src_path)
filename = '%s/%s' % (dest_path, src_fname)
return filename

def _get_network_os(self, task_vars):
if 'network_os' in self._task.args and self._task.args['network_os']:
display.vvvv('Getting network OS from task argument')
network_os = self._task.args['network_os']
elif self._play_context.network_os:
display.vvvv('Getting network OS from inventory')
network_os = self._play_context.network_os
elif 'network_os' in task_vars.get('ansible_facts', {}) and task_vars['ansible_facts']['network_os']:
display.vvvv('Getting network OS from fact')
network_os = task_vars['ansible_facts']['network_os']
else:
raise AnsibleError('ansible_network_os must be specified on this host to use platform agnostic modules')

return network_os

0 comments on commit 86c945a

Please sign in to comment.