#!/usr/bin/env python
#------------------------------------------------------------------------------
# Copyright 2008-2012 Istituto Nazionale di Fisica Nucleare (INFN)
#
# Licensed under the EUPL, Version 1.1 only (the "Licence").
# You may not use this work except in compliance with the Licence.
# You may obtain a copy of the Licence at:
#
# http://joinup.ec.europa.eu/system/files/EN/EUPL%20v.1.1%20-%20Licence.pdf
#
# Unless required by applicable law or agreed to in
# writing, software distributed under the Licence is
# distributed on an "AS IS" basis,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied.
# See the Licence for the specific language governing
# permissions and limitations under the Licence.
#------------------------------------------------------------------------------
"""
WNoD process running on the Hypervisor.
"""

import ConfigParser
import SocketServer
import commands
import fnmatch
import libvirt  # @UnresolvedImport
import libxml2  # @UnresolvedImport
import logging  # @UnusedImport
import logging.handlers  # @UnusedImport  still used??
import optparse
import os
import pickle
import shutil
import socket
import sys
import threading
import time
import traceback
import urllib
import urllib2

try:
    from xml.etree import ElementTree
except ImportError:
    try:
        from elementtree import ElementTree
    except ImportError:
        sys.exit("package ElementTree is missing, exiting.")

#__version__ = (1, 1, 5)
__short_name__ = os.path.basename(os.path.splitext(sys.argv[0])[0])
__dir_name__ = os.path.dirname(sys.argv[0])

try:
    from wnodes.utils import utils
    from wnodes.utils.utils import synchronized
    try:
        from wnodes.utils import  wsocket
    except ImportError:
        sys.exit("%s: python module 'wsocket' not found." % __short_name__)
except ImportError:
    sys.exit("%s: python module 'utils' not found." % __short_name__)

try:
    from wnodes.hypervisor import net
except ImportError:
    sys.exit("%s: python module 'net' not found." % __short_name__)

try:
    from wnodes.utils import plugins_generic
except ImportError:
    sys.exit("%s: python module 'plugins_generic' not found." % __short_name__)


# No WNoD parameters should be declared as global.
# All access to the internal data structures must be synchronized.
class WnodHypervisor(wsocket.ClientRequestHandler):
    """ Define the behavior of the WNoD hypervisor. """

    # lock used to rotate logfiles
    LOCK_LOG = threading.Lock()

    # locks used to handle VM management
    LOCK_REGENERATE = threading.Lock()
    LOCK_DESTROY = threading.Lock()
    LOCK_CREATE = threading.Lock()

    # locks used to synchronize data structures
    LOCK_VM = threading.Lock()
    LOCK_IMAGE = threading.Lock()
    LOCK_UPDATE_IMAGE = threading.Lock()

    def __init__(self, NS_HOST, NS_PORT):

        wsocket.ClientRequestHandler.__init__(self, NS_HOST, NS_PORT)

        self.NS_HOST = NS_HOST
        self.NS_PORT = NS_PORT
        self.config = {}
        self.LIBVIRT_INSTANCE = ''

        # Get configuration from WNODES Name Server
        conf = self._retrieveConfig(self.NS_HOST, self.NS_PORT)
        
        if conf[0] == 1:
            sys.exit(conf[1])

        self.DEFAULT_LOGGING_INFO = logging.INFO

        # set up logging
        try:
            self.wnodes_logger = logging.getLogger('WnodLogger')
            self.wnodes_logger.setLevel(self.DEFAULT_LOGGING_INFO)
            formatter = logging.Formatter(
                            "%(asctime)s - %(levelname)s - %(message)s")
            handler = utils.CompressedRotatingFileHandler(
                        os.path.join(__dir_name__,
                                     "/var/log/wnodes/hypervisor",
                                     self.config['LOG_FILE_NAME']),
                        maxBytes=self.config['MAX_LOG_FILE_SIZE'],
                        backupCount=self.config['MAX_COUNT_LOG_FILE'])
            handler.setFormatter(formatter)
            self.wnodes_logger.addHandler(handler)
        except Exception:
            sys.exit('Problem initializing logging:'
                     + ' %s, %s, %s' % (sys.exc_info()[:]))

        # Verify config
        self._verifyMadatoryConfigParams()

        self.virtualMachines = {}
        self.vmImagesAvailable = {}
        self.resources = {}
        self.localDNS = {}

        self.server_name = commands.getoutput('hostname -s')

        if not os.path.isfile(self.config['SSH_KEY_FILE']):
            SSH_KEYGEN = commands.getstatusoutput(
                            'ssh-keygen -t rsa -f %s -N "" '
                            % self.config['SSH_KEY_FILE'])
            if not SSH_KEYGEN[0] == 0:
                sys.exit('SSH key generation failed')

        # Find the ENABLED_VLAN for me
        PIECE_MATCHED = False
        HOST_GROUPS = []
        HOSTNAME = self.server_name
        for KEY in self.config.keys():
            if KEY.startswith('HOST_GROUP'):
                HOST_GROUPS.append(KEY)

        for HOST_GROUP in HOST_GROUPS:
            for HOST_PARSING_RULE in self.config[HOST_GROUP].split(';'):
                HOSTNAME = self.server_name
                if fnmatch.fnmatch(HOSTNAME, HOST_PARSING_RULE):
                    PIECE_MATCHED = True
                    break
            if PIECE_MATCHED:
                HOST_GROUP = HOST_GROUP.split('_')
                ENABLED_VLAN = ('ENABLED_VLAN_%s_%s'
                                % (HOST_GROUP[1], HOST_GROUP[2]))
                break
        if PIECE_MATCHED == False:
            sys.exit('I could not found a rule for my hostname %s'
                     % self.server_name)
        elif not ENABLED_VLAN in self.config:
            sys.exit('There is no an ENABLED_VLAN for host group %s'
                     % '_'.join(HOST_GROUP))

        # Network initialization
        self.config['FILE_NETWORK_CURRENT_STATUS'] = \
            '/var/log/wnodes/hypervisor/hv_network_current_status'
        self.network = net.NetworkCfg(
                            self.config['FILE_NETWORK_CURRENT_STATUS'])
        self.configbridge = net.Brctl(self.network)
        self.configvlan = net.Vlan(self.network)
        self.configtap = net.Tap(self.network)
        self.configif = net.Ifconfig(self.network)

        if self.configbridge.bridgeExist('default') == 1:
            defaultbridge = self.configbridge.createBridge('default')
            self.configif.ifUp(defaultbridge)
            self.configif.configAddress(self.network.default_nic, '0.0.0.0')
            self.configif.configAddress(defaultbridge, self.network.ipaddress,
                                        self.network.ipmask)
            commands.getoutput('route del default')
            commands.getoutput('route add default gw ' +
                               self.network.default_gw +
                               ' dev ' + defaultbridge)
            self.configbridge.addIfToBridge(self.network.default_nic)
            self.network.networkStatus.setdefault(self.network.default_nic +
                                                  '.default',
                                                  ['vlan',
                                                   'UP',
                                                   'default',
                                                   defaultbridge,
                                                   'localhost'])

        # Create a service bridge use to export shared storage to the VMs
        if self.configbridge.bridgeExist('service') == 1:
            bridgeid = self.configbridge.createBridge('service')
            self.configif.ifUp(bridgeid)
            self.configif.configAddress(bridgeid,
                                        self.config['SERVICE_NIC_IP'],
                                        self.config['SERVICE_NIC_IP_MASK'])

            # configure dnsmasq
            # TODO check: unused variable stop
            stop = commands.getstatusoutput('service dnsmasq stop')
            time.sleep(2)
            # TODO check: unused variable start
            start = commands.getstatusoutput('dnsmasq -i %s --dhcp-option=3 --dhcp-option=6 -F %s,%s'
                                             % (str(bridgeid),
                                                self.config['DNS_RANGE'],
                                                self.config['DNS_LEASE_TIME']))

        if self.config['ENABLE_MIXED_MODE'].upper() == 'NO':

            vm_bait_image_path, vm_bait_arch, vm_bait_image_format = \
                self._set_vm_image(self.config['BAIT_IMG_TAG'])

            #Request a new hostname for the bait vm host
            self.msg = {'acquire_Node':
                        ['BAIT_HOST',
                         self.server_name,
                         'OPEN',
                         self.config[ENABLED_VLAN]]}
            self.output = self.sendRequest(self.NS_HOST,
                                           self.NS_PORT,
                                           self. msg)
            if self.output[0] == 0:
                self.baitNetParameters = self.output[1]

                if self.baitNetParameters[0] == 1:
                    sys.exit(self.baitNetParameters[1])
                else:
                    #self.bait_name = 'vwn-103-13-26-bait^^00:16:3e:01:00:05'

                    self.baitNetParameters = self.baitNetParameters[1]

                    BAIT_MAC = self.baitNetParameters[2]
                    BAIT_VLAN = self.baitNetParameters[1]
                    if BAIT_VLAN == 'DEFAULT_VLAN':
                        BAIT_VLAN = 'default'
                    BAIT_HOSTNAME = self.baitNetParameters[0]
                    self.bait_name = BAIT_HOSTNAME

            else:
                sys.exit(self.output[1])
            # Configure network for bait host
            if BAIT_VLAN == 'default':
                bridgeid = 'br.default'
                pass
            else:
                if self.configvlan.vlanExist(BAIT_VLAN) == 1:
                    vlanif = self.configvlan.createVlan(BAIT_VLAN)
                    self.configif.ifUp(vlanif)
                    if self.configbridge.bridgeExist(BAIT_VLAN) == 1:
                        bridgeid = self.configbridge.createBridge(BAIT_VLAN)
                        self.configif.ifUp(bridgeid)
                        self.configbridge.addIfToBridge(vlanif)

                elif self.configvlan.vlanExist(str(BAIT_VLAN)) == 0:
                    if self.configbridge.bridgeExist(BAIT_VLAN) == 0:
                        bridgeid = 'br.%s' % str(BAIT_VLAN)
                        pass

            #Instantiate the bait vm
            # Define XML for BAIT VM

            bait = ElementTree
            domain = bait.Element('domain', {'type': 'kvm'})
            name = bait.SubElement(domain, 'name')
            name.text = BAIT_HOSTNAME
            uuid = bait.SubElement(domain, 'uuid')
            uuid.text = '%s' % (utils.guid())
            memory = bait.SubElement(domain, 'memory')
            memory.text = str('%s%s' % (self.config['BAIT_VM_RAM'], '000'))
            currentmemory = bait.SubElement(domain, 'currentmemory')
            currentmemory.text = str('%s%s'
                                     % (self.config['BAIT_VM_RAM'], '000'))
            vcpu = bait.SubElement(domain, 'vcpu')
            vcpu.text = '1'
            operatingSistem = bait.SubElement(domain, 'os')
            ostype = bait.SubElement(operatingSistem, 'type',
                                     {'arch': vm_bait_arch, 'machine': 'pc'})
            ostype.text = 'hvm'
            bait.SubElement(operatingSistem, 'boot', {'dev': 'hd'})
            features = bait.SubElement(domain, 'features')
            bait.SubElement(features, 'acpi')
            bait.SubElement(features, 'pae')
            bait.SubElement(domain, 'clock', {'offset': 'utc'})
            on_poweroff = bait.SubElement(domain, 'on_poweroff')
            on_poweroff.text = 'destroy'
            on_reboot = bait.SubElement(domain, 'on_reboot')
            on_reboot.text = 'restart'
            on_crash = bait.SubElement(domain, 'on_crash')
            on_crash.text = 'destroy'
            devices = bait.SubElement(domain, 'devices')
            emulatore = bait.SubElement(devices, 'emulator')
            emulatore.text = '/usr/libexec/qemu-kvm'
            disk = bait.SubElement(devices, 'disk',
                                   {'type': 'file', 'device': 'disk'})
            bait.SubElement(disk, 'source', {'file': vm_bait_image_path})
            bait.SubElement(disk, 'target', {'dev': 'hda', 'bus': 'ide'})
