src/checkforupdates.py
changeset 2078 a13afe24c79a
child 2092 0ef66bf272d3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/checkforupdates.py	Thu Sep 16 11:55:53 2010 +0100
@@ -0,0 +1,317 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+import getopt
+import gettext
+import locale
+import os
+import sys
+
+import pkg.client.api_errors as api_errors
+import pkg.client.progress as progress
+import pkg.gui.enumerations as enumerations
+import pkg.gui.misc_non_gui as nongui_misc
+import pkg.misc as misc
+import pkg.nrlock as nrlock
+from cPickle import UnpicklingError
+from pkg.client import global_settings
+
+logger = global_settings.logger
+
+# Put _() in the global namespace
+import __builtin__
+__builtin__._ = gettext.gettext
+
+IMAGE_DIRECTORY_DEFAULT = "/"   # Image default directory
+IMAGE_DIR_COMMAND = "svcprop -p update/image_dir svc:/application/pkg/update"
+
+PKG_CLIENT_NAME = "updatemanager"
+CACHE_VERSION =  3
+CACHE_NAME = ".last_refresh_cache"
+
+class CheckForUpdates:
+        def __init__(self, image_directory, application_path, nice, check_all,
+            check_cache):
+                global_settings.client_name = nongui_misc.get_um_name()
+                self.api_lock = nrlock.NRLock()
+                self.image_dir_arg = image_directory
+                if self.image_dir_arg == None:
+                        self.image_dir_arg = nongui_misc.get_image_path()
+                self.application_path = application_path
+                self.nice = nice
+                self.check_all = check_all
+                self.check_cache_only = check_cache
+                try:
+                        self.application_dir = os.environ["PACKAGE_MANAGER_ROOT"]
+                except KeyError:
+                        self.application_dir = "/"
+                misc.setlocale(locale.LC_ALL, "")
+
+                self.progress_tracker = progress.NullProgressTracker()
+                self.api_obj = None
+                self.return_status = enumerations.UPDATES_UNDETERMINED
+                self.pylintstub = None
+
+                # Check Updates - by default check all
+                if self.check_all:
+                        self.api_obj = self.__get_api_obj()
+                        self.__check_for_updates()
+                elif self.check_cache_only:
+                        self.api_obj = self.__get_api_obj()
+                        ret = self.__check_for_updates_cache_only()
+                        if ret == enumerations.UPDATES_UNDETERMINED:
+                                self.__send_return(enumerations.UPDATES_UNDETERMINED)
+                
+        def __get_api_obj(self):
+                if self.api_obj == None:
+                        api_obj = nongui_misc.get_api_object(self.image_dir_arg,
+                            self.progress_tracker)
+                return api_obj
+
+        def __check_for_updates_cache_only(self):
+                if self.nice:
+                        os.nice(20)
+
+                if self.api_obj == None:
+                        return enumerations.UPDATES_UNDETERMINED
+
+                ret = self.__check_last_refresh()
+                if ret == enumerations.UPDATES_AVAILABLE:
+                        if debug:
+                                print >> sys.stderr, "From cache: Updates Available"
+                        self.__send_return(ret)
+                elif ret == enumerations.NO_UPDATES_AVAILABLE:
+                        if debug:
+                                print >> sys.stderr, \
+                                        "From cache: No Updates Available"
+                        self.__send_return(ret)
+                elif debug:
+                        print >> sys.stderr, "From cache: Updates Undetermined"
+                return ret
+
+        def __check_for_updates(self):
+                ret = self.__check_for_updates_cache_only()
+                if ret != enumerations.UPDATES_UNDETERMINED:
+                        return
+                if debug:
+                        print >> sys.stderr, \
+                                "Checking image for updates..."
+                if self.api_obj == None:
+                        self.__send_return(enumerations.UPDATES_UNDETERMINED)
+                        return
+                try:
+                        plan_ret = \
+                            self.api_obj.plan_update_all(sys.argv[0],
+                            refresh_catalogs = True,
+                            noexecute = True, force = True)
+                        stuff_to_do = plan_ret[0]
+                except api_errors.CatalogRefreshException, cre:
+                        crerr = nongui_misc.get_catalogrefresh_exception_msg(cre)
+                        if debug:
+                                print >> sys.stderr, "Exception occurred: %s" % crerr
+                        logger.error(crerr)
+                        self.__send_return(enumerations.UPDATES_UNDETERMINED)
+                        return
+                except api_errors.ApiException, e:
+                        err = str(e)
+                        if debug:
+                                print >> sys.stderr, "Exception occurred: %s" % err
+                        logger.error(err)
+                        self.__send_return(enumerations.UPDATES_UNDETERMINED)
+                        return
+
+                self.__dump_updates_available(stuff_to_do)
+                if stuff_to_do:
+                        if debug:
+                                print >> sys.stderr, "From image: Updates Available"
+                        self.__send_return(enumerations.UPDATES_AVAILABLE)
+                else:
+                        if debug:
+                                print >> sys.stderr, "From image: No Updates Available"
+                        self.__send_return(enumerations.NO_UPDATES_AVAILABLE)
+
+        def __send_return(self, status):
+                self.return_status = status
+
+        def __check_last_refresh(self):
+                cache_dir = nongui_misc.get_cache_dir(self.api_obj)
+                if not cache_dir:
+                        return enumerations.UPDATES_UNDETERMINED
+                try:
+                        info = nongui_misc.read_cache_file(os.path.join(
+                            cache_dir, CACHE_NAME + '.cpl'))
+                        if len(info) == 0:
+                                if debug:
+                                        print >> sys.stderr, "No cache"
+                                return enumerations.UPDATES_UNDETERMINED
+                        # pylint: disable-msg=E1103
+                        if info.get("version") != CACHE_VERSION:
+                                if debug:
+                                        print >> sys.stderr, "Cache version mismatch: %s"\
+                                            % (info.get("version") + " " + CACHE_VERSION)
+                                return enumerations.UPDATES_UNDETERMINED
+                        if info.get("os_release") != os.uname()[2]:
+                                if debug:
+                                        print >> sys.stderr, "OS release mismatch: %s"\
+                                            % (info.get("os_release") + " " + \
+                                            os.uname()[2])
+                                return enumerations.UPDATES_UNDETERMINED
+                        if info.get("os_version") != os.uname()[3]:
+                                if debug:
+                                        print >> sys.stderr, "OS version mismatch: %s"\
+                                            % (info.get("os_version") + " " + \
+                                            os.uname()[3])
+                                return enumerations.UPDATES_UNDETERMINED
+                        old_publishers = info.get("publishers")
+                        count = 0
+                        for p in self.api_obj.get_publishers():
+                                if p.disabled:
+                                        continue
+                                try:
+                                        if old_publishers[p.prefix] != p.last_refreshed:
+                                                return enumerations.UPDATES_UNDETERMINED
+                                except KeyError:
+                                        return enumerations.UPDATES_UNDETERMINED
+                                count += 1
+
+                        if count != len(old_publishers):
+                                return enumerations.UPDATES_UNDETERMINED
+                        n_updates = 0
+                        n_installs = 0
+                        n_removes = 0
+                        if info.get("updates_available"):
+                                n_updates = info.get("updates")
+                                n_installs = info.get("installs")
+                                n_removes = info.get("removes")
+                        # pylint: enable-msg=E1103
+                        if self.check_cache_only:
+                                print "n_updates: %d\nn_installs: %d\nn_removes: %d" % \
+                                        (n_updates, n_installs, n_removes)
+                        if (n_updates + n_installs + n_removes) > 0:
+                                return enumerations.UPDATES_AVAILABLE
+                        else:
+                                return enumerations.NO_UPDATES_AVAILABLE
+
+                except (UnpicklingError, IOError):
+                        return enumerations.UPDATES_UNDETERMINED
+
+        def __dump_updates_available(self, stuff_to_do):
+                cache_dir = nongui_misc.get_cache_dir(self.api_obj)
+                if not cache_dir:
+                        return
+                publisher_list = {}
+                for p in self.api_obj.get_publishers():
+                        if p.disabled:
+                                continue
+                        publisher_list[p.prefix] = p.last_refreshed
+                n_installs = 0
+                n_removes = 0
+                n_updates = 0
+                plan_desc = self.api_obj.describe()
+                if plan_desc:
+                        plan = plan_desc.get_changes()
+                        for pkg_plan in plan:
+                                orig = pkg_plan[0]
+                                dest = pkg_plan[1]
+                                if orig and dest:
+                                        n_updates += 1
+                                elif not orig and dest:
+                                        n_installs += 1
+                                elif orig and not dest:
+                                        n_removes += 1
+                dump_info = {}
+                dump_info["version"] = CACHE_VERSION
+                dump_info["os_release"] = os.uname()[2]
+                dump_info["os_version"] = os.uname()[3]
+                dump_info["updates_available"] = stuff_to_do
+                dump_info["publishers"] = publisher_list
+                dump_info["updates"] = n_updates
+                dump_info["installs"] = n_installs
+                dump_info["removes"] = n_removes
+
+                try:
+                        nongui_misc.dump_cache_file(os.path.join(
+                            cache_dir, CACHE_NAME + '.cpl'), dump_info)
+                except IOError, e:
+                        err = str(e)
+                        if debug:
+                                print >> sys.stderr, "Failed to dump cache: %s" % err
+                        logger.error(err)
+                return
+
+###############################################################################
+#-----------------------------------------------------------------------------#
+# Main
+#-----------------------------------------------------------------------------#
+
+def main():
+        sys.exit(checkforupdates.return_status)
+        return 0
+
+if __name__ == '__main__':
+        misc.setlocale(locale.LC_ALL, "")
+        gettext.install("pkg", "/usr/share/locale")
+        debug = False
+        set_nice = False
+        set_check_all = True
+        set_check_cache = False
+        image_dir = "/"
+        try:
+                opts, pargs = getopt.getopt(sys.argv[1:], "hdnacR:",
+                    ["help", "debug", "nice", "checkupdates-all", "checkupdates-cache",
+                    "image-dir="])
+        except getopt.GetoptError, oex:
+                print >> sys.stderr, \
+                        ("Usage: illegal option -- %s, for help use -h or --help" %
+                            oex.opt )
+                sys.exit(enumerations.UPDATES_UNDETERMINED)
+        for opt, arg in opts:
+                if opt in ("-h", "--help"):
+                        print >> sys.stderr, """\n\
+Use -h (--help) to print out help.
+Use -d (--debug) to run in debug mode.
+Use -n (--nice) to run at nice level 20.
+Use -c (--checkupdates-cache) to check for updates from cache only (output results to stdout).
+Use -R (--image-dir) to specify image directory (defaults to '/')"""
+                        sys.exit(0)
+                elif opt in ( "-n", "--nice"):
+                        set_nice = True
+                elif opt in ("-d", "--debug"):
+                        debug = True
+                elif opt in ( "-c", "--checkupdates-cache"):
+                        set_check_cache = True
+                        set_check_all = False
+                elif opt in ("-R", "--image-dir"):
+                        image_dir = arg
+
+        if os.path.isabs(sys.argv[0]):
+                app_path = sys.argv[0]
+        else:
+                cmd = os.path.join(os.getcwd(), sys.argv[0])
+                app_path = os.path.realpath(cmd)
+
+        checkforupdates = CheckForUpdates(image_dir, app_path, set_nice,
+            set_check_all, set_check_cache)
+
+        main()