#!/usr/libexec/platform-python

import os
import subprocess
from optparse import OptionParser
from datetime import datetime
import copy
import json
import io
from contextlib import redirect_stdout
import shlex
import sys

ansible_commands = {
    "ping_all": {
        "command": "ansible -i /usr/share/ceph-ansible/hosts all -m ping",
        "cwd": None,
        "result": None,
        "time_stamp": None,
        "purge":[]
    },
    "device_alias": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts device-alias.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":[]
    },
    "deploy_core": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts core.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":[]
    },
    "remove_core": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts infrastructure-playbooks/purge-cluster.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_core","deploy_cephfs","deploy_dashboard"]
    },
    "remove_smb": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts infrastructure-playbooks/purge-smb.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_smb","deploy_dashboard"]
    },
    "remove_iscsi": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts infrastructure-playbooks/purge-iscsi-gateways.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_iscsi","deploy_dashboard"]
    },
    "remove_nfs": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts infrastructure-playbooks/purge-nfs.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_nfs","deploy_dashboard"]
    },
    "remove_rgw": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts infrastructure-playbooks/purge-rgw.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_rgwlb","deploy_radosgw","deploy_dashboard"]
    },
    "remove_vg": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts remove-vg.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":[]
    },
    "ping_mdss": {
        "command": "ansible -i /usr/share/ceph-ansible/hosts -m ping mdss",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":[]
    },
    "deploy_cephfs": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts cephfs.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_dashboard"]
    },
    "deploy_radosgw": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts radosgw.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_dashboard"]
    },
    "deploy_iscsi": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts iscsi.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_dashboard"]
    },
    "deploy_dashboard": {
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts dashboard.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":[]
    },
    "deploy_rgwlb":{
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts radosgw-lb.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_dashboard"]
    },
    "deploy_nfs":{
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts nfs.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_dashboard"]
    },
    "deploy_smb":{
        "prerun": "ansible-playbook -i /usr/share/ceph-ansible/hosts kerberos_init.yml",
        "command": "ansible-playbook -i /usr/share/ceph-ansible/hosts samba.yml",
        "cwd": "/usr/share/ceph-ansible",
        "result": None,
        "time_stamp": None,
        "purge":["deploy_dashboard"]
    }
}

def print_to_string(item):
    with io.StringIO() as buf, redirect_stdout(buf):
        print(item)
        output = buf.getvalue().rstrip("\n")
        return output

def update_ceph_playbook_state(cmd):
    state_file_path = "/usr/share/cockpit/ceph-deploy/state/playbook_state.json"
    state_file_dir = "/usr/share/cockpit/ceph-deploy/state"
    state_dict = {}

    # check to see if file needs to be created.
    if not os.path.exists(state_file_path):
        # file doesn't exist
        if not os.path.exists(state_file_dir):
            # file directory doesn't exist, make required directories
            os.makedirs(state_file_dir)
        #create new parameter file with default parameters
        state_file = open(state_file_path,"w")
        state_file.write(json.dumps(state_dict,indent=4))
        state_file.close()

    else:
        #file exists on disk, load contents and return json object.
        state_file = open(state_file_path,"r")
        state_file_content = print_to_string(state_file.read())
        state_file.close()
        state_dict = json.loads(state_file_content)
    
    state_dict[cmd] = copy.deepcopy(ansible_commands[cmd])
    state_file = open(state_file_path,"w")
    state_file.write(json.dumps(state_dict,indent=4))
    state_file.close()


def run_ansible_command(cmd,verbose_flag,force_flag):
    verbose_str = " -vvv" if verbose_flag else ""
    split_command = shlex.split(ansible_commands[cmd]["command"]+verbose_str)
    command = None

    if force_flag == True:
        print("\tansible_runner: Force Flag Used")
        print("\tUsing the force option will only pretend that a command was run successfully.")
        sel = input("Are you sure that you want to do this? (y/n): ")
        if sel in ["y","Y"]:
            print("forcing command: {c}".format(c=ansible_commands[cmd]["command"]))
            ansible_commands[cmd]["result"] = 0
            ansible_commands[cmd]["time_stamp"] = datetime.today().strftime("%Y-%m-%d-%H-%M")
            update_ceph_playbook_state(cmd)
            if len(ansible_commands[cmd]["purge"]) > 0 and ansible_commands[cmd]["result"] == 0:
                for affected_playbook in ansible_commands[cmd]["purge"]:
                    if affected_playbook in ansible_commands.keys():
                        remove_ceph_playbook_state_entry(affected_playbook)
            sys.exit(0)
        else:
            print("exiting..")
            sys.exit(0)
    
    try:
        command = subprocess.run(
            split_command,
            cwd=ansible_commands[cmd]["cwd"],
            stdout=sys.stdout,
            stderr=sys.stdout,
            universal_newlines=True,
            bufsize=1
        )
    except KeyboardInterrupt as ki:
        ansible_commands[cmd]["result"] = 1
        ansible_commands[cmd]["time_stamp"] = datetime.today().strftime("%Y-%m-%d-%H-%M")
        update_ceph_playbook_state(cmd)
        sys.exit(0)
        

    ansible_commands[cmd]["result"] = command.returncode
    ansible_commands[cmd]["time_stamp"] = datetime.today().strftime("%Y-%m-%d-%H-%M")
    update_ceph_playbook_state(cmd)
    if len(ansible_commands[cmd]["purge"]) > 0 and ansible_commands[cmd]["result"] == 0:
        for affected_playbook in ansible_commands[cmd]["purge"]:
            if affected_playbook in ansible_commands.keys():
                remove_ceph_playbook_state_entry(affected_playbook)

