#!/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 implementing name server functionalities.
"""

import time
import SocketServer  # @UnresolvedImport
import socket  # @UnresolvedImport
import threading  # @UnresolvedImport
import os
import sys
import optparse  # @UnresolvedImport
import ConfigParser  # @UnresolvedImport
import logging  # @UnresolvedImport
import shutil  # @UnresolvedImport
import shelve  # @UnresolvedImport
#import fileinput #@UnresolvedImport
#import pickle #@UnresolvedImport
from subprocess import call
from wnodes import nameserver  # @UnresolvedImport

version = nameserver.get_version()

__short_name__ = os.path.basename(os.path.splitext(sys.argv[0])[0])
__long_name__ = __short_name__ + "-" + ".".join(map(str, version))
__minimum_python__ = (2, 4, 0)

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


class NameServer:
    """
    This class is in charge to provide a valid hostname and MAC address
    for every Virtual Machine instantiated by the hypervisors.
    """
    # lock to protect internal data structures
    LOCK_INTERNALS = threading.Lock()
    # lock to protect the logging system
    LOCK_LOG = threading.Lock()
    # Lock for image_path
    LOCK_IMAGEPATH = threading.Lock()
    # Lock for bait_config
    LOCK_BAITCONFIG = threading.Lock()

    def __init__(self, hostlist_file, log_file, max_log_size, dump_file,
                 imagepath, config_dir):

        self.hostlist_file = hostlist_file
        # map between HV and VMs running on that HV
        self.hypervisor_to_vm = {}
        # map between VM/bait OR between bait/HV
        self.vm_to_bait = {}
        # list of available names for regular VMs
        self.hostnameAvail = []
        # list of available names for bait VMs
        self.baitAvail = []
        # list of VMs name in unknown status
        self.vm_unknown_status = []
        # dump of the internal data structures
        self.dump_file = dump_file
        # dump of the internal data structures
        self.imagepath = imagepath
        # dump of the internal data structures
        self.config_dir = config_dir

        self.localDNS = {}

        # set up logging
        max_log_count = 5
        self.logging_default = logging.DEBUG
        self.ns_logger = logging.getLogger('NameServerLogger')
        self.ns_logger.setLevel(self.logging_default)
        fmt = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
        handler = utils.CompressedRotatingFileHandler(log_file,
                                              maxBytes=max_log_size,
                                           backupCount=max_log_count)

        handler.setFormatter(fmt)
        self.ns_logger.addHandler(handler)

        # Verify component config parameters
        self._verifyMadatoryConfigParams()

        # Initialize CopyOnDisk of LocalData
        # for compatibility reasons:
        # hypervisor_to_vm --> section "dom0MappingHost"
        # vm_to_bait       --> section "baitMappingHost"

        if os.path.isfile(self.dump_file):
            # there is a file with the last NS status
            self.updateLog("Reading state file %s" % self.dump_file, "info")
            copyOnDisk = ConfigParser.RawConfigParser()
            copyOnDisk.read(self.dump_file)
            for section in copyOnDisk.sections():
                if section == "dom0MappingHost":

                    for item in copyOnDisk.items(section):
                        self.hypervisor_to_vm[item[0]] = item[1].split(';')

        else:
            shutil.copyfile(self.hostlist_file, self.dump_file)
            self.updateLog("Copying ini file %s" % self.hostlist_file, "info")

        bait_config_ini = os.path.join(
                            self.config_dir,
                            "/etc/wnodes/nameserver/wnodes_bait_config.ini")
        # populate self.bait_port if defined in wnodes_bait_config.ini
        output = utils.getValueFromFileSectionOption(bait_config_ini,
                                                     "BAIT_CONF",
                                                     "BAIT_PORT")
        if not output[1] == 0:
            self.bait_port = output[1]
        # populate self.hv_port if defined in wnodes_bait_config.ini
        output = utils.getValueFromFileSectionOption(bait_config_ini,
                                                     "BAIT_CONF",
                                                     "HV_PORT")
        if not output[1] == 0:
            self.hv_port = output[1]

        # create a local dir for imagepath
        fileDir = os.path.dirname(self.imagepath)
        if not os.path.exists(fileDir):
            os.makedirs(fileDir)
            self.updateLog('Successfully created directory %s' % fileDir)

    def _add_option_value(self, section, option, value):
        """
        add a value for a specific options and section
        in ConfigParse dump_file
        """
        try:
            value = value.rstrip(';').lstrip(';')
            conf = ConfigParser.RawConfigParser()
            # read actual configuration
            conf.read(self.dump_file)
            # create section if not exists
            if not conf.has_section(section):
                conf.add_section(section)
            # get actual value for a specific option in a section
            if conf.has_option(section, option):
                svalues = conf.get(section, option)
                # check if the value is already stored in the option's values
                if value in svalues.split(";"):
                    return [1, "value already stored"]
                # create a new values string which include the new value
                svalues = svalues.rstrip(';')
                svalues = svalues + ';' + value
                # case unique attribute
                value = svalues.lstrip(';')
            # set the string as new option value for the configuration
            conf.set(section, option, value)
            # write new configuration on file
            if not self._write_conf(conf, self.dump_file):
                return [0, "ok"]
            else:
                err_msg = ("Cannot write new value %s for option: %s"
                           % (value, option))
                self.updateLog(err_msg, "error")
                return [1, err_msg]
        except ConfigParser.NoSectionError:
            err_msg = "Cannot find a section called: %s, \
            error %s" % (section, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except ConfigParser.NoOptionError:
            err_msg = "Cannot find an option called: %s, \
            error %s" % (option, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except Exception:
            err_msg = ("Cannot set new value for option: %s, error: %s, %s, %s"
                       % (value, sys.exc_info()[:]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def _del_option_value(self, section, option, value):
        """delete a value for a specific options and section,
        in ConfigParse dump_file"""
        try:
            value = value.rstrip(';').lstrip(';')
            conf = ConfigParser.RawConfigParser()
            # read actual configuration
            conf.read(self.dump_file)
            # get actual value for a specific option in a section
            if conf.has_option(section, option):
                svalues = conf.get(section, option).strip(";")
            else:
                err_msg = ("Cannot find new section: %s option: %s"
                           % (section, option))
                self.updateLog(err_msg, "error")
                return [1, err_msg]
            # check if the value is already stored in the values of the option
            if value in svalues.split(";"):
                # create a new values string which remove the value
                tmp = svalues.split(value)
                l_tmp = tmp[0].rstrip(';')
                r_tmp = tmp[1].lstrip(';')
                svalues = l_tmp + ';' + r_tmp
                # remove external ";"
                svalues = svalues.strip(";")
                # set the string as new option value for the configuration
                conf.set(section, option, svalues)
                # write new configuration on file
                if not self._write_conf(conf, self.dump_file):
                    self.updateLog("Written new value: %s for option %s"
                                   % (svalues, option))
                    return [0, "ok"]
                else:
                    err_msg = (" -- Cannot write new value %s for option %s: "
                               % (svalues, option))
                    self.updateLog(err_msg, "error")
                    return [1, err_msg]
            else:
                return [0, "value not found"]
        except ConfigParser.NoSectionError:
            err_msg = "Cannot find a section called: %s, \
            error %s" % (section, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except ConfigParser.NoOptionError:
            err_msg = "Cannot find an option called: %s, \
            error %s" % (option, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except Exception:
            err_msg = "Cannot set new value for option:, \
            error: %s, %s, %s" % (sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def _ping(self):
        """
        Return component version. Used to check bait process availability
        """
        try:
            from wnodes import nameserver
            a = sys.version_info
            return [0, {'Python': '%s.%s.%s' % (a[0], a[1], a[2]),
                        'namesever': nameserver.get_version()}]

        except:
            self.updateLog('Ping: failed to get the version', 'info')
            return [1, False]

    def _set_option_value(self, myfile, section, option, value):
        """write the value of the option, in a section of the file"""
        try:
            conf = ConfigParser.RawConfigParser()
            # read the file conf
            conf.read(myfile)
            # set the string as new option value for the configuration
            conf.set(section, option, value)
            # write new configuration on file
            if not self._write_conf(conf, myfile):
                return [0, "ok"]
            else:
                err_msg = (" -- Cannot write new value %s for option %s: "
                           % (value, option))
                self.updateLog(err_msg, "error")
                return [1, err_msg]
        except ConfigParser.NoSectionError:
            err_msg = "Cannot find a section called: %s, \
            error %s" % (section, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except ConfigParser.NoOptionError:
            err_msg = "Cannot find an option called: %s, \
            error %s" % (option, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except Exception:
            err_msg = "Cannot set the value : %s, \
            error: %s, %s, %s" % (value, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def _verifyMadatoryConfigParams(self):

        a = {'bait': [
                      'BATCH_SYSTEM_TYPE',
                      'HV_PORT',
                      'BAIT_PORT',
                      'LOG_FILE_NAME',
                      'MAX_LOG_FILE_SIZE',
                      'MAX_COUNT_LOG_FILE',
                      'VM_UNREACH_TIMEOUT',
                      'STATUS_RETRY_COUNT',
                      'SCHEDULING_INTERVAL',
                      'RESERVATION_LENGTH',
                      'USE_LVM',
                      'TYPE',
                      'PX_FAILED_RETURN_STATUS',
                      'ENABLE_MIXED_MODE',
                      'MAX_VM_MEM',
                      'MAX_VM_BANDWIDTH',
                      'MAX_VM_CPU',
                      'MAX_VM_STORAGE',
                      'MIN_VM_MEM',
                      'MIN_VM_BANDWIDTH',
                      'MIN_VM_CPU',
                      'MIN_VM_STORAGE',
                      ],
             'hv': [

                      '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',
                      ]
             }

        for k in a.keys():
            currentConfig = self.get_config_from_file([k])[1]

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

                if k == 'hv':
                    try:
                        if currentConfig['ENABLE_MIXED_MODE'].upper() == 'NO':
                            a[k].append('BAIT_IMG_TAG')
                            a[k].append('BAIT_VM_RAM')

                        elif currentConfig['USE_LVM'].upper() == 'YES':
                            a[k].append('VOLUME_GROUP')
                    except KeyError:
                        pass

                elif k == 'bait':
                    try:
                        if self.config['BATCH_SYSTEM_TYPE'].upper() == 'LSF':
                            self.mandatoryParams.append('LSF_PROFILE')
                    except:
                        pass

                [self.mP.append(p) for p in a[k] if not (p in currentConfig)]

                if len(self.mP) > 0:
                    msg = ('\n\nError: found missing \'%s\' ' % k +
                           'mandatory config parameters %s\n' % str(self.mP))
                    sys.exit(msg)
                else:
                    self.updateLog('%s configuration is reliable' % k)
            else:
                sys.exit('%s config is not formatted as expected %s' %
                         (k, str(currentConfig)))

    def _write_conf(self, conf, myfile):
        try:
            fileDir = os.path.dirname(myfile)
            if not os.path.exists(fileDir):
                os.makedirs(fileDir)
                self.updateLog('Successfully created directory %s' % fileDir)
            #open file in write mode
            cfgfile = open(myfile, 'w')
            # write the configuration
            conf.write(cfgfile)
            cfgfile.close()
            return 0
        except Exception:
            cfgfile.close()
            return 1

    @utils.synchronized(LOCK_INTERNALS)
    def acquire_Node(self, *arguments):
        """
        Allocate a new host name for a Virtual Machine
        Input:
        -- VM type
        -- client
        -- Network type
        -- VLAN network
        Output:
        -- A list [Hostname, VLAN, MAC]
        """

        try:
            parameters = arguments[0]
            VM_TYPE = parameters[0]
            CLIENT = parameters[1]
            NETWORK_TYPE = parameters[2]
            VLAN_NETWORK = parameters[3]

        except:
            return [1, 'Raise exception in data format']

        if os.path.isfile(self.dump_file):
            self.updateLog("Reading host list file %s"
                           % self.dump_file, "info")
            HOSTLIST = ConfigParser.RawConfigParser()
            HOSTLIST.read(self.dump_file)
            HOSTNAME_AVAIL = []

            if len(VLAN_NETWORK) == 0:
                self.updateLog('All the VLANs available can be configured ' +
                               'on the host: %s' % CLIENT)
                for VLAN in HOSTLIST.sections():
                    if HOSTLIST.get(VLAN, 'NETWORK_TYPE') == NETWORK_TYPE:

                        HOSTNAME_AVAIL = HOSTLIST.get(VLAN, VM_TYPE)

                        if len(HOSTNAME_AVAIL) > 0:
                            HOSTNAME_AVAIL = (HOSTLIST.get(VLAN, VM_TYPE)
                                              .strip(';').split(';'))
                            break

            else:
                self.updateLog('Host %s supports just the following VLANs: %s'
                               % (CLIENT, str(VLAN_NETWORK)))
                for VLAN in HOSTLIST.sections():
                    if VLAN in VLAN_NETWORK.split(';'):
                        if HOSTLIST.get(VLAN, 'NETWORK_TYPE') == NETWORK_TYPE:

                            HOSTNAME_AVAIL = HOSTLIST.get(VLAN, VM_TYPE)

                            if len(HOSTNAME_AVAIL) > 0:
                                HOSTNAME_AVAIL = (HOSTLIST.get(VLAN, VM_TYPE)
                                                  .strip(';').split(';'))
                                break
            busy_ip = True

            self.updateLog('This hostnames could be available: %s'
                           % str(HOSTNAME_AVAIL), "debug")

            while busy_ip:
                # set for iteration
                if len(HOSTNAME_AVAIL) > 0:
                    self.updateLog('There are still hostname name available')

                    # extract the last hostname form the list
                    VM_PARAMETERS = HOSTNAME_AVAIL.pop(0).split('^')
                    VM_HOSTNAME = VM_PARAMETERS[0]
                    VM_MAC = VM_PARAMETERS[1]
                    VM_VLAN = VLAN

                elif len(HOSTNAME_AVAIL) == 0:
                    self.updateLog('There are no more hostname available',
                                   'error')
                    return [1, 'No more hostname available']

                #check host network status
                retcode = call("ping -c 1 " + VM_HOSTNAME, shell=True)
                if retcode == 0:
                    busy_ip = True
                    self.updateLog('The hostname: %s ' % VM_HOSTNAME +
                                   'seems to be already in use: ' +
                                   'trying to acquire a new one...', 'warning')
                    self.vm_unknown_status.append('^'.join([VM_HOSTNAME,
                                                            VM_VLAN,
                                                            VM_MAC]))
                    # remove hostname from the available ones
                    if self._del_option_value(VLAN, VM_TYPE,
                                              '^'.join(VM_PARAMETERS)):
                        self.updateLog('Removed option value: %s in %s %s'
                                       % ('^'.join(VM_PARAMETERS),
                                          VLAN, VM_TYPE), 'debug')
                    else:
                        self.updateLog('Error removing option value: ' +
                                       '%s ' % '^'.join(VM_PARAMETERS) +
                                       'from %s %s' % (VLAN, VM_TYPE), 'error')
                    # add the hostname to vm_unknown_status list
                    if self._add_option_value("vm_unknown_status", CLIENT,
                                              '^'.join([VM_HOSTNAME,
                                                        VM_VLAN,
                                                        VM_MAC])):
                        self.updateLog('Added option value: %s in %s %s'
                                       % ('^'.join([VM_HOSTNAME,
                                                    VM_VLAN,
                                                    VM_MAC]),
                                          "vm_unknown_status", CLIENT),
                                       'debug')
                    else:
                        self.updateLog('Error adding option value: %s in %s %s'
                                       % ('^'.join([VM_HOSTNAME,
                                                    VM_VLAN,
                                                    VM_MAC]),
                                          "vm_unknown_status", CLIENT),
                                       'error')
                else:
                    try:
                        # see if client is already an hypervisor
                        self.hypervisor_to_vm[CLIENT].append(
                            '^'.join([VM_HOSTNAME, VM_VLAN, VM_MAC]))
                    except KeyError:
                        # new hypervisor
                        self.hypervisor_to_vm[CLIENT] = ['^'.join([VM_HOSTNAME,
                                                                   VM_VLAN,
                                                                   VM_MAC])]

                    self.updateLog("Allocated new %s name (%s) to client %s )"
                                   % (VM_TYPE,
                                      str([VM_HOSTNAME, VM_VLAN, VM_MAC]),
                                      CLIENT),
                                   "info")
                    # remove hostname from the available ones
                    if self._del_option_value(
                        VLAN, VM_TYPE, '^'.join(VM_PARAMETERS)):

                        self.updateLog('Removed option value: %s in %s %s'
                                       % ('^'.join(VM_PARAMETERS),
                                          VLAN,
                                          VM_TYPE),
                                       'debug')
                    else:
                        self.updateLog(
                            'Error removing option value: %s from %s %s'
                            % ('^'.join(VM_PARAMETERS), VLAN, VM_TYPE),
                            'error')

                    # add the hostname to dom0MappingHost list
                    if self._add_option_value("dom0MappingHost",
                                              CLIENT,
                                              '^'.join([VM_HOSTNAME,
                                                        VM_VLAN,
                                                        VM_MAC])):
                        self.updateLog(
                            'Added option value: %s in %s %s'
                            % ('^'.join([VM_HOSTNAME,
                                         VM_VLAN,
                                         VM_MAC]),
                               "dom0MappingHost",
                               CLIENT),
                            'debug')
                    else:
                        self.updateLog(
                            'Error adding option value: %s in %s %s'
                            % ('^'.join([VM_HOSTNAME,
                                         VM_VLAN,
                                         VM_MAC]),
                              "dom0MappingHost",
                              CLIENT),
                            'error')
                    break
        return [0, [VM_HOSTNAME, VM_VLAN, VM_MAC]]

    @utils.synchronized(LOCK_IMAGEPATH)
    def add_image(self, *arguments):
        parameters = arguments[0]
        tag = parameters[0]
        loca = parameters[1]
        path = parameters[2]
        arch = parameters[3]
        form = parameters[4]
        dev = parameters[5]
        """add an image to the image_path file"""
        try:
            imagepathDir = os.path.dirname(self.imagepath)
            if not os.path.exists(imagepathDir):
                os.makedirs(imagepathDir)
                self.updateLog('Successfully created directory %s',
                               imagepathDir)
            image_path = shelve.open(self.imagepath, "c")
            #check if the tag is not already present
            if tag in image_path:
                err_msg = " -- Tag already stored: use replace_image"
                self.updateLog(err_msg, "info")
                image_path.close()
                return [1, err_msg]
            else:
                image_path[tag] = loca, path, arch, form, dev
                image_path.close()
                return [0, "ok"]

        except Exception:
            err_msg = " -- Cannot insert the image_tag %s" % tag
            self.updateLog(err_msg, "error")
            image_path.close()
            return [1, err_msg]

    def add_option_value(self, *arguments):
        """
        add a value for a specific options and section
        in ConfigParse hostlist_file
        """
        parameters = arguments[0]
        try:
            section = parameters[0]
            option = parameters[1]
            value = parameters[2].rstrip(';').lstrip(';')
            conf = ConfigParser.RawConfigParser()
            # read actual configuration
            conf.read(self.hostlist_file)
            # get actual value for a specific option in a section
            svalues = conf.get(section, option)
            # check if the value is already stored in the values of the option
            if value in svalues.split(";"):
                return [0, "value already stored"]
            else:
                # create a new values string which include the new value
                svalues = svalues.rstrip(';')
                svalues = svalues + ';' + value
                # case unique attribute
                svalues = svalues.lstrip(';')
                # set the string as new option value for the configuration
                conf.set(section, option, svalues)
                # write new configuration on file
                if not self._write_conf(conf, self.hostlist_file):
                    return [0, "ok"]
                else:
                    err_msg = (" -- Cannot write new value %s for option %s: "
                               % (value, option))
                    self.updateLog(err_msg, "error")
                    return [1, err_msg]
        except ConfigParser.NoSectionError:
            err_msg = "Cannot find a section called: %s, \
            error %s" % (section, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except ConfigParser.NoOptionError:
            err_msg = "Cannot find an option called: %s, \
            error %s" % (option, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except Exception:
            err_msg = "Cannot set new value for option: %s, \
            error: %s, %s, %s" % (value, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def closeBait(self, *arguments):
        """change the status of the bait in CLOSED_ADMIN"""
        parameters = arguments[0]
        host = parameters[0]
        port = self.bait_port
        try:
            c = Connection(host, port, self)
            msg = {'updateBaitBatchStatus':
                   ["CLOSED_ADMIN",
                    "wnodes_Nameserver.NameServer.closeBait..."]}
            self.updateLog("Sending msg: --%s-- to: %s, %s"
                           % (msg, host, port), "debug")
            response = c.sendRequest(msg)
            return [0, response]
        except:
            err_msg = "Cannot change the status of the bait: %s, port: %s, \
            error: %s, %s, %s" % (host, port, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    @utils.synchronized(LOCK_IMAGEPATH)
    def del_image(self, *arguments):
        parameters = arguments[0]
        tag = parameters[0]
        """remove an image to the image_path file"""
        try:
            image_path = shelve.open(self.imagepath)
            del image_path[tag]
            image_path.close()
            return [0, "ok"]
        except KeyError:
            err_msg = "-- Tag %s is not present in the list" % tag
            self.updateLog(err_msg, "warning")
            image_path.close()
            return [1, err_msg]
        except Exception:
            err_msg = " -- Cannot delete the image_tag %s" % tag
            self.updateLog(err_msg, "error")
            image_path.close()
            return [1, err_msg]

    def del_option_value(self, *arguments):
        """
        delete a value for a specific options and section
        in ConfigParse hostlist_file
        """
        parameters = arguments[0]
        try:
            section = parameters[0]
            option = parameters[1]
            value = parameters[2]
            conf = ConfigParser.RawConfigParser()
            # read actual configuration
            conf.read(self.hostlist_file)
            # get actual value for a specific option in a section
            svalues = conf.get(section, option)
            # check if the value is already stored in the values of the option
            if value in svalues.split(";"):
                # create a new values string which remove the value
                tmp = svalues.split(value)
                l_tmp = tmp[0].rstrip(';')
                r_tmp = tmp[1].lstrip(';')
                svalues = l_tmp + ';' + r_tmp
                # remove external ";"
                svalues = svalues.strip(";")
                # set the string as new option value for the configuration
                conf.set(section, option, svalues)
                # write new configuration on file
                if not self._write_conf(conf, self.hostlist_file):
                    return [0, "ok"]
                else:
                    err_msg = (" -- Cannot write new value %s for option %s: "
                               % (value, option))
                    self.updateLog(err_msg, "error")
                    return [1, err_msg]
            else:
                return [0, "value not found"]
        except ConfigParser.NoSectionError:
            err_msg = "Cannot find a section called: %s, \
            error %s" % (section, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except ConfigParser.NoOptionError:
            err_msg = "Cannot find an option called: %s, \
            error %s" % (option, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except Exception:
            err_msg = "Cannot set new value for option: %s, \
            error: %s, %s, %s" % (value, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def destroy_VM_instance(self, *arguments):
        """destroy a Virtual Machine instance"""
        parameters = arguments[0]
        vm_instance_id = parameters[0]
        bait = parameters[1]
        port = self.bait_port
        try:
            c = Connection(bait, port, self)
            msg = {'destroyVMInstance': [vm_instance_id]}
            self.updateLog("Sending msg: --%s-- to: %s, %s"
                           % (msg, bait, port), "debug")
            response = c.sendRequest(msg)
            return [0, response]
        except:
            err_msg = "Cannot destroy virtual machine instance: %s, \
            error: %s, %s, %s" % (vm_instance_id, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def get_bait_status(self, *arguments):
        """call an host asking to get the status of the bait"""
        parameters = arguments[0]
        host = parameters[0]
        port = self.bait_port
        try:
            if not wsocket.isOpen(host, port):
                raise Exception("cannot connect to host %s, port %s" \
                      % (host, port))
            c = Connection(host, port, self)
            msg = {'getStatus': []}
            self.updateLog("Sending msg: --%s-- to: %s, %s"
                           % (msg, host, port), "debug")
            response = c.sendRequest(msg)
            return [0, response]
        except:
            err_msg = ("Cannot get the status for bait: %s, " % host +
                       "port: %s, " % port +
                       "error: %s, %s, %s" % sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def get_config(self, *arguments):
        """call an host asking to get its actual configuration"""
        parameters = arguments[0]
        try:
            host = parameters[0]
            port = parameters[1]
            c = Connection(host, port, self)
            msg = {'getConfig': []}
            self.updateLog("Sending msg: %s to: %s, %s"
                           % (msg, host, port), "debug")
            response = c.sendRequest(msg)
            return [0, response]
        except:
            err_msg = "Cannot get the configuration for host: %s, port: %s, \
            error: %s, %s, %s" % (host, port, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    @utils.synchronized(LOCK_BAITCONFIG)
    def get_config_from_file(self, *arguments):
        """return WNoDeS component config.ini file"""
        parameters = arguments[0]
        component = parameters[0].lower()

        try:
            # config_file could be named:
            # wnodes_bait_config.ini or wnodes_hv_config.ini
            config_file = 'wnodes_' + component + '_config.ini'
            # set the full path in conf_file
            conf_file = os.path.join(self.config_dir, "/etc/wnodes/nameserver",
                                     config_file)
            # check if the file exist
            if os.path.isfile(conf_file):
                conf = ConfigParser.RawConfigParser()
                conf.read(conf_file)
                config = {}
                for myitem in  conf.items(component.upper() + "_CONF"):
                    config[myitem[0].upper()] = myitem[1]
                self.updateLog("  -- Config:  %s" % str(config), "info")
                return [0, config]
            else:
                err_msg = ("Cannot found wnodes_%s_config.ini" % component +
                           " in /etc/wnodes/nameserver ")
                self.updateLog(err_msg, "warning")
                return [1, err_msg]
        except:
            err_msg = 'error: %s, %s, %s' % (sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def get_hv_status(self, *arguments):
        """
        call an host asking to get the status of the hv
        """
        parameters = arguments[0]
        host = parameters[0]
        port = self.hv_port
        try:
            c = Connection(host, port, self)
            msg = {'getStatus': []}
            self.updateLog("Sending msg: --%s-- to: %s, %s"
                           % (msg, host, port), "debug")
            response = c.sendRequest(msg)
            return [0, response]
        except:
            err_msg = "Cannot get the status for hv: %s, port: %s, \
            error: %s, %s, %s" % (host, port, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def get_image_info(self, *arguments):
        """
        get information related to a specific tag
        stored in the image_path file
        """
        parameters = arguments[0]
        tag = parameters[0]
        try:
            image_info = shelve.open(self.imagepath)
            loca, path, arch, form, dev = image_info[tag]
            image_info.close()
            return [0, [loca, path, arch, form, dev]]

        except KeyError:
            err_msg = " -- Tag %s not present" % tag
            self.updateLog(err_msg, "error")
            image_info.close()
            return [1, err_msg]
        except:
            err_msg = " -- Cannot get the image_tags"
            self.updateLog(err_msg, "error")
            image_info.close()
            return [1, err_msg]

    def get_option_values(self, *arguments):
        """
        list all the value for a specific options and section
        in ConfigParse hostlist_file
        """
        parameters = arguments[0]
        try:
            section = parameters[0]
            option = parameters[1]
            conf = ConfigParser.RawConfigParser()
            conf.read(self.hostlist_file)
            return_list = conf.get(section, option)
            return [0, return_list]
        except ConfigParser.NoSectionError:
            err_msg = "Cannot find a section called: %s, \
            error %s" % (section, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except ConfigParser.NoOptionError:
            err_msg = "Cannot find an option called: %s, \
            error %s" % (option, str(sys.exc_info()[0]))
            self.updateLog(err_msg, "error")
            return [1, err_msg]
        except Exception:
            err_msg = "Cannot list the hostnames for vlan: %s, \
            error: %s, %s, %s" % (section, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def get_sections(self, *arguments):
        """
        list all the section in ConfigParse hostlist_file
        """
        #parameters = arguments[0]
        try:
            conf = ConfigParser.RawConfigParser()
            conf.read(self.hostlist_file)
            sections = conf.sections()
            return [0, sections]
        except Exception:
            err_msg = "Cannot list section \
            error: %s, %s, %s" % (sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def getNSStatus(self, *arguments):

        return [0, self.hypervisor_to_vm]

    def list_image(self, *arguments):
        """list all the images in the image_path file"""
        #parameters = arguments[0]
        try:
            image_path = shelve.open(self.imagepath)
            images_dict = dict(image_path)
            image_path.close()
            return [0, images_dict]
        except Exception:
            err_msg = "Cannot list the image_tags"
            self.updateLog(err_msg, "error")
            image_path.close()
            return [1, err_msg]

    def openBait(self, *arguments):
        """change the status of the bait in OPEN_ADMIN"""
        parameters = arguments[0]
        host = parameters[0]
        port = self.bait_port
        try:
            c = Connection(host, port, self)
            msg = {'updateBaitBatchStatus':
                   ["OPEN_ADMIN", "wnodes_Nameserver.NameServer.openBait..."]}
            self.updateLog("Sending msg: --%s-- to: %s, %s"
                           % (msg, host, port), "debug")
            response = c.sendRequest(msg)
            return [0, response]
        except:
            err_msg = "Cannot change the status of the bait: %s, port: %s, \
            error: %s, %s, %s" % (host, port, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    def ping(self, *arguments):
        """
        call an host asking to get its actual configuration
        """
        parameters = arguments[0]
        try:
            host = parameters[0]
            port = parameters[1]
            ns_port = '8219'
            if port != ns_port:
                c = Connection(host, port, self)
                msg = {'ping': []}
                self.updateLog("Sending msg: %s to: %s, %s"
                               % (msg, host, port), "debug")
                response = c.sendRequest(msg)
                return [response[0], response[1]]
            else:
                self.updateLog("Called method _ping", "debug")
                return [0, self._ping()]
        except:
            err_msg = "Cannot get the configuration for host-port: %s, \
                error: %s, %s, %s" % (parameters, sys.exc_info()[:])
            self.updateLog(err_msg, "error")
            return [1, err_msg]

    @utils.synchronized(LOCK_IMAGEPATH)
    def purge_image_list(self, *arguments):
        """remove all the images from image_path file"""
        #parameters = arguments[0]
        try:
            image_path = shelve.open(self.imagepath)
            image_path.clear()
            image_path.close()
            return [0, "ok"]
        except:
            err_msg = " -- Cannot purge the image_path list"
            self.updateLog(err_msg, "error")
            image_path.close()
            return [1, err_msg]

    @utils.synchronized(LOCK_INTERNALS)
    def register_Node(self, *arguments):
        """
            register a new HV
        """

        try:
            parameters = arguments[0]

            CLIENT = parameters[0]

        except:
            return [1, 'Raise exception in data format']

        if not CLIENT in self.hypervisor_to_vm:
            self.hypervisor_to_vm[CLIENT] = []
            # add the hostname to dom0MappingHost list
            if self._add_option_value("dom0MappingHost", CLIENT, ''):
                self.updateLog(
                    'Added option value: in %s %s'
                    % ("dom0MappingHost", CLIENT), 'debug')
            else:
                self.updateLog(
                    'Error adding option value: in %s %s'
                    % ("dom0MappingHost", CLIENT), 'error')
            return [0, 'host %s successfully registered' % CLIENT]
        else:
            return [0, 'host %s already registered' % CLIENT]

    @utils.synchronized(LOCK_INTERNALS)
    def release_Node(self, *arguments):
        """
            Release a hostname used to execute a Virtaul resource

            Input:
            - Hostname to release
            - VM type
            - Client requesting the release (must be an hypervisor)
            Output:
            - 0/1
        """
#        self.updateLog(' Function: %s . Variables: %s'
#                       % (utils.whoami(), str(arguments)))

        parameters = arguments[0]

        VM_HOSTNAME = parameters[0]
        CLIENT = parameters[1]
        vm_type = parameters[2]

        for long_host_name in self.hypervisor_to_vm[CLIENT]:
            if VM_HOSTNAME in long_host_name:
                self.hypervisor_to_vm[CLIENT].remove(long_host_name)
                break

        # remove long_host_name from dom0MappingHost
        if self._del_option_value("dom0MappingHost", CLIENT, long_host_name):
            pass
        else:
            return [1, "error removing host_name %s from dom0MappingHost"
                    % long_host_name]

        # vlan is the second split delimited by ^
        vlan = long_host_name.split('^')[1]
        # remove the vlan information
        hostname = '^'.join([long_host_name.split('^')[0],
                             long_host_name.split('^')[2]])

        # add hostname in the list of available host for his own vlan
        if self._add_option_value(vlan, vm_type, hostname):
            return [0, "host %s successfully released" % VM_HOSTNAME]
        else:
            return [1, "error releasing the hostname: %s" % hostname]

    def reload_config(self, *arguments):
        """call an host asking to reload its configuration
        with the one in the nameserver"""
        parameters = arguments[0]
        host = parameters[0]
        port = parameters[1]
        c = Connection(host, port, self)
        msg = {'reloadConfig': []}
        response = c.sendRequest(msg)
        return response

    @utils.synchronized(LOCK_IMAGEPATH)
    def replace_image(self, *arguments):
        parameters = arguments[0]
        tag = parameters[0]
        loca = parameters[1]
        path = parameters[2]
        arch = parameters[3]
        form = parameters[4]
        dev = parameters[5]
        """replace an image to the image_path file"""
        try:
            image_path = shelve.open(self.imagepath)
            #check if the tag is already present
            if tag in image_path:
                image_path[tag] = loca, path, arch, form, dev
                image_path.close()
                return [0, "ok"]
            else:
                err_msg = " -- Tag not found: use add_image"
                self.updateLog(err_msg, "warning")
                image_path.close()
                return [1, err_msg]
        except Exception:
            err_msg = " -- Cannot replay the image_tags"
            self.updateLog(err_msg, "error")
            image_path.close()
            return [1, err_msg]

    def selectVmType(self, *arguments):
        """
        Retrieve user default vm parameters like VMTYPE, SHAREDFS, ENABLEVIRTIO
        info from vmtype.ini file

        Arguments:
        -- User details in a list with username,
            primary groupname and secondary groups name
        -- Client requesting

        Return value:
        -- A dictionary with a parameter:value pairs
        """

        USER_DETAILS = arguments[0][0]
        CLIENT = arguments[0][1]
        USER_PARAMETERS = {}

        if not type(USER_DETAILS) == type([]):
            self.updateLog("%s: Argument is not formatted as desired"
                           % whoami(), "error")
            return [1, 'Argument is not formatted as desired: %s'
                    % str(USER_DETAILS)]
        else:
            pass

        self.updateLog('VWN %s is asking the VM image ' % CLIENT +
                       'to use for the following user:  %s' % USER_DETAILS,
                       "info")

        try:

            vmtype_file = os.path.join(self.config_dir,
                                       '/etc/wnodes/nameserver', 'vmtype.ini')
            if os.path.isfile(vmtype_file):

                vmtype = ConfigParser.RawConfigParser()
                vmtype.read(vmtype_file)
                for user in USER_DETAILS:
                    if user in vmtype.sections():
                        USER_PARAMETERS['IMG'] = vmtype.get(user, 'IMAGE')
                        USER_PARAMETERS['SHAREDFS'] = vmtype.get(user,
                                                                 'SHAREDFS')
                        USER_PARAMETERS['ENABLEVIRTIO'] = (vmtype.get(
                                                            user,
                                                            'ENABLEVIRTIO'))
                        return [0, USER_PARAMETERS]

                USER_PARAMETERS['IMG'] = vmtype.get('DEFAULT', 'IMAGE')
                USER_PARAMETERS['SHAREDFS'] = vmtype.get('DEFAULT', 'SHAREDFS')
                USER_PARAMETERS['ENABLEVIRTIO'] = vmtype.get('DEFAULT',
                                                             'ENABLEVIRTIO')

                return [0, USER_PARAMETERS]
            else:
#                msg = ('Cannot found "vmtype.ini" in %s ' % self.config_dir +
#                       '(set the "config_dir" in ' +
#                       '"/etc/wnodes/nameserver/wnodes.conf")')
                self.updateLog('File %s cannot be found' % vmtype_file,
                               'error')
                return [1, 'File %s cannot be found' % vmtype_file]
        except:
            self.updateLog('%s' % str(sys.exc_info()[0]), "error")
            return [1, '%s' % str(sys.exc_info()[0])]

    @utils.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.logging_default
        else:
            level = LOG_LEVELS.get(level, logging.NOTSET)

        self.ns_logger.log(level, msg)

    def whoIs_TheBait(self, *arguments):
        """
        Return the bait AND the hypervisor of a given client.
        Return string: bait hostname
        """

        parameters = arguments[0]
        CLIENT = parameters[0]
        self.updateLog("client %s is asking who is its bait" % CLIENT, "info")

        # support for mixed mode.
        # request comes from a job running on the HV
        if CLIENT in self.hypervisor_to_vm:

            FOUND_BAIT = False
            for HOST in self.hypervisor_to_vm[CLIENT]:
                if 'bait' in HOST:
                    # The bait is running on the HV as a VM
                    # request comes from a VM running on the HV
                    self.updateLog("Found the bait %s for client %s"
                                   % (HOST, CLIENT),
                                   "info")

                    BAIT = HOST.split('^')[0]
                    FOUND_BAIT = True
                    return [0, BAIT]

            if not FOUND_BAIT:
                # support for mixed mode.
                # The bait is running on the HV as a service
                # request comes from a VM running on the HV
                self.updateLog("Found the bait %s for client %s"
                               % (CLIENT, CLIENT),
                               "info")

                return [0, CLIENT]
        else:
            # request comes from a job running on the VM
            for HV in self.hypervisor_to_vm.keys():
                if CLIENT in ''.join(self.hypervisor_to_vm[HV]):
                    # request comes from a VM running on the HV
                    FOUND_BAIT = False
                    for HOST in self.hypervisor_to_vm[HV]:
                        if 'bait' in HOST:
                            # The bait is running on the HV as a VM
                            self.updateLog("Found the bait %s for client %s"
                                           % (HOST, CLIENT),
                                           "info")
                            BAIT = HOST.split('^')[0]
                            FOUND_BAIT = True
                            return [0, BAIT]

                    if not FOUND_BAIT:
                        # support for mixed mode.
                        # The bait is running on the HV as a  service
                        # request comes from a VM running on the HV
                        self.updateLog("Found the bait %s for client %s"
                                       % (HV, CLIENT),
                                       "info")
                        return [0, HV]

        # cannot find the bait of a given client
        self.updateLog("%s: cannot find bait for client %s"
                       % (whoami(), CLIENT),
                       "error")
        return [1, None]

    def whoIs_TheHV(self, *arguments):
        """ Return the hypervisor of a given client."""

        parameters = arguments[0]
        BAIT_HOSTNAME = parameters[0]

        self.updateLog("client %s is asking who is its hypervisor"
                       % BAIT_HOSTNAME, "info")
        for hv in self.hypervisor_to_vm:
            for HOST in self.hypervisor_to_vm[hv]:
                if BAIT_HOSTNAME in HOST:
                    self.updateLog("The hypervisor of client %s is %s"
                                   % (BAIT_HOSTNAME, hv), "info")
                    return [0, hv]
        # cannot find the hypervisor of a given client: this should not happen

        self.updateLog("%s: cannot find hypervisor for client %s"
                       % (whoami(), BAIT_HOSTNAME), "error")
        return [1, None]

    def write_bait_config(self, *arguments):
        """list all the value for a specific options and section
        in ConfigParse hostlist_file"""
        parameters = arguments[0]
        myfile = os.path.join(self.config_dir,
                            "/etc/wnodes/nameserver/wnodes_bait_config.ini")
        section = "BAIT_CONF"
        option = parameters[0]
        value = parameters[1]
        return [0, self._set_option_value(myfile, section, option, value)]

    def write_hv_config(self, *arguments):
        """list all the value for a specific options and section
        in ConfigParse hostlist_file"""
        parameters = arguments[0]
        myfile = os.path.join(self.config_dir,
                            "/etc/wnodes/nameserver/wnodes_hv_config.ini")
        section = "HV_CONF"
        option = parameters[0]
        value = parameters[1]
        return [0, self._set_option_value(myfile, section, option, value)]

#class NSThreadingServer(SocketServer.ThreadingTCPServer):
#    """ The multi-threading class for the WNoDeS name server. """
#
#    def server_activate(self):
#        """ Override to log server startup. """
#        self.RequestHandlerClass.ns.updateLog("WNoDeS Name Server starting.",
#                                              "info")
#        SocketServer.ThreadingTCPServer.server_activate(self)
#
#    def shutdown(self):
#        """ Shutdown this server. Requires Python 2.6 or above. """
#        try :
#            super_shutdown = SocketServer.ThreadingTCPServer.shutdown
#            self.RequestHandlerClass.ns.updateLog("WNoDeS Name Server " +
#                                                  "shutting down.", "info")
#            super_shutdown(self)
#        except AttributeError:
#            self.RequestHandlerClass.ns.updateLog("%s: " % whoami() +
#                                                  "Python 2.6 required, " +
#                                                  "for server shutdown",
#                                                  "error")


def main():
    """ Entry point for the WNoDeS Name Server. """

    if sys.version_info < __minimum_python__:
        sys.exit("%s: Python >= %s required."
                 % (__short_name__, '.'.join(map(str, __minimum_python__))))

    p = optparse.OptionParser(usage="%prog [options] host_list_file",
                              version=__long_name__)

    p.add_option("-d", "--daemonize", action="store_true", dest="daemon",
                 help="daemonize process")

    p.add_option("-l", "--logfile", dest="log_file",
                 default='/var/log/wnodes/nameserver/wnodes_ns.log',
                 help="set log file [default: %default]", metavar="FILE")

    p.add_option("-D", "--dumpfile", dest="dump_file",
                 default='/var/log/wnodes/nameserver/wnodes_ns_state',
                 help="set file to dump state data [default: %default]",
                 metavar="FILE")

    p.add_option("-m", "--maxlogsize", dest="max_log_size",
                 default=30000000,
                 help="set maximum log file size (bytes) [default: %default]")

    p.add_option("-p", dest="port", action="store_true",
                 default=8219,
                 help="set TCP server port [default: %default]")

    p.add_option("-i", "--imagepathfile", dest="image_path",
                 default='/etc/wnodes/nameserver/repo/image_repo.dat',
                 help="set imagepath file [default: %default]", metavar="FILE")

    p.add_option("-c", "--configdir", dest="config_dir",
                 default='/',
                 help="set configdir file [default: %default]")

    (options, args) = p.parse_args()

    if len(args) != 1:
        p.error("incorrect number of arguments")

    hostlist_file = args[0]
    if not os.path.isfile(hostlist_file):
        p.error("host list file %s not found, create a new one"
                % hostlist_file)

    # check if process is already running
    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." % __short_name__)
        except OSError:
            sys.stderr.write("%s: stale pid found.\n" % __short_name__)

    ns = NameServer(hostlist_file,
                    options.log_file,
                    options.max_log_size,
                    '/var/log/wnodes/nameserver/wnodes_ns_state',
                    options.image_path,
                    options.config_dir)

    if options.daemon is True:
        utils.daemonize(stdout="/tmp/%s.stdout" % __short_name__,
                             stderr="/tmp/%s.stderr" % __short_name__)
        try:
            of = open(pid, "w")
            of.write("%i\n" % os.getpid())
            of.close()
        except IOError:
            error_msg = ("%s: " % __short_name__ +
                         "cannot write pid file %s, exiting." % pid)
            ns.updateLog(error_msg, "error")
            sys.exit(error_msg)

    serviceDispatchTable = {
                            'acquire_Node': ns.acquire_Node,
                            'add_image': ns.add_image,
                            'add_option_value': ns.add_option_value,
                            'purge_image_list': ns.purge_image_list,
                            'closeBait': ns.closeBait,
                            'del_image': ns.del_image,
                            'del_option_value': ns.del_option_value,
                            'destroy_VM_instance': ns.destroy_VM_instance,
                            'getNSStatus': ns.getNSStatus,
                            'get_bait_status': ns.get_bait_status,
                            'get_config': ns.get_config,
                            'get_config_from_file': ns.get_config_from_file,
                            'get_hv_status': ns.get_hv_status,
                            'get_image_info': ns.get_image_info,
                            'get_option_values': ns.get_option_values,
                            'openBait': ns.openBait,
                            'ping': ns.ping,
                            'list_image': ns.list_image,
                            'list_vlan': ns.get_sections,
                            'register_Node': ns.register_Node,
                            'release_Node': ns.release_Node,
                            'reload_config': ns.reload_config,
                            'replace_image': ns.replace_image,
                            'selectVmType': ns.selectVmType,
                            'WhoIsTheHV': ns.whoIs_TheHV,
                            'whoIs_TheBait': ns.whoIs_TheBait,
                            'write_bait_config': ns.write_bait_config,
                            'write_hv_config': ns.write_hv_config,
#                            "hypervisor_connection":
#                                self.ns.hypervisor_connection,
                            }
    wsocket.initServerRequestHandler(ns, serviceDispatchTable)
    NameServerServerHandler = wsocket.ServerRequestHandler

    # address may be in use if socket shutdown/startup is too fast:
    # wait and retry
    max_retry = 3
    sleep_time = 30
    for i in range(max_retry):
        try:
            srv = SocketServer.ThreadingTCPServer(('', options.port),
                                                  NameServerServerHandler)
#            srv = NSThreadingServer(('', options.port), ServerHandler)
        except socket.error, msg:
            ns.updateLog("%s: socket error '%s' (%i/%i) - waiting for %i sec" %
                          (whoami(), 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__)
        ns.updateLog(error_msg, "error")
        sys.exit(error_msg)

    srv.daemon_threads = True
    srv.allow_reuse_address = True

    try:
        srv.serve_forever()
    except KeyboardInterrupt:
        # exit on ctrl-c
        ns.updateLog("WNoDeS Name Server shutting down.", "info")
        sys.exit(1)

if __name__ == "__main__":
    main()
