src/modules/misc.py
changeset 2339 aa5954c06b9d
parent 2329 2c62c77e511b
child 2407 a831f1491c86
--- a/src/modules/misc.py	Fri May 06 17:24:48 2011 -0700
+++ b/src/modules/misc.py	Sat May 07 00:25:10 2011 -0700
@@ -22,13 +22,14 @@
 
 # Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
 
+import OpenSSL.crypto as osc
+import cStringIO
 import calendar
-import cStringIO
 import datetime
 import errno
+import getopt
 import hashlib
 import locale
-import OpenSSL.crypto as osc
 import operator
 import os
 import pkg.client.api_errors as api_errors
@@ -44,9 +45,11 @@
 import urlparse
 import zlib
 
-from pkg.pkggzip import PkgGzipFile
+from pkg import VERSION
+from pkg.client import global_settings
+from pkg.client.debugvalues import DebugValues
 from pkg.client.imagetypes import img_type_names, IMG_NONE
-from pkg import VERSION
+from pkg.pkggzip import PkgGzipFile
 
 # Minimum number of days to issue warning before a certificate expires
 MIN_WARN_DAYS = datetime.timedelta(days=30)
@@ -296,7 +299,7 @@
         """Return its argument; used to mark strings for localization when
         their use is delayed by the program."""
         return message
- 
+
 def bytes_to_str(bytes, format=None):
         """Returns a human-formatted string representing the number of bytes
         in the largest unit possible.  If provided, 'format' should be a string
@@ -601,7 +604,7 @@
                                 raise AttributeError, "can't iterate"
                         return self.__iter(self.__obj)
 
-        def __init__(self, fget=None, fset=None, fdel=None, iteritems=None, 
+        def __init__(self, fget=None, fset=None, fdel=None, iteritems=None,
             keys=None, values=None, iterator=None, doc=None, fgetdefault=None,
             fsetdefault=None, update=None, pop=None):
                 self.__fget = fget
@@ -620,12 +623,12 @@
         def __get__(self, obj, objtype=None):
                 if obj is None:
                         return self
-                return self.__InternalProxy(obj, self.__fget, self.__fset, 
+                return self.__InternalProxy(obj, self.__fget, self.__fset,
                     self.__fdel, self.__iteritems, self.__keys, self.__values,
                     self.__iter, self.__fgetdefault, self.__fsetdefault,
                     self.__update, self.__pop)
 
-        
+
 def build_cert(path, uri=None, pub=None):
         """Take the file given in path, open it, and use it to create
         an X509 certificate object.
@@ -826,14 +829,15 @@
         def __init__(self, name, bases, dictionary):
                 super(Singleton, self).__init__(name, bases, dictionary)
                 self.instance = None
- 
+
         def __call__(self, *args, **kw):
                 if self.instance is None:
                         self.instance = super(Singleton, self).__call__(*args,
                             **kw)
- 
+
                 return self.instance
 
+
 EmptyDict = ImmutableDict()
 
 # Setting the python file buffer size to 128k gives substantial performance
@@ -862,3 +866,200 @@
                 for name in filenames:
                         path = os.path.join(dirpath, name)
                         portable.chown(path, uid, gid)
