#!/usr/bin/env python

# This file is part of Window-Switch.
# Copyright (c) 2009-2013 Antoine Martin <antoine@nagafix.co.uk>
# Window-Switch is released under the terms of the GNU GPL v3

import os
import time

from winswitch.objects.session import Session
from winswitch.objects.server_session import ServerSession
from winswitch.consts import LIBVIRT_TYPE, VNC_PORT_BASE, LOCALHOST
from winswitch.util.common import csv_list, escape_newlines, is_valid_dir
from winswitch.util.file_io import get_libvirt_dir
from winswitch.util.paths import SYSTEM_LIBVIRT_DIR
from winswitch.util.process_util import get_output
from winswitch.virt.server_util_base import ServerUtilBase
from winswitch.ui.icons import getraw
from winswitch.globals import USER_ID, USERNAME


class	LibvirtServerUtil(ServerUtilBase):

	def	__init__(self, config, add_session, remove_session, update_session_status, session_failed):
		ServerUtilBase.__init__(self, LIBVIRT_TYPE, VNC_PORT_BASE, config, add_session, remove_session, update_session_status, session_failed)

		self.prelaunch_enabled = False			#meaningless for libvirt
		self.test_using_sockets = True			#no other method implemented yet...
		self.connecting_timeout = 20			#20 seconds to go from connecting to connected
		for state_dir in self.get_libvirt_state_dirs():
			self.watched_directories.append(state_dir)

	def do_init(self):
		ServerUtilBase.do_init(self)
		self.detect_sessions()

	def get_libvirt_state_dirs(self):
		"""
		The list of directories we watch for changes to be able to trigger detect_sessions() at the appropriate time.
		"""
		dirs = []
		if USER_ID!=0 and is_valid_dir(get_libvirt_dir()):
			user_libvirt = get_libvirt_dir()
			dirs.append(user_libvirt)
			#add the "run" subdirectory of each subdirectory:
			for f in os.listdir(user_libvirt):
				d = os.path.join(user_libvirt, f)
				if is_valid_dir(d):
					subdir_files = os.listdir(d)
					if "run" in subdir_files:
						run_dir = os.path.join(d, "run")
						if is_valid_dir(run_dir):
							dirs.append(run_dir)

		#append all the subdirectories of /var/lib/libvirt if they exist
		if is_valid_dir(SYSTEM_LIBVIRT_DIR):
			dirs.append(SYSTEM_LIBVIRT_DIR)
			for f in os.listdir(SYSTEM_LIBVIRT_DIR):
				d = os.path.join(SYSTEM_LIBVIRT_DIR, f)
				if is_valid_dir(d):
					dirs.append(d)
		self.sdebug("=%s" % csv_list(dirs))
		return dirs


	def can_client_set_status(self, session, user_id, _from, _to):
		"""
		Only the client knows if the session is connected or not.
		"""
		return	ServerUtilBase.can_client_set_status(self, session, user_id, _from, _to) \
			or (_from==Session.STATUS_CONNECTING and _to==Session.STATUS_CONNECTED) \
			or (_from==Session.STATUS_CONNECTED and _to==Session.STATUS_AVAILABLE) \
			or (_from==Session.STATUS_IDLE and _to==Session.STATUS_AVAILABLE)

	def get_test_port(self, session):
		return	session.port

	def	do_prepare_session_for_attach(self, session, user, disconnect, call_when_done):
		"""
		libvirt (qemu vnc) sessions don't have a VNC password set?
		We can't do much! Just pretend it's ready.
		"""
		self.sdebug(None, session, user, disconnect, call_when_done)
		call_when_done()

	def start_display(self, session, user, is_preload):
		self.sdebug(None, session, user, is_preload)
		return False				#not implemented

	def stop_display(self, session, user, display):
		self.slog(None, session, user, display)
		#libvirt shutdown?

	def detect_sessions(self):
		self.sdebug("uris=%s" % csv_list(self.config.libvirt_uris))
		for uri in self.config.libvirt_uris:
			try:
				self.detect_session_for_uri(uri)
			except Exception, e:
				self.serr("error on %s" % uri, e)

	def detect_session_for_uri(self, uri):
		self.sdebug(None, uri)
		import libvirt		#@UnresolvedImport
		con = libvirt.openReadOnly(uri)
		if not con:
			self.serror("Failed to connect", uri)
			return
		self.slog("info=%s" % csv_list(con.getInfo()), uri)

		for local_id in con.listDomainsID():
			domain = con.lookupByID(local_id)
			name = domain.name()
			uuid = domain.UUIDString()
			self.sdebug("found domain %s for ID=%s, UUID=%s, info=%s" % (domain, local_id, uuid, csv_list(domain.info())), uri)
			state = domain.info()[0]
			#query the vnc display
			self.detect_session(uri, local_id, uuid, name, state)

	def detect_session(self, uri, local_id, uuid, name, state):
		self.sdebug(None, uri, local_id, uuid, name, state)
		import libvirt				   #@UnresolvedImport
		if state==libvirt.VIR_DOMAIN_RUNNING:
			status = Session.STATUS_AVAILABLE
		elif state==libvirt.VIR_DOMAIN_SHUTOFF:
			status = Session.STATUS_CLOSED
		else:
			self.serror("unknown state", uri, local_id, uuid, name, state)
			return
		display = self.detect_session_vncdisplay(uri, local_id)

		session = self.config.get_session(uuid)
		if not session:
			session = ServerSession(self.config)
			session.set_default_icon_data(getraw("libvirt"))
			session.ID = uuid
		session.command_uuid = ""
		session.name = name
		session.host = LOCALHOST
		session.port = -1
		if display and display.startswith(":"):
			session.port = int(display[1:])+5900
			session.display = ":%s" % session.port
		#session.requires_tunnel = ?
		session.password = "n/a"
		session.user = USERNAME
		session.status = status
		session.preload = False
		session.screen_size = None
		session.session_type = LIBVIRT_TYPE
		session.start_time = int(time.time())
		self.config.add_session(session)
		if self.add_session:
			self.add_session(session)



	def detect_session_vncdisplay(self, uri, local_id):
		cmd = ["virsh", "--connect=%s" % uri, "vncdisplay", local_id]
		self.sdebug("cmd=%s" % cmd, uri, local_id)
		try:
			code, out, _ = get_output(cmd)
		except Exception, e:
			self.serror("failed to run %s" % cmd, e, uri, local_id)
			return	None
		if code!=0 or not out:
			self.serror("error getting vncdisplay: %s" % escape_newlines(out), uri, local_id)
			return	None
		lines = out.splitlines()
		if len(lines)==0:
			return	None
		line = lines[0]
		if line.startswith("Connecting"):
			line = lines[1]
		if not line.startswith(":"):
			self.serror("expected display but found %s" % escape_newlines(line), uri, local_id)
			return	None
		display = line
		self.slog("found display=%s" % display, uri, local_id)
		return	display



def main():
	from winswitch.objects.server_settings import ServerSettings
	from winswitch.util.config import get_local_server_config
	ss = get_local_server_config() or ServerSettings()
	LibvirtServerUtil(ss, None, None, None, None)
	#ssu.detect_sessions()

if __name__ == "__main__":
	main()
