<filename>src/configuration_client.py
#!/usr/bin/env python
"""
Configuration server client.
"""

# system imports
import sys
import errno
import pprint
import os
import socket
import select
import types
import time
import imp
import getpass

# enstore imports
import generic_client
import enstore_constants
import enstore_functions2
import option
import Trace
import callback
import e_errors
import hostaddr

MY_NAME = enstore_constants.CONFIGURATION_CLIENT         #"CONFIG_CLIENT"
MY_SERVER = enstore_constants.CONFIGURATION_SERVER

class ConfigFlag:

    MSG_YES = 1
    MSG_NO = 0
    DISABLE = 1
    ENABLE = 0

    def __init__(self):
	self.new_config_file = self.MSG_NO
        self.do_caching = self.DISABLE

    def is_caching_enabled(self):
        return not self.do_caching

    def new_config_msg(self):
        #if self.do_caching == self.ENABLE:
        self.new_config_file = self.MSG_YES

    def reset_new_config(self):
        #if self.do_caching == self.ENABLE:
        self.new_config_file = self.MSG_NO

    def have_new_config(self):
        if self.do_caching == self.DISABLE:
            return 1
	elif self.new_config_file == self.MSG_YES:
	    return 1
	else:
	    return 0

    def disable_caching(self):
        self.do_caching = self.DISABLE

    def enable_caching(self):
        self.do_caching = self.ENABLE