#            bait.SubElement(disk, 'address',
#                            {'type': 'drive', 'controller': '0',
#                             'bus': '0', 'unit': '0'})
            bait.SubElement(disk, 'driver', {'type': vm_bait_image_format})
            bait.SubElement(devices, 'controller',
                            {'type': 'ide', 'index': '0'})
            interface = bait.SubElement(devices, 'interface',
                                        {'type': 'bridge'})
            bait.SubElement(interface, 'mac', {'address': BAIT_MAC})
            bait.SubElement(interface, 'source', {'bridge': bridgeid})
            bait.SubElement(interface, 'model', {'type': 'virtio'})
            serial = bait.SubElement(devices, 'serial', {'type': 'pty'})
            bait.SubElement(serial, 'target', {'port': '0'})
            console = bait.SubElement(devices, 'console', {'type': 'pty'})
            bait.SubElement(console, 'target', {'port': '0'})
            bait.SubElement(devices, 'input', {'type': 'mouse', 'bus': 'ps2'})
            bait.SubElement(devices, 'graphics',
                            {'type': 'vnc', 'port': '-1',
                             'autoport': 'yes', 'keymap': 'en-us'})
            bait.SubElement(devices, 'memballoon', {'model': 'virtio'})

            domZero = libvirt.open(None)
            BAIT_XML = bait.tostring(domain)

            BAIT_ISTANCE = domZero.defineXML(BAIT_XML)

            if BAIT_ISTANCE.create() == 0:

                BAIT_ID = BAIT_ISTANCE.ID()
                BAIT_PARAMETERS = {'HOSTNAME': BAIT_HOSTNAME,
                                   'MAC': BAIT_MAC,
                                   'VLAN': BAIT_VLAN,
                                   'TAP': 'vnet',
                                   'VM_TYPE': 'BAIT_HOST'}

                self.virtualMachines[BAIT_ID] = ['NoJobID',
                                                 BAIT_HOSTNAME,
                                                 ['BAIT', 'RESERVED'],
                                                 BAIT_PARAMETERS]
                domZero.close()
            else:
                domZero.close()
                sys.exit('Failed to create the BAIT virtual host')

        elif self.config['ENABLE_MIXED_MODE'].upper() == 'YES':
            # Mixed mode enabled. Start the bait as service on the hypervisor
            BAIT_STATUS = commands.getstatusoutput(
                                'service wnodes_bait status')
            #self.updateLog('%s' % str(self.config['BAIT_PORT']))
            #self.updateLog('%s' % str(BAIT_STATUS))
            if 'not running' in BAIT_STATUS[1]:
                BAIT_START = commands.getstatusoutput(
                                'service wnodes_bait start')

                if BAIT_START[0] != 0:
                    sys.exit('Failed to start the bait server %s'
                             % str(BAIT_START))

            msg = {'register_Node': [self.server_name]}
            output = self.sendRequest(self.NS_HOST, self.NS_PORT, msg)
            self.bait_name = self.server_name

    def _avail_disk_space(self, path):
        """Return disk usage statistic about available space in the given path.
        Returned value is the amount free space, in bytes.
        """
        st = os.statvfs(path)
        free = st.f_bavail * st.f_frsize
        return free

    def _checkSSH(self, host):
        serverSocket = socket.socket()
        serverSocket.settimeout(0.25)
        try:
            serverSocket.connect((host, 22))
            return True
        except socket.error:
            return False

    def _copy_image_locally(self, vm_info):
        """
        Provide a local copy (in "local_image_dir") of the image:
        different behaviours parsing the first term:
        http:address/file.img -> wget from http address
        file:/gpfs/file.img -> cp from gpfs file-system
        vm_info is an array of [repo_type, vm_path, ... (arch, format)]
        """

        local_repo_dir = self.config['LOCAL_REPO_DIR']

        # Create local_image_dir if not exist
        if not os.path.exists(local_repo_dir):
            #  420 is the octal form for: rw- r-- r--
            os.makedirs(local_repo_dir, 420)
            self.updateLog('Successfully created directory %s'
                           % local_repo_dir)

        repo_type = vm_info[0].lower()
        vm_path = vm_info[1]
        #vm_image = os.path.basename(vm_path)
        #vm_local_path = os.path.join(self.config['LOCAL_REPO_DIR'], vm_image)

        # Local copy for paths starting in "file"
        if repo_type == "file":