+def opts_parse(op, api_inst, args, table, pargs_limit, usage_cb):
+        """Generic table-based options parsing function.  Returns a tuple
+        consisting of a dictionary of parsed options and the remaining
+        unparsed options.
+
+        'op' is the operation being performed.
+
+        'api_inst' is an image api object that is passed to options handling
+        callbacks (passed in via 'table').
+
+        'args' is the arguments that should be parsed.
+
+        'table' is a list of options and callbacks.Each entry is either a
+        a tuple or a callback function.
+
+        tuples in 'table' specify allowable options and have the following
+        format:
+
+                (<short opt>, <long opt>, <key>, <default value>)
+
+        An example of a short opt is "f", which maps to a "-f" option.  An
+        example of a long opt is "foo", which maps to a "--foo" option.  Key
+        is the value of this option in the parsed option dictionary.  The
+        default value not only represents the default value assigned to the
+        option, but it also implicitly determines how the option is parsed.  If
+        the default value is True or False, the option doesn't take any
+        arguments, can only be specified once, and if specified it inverts the
+        default value.  If the default value is 0, the option doesn't take any
+        arguments, can be specified multiple times, and if specified its value
+        will be the number of times it was seen.  If the default value is
+        None, the option requires an argument, can only be specified once, and
+        if specified its value will be its argument string.  If the default
+        value is an empty list, the option requires an argument, may be
+        specified multiple times, and if specified its value will be a list
+        with all the specified argument values.
+
+        callbacks in 'table' specify callback functions that are invoked after
+        all options have been parsed.  Callback functions must have the
+        following signature:
+                callback(api_inst, opts, opts_new)
+
+        The opts parameter is a dictionary containing all the raw, parsed
+        options.  Callbacks should never update the contents of this
+        dictionary.  The opts_new parameter is a dictionary which is initially
+        a copy of the opts dictionary.  This is the dictionary that will be
+        returned to the caller of opts_parse().  If a callback function wants
+        to update the arguments dictionary that will be returned to the
+        caller, they should make all their updates to the opts_new dictionary.
+
+        'pargs_limit' specified how to handle extra arguments not parsed by
+        getops.  A value of -1 indicates that we allow an unlimited number of
+        extra arguments.  A value of 0 or greater indicates the number of
+        allowed additional unparsed options.
+
+        'usage_cb' is a function pointer that should display usage information
+        and will be invoked if invalid arguments are detected."""
+
+
+        assert type(table) == list
+
+        # return dictionary
+        rv = dict()
+
+        # option string passed to getopt
+        opts_s_str = ""
+        # long options list passed to getopt
+        opts_l_list = list()
+
+        # dict to map options returned by getopt to keys
+        opts_keys = dict()
+
+        # sanity checking to make sure each option is unique
+        opts_s_set = set()
+        opts_l_set = set()
+        opts_seen = dict()
+
+        # callbacks to invoke after processing options
+        callbacks = []
+
+        # process each option entry
+        for entry in table:
+                # check for a callback
+                if type(entry) != tuple:
+                        callbacks.append(entry)
+                        continue
+
+                # decode the table entry
+                # s: a short option, ex: -f
+                # l: a long option, ex: --foo
+                # k: the key value for the options dictionary
+                # v: the default value
+                (s, l, k, v) = entry
+
+                # make sure an option was specified
+                assert s or l
+                # sanity check the default value
+                assert (v == None) or (v == []) or \
+                    (type(v) == bool) or (type(v) == int)
+                # make sure each key is unique
+                assert k not in rv
+                # initialize the default return dictionary entry.
+                rv[k] = v
+                if l:
+                        # make sure each option is unique
+                        assert set([l]) not in opts_l_set
+                        opts_l_set |= set([l])
+
+                        if type(v) == bool:
+                                v = not v
+                                opts_l_list.append("%s" % l)
+                        elif type(v) == int:
+                                opts_l_list.append("%s" % l)
+                        else:
+                                opts_l_list.append("%s=" % l)
+                        opts_keys["--%s" % l] = k
+                if s:
+                        # make sure each option is unique
+                        assert set([s]) not in opts_s_set
+                        opts_s_set |= set([s])
+
+                        if type(v) == bool:
+                                v = not v
+                                opts_s_str += "%s" % s
+                        elif type(v) == int:
+                                opts_s_str += "%s" % s
+                        else:
+                                opts_s_str += "%s:" % s
+                        opts_keys["-%s" % s] = k
+
+        # parse options
+        try:
+                opts, pargs = getopt.getopt(args, opts_s_str, opts_l_list)
+        except getopt.GetoptError, e:
+                usage_cb(_("illegal option -- %s") % e.opt, cmd=op)
+
+        if (pargs_limit >= 0) and (pargs_limit < len(pargs)):
+                usage_cb(_("illegal argument -- %s") % pargs[pargs_limit],
+                    cmd=op)
+
+        # update options dictionary with the specified options
+        for opt, arg in opts:
+                k = opts_keys[opt]
+                v = rv[k]
+
+                # check for duplicate options
+                if k in opts_seen and (type(v) != list and type(v) != int):
+                        if opt == opts_seen[k]:
+                                opts_err_repeated(opt, op)
+                        usage_cb(_("'%s' and '%s' have the same meaning") %
+                            (opts_seen[k], opt), cmd=op)
+                opts_seen[k] = opt
+
+                # update the return dict value
+                if type(v) == bool:
+                        rv[k] = not rv[k]
+                elif type(v) == list:
+                        rv[k].append(arg)
+                elif type(v) == int:
+                        rv[k] += 1
+                else:
+                        rv[k] = arg
+
+        # invoke callbacks (cast to set() to eliminate dups)
+        rv_updated = rv.copy()
+        for cb in set(callbacks):
+                cb(op, api_inst, rv, rv_updated)
+
+        return (rv_updated, pargs)
+
+def api_cmdpath():
+        """Returns the path to the executable that is invoking the api client
+        interfaces."""
+
+        cmdpath = None
+
+        if global_settings.client_args[0]:
+                cmdpath = os.path.realpath(os.path.join(sys.path[0],
+                    os.path.basename(global_settings.client_args[0])))
+
+        if "PKG_CMDPATH" in os.environ:
+                cmdpath = os.environ["PKG_CMDPATH"]
+
+        if DebugValues.get_value("simulate_cmdpath"):
+                cmdpath = DebugValues.get_value("simulate_cmdpath")
+
+        return cmdpath
+
+def liveroot():
+        """Return path to the current live root image, i.e. the image
+        that we are running from."""
+
+        live_root = DebugValues.get_value("simulate_live_root")
+        if not live_root and "PKG_LIVE_ROOT" in os.environ:
+                live_root = os.environ["PKG_LIVE_ROOT"]
+        if not live_root:
+                live_root = "/"
+        return live_root