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

from wnodes.utils import threadpool
import ConfigParser
import Queue
import fnmatch
import optparse
import os
import pickle
import pprint
import socket
import sys
import time
#import traceback

__version__ = (2, 0, 2)
__dir_name__ = os.path.dirname(sys.argv[0])

#sys.path.append(os.path.join(__dir_name__, "..", "lib"))


def get_max_lenght(table, index):
    """
    gets the maximum width of the given column index
    """

    lenght_of_column = [len(row[index]) for row in table]
    #print ("lenght_of_column: %s") % lenght_of_column
    #print ("max(lenght_of_column): %s") % max(lenght_of_column)
    return max(lenght_of_column)


def print_aligned_dict(dictionary):
    """
    prints out a dictionary padded for alignment
    """
    if dictionary:
        max_lenght = 0
        for key in dictionary.keys():
            max_lenght = max(len(key), max_lenght)
        for key in dictionary:
            print "%s: %s" % (key.ljust(max_lenght + 1), dictionary[key])


def print_aligned_table(out, table):
    """
    prints out a table of data, padded for alignment
    @param out: Output stream (file-like object)
    @param table: The table to print. A list of lists.
    Each row must have the same number of columns.
    """

    col_paddings = []

    for i in range(len(table[0])):
        col_paddings.append(get_max_lenght(table, i))

    for row in table:
        for i in range(len(row)):
            #row in table:
            col = row[i].ljust(col_paddings[i] + 1)
            print >> out, col,
        print >> out


class Connection(object):
    """
    provides a socket-threaded connection using WNoDeS syntax.
    """

    def __init__(self, host, port):
        self.host = host
        self.port = int(port)

    def sendRequest(self, msg):
        """
        sends a message to a WNoDeS TCP socket server.
        Message format MUST BE a dictionary with one value-pair attribute.
        Key MUST BE the method name you want to execute on the NameServer.
        Value MUST BE the options method weather they exist.
        If message is not in this format an error is raised

        It returns a tuple with this format (status, data).
        """
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect((self.host, self.port))
            request = self._serialize_input(msg)
            sock.sendall(request + '\n')
            socketfile = sock.makefile('r', 0)
            response = socketfile.readline().strip()
            sock.shutdown(2)
            sock.close()
            return self._marshall_output(response)
        except socket.error:
            sys.exit('Server host %s:' % self.host +
                     '%s is unreachable' % self.port)
        except KeyboardInterrupt:
            sys.exit("Ctrl-C pressed, exiting")
        except:
            print ('%s, %s, %s' % (sys.exc_info()[:]))
            response = (1, '')
            return response

    def _serialize_input(self, msg):
        try:
            if len(msg.keys()) == 1:
                msg = str(pickle.dumps(msg).replace('\n', '1%2'))
                return msg
            else:
                print ('msg len(%s) is not 1: not formatted as expected!'
                                % len(msg.keys()))