class ConfigurationClient(generic_client.GenericClient):
    """
    Configuration client
    """
    def __init__(self, address=None):
        """
        :type address: :obj:`tuple`
        :arg address: (:obj:`str` - host, :obj:`int` - port) - configuration server address
        """
        if address is None:
            address = (enstore_functions2.default_host(),
                       enstore_functions2.default_port())
	flags = enstore_constants.NO_CSC | enstore_constants.NO_ALARM | \
		enstore_constants.NO_LOG
	generic_client.GenericClient.__init__(self, (), MY_NAME,
                                              address, flags=flags,
                                              server_name = MY_SERVER)
        self.new_config_obj = ConfigFlag()
	self.saved_dict = {}
        self.have_complete_config = 0
        self.config_load_timestamp = None

    #Return these values when requested.
    def get_address(self):
        return self.server_address
    def get_timeout(self):
        return self.timeout
    def get_retry(self):
        return self.retry

    #This function is needed by clients that use dump_and_save() to determine
    # if the cached configuration is the current configuration loaded into
    # the configuration_server.  Server's that use dump_and_save() have
    # have_new_config() to determine this same information from event_relay
    # NEWCONFIGFILE messages.
    def is_config_current(self):
        if self.config_load_timestamp == None:
            return False

        result = self.config_load_time(5, 5)
        if e_errors.is_ok(result):
            ##############################################################
            # This check added because a traceback was found 1-12-2009
            # on STKen where config_load_timestamp was not in result.
            if not result.has_key('config_load_timestamp'):
                Trace.log(e_errors.ERROR,
                          "ticket missing config_load_timestamp is: %s" % \
                          (result,))
            ##############################################################

            if result['config_load_timestamp'] <= self.config_load_timestamp:
                return True

        return False

    #Return which key in the 'known_config_servers' configuration dictionary
    # entry refers to this client's server (if present).  If there is
    # not an entry (like a developers test system) then a value is returned
    # based on the configuration servers nodename.
    def get_enstore_system(self, timeout=0, retry=0):

        while 1:
            ret = self.get('known_config_servers', timeout, retry)

            if e_errors.is_ok(ret):
                break
            else:
                #Return None if no responce from the configuration
                # server was received.
                return None

        for item in ret.items():
            if socket.getfqdn(item[1][0]) == \
               socket.getfqdn(self.server_address[0]):
                return item[0]

        #If we make it here, then we did receive a resonce from the
        # configuration server, however we did not find the system this
        # is looking for in the list received.
        return socket.getfqdn(self.server_address[0]).split(".")[0]

    def do_lookup(self, key, timeout, retry):
        """
        Lookup configuration item.

        :type key: :obj:`str`
        :arg key: item in question
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: value
        """

        request = {'work' : 'lookup', 'lookup' : key, 'new' : 1}
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            try:
                #New format.  This is requested by new configuration clients
                # by adding the "'new' : 1" to the request ticket above.
                self.saved_dict[key] = ret[key]
                ret_val = ret[key]
            except KeyError:
                #Old format.
                self.saved_dict[key] = ret
                ret_val = ret
            #Trace.trace(23, "Get %s config info from server"%(key,))
        else:
            ret_val = ret

        #Keep the hostaddr allow() information up-to-date on all lookups.
        hostaddr.update_domains(ret.get('domains', {}))

	return ret_val

    def get(self, key, timeout=0, retry=0):
        """
        Return value for requested item.

        :type key: :obj:`str`
        :arg key: item in question
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        self.timeout = timeout #Remember this.
        self.retry = retry     #Remember this.
        if key == enstore_constants.CONFIGURATION_SERVER:
            ret = {'hostip':self.server_address[0],
                   'port':self.server_address[1],
                   'status':(e_errors.OK, None)}
        else:
	    # if we have a new_config_obj, then only go to the config server if we
	    # have received a message saying a new one was loaded.
	    if not self.new_config_obj or self.new_config_obj.have_new_config():
		# clear out the cached copies
		self.saved_dict = {}
                #The config cache was just clobbered.
                self.have_complete_config = 0
                self.config_load_timestamp = None
		ret = self.do_lookup(key, timeout, retry)
		if self.new_config_obj:
		    self.new_config_obj.reset_new_config()
	    else:
                # there was no new config loaded, just return what we have.
                # if we do not have a stashed copy, go get it.
		if self.saved_dict.has_key(key):
		    #Trace.trace(23, "Returning %s config info from saved_dict"%(key,))
		    #Trace.trace(23, "saved_dict - %s"%(self.saved_dict,))
		    ret = self.saved_dict[key]
		else:
		    ret = self.do_lookup(key, timeout, retry)

        ##HACK:
        #Do a hack for the monitor server.  Since, it runs on all enstore
        # machines we need to add this information before continuing.
        if e_errors.is_ok(ret) and key == enstore_constants.MONITOR_SERVER:
            ret['host'] = socket.gethostname()
            #ret['hostip'] = socket.gethostbyname(ret['host'])
	    ret['hostip'] = hostaddr.name_to_address(ret['host'])
            ret['port'] = enstore_constants.MONITOR_PORT
        ##END HACK.

        ###Usefull for debugging.
        #if ret['status'][0] == e_errors.KEYERROR:
        #    import traceback
        #    Trace.log(e_errors.INFO, "Key %s requested from:" % (key,))
        #    # log it
        #    for l in traceback.format_stack():
        #        Trace.log(e_errors.INFO, l)

        return ret

    def dump(self, timeout=0, retry=0):
        """
        Dump the configuration dictionary (use active protocol).

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """
        ticket = {"work"          : "dump2",
                  }
        done_ticket = self.send(ticket, rcv_timeout = timeout,
                                tries = retry)

        #Try old way if the server is old too.
        if done_ticket['status'][0] == e_errors.KEYERROR and \
               done_ticket['status'][1].startswith("cannot find requested function"):
            done_ticket = self.dump_old(timeout, retry)
            return done_ticket #Avoid duplicate "convert to external format"
        if not e_errors.is_ok(done_ticket):
            return done_ticket
	hostaddr.update_domains(done_ticket.get("domains", {}))
        return done_ticket


    def dump_old(self, timeout=0, retry=0):
        """
        Dump the configuration dictionary.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        host, port, listen_socket = callback.get_callback()
        listen_socket.listen(4)

        request = {'work' : 'dump',
                   'callback_addr'  : (host,port)
                   }
        reply = self.send(request, timeout, retry)
        if not e_errors.is_ok(reply):
            #print "ERROR",reply
            return reply
        r, w, x = select.select([listen_socket], [], [], 15)
        if not r:
            reply = {'status' : (e_errors.TIMEDOUT,
                         "timeout waiting for configuration server callback")}
            return reply
        control_socket, address = listen_socket.accept()
        hostaddr.update_domains(reply.get('domains', {})) #Hackish.
        if not hostaddr.allow(address):
            listen_socket.close()
            control_socket.close()
            reply = {'status' : (e_errors.EPROTO,
                                 "address %s not allowed" % (address,))}
            return reply

        try:
            d = callback.read_tcp_obj(control_socket)
        except e_errors.EnstoreError, msg:
            d = {'status':(msg.type, str(msg))}
        except e_errors.TCP_EXCEPTION:
            d = {'status':(e_errors.TCP_EXCEPTION, e_errors.TCP_EXCEPTION)}
        listen_socket.close()
        control_socket.close()
        return d

    def dump_and_save(self, timeout=0, retry=0):
        """
        Dump the configuration dictionary and save it too.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        if not self.new_config_obj or self.new_config_obj.have_new_config() \
           or not self.is_config_current():

            config_ticket = self.dump(timeout = timeout, retry = retry)
            if e_errors.is_ok(config_ticket):
                self.saved_dict = config_ticket['dump'].copy()
                self.saved_dict['status'] = (e_errors.OK, None)
                self.have_complete_config = 1
                self.config_load_timestamp = \
                               config_ticket.get('config_load_timestamp', None)
                if self.new_config_obj:
                    self.new_config_obj.reset_new_config()

                return self.saved_dict  #Success.

            return config_ticket  #An error occured.

        return self.saved_dict #Used cached dictionary.

    def config_load_time(self, timeout=0, retry=0):
        """
        Find configuration load time.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """
        request = {'work' : 'config_timestamp' }
        x = self.send(request,  timeout,  retry )
        return x

    # get all keys in the configuration dictionary
    def get_keys(self, timeout=0, retry=0):
        """
        Get all keys in the configuration dictionary

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """
        request = {'work' : 'get_keys' }
        keys = self.send(request,  timeout,  retry )
        return keys

    def load(self, configfile, timeout=0, retry=0):
        """
        Reload a new  configuration dictionary.

        :type configfile: :obj:`str`
        :arg configfile: configuration file path
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """



        request = {'work' : 'load' ,  'configfile' : configfile, 'user': getpass.getuser() }
        x = self.send(request, timeout, retry)
        return x

    def threaded(self, on = 0, timeout=0, retry=0):
        """
        Multithreaded on / off

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """
        request = {'work' : 'thread_on' , 'on':on }
        x = self.send(request, timeout, retry)
        return x

    def copy_level(self, copy_level = 2, timeout=0, retry=0):
        """
        Set copy level in cofiguration server (may affect server performance).

        :type copy_level: :obj:`int`
        :arg copy_level: 2 = deepcopy, 1 = copy, 0 = direct reference.
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        request = {'work' : 'copy_level' , 'copy_level':copy_level }
        x = self.send(request, timeout, retry)
        return x

    def get_movers(self, library_manager, timeout=0, retry=0):
        """
        Get list of the Library manager movers.

        :type library_manager: :obj:`str`
        :arg library_manager: library manager name
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        request = {'work' : 'get_movers' ,  'library' : library_manager }
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            result = ret['movers']
        else:
            result = ret
        return result

    def get_movers2(self, library_manager, timeout=0, retry=0, conf_dict=None):
        """
        Get list of the Library manager movers with full config info.

        :type library_manager: :obj:`str`

        :arg library_manager: The library_manager parameter should be the full "name.library_manager"
                               style name.  If all movers are to be returned, pass None instead.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        mover_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
        if not e_errors.is_ok(conf_dict):
            return mover_list
        for item in conf_dict.items():
            if item[0][-6:] == ".mover":

                #If a library_manager was provided, make sure only
                # movers that use it are returned.
                if library_manager:
                    if type(item[1]['library']) == types.StringType:
                        lib_list = [item[1]['library']]
                    elif type(item[1]['library']) == types.ListType:
                        lib_list = item[1]['library']
                    else:
                        #Not an expected type, so it will never match.
                        continue
                    for library in lib_list:
                        if library_manager == library:
                            #Found a match for this mover to the
                            # requested library_manager.
                            break
                    else:
                        #No match.
                        continue

                item[1]['name'] = item[0]
                item[1]['mover'] = item[0][:-6]
                mover_list.append(item[1])

        return mover_list

    def get_migrators2(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of the migrators with full config info

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :type conf_dict: :obj:`str`
        :arg conf_dict: if not specified reload update configuration in cache
        :rtype: :obj:`dict` configuration server reply
        """

        migrator_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
        if not e_errors.is_ok(conf_dict):
            return migrator_list

        for key, value in conf_dict.items():
            if key[-9:] == ".migrator":
                value['name'] = key
                migrator_list.append(value)
        return migrator_list

    def get_migrators(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of the migrators.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :type conf_dict: :obj:`str`
        :arg conf_dict: if not specified reload update configuration in cache
        :rtype: :obj:`list` list of migrators
        """

        migrator_list = []
        migrator_list1 = self.get_migrators2(timeout, retry, conf_dict)
        for migrator in migrator_list1:
           migrator_list.append(migrator['name'])
        return migrator_list

    def get_media_changer(self, library_manager, timeout=0, retry=0):
        """
        Get media changer associated with a library manager.

        :type library_manager: :obj:`str`
        :arg library_manager: library manager
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`str` media changer
         """

        request = {'work' : 'get_media_changer' ,
                   'library' : library_manager }
        ret = self.send(request, timeout, retry)

        return  ret.get("media_changer", "")


    def get_library_managers(self, timeout=0, retry=0):
        """
        Get list of library managers.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of library managers
         """

        request = {'work': 'get_library_managers'}
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            result = ret['library_managers']
        else:
            result = ret
        return result

    def get_library_managers2(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of library managers with full config info.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of library managers
         """

        library_manager_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
        if not e_errors.is_ok(conf_dict):
            return library_manager_list

        for item in conf_dict.items():
            if item[0][-16:] == ".library_manager":
                item[1]['name'] = item[0]
                item[1]['library_manager'] = item[0][:-16]
                library_manager_list.append(item[1])

        return library_manager_list

    def get_media_changers(self, timeout=0, retry=0):
        """
        Get list of media changers.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of media changers
         """

        request = {'work': 'get_media_changers'}
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            result = ret['media_changers']
        else:
            result = ret
        return result

    def get_media_changers2(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of media changers with full config info.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of media changers
         """
        media_changer_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
        if not e_errors.is_ok(conf_dict):
            return media_changer_list

        for item in conf_dict.items():
            if item[0][-14:] == ".media_changer":
                item[1]['name'] = item[0]
                item[1]['media_changer'] = item[0][:-14]
                media_changer_list.append(item[1])

        return media_changer_list

    def get_migrators_list(self, timeout=0, retry=0):
        """
        Get list of the migrators directly from configuration server.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of migrators
        """
        request = {'work': 'get_migrators'}
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            result = ret['migrators']
        else:
            result = ret
        return result

    def get_proxy_servers2(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of proxy servers with full config info.
        Deprecated as we do not use proxy servers
        """
        proxy_server_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
            if not e_errors.is_ok(conf_dict):
                return proxy_server_list

        for item in conf_dict.items():
            if item[0][-17:] == ".udp_proxy_server":
                item[1]['name'] = item[0]
                item[1]['udp_proxy_server'] = item[0][:-17]
                proxy_server_list.append(item[1])

        return proxy_server_list

    def get_dict_entry(self, keyValue, timeout=0, retry=0):
        """
        Get the configuration dictionary element(s) that contain the specified
        key, value pair.

        :type keyValue: :obj:`tuple`
        :arg keyValue: key - value tuple
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict`

        """

        request = {'work': 'get_dict_element',
                   'keyValue': keyValue }
        return self.send(request, timeout, retry)

    def reply_serverlist(self, timeout=0, retry=0):
        """
        Get the configuration dictionary keys that refer to enstore servers.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict`
         """

        request = {'work': 'reply_serverlist',
                   }
        return self.send(request, timeout, retry)

class ConfigurationClientInterface(generic_client.GenericClientInterface):
    def __init__(self, args=sys.argv, user_mode=1):
        # fill in the defaults for the possible options
        #self.do_parse = flag
        #self.restricted_opts = opts
        self.config_file = ""
        self.show = 0
        self.load = 0
        self.server=""
        self.alive_rcv_timeout = generic_client.DEFAULT_TIMEOUT
        self.alive_retries = generic_client.DEFAULT_TRIES
        self.summary = 0
        self.timestamp = 0
        self.threaded_impl = None
        self.list_library_managers = 0
        self.list_media_changers = 0
        self.list_movers = 0
        self.list_migrators = 0
        self.file_fallback = 0
        self.print_1 = 0
        self.copy = None

        generic_client.GenericClientInterface.__init__(self, args=args,
                                                       user_mode=user_mode)

        # if we are using the default host and port, warn the user
        option.check_for_config_defaults()

    def valid_dictionaries(self):
        return (self.help_options, self.alive_options, self.trace_options,
                self.config_options)

    config_options = {
        option.CONFIG_FILE:{option.HELP_STRING:"config file to load",
                            option.VALUE_USAGE:option.REQUIRED,
                            option.DEFAULT_TYPE:option.STRING,
			    option.USER_LEVEL:option.ADMIN},
        option.COPY:{option.HELP_STRING:"internal copy level",
                            option.VALUE_USAGE:option.REQUIRED,
                            option.DEFAULT_TYPE:option.INTEGER,
			    option.USER_LEVEL:option.HIDDEN},
        option.FILE_FALLBACK:{option.HELP_STRING:"return configuration from"
                              " file if configuration server is down",
                              option.DEFAULT_TYPE:option.INTEGER,
                              option.USER_LEVEL:option.ADMIN},
        option.LIST_LIBRARY_MANAGERS:{option.HELP_STRING:
                                      "list all library managers in "
                                      "configuration",
                                      option.DEFAULT_VALUE:option.DEFAULT,
                                      option.DEFAULT_TYPE:option.INTEGER,
                                      option.VALUE_USAGE:option.IGNORED,
                                      option.USER_LEVEL:option.ADMIN},
        option.LIST_MEDIA_CHANGERS:{option.HELP_STRING:
                                    "list all media changers in "
                                    "configuration",
                                    option.DEFAULT_VALUE:option.DEFAULT,
                                    option.DEFAULT_TYPE:option.INTEGER,
                                    option.VALUE_USAGE:option.IGNORED,
                                    option.USER_LEVEL:option.ADMIN},
        option.LIST_MOVERS:{option.HELP_STRING:
                            "list all movers in configuration",
                            option.DEFAULT_VALUE:option.DEFAULT,
                            option.DEFAULT_TYPE:option.INTEGER,
                            option.VALUE_USAGE:option.IGNORED,
                            option.USER_LEVEL:option.ADMIN},
        option.LIST_MIGRATORS:{option.HELP_STRING:
                            "list all migrators in configuration",
                            option.DEFAULT_VALUE:option.DEFAULT,
                            option.DEFAULT_TYPE:option.INTEGER,
                            option.VALUE_USAGE:option.IGNORED,
                            option.USER_LEVEL:option.ADMIN},
        option.LOAD:{option.HELP_STRING:"load a new configuration",
                     option.DEFAULT_TYPE:option.INTEGER,
		     option.USER_LEVEL:option.ADMIN},
        option.PRINT:{option.HELP_STRING:"print the current configuration",
                      option.DEFAULT_TYPE:option.INTEGER,
                      #Default label is used for switches that take an
                      # unknown number arguments from intf.args and not
                      # from the specification in this dictionary.
                      option.DEFAULT_LABEL:"[value_name [value_name [...]]]",
                      option.USER_LEVEL:option.ADMIN,
                      option.DEFAULT_NAME:"print_1",
                      },
        option.SHOW:{option.HELP_STRING:"print the current configuration in python format",
                     option.DEFAULT_TYPE:option.INTEGER,
                     #Default label is used for switches that take an
                     # unknown number arguments from intf.args and not
                     # from the specification in this dictionary.
                     option.DEFAULT_LABEL:"[value_name [value_name [...]]]",
                     option.USER_LEVEL:option.ADMIN,
                     #option.VALUE_LABEL:"[value_name [value_name [...]]]",
                     #option.EXTRA_VALUES:[{
                     #    option.VALUE_NAME:"server",
                     #    option.VALUE_TYPE:option.STRING,
                     #    option.VALUE_USAGE:option.OPTIONAL,
                     #    option.DEFAULT_TYPE:None,
                     #    option.DEFAULT_VALUE:None
                     #    }]
                     },
        option.SUMMARY:{option.HELP_STRING:"summary for saag",
                        option.DEFAULT_TYPE:option.INTEGER,
			option.USER_LEVEL:option.ADMIN},
        option.TIMESTAMP:{option.HELP_STRING:
                          "last time configfile was reloaded",
                          option.DEFAULT_TYPE:option.INTEGER,
                          option.USER_LEVEL:option.ADMIN},
        option.THREADED_IMPL:{option.HELP_STRING:
                              "Turn on / off threaded implementation",
                              option.VALUE_USAGE:option.REQUIRED,
                              option.DEFAULT_TYPE:option.INTEGER,
                              option.USER_LEVEL:option.ADMIN},
         }

#Used for --print.
def flatten2(prefix, value, flat_dict):
    if type(value) == type({}):
        for i in value.keys():
            if prefix:
                flatten2(prefix+'.'+str(i), value[i], flat_dict)
            else:
                flatten2(str(i), value[i], flat_dict) #Avoid . for first char.
    elif type(value) == type([]) or type(value) == type(()):
        for i in range(len(value)):
            if prefix:
                flatten2(prefix+'.'+str(i), value[i], flat_dict)
            else:
                flatten2(str(i), value[i], flat_dict) #Avoid . for first char.
    else:
            flat_dict[prefix] = value

def print_configuration(config_dict, intf, prefix = ""):

    if intf.show:
        #If there wasn't a problem finding the information, print it.
        if type(config_dict) == types.StringType:
            #Suppress the '' that pprint.pprint() wants to surround
            # native strings.
            print config_dict
        else:
            pprint.pprint(config_dict)

    elif intf.print_1:
        #Make a dictionary that only contains the flattened names for the
        # keys with their values.
        flat_dict = {}
        flatten2(prefix, config_dict, flat_dict)

        #Sort the list and print the values out.
        sorted_list = flat_dict.keys()
        sorted_list.sort()
        for key in sorted_list:
            print "%s:%s"%(key, flat_dict[key])



def do_work(intf):
    csc = ConfigurationClient((intf.config_host, intf.config_port))
    csc.csc = csc
    result = csc.handle_generic_commands(MY_SERVER, intf)
    if intf.alive:
        if result['status'] == (e_errors.OK, None):
            print "Server configuration found at %s." % (result['address'],)
    if result:
        pass
    elif intf.show or intf.print_1:
        if intf.alive_rcv_timeout != generic_client.DEFAULT_TIMEOUT:
            use_timeout = intf.alive_rcv_timeout
        elif intf.file_fallback:
            use_timeout = 3  #Need to override in this case.
        else:
            use_timeout = generic_client.DEFAULT_TIMEOUT

        if intf.alive_retries != generic_client.DEFAULT_TIMEOUT:
            use_tries = intf.alive_retries
        elif intf.file_fallback:
            use_tries = 3    #Need to override in this case.
        else:
            use_tries = generic_client.DEFAULT_TRIES

        #Attempt to get the configuration from the configuration server.
        try:
            result = csc.dump(use_timeout, use_tries)
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        #If we didn't get the configuration from the server, attempt
        # to get it from the local copy of the configuration file.
        if result['status'][0] in [e_errors.TIMEDOUT, e_errors.NET_ERROR]:
            if intf.file_fallback:
                result = {}
                try:
                    result['dump'] = configdict_from_file()
                    result['status'] = (e_errors.OK, None)
                except IOError, msg:
                    result['status'] = (e_errors.IOERROR, str(msg))
                except OSError, msg:
                    result['status'] = (e_errors.OSERROR, str(msg))

        #If there is an error it is printed out at the end of the function
        # in csc.check_ticket().  On success, work as normal.
        if e_errors.is_ok(result):
            #Loop through what the user specified (if anything) and return
            # the desired result(s).
            use_config = result['dump']
            prefix = ""   #prefix is only used if --print was given.
            for item in intf.args:
                if type(use_config) == types.DictType:
                    try:
                        use_config = use_config[item]
                        #prefix is only used if --print was given.
                        if prefix:
                            prefix = "%s.%s" % (prefix, item)
                        else:
                            prefix = "%s" % (item,)
                    except KeyError:
                        result['status'] = (e_errors.KEYERROR,
                            "Unable to find requested information (1).\n")
                        break
                else:
                    result['status'] = (e_errors.CONFLICT,
                            "Unable to find requested information (2).\n")
                    break
            else:
                #Print the configuration to the terminal/stdout.
                print_configuration(use_config, intf, prefix)

    elif intf.load:
        result= csc.load(intf.config_file, intf.alive_rcv_timeout,
	                intf.alive_retries)

    elif intf.summary:
        result= csc.get_keys(intf.alive_rcv_timeout,intf.alive_retries)
        pprint.pprint(result['get_keys'])

    elif intf.timestamp:
        result = csc.config_load_time(intf.alive_rcv_timeout,
                                      intf.alive_retries)
        if e_errors.is_ok(result):
            print time.ctime(result['config_load_timestamp'])
    elif intf.threaded_impl != None:
        result = csc.threaded(intf.threaded_impl, intf.alive_rcv_timeout,
                                      intf.alive_retries)
        print result
    elif intf.copy != None:
        result = csc.copy_level(intf.copy, intf.alive_rcv_timeout,
                                intf.alive_retries)
        print result
    elif intf.list_library_managers:
        try:
            result = csc.get_library_managers(
                timeout = intf.alive_rcv_timeout, retry = intf.alive_retries)
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        if result.get("status", None) == None or e_errors.is_ok(result):
            msg_spec = "%25s %15s"
            print msg_spec % ("library manager", "host")
            for lm_name in result.values():
                lm_info = csc.get(lm_name['name'])
                print msg_spec % (lm_name['name'], lm_info['host'])

    elif intf.list_media_changers:
        try:
            result = csc.get_media_changers(
                timeout = intf.alive_rcv_timeout, retry = intf.alive_retries)
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        if result.get("status", None) == None or e_errors.is_ok(result):
            msg_spec = "%25s %15s %20s"
            print msg_spec % ("media changer", "host", "type")
            for mc_name in result.values():
                mc_info = csc.get(mc_name['name'])
                print msg_spec % (mc_name['name'], mc_info['host'],
                                  mc_info['type'])

    elif intf.list_movers:
        try:
            movers_list = csc.get_movers(None,
                timeout = intf.alive_rcv_timeout, retry = intf.alive_retries)
            result = {'status' : (e_errors.OK, None)}
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        if type(movers_list) == types.ListType:
            msg_spec = "%15s %15s %9s %10s %15s"
            print msg_spec % ("mover", "host", "mc_device", "driver", "library")
            for mover_name in movers_list:
                mover_info = csc.get(mover_name['mover'])
                print msg_spec % (mover_name['mover'], mover_info['host'],
                                  mover_info.get('mc_device','N/A'), mover_info['driver'],
                                  mover_info['library'])

    elif intf.list_migrators:
        try:
            result = csc.get_migrators_list(
                timeout = intf.alive_rcv_timeout, retry = intf.alive_retries)
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        if result.get("status", None) == None or e_errors.is_ok(result):
            msg_spec = "%25s %15s"
            print msg_spec % ("migrator", "host")
            for migrator in result.values():
                mig_info = csc.get(migrator['name'])
                print msg_spec % (migrator['name'], mig_info['host'])
    else:
	intf.print_help()
        sys.exit(0)

    csc.check_ticket(result)

# configdict_from_file() -- make configdict from config file
def configdict_from_file(config_file = None):
    # if no config_file, get it from ENSTORE_CONFIG_FILE
    if not config_file:
        config_file = os.environ['ENSTORE_CONFIG_FILE']
    f = open(config_file)
    res = imp.load_module("fake config", f, 'config-file', ('.py', 'r', imp.PY_SOURCE))
    f.close()
    return res.configdict


def get_config_dict(timeout=5, retry=2):
    config_host = enstore_functions2.default_host()
    config_port = enstore_functions2.default_port()
    csc = ConfigurationClient((config_host,config_port))
    config_dict = csc.dump_and_save(timeout, retry)
    if not e_errors.is_ok(config_dict):
        try:
            config_dict = configdict_from_file()
            print "configuration_server is not responding ..."\
                  "Get configuration from local file: %s" % \
                  (os.environ['ENSTORE_CONFIG_FILE'],)
        except KeyError:
           config_dict ={}
    return config_dict


if __name__ == "__main__":
    Trace.init(MY_NAME)

    # fill in interface
    intf = ConfigurationClientInterface(user_mode=0)
    do_work(intf)

<filename>src/configuration_client.py
#!/usr/bin/env python
"""
Configuration server client.
"""

# system imports
import sys
import errno
import pprint
import os
import socket
import select
import types
import time
import imp
import getpass

# enstore imports
import generic_client
import enstore_constants
import enstore_functions2
import option
import Trace
import callback
import e_errors
import hostaddr

MY_NAME = enstore_constants.CONFIGURATION_CLIENT         #"CONFIG_CLIENT"
MY_SERVER = enstore_constants.CONFIGURATION_SERVER

class ConfigFlag:

    MSG_YES = 1
    MSG_NO = 0
    DISABLE = 1
    ENABLE = 0

    def __init__(self):
	self.new_config_file = self.MSG_NO
        self.do_caching = self.DISABLE

    def is_caching_enabled(self):
        return not self.do_caching

    def new_config_msg(self):
        #if self.do_caching == self.ENABLE:
        self.new_config_file = self.MSG_YES

    def reset_new_config(self):
        #if self.do_caching == self.ENABLE:
        self.new_config_file = self.MSG_NO

    def have_new_config(self):
        if self.do_caching == self.DISABLE:
            return 1
	elif self.new_config_file == self.MSG_YES:
	    return 1
	else:
	    return 0

    def disable_caching(self):
        self.do_caching = self.DISABLE

    def enable_caching(self):
        self.do_caching = self.ENABLE

class ConfigurationClient(generic_client.GenericClient):
    """
    Configuration client
    """
    def __init__(self, address=None):
        """
        :type address: :obj:`tuple`
        :arg address: (:obj:`str` - host, :obj:`int` - port) - configuration server address
        """
        if address is None:
            address = (enstore_functions2.default_host(),
                       enstore_functions2.default_port())
	flags = enstore_constants.NO_CSC | enstore_constants.NO_ALARM | \
		enstore_constants.NO_LOG
	generic_client.GenericClient.__init__(self, (), MY_NAME,
                                              address, flags=flags,
                                              server_name = MY_SERVER)
        self.new_config_obj = ConfigFlag()
	self.saved_dict = {}
        self.have_complete_config = 0
        self.config_load_timestamp = None

    #Return these values when requested.
    def get_address(self):
        return self.server_address
    def get_timeout(self):
        return self.timeout
    def get_retry(self):
        return self.retry

    #This function is needed by clients that use dump_and_save() to determine
    # if the cached configuration is the current configuration loaded into
    # the configuration_server.  Server's that use dump_and_save() have
    # have_new_config() to determine this same information from event_relay
    # NEWCONFIGFILE messages.
    def is_config_current(self):
        if self.config_load_timestamp == None:
            return False

        result = self.config_load_time(5, 5)
        if e_errors.is_ok(result):
            ##############################################################
            # This check added because a traceback was found 1-12-2009
            # on STKen where config_load_timestamp was not in result.
            if not result.has_key('config_load_timestamp'):
                Trace.log(e_errors.ERROR,
                          "ticket missing config_load_timestamp is: %s" % \
                          (result,))
            ##############################################################

            if result['config_load_timestamp'] <= self.config_load_timestamp:
                return True

        return False

    #Return which key in the 'known_config_servers' configuration dictionary
    # entry refers to this client's server (if present).  If there is
    # not an entry (like a developers test system) then a value is returned
    # based on the configuration servers nodename.
    def get_enstore_system(self, timeout=0, retry=0):

        while 1:
            ret = self.get('known_config_servers', timeout, retry)

            if e_errors.is_ok(ret):
                break
            else:
                #Return None if no responce from the configuration
                # server was received.
                return None

        for item in ret.items():
            if socket.getfqdn(item[1][0]) == \
               socket.getfqdn(self.server_address[0]):
                return item[0]

        #If we make it here, then we did receive a resonce from the
        # configuration server, however we did not find the system this
        # is looking for in the list received.
        return socket.getfqdn(self.server_address[0]).split(".")[0]

    def do_lookup(self, key, timeout, retry):
        """
        Lookup configuration item.

        :type key: :obj:`str`
        :arg key: item in question
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: value
        """

        request = {'work' : 'lookup', 'lookup' : key, 'new' : 1}
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            try:
                #New format.  This is requested by new configuration clients
                # by adding the "'new' : 1" to the request ticket above.
                self.saved_dict[key] = ret[key]
                ret_val = ret[key]
            except KeyError:
                #Old format.
                self.saved_dict[key] = ret
                ret_val = ret
            #Trace.trace(23, "Get %s config info from server"%(key,))
        else:
            ret_val = ret

        #Keep the hostaddr allow() information up-to-date on all lookups.
        hostaddr.update_domains(ret.get('domains', {}))

	return ret_val

    def get(self, key, timeout=0, retry=0):
        """
        Return value for requested item.

        :type key: :obj:`str`
        :arg key: item in question
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        self.timeout = timeout #Remember this.
        self.retry = retry     #Remember this.
        if key == enstore_constants.CONFIGURATION_SERVER:
            ret = {'hostip':self.server_address[0],
                   'port':self.server_address[1],
                   'status':(e_errors.OK, None)}
        else:
	    # if we have a new_config_obj, then only go to the config server if we
	    # have received a message saying a new one was loaded.
	    if not self.new_config_obj or self.new_config_obj.have_new_config():
		# clear out the cached copies
		self.saved_dict = {}
                #The config cache was just clobbered.
                self.have_complete_config = 0
                self.config_load_timestamp = None
		ret = self.do_lookup(key, timeout, retry)
		if self.new_config_obj:
		    self.new_config_obj.reset_new_config()
	    else:
                # there was no new config loaded, just return what we have.
                # if we do not have a stashed copy, go get it.
		if self.saved_dict.has_key(key):
		    #Trace.trace(23, "Returning %s config info from saved_dict"%(key,))
		    #Trace.trace(23, "saved_dict - %s"%(self.saved_dict,))
		    ret = self.saved_dict[key]
		else:
		    ret = self.do_lookup(key, timeout, retry)

        ##HACK:
        #Do a hack for the monitor server.  Since, it runs on all enstore
        # machines we need to add this information before continuing.
        if e_errors.is_ok(ret) and key == enstore_constants.MONITOR_SERVER:
            ret['host'] = socket.gethostname()
            #ret['hostip'] = socket.gethostbyname(ret['host'])
	    ret['hostip'] = hostaddr.name_to_address(ret['host'])
            ret['port'] = enstore_constants.MONITOR_PORT
        ##END HACK.

        ###Usefull for debugging.
        #if ret['status'][0] == e_errors.KEYERROR:
        #    import traceback
        #    Trace.log(e_errors.INFO, "Key %s requested from:" % (key,))
        #    # log it
        #    for l in traceback.format_stack():
        #        Trace.log(e_errors.INFO, l)

        return ret

    def dump(self, timeout=0, retry=0):
        """
        Dump the configuration dictionary (use active protocol).

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """
        ticket = {"work"          : "dump2",
                  }
        done_ticket = self.send(ticket, rcv_timeout = timeout,
                                tries = retry)

        #Try old way if the server is old too.
        if done_ticket['status'][0] == e_errors.KEYERROR and \
               done_ticket['status'][1].startswith("cannot find requested function"):
            done_ticket = self.dump_old(timeout, retry)
            return done_ticket #Avoid duplicate "convert to external format"
        if not e_errors.is_ok(done_ticket):
            return done_ticket
	hostaddr.update_domains(done_ticket.get("domains", {}))
        return done_ticket


    def dump_old(self, timeout=0, retry=0):
        """
        Dump the configuration dictionary.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        host, port, listen_socket = callback.get_callback()
        listen_socket.listen(4)

        request = {'work' : 'dump',
                   'callback_addr'  : (host,port)
                   }
        reply = self.send(request, timeout, retry)
        if not e_errors.is_ok(reply):
            #print "ERROR",reply
            return reply
        r, w, x = select.select([listen_socket], [], [], 15)
        if not r:
            reply = {'status' : (e_errors.TIMEDOUT,
                         "timeout waiting for configuration server callback")}
            return reply
        control_socket, address = listen_socket.accept()
        hostaddr.update_domains(reply.get('domains', {})) #Hackish.
        if not hostaddr.allow(address):
            listen_socket.close()
            control_socket.close()
            reply = {'status' : (e_errors.EPROTO,
                                 "address %s not allowed" % (address,))}
            return reply

        try:
            d = callback.read_tcp_obj(control_socket)
        except e_errors.EnstoreError, msg:
            d = {'status':(msg.type, str(msg))}
        except e_errors.TCP_EXCEPTION:
            d = {'status':(e_errors.TCP_EXCEPTION, e_errors.TCP_EXCEPTION)}
        listen_socket.close()
        control_socket.close()
        return d

    def dump_and_save(self, timeout=0, retry=0):
        """
        Dump the configuration dictionary and save it too.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        if not self.new_config_obj or self.new_config_obj.have_new_config() \
           or not self.is_config_current():

            config_ticket = self.dump(timeout = timeout, retry = retry)
            if e_errors.is_ok(config_ticket):
                self.saved_dict = config_ticket['dump'].copy()
                self.saved_dict['status'] = (e_errors.OK, None)
                self.have_complete_config = 1
                self.config_load_timestamp = \
                               config_ticket.get('config_load_timestamp', None)
                if self.new_config_obj:
                    self.new_config_obj.reset_new_config()

                return self.saved_dict  #Success.

            return config_ticket  #An error occured.

        return self.saved_dict #Used cached dictionary.

    def config_load_time(self, timeout=0, retry=0):
        """
        Find configuration load time.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """
        request = {'work' : 'config_timestamp' }
        x = self.send(request,  timeout,  retry )
        return x

    # get all keys in the configuration dictionary
    def get_keys(self, timeout=0, retry=0):
        """
        Get all keys in the configuration dictionary

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """
        request = {'work' : 'get_keys' }
        keys = self.send(request,  timeout,  retry )
        return keys

    def load(self, configfile, timeout=0, retry=0):
        """
        Reload a new  configuration dictionary.

        :type configfile: :obj:`str`
        :arg configfile: configuration file path
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """



        request = {'work' : 'load' ,  'configfile' : configfile, 'user': getpass.getuser() }
        x = self.send(request, timeout, retry)
        return x

    def threaded(self, on = 0, timeout=0, retry=0):
        """
        Multithreaded on / off

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """
        request = {'work' : 'thread_on' , 'on':on }
        x = self.send(request, timeout, retry)
        return x

    def copy_level(self, copy_level = 2, timeout=0, retry=0):
        """
        Set copy level in cofiguration server (may affect server performance).

        :type copy_level: :obj:`int`
        :arg copy_level: 2 = deepcopy, 1 = copy, 0 = direct reference.
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        request = {'work' : 'copy_level' , 'copy_level':copy_level }
        x = self.send(request, timeout, retry)
        return x

    def get_movers(self, library_manager, timeout=0, retry=0):
        """
        Get list of the Library manager movers.

        :type library_manager: :obj:`str`
        :arg library_manager: library manager name
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        request = {'work' : 'get_movers' ,  'library' : library_manager }
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            result = ret['movers']
        else:
            result = ret
        return result

    def get_movers2(self, library_manager, timeout=0, retry=0, conf_dict=None):
        """
        Get list of the Library manager movers with full config info.

        :type library_manager: :obj:`str`

        :arg library_manager: The library_manager parameter should be the full "name.library_manager"
                               style name.  If all movers are to be returned, pass None instead.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict` configuration server reply
        """

        mover_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
        if not e_errors.is_ok(conf_dict):
            return mover_list
        for item in conf_dict.items():
            if item[0][-6:] == ".mover":

                #If a library_manager was provided, make sure only
                # movers that use it are returned.
                if library_manager:
                    if type(item[1]['library']) == types.StringType:
                        lib_list = [item[1]['library']]
                    elif type(item[1]['library']) == types.ListType:
                        lib_list = item[1]['library']
                    else:
                        #Not an expected type, so it will never match.
                        continue
                    for library in lib_list:
                        if library_manager == library:
                            #Found a match for this mover to the
                            # requested library_manager.
                            break
                    else:
                        #No match.
                        continue

                item[1]['name'] = item[0]
                item[1]['mover'] = item[0][:-6]
                mover_list.append(item[1])

        return mover_list

    def get_migrators2(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of the migrators with full config info

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :type conf_dict: :obj:`str`
        :arg conf_dict: if not specified reload update configuration in cache
        :rtype: :obj:`dict` configuration server reply
        """

        migrator_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
        if not e_errors.is_ok(conf_dict):
            return migrator_list

        for key, value in conf_dict.items():
            if key[-9:] == ".migrator":
                value['name'] = key
                migrator_list.append(value)
        return migrator_list

    def get_migrators(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of the migrators.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :type conf_dict: :obj:`str`
        :arg conf_dict: if not specified reload update configuration in cache
        :rtype: :obj:`list` list of migrators
        """

        migrator_list = []
        migrator_list1 = self.get_migrators2(timeout, retry, conf_dict)
        for migrator in migrator_list1:
           migrator_list.append(migrator['name'])
        return migrator_list

    def get_media_changer(self, library_manager, timeout=0, retry=0):
        """
        Get media changer associated with a library manager.

        :type library_manager: :obj:`str`
        :arg library_manager: library manager
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`str` media changer
         """

        request = {'work' : 'get_media_changer' ,
                   'library' : library_manager }
        ret = self.send(request, timeout, retry)

        return  ret.get("media_changer", "")


    def get_library_managers(self, timeout=0, retry=0):
        """
        Get list of library managers.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of library managers
         """

        request = {'work': 'get_library_managers'}
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            result = ret['library_managers']
        else:
            result = ret
        return result

    def get_library_managers2(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of library managers with full config info.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of library managers
         """

        library_manager_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
        if not e_errors.is_ok(conf_dict):
            return library_manager_list

        for item in conf_dict.items():
            if item[0][-16:] == ".library_manager":
                item[1]['name'] = item[0]
                item[1]['library_manager'] = item[0][:-16]
                library_manager_list.append(item[1])

        return library_manager_list

    def get_media_changers(self, timeout=0, retry=0):
        """
        Get list of media changers.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of media changers
         """

        request = {'work': 'get_media_changers'}
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            result = ret['media_changers']
        else:
            result = ret
        return result

    def get_media_changers2(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of media changers with full config info.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of media changers
         """
        media_changer_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
        if not e_errors.is_ok(conf_dict):
            return media_changer_list

        for item in conf_dict.items():
            if item[0][-14:] == ".media_changer":
                item[1]['name'] = item[0]
                item[1]['media_changer'] = item[0][:-14]
                media_changer_list.append(item[1])

        return media_changer_list

    def get_migrators_list(self, timeout=0, retry=0):
        """
        Get list of the migrators directly from configuration server.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`list` list of migrators
        """
        request = {'work': 'get_migrators'}
        ret = self.send(request, timeout, retry)
        if e_errors.is_ok(ret):
            result = ret['migrators']
        else:
            result = ret
        return result

    def get_proxy_servers2(self, timeout=0, retry=0, conf_dict=None):
        """
        Get list of proxy servers with full config info.
        Deprecated as we do not use proxy servers
        """
        proxy_server_list = []

        if conf_dict == None:
            conf_dict = self.dump_and_save(timeout = timeout, retry = retry)
            if not e_errors.is_ok(conf_dict):
                return proxy_server_list

        for item in conf_dict.items():
            if item[0][-17:] == ".udp_proxy_server":
                item[1]['name'] = item[0]
                item[1]['udp_proxy_server'] = item[0][:-17]
                proxy_server_list.append(item[1])

        return proxy_server_list

    def get_dict_entry(self, keyValue, timeout=0, retry=0):
        """
        Get the configuration dictionary element(s) that contain the specified
        key, value pair.

        :type keyValue: :obj:`tuple`
        :arg keyValue: key - value tuple
        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict`

        """

        request = {'work': 'get_dict_element',
                   'keyValue': keyValue }
        return self.send(request, timeout, retry)

    def reply_serverlist(self, timeout=0, retry=0):
        """
        Get the configuration dictionary keys that refer to enstore servers.

        :type timeout: :obj:`float`
        :arg timeout: reply waiting time
        :type retry: :obj:`int`
        :arg retry: number of retries
        :rtype: :obj:`dict`
         """

        request = {'work': 'reply_serverlist',
                   }
        return self.send(request, timeout, retry)

class ConfigurationClientInterface(generic_client.GenericClientInterface):
    def __init__(self, args=sys.argv, user_mode=1):
        # fill in the defaults for the possible options
        #self.do_parse = flag
        #self.restricted_opts = opts
        self.config_file = ""
        self.show = 0
        self.load = 0
        self.server=""
        self.alive_rcv_timeout = generic_client.DEFAULT_TIMEOUT
        self.alive_retries = generic_client.DEFAULT_TRIES
        self.summary = 0
        self.timestamp = 0
        self.threaded_impl = None
        self.list_library_managers = 0
        self.list_media_changers = 0
        self.list_movers = 0
        self.list_migrators = 0
        self.file_fallback = 0
        self.print_1 = 0
        self.copy = None

        generic_client.GenericClientInterface.__init__(self, args=args,
                                                       user_mode=user_mode)

        # if we are using the default host and port, warn the user
        option.check_for_config_defaults()

    def valid_dictionaries(self):
        return (self.help_options, self.alive_options, self.trace_options,
                self.config_options)

    config_options = {
        option.CONFIG_FILE:{option.HELP_STRING:"config file to load",
                            option.VALUE_USAGE:option.REQUIRED,
                            option.DEFAULT_TYPE:option.STRING,
			    option.USER_LEVEL:option.ADMIN},
        option.COPY:{option.HELP_STRING:"internal copy level",
                            option.VALUE_USAGE:option.REQUIRED,
                            option.DEFAULT_TYPE:option.INTEGER,
			    option.USER_LEVEL:option.HIDDEN},
        option.FILE_FALLBACK:{option.HELP_STRING:"return configuration from"
                              " file if configuration server is down",
                              option.DEFAULT_TYPE:option.INTEGER,
                              option.USER_LEVEL:option.ADMIN},
        option.LIST_LIBRARY_MANAGERS:{option.HELP_STRING:
                                      "list all library managers in "
                                      "configuration",
                                      option.DEFAULT_VALUE:option.DEFAULT,
                                      option.DEFAULT_TYPE:option.INTEGER,
                                      option.VALUE_USAGE:option.IGNORED,
                                      option.USER_LEVEL:option.ADMIN},
        option.LIST_MEDIA_CHANGERS:{option.HELP_STRING:
                                    "list all media changers in "
                                    "configuration",
                                    option.DEFAULT_VALUE:option.DEFAULT,
                                    option.DEFAULT_TYPE:option.INTEGER,
                                    option.VALUE_USAGE:option.IGNORED,
                                    option.USER_LEVEL:option.ADMIN},
        option.LIST_MOVERS:{option.HELP_STRING:
                            "list all movers in configuration",
                            option.DEFAULT_VALUE:option.DEFAULT,
                            option.DEFAULT_TYPE:option.INTEGER,
                            option.VALUE_USAGE:option.IGNORED,
                            option.USER_LEVEL:option.ADMIN},
        option.LIST_MIGRATORS:{option.HELP_STRING:
                            "list all migrators in configuration",
                            option.DEFAULT_VALUE:option.DEFAULT,
                            option.DEFAULT_TYPE:option.INTEGER,
                            option.VALUE_USAGE:option.IGNORED,
                            option.USER_LEVEL:option.ADMIN},
        option.LOAD:{option.HELP_STRING:"load a new configuration",
                     option.DEFAULT_TYPE:option.INTEGER,
		     option.USER_LEVEL:option.ADMIN},
        option.PRINT:{option.HELP_STRING:"print the current configuration",
                      option.DEFAULT_TYPE:option.INTEGER,
                      #Default label is used for switches that take an
                      # unknown number arguments from intf.args and not
                      # from the specification in this dictionary.
                      option.DEFAULT_LABEL:"[value_name [value_name [...]]]",
                      option.USER_LEVEL:option.ADMIN,
                      option.DEFAULT_NAME:"print_1",
                      },
        option.SHOW:{option.HELP_STRING:"print the current configuration in python format",
                     option.DEFAULT_TYPE:option.INTEGER,
                     #Default label is used for switches that take an
                     # unknown number arguments from intf.args and not
                     # from the specification in this dictionary.
                     option.DEFAULT_LABEL:"[value_name [value_name [...]]]",
                     option.USER_LEVEL:option.ADMIN,
                     #option.VALUE_LABEL:"[value_name [value_name [...]]]",
                     #option.EXTRA_VALUES:[{
                     #    option.VALUE_NAME:"server",
                     #    option.VALUE_TYPE:option.STRING,
                     #    option.VALUE_USAGE:option.OPTIONAL,
                     #    option.DEFAULT_TYPE:None,
                     #    option.DEFAULT_VALUE:None
                     #    }]
                     },
        option.SUMMARY:{option.HELP_STRING:"summary for saag",
                        option.DEFAULT_TYPE:option.INTEGER,
			option.USER_LEVEL:option.ADMIN},
        option.TIMESTAMP:{option.HELP_STRING:
                          "last time configfile was reloaded",
                          option.DEFAULT_TYPE:option.INTEGER,
                          option.USER_LEVEL:option.ADMIN},
        option.THREADED_IMPL:{option.HELP_STRING:
                              "Turn on / off threaded implementation",
                              option.VALUE_USAGE:option.REQUIRED,
                              option.DEFAULT_TYPE:option.INTEGER,
                              option.USER_LEVEL:option.ADMIN},
         }

#Used for --print.
def flatten2(prefix, value, flat_dict):
    if type(value) == type({}):
        for i in value.keys():
            if prefix:
                flatten2(prefix+'.'+str(i), value[i], flat_dict)
            else:
                flatten2(str(i), value[i], flat_dict) #Avoid . for first char.
    elif type(value) == type([]) or type(value) == type(()):
        for i in range(len(value)):
            if prefix:
                flatten2(prefix+'.'+str(i), value[i], flat_dict)
            else:
                flatten2(str(i), value[i], flat_dict) #Avoid . for first char.
    else:
            flat_dict[prefix] = value

def print_configuration(config_dict, intf, prefix = ""):

    if intf.show:
        #If there wasn't a problem finding the information, print it.
        if type(config_dict) == types.StringType:
            #Suppress the '' that pprint.pprint() wants to surround
            # native strings.
            print config_dict
        else:
            pprint.pprint(config_dict)

    elif intf.print_1:
        #Make a dictionary that only contains the flattened names for the
        # keys with their values.
        flat_dict = {}
        flatten2(prefix, config_dict, flat_dict)

        #Sort the list and print the values out.
        sorted_list = flat_dict.keys()
        sorted_list.sort()
        for key in sorted_list:
            print "%s:%s"%(key, flat_dict[key])



def do_work(intf):
    csc = ConfigurationClient((intf.config_host, intf.config_port))
    csc.csc = csc
    result = csc.handle_generic_commands(MY_SERVER, intf)
    if intf.alive:
        if result['status'] == (e_errors.OK, None):
            print "Server configuration found at %s." % (result['address'],)
    if result:
        pass
    elif intf.show or intf.print_1:
        if intf.alive_rcv_timeout != generic_client.DEFAULT_TIMEOUT:
            use_timeout = intf.alive_rcv_timeout
        elif intf.file_fallback:
            use_timeout = 3  #Need to override in this case.
        else:
            use_timeout = generic_client.DEFAULT_TIMEOUT

        if intf.alive_retries != generic_client.DEFAULT_TIMEOUT:
            use_tries = intf.alive_retries
        elif intf.file_fallback:
            use_tries = 3    #Need to override in this case.
        else:
            use_tries = generic_client.DEFAULT_TRIES

        #Attempt to get the configuration from the configuration server.
        try:
            result = csc.dump(use_timeout, use_tries)
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        #If we didn't get the configuration from the server, attempt
        # to get it from the local copy of the configuration file.
        if result['status'][0] in [e_errors.TIMEDOUT, e_errors.NET_ERROR]:
            if intf.file_fallback:
                result = {}
                try:
                    result['dump'] = configdict_from_file()
                    result['status'] = (e_errors.OK, None)
                except IOError, msg:
                    result['status'] = (e_errors.IOERROR, str(msg))
                except OSError, msg:
                    result['status'] = (e_errors.OSERROR, str(msg))

        #If there is an error it is printed out at the end of the function
        # in csc.check_ticket().  On success, work as normal.
        if e_errors.is_ok(result):
            #Loop through what the user specified (if anything) and return
            # the desired result(s).
            use_config = result['dump']
            prefix = ""   #prefix is only used if --print was given.
            for item in intf.args:
                if type(use_config) == types.DictType:
                    try:
                        use_config = use_config[item]
                        #prefix is only used if --print was given.
                        if prefix:
                            prefix = "%s.%s" % (prefix, item)
                        else:
                            prefix = "%s" % (item,)
                    except KeyError:
                        result['status'] = (e_errors.KEYERROR,
                            "Unable to find requested information (1).\n")
                        break
                else:
                    result['status'] = (e_errors.CONFLICT,
                            "Unable to find requested information (2).\n")
                    break
            else:
                #Print the configuration to the terminal/stdout.
                print_configuration(use_config, intf, prefix)

    elif intf.load:
        result= csc.load(intf.config_file, intf.alive_rcv_timeout,
	                intf.alive_retries)

    elif intf.summary:
        result= csc.get_keys(intf.alive_rcv_timeout,intf.alive_retries)
        pprint.pprint(result['get_keys'])

    elif intf.timestamp:
        result = csc.config_load_time(intf.alive_rcv_timeout,
                                      intf.alive_retries)
        if e_errors.is_ok(result):
            print time.ctime(result['config_load_timestamp'])
    elif intf.threaded_impl != None:
        result = csc.threaded(intf.threaded_impl, intf.alive_rcv_timeout,
                                      intf.alive_retries)
        print result
    elif intf.copy != None:
        result = csc.copy_level(intf.copy, intf.alive_rcv_timeout,
                                intf.alive_retries)
        print result
    elif intf.list_library_managers:
        try:
            result = csc.get_library_managers(
                timeout = intf.alive_rcv_timeout, retry = intf.alive_retries)
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        if result.get("status", None) == None or e_errors.is_ok(result):
            msg_spec = "%25s %15s"
            print msg_spec % ("library manager", "host")
            for lm_name in result.values():
                lm_info = csc.get(lm_name['name'])
                print msg_spec % (lm_name['name'], lm_info['host'])

    elif intf.list_media_changers:
        try:
            result = csc.get_media_changers(
                timeout = intf.alive_rcv_timeout, retry = intf.alive_retries)
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        if result.get("status", None) == None or e_errors.is_ok(result):
            msg_spec = "%25s %15s %20s"
            print msg_spec % ("media changer", "host", "type")
            for mc_name in result.values():
                mc_info = csc.get(mc_name['name'])
                print msg_spec % (mc_name['name'], mc_info['host'],
                                  mc_info['type'])

    elif intf.list_movers:
        try:
            movers_list = csc.get_movers(None,
                timeout = intf.alive_rcv_timeout, retry = intf.alive_retries)
            result = {'status' : (e_errors.OK, None)}
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        if type(movers_list) == types.ListType:
            msg_spec = "%15s %15s %9s %10s %15s"
            print msg_spec % ("mover", "host", "mc_device", "driver", "library")
            for mover_name in movers_list:
                mover_info = csc.get(mover_name['mover'])
                print msg_spec % (mover_name['mover'], mover_info['host'],
                                  mover_info.get('mc_device','N/A'), mover_info['driver'],
                                  mover_info['library'])

    elif intf.list_migrators:
        try:
            result = csc.get_migrators_list(
                timeout = intf.alive_rcv_timeout, retry = intf.alive_retries)
        except (KeyboardInterrupt, SystemExit):
            sys.exit(1)
        except (socket.error, select.error), msg:
            if msg.args[0] == errno.ETIMEDOUT:
                result = {'status' : (e_errors.TIMEDOUT, str(msg))}
            else:
                result = {'status' : (e_errors.NET_ERROR, str(msg))}

        if result.get("status", None) == None or e_errors.is_ok(result):
            msg_spec = "%25s %15s"
            print msg_spec % ("migrator", "host")
            for migrator in result.values():
                mig_info = csc.get(migrator['name'])
                print msg_spec % (migrator['name'], mig_info['host'])
    else:
	intf.print_help()
        sys.exit(0)

    csc.check_ticket(result)

# configdict_from_file() -- make configdict from config file
def configdict_from_file(config_file = None):
    # if no config_file, get it from ENSTORE_CONFIG_FILE
    if not config_file:
        config_file = os.environ['ENSTORE_CONFIG_FILE']
    f = open(config_file)
    res = imp.load_module("fake config", f, 'config-file', ('.py', 'r', imp.PY_SOURCE))
    f.close()
    return res.configdict


def get_config_dict(timeout=5, retry=2):
    config_host = enstore_functions2.default_host()
    config_port = enstore_functions2.default_port()
    csc = ConfigurationClient((config_host,config_port))
    config_dict = csc.dump_and_save(timeout, retry)
    if not e_errors.is_ok(config_dict):
        try:
            config_dict = configdict_from_file()
            print "configuration_server is not responding ..."\
                  "Get configuration from local file: %s" % \
                  (os.environ['ENSTORE_CONFIG_FILE'],)
        except KeyError:
           config_dict ={}
    return config_dict


if __name__ == "__main__":
    Trace.init(MY_NAME)

    # fill in interface
    intf = ConfigurationClientInterface(user_mode=0)
    do_work(intf)

#!/usr/bin/env python Configuration server client. # system imports # enstore imports #"CONFIG_CLIENT" #if self.do_caching == self.ENABLE: #if self.do_caching == self.ENABLE: Configuration client :type address: :obj:`tuple`         :arg address: (:obj:`str` - host, :obj:`int` - port) - configuration server address #Return these values when requested. #This function is needed by clients that use dump_and_save() to determine # if the cached configuration is the current configuration loaded into # the configuration_server.  Server's that use dump_and_save() have # have_new_config() to determine this same information from event_relay # NEWCONFIGFILE messages. ############################################################## # This check added because a traceback was found 1-12-2009 # on STKen where config_load_timestamp was not in result. ############################################################## #Return which key in the 'known_config_servers' configuration dictionary # entry refers to this client's server (if present).  If there is # not an entry (like a developers test system) then a value is returned # based on the configuration servers nodename. #Return None if no responce from the configuration # server was received. #If we make it here, then we did receive a resonce from the # configuration server, however we did not find the system this # is looking for in the list received. Lookup configuration item.          :type key: :obj:`str`         :arg key: item in question         :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: value #New format.  This is requested by new configuration clients # by adding the "'new' : 1" to the request ticket above. #Old format. #Trace.trace(23, "Get %s config info from server"%(key,)) #Keep the hostaddr allow() information up-to-date on all lookups. Return value for requested item.          :type key: :obj:`str`         :arg key: item in question         :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply #Remember this. #Remember this. # if we have a new_config_obj, then only go to the config server if we # have received a message saying a new one was loaded. # clear out the cached copies #The config cache was just clobbered. # there was no new config loaded, just return what we have. # if we do not have a stashed copy, go get it. #Trace.trace(23, "Returning %s config info from saved_dict"%(key,)) #Trace.trace(23, "saved_dict - %s"%(self.saved_dict,)) ##HACK: #Do a hack for the monitor server.  Since, it runs on all enstore # machines we need to add this information before continuing. #ret['hostip'] = socket.gethostbyname(ret['host']) ##END HACK. ###Usefull for debugging. #if ret['status'][0] == e_errors.KEYERROR: #    import traceback #    Trace.log(e_errors.INFO, "Key %s requested from:" % (key,)) #    # log it #    for l in traceback.format_stack(): #        Trace.log(e_errors.INFO, l) Dump the configuration dictionary (use active protocol).          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply #Try old way if the server is old too. #Avoid duplicate "convert to external format" Dump the configuration dictionary.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply #print "ERROR",reply #Hackish. Dump the configuration dictionary and save it too.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply #Success. #An error occured. #Used cached dictionary. Find configuration load time.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply # get all keys in the configuration dictionary Get all keys in the configuration dictionary          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply Reload a new  configuration dictionary.          :type configfile: :obj:`str`         :arg configfile: configuration file path         :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply Multithreaded on / off          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply Set copy level in cofiguration server (may affect server performance).          :type copy_level: :obj:`int`         :arg copy_level: 2 = deepcopy, 1 = copy, 0 = direct reference.         :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply Get list of the Library manager movers.          :type library_manager: :obj:`str`         :arg library_manager: library manager name         :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply Get list of the Library manager movers with full config info.          :type library_manager: :obj:`str`          :arg library_manager: The library_manager parameter should be the full "name.library_manager"                                style name.  If all movers are to be returned, pass None instead.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` configuration server reply #If a library_manager was provided, make sure only # movers that use it are returned. #Not an expected type, so it will never match. #Found a match for this mover to the # requested library_manager. #No match. Get list of the migrators with full config info          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :type conf_dict: :obj:`str`         :arg conf_dict: if not specified reload update configuration in cache         :rtype: :obj:`dict` configuration server reply Get list of the migrators.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :type conf_dict: :obj:`str`         :arg conf_dict: if not specified reload update configuration in cache         :rtype: :obj:`list` list of migrators Get media changer associated with a library manager.          :type library_manager: :obj:`str`         :arg library_manager: library manager         :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`str` media changer Get list of library managers.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`list` list of library managers Get list of library managers with full config info.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`list` list of library managers Get list of media changers.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`list` list of media changers Get list of media changers with full config info.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`list` list of media changers Get list of the migrators directly from configuration server.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`list` list of migrators Get list of proxy servers with full config info.         Deprecated as we do not use proxy servers Get the configuration dictionary element(s) that contain the specified         key, value pair.          :type keyValue: :obj:`tuple`         :arg keyValue: key - value tuple         :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` Get the configuration dictionary keys that refer to enstore servers.          :type timeout: :obj:`float`         :arg timeout: reply waiting time         :type retry: :obj:`int`         :arg retry: number of retries         :rtype: :obj:`dict` # fill in the defaults for the possible options #self.do_parse = flag #self.restricted_opts = opts # if we are using the default host and port, warn the user #Default label is used for switches that take an # unknown number arguments from intf.args and not # from the specification in this dictionary. #Default label is used for switches that take an # unknown number arguments from intf.args and not # from the specification in this dictionary. #option.VALUE_LABEL:"[value_name [value_name [...]]]", #option.EXTRA_VALUES:[{ #    option.VALUE_NAME:"server", #    option.VALUE_TYPE:option.STRING, #    option.VALUE_USAGE:option.OPTIONAL, #    option.DEFAULT_TYPE:None, #    option.DEFAULT_VALUE:None #    }] #Used for --print. #Avoid . for first char. #Avoid . for first char. #If there wasn't a problem finding the information, print it. #Suppress the '' that pprint.pprint() wants to surround # native strings. #Make a dictionary that only contains the flattened names for the # keys with their values. #Sort the list and print the values out. #Need to override in this case. #Need to override in this case. #Attempt to get the configuration from the configuration server. #If we didn't get the configuration from the server, attempt # to get it from the local copy of the configuration file. #If there is an error it is printed out at the end of the function # in csc.check_ticket().  On success, work as normal. #Loop through what the user specified (if anything) and return # the desired result(s). #prefix is only used if --print was given. #prefix is only used if --print was given. #Print the configuration to the terminal/stdout. # configdict_from_file() -- make configdict from config file # if no config_file, get it from ENSTORE_CONFIG_FILE # fill in interface