#!/usr/bin/env python
#
# (c) Copyright 2018 SUSE LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# An Ansible module to allow playbooks to communicate with
# HP ILO devices using SMASH CLP commands.

DOCUMENTATION = '''
---
module: hpiLO
author: Wayne Okuma
short_description: Issue a SMASH CLP command to HP-ILO hosts
description:
    - Issue SMASH CLP commands to an HP ILO server.
    - HPILO credentials and network addresses can be specified in a JSON or
      YAML file, or can be retrieved from Cobbler.
options:
    name:
        required: true
        description:
            - Name of the node to manage.
    credsfile:
        required: false
        description:
            - The Credentals file which contains the HP ILO user, password and
              IP-Address of the HP ILO server by the name specified (e.g., the
              servers.yml file).
    command:
        required: true
        description:
            - the SMASH CLP command to issue to the server.
'''

EXAMPLES = '''
- hpilo: name=compute2 command="show /system1/bootconfig1 oemhp_bootmode"
- hpilo: name=compute2 credsfile="/tmp/nodeinfo.yml" \
         command="set /system1/bootconfig1/oemhp_uefibootsource4 bootorder=1"
'''

import os
import pexpect
import re
import time
import yaml

# Load the local module
import imp
ardanaencrypt = imp.load_source('ardanaencrypt', './ardanaencrypt.py')

openssl_prefix = ardanaencrypt.openssl.prefix

def decrypt(value):
    if value.startswith(openssl_prefix):
        decrypter = hosencrypt.openssl
    else:
        return value
    obj = decrypter()
    return obj.decrypt(value[len(obj.prefix):])

def parse_command_result_status(command_result):
    for line in command_result.strip().splitlines():
        if line.find("status=") != -1:
            return line.strip("status=")
    return("-1")

class HPilo(object):
    def __init__(self, module):
        self.module = module

        try:
            self.node = module.params["name"]
            self.credsfile = module.params["credsfile"]
            self.command = module.params["command"]
        except ValueError as e:
            self.module.fail_json(msg="hpilo: " + str(e))

    def cobbler_creds(self, node):
        cmd = ["sudo", "cobbler", "system", "dumpvars", "--name=%s" % node]
        rc, out, err = self.execv(cmd, check_rc=True)
        creds = dict()
        translation = {"power_address": "ip", "power_user": "user", "power_pass": "password"}
        for line in out.splitlines():
            key, value = line.split(" : ")
            if key in translation:
                if value == "":
                    break
                creds[translation[key]] = value
        if len(translation) != len(creds):
            self.fail(msg="hpilo: missing creds for %s in cobbler" % node)
        return creds

    def file_creds(self, fname):
        data = yaml.safe_load(file(fname))
        creds = dict()
        if "servers" in data:
            field = "servers"
            ident = "id"
            iloip = "ilo-ip"
            ilouser = "ilo-user"
            ilopassword = "ilo-password"
        else:
            # Backward compatibility
            field = "baremetal_servers"
            ident = "node_name"
            iloip = "ilo_ip"
            ilouser = "ilo_user"
            ilopassword = "ilo_password"

        for srv in data[field]:
            if srv[ident] == self.node:
                creds["ip"] = srv[iloip]
                creds["user"] = srv[ilouser]
                creds["password"] = decrypt(srv[ilopassword])
                break
        return creds

    def get_creds(self, node):
        if self.credsfile:
            return self.file_creds(self.credsfile)
        else:
            return self.cobbler_creds(node)

    def execute(self):
        ssh_disable_host_key = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no '
        try:
            creds = self.get_creds(self.node)

            child = pexpect.spawn('ssh ' + ssh_disable_host_key + creds["user"] + '@' + creds["ip"])
            child.expect('password')
            child.sendline(creds["password"])
            child.expect('</>hpiLO->')
            child.sendline(self.command)
            child.expect('</>hpiLO->')
            command_result = child.before
            child.sendline('exit')

            result = dict(
                changed=False,
                stdout=command_result,
                stderr=''
            )
            print(command_result)
            rc = parse_command_result_status(command_result)
            if rc == '0':
               result['changed']=True
               self.module.exit_json(**result)
            else:
               self.module.fail_json(msg='command failed', **result)
        except Exception as e:
            self.module.fail_json(msg='hpilo: processing failed ' + str(e))


def main():
    module = AnsibleModule(
        argument_spec=dict(
            name=dict(required=True),
            command=dict(required=True),
            credsfile=dict(required=False),
        )
    )

    hpilo = HPilo(module)
    return hpilo.execute()


from ansible.module_utils.basic import *
main()