#            print("copy_cmd: cp vm_path: %s, self.local_image_dir: %s"
#                  %(vm_path, self.local_image_dir))
            # download version using shutil.copy
            try:
                shutil.copy(vm_path, local_repo_dir)
                return 0
            except:
                msg = '%s, %s, %s' % sys.exc_info()[:]
                self.updateLog(' -- Copy of the VM image file, Failed: \n%s'
                               % msg, "error")
                return 1
            # download version using shell cp
            # cmd = 'cp %s %s' % (vm_path, local_repo_dir)
            # TODO check: unused variable use_timeout
            # use_timeout = 'no'
            # copy_cmd = commands.getstatusoutput(cmd)

        # Remote copy for paths starting in "http"
        elif repo_type == "http":
            try:
                # Check image size
                try:
                    f = urllib2.urlopen('http://'+vm_path)
                    vm_path = 'http://'+ vm_path
                except:
                    f = urllib2.urlopen(vm_path)
                vm_size = int(f.headers["Content-Length"])
                # Check available space in the download directory
                free_space = int(self._avail_disk_space(local_repo_dir))
                if vm_size < free_space:
                    # download version using urllib
                    file_name = os.path.basename(vm_path)
                    local_file_path = os.path.join(local_repo_dir, file_name)
                    urllib.urlretrieve(vm_path, local_file_path)
                    return 0
                else:
                    self.updateLog('Error downloading the VM image file:' +
                                   'Not enough free space in %s'
                                    % local_repo_dir, "error")
                    return 1
            except:
                msg = '%s, %s, %s' % sys.exc_info()[:]
                self.updateLog(' -- Fail downloading the VM image file: \n%s'
                               % msg, "error")
                
                return 1
#                 download version using shell wget
#                cmd = ('wget %s --no-verbose --directory-prefix=%s'
#                       % (vm_path, local_repo_dir))
#                 TODO check: unused variable use_timeout
#                use_timeout = 'no'
#                copy_cmd = commands.getstatusoutput(cmd)
#        if copy_cmd[0] != 0:
#            self.updateLog(' -- wget Failed:' + str(copy_cmd), "error")
#            return 1
#        else:
#            return 0

    def _load_plugin(self, name):

        mod = __import__(name)

        return mod

    def _menage_image_deletion(self, path_old_link, occurrence):
        """ Takes care about delete unused vm image files"""
        # count number of link to the image file
        if self._link_counter(path_old_link) <= occurrence:
            self.updateLog('No other links connected to IMAGE:' +
                           ' %s ' % (path_old_link), "debug")
            #initialize default value for path_old_link_used
            path_old_link_used = False
            # check if the image is in use connecting to libvirt API
            used_vm_images = self._get_used_vm_images()
            for vm_image in used_vm_images:
                if vm_image == path_old_link:
                    path_old_link_used = True
                    break
            if path_old_link_used == False:
                os.remove(path_old_link)
                self.updateLog('Removed %s ,' % (path_old_link) +
                               ' the image was not in use ' +
                               'and not connected to other links', "info")

    def _get_used_vm_images(self):
        conn = libvirt.openReadOnly(None)
        active_ids = conn.listDomainsID()
        val = []
        for i in active_ids:
            # create a connection for every vm_id
            vmconn = conn.lookupByID(i)
            # extract the xml description of the vm
            xmlstr = vmconn.XMLDesc(0)
            # parse xml string
            doc = libxml2.parseDoc(xmlstr)
            ctx = doc.xpathNewContext()
            # extract the value of domain -> device -> disk -> source file
            source_file = ctx.xpathEval("/domain/devices/disk[1]/source/@file")
            val.append(source_file and source_file[0].content)
        return val

    def _link_counter(self, file_path):
        '''
        return the total number of file and link, linked to a file
        and located in the same directory of the file
        '''
        count = commands.getoutput('find -L %s -samefile %s | wc -l'
                                   % (os.path.dirname(file_path), file_path))
        try:
            count = int(count)
            return count
        except:
            print "Error : no file found named %s" % file_path

    def _gen_vm_XML(self, vmParameters, vm_image_local_path, arch, form):
        """
        Generate a suitable command line to run a KVM-based VM.
        """
        self.updateLog(' Function: %s . Variables: %s, %s, %s, %s'
                       % (utils.whoami(), str(vmParameters),
                          vm_image_local_path, arch, form))

        #vmParameters = arguments[0]

        if 'ENABLEVIRTIO' in vmParameters:
            if  vmParameters['ENABLEVIRTIO'].upper() == 'YES':
                ETH_MODEL = 'virtio'
            else:
                ETH_MODEL = 'e1000'
        else:
            ETH_MODEL = 'e1000'

        # Define XML for VM

        vm = ElementTree
        domain = vm.Element('domain', {'type': 'kvm'})
        name = vm.SubElement(domain, 'name')
        name.text = vmParameters['HOSTNAME']
        uuid = vm.SubElement(domain, 'uuid')
        uuid.text = '%s' % (utils.guid())
        memory = vm.SubElement(domain, 'memory')
        memory.text = str('%s%s' % (vmParameters['MEM'], '000'))
        currentmemory = vm.SubElement(domain, 'currentmemory')
        currentmemory.text = str('%s%s' % (vmParameters['MEM'], '000'))
        vcpu = vm.SubElement(domain, 'vcpu')
        vcpu.text = str(vmParameters['CPU'])
        opearatingsystem = vm.SubElement(domain, 'os')
        ostype = vm.SubElement(opearatingsystem, 'type',
                               {'arch': arch, 'machine': 'pc'})
        ostype.text = 'hvm'
        vm.SubElement(opearatingsystem, 'boot', {'dev': 'hd'})
        features = vm.SubElement(domain, 'features')
        vm.SubElement(features, 'acpi')
        vm.SubElement(features, 'pae')
        vm.SubElement(domain, 'clock', {'offset': 'utc'})
        on_poweroff = vm.SubElement(domain, 'on_poweroff')
        on_poweroff.text = 'destroy'
        on_reboot = vm.SubElement(domain, 'on_reboot')
        on_reboot.text = 'restart'
        on_crash = vm.SubElement(domain, 'on_crash')
        on_crash.text = 'destroy'
        devices = vm.SubElement(domain, 'devices')

        # Define the Emulatar. It is modifed in order to use snaphost feature
        emulatore = vm.SubElement(devices, 'emulator')
        emulatore.text = '/usr/bin/wnodes_hypervisor/wnodes-qemu-kvm.sh'

        # Define fisrt disk with the OS use with snapshot option
        disk = vm.SubElement(devices, 'disk',
                             {'type': 'file', 'device': 'disk'})
        vm.SubElement(disk, 'source', {'file': vm_image_local_path})
        vm.SubElement(disk, 'target', {'dev': 'vda', 'bus': 'virtio'})

        if form == 'qcow2':
            vm.SubElement(disk, 'driver',
                          {'name': 'qemu', 'cache': 'writeback',
                           'type': form})
        else:
            vm.SubElement(disk, 'driver',
                          {'name': 'qemu', 'cache': 'writethrough',
                           'type': form})
