Skip to content
Snippets Groups Projects
Forked from DM / dm-docs
261 commits behind, 173 commits ahead of the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
dmCli.py 12.17 KiB
#!/usr/bin/env python

import sys
import os
import os.path
import stat
from optparse import OptionGroup

import dm
from dm.common.utility.loggingManager import LoggingManager
from dm.common.utility.configurationManager import ConfigurationManager
from dm.common.utility.osUtility import OsUtility
from dm.common.objects.dmObject import DmObject
from dm.common.exceptions.dmException import DmException
from dm.common.exceptions.invalidRequest import InvalidRequest
from dm.common.exceptions.invalidArgument import InvalidArgument
from dm.common.exceptions.internalError import InternalError
from dm.common.cli.dmOptionParser import DmOptionParser
from dm.common.constants import dmStatus

class DmCli(object):
    """ Base dm command line interface class. """
    DEFAULT_SESSION_CACHE_FILE = OsUtility.getUserHomeDir() + '/.dm/.session.cache'
    def __init__(self, validArgCount=0):
        self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
        # Do not log into a file for CLIs
        LoggingManager.getInstance().setFileLogLevel('CRITICAL') 
        LoggingManager.getInstance().setConsoleLogLevel('CRITICAL') 
        self.parser = DmOptionParser()
        self.options = {}
        self.args = []
        self.validArgCount = validArgCount
        self.serviceHost = None
        self.servicePort = None
        self.optionGroupDict = {}

        commonGroup = 'Common Options'
        self.addOptionGroup(commonGroup, None)
        self.addOptionToGroup(commonGroup, '-h', '--help', action='help', help='Show this help message and exit.')
        self.addOptionToGroup(commonGroup, '-?', '',       action='help', help='Show this help message and exit.')
        self.addOptionToGroup(commonGroup, '-v', '--version', action='store_true', dest='version', default=False, help='Print version and exit.')
        self.addOptionToGroup(commonGroup, '-d', '--debug', dest='consoleLogLevel', help='Set debug level (valid values: CRITICAL, ERROR, WARNING, INFO, DEBUG). Console log level can also be set via DM_CONSOLE_LOG_LEVEL environment variable,')
        self.addOptionToGroup(commonGroup, '', '--display-format', dest='displayFormat', default=DmObject.TEXT_DISPLAY_FORMAT, help='Display format for output objects. Possible options are: %s, %s, and %s (default: %s).' % (DmObject.TEXT_DISPLAY_FORMAT, DmObject.DICT_DISPLAY_FORMAT, DmObject.JSON_DISPLAY_FORMAT, DmObject.TEXT_DISPLAY_FORMAT)) 
        self.addOptionToGroup(commonGroup, '', '--display-keys', dest='displayKeys', default=DmObject.DEFAULT_KEYS, help='List of output object keys to display. Possible options are: %s, %s, and string containing comma-separated keys (default: %s, represents class default keys).' % (DmObject.DEFAULT_KEYS, DmObject.ALL_KEYS, DmObject.DEFAULT_KEYS)) 

        serviceGroup = 'Service Options'
        self.addOptionGroup(serviceGroup, None)
        # These will be set via env variables
        self.addOptionToGroup(serviceGroup, '', '--service-host', dest='serviceHost', default=self.getDefaultServiceHost(), help='Service host (default: %s, can be set via DM_SERVICE_HOST environment variable).' % self.getDefaultServiceHost())
        self.addOptionToGroup(serviceGroup, '', '--service-port', dest='servicePort', default=self.getDefaultServicePort(), help='Service port (default: %s, can be set via DM_SERVICE_PORT environment variable).' % self.getDefaultServicePort())
        self.addOptionToGroup(serviceGroup, '', '--service-protocol', dest='serviceProtocol', default=self.getDefaultServiceProtocol(), help='Service protocol (default: %s, can be set via DM_SERVICE_PROTOCOL environment variable).' % self.getDefaultServiceProtocol())

        # SSL options, disabled for now.
        #self.addOptionToGroup(commonGroup, '', '--ssl-key', dest='sslKeyFile', help='SSL key file (needed if service requires peer verification, can be set via DM_SSL_KEY_FILE environment variable).')
        #self.addOptionToGroup(commonGroup, '', '--ssl-cert', dest='sslCertFile', help='SSL certificate file (needed if service requires peer verification, can be set via DM_SSL_CERT_FILE environment variable).')
        #self.addOptionToGroup(commonGroup, '', '--ssl-ca-cert', dest='sslCaCertFile', help='SSL CA certificate file (needed if client requires peer verification, can be set via DM_SSL_CA_CERT_FILE environment variable).')
        
    def getDefaultServiceHost(self):
        return ConfigurationManager.getInstance().getServiceHost()

    def getDefaultServicePort(self):
        return ConfigurationManager.getInstance().getServicePort()

    def getDefaultServiceProtocol(self):
        return ConfigurationManager.getInstance().getServiceProtocol()

    def getUsername(self):
        return None

    def getPassword(self):
        return None

    def getDisplayFormat(self):
        return self.options.displayFormat

    def getDisplayKeys(self):
        return self.options.displayKeys

    def getLogger(self):
        return self.logger

    def getParser(self):
        return self.parser

    def addOption(self, *args, **kwargs):
        self.parser.add_option(*args, **kwargs)

    def addOptionToGroup(self, groupName, *args, **kwargs):
        """ Add group option. Group must be created using addOptionGroup(). """
        group = self.optionGroupDict.get(groupName)
        group.add_option(*args, **kwargs)

    def addOptionGroup(self, groupName, desc):
        group = OptionGroup(self.parser, groupName, desc)
        self.parser.add_option_group(group)
        self.optionGroupDict[groupName] = group

    def processArgs(self):
        pass

    def parseArgs(self, usage=None):
        if usage:
            self.parser.usage = usage
        try:
            (self.options, self.args) = self.parser.parse_args()
            self.processArgs()
        except SystemExit, rc:
            sys.stdout.flush()
            sys.stderr.flush()
            sys.exit(int(str(rc)))

        if self.validArgCount < len(self.args):
            # Positional args are not enabled and we have some
            msg = 'Invalid positional argument(s):'
            for arg in self.args[self.validArgCount:]:
                msg += ' ' + arg
            msg += ' (This command allows %s positional arguments.)' % self.validArgCount
            raise InvalidArgument(msg)

        optDict = self.options.__dict__
        if optDict.get('version'):
            print 'DM Software Version: %s' % (dm.__version__)
            sys.exit(0)

        # Logging level. First try from command line, then from env variable.
        consoleLogLevel = optDict.get('consoleLogLevel', None)
        if consoleLogLevel:
            LoggingManager.getInstance().setConsoleLogLevel(consoleLogLevel)
        else:
            consoleLogLevel = ConfigurationManager.getInstance().getConsoleLogLevelFromEnvVar()
            if consoleLogLevel:
                LoggingManager.getInstance().setConsoleLogLevel(consoleLogLevel)

        # Service host, port, etc.
        configManager = ConfigurationManager.getInstance()
        self.serviceHost = self.options.serviceHost
        configManager.setServiceHost(self.serviceHost)
        self.servicePort = self.options.servicePort
        configManager.setServicePort(self.servicePort)
        self.serviceProtocol = self.options.serviceProtocol
        configManager.setServiceProtocol(self.serviceProtocol)

        # SSL options, comment out for now
        #self.sslCaCertFile = self.options.sslCaCertFile
        #if self.sslCaCertFile:
        #    configManager.setSslCaCertFile(self.sslCaCertFile)
        #self.sslCertFile = self.options.sslCertFile
        #if self.sslCertFile:
        #    configManager.setSslCertFile(self.sslCertFile)
        #self.sslKeyFile = self.options.sslKeyFile
        #if self.sslKeyFile:
        #    configManager.setSslKeyFile(self._sslKeyFile)

        # Check session cache.
        try:
            self.checkSessionCache()
        except Exception, ex:
            self.logger.warn('Disabling session cache: %s' % ex)
            configManager.setSessionCacheFile(None)

        return (self.options, self.args)

    def checkSessionCache(self):
        configManager = ConfigurationManager.getInstance()
        sessionCacheFile = configManager.getSessionCacheFile()
        if sessionCacheFile is None:
            sessionCacheFile = DmCli.DEFAULT_SESSION_CACHE_FILE 
        sessionCacheFile = sessionCacheFile.strip()
        if len(sessionCacheFile):
            sessionCacheDir = os.path.dirname(sessionCacheFile)
            OsUtility.createDir(sessionCacheDir, stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR)
            configManager.setSessionCacheFile(sessionCacheFile)
            configManager.setRequireSessionCredentials(True)

    def usage(self, s=None):
        """ Print help provided by optparse. """
        if s:
            print >>sys.stderr, 'Error: ', s, '\n'
        self.parser.print_help()
        sys.exit(dmStatus.DM_ERROR)

    def getOptions(self):
        return self.options

    def getNArgs(self):
        """ Returns the number of command line arguments. """
        return len(self.args)

    def getArgs(self):
        """ Returns the command line argument list. """
        return self.args

    def getArg(self, i):
        """ Returns the i-th command line argument. """
        return self.args[i]

    def getServiceHost(self):
        #return self.serviceHost
        return ConfigurationManager.getInstance().getServiceHost()

    def getServicePort(self):
        #return self.servicePort
        return ConfigurationManager.getInstance().getServicePort()

    def getServiceProtocol(self):
        #return self.serviceProtocol
        return ConfigurationManager.getInstance().getServiceProtocol()

    def getSslCaCertFile(self):
        return self.sslCaCertFile

    def getSslCertFile(self):
        return self.sslCertFile

    def getSslKeyFile(self):
        return self.sslKeyFile

    def displayDmObject(self, dmObject):
        if isinstance(dmObject, dmObject):
            return '%s' % dmObject.getJsonRep()
        else:
            return '%s' % dmObject

    def runCommand(self):
        """ This method must be implemented by the derived class. """
        raise InternalError('Method runCommand() must be overriden in the derived class.')

    def run(self):
        """ This method invokes runCommand() and handles any exceptions. """
        try:
            self.runCommand()
        except DmException, ex:
            self.logger.exception('%s' % ex)
            print '%s' % ex.getErrorMessage()
            raise SystemExit(ex.getErrorCode())
        except SystemExit, ex:
            raise
        except Exception, ex:
            self.logger.exception('%s' % ex)
            print >>sys.stderr, '%s' % ex
            raise SystemExit(dmStatus.DM_ERROR)

    def getId(self):
        id = self.options.id
        if id == None:
            raise InvalidRequest('Missing id.')
        return id

    def displayDmObject(self, dmObject):
        optDict = self._options.__dict__
        if isinstance(dmObject, DmObject):
            if optDict.get('dict'):
                 return '%s' % dmObject
            else:
                 return dmObject.display()
        else:
            return '%s' % dmObject

#######################################################################
# Testing

if __name__ == '__main__':
    cli = DmCli(3)
    cli.addOption("-f", "--file", dest="filename", help="write report to FILE", metavar="FILE")
    cli.addOption("-q", "--quiet", action="store_false", dest="verbose", default=True, help="don't print log messages to stdout")
    (options, args) = cli.parseArgs()
    print 'After parse:'
    print 'OPTIONS: ', options
    print 'ARGS: ', args
    print 'From CLI'
    print 'OPTIONS: ', cli.getOptions()
    print 'ARGS: ', cli.getArgs()
    print
    print 'FILENAME'
    print 'options.filename', options.filename
    print 'cli.getOptions().filename', cli.getOptions().filename
    o = cli.getOptions()
    print 'o.filename', o.filename
    print 'cli.getArgs()', cli.getArgs()
    print 'len(cli.getArgs())', len(cli.getArgs())

    for a in cli.getArgs():
        print 'arg', a

    first_arg = cli.getArg(0)
    print 'first_arg', first_arg

    second_arg = cli.getArg(1)
    print 'second_arg', second_arg

    try:
        third_arg = cli.getArg(2)
        print 'third_arg', third_arg
    except:
        print 'no third arg'