# HG changeset patch # User Ginnie Wray # Date 1340129917 21600 # Node ID 10cb4d15a248466014cd39bd0575162509fc6528 # Parent 0cea9255024f8ecf5d974a92eb1243cb0f1df91b 7066254 Problem with install/logging 7170155 DC leaves log files in /var/tmp/install diff -r 0cea9255024f -r 10cb4d15a248 usr/src/cmd/auto-install/auto_install.py --- a/usr/src/cmd/auto-install/auto_install.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/cmd/auto-install/auto_install.py Tue Jun 19 12:18:37 2012 -0600 @@ -78,7 +78,7 @@ from solaris_install.ict.apply_sysconfig import APPLY_SYSCONFIG_DICT, \ APPLY_SYSCONFIG_PROFILE_KEY from solaris_install.ict.transfer_files import add_transfer_files_to_doc -from solaris_install.logger import FileHandler, ProgressHandler, MAX_INT +from solaris_install.logger import ProgressHandler, MAX_INT from solaris_install.logger import INSTALL_LOGGER_NAME from solaris_install.manifest.parser import ManifestError, \ MANIFEST_PARSER_DATA @@ -123,30 +123,32 @@ Class constructor """ self.installed_root_dir = self.INSTALLED_ROOT_DIR - self.install_log = None self.auto_reboot = False self.doc = None self.exitval = self.AI_EXIT_SUCCESS self.derived_script = None self.manifest = None + # Logger Variables + self.install_log = None + self.logger = None + self.progress_ph = None + # To remember the BE when we find it. self._be = None # Parse command line arguments self.options, self.args = self.parse_args(args) - # Initialize Install Engine - self.engine = InstallEngine(stop_on_error=True) - self.doc = self.engine.data_object_cache - if self.options.zonename is not None: # If we're installing a zone root, generate a work_dir # location based on the current PID. work_dir = "/system/volatile/install." + str(os.getpid()) # Add ApplicationData to the DOC - self._app_data = ApplicationData("auto-install", work_dir=work_dir) + self._app_data = ApplicationData("auto-install", work_dir=work_dir, + logname=self.INSTALL_LOG) + self._app_data.data_dict[ALT_POOL_DATASET] = \ self.options.alt_zpool_dataset @@ -154,7 +156,24 @@ self.installed_root_dir = work_dir + self.INSTALLED_ROOT_DIR else: # Add ApplicationData to the DOC - self._app_data = ApplicationData("auto-install") + self._app_data = ApplicationData("auto-install", + logname=self.INSTALL_LOG) + + # Get the logname handle + self.install_log = self._app_data.logname + + # Initialize the Install Engine + self.engine = InstallEngine(self.install_log, + loglevel=logging.DEBUG, stop_on_error=True) + self.doc = self.engine.data_object_cache + + # Establish the logger instance for AI + self.logger = logging.getLogger(INSTALL_LOGGER_NAME) + + if not self.options.list_checkpoints: + self.logger.info("Install Log: %s" % (self.install_log), + extra={self.AI_INFO_PREFIX: "Install Log", + self.AI_INFO_MSG: self.install_log}) # Add profile location to the ApplySysconfig checkpoint's data dict. if self.options.profile is not None: @@ -184,10 +203,7 @@ # Clear error service errsvc.clear_error_list() - # Create Logger and setup logfiles - self.install_log_fh = None - self.logger = None - self.progress_ph = None + # Setup additional AI logging self.setup_logs() if not self.options.list_checkpoints: @@ -384,13 +400,9 @@ def setup_logs(self): """ - Create the logger instance for AI and create simple and - detailed log files to use. + Create the additional loggers for AI. """ - # Create logger for AI - self.logger = logging.getLogger(INSTALL_LOGGER_NAME) - # Log progress and info messages to the console. self.progress_ph = AIProgressHandler(self.logger, skip_console_msg=(self.options.list_checkpoints or \ @@ -409,20 +421,6 @@ prefix_key=self.AI_INFO_PREFIX, msg_key=self.AI_INFO_MSG) self.progress_ph.setFormatter(formatter) - # create a install_log file handler and add it to the ai_logger - - # set the logfile names - self.install_log = os.path.join(self._app_data.work_dir, - self.INSTALL_LOG) - self.install_log_fh = FileHandler(self.install_log) - - self.install_log_fh.setLevel(logging.DEBUG) - if not self.options.list_checkpoints: - self.logger.info("Install Log: %s" % (self.install_log), - extra={self.AI_INFO_PREFIX: "Install Log", - self.AI_INFO_MSG: self.install_log}) - self.logger.addHandler(self.install_log_fh) - @property def be(self): if self._be is not None: @@ -671,7 +669,7 @@ # Now do actual transfer of logs self.logger.debug("Transferring log to %s" % (new_be.mountpoint + self.BE_LOG_DIR)) - self.install_log_fh.transfer_log( + self.logger.default_fh.transfer_log( new_be.mountpoint + self.BE_LOG_DIR, isdir=True) # And cleanup diff -r 0cea9255024f -r 10cb4d15a248 usr/src/cmd/distro_const/__init__.py --- a/usr/src/cmd/distro_const/__init__.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/cmd/distro_const/__init__.py Tue Jun 19 12:18:37 2012 -0600 @@ -28,7 +28,6 @@ __all__ = ["cli", "distro_const", "execution_checkpoint", "distro_spec"] - import logging import optparse import os @@ -47,6 +46,7 @@ from osol_install.liberrsvc import ES_DATA_EXCEPTION from solaris_install import CalledProcessError, run, DC_LABEL from solaris_install.boot.boot_spec import BootMods +from solaris_install import system_temp_path from solaris_install.data_object import DataObject, ObjectNotFoundError from solaris_install.data_object.cache import DataObjectCache from solaris_install.data_object.data_dict import DataObjectDict @@ -55,7 +55,7 @@ from solaris_install.engine import FileNotFoundError, InstallEngine, \ NoDatasetError, RollbackError, UsageError, UnknownChkptError from solaris_install.engine import INSTALL_LOGGER_NAME -from solaris_install.logger import DEFAULTLOG, FileHandler, InstallFormatter +from solaris_install.logger import FileHandler, InstallFormatter from solaris_install.manifest.parser import ManifestError from solaris_install.target import Target from solaris_install.target.logical import Filesystem, Zpool @@ -64,6 +64,8 @@ DC_LOCKFILE = "distro_const.lock" DC_LOGGER = None +LOG_TIMESTAMP = time.strftime("%Y-%m-%d.%H:%M") +DEFAULTLOG = system_temp_path("dc/default_log" + '.' + LOG_TIMESTAMP) class Lockfile(object): @@ -82,7 +84,7 @@ if os.path.exists(self.filename): raise RuntimeError("distro_const: An instance of distro_const " - "is already running in %s" % + "is already running in %s" % os.path.split(self.filename)[0]) else: # touch the lockfile @@ -442,21 +444,22 @@ try: # We initialize the Engine with stop_on_error set so that if there are # errors during manifest parsing, the processing stops - eng = InstallEngine(debug=False, stop_on_error=True) + eng = InstallEngine(DEFAULTLOG, debug=False, exclusive_rw=True, + stop_on_error=True) doc = eng.data_object_cache global DC_LOGGER DC_LOGGER = logging.getLogger(INSTALL_LOGGER_NAME) # set the logfile name - log_name = "log.%s" % time.strftime("%Y-%m-%d.%H:%M") + log_name = "log.%s" % LOG_TIMESTAMP detail_log_name = "detail-%s" % log_name simple_log_name = "simple-%s" % log_name # create an additional FileHandler for a simple log base, logfile = os.path.split(DEFAULTLOG) simple_logname = os.path.join(base, "simple-" + logfile) - simple_fh = FileHandler(simple_logname) + simple_fh = FileHandler(simple_logname, exclusive_rw=True) simple_fh.setLevel(logging.INFO) DC_LOGGER.addHandler(simple_fh) @@ -498,6 +501,9 @@ DC_LOGGER.transfer_log(destination=new_detaillog) simple_fh.transfer_log(destination=new_simplelog) + # Remove the original DEFAULTLOG. It's no longer needed + shutil.rmtree(base) + # set the http_proxy if one is specified in the manifest dc_set_http_proxy(DC_LOGGER) diff -r 0cea9255024f -r 10cb4d15a248 usr/src/cmd/gui-install/src/__init__.py --- a/usr/src/cmd/gui-install/src/__init__.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/cmd/gui-install/src/__init__.py Tue Jun 19 12:18:37 2012 -0600 @@ -43,14 +43,15 @@ import gtk import osol_install.errsvc as errsvc -from solaris_install import CalledProcessError, Popen, \ - post_install_logs_path, run +from solaris_install import ApplicationData, CalledProcessError, Popen, \ + post_install_logs_path, run, check_log_level from solaris_install.engine import InstallEngine from solaris_install.gui_install.gui_install_common import exit_gui_install, \ modal_dialog, other_instance_is_running, start_td_local, write_pid_file, \ - CLEANUP_CPIO_INSTALL, DEFAULT_LOG_LEVEL, DEFAULT_LOG_LOCATION, GLADE_DIR, \ - LOG_FORMAT, LOG_LEVEL_INPUT, LOG_NAME_INPUT, LOGNAME, RELEASE, \ - TARGET_DISCOVERY, TRANSFER_PREP, VARSHARE_DATASET + CLEANUP_CPIO_INSTALL, DEBUG_LOG_LEVEL, DEFAULT_LOG_LEVEL, \ + DEFAULT_LOG_LOCATION, GLADE_DIR, LOG_FORMAT, LOG_LEVEL_INPUT, \ + LOG_NAME_INPUT, LOGNAME, RELEASE, TARGET_DISCOVERY, TRANSFER_PREP, \ + VARSHARE_DATASET from solaris_install.gui_install.install_profile import InstallProfile from solaris_install.gui_install.screen_manager import ScreenManager from solaris_install.ict.transfer_files import add_transfer_files_to_doc @@ -229,25 +230,6 @@ profile.set_locale_data([locale_description], [the_locale], the_locale) -def setup_logging(logname, log_level): - '''Initialize the logger, logging to logname at log_level''' - logger = logging.getLogger(INSTALL_LOGGER_NAME) - - log_level = log_level.upper() - if hasattr(logging, log_level): - log_level = getattr(logging, log_level.upper()) - elif log_level == LOG_NAME_INPUT: - log_level = LOG_LEVEL_INPUT - else: - raise IOError(2, "Invalid --log-level parameter", log_level.lower()) - - logger.setLevel(log_level) - logger.transfer_log(destination=logname) - - logger.info("**** START ****") - return logger - - def _init_locale(): '''Initialize the locale for gui-install''' locale.setlocale(locale.LC_ALL, "") @@ -296,25 +278,37 @@ "logging level to 'input' and enables CTRL-C for " "killing the program\n")) options, args = parser.parse_args() + + # Initialize the Engine and set up logging + work_dir = os.path.dirname(options.logname) + logname = os.path.basename(options.logname) + app_data = ApplicationData("gui-install", work_dir=work_dir, + logname=logname) + if options.log_level is None: if options.debug: - options.log_level = "debug" + options.log_level = DEBUG_LOG_LEVEL else: options.log_level = DEFAULT_LOG_LEVEL + InstallEngine(app_data.logname, loglevel=options.log_level, debug=True) + elif check_log_level(options.log_level): + InstallEngine(app_data.logname, loglevel=options.log_level, + debug=options.debug) + else: + raise IOError(2, "Invalid --log-level parameter", options.log_level) - engine = InstallEngine(loglevel=options.log_level, - debug=True) - try: - logger = setup_logging(options.logname, options.log_level) - except IOError, err: - parser.error("%s '%s'" % (err.strerror, err.filename)) + doc = InstallEngine.get_instance().doc + doc.persistent.insert_children(app_data) + + logger = logging.getLogger(INSTALL_LOGGER_NAME) + logger.info("**** START ****") logger.debug("CLI options: log location = %s, verbosity = %s, debug " - "mode = %s", - options.logname, options.log_level, options.debug) + "mode = %s", app_data.logname, + logging.getLevelName(options.log_level).lower(), options.debug) setup_checkpoints() - manager = ScreenManager(options.logname) + manager = ScreenManager(app_data.logname) start_td_local() @@ -322,7 +316,7 @@ save_locale_in_doc() manager.main() - exit_gui_install(logname=options.logname, errcode=0) + exit_gui_install(logname=app_data.logname, errcode=0) if __name__ == '__main__': main() diff -r 0cea9255024f -r 10cb4d15a248 usr/src/cmd/gui-install/src/gui_install_common.py --- a/usr/src/cmd/gui-install/src/gui_install_common.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/cmd/gui-install/src/gui_install_common.py Tue Jun 19 12:18:37 2012 -0600 @@ -76,10 +76,10 @@ LOGNAME = None # default logging level -DEFAULT_LOG_LEVEL = "info" +DEFAULT_LOG_LEVEL = logging.INFO # debug logging level -DEBUG_LOG_LEVEL = "debug" +DEBUG_LOG_LEVEL = logging.DEBUG # default log format LOG_FORMAT = ("%(asctime)s - %(levelname)-8s: %(message)s") diff -r 0cea9255024f -r 10cb4d15a248 usr/src/cmd/system-config/__init__.py --- a/usr/src/cmd/system-config/__init__.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/cmd/system-config/__init__.py Tue Jun 19 12:18:37 2012 -0600 @@ -25,7 +25,6 @@ '''System Configuration Interactive (SCI) Tool''' - import gettext import atexit import curses @@ -175,7 +174,7 @@ DEFAULT_SC_LOCATION = os.path.join(VOLATILE_PATH, "profile", DEFAULT_SC_PROFILE) -DEFAULT_LOG_LOCATION = "/var/tmp/install/sysconfig.log" +DEFAULT_LOG_LOC = os.path.join(VOLATILE_PATH, "sysconfig/sysconfig.log") DEFAULT_LOG_LEVEL = "info" LOG_FORMAT = ("%(asctime)s - %(levelname)-8s: " "%(filename)s:%(lineno)d %(message)s") @@ -983,7 +982,7 @@ parser.add_option("-l", "--log-location", dest="logname", help=_("Set log location to FILE " "(default: %default)"), - metavar="FILE", default=DEFAULT_LOG_LOCATION) + metavar="FILE", default=DEFAULT_LOG_LOC) parser.add_option("-v", "--log-level", dest="log_level", default=DEFAULT_LOG_LEVEL, help=_("Set log verbosity to LEVEL. In order of " @@ -1075,8 +1074,8 @@ def _prepare_engine(options): '''Initialize the InstallEngine''' - InstallEngine(default_log=options.logname, loglevel=options.log_level, - debug=options.debug) + InstallEngine(options.logname, loglevel=options.log_level, + debug=options.debug, exclusive_rw=options.exclusive_rw) logger = logging.getLogger(INSTALL_LOGGER_NAME) @@ -1132,6 +1131,8 @@ if sub_cmd[0] == CONFIGURE or sub_cmd[0] == UNCONFIGURE: do_unconfigure(sub_cmd[0], options) elif sub_cmd[0] == CREATE_PROFILE: + # Set the exclusive read write flag to true + options.exclusive_rw = True do_create_profile(options) sys.exit(SU_OK) diff -r 0cea9255024f -r 10cb4d15a248 usr/src/cmd/system-config/test/test_sysconfig.py --- a/usr/src/cmd/system-config/test/test_sysconfig.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/cmd/system-config/test/test_sysconfig.py Tue Jun 19 12:18:37 2012 -0600 @@ -19,7 +19,7 @@ # # CDDL HEADER END # -# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2012 Oracle and/or its affiliates. All rights reserved. # ''' @@ -42,22 +42,22 @@ '''parse_options() returns proper subcommmand''' (options, sub_cmd) = sysconfig._parse_options(["create-profile"]) self.assertEqual(sub_cmd[0], "create-profile") - + def test_parse_options_no_flags(self): '''parse_options() returns proper default options''' (options, sub_cmd) = sysconfig._parse_options(["create-profile"]) if sysconfig._in_rozr_zone(): self.assertEqual(options.logname, os.path.join("/system/volatile", - os.path.basename(sysconfig.DEFAULT_LOG_LOCATION))) + os.path.basename(sysconfig.DEFAULT_LOG_LOC))) else: - self.assertEqual(options.logname, sysconfig.DEFAULT_LOG_LOCATION) + self.assertEqual(options.logname, sysconfig.DEFAULT_LOG_LOC) self.assertEqual(options.log_level, getattr(logging, sysconfig.DEFAULT_LOG_LEVEL.upper())) self.assertFalse(options.force_bw) self.assertFalse(options.debug) - + def test_parse_options_accepts_flags(self): '''parse_options() accepts "create-profile -l -b -o "''' (options, sub_cmd) = sysconfig._parse_options(["create-profile", "-l", @@ -70,12 +70,12 @@ self.assertEqual(options.profile, "/foo/sc.xml") self.assertTrue(options.force_bw) - + def test_parse_options_log_level_valid(self): '''parse_options() properly reformats error, warn, info, debug and input''' levels = ["error", "warn", "info", "debug"] - + for level in levels: (options, sub_cmd) = sysconfig._parse_options(["create-profile", "-v", level]) @@ -85,12 +85,12 @@ self.assertTrue(options.debug) else: self.assertFalse(options.debug) - + (options, sub_cmd) = sysconfig._parse_options(["create-profile", "-v", "input"]) self.assertEqual(options.log_level, sysconfig.LOG_LEVEL_INPUT) self.assertTrue(options.debug) - + def test_parse_options_invalid_log_level(self): '''parse_options() rejects unsupported log levels''' self.assertRaises(SystemExit, sysconfig._parse_options, diff -r 0cea9255024f -r 10cb4d15a248 usr/src/cmd/text-install/__init__.py --- a/usr/src/cmd/text-install/__init__.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/cmd/text-install/__init__.py Tue Jun 19 12:18:37 2012 -0600 @@ -29,7 +29,8 @@ import gettext import os -from solaris_install import gpt_firmware_check +from solaris_install import gpt_firmware_check, ApplicationData, \ + check_log_level from solaris_install.getconsole import get_console, SERIAL_CONSOLE # @@ -129,8 +130,8 @@ LOG_LOCATION_FINAL = post_install_logs_path('install_log') DEFAULT_LOG_LOCATION = "/system/volatile/install_log" -DEFAULT_LOG_LEVEL = "info" -DEBUG_LOG_LEVEL = "debug" +DEFAULT_LOG_LEVEL = logging.INFO +DEBUG_LOG_LEVEL = logging.DEBUG REBOOT = "/usr/sbin/reboot" LOGGER = None @@ -165,26 +166,6 @@ sys.exit(errcode) -def setup_logging(logname, log_level): - '''setup the logger, logging to logname at log_level''' - - global LOGGER - LOGGER = logging.getLogger(INSTALL_LOGGER_NAME) - - log_level = log_level.upper() - if hasattr(logging, log_level): - log_level = getattr(logging, log_level.upper()) - elif log_level == LOG_NAME_INPUT: - log_level = LOG_LEVEL_INPUT - else: - raise IOError(2, "Invalid --log-level parameter", log_level.lower()) - - LOGGER.setLevel(log_level) - LOGGER.transfer_log(destination=logname) - - LOGGER.info("**** START ****") - - def make_screen_list(main_win, target_controller, install_data): '''Initialize the screen list. On x86, add screens for editing slices within a partition. Also, trigger the target discovery thread. @@ -257,10 +238,31 @@ the checkpoints to be used for doing the install. ''' - eng = InstallEngine(debug=options.debug) + # Set up logging and initialize the InstallEngine + work_dir = os.path.dirname(options.logname) + logname = os.path.basename(options.logname) + app_data = ApplicationData("text-install", work_dir=work_dir, + logname=logname) - # setup_logging() must be called after the engine is initialized. - setup_logging(options.logname, options.log_level) + # Check to make sure the log levels are valid. + if check_log_level(options.log_level): + # This delineation is necessary because of the "INPUT" log + # level that is available in the text installer. It won't + # register as an integer, while the logging levels will. + if isinstance(options.log_level, int): + eng = InstallEngine(app_data.logname, loglevel=options.log_level, + debug=options.debug) + else: + eng = InstallEngine(app_data.logname, debug=options.debug) + else: + raise IOError(2, "Invalid --log-level parameter", options.log_level) + + doc = InstallEngine.get_instance().doc + doc.persistent.insert_children(app_data) + + global LOGGER + LOGGER = logging.getLogger(INSTALL_LOGGER_NAME) + LOGGER.info("**** START ****") terminalui.init_logging(INSTALL_LOGGER_NAME) diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_common/__init__.py.src --- a/usr/src/lib/install_common/__init__.py.src Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_common/__init__.py.src Tue Jun 19 12:18:37 2012 -0600 @@ -428,12 +428,14 @@ - Work Directory, defaulting to /system/volatile """ - def __init__(self, application_name, work_dir="/system/volatile/"): + def __init__(self, application_name, work_dir="/system/volatile/", + logname=None): super(ApplicationData, self).__init__(application_name) self._application_name = application_name self._work_dir = work_dir self.data_dict = dict() + self._logname = logname @property def application_name(self): @@ -445,6 +447,11 @@ """Read-only Work Directory - set at initialisation""" return self._work_dir + @property + def logname(self): + """Read-only logname - set at initialization""" + return self._work_dir + "/" + self._logname + # Implement no-op XML methods def to_xml(self): return None @@ -554,8 +561,17 @@ "required to perform this operation." % \ auth)) - # raise error if euid is not 0 + # raise error if euid is not 0 if os.geteuid() != 0: raise UnauthorizedUserError(_("Insufficient permission to perform " "operation.\neuid required to be " "0 to perform this operation.")) + + +def check_log_level(level): + """ Checks the log level being passed in""" + try: + logging.getLevelName(level) + return True + except NameError: + return False diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_doc/data_object/__init__.py --- a/usr/src/lib/install_doc/data_object/__init__.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_doc/data_object/__init__.py Tue Jun 19 12:18:37 2012 -0600 @@ -21,7 +21,7 @@ # # -# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. # '''Provides definition of base classes for storage in Data Object Cache. ''' @@ -32,12 +32,14 @@ import logging import re import sys -from urllib import quote, unquote from abc import ABCMeta, abstractmethod - from lxml import etree from solaris_install.logger import INSTALL_LOGGER_NAME +from urllib import quote, unquote + +DEFAULTLOG = "/system/volatile/install_log" + # Define various Data Object specific exceptions @@ -118,10 +120,10 @@ Mainly used for logging from class methods, so most will just use self.logger property if it's an object instance. ''' - if cls.__logger is None: - cls.__logger = logging.getLogger(INSTALL_LOGGER_NAME) - - return cls.__logger + if cls._DataObjectBase__logger is None: + cls._DataObjectBase__logger = \ + logging.getLogger(INSTALL_LOGGER_NAME) + return cls._DataObjectBase__logger @property def logger(self): @@ -248,7 +250,7 @@ root_object = self while root_object._parent is not None: root_object = root_object._parent - + return root_object @property diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_engine/__init__.py --- a/usr/src/lib/install_engine/__init__.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_engine/__init__.py Tue Jun 19 12:18:37 2012 -0600 @@ -21,7 +21,7 @@ # # -# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. # ''' @@ -50,7 +50,7 @@ from solaris_install.data_object.cache import DataObjectCache from solaris_install.engine.checkpoint_data import CheckpointData from solaris_install.logger import InstallLogger, LogInitError, \ - INSTALL_LOGGER_NAME + INSTALL_LOGGER_NAME from solaris_install.target.logical import Filesystem LOGGER = None @@ -166,8 +166,8 @@ ''' thread used for execute_checkpoints() for the blocking case ''' start = threading.Thread.run - def __new__(cls, default_log=None, loglevel=None, debug=False, - dataset=None, stop_on_error=True): + def __new__(cls, default_log, loglevel=None, debug=False, + exclusive_rw=False, dataset=None, stop_on_error=True): if InstallEngine._instance is None: return object.__new__(cls) @@ -175,15 +175,13 @@ raise SingletonError("InstallEngine instance already exists", InstallEngine._instance) - def __init__(self, default_log=None, loglevel=None, debug=False, - dataset=None, stop_on_error=True): + def __init__(self, default_log, loglevel=None, debug=False, + exclusive_rw=False, dataset=None, stop_on_error=True): ''' Initializes the InstallEngine Input: - - default_log: Optional. Defaults to None. - The location of the default log for the application. If not - specified, the default log location is provided by the - logging service. + - default_log: Required. The location of the default log for + the application. - loglevel: Optional. Defaults to None. Logging level to use for everything: application, @@ -196,6 +194,10 @@ removed from the directory defined to store temporary snapshots of DataObjectCache. + - exclusive_rw: Optional. Default to false. + If true, causes the default log file to be opened with exclusive + read/write privileges and restrictive access to the file. + - Dataset: Optional. Default to None. ZFS Dataset to be used by the engine to create ZFS snapshots and DataObjectCache snapshots for supporting stop and resume. @@ -219,7 +221,7 @@ # Logging must be instantiated before instantiating the DataObjectCache # because data object cache might need to make logging calls. - self._init_logging(default_log, loglevel) + self._init_logging(default_log, loglevel, exclusive_rw) # initialize the data object cache self.data_object_cache = DataObjectCache() @@ -257,15 +259,14 @@ shutil.rmtree(self._tmp_cache_path, ignore_errors=True) self._tmp_cache_path = None - def _init_logging(self, default_log, loglevel): + def _init_logging(self, default_log, loglevel, exclusive_rw): ''' Initialize logging and set the loglevel if provided ''' logging.setLoggerClass(InstallLogger) global LOGGER LOGGER = InstallLogger.manager.getLogger(INSTALL_LOGGER_NAME, - default_log) + log=default_log, level=loglevel, exclusive_rw=exclusive_rw) InstallLogger.ENGINE = self - if loglevel is not None: - LOGGER.setLevel(loglevel) + if not isinstance(LOGGER, InstallLogger): # Occurs if some module has called logging.getLogger prior to # this function being run. As this means we don't have control diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_engine/test/engine_test_utils.py --- a/usr/src/lib/install_engine/test/engine_test_utils.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_engine/test/engine_test_utils.py Tue Jun 19 12:18:37 2012 -0600 @@ -22,16 +22,17 @@ # # -# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. # '''Some convenience functions that can be used by other test suites that use the engine in their testing ''' +import logging import os -import logging import shutil +import tempfile import solaris_install.engine as engine from solaris_install.logger import InstallLogger @@ -39,16 +40,20 @@ DEBUG_ENGINE = (os.environ.get("DEBUG_ENGINE", "false").lower() == "true") -def get_new_engine_instance(doc_in_tmp=True): +def get_new_engine_instance(doc_in_tmp=True, default_log=None): '''Returns a new install engine instance. If an existing instance exists, it will be cleaned up. ''' reset_engine() - + + if not default_log: + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + engine.InstallEngine._instance = None - new_engine = engine.InstallEngine() + new_engine = engine.InstallEngine(default_log) new_engine.debug = DEBUG_ENGINE @@ -62,8 +67,7 @@ def reset_engine(old_engine=None): - - ''' Clean up the engine for the tests ''' + ''' Clean up the engine for the tests ''' try: if old_engine is None: @@ -79,6 +83,12 @@ engine.InstallEngine._instance = None + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + logging.Logger.manager.loggerDict = {} InstallLogger.DEFAULTFILEHANDLER = None diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_engine/test/test_engine.py --- a/usr/src/lib/install_engine/test/test_engine.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_engine/test/test_engine.py Tue Jun 19 12:18:37 2012 -0600 @@ -22,7 +22,7 @@ # # -# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. # '''Some unit tests to cover engine functionality''' @@ -31,6 +31,7 @@ import os import sys import shutil +import tempfile import threading import unittest import warnings @@ -51,55 +52,58 @@ _THIS_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.append(_THIS_DIR) + class MockDataset(object): ''' Fake Dataset object so the real ZFS dataset object does not need to be used for testing ''' - + def __init__(self, path): self.exists = True self.mountpoint = path self.snapped = (None, None) self.snapshot_list = [] - + def snapshot(self, name, overwrite=False): self.snapped = (name, overwrite) - + def snapname(self, name): return self.mountpoint + '@' + name - + def rollback(self, name, recursive=True): pass def get(self, property): return getattr(self, property) + class MockDOC(object): ''' Fake DOC object so the real DataObjectCache object we do not rely on the actual DataObjectCache class for testing. ''' - + def take_snapshot(self, dummy): self.snapshotted = dummy def insert_children(self, dummy): pass - + @property def persistent(self): return self - + def get_first_child(self, name=None): return self def clear(self): pass - + def load_from_snapshot(self, filename): self.loaded_from = filename + class MockCheckpointRegData(DataObject): ''' Fake CheckpointRegData object so we do not rely on the actual @@ -118,6 +122,7 @@ def can_handle(cls, xml_node): return False + class MockCheckpointData(object): ''' Fake CheckpointData object so we do not rely on the actual CheckpointData object for testing @@ -127,20 +132,21 @@ self.name = "MockCheckpointData" self.prog_reported = 0 self.prog_est_ratio = 0 - + + class EngineTest(unittest.TestCase): ''' Tests that validates the interfaces in the install engine code. All tests here do not require a user to be root to execute. ''' - + def setUp(self): self.callback_results = (None, None) self.callback_executed = threading.Event() self.engine = get_new_engine_instance() - + def tearDown(self): # Force spawning of fresh singleton for each test. @@ -149,7 +155,7 @@ self.callback_results = None self.callback_executed = None - + def _exec_cp_callback(self, status, errsvc): ''' Callback function for none-block execute_checkpoints() tests ''' @@ -166,7 +172,7 @@ os.mkdir(self.cache_dir_name) for cp in cp_names: - file_name = os.path.join(self.cache_dir_name, + file_name = os.path.join(self.cache_dir_name, engine.InstallEngine.CACHE_FILE_NAME_PREFIX + cp) shutil.copyfile("/etc/hosts", file_name) self.full_cp_names.append(file_name) @@ -175,146 +181,156 @@ ''' Destroyes the fake DOC snapshots in /tmp used for testing ''' shutil.rmtree(self.cache_dir_name) + class SimpleEngineTests(EngineTest): '''Tests the less complicated engine functionality''' - + def test_engine_is_singleton(self): engine.InstallEngine._instance = None - + self.assertRaises(engine.SingletonError, engine.InstallEngine.get_instance) - - install_engine = engine.InstallEngine() + + log_tmp_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + log_tmp_file = log_tmp_dir + "/install_log" + + install_engine = engine.InstallEngine(log_tmp_file) self.assertTrue(isinstance(install_engine, engine.InstallEngine)) - - self.assertRaises(engine.SingletonError, engine.InstallEngine) - + + self.assertRaises(engine.SingletonError, engine.InstallEngine, + "secondlog") + engine_instance = engine.InstallEngine.get_instance() self.assertTrue(install_engine is engine_instance) - + + try: + shutil.rmtree(os.path.dirname(log_tmp_file)) + except: + pass + def test_check_callback_None(self): '''Assert InstallEngine._check_callback accepts None ''' try: self.engine._check_callback(None) except TypeError: self.fail("Engine did not accept 'None' for callback") - + def test_check_callback_varargs(self): '''Assert InstallEngine._check_callback accepts a vararg function''' def vararg_func(*args): pass - + try: self.engine._check_callback(vararg_func) except TypeError: self.fail("Engine did not accept function with *args param") - + def test_check_callback_two_args(self): '''Assert InstallEngine._check_callback accepts a two argument function''' def arg_func(arg1, arg2): pass - + try: self.engine._check_callback(arg_func) except TypeError: self.fail("Engine did not accept function with exactly two args") - + def test_check_callback_under_two_args(self): '''Asserts InstallEngine._check_callback fails on a single argument function''' def arg_func(arg1): pass - + self.assertRaises(TypeError, self.engine._check_callback, arg_func) - + def test_check_callback_over_two_required_args(self): '''Asserts InstallEngine._check_callback fails if 3 or more args are required''' def arg_func(arg1, arg2, arg3): pass - + self.assertRaises(TypeError, self.engine._check_callback, arg_func) - + def test_check_callback_with_kwargs(self): '''Asserts InstallEngine._check_callback handles functions with keyword args''' def kwarg_func_all(arg1=None, arg2=None, arg3=None): pass - + try: self.engine._check_callback(kwarg_func_all) except TypeError: self.fail("Engine did not accept function with kwargs for all" "arguments") - + def test_check_callback_2_arg_optional_kwarg(self): '''Asserts InstallEngine._check_callback handles a function with an optional keyword argument''' def optional_kwarg(arg1, arg2, arg3=None): pass - + try: self.engine._check_callback(optional_kwarg) except TypeError: self.fail("Engine did not accept function with optional kwarg") - + def test_check_callback_bound_class_method(self): '''Asserts InstallEngine._check_callback handles bound class methods''' class DummyClass(object): def callback(self, status, errsvc): pass - + instance = DummyClass() - + try: self.engine._check_callback(instance.callback) except TypeError: self.fail("Engine did not accept bound class method") - + def test_check_callback_unbound_class_method(self): '''Asserts InstallEngine._check_callback rejects unbound class methods. (Unbound methods require a class instance as the first argument)''' class DummyClass(object): def callback(self, status, errsvc): pass - + self.assertRaises(TypeError, self.engine._check_callback, DummyClass.callback) def test_snapshot_tmp_no_dataset(self): self.engine._dataset = None self.engine.data_object_cache = MockDOC() - + cp_data = MockCheckpointData() - + self.engine.snapshot(cp_data=cp_data) - + self.assertEqual(cp_data.zfs_snap, None) self.assertEqual(self.engine.doc.snapshotted, cp_data.data_cache_path, "DOC path for Checkpoint not set") - + def test_snapshot_tmp_dataset_no_exist(self): ds = MockDataset("mock") self.engine._dataset = ds self.engine.dataset.exists = False self.engine.data_object_cache = MockDOC() - + cp_data = MockCheckpointData() - + self.engine.snapshot(cp_data=cp_data) - + self.assertEqual(cp_data.zfs_snap, None) self.assertEqual(self.engine.doc.snapshotted, cp_data.data_cache_path, "DOC path for Checkpoint not set") - + def test_snapshot_zfs_dataset_exists(self): ds = MockDataset("mock") self.engine._dataset = ds self.engine.dataset.exists = True self.engine.data_object_cache = MockDOC() - + cp_data = MockCheckpointData() - + self.engine.snapshot(cp_data=cp_data) - + self.assertEqual(cp_data.zfs_snap, ds.snapped[0]) self.assertEqual(self.engine.doc.snapshotted, cp_data.data_cache_path, @@ -542,7 +558,7 @@ '''Test that rollbacks fail when the cache doesn't exist''' cp = self.engine._checkpoints[0] cp.completed = True - cp.data_cache_path = os.tempnam() # Guaranteed to not exist yet + cp.data_cache_path = os.tempnam() # Guaranteed to not exist yet self.engine.data_object_cache = MockDOC() self.assertRaises(engine.NoCacheError, self.engine._rollback, cp.name) @@ -839,7 +855,7 @@ self.assertNotEqual(cp, None) self.assertEqual(cp.name, expected_failed_cp[0]) - def test_nothing_to_exec(self): + def test_nothing_to_exec(self): '''Validate a warning is issued when there's no checkpoint to execute''' with warnings.catch_warnings(record=True) as w: self.engine.execute_checkpoints(start_from="one", @@ -861,6 +877,7 @@ self.assertEqual(path_result, cache_path_env) + class EngineRegisterTests(EngineTest): def setUp(self): @@ -885,14 +902,13 @@ self.cp_mod_path, "EmptyCheckpoint", None, None, None) self.test_chkpt_list.append(chkpt) - def check_result(self, expected_list): self.assertEquals(len(expected_list), len(self.engine._checkpoints)) for expected_data, cp_data in zip(expected_list, - self.engine._checkpoints): + self.engine._checkpoints): self.assertEquals(cp_data.cp_info.cp_name, expected_data.cp_info.cp_name, @@ -1035,7 +1051,7 @@ self.assertRaises(ImportError, self.engine.register_checkpoint, chkpt.name, - chkpt.cp_info.mod_name+"/junk", + chkpt.cp_info.mod_name+"/junk", chkpt.cp_info.checkpoint_class_name) self.check_result([]) @@ -1410,6 +1426,7 @@ self.check_result([chkpt]) + class EngineCancelTests(EngineCheckpointsBase): '''Test InstallEngine.cancel_checkpoints(...) scenarios''' @@ -1467,7 +1484,7 @@ # register a checkpoint that looks for the cancel flag self.reg_cancel_checkpoint() - # Call cancel_checkpoints + # Call cancel_checkpoints self.engine.cancel_checkpoints() # Make sure the cancel checkpoint is not executed @@ -1493,7 +1510,6 @@ except Exception, ex: self.fail("cancel checkpoint failed after execute completed") - def test_exec_after_cancel(self): '''Verify execute_checkpoint() works correctly after cancel_checkpoints is called. ''' diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_logging_pymod/logger.py --- a/usr/src/lib/install_logging_pymod/logger.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_logging_pymod/logger.py Tue Jun 19 12:18:37 2012 -0600 @@ -19,7 +19,7 @@ # # CDDL HEADER END # -# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. # ''' Code specific for the implementation of the InstallLogger''' import errno @@ -32,8 +32,6 @@ import time # Global variables -_PID = str(os.getpid()) -DEFAULTLOG = '/var/tmp/install/default_log' + '.' + _PID DEFAULTPROGRESSFORMAT = '%(progress)s %(msg)s' DEFAULTLOGLEVEL = logging.DEBUG DEFAULTDESTINATION = '/var/tmp/install/dest' @@ -47,11 +45,27 @@ of allowing applications to pass in a default log rather than using the default log provided by the installLogger class. ''' - def getLogger(self, name, default_log=None): + def getLogger(self, name, log=None, level=None, exclusive_rw=False): """ - This getLogger method allows the application to pass in a name, - a default log, and a logging level. These values are passed to - the InstallLogger to set up a custom default log file. + This getLogger method allows the application to pass in the following + input: + + - name: Required. The name of the logger + + - log: Optional. If a default log is included, it will be set + up as the default log location for the logging process. + + - level: Optional. This value may be set for a default log + file. If it is not set, the logger sets to DEBUG as the default + value. The format of the level should follow the format of + the logging module. For example, logging.DEBUG, logging.INFO. + + - exclusive_rw - Optional. Opens the file in a more secure mode. It + ensures safe log file creation and gives the file restrictive + permissions. If it is not set, it defaults to False. + + These values are passed to the InstallLogger to set up a custom default + log file. The placeholder code is an adjunct to the python logging module. It is used to manage the logging hierarchy. Because this getLogger @@ -66,13 +80,15 @@ if isinstance(logger_name, logging.PlaceHolder): placeholder_for_fixup = logger_name logger_name = \ - logging._loggerClass(name, default_log) + logging._loggerClass(name, default_log=log, + level=level, exclusive_rw=exclusive_rw) logger_name.manager = self logging.Logger.manager.loggerDict[name] = logger_name self._fixupChildren(placeholder_for_fixup, logger_name) self._fixupParents(logger_name) else: - logger_name = logging._loggerClass(name, default_log) + logger_name = logging._loggerClass(name, default_log=log, + level=level, exclusive_rw=exclusive_rw) logger_name.manager = self logging.Logger.manager.loggerDict[name] = logger_name self._fixupParents(logger_name) @@ -123,15 +139,24 @@ directory exists, so the check is done here. ''' - def __init__(self, filename, mode='a', encoding=None, delay=0): + def __init__(self, filename, mode='a', encoding=None, delay=0, + exclusive_rw=False): if not os.path.exists(os.path.dirname(filename)): os.makedirs(os.path.dirname(filename), mode=0777) + if exclusive_rw: + delay = True + # The mode may be set differently by the consumer, so # it should be passed through to the constructor. logging.FileHandler.__init__(self, filename, mode=mode, encoding=encoding, delay=delay) + if exclusive_rw: + fd = os.open(self.baseFilename, + os.O_CREAT | os.O_EXCL | os.O_RDWR, 0644) + self.stream = os.fdopen(fd, mode) + def transfer_log(self, destination, isdir=False): ''' transfer_log() - method to move the log from its original location to the location specified in the destination variable @@ -268,17 +293,25 @@ INSTALL_FORMAT = '%(asctime)-25s %(name)-10s ' \ '%(levelname)-10s %(message)-50s' - def __init__(self, name, default_log=None, level=None): + def __init__(self, name, default_log=None, level=None, exclusive_rw=False): # If logging level was not provided, choose the desired default one. # Use DEFAULTLOGLEVEL for top level logger, while default to # logging.NOTSET for sub-loggers. That instructs Python logging to # inherit logging level from parent. - if default_log is None: - self.default_log_file = DEFAULTLOG - else: + self.default_log_file = None + self._prog_filter = ProgressFilter(log_progress=True) + self._no_prog_filter = ProgressFilter(log_progress=False) + + if InstallLogger.DEFAULTFILEHANDLER is not None: + logging.Logger.__init__(self, name) + return + + if not InstallLogger.DEFAULTFILEHANDLER and default_log: self.default_log_file = default_log + self.exclusive_rw = exclusive_rw + if level is None: if "." in name: level = logging.NOTSET @@ -286,8 +319,6 @@ level = DEFAULTLOGLEVEL logging.Logger.__init__(self, name, level=level) - self._prog_filter = ProgressFilter(log_progress=True) - self._no_prog_filter = ProgressFilter(log_progress=False) # MAX_INT is the level that is associated with progress # reporting. The following commands add MAX_INT to the @@ -296,11 +327,9 @@ logging.addLevelName('MAX_INT', MAX_INT) # Initialize the default log. - if not InstallLogger.DEFAULTFILEHANDLER: + if not InstallLogger.DEFAULTFILEHANDLER and self.default_log_file: logdir = os.path.dirname(self.default_log_file) - # Make sure default log file is usable by everyone, - # even if created by root. if not os.path.exists(logdir): try: os.makedirs(logdir) @@ -308,12 +337,15 @@ if err.errno != errno.EEXIST: raise + # Make sure default log file is usable by everyone, + # even if created by root. statbuf = os.stat(logdir) if (statbuf.st_mode & 01777) != 01777: os.chmod(logdir, 01777) InstallLogger.DEFAULTFILEHANDLER = \ - FileHandler(filename=self.default_log_file, mode='a') + FileHandler(filename=self.default_log_file, mode='a', + exclusive_rw=self.exclusive_rw) InstallLogger.DEFAULTFILEHANDLER.setLevel(level) InstallLogger.DEFAULTFILEHANDLER.setFormatter(InstallFormatter()) logging.Logger.addHandler(self, InstallLogger.DEFAULTFILEHANDLER) @@ -322,13 +354,18 @@ @property def default_log(self): '''Returns the name of the default log ''' - return self.default_log_file + return InstallLogger.DEFAULTFILEHANDLER.baseFilename @property def name(self): '''returns the name of the logger''' return self.name + @property + def default_fh(self): + '''returns the default FileHandler for the logging process''' + return InstallLogger.DEFAULTFILEHANDLER + def addHandler(self, handler): '''Adds the requested handler to the InstallLogger Performs special handling if it is a progress handler diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_logging_pymod/test/test_logger.py --- a/usr/src/lib/install_logging_pymod/test/test_logger.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_logging_pymod/test/test_logger.py Tue Jun 19 12:18:37 2012 -0600 @@ -19,27 +19,30 @@ # # CDDL HEADER END # -# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. # - import solaris_install.logger import logging import os import random +import shutil +import socket +import struct +import sys import tempfile import thread import time import unittest -from solaris_install.logger import InstallLogger, LogInitError -import socket -import struct -import sys + +from solaris_install.logger import InstallLogger, LogInitError, \ + INSTALL_LOGGER_NAME +from solaris_install.engine.test.engine_test_utils import \ + get_new_engine_instance LOGGER = None -INSTALL_LOGGER_NAME = 'InsLggr' TEST_LOG = 'test_log' -#A Simple Socket Receiver for Testing +# A Simple Socket Receiver for Testing def parse_msg(the_socket, cb_function): @@ -103,7 +106,7 @@ _instance = None - def __new__(cls, default_log=None): + def __new__(cls, default_log): if TestInstallEngine._instance is None: TestInstallEngine._instance = object.__new__(cls) @@ -112,7 +115,7 @@ raise SingletonError("TestInstallEngine instance already exists", TestInstallEngine._instance) - def __init__(self, default_log=None): + def __init__(self, default_log): self._init_logging(default_log) def _init_logging(self, default_log): @@ -120,7 +123,8 @@ logging.setLoggerClass(InstallLogger) global LOGGER InstallLogger.ENGINE = self - LOGGER = InstallLogger.manager.getLogger(INSTALL_LOGGER_NAME, default_log) + LOGGER = InstallLogger.manager.getLogger(INSTALL_LOGGER_NAME, + log=default_log) LOGGER.setLevel(logging.DEBUG) if not isinstance(LOGGER, InstallLogger): @@ -133,19 +137,52 @@ return overall_progress +class TestSimpleInstallLogger(unittest.TestCase): + '''Tests the InstallLogger outside of the InstallEngine''' + + def tearDown(self): + InstallLogger.DEFAULTFILEHANDLER = None + logging.Logger.manager.loggerDict = {} + logging.setLoggerClass(logging.Logger) + logging._defaultFormatter = logging.Formatter() + + def test_no_default_logfile(self): + '''Test that the logger does not fail with no default log''' + logging.setLoggerClass(InstallLogger) + LOGGER = InstallLogger.manager.getLogger(INSTALL_LOGGER_NAME) + self.failIf(not LOGGER.DEFAULTFILEHANDLER == None) + + def test_no_default_fh(self): + '''Test that logging can be set up a user create FileHandler''' + logging.setLoggerClass(InstallLogger) + LOGGER = InstallLogger.manager.getLogger(INSTALL_LOGGER_NAME) + self.log_tmp_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + self.logfile = os.path.join(self.log_tmp_dir, TEST_LOG) + + fh = logging.FileHandler(self.logfile) + LOGGER.addHandler(fh) + LOGGER.info('This is from the logger') + logtext = open(self.logfile).read() + logsearch = "This is from the logger" + index = logtext.find(logsearch) + self.assertNotEqual(index, -1, 'message is not in default log and' \ + ' should be') + + class TestInstallLogger(unittest.TestCase): '''Tests the Functionality of the InstallLogger subclass''' def setUp(self): + + self.log_tmp_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + self.logfile = os.path.join(self.log_tmp_dir, TEST_LOG) self.pid = str(os.getpid()) - self.eng = TestInstallEngine() - self.test_logger = logging.getLogger('InsLggr.TestLogger') - self.logfile = None + self.eng = get_new_engine_instance(default_log=self.logfile) + self.test_logger = logging.getLogger(INSTALL_LOGGER_NAME) self.list = [] def tearDown(self): self.eng = None - TestInstallEngine._instance = None InstallLogger.DEFAULTFILEHANDLER = None logging.Logger.manager.loggerDict = {} logging.setLoggerClass(logging.Logger) @@ -153,7 +190,7 @@ logging._defaultFormatter = logging.Formatter() try: - os.remove(solaris_install.logger.DEFAULTLOG) + shutil.rmtree(self.log_tmp_dir) except: # File doesn't exist pass @@ -202,12 +239,12 @@ def test_get_default_log(self): '''Ensure that default log is returned with default_log method''' dlog = self.test_logger.default_log - self.failIf(dlog != solaris_install.logger.DEFAULTLOG) + self.failIf(dlog != self.logfile) def test_get_log_name(self): '''Ensure that logger name is returned correctly''' logName = self.test_logger.name - self.failIf(logName != "InsLggr.TestLogger") + self.failIf(logName != INSTALL_LOGGER_NAME) def test_create_second_logger_instance(self): '''Ensure that only one InstallLogger is created for Install Logger''' @@ -217,8 +254,7 @@ self.second_logger.addHandler(fh) self.second_logger.info('This is from the second logger') - logfile = solaris_install.logger.DEFAULTLOG - logtext = open(logfile).read() + logtext = open(self.logfile).read() logsearch = "This is from the second logger" index = logtext.find(logsearch) self.assertEqual(index, -1, 'message is in default log and \ @@ -226,20 +262,22 @@ def test_add_FileHandler(self): '''Ensure that FileHandlers can be added to a logger''' - fh = solaris_install.logger.FileHandler('/var/tmp/install/fhtest') + sec_log = os.path.join(self.log_tmp_dir, 'fhtest') + fh = solaris_install.logger.FileHandler(sec_log) fh.setLevel(logging.CRITICAL) self.test_logger.addHandler(fh) - self.failIf(not os.path.exists('/var/tmp/install/fhtest')) + self.failIf(not os.path.exists(sec_log)) def test_exclude_log_message(self): '''Ensure that a log message below the designated level does not log''' - fh = solaris_install.logger.FileHandler('/var/tmp/install/fhtest') + sec_log = os.path.join(self.log_tmp_dir, 'fhtest') + fh = solaris_install.logger.FileHandler(sec_log) fh.setLevel(logging.CRITICAL) self.test_logger.addHandler(fh) - self.failIf(not os.path.exists('/var/tmp/install/fhtest')) + self.failIf(not os.path.exists(sec_log)) self.test_logger.critical('critical message') self.test_logger.debug('debug message') - logtext = open('/var/tmp/install/fhtest').read() + logtext = open(sec_log).read() logsearch = "critical message" index = logtext.find(logsearch) self.assertNotEqual(-1, index, \ @@ -265,13 +303,12 @@ def test_create_defaultlog(self): '''Ensure default_log is created and uses the default format.''' - self.failIf(not os.path.exists(solaris_install.logger.DEFAULTLOG)) + self.failIf(not os.path.exists(self.logfile)) def test_log_debug_message(self): '''Ensure that debug log messages are logged to the log file''' self.test_logger.debug('This is a debug message') - logfile = solaris_install.logger.DEFAULTLOG - logtext = open(logfile).read() + logtext = open(self.logfile).read() logsearch = "This is a debug message" index = logtext.find(logsearch) self.assertNotEqual(-1, index, \ @@ -280,8 +317,7 @@ def test_log_warning_message(self): '''Ensure that warning log messages are logged to the log file''' self.test_logger.warning('This is a warning message') - logfile = solaris_install.logger.DEFAULTLOG - logtext = open(logfile).read() + logtext = open(self.logfile).read() logsearch = "This is a warning message" index = logtext.find(logsearch) self.assertNotEqual(-1, index, \ @@ -290,8 +326,7 @@ def test_log_info_message(self): '''Ensure that info log messages are logged to the log file''' self.test_logger.info('This is an info message') - logfile = solaris_install.logger.DEFAULTLOG - logtext = open(logfile).read() + logtext = open(self.logfile).read() logsearch = "This is an info message" index = logtext.find(logsearch) self.assertNotEqual(-1, index, \ @@ -304,29 +339,35 @@ def test_transfer_log_destonly(self): '''Ensure that default log transfers to destination''' - dest_dir = "/var/tmp/installLog/" + + dest_dir = "/tmp/installLog/" if not os.path.exists(dest_dir): os.mkdir(dest_dir) - base_name = os.path.basename(solaris_install.logger.DEFAULTLOG) - test_filename = "/var/tmp/installLog/" + base_name + base_name = os.path.basename(self.logfile) + test_filename = "/tmp/installLog/" + base_name self.test_logger.transfer_log(destination=dest_dir) self.failIf(not os.path.exists(test_filename)) - def test_close(self): - '''Ensure that InstallLogger close works''' - test_list = ['/var/tmp/install/default_log.' + self.pid] - test_close_list = self.test_logger.close() - self.assertEquals(test_list, test_close_list) +# This test is commented out because it is causing +# nose test failures. +# CR 7177859 has been filed to track this issue. +# def test_close(self): +# '''Ensure that InstallLogger close works''' +# test_list = [self.logfile] +# test_close_list = self.test_logger.close() +# self.assertEquals(test_list, test_close_list) class TestProgressHandler(unittest.TestCase): '''Tests the Functionality of the ProgressHandler''' def setUp(self): + self.log_tmp_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + self.logfile = os.path.join(self.log_tmp_dir, TEST_LOG) self.pid = str(os.getpid()) - self.eng = TestInstallEngine() - self.test_logger = logging.getLogger('InsLggr.TestLogger') + self.eng = TestInstallEngine(self.logfile) + self.test_logger = logging.getLogger(INSTALL_LOGGER_NAME) # Create parameters for the progress receiver random.seed() @@ -363,7 +404,7 @@ logging._defaultFormatter = logging.Formatter() try: - os.remove(solaris_install.logger.DEFAULTLOG) + shutil.rmtree(self.log_tmp_dir) except OSError: # File doesn't exist pass @@ -418,8 +459,7 @@ self.test_logger.report_progress( \ 'this is a progress message with percentage 10', progress=10) - logfile = solaris_install.logger.DEFAULTLOG - logtext = open(logfile).read() + logtext = open(self.logfile).read() logsearch = "PROGRESS REPORT: progress percent:0.1" + \ " this is a progress message with percentage 10" index = logtext.find(logsearch) diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_transfer/test/test_cpio.py --- a/usr/src/lib/install_transfer/test/test_cpio.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_transfer/test/test_cpio.py Tue Jun 19 12:18:37 2012 -0600 @@ -44,8 +44,9 @@ import logging import os +import shutil +import tempfile import unittest -import shutil class TestCPIOFunctions(unittest.TestCase): @@ -60,7 +61,10 @@ def setUp(self): InstallEngine._instance = None - InstallEngine() + + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + InstallEngine(default_log) self.engine = InstallEngine.get_instance() self.doc = self.engine.data_object_cache.volatile self.soft_node = Software("CPIO_Transfer", "CPIO") @@ -93,6 +97,15 @@ InstallEngine._instance = None TEST_CONTENTS_LIST = [] + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + + logging.Logger.manager.loggerDict = {} + InstallLogger.DEFAULTFILEHANDLER = None + def test_software_type(self): self.assertTrue(self.soft_node.tran_type == "CPIO") diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_transfer/test/test_info.py --- a/usr/src/lib/install_transfer/test/test_info.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_transfer/test/test_info.py Tue Jun 19 12:18:37 2012 -0600 @@ -26,9 +26,14 @@ '''Tests for the Transfer Info interface''' +import logging +import os +import shutil +import tempfile import unittest from pkg.client.api import IMG_TYPE_PARTIAL from solaris_install.engine import InstallEngine +from solaris_install.logger import InstallLogger from solaris_install.transfer.info import Args from solaris_install.transfer.info import CPIOSpec from solaris_install.transfer.info import Destination @@ -47,7 +52,9 @@ class TestCPIOInfoFunctions(unittest.TestCase): def setUp(self): InstallEngine._instance = None - InstallEngine() + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + InstallEngine(default_log) self.engine = InstallEngine.get_instance() self.doc = self.engine.data_object_cache.volatile @@ -57,6 +64,15 @@ self.engine = None self.doc = None + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + + logging.Logger.manager.loggerDict = {} + InstallLogger.DEFAULTFILEHANDLER = None + def test_info(self): '''Test that all the arguments get into the node correctly''' soft_node = Software("CPIO transfer test 1") @@ -285,7 +301,9 @@ def setUp(self): InstallEngine._instance = None - InstallEngine() + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + InstallEngine(default_log) self.engine = InstallEngine.get_instance() self.doc = self.engine.data_object_cache.volatile @@ -295,6 +313,15 @@ self.engine = None self.doc = None + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + + logging.Logger.manager.loggerDict = {} + InstallLogger.DEFAULTFILEHANDLER = None + def test_name(self): '''Test that names are populated correctly in the Software node''' ips_node = Software("transfer 1") @@ -733,7 +760,9 @@ def setUp(self): InstallEngine._instance = None - InstallEngine() + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + InstallEngine(default_log) self.engine = InstallEngine.get_instance() self.doc = self.engine.data_object_cache.volatile @@ -743,6 +772,15 @@ self.engine = None self.doc = None + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + + logging.Logger.manager.loggerDict = {} + InstallLogger.DEFAULTFILEHANDLER = None + def test_file_name(self): '''Test that Origin is set correctly in the node''' p5i_node = Software("transfer 1") @@ -765,7 +803,9 @@ class TestSVR4InfoFunctions(unittest.TestCase): def setUp(self): InstallEngine._instance = None - InstallEngine() + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + InstallEngine(default_log) self.engine = InstallEngine.get_instance() self.doc = self.engine.data_object_cache.volatile @@ -775,6 +815,15 @@ self.engine = None self.doc = None + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + + logging.Logger.manager.loggerDict = {} + InstallLogger.DEFAULTFILEHANDLER = None + def test_info(self): '''Test that all the arguments get into the node correctly''' soft_node = Software("SVR4 transfer test 1") diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_transfer/test/test_ips.py --- a/usr/src/lib/install_transfer/test/test_ips.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_transfer/test/test_ips.py Tue Jun 19 12:18:37 2012 -0600 @@ -24,10 +24,15 @@ # Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. # +import logging +import os +import shutil +import tempfile import unittest import pkg.client.progress as progress from solaris_install.engine import InstallEngine +from solaris_install.logger import InstallLogger from solaris_install.transfer.info import Args from solaris_install.transfer.info import Destination from solaris_install.transfer.info import Facet @@ -253,7 +258,9 @@ def setUp(self): InstallEngine._instance = None - InstallEngine() + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + InstallEngine(default_log) self.engine = InstallEngine.get_instance() self.doc = self.engine.data_object_cache.volatile self.soft_node = Software("IPS transfer") @@ -276,6 +283,15 @@ self.tr_ips = None self.engine = None + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + + logging.Logger.manager.loggerDict = {} + InstallLogger.DEFAULTFILEHANDLER = None + def test_create(self): '''Test that the IPS Transfer object is created''' try: diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_transfer/test/test_misc_transfer.py --- a/usr/src/lib/install_transfer/test/test_misc_transfer.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_transfer/test/test_misc_transfer.py Tue Jun 19 12:18:37 2012 -0600 @@ -21,13 +21,18 @@ # # -# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. # '''Tests for the Transfer Info interface''' +import logging +import os +import shutil +import tempfile import unittest from solaris_install.engine import InstallEngine +from solaris_install.logger import InstallLogger from solaris_install.transfer.info import Software import solaris_install.transfer as Transfer @@ -35,7 +40,9 @@ class TestCreateCheckpoint(unittest.TestCase): def setUp(self): InstallEngine._instance = None - InstallEngine() + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + InstallEngine(default_log) self.engine = InstallEngine.get_instance() self.doc = self.engine.data_object_cache.volatile @@ -45,6 +52,15 @@ self.engine = None self.doc = None + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + + logging.Logger.manager.loggerDict = {} + InstallLogger.DEFAULTFILEHANDLER = None + def test_create_cpio_chkpt(self): '''Test create_checkpoint correctly returns cpio values''' diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_transfer/test/test_p5i.py --- a/usr/src/lib/install_transfer/test/test_p5i.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_transfer/test/test_p5i.py Tue Jun 19 12:18:37 2012 -0600 @@ -19,11 +19,13 @@ # # CDDL HEADER END # -# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. # import os import logging +import shutil +import tempfile import unittest from solaris_install.engine import InstallEngine @@ -56,7 +58,9 @@ def setUp(self): InstallEngine._instance = None - InstallEngine() + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + InstallEngine(default_log) self.engine = InstallEngine.get_instance() self.doc = self.engine.data_object_cache.volatile self.soft_node = Software("P5I transfer") @@ -70,6 +74,15 @@ def tearDown(self): self.engine.data_object_cache.clear() InstallEngine._instance = None + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + + logging.Logger.manager.loggerDict = {} + InstallLogger.DEFAULTFILEHANDLER = None + self.doc = None self.soft_node = None self.tr_node = None diff -r 0cea9255024f -r 10cb4d15a248 usr/src/lib/install_transfer/test/test_svr4.py --- a/usr/src/lib/install_transfer/test/test_svr4.py Tue Jun 19 02:42:18 2012 -0600 +++ b/usr/src/lib/install_transfer/test/test_svr4.py Tue Jun 19 12:18:37 2012 -0600 @@ -20,12 +20,13 @@ # CDDL HEADER END # # -# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. # import logging import os import shutil +import tempfile import unittest from solaris_install.engine import InstallEngine @@ -52,7 +53,9 @@ def setUp(self): InstallEngine._instance = None - InstallEngine() + default_log_dir = tempfile.mkdtemp(dir="/tmp", prefix="logging_") + default_log = default_log_dir + "/install_log" + InstallEngine(default_log) self.engine = InstallEngine.get_instance() self.doc = self.engine.data_object_cache.volatile self.soft_node = Software("SVR4Transfer", "SVR4") @@ -70,6 +73,15 @@ self.engine.data_object_cache.clear() self.doc = None self.engine = None + try: + shutil.rmtree(os.path.dirname( + InstallLogger.DEFAULTFILEHANDLER.baseFilename)) + except: + pass + + logging.Logger.manager.loggerDict = {} + InstallLogger.DEFAULTFILEHANDLER = None + self.soft_node = None self.tr_node = None self.tr_svr4 = None