def get_prerun_conditions(cmd):
    if cmd == "deploy_smb":
        param_file = open("/usr/share/cockpit/ceph-deploy/params/core_params.json","r")
        params = json.load(param_file)
        param_file.close()
        if params.get("groups").get("smbs").get("domain_member") != None and params.get("groups").get("smbs").get("active_directory_info").get("join_auth") == "kerberos":
            return True
        return False
    return False

def prerun_ansible_command(cmd,verbose_flag):
    if not get_prerun_conditions(cmd):
        return True
    
    verbose_str = " -vvv" if verbose_flag else ""
    split_command = shlex.split(ansible_commands[cmd]["prerun"]+verbose_str)
    command = None
    
    try:
        command = subprocess.run(
            split_command,
            cwd=ansible_commands[cmd]["cwd"],
            stdout=sys.stdout,
            stderr=sys.stdout,
            universal_newlines=True,
            bufsize=1
        )
    except KeyboardInterrupt as ki:
        ansible_commands[cmd]["result"] = 1
        ansible_commands[cmd]["time_stamp"] = datetime.today().strftime("%Y-%m-%d-%H-%M")
        update_ceph_playbook_state(cmd)
        sys.exit(0)
        

    if command.returncode != 0:
        # prerun failed
        ansible_commands[cmd]["result"] = command.returncode
        ansible_commands[cmd]["time_stamp"] = datetime.today().strftime("%Y-%m-%d-%H-%M")
        update_ceph_playbook_state(cmd)
        sys.exit(0)
    else:
        # prerun success
        return True


def remove_ceph_playbook_state_entry(cmd):
    state_file_path = "/usr/share/cockpit/ceph-deploy/state/playbook_state.json"
    state_file_dir = "/usr/share/cockpit/ceph-deploy/state"
    state_dict = {}

    # check to see if file needs to be created.
    if not os.path.exists(state_file_path):
        # file doesn't exist
        if not os.path.exists(state_file_dir):
            # file directory doesn't exist, make required directories
            os.makedirs(state_file_dir)
        #create new parameter file with default parameters
        state_file = open(state_file_path,"w")
        state_file.write(json.dumps(state_dict,indent=4))
        state_file.close()

    else:
        #file exists on disk, load contents and return json object.
        state_file = open(state_file_path,"r")
        state_file_content = print_to_string(state_file.read())
        state_file.close()
        state_dict = json.loads(state_file_content)
    
    if cmd in state_dict.keys():
        del state_dict[cmd]
    state_file = open(state_file_path,"w")
    state_file.write(json.dumps(state_dict,indent=4))
    state_file.close()

def main():
    cmd_lst_str = "[\n"
    for key in ansible_commands.keys():
        cmd_lst_str += key + ",\n"
    cmd_lst_str = cmd_lst_str[:-2] + "\n]"

    parser = OptionParser()
    parser.add_option("-c","--command",action="store",dest="command",default=None, help="The ansible command to run. \nvalid commands:\n{c}".format(c=cmd_lst_str))
    parser.add_option("-v","--verbose",action="store_true",dest="verbose",default=False,help="runs the ansible playbook using -vvv flag")
    parser.add_option("-f","--force", action="store_true", dest="force",default=False,help="skips running the playbook, forces a success result.")
    (options, args) = parser.parse_args()

    if options.command != None and options.command in ansible_commands.keys():
        if "prerun" in ansible_commands[options.command].keys():
            prerun_ansible_command(options.command, options.verbose)
        run_ansible_command(options.command,options.verbose,options.force)


if __name__ == "__main__":
    main()