#!/usr/bin/python
#
# Copyright (c) 2015-2017, Parallels International GmbH
#
# This file is part of Virtuozzo Core. Virtuozzo Core is free
# software; you can redistribute it and/or modify it under the terms
# of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# Our contact details: Parallels International GmbH, Vordergasse 59, 8200
# Schaffhausen, Switzerland.
#

import subprocess32 as subprocess
import sys
import os
import logging
import threading
import prlsdkapi

def error(mess):
    logger.error(mess)
    sys.exit(1)

def process_vms(timeout = 60 * 1000, do_stop = True):
    threads = []
    # Connect to dispatcher
    logger.info("SDK init...")
    prlsdkapi.init_server_sdk()
    logger.info("Connecting to dispatcher...")
    server = prlsdkapi.Server()
    server.login_local().wait(msecs = timeout)

    # Create file with list of auto autostart
    autostart = "units_to_start = ("

    logger.info("Retreiving VMs list...")

    # Get VMs list
    vms = server.get_vm_list_ex(prlsdkapi.consts.PVTF_VM + prlsdkapi.consts.PVTF_CT)
    for res in [ r for r in vms.wait(msecs = timeout) ]:
        # wrap get_config in a separate try-catch to make sure
        # we will not get another Exception in "except" due to get_config
        try:
            rescfg = res.get_config()
        except Exception as e:
            logger.error("Unable to get config, skip resource: %s" % e)
            continue

        try:
            # Place to list only if it was not in running state
            logger.info("UUID: %s" % rescfg.get_uuid())
            if (rescfg.get_auto_start() == prlsdkapi.consts.PAO_VM_START_ON_RELOAD and
                    res.get_vm_info().get_state() == prlsdkapi.consts.VMS_RUNNING):
                autostart += "\"%s\", " % rescfg.get_uuid()
                logger.info("%s: Autostart enabled" % rescfg.get_uuid())

            # Skip CTs and stopped/suspended VMs
            state = res.get_vm_info().get_state()
            if (rescfg.get_vm_type() != prlsdkapi.consts.PVT_VM or
                    state in (prlsdkapi.consts.VMS_STOPPED, prlsdkapi.consts.VMS_STOPPING,
                    prlsdkapi.consts.VMS_SUSPENDING, prlsdkapi.consts.VMS_SUSPENDED)):
                continue

            if not do_stop:
                continue

            # Suspend or Stop VMs based on Autostop configuration
            stopmode = rescfg.get_auto_stop()
            if stopmode == prlsdkapi.consts.PAO_VM_SUSPEND:
                logger.info("Suspending VM %s (state %d)..." % (rescfg.get_uuid(), state))
                thread = threading.Thread(target=res.suspend().wait)
            elif stopmode in [prlsdkapi.consts.PAO_VM_STOP, prlsdkapi.consts.PAO_VM_SHUTDOWN]:
                logger.info("Stopping VM %s (state %d)..." %  (rescfg.get_uuid(), state))
                thread = threading.Thread(target=res.stop_ex(prlsdkapi.consts.PSM_ACPI, 0).wait)
            else:
                logger.error("Unknown autostop mode %d for VM %s (state %d). Stopping..." %
                        (stopmode, rescfg.get_uuid()), state)
                thread = threading.Thread(target=res.stop_ex(prlsdkapi.consts.PSM_ACPI, 0).wait)

            thread.start()
            threads.append(thread)

        except Exception as e:
            logger.error("Exception on VM %s:" % rescfg.get_uuid())
            logger.error("\t%s" % e)

    with open("/var/spool/prl-disp/prl_disp_autostart.py", "w") as f:
        f.write(autostart + ")\n")
        f.close()

    logger.info("Waiting for suspend threads...")

    # Wait for VMs suspended
    for thread in threads:
        thread.join()

    # Disconnect
    logger.info("Disconnecting from dispatcher...")
    server.logoff().wait(msecs = timeout)
    logger.info("SDK deinit...")
    prlsdkapi.deinit_sdk()

def prepare_to_reboot(do_stop = True):
    try:
        process_vms(do_stop = do_stop)
    except Exception, err:
        error(repr(err))

# Redirect messages to /dev/null
devnull = open("/dev/null", 'w')
sys.stdout.flush()
sys.stderr.flush()
sys.stdout, sys.stderr = devnull, devnull

progname = "prl_disp_reboot"

# Setup logger
defloglevel = logging.DEBUG
logformat = "%(asctime)-15s %(message)s"
logging.basicConfig(format=logformat)
logger = logging.getLogger(progname)
logger.setLevel(defloglevel)
fh = logging.FileHandler("/var/log/%s.log" % progname)
fh.setLevel(defloglevel)
fh.setFormatter(logging.Formatter(logformat))
logger.addHandler(fh)

# Detect that we're started from cronjob as intermediate list creator
if os.path.basename(sys.argv[0]) == "prl_disp_autostart_list_update":
    logger.info("Started as autostart list updater")
    prepare_to_reboot(do_stop = False)
    logger.info("All done!")
    sys.exit(0)

# Detect that we're going to reboot/shutdown
proc = subprocess.Popen("/usr/bin/systemctl -q --no-legend list-jobs",
        stderr=subprocess.PIPE, stdout=subprocess.PIPE,
        stdin=subprocess.PIPE, shell = True)
(output, err) = proc.communicate()

logger.debug("systemctl return code: %d" % proc.returncode)
logger.debug("stdout of systemctl:")
logger.debug(output)
logger.debug("stderr of systemctl:")
logger.debug(err)
logger.info("Determining state...")

for line in output.splitlines():
    try:
        if not line.split(" ")[1] in ("reboot.target", "shutdown.target"):
            logger.info("Not in reboot or shutdown state!")
            continue
        prepare_to_reboot()
        break
    except IndexError:
        pass

logger.info("All done!")