#        vm.SubElement(disk, 'address',
#                      {'type': 'drive', 'controller': '0',
#                       'bus': '0', 'unit': '0'}
        # Define second disk used as work FS with rw rights
        if self.config['USE_LVM'].upper() == 'YES':
            disk1 = vm.SubElement(devices, 'disk',
                                  {'type': 'block', 'device': 'disk'})
            vm.SubElement(disk1, 'driver', {'name': 'qemu', 'cache': 'none'})
            vm.SubElement(disk1, 'source',
                          {'dev': os.path.join('/', 'dev',
                                    vmParameters['RELATIVE_LVM_PATH'])})
            vm.SubElement(disk1, 'target', {'dev': 'vdb', 'bus': 'virtio'})
        else:
            pass
        vm.SubElement(devices, 'controller', {'type': 'ide', 'index': '0'})

        # Define first Eth. It is used to provide direct connect with the HV
        interface = vm.SubElement(devices, 'interface', {'type': 'bridge'})
        vm.SubElement(interface, 'model', {'type': ETH_MODEL})
        vm.SubElement(interface, 'source', {'bridge': 'br.service'})

        # Define second Eth. It used to provide connection outside
        interface = vm.SubElement(devices, 'interface', {'type': 'bridge'})
        vm.SubElement(interface, 'mac', {'address': vmParameters['MAC']})
        vm.SubElement(interface, 'model', {'type': ETH_MODEL})
        vm.SubElement(interface, 'source', {'bridge': vmParameters['BRIDGE']})

        serial = vm.SubElement(devices, 'serial', {'type': 'pty'})
        vm.SubElement(serial, 'target', {'port': '0'})
        console = vm.SubElement(devices, 'console', {'type': 'pty'})
        vm.SubElement(console, 'target', {'port': '0'})
        vm.SubElement(devices, 'input', {'type': 'mouse', 'bus': 'ps2'})
        vm.SubElement(devices, 'graphics',
                      {'type': 'vnc', 'port': '-1',
                       'autoport': 'yes', 'keymap': 'en-us'})
        vm.SubElement(devices, 'memballoon', {'model': 'virtio'})

        # Returns an encoded string containing the XML data fo domanin
        VM_XML = vm.tostring(domain)
        return [0, VM_XML]

    def _check_image_file(self, image_path):
        """ check if the VM image has to be downloaded """
        if os.path.isfile(image_path):
            self.updateLog('IMAGE is already available in %s' % (image_path))
            return True
        else:
            self.updateLog('IMAGE is not available in : %s, '
                           % image_path
                           + 'marked to be downloaded')
            return False

    def _retrieveConfig(self, ns_host, ns_port):
        """
        Retrieve an updated HV configuration
        sending a request to the NameServer
        """
        msg = {'get_config_from_file': ['HV']}
        msg = str(pickle.dumps(msg).replace('\n', '1%2'))
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect((ns_host, int(ns_port)))
            sock.sendall(msg + '\n')
            socketfile = sock.makefile('r', 0)
            response = socketfile.readline().strip()
            sock.shutdown(2)
            sock.close()
            try:
                self.config = pickle.loads(response.replace('1%2', '\n'))[1]
                return [0, 'Configuration updated']
            except pickle.UnpicklingError:
                msg = 'Data received cannot be loaded'

                return [1, msg]
            except Exception:
                msg = ('Data received from cannot be loaded for this reason:' +
                       ' %s, %s, %s' % ((sys.exc_info()[:])))
                return [1, msg]
        except socket.error:
            msg = ('Server HOST %s:%s is unreachable. '
                   % (ns_host, str(ns_port)) +
                   'I cannot retrieve HOST configuration')
            return [1, msg]

    def _set_link(self, tag_path, image_path):
        """ check if the link has to be created
        this link has the same name of the related tag
        and it links to a VM image
        if an old link is found it will be update
        and the old image will be deleted if unused"""
        if os.path.islink(tag_path):
            path_old_link = os.path.realpath(tag_path)
            self.updateLog("Found an old link, in: %s, VM image linked: %s"
                           % (tag_path, path_old_link))
            if (path_old_link == image_path):
                self.updateLog('Link is already updated')
            else:
                # remove old vm Image file (if unused)
                self._menage_image_deletion(path_old_link, 2)
                # crate a new update link
                os.remove(tag_path)
                self.updateLog('Crating a link, tag: %s, VM image linked: %s'
                               % (tag_path, image_path))
                os.symlink(image_path, tag_path)
        else:
            # crate a new update link
            self.updateLog('Crating a link, tag: %s, VM image linked: %s'
                           % (tag_path, image_path))
            os.symlink(image_path, tag_path)

    def _configure_image(self, vm_info, vm_path):
        self.updateLog('Trying to configure vm Image: %s' % vm_path)
        vm_image = os.path.basename(vm_path)
        vm_local_path = os.path.join(self.config['LOCAL_REPO_DIR'], vm_image)
        # call optional plugin after image copy
        try:
            plugins_generic.post_copy_image(vm_local_path,
                                            self.updateLog,
                                            self.config['SSH_KEY_FILE'],
                                            self.server_name)
            #return 0
        except:
            self.updateLog(' -- Error executing post_copy_image plugin: '
                           + '%s,%s,%s'
                           % sys.exc_info()[:], "error")
            #return [1, 'Error executing post_copy_image plugin']

    def _get_vm_image_info(self, vm_tag):
        """
        retrieve vm image path and other information from vm tag

        return remote_path, arch, form
        """
        # retrieve vm image path and other information from vm tag
        image_info = {'get_image_info': [vm_tag]}
        # response will be in the form:
        # [0,[0, ['file', '/home/image/name', 'x86_64']]]
        response = self.sendRequest(self.NS_HOST, self.NS_PORT, image_info)
        status = response[0]
        if status == 0:

            # incapsule the array in  ret_info (retrieved information)
            retr_info = response[1]
            # extact the information
            status_2 = retr_info[0]
            if status_2 == 0:
                repo_type = retr_info[1][0]
                remote_path = retr_info[1][1]
                arch = retr_info[1][2]
                form = retr_info[1][3]
                dev = retr_info[1][4]
                return repo_type, remote_path, arch, form, dev
            else:
                raise Exception
        else:
            raise Exception

    #@synchronized(LOCK_UPDATE_IMAGE)
    def _set_vm_image(self, vm_tag):
        self.updateLog(' Function: %s . Variables: %s' % (utils.whoami(),
                                                          vm_tag))

        try:
            repo_type, remote_path, arch, form, dev = \
                self._get_vm_image_info(vm_tag)
        except:
            self.updateLog(" -- Error executing _get_vm_image_info: %s,%s,%s"
                           % sys.exc_info()[:], "error")
            raise Exception

        local_repo_dir = self.config['LOCAL_REPO_DIR']

        # check or prepare image repository folder
        if not os.path.isdir(local_repo_dir):
            os.makedirs(local_repo_dir, 420)
            self.updateLog('Successfully created directory %s'
                            % local_repo_dir)

        # set local path for vm image and tag
        vm_image_filename = os.path.basename(remote_path)
        vm_image_local_path = os.path.join(local_repo_dir,
                                           vm_image_filename)
        tag_local_path = os.path.join(local_repo_dir, vm_tag)

        self.updateLog('Successfully got vm_tag file name %s'
                       % str(tag_local_path))
        # UPDATES= False

        if self._check_image_file(vm_image_local_path):
            dowload_image = False
        else:
            dowload_image = True
        try:
            self._set_link(tag_local_path, vm_image_local_path)
        except:
            self.updateLog('Exception in : _set_link')

        # copy VM image if needed
        if dowload_image == True:
            #retr_info[1] = 'file', '/home/image/name', 'x86_64'
            # [repo_type, vm_path, arch, format]
            download_info = [repo_type, remote_path]
            self.updateLog('I have to download the new vm image available: %s'
                           % download_info)
            if self._copy_image_locally(download_info) == 0:
                # call optional plugin after image copy
                try:
                    vm_image_name = os.path.basename(remote_path)
                    vm_local_path = os.path.join(local_repo_dir,
                                                 vm_image_name)
                    plugins_generic.post_copy_image(
                        vm_local_path, self.updateLog,
                        self.config['SSH_KEY_FILE'], self.server_name, dev)
                    #return 0
                except:
                    self.updateLog(' -- Error executing post_copy_image ' +
                                   'plugin: %s' % sys.exc_info()[0], 'error')
                    #return [1, 'Error executing post_copy_image plugin']
            else:
                sys.exit("Problem while download of %s." % download_info)
        return [vm_image_local_path, arch, form]

    def _set_vlan(self, vlan):
        '''
        Return bridgeid
        Configure network on the HV, bridge and vlan
        required values:
        vlan (string)
        '''
        if vlan == 'DEFAULT_VLAN':
            vlan = 'default'

        # TODO. aumentare i controlli
        # check the exstence of the vlan
        if self.configvlan.vlanExist(vlan) == 0:
            # if vlan exist, set bridgeid as br.vlan
            bridgeid = ('br.%s' % str(vlan))
        else:
            # if vlan doesn't exist, create a new VLAN:
            # vconfig add NIC VLAN_ID, vconfig add br.default 2
            vlanif = self.configvlan.createVlan(vlan)

            if vlanif == 1:
                err_msg = ('HyperVisor failed creating a new Vlan :' +
                           ' vconfig add NIC %s' % vlan)
                self.updateLog(err_msg)
                #return [1, err_msg]
                self.nic = self.configvlan.netCfgIstance.default_nic
                vlanif = ("%s.%s" % (self.nic, vlan))
                try:
                    self.configif.ifUp(vlanif)
                except:
                    err_msg = 'Failed: "self.configif.ifUp( %s )"' % vlanif
                    self.updateLog(err_msg, "error")
                    raise
            else:
                self.configif.ifUp(vlanif)
                # add a bridge related to the vlan:
                # brctl addbr br.2
                bridgeid = self.configbridge.createBridge(vlan)
                if bridgeid == 1:
                    err_msg = ('HyperVisor failed creating a new bridge : ' +
                               'brctl addbr br.%s' % vlan)
                    self.updateLog(err_msg)
                    warn_msg = 'Trying to continue....'
                    self.updateLog(warn_msg)
                    bridgeid = ('br.%s' % str(vlan))
                try:
                    self.configif.ifUp(bridgeid)
                except:
                    err_msg = 'Failed: "self.configif.ifUp( %s )"' % bridgeid
                    self.updateLog(err_msg, "error")
                    raise
                # add a bridge interface
                # brctl addif br.2 br.default.2
                self.configbridge.addIfToBridge(vlanif)
        return bridgeid

    def _handleLVM(self, vmParameters, ACTION):
        self.updateLog(' Function: %s .' % utils.whoami() +
                       ' Variables: %s, %s' % (ACTION, vmParameters))

        if ACTION == 'CREATE':

            # find the Volume Group where I can create the requested LVM
            VOLUME_GROUP = self.config['VOLUME_GROUP'].split()
            if len(VOLUME_GROUP) > 0:
                for VG in VOLUME_GROUP:
                                    
                    PE = commands.getstatusoutput('vgdisplay -c %s' % VG)
                    
                    if PE[0] == 0:
                        # output im MB
                        try:
                            PE_SIZE = int(PE[1].split(':')[12])
                            FREE_PE = int(PE[1].split(':')[15])
                        except ValueError:
                            # due to a bug in lvm which left FD open
                            PE = PE[1].split().pop()
                            PE_SIZE = int(PE.split(':')[12])
                            FREE_PE = int(PE.split(':')[15])
                            
                        PE_NEEDED = (vmParameters['STORAGE'] * 1024 * 1024) / PE_SIZE
                                            
                        if FREE_PE > PE_NEEDED:
                            self.updateLog('This Volume Group %s has ' % VG +
                                                            'enough space/pe to create the LVM',
                                                            'info')
                            USE_THIS_VG = VG
                            FIND_FREE_PE = True
                            break
                        else:
                            self.updateLog('This Volume Group %s has not enough space '
                                                    % self.config['VOLUME_GROUP'] +
                                                    'to create the LVM', 'info')
                            FIND_FREE_PE = False
                    
                    else:
                        self.updateLog('%s: Problem ' % str(STORAGE[1]) +
                                       'retriving available space for ' +
                                       'Volume Group %s' % VG)
                        FIND_FREE_PE = False
                        
            else:
                self.updateLog('No volume group available in the configuration')
                FIND_FREE_PE = False
            
            if FIND_FREE_PE:
                
                LVM_NAME = 'lvm-%s-%s' % (vmParameters['HOSTNAME'],
                                          vmParameters['BATCH_JOBID'])
    
                vmParameters['RELATIVE_LVM_PATH'] = ('%s/%s'
                                                     % (USE_THIS_VG, LVM_NAME))
                vmParameters['VOLUME_GROUP'] = USE_THIS_VG
                vmParameters['LVM'] = LVM_NAME
    
                create_lvm = commands.getstatusoutput(
                                'lvcreate -L %sG -n %s %s'
                                % (str(vmParameters['STORAGE']),
                                   LVM_NAME, USE_THIS_VG))
                if create_lvm[0] == 0:
                    self.updateLog('LVM %s succesfully created' % LVM_NAME, 'info')
                    LVM_PATH = os.path.join('/', 'dev', USE_THIS_VG, LVM_NAME)
                    vmParameters['VOLUME_GROUP'] = USE_THIS_VG
                    vmParameters['LVM'] = LVM_NAME
    
                    create_fs = commands.getstatusoutput('mkfs.xfs %s' % LVM_PATH)
                    if create_fs[0] == 0:
                        self.updateLog('LVM %s succesfully formatted'
                                       % LVM_NAME, 'info')
                        return [True, vmParameters]
                    else:
                        self.updateLog('LVM %s failed during formatting'
                                       % LVM_NAME, 'error')
                        remove_lvm = commands.getstatusoutput(
                                        'lvm lvremove -f %s'
                                        % vmParameters['RELATIVE_LVM_PATH'])
                        self.updateLog('REMOVE: %s ' % str(remove_lvm), 'info')
                        return [False, None]
                else:
                    self.updateLog('CREATE: %s ' % str(create_lvm), 'info')
                    return [False, None]
            else:
                self.updateLog('CREATE: no space available on the following VG %s ' % str(), 'info')
                return [False, None]

        elif ACTION == 'REMOVE':

            remove_lvm = commands.getstatusoutput(
                            'lvm lvremove -f %s'
                            % vmParameters['RELATIVE_LVM_PATH'])
            if not remove_lvm[0] == 0:
                TRY = 0
                while 1:
                    if TRY > 10:
                        break
                    self.updateLog('lvm remove failed. I try again')
                    remove_lvm = commands.getstatusoutput(
                                    'lvm lvremove -f %s'
                                    % vmParameters['RELATIVE_LVM_PATH'])
                    self.updateLog('REMOVE: %s ' % str(remove_lvm), 'info')
                    if remove_lvm[0] == 0:
                        TRY = 0
                        break
                    else:
                        TRY = TRY + 1
                        time.sleep(2)
            self.updateLog('REMOVE: %s ' % str(remove_lvm), 'info')
            return [True, None]

        else:
            self.updateLog('%s action not supported' % ACTION, 'warning')
            return [False, None]

    def _verifyMadatoryConfigParams(self):

        if type(self.config) == type({}):
            self.mP = []
            self.mandatoryParams = [

                              'HV_PORT',
                              'BAIT_PORT',
                              'LOG_FILE_NAME',
                              'MAX_LOG_FILE_SIZE',
                              'MAX_COUNT_LOG_FILE',
                              'LOCAL_REPO_DIR',
                              'SSH_KEY_FILE',
                              'USE_LVM',
                              'SERVICE_NIC_IP',
                              'SERVICE_NIC_IP_MASK',
                              'DNS_RANGE',
                              'DNS_RANGE',
                              'ENABLE_MIXED_MODE',
                              ]
            try:
                if self.config['ENABLE_MIXED_MODE'].upper() == 'NO':
                    self.mandatoryParams.append('BAIT_IMG_TAG')
                    self.mandatoryParams.append('BAIT_VM_RAM')

                elif self.config['USE_LVM'].upper() == 'YES':
                    self.mandatoryParams.append('VOLUME_GROUP')
            except KeyError:
                pass

            [ self.mP.append(p) for p in self.mandatoryParams if not self.config.has_key(p)]

            if len(self.mP) > 0:
                sys.exit('found missing mandatory config parameters %s' % str(self.mP))
            else:
                self.updateLog('configuration is reliable')
        else:
            sys.exit('config is not formatted as expected %s' % str(self.config))



    @synchronized(LOCK_LOG)
    def updateLog(self, msg, level=None):
        """
        Emit a string to the WNoD log file.
        Thread safe.
        """
        if level is None:
            level = self.DEFAULT_LOGGING_INFO
        else:
            level = utils.LOG_LEVELS.get(level, logging.NOTSET)

        self.wnodes_logger.log(level, msg)

    def check_resources(self):
        """ Verify hardware resources. """

        resources = self.LIBVIRT_INSTANCE.getInfo()

        self.check_memory()
        self.resources['CPU'] = resources[2]
        self.check_bandwidth()
        self.check_storage()
        self.set_vm_image_data()

    def check_bandwidth(self):
        """Check Max bandwidth available
        da cambiare in un modo piu' affidabile,
        ogni tanto trova nulla in dmesg"""

        info = commands.getstatusoutput('dmesg | grep "NIC Link is Up"')
        if info[0] == 0:

            self.resources['BANDWIDTH'] = int(info[1].split()[6])

        else:
            # per rendere il parametro bloccante setto bandwith a 1000
            self.resources['BANDWIDTH'] = 1000
            self.updateLog('Error while retrieving output: %s, ' % str(info[1])
                           + 'setted by default to "1000" Warning!!"', 'error')

    def check_storage(self):

        if self.config['USE_LVM'] == 'YES':

            try:
                self.resources['STORAGE'] = 0
                for VOLUME_GROUP in self.config['VOLUME_GROUP'].split():
                    cmd = ('lvm vgs %s --nosuffix --option vg_free --units B'
                           % VOLUME_GROUP)
                    STORAGE = commands.getstatusoutput(cmd)
                    if STORAGE[0] == 0:
                        # conversion Byte->GigaBtye
                        STORAGE_AVAILABLE = int(STORAGE[1].split('\n')[\
                                            -1].strip()) / 1024 / 1024 / 1024
                        self.resources['STORAGE'] = (self.resources['STORAGE']
                                                     + int(STORAGE_AVAILABLE))
                    else:
                        self.updateLog('%s: I got a problem retriving '
                                       % str(STORAGE[1]) +
                                       'available space for Volume Group %s'
                                       % VOLUME_GROUP)
            except:
                a, b, c = sys.exc_info()
                traceback.print_exception(a, b, c)
                self.resources['STORAGE'] = 0
                self.updateLog('Error while retrieving storage space available'
                               + ': %s ' % str(sys.exc_info()[0]), 'error')
        else:
            self.resources['STORAGE'] = 0
            self.updateLog('You do not want to use LVM, ' +
                           'so you do not need space to create a LVM %s'
                           % str(sys.exc_info()[0]), 'warning')
