#!/usr/bin/env python
#
#   ConVirt   -  Copyright (c) 2008 Convirture Corp.
#   ======
#
# ConVirt is a Virtualization management tool with a graphical user
# interface that allows for performing the standard set of VM operations
# (start, stop, pause, kill, shutdown, reboot, snapshot, etc...). It
# also attempts to simplify various aspects of VM lifecycle management.
#
#
# This software is subject to the GNU General Public License, Version 2 (GPLv2)
# and for details, please consult it at:
#
#    http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
# 
#
# author : Jd <jd_jedi@users.sourceforge.net>
#

# Xen specific ui implementation required by the client.

import gtk, gtk.glade, gobject
import traceback

from convirt.core.model.PlatformUIHelper import PlatformUIHelper
import convirt.core.utils.constants
from convirt.core.platforms.xen.xen_constants import *

from convirt.core.utils.utils import get_path, get_prop, is_host_remote


from convirt.core.utils.utils import XMConfig
constants = convirt.core.utils.constants 


## TODO : Move this to ui utils/platform utils.
from convirt.client.dialogs import showmsg
#### TODO :
 # Make left_nav partof the callback             

### TODO : Move this out... as a part of callback.
from convirt.core.utils.NodeProxy import Node

my_platform = convirt.core.platforms.xen.xen_constants.my_platform
(path, gladefile) = get_path("xenui.glade")

from XenSettings import XenSettingsDlg
from convirt.client.VMInfoHelper import VMInfoHelper

class XenUIHelper(PlatformUIHelper):
    def __init__(self, ui_context, platform_config):
        PlatformUIHelper.__init__(self, ui_context, platform_config)

    def show_add_server_dialog(self, mode, group_name, platform,
                               existing_node=None,
                               parentwin=None):
        
        add_node_dlg = AddNodeDialog(self.manager,
                                     self.client_config,
                                     self.left_nav)

        if existing_node is not None and existing_node.platform != my_platform :
            add_node_dlg.dialog.destroy()
            raise Exception("Wrong Server passed. Expecting %s, got %s" %
                            (my_platform, existing_node.platform))
            
        ret = add_node_dlg.show(mode, group_name, platform = my_platform,
                                existing_node = existing_node,
                                parentwin = parentwin)
        return ret


    def get_vm_settings_dialog(self, mode, ctx, mainwin):
        return XenSettingsDlg(mode, ctx, mainwin)

    def get_vm_info_helper(self):
        return XenVMInfoHelper()



