#!/usr/bin/env python

# Copyright (c) 1999-2015, Juniper Networks Inc.
#               2014, Jeremy Schulman
#
# All rights reserved.
#
# License: Apache 2.0
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
#
# * Neither the name of the Juniper Networks nor the
#   names of its contributors may be used to endorse or promote products
#   derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY Juniper Networks, Inc. ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Juniper Networks, Inc. BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

DOCUMENTATION = '''
---
module: junos_cli
author: Damien Garros, Juniper Networks
version_added: "1.2.0"
short_description: Execute CLI on device and save the output locally
description:
    - Execute CLI on device and save the output locally on a file
requirements:
    - junos-eznc >= 1.2.2
options:
    host:
        description:
            - Set to {{ inventory_hostname }}
        required: true
    user:
        description:
            - Login username
        required: false
        default: $USER
    cli:
        description:
            - CLI command to execute on the host
        required: true
    passwd:
        description:
            - Login password
        required: false
        default: assumes ssh-key active
    port:
        description:
            - port number to use when connecting to the device
        required: false
        default: 830
    ssh_private_key_file:
        description:
            - This can be used if you need to provide a private key rather than
              loading the key into the ssh-key-ring/environment.  if your
              ssh-key requires a password, then you must provide it via
              **passwd**
        required: false
        default: None
    mode:
        description:
            - mode of console connection (telnet/serial). If mode is not
              provided SSH connection is used.
        required: false
        default: None
    timeout:
        description:
            - Set the NETCONF RPC timeout. Set this value to accommodate Cli
              commands that might take longer than the default timeout interval.
              Setting to 0 will use the PyEZ default (30 seconds).
        required: false
        default: "0"
    logfile:
        description:
            - Path on the local server where the progress status is logged
              for debugging purposes
        required: false
        default: None
    dest:
        description:
            - Path to the local server directory where cli output will
              be saved.
        required: false
        default: None
    format:
        description:
            - text - Cli output saved in text format
            - xml - Cli output saved as XML
        required: false
        choices: ['text','xml']
        default: 'text'
'''

EXAMPLES = '''
- junos_cli:
   host: "{{ inventory_hostname }}"
   cli: "show chassis hardware"
   logfile: cli.log
   dest: "{{ inventory_hostname }}.xml"
   format: xml

# Run cli over console server connection using PyEZ >= 2.0
- junos_cli:
    cli="show chassis hardware"
    host={{ inventory_hostname }}
    port=7001
    mode='telnet'
    dest="{{ inventory_hostname }}.xml"
    format='xml'
'''
from distutils.version import LooseVersion
import logging
from lxml import etree
from lxml.builder import E

def main():

    module = AnsibleModule(
        argument_spec=dict(host=dict(required=True, default=None),  # host or ipaddr
                           cli=dict(required=True, default=None),
                           user=dict(required=False, default=os.getenv('USER')),
                           passwd=dict(required=False, default=None, no_log=True),
                           port=dict(required=False, default=830),
                           ssh_private_key_file=dict(required=False, default=None),
                           mode=dict(required=False, default=None),
                           timeout=dict(required=False, type='int', default=0),
                           logfile=dict(required=False, default=None),
                           dest=dict(required=False, default=None),
                           format=dict(required=False, choices=['text', 'xml'], default='text')
                           ),
        supports_check_mode=True)

    args = module.params

    try:
        from jnpr.junos import Device
        from jnpr.junos.version import VERSION
        from jnpr.junos.exception import RpcError
        if not LooseVersion(VERSION) >= LooseVersion('1.2.2'):
            module.fail_json(msg='junos-eznc >= 1.2.2 is required for this module')
    except ImportError as ex:
        module.fail_json(msg='ImportError: %s' % ex.message)

    if args['mode'] is not None and LooseVersion(VERSION) < LooseVersion('2.0.0'):
        module.fail_json(msg='junos-eznc >= 2.0.0 is required for console connection.')

    logfile = args['logfile']
    if logfile is not None:
        logging.basicConfig(filename=logfile, level=logging.INFO,
                            format='%(asctime)s:%(name)s:%(message)s')
        logging.getLogger().name = 'CONFIG:' + args['host']

    logging.info("connecting to host: {0}@{1}:{2}".format(args['user'], args['host'], args['port']))

    try:
        dev = Device(args['host'], user=args['user'], password=args['passwd'],
                     port=args['port'], ssh_private_key_file=args['ssh_private_key_file'],
                     mode=args['mode'], gather_facts=False).open()
    except Exception as err:
        msg = 'unable to connect to {0}: {1}'.format(args['host'], str(err))
        logging.error(msg)
        module.fail_json(msg=msg)
        # --- UNREACHABLE ---

    ## Change default Timeout
    if args['timeout'] > 0:
        dev.timeout = args['timeout']

    try:
        options = {}
        options['format'] = args['format']

        logging.info("Getting cli output")

        cli_output = dev.cli(command=args['cli'], format=args['format'] )

        if args['dest'] is not None:
            with open(args['dest'], 'w') as outputfile:
                if args['format'] == 'text':
                    outputfile.write(cli_output)
                elif args['format'] == 'xml':
                    outputfile.write(etree.tostring(cli_output))

    except (ValueError, RpcError) as err:
        msg = 'Unable to get cli output: {0}'.format(str(err))
        logging.error(msg)
        dev.close()
        module.fail_json(msg=msg)

    except Exception as err:
        msg = 'Uncaught exception - please report: {0}'.format(str(err))
        logging.error(msg)
        dev.close()
        module.fail_json(msg=msg)

    dev.close()

    module.exit_json()

from ansible.module_utils.basic import *

if __name__ == '__main__':
    main()