#--- in progress..
#   def delete_image_if_unused(self, IMAGE_FULL_PATH):
#        """
#        delete the image if no Vm is running it
#        """
#        for ID in self.get_vm_IDs():
#            self.get_vm_image_from_ID(ID)
#
#    def get_vm_IDs(self):
#        """
#        get the ID of the Vm active on the Hypervisor
#        """
#        vm_ID = self.virtualMachines.keys()
#        return vm_ID
#
#    def get_vm_image_from_ID(self, ID):
#        vmParameters = "..."
#        vm_image = self._get_vm_image_filename(vmParameters['IMG'])
#        return vm_image
#
    def check_memory(self):
        
        try:
            mem = commands.getoutput('free -m').split('\n')[1].split()
            mem = int(mem[3]) + int(mem[6])
            self.resources['MEM'] = mem
        except:
            self.resources['MEM'] = 0
        
    @synchronized(LOCK_IMAGE)
    def set_vm_image_data(self):
        """
        Set up the self.vmImagesAvailable data structure,
        checking which VM images are locally available.
        Thread safe.
        """
        local_repo_dir = self.config['LOCAL_REPO_DIR']
        try:
            list_file = os.listdir(local_repo_dir)
            if len(list_file) > 0:
                for current_file in list_file:
                    if os.path.islink(os.path.join(local_repo_dir,
                            current_file)):
                        source_filename = os.path.split(
                            os.path.realpath(os.path.join(local_repo_dir,
                                current_file)))[1]
                        self.vmImagesAvailable[current_file] = ['AVAILABLE',
                                                            source_filename]
        except OSError, e:
            # manage the case of 'No such file or directory'
            if e.errno == 2:
                # create a local dir for local_repo_dir
                os.makedirs(local_repo_dir, 420)
                self.updateLog('Successfully created directory %s'
                               % local_repo_dir)
            else:
                pass

    @synchronized(LOCK_CREATE)
    def do_create(self, *arguments):
        """
        Create A virtual resource
        Input:
        -- VM parameters a dictionary with parameter:value pairs
        Output:
        --  a list [VMID, VMHOSTNAME]
        """
        self.updateLog(' Function: %s . Variables: %s' % (utils.whoami(),
                                                          str(arguments)))

        vmParameters = arguments[0][0]
        batchJobId = vmParameters['BATCH_JOBID']

        if not type(vmParameters) == type({}):
            self.updateLog('Data are not formatted as desired %s. '
                           % str(type(vmParameters)) +
                           'I cannot go on', 'error')
            return [1, 'Data are not formatted as desired %s. '
                    % str(type(vmParameters)) + 'I cannot go on']
        else:
            self.updateLog('Received data are formated as desired %s'
                           % str(vmParameters))

            if 'ENABLED_VLAN' in self.config:
                self.updateLog('This HyperVisor support ' +
                               'only the following VLANs: %s'
                               % str(self.config['ENABLED_VLAN']))

            # get vm network parameters, hostname, mac and VLAN
            VLAN = vmParameters['VLAN']
            if VLAN == 'DEFAULT_VLAN':
                VLAN = 'default'

            # set vlan
            vmParameters['BRIDGE'] = self._set_vlan(VLAN)

            if self.config['USE_LVM'].upper() == 'YES':
                CREATE_LVM = self._handleLVM(vmParameters, 'CREATE')
                if not CREATE_LVM[0]:
                    return [1, 'Failed to create the LVM']
                else:
                    vmParameters = CREATE_LVM[1]