#                self.updateLog('msg len(%s) is not 1, ' % len(msg.keys()) +
#                               'so it is not formatted as expected ', "error")
                response = (1, None)
                return response
        except pickle.PicklingError:
            sys.exit('Data received cannot be loaded')
        except:
            print ('%s' % str(sys.exc_info()[0]))
            # self.updateLog('%s' % str(sys.exc_info()[0]), "error")
            response = (1, '')
            return response

    def _marshall_output(self, response):
        try:
            output = pickle.loads(response.replace('1%2', '\n'))
            if output[0] == 1:
                return "Error: %s" % output[1]
            elif output[0] == 0:
                return output[1]
            else:
                return "wrong exit syntax: %s" % output

        except pickle.UnpicklingError:
            sys.exit('Data received cannot be loaded')
        except Exception:
            #a,b,c = sys.exc_info()
            #traceback.print_exception(a,b,c)
            sys.exit('Data received from cannot be loaded for this reason: ' +
                     '%s, %s, %s' % (sys.exc_info()[:]))

    def _checktagpath(self, tag, path):
        """
        checks if tag is different from filename
        otherwise returns 0
        """
        if (tag != os.path.basename(path)):
            return 1
        else:
            print ('tag: %s, ' % tag +
                   'must be different from filename of the image: %s' % path)
            return 0

    def _checktype(self, repo_type):
        """
        checks if type is: http or file
        otherwise returns 0
        """
        if ((repo_type == "http") | (repo_type == "file")):
            return 1
        else:
            print ('repo_type must be "http" or "file", not: %s' % repo_type)
            return 0

    def _checkarch(self, arch):
        """
        checks if arch is: i386 or i686 or x86_64 or ia64
        otherwise returns 0
        """
        if ((arch == "i386") | (arch == "i686") |
            (arch == "x86_64") | (arch == "ia64")):
            return 1
        else:
            print ('type must be "i386" or "i686" or "x86_64" or "ia64", ' +
                   'not: %s' % arch)
            return 0

    def _checkform(self, img_format):
        """
        checks if format is: raw or qcow2,
        (or: bochs, cloop, cow, dmg, vmdk, iso, qcow, vmdk, vpc)
        otherwise returns 0
        """
        if ((img_format == "raw") | (img_format == "qcow2") |
            (img_format == "bochs") | (img_format == "cloop") |
            (img_format == "cow") | (img_format == "dmg") |
            (img_format == "iso") | (img_format == "qcow") |
            (img_format == "vmdk") | (img_format == "vpc")):
            return 1
        else:
            print ('img_format must be one these: "raw", "qcow2", "bochs", ' +
                   '"cloop", "cow", "dmg", "iso", "qcow", "vmdk", "vpc". ' +
                   'not: %s' % type)
            return 0

    def _checkhostname(self, hostname):
        """
        checks if hostname is: in the form
        otherwise returns 0
        """
        if (len(hostname) < 17):
            print ('hostname to short, it must be in the form:\
            \n vwn-XXX^XX:XX:XX:XX:XX:XX or \
            \n wn-XXX^XX:XX:XX:XX:XX:XX or \
            \n sb-XXX^XX:XX:XX:XX:XX:XX, not: \
            \n %s' % hostname)
            return 0
        if (  # check if the syntax of the MAC address is in the form:
              # ^XX:XX:XX:XX:XX:XX
            (hostname[-18] == "^")
            & (hostname[-15] == ":")
            & (hostname[-12] == ":")
            & (hostname[-9] == ":")
            & (hostname[-6] == ":")
            & (hostname[-3] == ":")
            # uncomment if you want to add a check for the prefix
            # ex: check the prefix is one of the admitted: vmn- wn- sb-
            #((hostname[:4] == "vwn-") |
            #(hostname[:3] == "wn-") |
            #(hostname[:3] == "sb-") )
            ):
            return hostname
        else:
            print ('hostname must be in the form:\
            \n vwn-XXX^XX:XX:XX:XX:XX:XX or \
            \n wn-XXX^XX:XX:XX:XX:XX:XX or \
            \n sb-XXX^XX:XX:XX:XX:XX:XX, not: \
            \n %s' % hostname)
            return 0

    def _checkvlan(self, vlan):
        """
        checks if vlan is: in the form
        otherwise returns 0
        """
        if ((vlan.lower() == "default_vlan") | (vlan.isdigit())):
            return vlan
        else:
            print ('vlan must be an integer (or default_vlan), not: %s' % vlan)
            return 0

    def _check_is_bait(self, msg):
        try:
            # Get active bait list
            list_of_bait = self._get_active_bait()
            bait_selected = []
            # chose baits complaint with a given regular expression (msg)
            for bait in list_of_bait:
                if fnmatch.fnmatch(bait, msg):
                    bait_selected.append(bait)
            # check if bait selected is void
            if bait_selected:
                return bait_selected
            else:
                print ('cannot find any bait named : %s' % msg)
                return False

        except Exception:
            sys.exit('Data received from cannot be loaded for this reason: ' +
                     '%s, %s, %s' % (sys.exc_info()[:]))
            return False

    def _check_is_host(self, host):
        msg = {'getNSStatus': []}
        msg = self.sendRequest(msg)
        found = False
        for value in msg.values():
            # for every host remove the first host-name,
            # which is reserved for the bait host-name
            value.pop(0)
            for i in value:
                if i.split("^")[0] == host:
                    found = True
                    return True
        if not found:
            print "Cannot find host named: %s" % host
            return False

    def _get_active_bait(self):
        msg = {'getNSStatus': []}
        msg = self.sendRequest(msg)
        baits = []

        hypervisor_to_vm = msg

        for HV in hypervisor_to_vm.keys():
            if not 'bait' in ''.join(hypervisor_to_vm[HV]):
                # The Bait is running on the HV as service.
                # Support for mixed mode
                baits.append(HV)
            else:
                VM_LIST = hypervisor_to_vm[HV]
                for VM in VM_LIST:
                    if 'bait' in VM:
                        baits.append(VM.split("^")[0])
        return baits

    def _get_active_host(self):
        msg = {'getNSStatus': []}
        msg = self.sendRequest(msg)
        hosts = []
        for key in msg.keys():
            if msg[key]:
                for i in range(len(msg[key])):
                    # initialize values string
                    host = msg[key][i]
                    hosts.append(host.split("^")[0])
        return hosts

    def _get_bait_from_host(self, host):
        """
        returns the bait connected to the host-name
        if not found returns false
        required values:
        host (string)
        """
        msg = {'whoIs_TheBait': [host]}
        msg = self.sendRequest(msg)
        bait = msg.split(':')[0]
        if bait != "Error":
            return bait
        else:
            print "Cannot find host named: %s" % host
            return False

    def _print_bait_status(self, bait):
        """
        prints the bait's information
        if not found, nothing will be printed
        required values:
        bait host-name (string)
        """
        msg = {'get_bait_status': [bait]}
        OUTPUT = self.sendRequest(msg)
        if OUTPUT[0] == 0:
            if OUTPUT[1][0] == 0:
                BAIT_STATUS = OUTPUT[1][1][0]
                ACTIVE_JOB = OUTPUT[1][1][1]
                ACTIVE_VM = OUTPUT[1][1][2]
                print ('Bait\t\t: %s;\n' % bait +
                       'Bait status\t: %s\n' % str(BAIT_STATUS))
                #print str(ACTIVE_JOB)
                if ACTIVE_JOB:
                    new_list = []
                    new_list.append(['JobId', 'JobStatus', 'JobType',
                                     'vmID', 'VM', 'Owner', 'vmImage',
                                     'vmResources', 'vmStatus', 'TS',
                                     'TimeSpentToReachLastStatus'])
                    # populate the list, extracting the values
                    # from ACTIVE_JOB list
                    L = ACTIVE_JOB.items()
                    L.sort(cmp=lambda x, y: cmp(x[1][4], y[1][4]))
                    for JOB_ID, JOB_PARAMS in L:
                        try:
                            # JobStatus
                            JOB_STATUS = JOB_PARAMS[0]
                            # VM
                            VM_HOSTNAME = JOB_PARAMS[2]
                            VM_ID = JOB_PARAMS[1]
                            #Job Type
                            if VM_ID in ACTIVE_VM:
                                JOB_TYPE = ACTIVE_VM[VM_ID][3]['TYPE']
                            else:
                                JOB_TYPE = JOB_PARAMS[3]['TYPE']
                            TS = time.strftime('%d/%m-%H:%M',
                                               time.localtime(JOB_PARAMS[4]))
                            if not JOB_TYPE == 'BATCH_REAL':
                                if VM_ID in ACTIVE_VM:
                                    VM_PARAMS = ACTIVE_VM[VM_ID][3]
                                    USER = str(VM_PARAMS['USER_DETAILS'][0])
                                    CPU = str(VM_PARAMS['CPU'])
                                    RAM = str(VM_PARAMS['MEM'])
                                    STORAGE = str(VM_PARAMS['STORAGE'])
                                    VM_RESOURCE = ('[cpu:%s mem:%s disk:%s]'
                                         % (CPU, RAM, STORAGE))
                                    VM_IMG = str(VM_PARAMS['IMG'])
                                else:
                                    msg = ('Warning: No consistency ' +
                                           'between JOB and VM dict\n')
                                    print msg
                            else:
                                USER = str(JOB_PARAMS[3]['USER_DETAILS'][0])
                                RAM = str(JOB_PARAMS[3]['CPU'])
                                CPU = str(JOB_PARAMS[3]['MEM'])
                                STORAGE = str(JOB_PARAMS[3]['STORAGE'])
                                VM_RESOURCE = ('[cpu:%s mem:%s disk:%s]'
                                               % (CPU, RAM, STORAGE))
                                VM_IMG = 'NoImg'
                            # vmStatus
                            VM_STATUS = JOB_PARAMS[5]
                            # TimeSpentToReachLastStatus
                            TIME_SPENT = ('%d') % (JOB_PARAMS[7])
                            new_list.append([JOB_ID, JOB_STATUS,
                                             JOB_TYPE, str(VM_ID),
                                             VM_HOSTNAME, USER, VM_IMG,
                                             VM_RESOURCE, VM_STATUS,
                                             TS, TIME_SPENT + "(sec)"])
                        except:
                            pass
                    out = sys.stdout
                    print_aligned_table(out, new_list)
                else:
                    print ('No active jobs')
                print "\n"

    def test(self, option, opt, value, parser):
        msg = value
        print self._check_is_host(msg)

    def get_config(self, option, opt, value, parser):
        """
        contacts name server and asks the configuration in use
        for the specified host
        required values:
        host (string)
        port (string)
        """
        host = value[0]
        port = value[1]
        #if self._checkhost(host):
        msg = {'get_config': [host, port]}
        print self.sendRequest(msg)

    def get_bait_status(self, option, opt, value, parser):
        """
        contacts name server and asks the status
        for the specified bait
        required values:
        bait = value (string / regular expression)
        """
        bait = value
        thread_pool_size = 5
        outQueue = Queue.Queue()

        def my_handler(info):
            print "**** Exception occured: %s" % (info,)

        # get a list of active baits, complaint with given regular expression
        baits = self._check_is_bait(bait)
        if not baits:
            return
        # print "Creating a pool of %s threads\n" % size
        try:
            p = threadpool.ThreadPool(size=thread_pool_size,
                                      errback=my_handler,
                                      out=outQueue)
            # print "Dispatching task",
            # print the status to each bait in the list
            for bait in baits:
                #print bait
                p.dispatch(self._print_bait_status, bait)
            #print "\n"
            while p.busy:
                time.sleep(0.2)

        except KeyboardInterrupt:
            print "\nWaiting for running threads to terminate..."
            try:
                p.stop()
            except KeyboardInterrupt:
                pass  # a try... except... finally clause would be better.
        else:
            p.stop()  # don't forget to call this before exiting

            # print "\nget_bait_status completed."

    def get_hv_status(self, option, opt, value, parser):
        """
        contacts name server and asks the status
        for the specified hv
        required values:
        host (string)
        """
        host = value
        #if self._checkhost(host):
        msg = {'get_hv_status': [host]}
        response = self.sendRequest(msg)
        if response[0]:
            # print error msg
            print response[1]
        else:
            pp = pprint.PrettyPrinter()
            for item in response[1][1][0].items():
                print ('\nvmID: %s, jobID: %s, hostname: %s, status: %s'
                       % (item[0], item[1][0], item[1][1], item[1][2]))
                pp.pprint(item[1][3])

    def reload_host_config(self, option, opt, value, parser):
        """
        contacts name server and asks to notify to an host
        to reload his configuration using
        the value the configuration stored in the nameserver
        required values:
        host (string)
        port (string)
        """
        host = value[0]
        port = value[1]
        if 1:
        #if self._checkhost(host):
            msg = {'reload_config': [host, port]}
            print self.sendRequest(msg)

    def replace_bait_config(self, option, opt, value, parser):
        """
        contacts name server and asks
        to replace the value of the option in the bait config file
        required values:
        option (string)
        value (string)
        """
        option = value[0]
        value = value[1]
        #if self._checkhost(host):
        msg = {'write_bait_config': [option, value]}
        print self.sendRequest(msg)[1]

    def replace_hv_config(self, option, opt, value, parser):
        """
        contacts name server and asks
        to replace the value of the option in the hv config file
        required values:
        option (string)
        value (string)
        """
        option = value[0]
        value = value[1]
        msg = {'write_hv_config': [option, value]}
        print self.sendRequest(msg)[1]

    def get_option_values(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list all the value for a specific options and vlan in mac_list.ini
        required value:
        option (string)
        vlan (string)
        """
        option = value[0]
        vlan = value[1].upper()
        if self._checkvlan(vlan):
            msg = {'get_option_values': [vlan, option]}
            print self.sendRequest(msg)

    def list_vlan(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list the stored vlan
        no value required:
        """
        msg = {'list_vlan': []}
        try:
            for vlan in self.sendRequest(msg):
                print vlan
        except Exception:
            sys.exit('Exception due to: %s, %s, %s'
                     % (sys.exc_info()[:]))

    def add_hostname(self, option, opt, value, parser):
        """
        contacts name server and asks
        to add an hostname to a specific vlan
        required 2 values:
        hostname (string)
        vlan (string)
        """
        hostname = value[0]
        vlan = value[1].upper()
        if self._checkhostname(hostname):
            #msg = {'add_hostname': [hostname, vlan]}
            msg = {'add_option_value': [vlan, "vm_host", hostname]}
            print self.sendRequest(msg)

    def add_baitname(self, option, opt, value, parser):
        """
        contacts name server and asks
        to add a bait hostname to a specific vlan
        required 2 values:
        bait hostname (string)
        vlan (string)
        """
        hostname = value[0]
        vlan = value[1].upper()
        if self._checkhostname(hostname):
            #msg = {'add_hostname': [hostname, vlan]}
            msg = {'add_option_value': [vlan, "bait_host", hostname]}
            print self.sendRequest(msg)

    def del_hostname(self, option, opt, value, parser):
        """
        contacts name server and asks
        to del an hostname to a specific vlan
        required 2 values:
        hostname  (string)
        vlan (string)
        """
        hostname = value[0]
        vlan = value[1].upper()
        if self._checkhostname(hostname):
            msg = {'del_option_value': [vlan, "vm_host", hostname]}
            print self.sendRequest(msg)

    def del_baitname(self, option, opt, value, parser):
        """
        contacts name server and asks
        to delete a bait hostname to a specific vlan
        required 2 values:
        baitname  (string)
        vlan (string)
        """
        baitname = value[0]
        vlan = value[1].upper()
        if self._checkhostname(baitname):
            msg = {'del_option_value': [vlan, "bait_host", baitname]}
            print self.sendRequest(msg)

    def destroy_VM_instance(self, option, opt, value, parser):
        """
        contacts name server and asks
        to destroy a VM_instance
        required value:
        vm_instance hostname  (string)
        """
        vm_instance = value
        # find the bait connected to the hostname
        bait = self._get_bait_from_host(vm_instance)
        if bait:
            msg = {'destroy_VM_instance': [vm_instance, bait]}
            print self.sendRequest(msg)

    def list_hostname(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list the hostnames connected to a specific vlan
        required value:
        vlan (string)
        """
        vlan = value.upper()
        cp_opt = "vm_host"
        if self._checkvlan(vlan):
            msg = {'get_option_values': [vlan, cp_opt]}
            output = self.sendRequest(msg)
            for i in output.split(';'):
                try:
                    print ('HOSTNAME= %s  MAC= %s'
                           % (i.split('^')[0], i.split('^')[1]))
                except:
                    pass

    def list_baitname(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list the bait_host connected to a specific vlan
        required value:
        vlan (string)
        """
        vlan = value.upper()
        cp_opt = "bait_host"
        if self._checkvlan(vlan):
            msg = {'get_option_values': [vlan, cp_opt]}
            return self.sendRequest(msg)

    def list_network_type(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list the network_type connected to a specific vlan
        required value:
        vlan (string)
        """
        vlan = value.upper()
        cp_opt = "network_type"
        if self._checkvlan(vlan):
            msg = {'get_option_values': [vlan, cp_opt]}
            print self.sendRequest(msg)

    def print_active_baitname(self, option, opt, value, parser):
        try:
            # Get active bait list
            list_of_bait = self._get_active_bait()
            for i in list_of_bait:
                try:
                    print 'BAITNAME= %s ' % (i)
                except:
                    pass
        except Exception:
            sys.exit('Exception due to: %s, %s, %s'
                     % (sys.exc_info()[:]))

    def print_active_hostname(self, option, opt, value, parser):
        try:
            # Get active host list
            list_of_bait = self._get_active_host()
            for i in list_of_bait:
                try:
                    # remove void hostname
                    if i:
                        print 'HOSTNAME= %s ' % (i)
                except:
                    pass
        except Exception:
            sys.exit('Exception due to: %s, %s, %s'
                     % (sys.exc_info()[:]))

    def print_available_baitname(self, option, opt, value, parser):
        # Get active bait list
        list_of_active_bait = self._get_active_bait()

        # Get registered bait list
        list_stored_bait = []
        vlan = "DEFAULT_VLAN"
        cp_opt = "bait_host"
        if self._checkvlan(vlan):
            msg = {'get_option_values': [vlan, cp_opt]}
            output = self.sendRequest(msg)
            for i in output.split(';'):
                try:
                    list_stored_bait.append(i.split('^')[0])
                except:
                    pass
        for bait in list_stored_bait:
            if not bait in list_of_active_bait:
                print ('BAITNAME= %s  ') % bait

    def print_available_hostname(self, option, opt, value, parser):
        # Get active bait list
        list_of_active_host = self._get_active_host()

        # Get registered host list
        list_stored_host = []
        vlan = "DEFAULT_VLAN"
        cp_opt = "vm_host"
        if self._checkvlan(vlan):
            msg = {'get_option_values': [vlan, cp_opt]}
            output = self.sendRequest(msg)
            for i in output.split(';'):
                try:
                    list_stored_host.append(i.split('^')[0])
                except:
                    pass
        for host in list_stored_host:
            if not host in list_of_active_host:
                print ('HOSTNAME= %s  ') % host

    def print_baitname(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list the bait_host connected to a specific vlan
        required value:
        vlan (string)
        """
        vlan = value.upper()
        cp_opt = "bait_host"
        if self._checkvlan(vlan):
            msg = {'get_option_values': [vlan, cp_opt]}
            output = self.sendRequest(msg)
            for i in output.split(';'):
                try:
                    print ('BAITNAME= %s  MAC= %s'
                    % (i.split('^')[0], i.split('^')[1]))
                except:
                    pass

    def list_bait_config(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list the option and value for bait configuration
        """
        msg = {'get_config_from_file': ['bait']}
        out = self.sendRequest(msg)
        print_aligned_dict(out)

    def list_hv_config(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list the option and value for hyperisor configuration
        """
        msg = {'get_config_from_file': ['hv']}
        out = self.sendRequest(msg)
        print_aligned_dict(out)

    def add_image(self, option, opt, value, parser):
        """
        adds information about the virtual machine image file
        to the image_path file
        required 6 values:
        tag  (string)
        loca (string: http/file)
        path (string)
        arch (string)
        form (string: raw/qcow2)
        dev  (string)
        """
        tag = value[0]
        loca = value[1]
        path = value[2]
        arch = value[3]
        form = value[4]
        dev = value[5]
        if (self._checktagpath(tag, path) &
            self._checktype(loca) &
            self._checkarch(arch) &
            self._checkform(form)):
            msg = {'add_image': [tag, loca, path, arch, form, dev]}
            print self.sendRequest(msg)

    def del_image(self, option, opt, value, parser):
        """
        deletes the tag and all the other information associated
        to the tag in the image_path file
        required value:
        tag  (string)
        """
        tag = value
        msg = {'del_image': [tag]}
        print self.sendRequest(msg)

    def list_image(self, option, opt, value, parser):
        """
        lists all the images in the image_path file
        no required value
        """
        msg = {'list_image': [""]}
        images_dict = self.sendRequest(msg)
        new_list = []
        new_list.append(['tag', 'loca', 'path', 'arch', 'form', 'dev'])
        # populate the list, extracting the values from ACTIVE_JOB list
        try:
            for key in images_dict.keys():
                new_list.append([key, images_dict[key][0], images_dict[key][1],
                                 images_dict[key][2], images_dict[key][3],
                                 images_dict[key][4]])
        except:
            pass
        out = sys.stdout
        print_aligned_table(out, new_list)

    def replace_image(self, option, opt, value, parser):
        """
        replaces an image to the image_path file
        required 6 values:
        tag  (string)
        loca (string: http/file)
        path (string)
        arch (string)
        form (string: raw/qcow2)
        dev  (string)
        """
        tag = value[0]
        loca = value[1]
        path = value[2]
        arch = value[3]
        form = value[4]
        dev = value[5]
        if (self._checktagpath(tag, path) &
            self._checktype(loca) &
            self._checkarch(arch) &
            self._checkform(form)):
            msg = {'replace_image': [tag, loca, path, arch, form, dev]}
            print self.sendRequest(msg)

    def get_image_info(self, option, opt, value, parser):
        """
        gets information about the virtual machine image file identified
        by the specified tag, stored in the image_path file
        required value:
        tag  (string)
        """
        tag = value
        msg = {'get_image_info': [tag]}
        print self.sendRequest(msg)

    def ping(self, option, otp, value, parser):
        """
        contacts a component and returns service version
        required value:
        host (string)
        port (string)
        """
        host = value[0]
        port = value[1]
        #if self._checkhost(host):
        msg = {'ping': [host, port]}
        output = self.sendRequest(msg)
        if output[0]:
            print output
        else:
            output = output[1]
            try:
                for item in output.items():
                    print item
            except:
                print "Output error in ping message"

    def purge_image_list(self, option, opt, value, parser):
        """
        removes all the images information from image_path file
        """
        msg = {'purge_image_list': []}
        print self.sendRequest(msg)

    def bait_connection(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list the information of the bait(s)
        required value:
        bait (string) / "all"
        admitted special characters = * ? [1-9] [a-z]
        """
        bait_arg = value
        # case: all TODO
#        if (bait == "all"):
#            msg =  {'getNSStatus' : []}
#            msg  = self.sendRequest(msg)
#            for key in sorted(msg.keys()):
#                # initialize values string
#                values = ""
#                # populate values string
#                # with the vm connected to an hypervisor
#                for value in msg[key]:
#                    # add only the name of the vm and at the end a blank space
#                    values += value.split("^")[0] +" "
#                print key.split("^")[0], ":", values
        compliant_baits = self._check_is_bait(bait_arg)

        if not compliant_baits:
            print "Cannot find any bait named: %s" % bait_arg
        else:
            for bait in compliant_baits:
                # get info a specific bait
                msg = {'get_bait_status': [bait]}
                msg = self.sendRequest(msg)
                vm = msg[1][1][2]
                for i in vm:
                    print ('vm_type: %s,' % vm[i][3]['VM_TYPE'] +
                           '\tname: %s' % vm[i][1])
                print ""
                #for i in msg[1][1][2]:
                #    print msg[1][1][2][i][1]
#                for key in sorted(msg.keys()):
#                    if (bait == key):
#                        #initialize values string
#                        values = ""
#                        # populate values string with the vm
#                        # connected to an hypervisor
#                        for value in msg[key]:
#                            # add only the name of the vm
#                            # and at the end a blank space
#                            values += value.split("^")[0] +" "
#                            print key.split("^")[0], ":", values
#                            break

    def hv_connection(self, option, opt, value, parser):
        """
        contacts name server and asks
        to list the vm connected to a specific hypervisor
        or all the vms connected to theirs related hypervisors
        required value:
        hypervisor (string) / "all"
        """
        hv = value
        if ((hv == "all") | (hv == "*")):
            msg = {'getNSStatus': []}
            msg = self.sendRequest(msg)
            for key in sorted(msg.keys()):
                # initialize values string
                values = ""
                # populate values string with the vm connected to an hypervisor
                for value in msg[key]:
                    # add only the name of the vm and at the end a blank space
                    values += value.split("^")[0] + " "
                print key.split("^")[0], ":", values
        # search a
        else:  # self._check_is_host(hv):
            msg = {'getNSStatus': []}
            msg = self.sendRequest(msg)
            found = False
            for key in sorted(msg.keys()):
                if (hv == key):
                    # initialize values string
                    values = ""
                    # populate values string with the
                    # vm connected to an hypervisor
                    for value in msg[key]:
                        # add only the name of the vm
                        # and at the end a blank space
                        values += value.split("^")[0] + " "
                    print key.split("^")[0], ":", values
                    found = True
                    break
            if not found:
                print "Cannot find hypervisor named: %s" % hv

    def open_bait(self, option, opt, value, parser):
        """
        sends a message to Nameserver asking to
        change the status of the bait in OPEN_ADMIN
        """
        bait = value
        if ((bait == "all") | (bait == "*")):
            bait = self._get_active_bait()
        else:
            # get a list of active baits,
            # compliant with given regular expression
            bait = self._check_is_bait(bait)
            if not bait:
                return
        # ask to open all the baits in the list
        for host in bait:
            msg = {'openBait': [host]}
            msg = self.sendRequest(msg)
            print "Bait %s found: opened" % host

    def close_bait(self, option, opt, value, parser):
        """
        sends a message to Nameserver asking to
        change the status of the bait in CLOSED_ADMIN
        """
        bait = value
        if ((bait == "all") | (bait == "*")):
            bait = self._get_active_bait()
        else:
            # get a list of active baits,
            # compliant with given regular expression
            bait = self._check_is_bait(bait)
            if not bait:
                return
        # ask to open all the baits in the list
        for host in bait:
            msg = {'closeBait': [host]}
            msg = self.sendRequest(msg)
            print "Bait %s found: closed" % host


def loadNameserverAddress(configFile):
    try:
        if os.path.isfile(configFile):
            conf = ConfigParser.RawConfigParser()
            try:
                conf.read(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')
                return ns_host, 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'
                             % configFile)
            sys.exit(1)
    except IOError:
        sys.stdout.write('IOError')
        sys.exit(1)


def main():
    """
    Defines the behavior of the wnodes_manager.
    """

    # displayed if the user makes an error or specifies the -h or --help option
    usage_str = """%prog <options>
    
-------------------------------------------------------------------------------
---- Administration command for image path list -------------------------------
-------------------------------------------------------------------------------
-h / --help
    show help message
-------------------------------------------------------------------------------
---- image path list {'tag':('loca','path','arch','type','dev'), ... } --------
-------------------------------------------------------------------------------
-a / --add_image (tag, loca, path, arch, type, dev)
    the option, followed by an ordered-parameters list separated by a space,
    adds information about the virtual machine image file.
--purge_image_list
     removes all the images information from the repository.
     Please be careful.
-d / --delete_image (tag)
    the option, followed by the tag parameter, deletes the tag and all the
    other information associated to the tag.
-g / --get_image_info (tag)
    the option, followed by the tag parameter, gets information about
    the virtual machine image file identified by the specified tag.
-l / --list_image
    lists information about the registered virtual machine image files.
-r / --replace_image (tag, loca, path, arch, type, dev)
    the option, followed by an ordered-parameters list separated by a space,
    replaces information about the virtual machine image file.
-------------------------------------------------------------------------------
---- Administrative option mac_list.ini file ----------------------------------
-------------------------------------------------------------------------------
-V / --list_vlan
    provides a list of the VLANs registered in the current configuration
-B / --list_baitname (vlan)
    the options, followed by the vlan name, provides a list of the
    bait host-names registered in a specific VLAN configuration
-L / --list_hostname (vlan)
    the options, followed by the vlan name, provides a list of the
    virtual machine hostnames registered in a specific VLAN configuration.
-N / --list_network_type (vlan)
    the options, followed by the vlan name, provides information about
    the network type (e.g., open/closed) of a specific VLAN.
-O / --list_option_values (option, vlan)
    the option, followed by an ordered-parameters list separated by a space,
    provides information about the values of the given option for the
    specified VLAN. This option is useful to handle new options in the
    /etc/wnodes/nameserver/mac_list.ini file
    that do not have a specific method in the code.
-------------------------------------------------------------------------------
---- Options to modofy the registerd hostname ---------------------------------
-------------------------------------------------------------------------------
-A / --add_hostname (hostname, vlan)
    the option, followed by a virtual machine hostname with the mac address and
    a vlan name, adds the virtual machine hostname to the specific vlan.
-T / --add_baitname (baitname, vlan)
    the option, followed by a bait hostname with the mac address and a vlan
    name, adds the bait hostname to the specific vlan.
-D / --delete_hostname (hostname, vlan)
    the option, followed by a virtual machine hostname with the mac address and
    a vlan name, deletes the vitual machine hostname from the specific vlan.
-E / --delete_baitname (baitname, vlan)
    the option, followed by a bait hostname with the mac address and a vlan
    name, deletes the bait hostname from the specific vlan.
-------------------------------------------------------------------------------
---- Options to menage the configuration files of the bait and hypervisor -----
-------------------------------------------------------------------------------
---- administration of bait and hypervisor configuration,
---- stored in wnodes_bait_config.ini and wnodes_hv_config
-v / --list_hv_config
    lists the values of the options specified in the wnodes_hypervisor
    configuration file.
-b / --list_bait_config
    lists the values of the options specified in the bait configuration file.
-P / --replace_hv_config (option, value)
    the option, followed by the option name and its value, replaces the value
    of the option in the wnodes_hypervisor configuration file.
-p / --replace_bait_config  (option, value)
    the option, followed by the option name and its value, replaces the value
    of the option in the bait configuration file.
-------------------------------------------------------------------------------
---- Options to manage the bait and wnodes_hypervisor status ------------------
-------------------------------------------------------------------------------
---- administration of bait and hypervisor runtime variables
-n / --node_connection (host, port)
    TODO
-o / --get_config (host, port)
    the option, followed by the hostname and port values, contacts the relative
    server and asks the current configuration.
-s / --get_bait_status (host/RegularExpression)
    the option, followed by the bait hostname value, gets the status for the
    specified bait. The value can also contain a regular expression.
-S / --get_hv_status (host)
    the option, followed by the virtual machine hostname and port values,
    gets the status for the specified wnodes_hypervisor.
-R / --reload_host_config (host, port)
    the option, followed by the virtual machine hostname and port values,
    reloads the configuration of the specified service.
-t / --hv_connection (host/"all")
    the option, followed by the wnodes_hypervisor hostname value, gets
    the virtual machine connected to the specified wnodes_hypervisor service.
    The value can contain 'all': in this case the option will return all the
    deployed wnodes_hypervisor hostnames and their virtual machine hostnames.
--bait_connection (host/RegularExpression)
    the option, followed by the bait hostname value, gets the virtual machine
    connected to the specified bait service.
    The value can also contain a regular expression.
-w / --open_bait (host/"all"/RegularExpression)
    the option, followed by the bait hostname value, changes the status of the
    bait service in OPEN_ADMIN.
-x / --close_bait (host/"all"/RegularExpression)
    the option, followed by the bait hostname value, changes the status of the
    bait in CLOSED_ADMIN. The value can contain 'all' or a regular expression.
-X / --destroy_VM_instance (vm_hostname)
    the option, followed by the virtual machine hostname value, destroys and
    removes the specified virtual machine instance.
--list_available_hostname
    gets the available virtual machine hostnames (not yet assigned)
--list_available_baitname
    gets the available bait hostnames (not yet assigned)
--list_active_hostname
    get the active virtual machine hostnames
--list_active_baitname
    get the active bait hostname
--ping
    contacts a component and returns service version
-------------------------------------------------------------------------------
"""

    conf = os.path.join("/etc/wnodes/manager", "wnodes.ini")
    #conf = os.path.join(__dir_name__, "..", "etc", "wnodes.ini")
    host, port = loadNameserverAddress(conf)
    c = Connection(host, port)

    p = optparse.OptionParser(usage=usage_str)
    p.add_option("-a", "--add_image", action="callback",
                 callback=c.add_image, nargs=6, type="str")
    p.add_option("--purge_image_list", action="callback",
                 callback=c.purge_image_list)
    p.add_option("-d", "--delete_image", action="callback",
                 callback=c.del_image, nargs=1, type="str")
    p.add_option("-g", "--get_image_info", action="callback",
                 callback=c.get_image_info, nargs=1, type="str")
    p.add_option("-l", "--list_image", action="callback",
                 callback=c.list_image)
    p.add_option("-r", "--replace_image", action="callback",
                 callback=c.replace_image, nargs=6, type="str")
    p.add_option("-V", "--list_vlan", action="callback",
                 callback=c.list_vlan)
    p.add_option("-L", "--list_hostname", action="callback",
                 callback=c.list_hostname, nargs=1, type="str")
    p.add_option("-B", "--list_baitname", action="callback",
                 callback=c.print_baitname, nargs=1, type="str")
    p.add_option("-N", "--list_network_type", action="callback",
                 callback=c.list_network_type, nargs=1, type="str")
    p.add_option("-O", "--list_option_values", action="callback",
                 callback=c.get_option_values, nargs=2, type="str")
    p.add_option("-A", "--add_hostname", action="callback",
                 callback=c.add_hostname, nargs=2, type="str")
    p.add_option("-T", "--add_baitname", action="callback",
                 callback=c.add_baitname, nargs=2, type="str")
    p.add_option("-D", "--delete_hostname", action="callback",
                 callback=c.del_hostname, nargs=2, type="str")
    p.add_option("-E", "--delete_baitname", action="callback",
                 callback=c.del_baitname, nargs=2, type="str")
    p.add_option("-v", "--list_hv_config", action="callback",
                 callback=c.list_hv_config)
    p.add_option("-b", "--list_bait_config", action="callback",
                 callback=c.list_bait_config)
    p.add_option("-o", "--get_config", action="callback",
                 callback=c.get_config, nargs=2, type="str")
    p.add_option("-R", "--reload_host_config", action="callback",
                 callback=c.reload_host_config,  nargs=2, type="str")
    p.add_option("-P", "--replace_hv_config", action="callback",
                 callback=c.replace_hv_config,  nargs=2, type="str")
    p.add_option("-p", "--replace_bait_config", action="callback",
                 callback=c.replace_bait_config,  nargs=2, type="str")
    p.add_option("-S", "--get_hv_status", action="callback",
                 callback=c.get_hv_status, nargs=1, type="str")
    p.add_option("-s", "--get_bait_status", action="callback",
                 callback=c.get_bait_status, nargs=1, type="str")
    p.add_option("--bait_connection", action="callback",
                 callback=c.bait_connection, nargs=1, type="str")
    p.add_option("-t", "--hv_connection", action="callback",
                 callback=c.hv_connection, nargs=1, type="str")
    p.add_option("-w", "--open_bait", action="callback",
                 callback=c.open_bait, nargs=1, type="str")
    p.add_option("-x", "--close_bait", action="callback",
                 callback=c.close_bait, nargs=1, type="str")
    p.add_option("--test", action="callback",
                 callback=c.test, nargs=1, type="str")
#    p.add_option("--node_connection", action="callback",
#                 callback=c.node_connection, nargs=1, type="str")
#    p.add_option("-f", "--configFile", dest="configFile",
#                 default=os.path.join(__dir_name__, "..",
#                                      "etc", "wnodes.ini"),
#                 help="set configuration file [default: %default]",
#                 metavar="FILE")
    p.add_option("--list_available_hostname", action="callback",
                 callback=c.print_available_hostname)
    p.add_option("--list_available_baitname", action="callback",
                 callback=c.print_available_baitname)
    p.add_option("--list_active_hostname", action="callback",
                 callback=c.print_active_hostname)
    p.add_option("--list_active_baitname", action="callback",
                 callback=c.print_active_baitname)
    p.add_option("--ping", action="callback",
                 callback=c.ping, nargs=2, type="str")
    p.add_option("-X", "--destroy_VM_instance", action="callback",
                 callback=c.destroy_VM_instance, nargs=1, type="str")

    if len(sys.argv) == 1:
        p.print_help()
    else:
        options, args = p.parse_args()  # @UnusedVariable

if __name__ == "__main__":
    main()
