#!/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>
#


# All classes in thse files are required Interfaces for managing
# machines/host/nodes with virtualization technology.

import sys,os,re,types, traceback
import pprint
        
from convirt.core.utils.utils import XMConfig, constants, search_tree,Poller

from convirt.core.model.ManagedNode import ManagedNode

from convirt.core.model.VM import VM
from convirt.core.model.VNode import VNode

from KVMDomain import *
from KVMProxy import KVMProxy
from kvm_constants import *

from convirt.core.utils.phelper import PHelper
from convirt.core.utils.phelper import HostValidationException,\
     AuthenticationException, CommunicationException




class KVMNode(VNode):
    """
    Interface that represents a node being managed.It defines useful APIs
    for clients to be able to do Management for a virtualized Node
    """                

    def __init__(self,
                 hostname = None,
                 username= Node.DEFAULT_USER,
                 password=None,
                 isRemote=False,
                 ssh_port = 22,
                 migration_port = 8002,
                 helper = None,
                 store = None,
                 use_keys = False,
                 address = None):

        VNode.__init__(self,
                       "kvm",
                       store,
                       hostname,
                       username, password,
                       isRemote,
                       ssh_port,
                       helper,
                       use_keys,
                       address)


        self.migration_port = migration_port 

    def get_auto_config_dir(self):
        return "/etc/kvm/auto"

    #def get_config_dir(self):
    #    return '/etc/kvm'


    def is_hvm():
        return True

    def is_image_compatible(self, image):
        if image:
            # can run all hvm images and
            #can not run non-hvm images (at least for now)
            return image.is_hvm()
        return False

    # Factory method to create vm
    def new_config(self, filename):
        return KVMConfig(self, filename)

    def new_vm_from_config(self, config):
        return KVMDomain(self, config=config)

    def new_vm_from_info(self, info):
        return KVMDomain(self, vm_info=info)


    def get_running_vms(self):
        current_dict = {}
        vm_info_list = self.get_vmm().get_vms()
        for k, vm_info in vm_info_list.iteritems():
            vm = self.new_vm_from_info(vm_info)
            current_dict[k] = vm

        return current_dict


    def _init_vmm(self):
        return KVMProxy(self)

    def get_metric_snapshot(self):
        # do top -b -p pids to get the info and stuff it here.
        # use std names
        pid_name_list = [(vm["pid"],vm["name"]) for vm in self.get_doms() \
                        if vm.get_state() == VM.RUNNING ]

        pid_name_map = dict(pid_name_list)
        pids = pid_name_map.keys()


        pid_string = "99999999999"
        if pids :
            pid_string = str(pids).replace("[", "").replace("]","")
            
        FRAME_CMD = 'top -b -n 2 -d 1'
        if pid_string:
            FRAME_CMD = FRAME_CMD + " -p " + pid_string
    
        """returns a dictionary containing metrics for all running domains
        in a frame"""

        # construct the frame: Map
        # Top level / node level metrics : frame[toplevel] = value
        # followed by each domain
        #   frame [domain_name] = metrics map

        #
        frame = {} # the output metric frame (dict of dict's)
        (retbuf, retcode) = self.node_proxy.exec_cmd(FRAME_CMD,
                                                     self.exec_path)
        if retcode:
            print FRAME_CMD
            print "ERROR : cmd = (%s) \n retcode is not 0, error :%s" % (FRAME_CMD,
                                                                         retbuf)
            return None

        # process and fill frame buffer
        frame_buffers = re.split("top -.*\n", retbuf)
        if len(frame_buffers) < 2:
            return None

        fbuffer = frame_buffers[1]
        
        #print  retbuf, FRAME_CMD, pids, pid_string
        m = re.search("Cpu\\(s\\):.*\n", fbuffer)
        tokens = m.group().replace(':', ',').split(',  ')
        total_cpu = reduce(lambda x,y: x + y,
                           [ float(tokens[ndx].split('%')[0]) for ndx in range(1,4)])
        mem =  re.search("Mem:[ \t]+(\d+)k total,[ \t]+(\d+).*\n", fbuffer)
        total_mem = int(mem.group(1))/1000  # in MB
        used_mem = int(mem.group(2))/1000   # in MB


        #print "Total CPU %f , mem %f" % (total_cpu, (float(used) * 100 /total))

        

        lines = fbuffer.split("\n")
        ndx = 0
        for line in lines:
            ndx += 1
            if re.search("PID USER", line):
                break




        frame['RUNNING_VMs'] = 0
        frame['PAUSED_VMs']  = 0
        frame['CRASHED_VMs'] = 0

        frame['VM_TOTAL_CPU'] = 0.0  # not normalized cpu %
        frame['VM_TOTAL_MEM'] = 0.0  # memory used (not %)
        frame['VM_TOTAL_CPU(%)'] = 0.0
        frame['VM_TOTAL_MEM(%)'] = 0.0

        cpu_info = self.get_cpu_info()
        nr_cpus = cpu_info[key_cpu_count]
        for line in lines[ndx:]:
            d_frame = {}
            if line:
                #print line
                elem = line.split()
                cpu_pct = float(elem[8])
                mem_pct = float(elem[9])
                #print "VM CPU %f MEM %f"% (cpu_pct, mem_pct)
                
                d_frame["CPU(%)"] = cpu_pct
                d_frame["MEM(%)"] = mem_pct 
                d_frame["NAME"] = pid_name_map.get(elem[0])
                d_frame["PID"] = elem[0]
                d_frame['STATE'] = VM.RUNNING
                
                if nr_cpus > 1: # adjust the utilization to 100%
                    d_frame['CPU(%)'] = str(float(d_frame['CPU(%)']) / nr_cpus)


                d_frame['NETRX(k)'] = 0
                d_frame['NETS'] = 0
                d_frame['NETTX(k)'] = 0
                
                
                d_frame['VBDS'] = 0
                d_frame['VBD_OO'] = 0 
                d_frame['VBD_RD'] = 0
                d_frame['VBD_WR'] = 0

                self.augment_storage_stats(d_frame["NAME"], d_frame)
                # runnung counts
                frame['RUNNING_VMs'] += 1

                frame['VM_TOTAL_CPU'] += cpu_pct  # not normalized cpu %
                frame['VM_TOTAL_MEM'] += (mem_pct * total_mem)/100 # memory used (not %)
                frame['VM_TOTAL_CPU(%)'] += float(d_frame['CPU(%)'])
                frame['VM_TOTAL_MEM(%)'] += mem_pct

                frame[d_frame["NAME"]] = d_frame

        # build info from node info
        cpu_str = str(cpu_info[key_cpu_count]) + " @ " + \
                  str(cpu_info[key_cpu_mhz]) + "MHz"
        frame['SERVER_CPUs'] = cpu_str
                
        frame['SERVER_MEM'] =  str(self.get_memory_info()[key_memory_total]) + "M"
        info = self.get_platform_info()
        if info and info.get(key_version):
            frame['VER'] = info[key_version]

        self.update_storage_totals(frame)
        #pprint.pprint(frame)
        return frame
        
    
    def get_platform_info(self):
        vmm_info = self.get_vmm_info()
        return vmm_info

    def get_platform_info_display_names(self):
        return { key_version : display_version }


    

# Test code
if __name__ == "__main__":
    test_domu = "test"
    host = "localhost"
    dom_file = '/etc/kvm/test'
    dom_2b_started = 'test'
    dom_2b_shutdown = 'test'

    username = 'root'
    print "Enter password "
    passwd = sys.stdin.readline().strip()

    
    # basic connectivity
    remote = True
    if not remote:
        host = "localhost"
    else:
        host = '192.168.12.101'
        test_domu = "test"
        dom_file = '/etc/kvm/test'
        dom_2b_started = 'test'
        dom_2b_shutdown = 'test'
        
    managed_node = KVMNode(hostname=host,
                           username = username,
                           password = passwd,
                           isRemote=remote)

    ## list doms, knows and running
    names = managed_node.get_dom_names()

    
    for dom_name in names:
        print dom_name, managed_node.get_state(dom_name)

#    managed_node.start_dom("ubuntu_vm")

    for dom_name in names:
        print dom_name, managed_node.get_state(dom_name)

    ## create/destroy dom
    
    

    
    