#            #preparo il file immagine ed il link sul filesystem
#            #recupero le informazioni necessarie per creare l'xml

            vm_image_local_path, arch, form = self._set_vm_image(
                                                vmParameters['IMG'])

            VM_XML = self._gen_vm_XML(vmParameters, vm_image_local_path,
                                      arch, form)

            if VM_XML[0] == 0:
                self.updateLog('vm istance successfully defined')

                VM_ISTANCE = self.LIBVIRT_INSTANCE.defineXML(VM_XML[1])

                if VM_ISTANCE.create() == 0:
                    self.updateLog('Virtual Resource successfully created:')
                    vmParameters['ID'] = VM_ISTANCE.ID()
                    self.virtualMachines[vmParameters['ID']] = \
                        [batchJobId,
                         vmParameters['HOSTNAME'],
                         ['OFF', 'RESERVED'],
                         vmParameters]

                    # call plugin
                    try:
                        plugins_generic.post_vm_create(\
                            vmParameters["HOSTNAME"], self.updateLog)
                    except:
                        self.updateLog(' -- Error executing ' +
                                       'post_vm_create plugin: %s'
                                       % sys.exc_info()[0], "error")

                    return [0, [vmParameters['ID'],
                                self.virtualMachines[vmParameters['ID']]]]
                else:

                    if (self.config['USE_LVM'].upper() == 'YES'
                        and not vmParameters['VM_TYPE'] == 'BAIT_HOST'):

                        self._handleLVM(vmParameters, 'REMOVE')
                    else:
                        pass

                    return [1, '']
            else:
                if (self.config['USE_LVM'].upper() == 'YES'
                    and not vmParameters['VM_TYPE'] == 'BAIT_HOST'):

                    self._handleLVM(vmParameters, 'REMOVE')
                else:
                    pass

                return [1, '']

    @synchronized(LOCK_REGENERATE)
    def do_regenerate(self, *arguments):
        """
        Regenarate a Virtual Machine
        Input:
        - VM ID to Regenerate
        - VM parameters a dictionary with parameter:value pairs
        Output:
        - a list [VMID, VM local data structure]
        """
        self.updateLog(' Function: %s . Variables: %s' % (utils.whoami(),
                                                          str(arguments)))

        VM_ID = arguments[0][0]
        vmParameters = arguments[0][1]
        VM_TO_BE_DESTROYED = arguments[0][2]
        RELEASE_HOSTNAME = arguments[0][3]

        # If you have to regenerae a VM you should reuse the same HOSTNAME.
        # In this case you don't have to release it.
        # First of all I destroy all the VMs in order to free resources and
        # in this case I have to release the attached hostname

        if len(VM_TO_BE_DESTROYED) > 0:
            DESTROY = self.do_destroy([VM_TO_BE_DESTROYED, True])
            if DESTROY[0] == 1:
                return [1, DESTROY[1]]
            else:
                pass

        #Now I destroy the VM that will be reuse

        DESTROY = self.do_destroy([[VM_ID], RELEASE_HOSTNAME])
        if DESTROY[0] == 0:
            CREATE = self.do_create([vmParameters])
            VM_TO_BE_DESTROYED.append(VM_ID)
            VM_DESTROYED = VM_TO_BE_DESTROYED
            if CREATE[0] == 0:
                return [0, CREATE[1], VM_DESTROYED]
            else:
                return [1, CREATE[1]]
        else:
            return [1, DESTROY[1]]

    def do_recycle(self, *arguments):
        """
        Recycle a VM.
        """
        self.updateLog(' Function: %s . Variables: %s' % (utils.whoami(),
                                                          str(arguments)))

        VM_ID = arguments[0][0]
        vmParameters = arguments[0][1]

        #Unmount all the configured FSs
        if not vmParameters['VM_TYPE'] == 'BAIT_HOST':
            CONFIG_COMPONENTS = {}
            for VM_PARAM_NAME in vmParameters.keys():
                if 'VM_CONFIG_MOUNT' in VM_PARAM_NAME:
                    COMPONENT = VM_PARAM_NAME.split('_')[2]
                    ELEMENT_CONFIG = str(VM_PARAM_NAME.split('_')[3]).upper(\
                                        ).replace('MOUNT', 'UNMOUNT')
                    if not COMPONENT in CONFIG_COMPONENTS:
                        CONFIG_COMPONENTS[COMPONENT] = {
                                                ELEMENT_CONFIG:
                                                vmParameters[VM_PARAM_NAME]}
                    else:
                        CONFIG_COMPONENTS[COMPONENT][ELEMENT_CONFIG] = \
                            vmParameters[VM_PARAM_NAME]
                    vmParameters.pop(VM_PARAM_NAME)
                elif 'VM_CONFIG' in VM_PARAM_NAME:
                    vmParameters.pop(VM_PARAM_NAME)

            self.vmSetup([VM_ID, CONFIG_COMPONENTS])

        for PARAM in vmParameters.keys():
            self.virtualMachines[VM_ID][3][PARAM] = vmParameters[PARAM]

        self.virtualMachines[VM_ID][0] = vmParameters['BATCH_JOBID']
        self.virtualMachines[VM_ID][2] = ['OFF', 'RESERVED']

        return [0, [VM_ID, self.virtualMachines[VM_ID]]]

    @synchronized(LOCK_DESTROY)
    def do_destroy(self, *arguments):
        """
        Destroy a VM.
        required value:
        vm_id (list)
        release_hostname (str)
        """

        self.updateLog(' Function: %s .' % utils.whoami() +
                       ' Variables: %s' % str(arguments))

        VM_ID = arguments[0][0]
        RELEASE_HOSTNAME = arguments[0][1]
        DESTROY_SUCCESSFULLY_COMPLETED = 'YES'

        #VM_ID must be a list
        for ID in VM_ID:
            self.updateLog(' Trying to kill the following vm: %s'
                           % str(self.virtualMachines[ID]))
            vmParameters = self.virtualMachines[ID][3]
            VM_ISTANCE = self.LIBVIRT_INSTANCE.lookupByID(ID)
            #VM_TAP = vmParameters['TAP'] #commented because caused exceptions

            #Unmount all the configured FSs
            if not vmParameters['VM_TYPE'] == 'BAIT_HOST':
                CONFIG_COMPONENTS = {}
                for VM_PARAM_NAME in vmParameters.keys():
                    if 'VM_CONFIG_MOUNT' in VM_PARAM_NAME:
                        COMPONENT = VM_PARAM_NAME.split('_')[2]
                        ELEMENT_CONFIG = str(VM_PARAM_NAME.split(\
                                '_')[3]).upper().replace('MOUNT', 'UNMOUNT')

                        if not COMPONENT in CONFIG_COMPONENTS:
                            CONFIG_COMPONENTS[COMPONENT] = {
                                ELEMENT_CONFIG: vmParameters[VM_PARAM_NAME]}
                        else:
                            CONFIG_COMPONENTS[COMPONENT][ELEMENT_CONFIG] = \
                                vmParameters[VM_PARAM_NAME]

                self.vmSetup([ID, CONFIG_COMPONENTS])

            if VM_ISTANCE.destroy() == 0 and  VM_ISTANCE.undefine() == 0:
                self.updateLog('Successfully destroy the following VM: %s'
                               % str(self.virtualMachines[ID]))

                if (self.config['USE_LVM'].upper() == 'YES'
                    and not vmParameters['VM_TYPE'] == 'BAIT_HOST'):

                    self._handleLVM(vmParameters, 'REMOVE')
                else:
                    pass
                self.virtualMachines.pop(ID)

                if RELEASE_HOSTNAME:
                    msg = {'release_Node': [vmParameters['HOSTNAME'],
                                            self.server_name,
                                            vmParameters['VM_TYPE']]}
                    output = self.sendRequest(self.NS_HOST, self.NS_PORT, msg)
                    self.updateLog('Release output %s' % str(output))
                if not DESTROY_SUCCESSFULLY_COMPLETED == 'NO':
                    DESTROY_SUCCESSFULLY_COMPLETED = 'YES'

            else:
                self.updateLog('Failed to destroy the following VM: %s'
                               % str(self.virtualMachines[ID]))
                # TODO check: unused variable DESTROY_SUCCESSFULLY_COMPLETED
                DESTROY_SUCCESSFULLY_COMPLETED == 'NO'

        if DESTROY_SUCCESSFULLY_COMPLETED == 'YES':
            # call plugin
            try:
                plugins_generic.post_vm_destroy(vmParameters["HOSTNAME"],
                                                self.updateLog)
            except:
                self.updateLog(" -- Error executing post_vm_destroy plugin: %s"
                               % sys.exc_info()[0], "error")

            return [0, self.virtualMachines]
        else:
            return [1, self.virtualMachines]

    def getConfig(self, *arguments):
        """
            Return Current BAIT configuration
        """
        try:
            return [0, [self.config]]
        except:
            msg = "configuration doesn't exist"
            self.updateLog(msg, 'error')
            return [1, msg]

    def getResourceAvailable(self, *arguments):
        """
        Return the available resources
        """
        self.check_memory()
        return [0, self.resources]

    def getStatus(self, *arguments):
        """
        Return Virtual Machine instances available
        """
        
        return [0, [self.virtualMachines]]

    def getVMAvailable(self, *arguments):
        """ Return Virtual Machine instances available"""

        return [0, self.virtualMachines]

    def getImageAvailable(self, *arguments):
        """
        Return Image available in the local FileSystem
        """

        return [0, self.vmImagesAvailable]

    def ping(self, *arguments):
        """
        Return bait version. Used to check bait process availability
        """
        self.updateLog(' Function: %s . Variables: %s'
                       % (utils.whoami(), str(arguments)))
        try:
            from wnodes import hypervisor
            a = sys.version_info
            return [0, {'Python': '%s.%s.%s' % (a[0], a[1], a[2]),
                        'hypervisor': hypervisor.get_version()}]
        except:
            self.updateLog('Ping: failed to get the version', 'info')
            return [1, False]

    def reloadConfig(self, *arguments):
        """
        Alert the bait in order to reload its configuration
        """
        # Get configuration from WNODES Name Server
        return self._retrieveConfig(self.NS_HOST, self.NS_PORT)

    def stopProcess(self, *arguments):
        """
        Kill all the VMs before to stop the process
        """

        self.updateLog(' Function: %s . Variables: %s' % (utils.whoami(),
                                                          str(arguments)))

        VM_ID = arguments[0]

        # First of all stop the bait process in order to prevent new job
        # submission
        # TODO check: unused variable BAIT_STOP
        BAIT_STOP = commands.getstatusoutput('service wnodes_bait stop')

        if VM_ID[0] == 'all':
            output = self.do_destroy([self.virtualMachines.keys(), True])

        self.LIBVIRT_INSTANCE.close()

        return [0, output]

    def vmSetup(self, *arguments):
        """
        Configure the Virtual Machine in the appropiate way
        """

        VM_ID = arguments[0][0]
        CONFIG_COMPONENTS = arguments[0][1]
        vmParameters = self.virtualMachines[VM_ID][3]

        # Check whether the host is still reachable
        if not self._checkSSH(vmParameters['HOSTNAME']):
            self.updateLog('Host %s is not reachable'
                           % vmParameters['HOSTNAME'])
            return [1, 'Host %s is not reachable' % vmParameters['HOSTNAME']]

        if not type(CONFIG_COMPONENTS) == type({}):
            CONFIG_COMPONENTS = {}

            for VM_PARAM_NAME in vmParameters.keys():
                if 'VM_CONFIG_' in VM_PARAM_NAME:
                    COMPONENT = VM_PARAM_NAME.split('_')[2]
                    ELEMENT_CONFIG = VM_PARAM_NAME.split('_')[3]
                    if 'MY_HV_SERVER' in vmParameters[VM_PARAM_NAME]:
                        vmParameters[VM_PARAM_NAME] = \
                            vmParameters[VM_PARAM_NAME].upper().replace(\
                            'MY_HV_SERVER', self.config['SERVICE_NIC_IP'])
                        vmParameters[VM_PARAM_NAME] = \
                            vmParameters[VM_PARAM_NAME].lower()
                    if not COMPONENT in CONFIG_COMPONENTS:
                        CONFIG_COMPONENTS[COMPONENT] = {
                            ELEMENT_CONFIG: vmParameters[VM_PARAM_NAME]}

                    else:
                        CONFIG_COMPONENTS[COMPONENT][ELEMENT_CONFIG] = \
                            vmParameters[VM_PARAM_NAME]

        self.updateLog('The VM %s' % vmParameters['HOSTNAME']
                       + ' needs the following components %s are configured'
                       % str(CONFIG_COMPONENTS.keys()))

        PLUGINS_OUTPUT = ''

        for COMPONENT in CONFIG_COMPONENTS.keys():

            PLUGIN = ""
            try:
                cmd = ('from wnodes.utils.plugins import plugin_%s as PLUGIN'
                       % COMPONENT.lower())
                exec cmd
                PLUGIN_RETURN_OUTPUT = PLUGIN.handlePlugin(
                                            self.config['SSH_KEY_FILE'],
                                            vmParameters['HOSTNAME'],
                                            CONFIG_COMPONENTS[COMPONENT])
                PLUGINS_OUTPUT = PLUGINS_OUTPUT + str(PLUGIN_RETURN_OUTPUT)
                if PLUGIN_RETURN_OUTPUT[0] == 1:
                    return [1, PLUGINS_OUTPUT]
            except:
                a, b, c = sys.exc_info()
                traceback.print_exception(a, b, c)
                self.updateLog('Plugin missing to configure the component %s'
                               % COMPONENT)
                return [1, 'Plugin missing to configure the component %s'
                        % COMPONENT]

        return [0, PLUGINS_OUTPUT]

    def vmUpdateStatus(self, *arguments):

        VM_ID = arguments[0][0]
        vmParameters = arguments[0][1]

        self.virtualMachines[VM_ID] = vmParameters

        return [0, '']