class AddNodeDialog:
    """ Class that handles adding new managed node to the host"""
    
    # mapping for the combo box. Sequence related to entries in the
    # comobo box
    protocols = ["tcp", "ssl", "ssh_tunnel"]
    
    def __init__(self, manager, client_config, left_nav):
        """ Constructor"""
        wtree = gtk.glade.XML(gladefile, "AddNode")
        self.client_config = client_config
        self.left_nav = left_nav
        self.manager = manager

        
        self.dialog = wtree.get_widget("AddNode")
        
            
        self.hostname = wtree.get_widget("add_node_hostname")

        self.default_ssh_port = 22
        self.default_xen_protocol = "tcp"
        self.default_xen_port = 8006
        self.default_use_keys = False
        self.default_migration_port = 8002
        
        self.protocol_combo = wtree.get_widget("add_node_xen_protocol")
        self.xen_port = wtree.get_widget("add_node_xen_port")
        self.ssh_port = wtree.get_widget("add_node_ssh_port")
        self.migration_port = wtree.get_widget("add_node_migration_port")
        self.username = wtree.get_widget("add_node_username")
        self.password = wtree.get_widget("add_node_password")
        self.use_keys_checkbox = wtree.get_widget("add_node_use_keys_checkbox")

        self.advanced = wtree.get_widget("advanced_expander")
        
        #self.protocol_combo.set_editable(False)
        # NOTE :set activate_default for widgets done in glade file.
        self.dialog.set_default_response(gtk.RESPONSE_OK)
                
        # setup handlers
        wtree.signal_connect("on_add_node_cancelbutton_clicked",
                             self.on_cancel_button_clicked)
        wtree.signal_connect("on_add_node_okbutton_clicked",
                             self.on_ok_button_clicked)
        wtree.signal_connect("on_add_node_use_keys_checkbox_toggled",
                             self.on_use_keys_toggled)
            
        default_use_keys = client_config.get(XMConfig.CLIENT_CONFIG,
                                             constants.prop_default_use_keys)
        if default_use_keys:
            self.default_use_keys = eval(default_use_keys)

        default_ssh_port = client_config.get(XMConfig.CLIENT_CONFIG,
                                             constants.prop_default_ssh_port)
        if default_ssh_port:
            self.default_ssh_port = int(default_ssh_port)

        xen_defaults = convirt.core.platforms.xen.XenPlatform.XenPlatform.get_defaults()
        
        self.default_xen_port = int(get_prop(xen_defaults,
                                             prop_default_xen_port,
                                             self.default_xen_port))

        self.default_migration_port =int(get_prop(xen_defaults,
                                             prop_default_migration_port,
                                             self.default_migration_port))

        self.default_xen_protocol = get_prop(xen_defaults,
                                             prop_default_xen_protocol,
                                             self.default_xen_protocol)
        
        #print "ssh_port is ", self.default_ssh_port, type(self.default_ssh_port)
        self.init()

    def on_use_keys_toggled(self, widget):
        use_keys = widget.get_active()
        #self.username.set_sensitive(not use_keys)
        self.password.set_sensitive(not use_keys)

    def init(self):
        self.protocol_combo.set_active(self.protocols.index(self.default_xen_protocol))
        self.ssh_port.set_text(str(self.default_ssh_port))
        self.migration_port.set_text(str(self.default_migration_port))
        self.xen_port.set_text(str(self.default_xen_port))
        self.username.set_text("root")
        self.password.set_text("")
        self.hostname.set_text("")
        self.hostname.set_sensitive(True)
        self.use_keys_checkbox.set_active(self.default_use_keys)
        self.advanced.set_expanded(self.default_use_keys)
        self.hostname.grab_focus()

    def show_in_edit_mode(self):
        """ Displays add node dialog in edit mode """
        self.init()
        self.hostname.set_sensitive(False)
        self.dialog.set_title("Edit Server")

        self.protocol_combo.set_active(self.protocols.index(self.existing_node.protocol))
        self.ssh_port.set_text(str(self.existing_node.ssh_port))
        self.migration_port.set_text(str(self.existing_node.migration_port))
        self.xen_port.set_text(str(self.existing_node.tcp_port))
        self.username.set_text(self.existing_node.username)
        self.password.set_text("")
        self.hostname.set_text(self.existing_node.hostname)
        if self.existing_node.use_keys is None:
            self.existing_node.use_keys = False
        self.use_keys_checkbox.set_active(self.existing_node.use_keys)
        self.advanced.set_expanded(self.existing_node.use_keys)

        self.hostname.grab_focus()

        ret = self.dialog.run()
        self.dialog.destroy()
        return ret
        
    def show(self, mode, group_name = None, platform = None,
             existing_node = None,
             parentwin = None):
        """ Displays add node dialog"""
        self.mode = mode
        self.parentwin = parentwin
        self.existing_node = existing_node
        self.group_name = group_name
        self.platform = platform
        
        if parentwin is not None:
            self.dialog.set_transient_for(parentwin)

        if mode == "add":
            return self.show_in_add_mode()
        else:
            return self.show_in_edit_mode()
        

    def show_in_add_mode(self):
        self.init()
        self.dialog.set_title("Add Server")
        ret = self.dialog.run()

        if ret == gtk.RESPONSE_DELETE_EVENT:
            self.dialog.destroy()


    def on_cancel_button_clicked(self, widget):
       """  Cancel on add node dialog """
       self.dialog.destroy()
       
    def on_ok_button_clicked(self, widget):
       """  Ok  button on add node """
       # validate parameters
       if self.hostname.get_text() == "":
           showmsg("Please enter valid host name")
           return

       if self.xen_port.get_text() == "":
           showmsg("Please enter valid xen port")
           return

       if self.ssh_port.get_text() == "":
           showmsg("Please enter valid ssh port")
           return

       if self.migration_port.get_text() == "":
           showmsg("Please enter valid migration port")
           return

       remote = is_host_remote(self.hostname.get_text())
       
       # allow for local host to be added.
       #if not remote:
       #    showmsg("Local host can not be added")
       #    return
       
       # create a new node
       ndx = self.protocol_combo.get_active()
       if ndx > -1:
           selected_protocol = self.protocols[ndx]
       else:
           showmsg("Invalid selection of protocol")

       try:
           factory = self.manager.getFactory(self.platform)
           node = factory.create_node( platform = self.platform, 
                                       hostname = self.hostname.get_text(),
                                       username = self.username.get_text(),
                                       password = self.password.get_text(),
                                       is_remote = remote, 
                                       protocol = selected_protocol,
                                       tcp_port = int(self.xen_port.get_text()),
                                       migration_port = int(self.migration_port.get_text()),
                                       ssh_port = int(self.ssh_port.get_text()),
                                       use_keys = self.use_keys_checkbox.get_active(),
                                       address  = self.hostname.get_text()
                                       )
           node.connect()

           if self.mode == "edit":
               self.left_nav.remove_node(self.existing_node, self.group_name)
               # do cleanup for the main thread.
               # invading privacy of node uggh
               if self.existing_node is not None and \
                      self.existing_node._node_proxy is not None: 
                   try:
                       Node.clean_locals(self.existing_node.node_proxy)
                   except Exception, ex:
                       print "Exception while cleaning locals ", ex
                       pass
           self.left_nav.add_node(node, self.group_name)
       except Exception , ex:
           msg=None
           import socket
           if isinstance(ex, socket.error):
               if (type(ex.args) is tuple) and (len(ex.args) > 1):
                   (code, msg) = (ex.args[0], ex.args[1])
                   
                   # this is pure huristic and depends on how code is arranged.
                   # the errors from phelper comes out as a CommunicationsException
                   if code == 113: 
                       msg = "Could not connect to Xend daemon on %s:%s. \n Please make sure the Xend server is running and is accesible." % (node.hostname, str(node.tcp_port))
           traceback.print_exc()
           #try:
           #    if node is not None and node.node_proxy is not None:
           #        Node.clean_locals(node.node_proxy)
           #except Exception, e:
           #    pass
           if msg:
               showmsg(msg)
           else:
               showmsg(ex)
           return
       # username and password are not stored

       # clear the passwords from memory.
       self.init()
       self.dialog.destroy()



class XenVMInfoHelper(VMInfoHelper):
    def __init__(self):
        VMInfoHelper.__init__(self)


    # Add Xen specific items 
    def get_category_keys(self):
        cat_keys = VMInfoHelper.get_category_keys(self)
        boot_base_keys = cat_keys["BOOT"]
        xen_boot_keys = [ ("kernel", "Kernel"),
                          ("ramdisk", "Ramdisk"),
                          ("bootloader", "Bootloader"),
                         ]
        for ndx in range(0,3):
            boot_base_keys.insert(ndx, xen_boot_keys[ndx])

        return cat_keys
