2297 top level error handler for out of memory conditions needed
authorDanek Duvall <danek.duvall@sun.com>
Fri, 10 Apr 2009 08:38:03 -0700
changeset 1035 0e243b7eb121
parent 1034 482b78e90636
child 1036 e79c66ac6bc6
2297 top level error handler for out of memory conditions needed
src/client.py
src/modules/client/history.py
src/modules/misc.py
--- a/src/client.py	Thu Apr 09 23:22:23 2009 -0700
+++ b/src/client.py	Fri Apr 10 08:38:03 2009 -0700
@@ -74,7 +74,8 @@
 from pkg.client import global_settings
 from pkg.client.debugvalues import DebugValues
 from pkg.client.history import (RESULT_CANCELED, RESULT_FAILED_BAD_REQUEST,
-    RESULT_FAILED_CONFIGURATION, RESULT_FAILED_TRANSPORT, RESULT_FAILED_UNKNOWN)
+    RESULT_FAILED_CONFIGURATION, RESULT_FAILED_TRANSPORT, RESULT_FAILED_UNKNOWN,
+    RESULT_FAILED_OUTOFMEMORY)
 from pkg.client.filelist import FileListRetrievalError
 from pkg.client.retrieve import (CatalogRetrievalError,
     DatastreamRetrievalError, ManifestRetrievalError)
@@ -2513,7 +2514,20 @@
 #
 if __name__ == "__main__":
         try:
-                __ret = main_func()
+                # Out of memory errors can be raised as EnvironmentErrors with
+                # an errno of ENOMEM, so in order to handle those exceptions
+                # with other errnos, we nest this try block and have the outer
+                # one handle the other instances.
+                try:
+                        __ret = main_func()
+                except (MemoryError, EnvironmentError), __e:
+                        if isinstance(__e, EnvironmentError) and \
+                            __e.errno != errno.ENOMEM:
+                                        raise
+                        if __img:
+                                __img.history.abort(RESULT_FAILED_OUTOFMEMORY)
+                        error("\n" + misc.out_of_memory())
+                        __ret = 1
         except SystemExit, __e:
                 if __img:
                         __img.history.abort(RESULT_FAILED_UNKNOWN)
--- a/src/modules/client/history.py	Thu Apr 09 23:22:23 2009 -0700
+++ b/src/modules/client/history.py	Fri Apr 10 08:38:03 2009 -0700
@@ -65,6 +65,8 @@
 RESULT_FAILED_TRANSPORT = ["Failed", "Transport"]
 # Indicates that the operation failed due to an actuator problem
 RESULT_FAILED_ACTUATOR = ["Failed", "Actuator"]
+# Indicates that the operation failed due to not enough memory
+RESULT_FAILED_OUTOFMEMORY = ["Failed", "Out of Memory"]
 # Indicates that the operation failed for an unknown reason.
 RESULT_FAILED_UNKNOWN = ["Failed", "Unknown"]
 
@@ -88,6 +90,7 @@
     api_errors.IpkgOutOfDateException: RESULT_FAILED_CONSTRAINED,
     fmri.IllegalFmri: RESULT_FAILED_BAD_REQUEST,
     KeyboardInterrupt: RESULT_CANCELED,
+    MemoryError: RESULT_FAILED_OUTOFMEMORY,
 }
 
 class _HistoryException(Exception):
--- a/src/modules/misc.py	Thu Apr 09 23:22:23 2009 -0700
+++ b/src/modules/misc.py	Fri Apr 10 08:38:03 2009 -0700
@@ -40,6 +40,7 @@
 import sha
 import shutil
 import socket
+import struct
 import sys
 import time
 import urllib
@@ -431,9 +432,11 @@
 
                 return False, text
 
-def bytes_to_str(bytes):
+def bytes_to_str(bytes, format=None):
         """Returns a human-formatted string representing the number of bytes
-        in the largest unit possible."""
+        in the largest unit possible.  If provided, 'format' should be a string
+        which can be formatted with a dictionary containing a float 'num' and
+        string 'unit'."""
 
         units = [
             (_("B"), 2**10),
@@ -452,8 +455,12 @@
                         # unit of measure's range.
                         continue
                 else:
-                        return "%.2f %s" % (round(bytes / float(
-                            limit / 2**10), 2), uom)
+                        if not format:
+                                format = "%(num).2f %(unit)s"
+                        return format % {
+                            "num": round(bytes / float(limit / 2**10), 2),
+                            "unit": uom
+                        }
 
 def get_rel_path(request, uri):
         # Calculate the depth of the current request path relative to our base
@@ -558,6 +565,46 @@
 
         return fhash.hexdigest(), content.read()
 
+def __getvmusage():
+        """Return the amount of virtual memory in bytes currently in use."""
+
+        # This works only on Solaris, in 32-bit mode.  It may not work on older
+        # or newer versions than 5.11.  Ideally, we would use libproc, or check
+        # sbrk(0), but this is expedient.  In most cases (there's a small chance
+        # the file will decode, but incorrectly), failure will raise an
+        # exception, and we'll fail safe.
+        try:
+                # Read just the psinfo_t, not the tacked-on lwpsinfo_t
+                psinfo_arr = file("/proc/self/psinfo").read(232)
+                psinfo = struct.unpack("6i5I4LHH6L16s80siiIIc3x7i", psinfo_arr)
+                vsz = psinfo[11] * 1024
+        except Exception:
+                vsz = None
+
+        return vsz
+
+def out_of_memory():
+        """Return an out of memory message, for use in a MemoryError handler."""
+
+        vsz = bytes_to_str(__getvmusage(), format="%(num).0f%(unit)s")
+
+        if vsz is not None:
+                error = """\
+There is not enough memory to complete the requested operation.  At least
+%(vsz)s of virtual memory was in use by this command before it ran out of memory.
+You must add more memory (swap or physical) or allow the system to access more
+existing memory, or quit other programs that may be consuming memory, and try
+the operation again."""
+        else:
+                error = """\
+There is not enough memory to complete the requested operation.  You must
+add more memory (swap or physical) or allow the system to access more existing
+memory, or quit other programs that may be consuming memory, and try the
+operation again."""
+
+        return _(error) % locals()
+
+
 class CfgCacheError(Exception):
         """Thrown when there are errors with the cfg cache."""
         def __init__(self, args=None):