def main():
    """
    Entry point for the WNoD process running on the hypervisor.

    A WnodHypervisor (wh) will be instantiated.
    wh will check that the config file exists and read it.
    An instance of wh will then be passed to the daemon class.
    In the run() method of the daemon class:
    - call the wh method to do basic checking (parameters, filesystems)
    - start the threading socket server passing wh as a parameter.

    In the socket server the handle() method will call the two routines to
    handle service messages and kvm VM requests.
    #
    """
    p = optparse.OptionParser()
    p.add_option("-d", "--daemonize", action="store_true", dest="daemon",
                 help="daemonize process")
    p.add_option('-c', '--configFile',
                 dest='configFile',
                 default=os.path.join(__dir_name__,
                                      '/etc/wnodes/hypervisor',
                                      'wnodes.ini'),
                 help='set configuration file [default: %default]',
                 metavar='FILE')
    (options, args) = p.parse_args()  # @UnusedVariable

    pid = "/var/run/%s.pid" % __short_name__
    if os.path.isfile(pid):
        try:
            stale_pid = int(open(pid).readline().strip("\n"))
        except ValueError:
            sys.exit("%s: cannot read pid file." % __short_name__)
        try:
            os.kill(stale_pid, 0)
            sys.exit("%s: already running. Killed and restart"
                     % __short_name__)
        except OSError:
            sys.stderr.write("%s: stale pid found.\n" % __short_name__)

    try:
        if os.path.isfile(options.configFile):
            conf = ConfigParser.RawConfigParser()
            try:
                conf.read(options.configFile)
            except ConfigParser.MissingSectionHeaderError:
                sys.stdout.write('There is no SECTION header [NameServer] ' +
                                 'in configuration file)\n')
                sys.exit(1)
            except:
                sys.stdout.write('Error reading configuration: %s\n'
                                 % str(sys.exc_info()[0]))
                sys.exit(1)
            try:
                NS_HOST = conf.get('NAMESERVER', 'NS_HOST')
                NS_PORT = conf.get('NAMESERVER', 'NS_PORT')
            except ConfigParser.NoSectionError:
                sys.stdout.write('There is no SECTION ' +
                                 'for WNODES Name Server host\n')
                sys.exit(1)
            except:
                sys.stdout.write('Error reading configuration: %sn\n'
                                 % str(sys.exc_info()[0]))
                sys.exit(1)
        else:
            sys.stdout.write('Configuration file location is not present: %s\n'
                             % options.configFile)
            sys.exit(1)
    except IOError:
        sys.stdout.write('IOEroor')
        sys.exit(1)

    wh = WnodHypervisor(NS_HOST, NS_PORT)

    if options.daemon is True:
        utils.daemonize(stdout="/tmp/%s.stdout" % __short_name__,
                        stderr="/tmp/%s.stderr" % __short_name__)

        wh.LIBVIRT_INSTANCE = libvirt.open(None)

        of = open(pid, "w")
        of.write("%i\n" % os.getpid())
        of.close()
        # basic checks.
        wh.check_resources()

        wh.updateLog('###### HyperVisor Server Starting ... ######')
        wh.updateLog('######')
        wh.updateLog('###### CURRENT BAIT: %s' % wh.bait_name)
        wh.updateLog('######')
        wh.updateLog('###### CURRENT IMAGE AVAILABLE ON THE HV')
        for img in wh.vmImagesAvailable.keys():
            wh.updateLog(('###### -- %s : %s')
                         % (str(img), str(wh.vmImagesAvailable[img])))
        wh.updateLog('###### CURRENT VM AVAILABLE ')
        for ID in wh.virtualMachines.keys():
            wh.updateLog(('###### -- %s : %s')
                         % (str(ID), str(wh.virtualMachines[ID])))
        wh.updateLog('######')
        wh.updateLog('###### CURRENT RESOURCES ON THE HV')
        for res in wh.resources.keys():
            wh.updateLog(('###### -- %s : %s')
                         % (str(res), str(wh.resources[res])))
        wh.updateLog('######')
        wh.updateLog('######################################')

        # start the main socket server
        serviceDispatchTable = {
                            'do_create': wh.do_create,
                            'do_regenerate': wh.do_regenerate,
                            'do_recycle': wh.do_recycle,
                            'do_destroy': wh.do_destroy,
                            'getConfig': wh.getConfig,
                            'getResourceAvailable': wh.getResourceAvailable,
                            'getStatus': wh.getStatus,
                            'getVMAvailable': wh.getVMAvailable,
                            'getImageAvailable': wh.getImageAvailable,
                            'ping': wh.ping,
                            'reloadConfig': wh.reloadConfig,
                            'stopProcess': wh.stopProcess,
                            'vmSetup': wh.vmSetup,
                            'vmUpdateStatus': wh.vmUpdateStatus,
                            }

        wsocket.initServerRequestHandler(wh, serviceDispatchTable)
        HVServerHandler = wsocket.ServerRequestHandler

        max_retry = 3
        sleep_time = 30
        for i in range(max_retry):
            try:
                srv = SocketServer.ThreadingTCPServer(
                            ('', int(wh.config['HV_PORT'])),
                            HVServerHandler)
#                srv = NSThreadingServer(('', options.port), ServerHandler)
            except socket.error, msg:
                wh.updateLog(" socket error '%s' (%i/%i) - waiting for %i sec"
                             % (msg, i + 1, max_retry, sleep_time), "error")
                time.sleep(sleep_time)
            else:
                break
        else:
            error_msg = ("%s: could not start socket server, exiting."
                         % __short_name__)
            wh.updateLog(error_msg, "error")
            sys.exit(error_msg)

        srv.daemon_threads = True
        srv.allow_reuse_address = True
        srv.serve_forever()


if __name__ == "__main__":
    main()
