#!/usr/bin/env python

# Copyright (c) 1999-2014, 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_shutdown
author: Jeremy Schulman, Juniper Networks
version_added: "1.0.0"
short_description: Shut down or reboot a device running Junos OS.
description:
    - Shut down (power off) or reboot a device running Junos OS. This includes
      all Routing Engines in a Virtual Chassis or a dual Routing Engine system.
      This is equivalent to executing either the Junos OS B(request system power-off)
      or B(request system reboot) operational command.
requirements:
    - junos-eznc >= 1.2.2
options:
    host:
        description:
            - Set to {{ inventory_hostname }}
        required: true
    user:
        description:
            - Login username
        required: false
        default: $USER
    passwd:
        description:
            - Login password
        required: false
        default: assumes ssh-key active
    reboot:
        description:
            - If set to B(yes), then the device is rebooted rather than powered off.
        required: false
        default: no
        choices: ['yes','no']
    at:
        description:
            - Specify a time for the reboot (yyyymmddhhmm). Can be used only with reboot=yes.
        required: false
        default: None
    in_min:
        description:
            - Specify a delay, in minutes, before the shutdown or reboot.
              If both "at" and "in_min" are specified, "in_min" will be ignored.
        required: false
        default: 0
    shutdown:
        description:
            - Safety mechanism. You B(MUST) set this to 'shutdown'.
        required: yes
        default: None
    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
'''

EXAMPLES = '''
- junos_shutdown:
    host={{ inventory_hostname }}
    shutdown="shutdown"
    reboot=yes

# over console server connection using PyEZ >= 2.0
- junos_shutdown:
    host={{ inventory_hostname }}
    shutdown="shutdown"
    reboot=yes
    mode='telnet'
    port=7016
'''
from distutils.version import LooseVersion

def main():

    module = AnsibleModule(
        argument_spec=dict(
            shutdown=dict(required=True, default=None),   # must be set to 'shutdown'
            host=dict(required=True, default=None),       # host or ipaddr
            user=dict(required=False, default=os.getenv('USER')),
            passwd=dict(required=False, default=None, no_log=True),
            reboot=dict(required=False, type='bool', choices=BOOLEANS, default=False),
            port=dict(required=False, default=830),
            ssh_private_key_file=dict(required=False, default=None),
            mode=dict(required=False, default=None),
            in_min=dict(required=False, default=0),
            at=dict(required=False, type='str', default=None)
        ),
        supports_check_mode=False)

    args = module.params

    try:
        from jnpr.junos import Device
        from jnpr.junos.utils.sw import SW
        from jnpr.junos.version import VERSION
        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.')

    if args['shutdown'] != 'shutdown':
        module.fail_json(msg='Say "shutdown" to proceed')

    restart_mode = module.boolean(args['reboot'])
    delay = args['in_min']

    # because PyEZ does not support 'at' option on shutdown, only with reboot
    if args['at'] and not restart_mode:
        module.fail_json(msg="Argument *at* must be used with argument *reboot='yes'*")
        # --- UNREACHABLE ---

    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))
        module.fail_json(msg=msg)
        # --- UNREACHABLE ---

    sw = SW(dev)
    do_it = sw.reboot if restart_mode is True else sw.poweroff

    try:
        if args['at']:
            msg = do_it(at=args['at'])
            dev.close()
        else:
            msg = do_it(in_min=delay)
            # no need to close session with immediate shutdown/reboot because
            # the device will be shutting and we will lose connectivity anyway
            if delay > 0:
                dev.close()
    except Exception as err:
        msg = 'error during device reboot or shutdown {0}: {1}'.format(args['host'], str(err))
        module.fail_json(msg=msg)

    results = {'changed': True, 'reboot': restart_mode, 'msg':msg}
    module.exit_json(**results)

from ansible.module_utils.basic import *

if __name__ == '__main__':
    main()
