#!/usr/bin/python3
# by skvidal
# ported by doteast to ansible 2.0
# gplv2+
# print out the ACTUAL freemem - not overcommitted value


import sys
from ansible.module_utils.common.collections import ImmutableDict
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible import constants as C
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
from ansible import context
from optparse import OptionParser


class OutputCallback(CallbackBase):
    def __init__(self, *args, **kwargs):
        super(OutputCallback, self).__init__(*args, **kwargs)
        self.unreachable = set()
        self.cpu_per_host = {}
        self.mem_per_host = {}
        self.mem_used_in_vm = {}
        self.cpu_used_in_vm = {}

    # To increate debugging info
    #        self._display.verbosity = 5

    def v2_runner_on_unreachable(self, result):
        self.unreachable.add(result._host.get_name())

    # Comment this out if you need to debug further the script
    #    def v2_on_any(self, *args, **kwargs):
    #        print("OutputCallback - any")
    #        print(args)
    #        print(kwargs)
    #        result = args[0]
    #        print(result._result)

    def v2_runner_on_ok(self, result, *args, **kwargs):
        vhostname = result._host.get_name()
        if result._result["invocation"]["module_args"]["command"] == "nodeinfo":
            self.cpu_per_host[vhostname] = int(result._result["cpus"])
            self.mem_per_host[vhostname] = int(result._result["phymemory"])

        if result._result["invocation"]["module_args"]["command"] == "info":
            mem_used = 0
            cpu_used = 0
            for vm in result._result:
                if vm not in ["invocation", "changed", "_ansible_no_log"]:
                    if vm and type(result._result[vm]) == dict:
                        mem_used += int(result._result[vm]["memory"]) / 1024
                        cpu_used += int(result._result[vm]["nrVirtCpu"])

                self.mem_used_in_vm[vhostname] = mem_used
                self.cpu_used_in_vm[vhostname] = cpu_used


parser = OptionParser(version="1.0")
parser.add_option(
    "--host",
    default=[],
    action="append",
    help="hosts to act on, defaults to virtservers",
)
parser.add_option(
    "--hosts-from-file",
    default=C.DEFAULT_HOST_LIST,
    dest="host_file",
    help="read list of hosts from this file",
)
(opts, args) = parser.parse_args(sys.argv[1:])

if not opts.host:
    hosts = ["virtservers"]
else:
    hosts = ";".join(opts.host)

# since the API is constructed for CLI it expects certain options to always be set in the context object
context.CLIARGS = ImmutableDict(
    connection="ssh",
    module_path=["/usr/lib/python3.6/site-packages/ansible/modules/"],
    forks=25,
    become=None,
    become_method=None,
    become_user=None,
    check=False,
    diff=False,
    verbosity=0,
)

# create inventory and pass to var manager
loader = DataLoader()
inv = InventoryManager(loader=loader, sources=opts.host_file)
variable_manager = VariableManager(loader=loader, inventory=inv)

unpatched_spectre = loader.load_from_file("/srv/private/ansible/vars.yml")[
    "non_spectre_patched"
]

# create play with tasks
play_source = dict(
    name="vhost-info",
    hosts=hosts,
    gather_facts="no",
    tasks=[
        dict(action=dict(module="virt", args=dict(command="nodeinfo"))),
        dict(action=dict(module="virt", args=dict(command="info"))),
    ],
)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)

cb = OutputCallback()
tqm = None
try:
    tqm = TaskQueueManager(
        inventory=inv,
        variable_manager=variable_manager,
        loader=loader,
        passwords=None,
        run_additional_callbacks=False,
        stdout_callback=cb,
    )
    result = tqm.run(play)
finally:
    if tqm is not None:
        tqm.cleanup()

for vhostname in sorted(cb.mem_per_host):
    freemem = cb.mem_per_host[vhostname] - cb.mem_used_in_vm[vhostname]
    freecpu = cb.cpu_per_host[vhostname] - cb.cpu_used_in_vm[vhostname]
    insecure = ""
    if vhostname in unpatched_spectre:
        insecure = " (NOT PATCHED FOR SPECTRE)"
    print(
        (
            "%s:\t%s/%s mem(unused/total)\t%s/%s cpus(unused/total) %s"
            % (
                vhostname,
                freemem,
                cb.mem_per_host[vhostname],
                freecpu,
                cb.cpu_per_host[vhostname],
                insecure,
            )
        )
    )
