diff --git a/lib/ansible/modules/network/icx/icx_copy.py b/lib/ansible/modules/network/icx/icx_copy.py new file mode 100644 index 00000000000..08f2e8366d9 --- /dev/null +++ b/lib/ansible/modules/network/icx/icx_copy.py @@ -0,0 +1,373 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# 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': 'community'} + + +DOCUMENTATION = """ +--- +module: icx_copy +version_added: "2.9" +author: "Ruckus Wireless (@Commscope)" +short_description: Transfer files from or to remote Ruckus ICX 7000 series switches +description: + - This module transfers files from or to remote devices running ICX. +notes: + - Tested against ICX 10.1. + - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). +options: + upload: + description: + - Name of the resource to be uploaded. Mutually exclusive with download. + type: str + choices: ['running-config', 'startup-config', 'flash_primary', 'flash_secondary'] + download: + description: + - Name of the resource to be downloaded. Mutually exclusive with upload. + type: str + choices: ['running-config', 'startup-config', 'flash_primary', 'flash_secondary', 'bootrom', 'fips-primary-sig', 'fips-secondary-sig', 'fips-bootrom-sig'] + protocol: + description: + - Data transfer protocol to be used + type: str + choices: ['scp', 'https'] + required: true + remote_server: + description: + - IP address of the remote server + type: str + required: true + remote_port: + description: + - The port number of the remote host. Default values will be selected based on protocol type. + Default scp:22, http:443 + type: str + remote_filename: + description: + - The name or path of the remote file/resource to be uploaded or downloaded. + type: str + required: true + remote_user: + description: + - remote username to be used for scp login. + type: str + remote_pass: + description: + - remote password to be used for scp login. + type: str + public_key: + description: + - public key type to be used to login to scp server + type: str + choices: ['rsa', 'dsa'] + +""" + +EXAMPLES = """ +- name: upload running-config to the remote scp server + icx_copy: + upload: running-config + protocol: scp + remote_server: 172.16.10.49 + remote_filename: running.conf + remote_user: user1 + remote_pass: pass123 + +- name: download running-config from the remote scp server + icx_copy: + download: running-config + protocol: scp + remote_server: 172.16.10.49 + remote_filename: running.conf + remote_user: user1 + remote_pass: pass123 + +- name: download running-config from the remote scp server using rsa public key + icx_copy: + download: running-config + protocol: scp + remote_server: 172.16.10.49 + remote_filename: running.conf + remote_user: user1 + remote_pass: pass123 + public_key: rsa + +- name: upload startup-config to the remote https server + icx_copy: + upload: startup-config + protocol: https + remote_server: 172.16.10.49 + remote_filename: config/running.conf + remote_user: user1 + remote_pass: pass123 + +- name: upload startup-config to the remote https server + icx_copy: + upload: startup-config + protocol: https + remote_server: 172.16.10.49 + remote_filename: config/running.conf + remote_user: user1 + remote_pass: pass123 + +- name: Download OS image into the flash from remote scp ipv6 server + icx_copy: + download: startup-config + protocol: scp + remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C + remote_filename: img.bin + remote_user: user1 + remote_pass: pass123 + +- name: Download OS image into the secondary flash from remote scp ipv6 server + icx_copy: + Download: flash_secondary + protocol: scp + remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C + remote_filename: img.bin + remote_user: user1 + remote_pass: pass123 + +- name: Download OS image into the secondary flash from remote scp ipv6 server on port 5000 + icx_copy: + Download: flash_secondary + protocol: scp + remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C + remote_port: 5000 + remote_filename: img.bin + remote_user: user1 + remote_pass: pass123 + +- name: Download OS image into the primary flash from remote https ipv6 server + icx_copy: + Download: flash_primary + protocol: https + remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C + remote_filename: images/img.bin + remote_user: user1 + remote_pass: pass123 + +- name: Download OS image into the primary flash from remote https ipv6 server on port 8080 + icx_copy: + Download: flash_primary + protocol: https + remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C + remote_port: 8080 + remote_filename: images/img.bin + remote_user: user1 + remote_pass: pass123 +""" + +RETURN = """ +changed: + description: true when downloaded any configuration or flash. false otherwise. + returned: always + type: bool +""" + + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import ConnectionError, exec_command +from ansible.module_utils.network.icx.icx import exec_scp, run_commands + + +def map_params_to_obj(module): + command = dict() + + if(module.params['protocol'] == 'scp'): + if(module.params['upload'] is not None): + module.params["upload"] = module.params["upload"].replace("flash_primary", "primary") + module.params["upload"] = module.params["upload"].replace("flash_secondary", "secondary") + if(module.params["upload"] == 'running-config' or module.params["upload"] == 'startup-config'): + command["command"] = "copy %s scp %s%s %s%s" % (module.params['upload'], + module.params["remote_server"], + " " + module.params["remote_port"] if module.params["remote_port"] else "", + module.params["remote_filename"], + "public-key " + module.params["public_key"] if module.params["public_key"] else "") + else: + command["command"] = "copy flash scp %s%s %s%s %s" % (module.params["remote_server"], + " " + module.params["remote_port"] if module.params["remote_port"] else "", + module.params["remote_filename"], + "public-key " + module.params["public_key"] if module.params["public_key"] else "", + module.params["upload"]) + command["scp_user"] = module.params["remote_user"] + command["scp_pass"] = module.params["remote_pass"] + if(module.params['download'] is not None): + module.params["download"] = module.params["download"].replace("flash_primary", "primary") + module.params["download"] = module.params["download"].replace("flash_secondary", "secondary") + if(module.params["download"] == 'running-config' or module.params["download"] == 'startup-config'): + command["command"] = "copy scp %s %s%s %s%s" % (module.params['download'], + module.params["remote_server"], + " " + module.params["remote_port"] if module.params["remote_port"] else "", + module.params["remote_filename"], + "public-key " + module.params["public_key"] if module.params["public_key"] else "") + else: + command["command"] = "copy scp flash %s%s %s%s %s" % (module.params["remote_server"], + " " + module.params["remote_port"] if module.params["remote_port"] else "", + module.params["remote_filename"], + "public-key " + module.params["public_key"] if module.params["public_key"] else "", + module.params["download"]) + command["scp_user"] = module.params["remote_user"] + command["scp_pass"] = module.params["remote_pass"] + if(module.params['protocol'] == 'https'): + if(module.params['upload'] is not None): + module.params["upload"] = module.params["upload"].replace("flash_primary", "primary") + module.params["upload"] = module.params["upload"].replace("flash_secondary", "secondary") + if(module.params["upload"] == 'running-config' or module.params["upload"] == 'startup-config'): + command["command"] = "copy %s https %s %s%s" % (module.params['upload'], + module.params["remote_server"], + module.params["remote_filename"], + " port " + module.params["remote_port"] if module.params["remote_port"] else "") + else: + command["command"] = "copy https flash %s %s %s%s" % (module.params["remote_server"], + module.params["remote_filename"], + module.params['upload'], + " port " + module.params["remote_port"] if module.params["remote_port"] else "") + if(module.params['download'] is not None): + module.params["download"] = module.params["download"].replace("flash_primary", "primary") + module.params["download"] = module.params["download"].replace("flash_secondary", "secondary") + if(module.params["download"] == 'running-config' or module.params["download"] == 'startup-config'): + command["command"] = "copy https %s %s %s%s" % (module.params['download'], + module.params["remote_server"], + module.params["remote_filename"], + " port " + module.params["remote_port"] if module.params["remote_port"] else "") + else: + command["command"] = "copy https flash %s %s %s%s" % (module.params["remote_server"], + module.params["remote_filename"], + module.params['download'], + " port " + module.params["remote_port"] if module.params["remote_port"] else "") + return command + + +def checkValidations(module): + validation = dict( + scp=dict( + upload=[ + 'running-config', + 'startup-config', + 'flash_primary', + 'flash_secondary'], + download=[ + 'running-config', + 'startup-config', + 'flash_primary', + 'flash_secondary', + 'bootrom', + 'fips-primary-sig', + 'fips-secondary-sig', + 'fips-bootrom-sig']), + https=dict( + upload=[ + 'running-config', + 'startup-config'], + download=[ + 'flash_primary', + 'flash_secondary', + 'startup-config'])) + protocol = module.params['protocol'] + upload = module.params['upload'] + download = module.params['download'] + + if(protocol == 'scp' and module.params['remote_user'] is None): + module.fail_json(msg="While using scp remote_user argument is required") + if(upload is None and download is None): + module.fail_json(msg="Upload or download params are required.") + if(upload is not None and download is not None): + module.fail_json(msg="Only upload or download can be used at a time.") + if(upload): + if(not (upload in validation.get(protocol).get("upload"))): + module.fail_json(msg="Specified resource '" + upload + "' can't be uploaded to '" + protocol + "'") + if(download): + if(not (download in validation.get(protocol).get("download"))): + module.fail_json(msg="Specified resource '" + download + "' can't be downloaded from '" + protocol + "'") + + +def main(): + """entry point for module execution + """ + argument_spec = dict( + upload=dict( + type='str', + required=False, + choices=[ + 'running-config', + 'flash_primary', + 'flash_secondary', + 'startup-config']), + download=dict( + type='str', + required=False, + choices=[ + 'running-config', + 'startup-config', + 'flash_primary', + 'flash_secondary', + 'bootrom', + 'fips-primary-sig', + 'fips-secondary-sig', + 'fips-bootrom-sig']), + protocol=dict( + type='str', + required=True, + choices=[ + 'https', + 'scp']), + remote_server=dict( + type='str', + required=True), + remote_port=dict( + type='str', + required=False), + remote_filename=dict( + type='str', + required=True), + remote_user=dict( + type='str', + required=False), + remote_pass=dict( + type='str', + required=False, + no_log=True), + public_key=dict( + type='str', + required=False, + choices=[ + 'rsa', + 'dsa'])) + mutually_exclusive = [['upload', 'download']] + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, mutually_exclusive=mutually_exclusive) + + checkValidations(module) + warnings = list() + result = {'changed': False, 'warnings': warnings} + exec_command(module, 'skip') + + response = '' + try: + command = map_params_to_obj(module) + result['commands'] = [command["command"]] + + if(module.params['protocol'] == 'scp'): + response = exec_scp(module, command) + else: + response = run_commands(module, command) + if('Response Code: 404' in response): + module.fail_json(msg=response) + else: + result['response'] = "in progress..." + if(module.params["download"] is not None): + result['changed'] = True + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/units/modules/network/icx/fixtures/icx_copy.txt b/test/units/modules/network/icx/fixtures/icx_copy.txt new file mode 100644 index 00000000000..b4165c19689 --- /dev/null +++ b/test/units/modules/network/icx/fixtures/icx_copy.txt @@ -0,0 +1,2 @@ +Translating hostname client fileserver.alethea.in .... +...... \ No newline at end of file diff --git a/test/units/modules/network/icx/test_icx_copy.py b/test/units/modules/network/icx/test_icx_copy.py new file mode 100644 index 00000000000..1a5b5781bbc --- /dev/null +++ b/test/units/modules/network/icx/test_icx_copy.py @@ -0,0 +1,203 @@ +# Copyright: (c) 2019, Ansible Project +# 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 + +from units.compat.mock import patch +from ansible.modules.network.icx import icx_copy +from units.modules.utils import set_module_args +from .icx_module import TestICXModule, load_fixture + + +class TestICXSCPModule(TestICXModule): + + module = icx_copy + + def setUp(self): + super(TestICXSCPModule, self).setUp() + self.mock_exec_scp = patch('ansible.modules.network.icx.icx_copy.exec_scp') + self.mock_run_commands = patch('ansible.modules.network.icx.icx_copy.run_commands') + self.exec_command = self.mock_exec_scp.start() + self.run_commands = self.mock_run_commands.start() + self.mock_exec_command = patch('ansible.modules.network.icx.icx_copy.exec_command') + self.exec_commands = self.mock_exec_command.start() + + def tearDown(self): + super(TestICXSCPModule, self).tearDown() + self.mock_exec_scp.stop() + self.mock_run_commands.stop() + self.mock_exec_command.stop() + + def load_fixtures(self, commands=None): + self.exec_commands.return_value = (0, load_fixture('icx_copy.txt').strip(), None) + # self.exec_command.return_value = (0, load_fixture('icx_banner_show_banner.txt').strip(), None) + if(commands is not None): + fixtureName = commands[0].replace(" ", "_") + ".txt" + # print("loading fixture: ",load_fixture(fixtureName).strip()) + self.mock_exec_scp.return_value = load_fixture("icx_copy.txt").strip() + self.mock_run_commands.return_value = load_fixture("icx_copy.txt").strip() + else: + self.exec_command.return_value = "" + + def test_icx_scp_upload_running(self): + set_module_args( + dict( + upload='running-config', + protocol='scp', + remote_server='172.16.10.49', + remote_filename='running.conf', + remote_user='alethea', + remote_pass='alethea123')) + commands = ['copy running-config scp 172.16.10.49 running.conf'] + self.execute_module(commands=commands) + + def test_icx_scp_download_running(self): + set_module_args( + dict( + download='running-config', + protocol='scp', + remote_server='172.16.10.49', + remote_filename='running.conf', + remote_user='alethea', + remote_pass='alethea123')) + commands = ['copy scp running-config 172.16.10.49 running.conf'] + self.execute_module(commands=commands, changed=True) + + def test_icx_scp_upload_startup(self): + set_module_args( + dict( + upload='startup-config', + protocol='scp', + remote_server='172.16.10.49', + remote_filename='running.conf', + remote_user='alethea', + remote_pass='alethea123')) + commands = ['copy startup-config scp 172.16.10.49 running.conf'] + self.execute_module(commands=commands, changed=False) + + def test_icx_scp_download_startup(self): + set_module_args( + dict( + download='startup-config', + protocol='scp', + remote_server='172.16.10.49', + remote_filename='running.conf', + remote_user='alethea', + remote_pass='alethea123')) + commands = ['copy scp startup-config 172.16.10.49 running.conf'] + self.execute_module(commands=commands, changed=True) + + def test_icx_scp_upload_primary(self): + set_module_args( + dict( + upload='flash_primary', + protocol='scp', + remote_server='172.16.10.49', + remote_filename='SPS08080b.bin', + remote_user='alethea', + remote_pass='alethea123')) + commands = ['copy flash scp 172.16.10.49 SPS08080b.bin primary'] + self.execute_module(commands=commands, changed=False) + + def test_icx_scp_download_primary(self): + set_module_args( + dict( + download='flash_primary', + protocol='scp', + remote_server='172.16.10.49', + remote_filename='SPS08080b.bin', + remote_user='alethea', + remote_pass='alethea123')) + commands = ['copy scp flash 172.16.10.49 SPS08080b.bin primary'] + self.execute_module(commands=commands, changed=True) + + # HTTPS tests + + def test_icx_https_upload_running(self): + set_module_args( + dict( + upload='running-config', + protocol='https', + remote_server='fileserver.alethea.in', + remote_filename='filestorage/test/upload_running')) + commands = ['copy running-config https fileserver.alethea.in filestorage/test/upload_running'] + self.execute_module(commands=commands) + + def test_icx_https_download_running(self): + set_module_args( + dict( + download='running-config', + protocol='https', + remote_server='fileserver.alethea.in', + remote_filename='filestorage/test/running.conf')) + commands = ['copy https running-config fileserver.alethea.in filestorage/test/running.conf'] + self.execute_module(failed=True) + + def test_icx_https_upload_startup(self): + set_module_args( + dict( + upload='startup-config', + protocol='https', + remote_server='fileserver.alethea.in', + remote_filename='filestorage/test/upload_startup')) + commands = ['copy startup-config https fileserver.alethea.in filestorage/test/upload_startup'] + self.execute_module(commands=commands) + + def test_icx_https_download_startup(self): + set_module_args( + dict( + download='startup-config', + protocol='https', + remote_server='fileserver.alethea.in', + remote_filename='filestorage/test/startup.conf')) + commands = ['copy https startup-config fileserver.alethea.in filestorage/test/startup.conf'] + self.execute_module(commands=commands, changed=True) + + def test_icx_https_upload_primary(self): + set_module_args( + dict( + upload='flash_primary', + protocol='https', + remote_server='fileserver.alethea.in', + remote_filename='filestorage/test/upload_primary')) + commands = ['copy startup-config https fileserver.alethea.in filestorage/test/upload_primary'] + self.execute_module(failed=True) + + def test_icx_https_download_primary(self): + set_module_args(dict(download='flash_primary', protocol='https', remote_server='fileserver.alethea.in', remote_filename='filestorage/test/primary.bin')) + commands = ['copy https flash fileserver.alethea.in filestorage/test/primary.bin primary'] + self.execute_module(commands=commands, changed=True) + + def test_icx_https_upload_secondary(self): + set_module_args( + dict( + upload='flash_secondary', + protocol='https', + remote_server='fileserver.alethea.in', + remote_filename='filestorage/test/upload_secondary')) + commands = ['copy flash https fileserver.alethea.in filestorage/test/upload_secondary secondary'] + self.execute_module(failed=True) + + def test_icx_https_download_secondary(self): + set_module_args( + dict( + download='flash_secondary', + protocol='https', + remote_server='fileserver.alethea.in', + remote_filename='filestorage/test/secondary.bin')) + commands = ['copy https flash fileserver.alethea.in filestorage/test/secondary.bin secondary'] + self.execute_module(commands=commands, changed=True) + + def test_icx_https_upload_download(self): + set_module_args( + dict( + upload='flash_secondary', + download='flash_secondary', + protocol='https', + remote_server='fileserver.alethea.in', + remote_filename='filestorage/test/secondary.bin')) + self.execute_module(failed=True) + + def test_icx_scp_no_user(self): + set_module_args(dict(upload='running-config', protocol='scp', remote_server='172.16.10.49', remote_filename='running.conf')) + self.execute_module(failed=True)