--- 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