9852 UMN and PM check for updates should use plan_update_all
authorPadraig O'Briain <padraig.obriain@sun.com>
Thu, 16 Jul 2009 18:19:39 +0100
changeset 1228 07ef6e279231
parent 1227 5ebefe59230e
child 1229 4e775e2baecd
9852 UMN and PM check for updates should use plan_update_all
src/checkforupdates.py
src/gui/modules/cache.py
src/gui/modules/enumerations.py
src/gui/modules/misc.py
src/packagemanager.py
src/pkgdefs/SUNWipkg-gui/prototype
src/pkgdefs/SUNWipkg-um/prototype
src/setup.py
src/um/update-refresh.sh
src/updatemanager.py
src/updatemanagernotifier.py
--- a/src/checkforupdates.py	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/checkforupdates.py	Thu Jul 16 18:19:39 2009 +0100
@@ -25,60 +25,149 @@
 
 import os
 import sys
-import gettext
+import time
+import getopt
 
 import pkg.client.progress as progress
-import pkg.misc as misc
+import pkg.client.api_errors as api_errors
 import pkg.gui.misc as gui_misc
+import pkg.gui.enumerations as enumerations
 from pkg.client import global_settings
+from cPickle import UnpicklingError
 
-UPDATES_AVAILABLE = 0
-NO_UPDATES_AVAILABLE = 1
-ERROR_OCCURRED = 2
-PKG_CLIENT_NAME = "updatemanagernotifier"
-
+PKG_CLIENT_NAME = "check_for_updates"
+CACHE_VERSION =  1
+CACHE_NAME = ".last_refresh_cache"
 
-# Put _() in the global namespace
-import __builtin__
-__builtin__._ = gettext.gettext
-
-def check_for_updates(image_directory):
-        os.nice(20)
+def __check_for_updates(image_directory, nice):
+        if nice:
+                os.nice(20)
         global_settings.client_name = PKG_CLIENT_NAME
         pr = progress.NullProgressTracker()
 
         api_obj = gui_misc.get_api_object(image_directory, pr, None)
-        api_obj.refresh()
+        if api_obj == None:
+                return enumerations.UPDATES_UNDETERMINED
+        ret = __check_last_refresh(api_obj)
+        if ret != enumerations.UPDATES_UNDETERMINED:
+                return ret
+        elif debug:
+                print "Updates undetermined in check_last_refresh"
 
-        pkg_upgradeable = None
-        for pkg, state in misc.get_inventory_list(api_obj.img, [],
-           all_known=True, all_versions=False):
-                if state["upgradable"] and state["state"] == "installed":
-                        pkg_upgradeable = pkg
-                        break
-                
-        if pkg_upgradeable != None:
+        try:
+                stuff_to_do, opensolaris_image, cre = \
+                    api_obj.plan_update_all(sys.argv[0],
+                    refresh_catalogs = True,
+                    noexecute = True, force = True, verbose = False) 
+        except api_errors.ApiException, e:
+                if debug:
+                        print "Exception occurred: ", str(e)
+                return enumerations.UPDATES_UNDETERMINED
+        if debug:
+                print "stuff_to_do: ", stuff_to_do
+                print "opensolaris_image: ", opensolaris_image
+                print "cre: ", cre
+
+        if cre and not cre.succeeded:
+                if debug:
+                        print "Error occurred in plan_update_all"
+                return enumerations.UPDATES_UNDETERMINED
+        __dump_updates_available(api_obj, stuff_to_do)
+        if stuff_to_do:
                 if debug:
                         print "Updates Available"
-                sys.exit(UPDATES_AVAILABLE)
+                return enumerations.UPDATES_AVAILABLE
         else:
                 if debug:
                         print "No updates Available"
-                sys.exit(NO_UPDATES_AVAILABLE)
+                return enumerations.NO_UPDATES_AVAILABLE
+
+def __check_last_refresh(api_obj):
+        cache_dir = gui_misc.get_cache_dir(api_obj)
+        if not cache_dir:
+                return enumerations.UPDATES_UNDETERMINED
+        try:
+                info = gui_misc.read_cache_file(os.path.join(
+                    cache_dir, CACHE_NAME + '.cpl'))
+                if info.get("version") != CACHE_VERSION:
+                        return enumerations.UPDATES_UNDETERMINED
+                old_publishers = info.get("publishers")
+                count = 0
+                for p in 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
+                if info.get("updates_available"):
+                        return enumerations.UPDATES_AVAILABLE
+                else:
+                        return enumerations.NO_UPDATES_AVAILABLE
+
+        except (UnpicklingError, IOError):
+                return enumerations.UPDATES_UNDETERMINED
+
+def __dump_updates_available(api_obj, stuff_to_do):
+        cache_dir = gui_misc.get_cache_dir(api_obj)
+        if not cache_dir:
+                return
+        publisher_list = {}
+        for p in api_obj.get_publishers():
+                if p.disabled:
+                        continue
+                publisher_list[p.prefix] = p.last_refreshed
+        if debug:
+                print "publisher_list:", publisher_list
+        dump_info = {}
+        dump_info["version"] = CACHE_VERSION
+        dump_info["updates_available"] = stuff_to_do
+        dump_info["publishers"] = publisher_list
+
+        try:
+                gui_misc.dump_cache_file(os.path.join(
+                    cache_dir, CACHE_NAME + '.cpl'), dump_info)
+        except IOError, e:
+                if debug:
+                        print "Failed to dump cache: %s" % str(e)
+
+        return
 
 ###############################################################################
 #-----------------------------------------------------------------------------#
 # Main
 #-----------------------------------------------------------------------------#
 
-def main(image_directory):
-        check_for_updates(image_directory)
-        return ERROR_OCCURRED
+def main(image_directory, nice):
+        return __check_for_updates(image_directory, nice)
 
 if __name__ == '__main__':
         debug = False
-        if len(sys.argv) != 2:
-                print "One argument, image directory must be specified"
-                sys.exit(ERROR_OCCURRED)
-        image_dir = sys.argv[1]
-        main(image_dir)
+        set_nice = False
+        try:
+                opts, pargs = getopt.getopt(sys.argv[1:], "n", ["nice"])
+        except getopt.GetoptError, ex:
+                print "Usage: illegal option -- %s" % ex.opt
+                sys.exit(enumerations.UPDATES_UNDETERMINED)
+        if len(pargs) != 1:
+                print "Usage: One argument, image directory must be specified"
+                sys.exit(enumerations.UPDATES_UNDETERMINED)
+        image_dir = pargs[0]
+        for opt, args in opts:
+                if debug:
+                        print "opt: ", opt
+                        print "args: ", args
+                if opt in ( "-n", "--nice"):
+                        set_nice = True
+        if debug:
+                print "Start check_for_updates for: ", image_dir, set_nice
+                a = time.time()
+        return_value = main(image_dir, set_nice)
+        if debug:
+                print "time taken: ", time.time() - a
+        sys.exit(return_value)
--- a/src/gui/modules/cache.py	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/gui/modules/cache.py	Thu Jul 16 18:19:39 2009 +0100
@@ -23,7 +23,6 @@
 # Use is subject to license terms.
 #
 
-import cPickle
 import os
 import sys
 try:
@@ -51,8 +50,9 @@
                 self.update_available_icon = update_available_icon
                 self.installed_icon = installed_icon
                 self.not_installed_icon = not_installed_icon
-                self.category_icon = gui_misc.get_pixbuf_from_path(application_dir +
-                    "/usr/share/package-manager/", "legend_newupdate")
+                self.category_icon = gui_misc.get_pixbuf_from_path(
+                    os.path.join(application_dir,
+                    "usr/share/package-manager/"), "legend_newupdate")
 
         def check_if_cache_uptodate(self, publisher):
                 try:
@@ -82,22 +82,11 @@
                 return True
 
         def __get_cache_dir(self):
-                img = self.api_o.img
-                cache_dir = os.path.join(img.imgdir, "gui_cache")
-                try:
-                        self.__mkdir(cache_dir)
-                except OSError:
-                        cache_dir = None
-                return cache_dir
-
-        @staticmethod
-        def __mkdir(directory_path):
-                if not os.path.isdir(directory_path):
-                        os.makedirs(directory_path)
+                return gui_misc.get_cache_dir(self.api_o)
 
         def get_index_timestamp(self):
                 img = self.api_o.img
-                index_path = "%s/state/installed" % (img.imgdir)
+                index_path = os.path.join(img.imgdir, "state/installed")
                 try:
                         return os.path.getmtime(index_path)
                 except (OSError, IOError):
@@ -121,7 +110,8 @@
                 dump_info["be_name"] = None
 
                 try:
-                        self.__dump_cache_file(os.path.join(cache_dir, publisher+".cpl"),
+                        gui_misc.dump_cache_file(
+                            os.path.join(cache_dir, publisher+".cpl"),
                             dump_info)
                 except IOError:
                         #Silently return, as probably user doesn't have permissions or
@@ -141,7 +131,8 @@
                 dump_info["be_name"] = self.__get_active_be_name()
 
                 try:
-                        self.__dump_cache_file(os.path.join(cache_dir, publisher+".cpl"),
+                        gui_misc.dump_cache_file(
+                            os.path.join(cache_dir, publisher+".cpl"),
                             dump_info)
                         self.__dump_category_list(publisher, category_list)
                         self.__dump_application_list(publisher, application_list)
@@ -167,7 +158,7 @@
                         cat["visible"] = category[enumerations.CATEGORY_VISIBLE]
                         cat["section_list"] = category[enumerations.SECTION_LIST_OBJECT]
                         categories.append(cat)
-                self.__dump_cache_file(os.path.join(cache_dir, 
+                gui_misc.dump_cache_file(os.path.join(cache_dir, 
                     publisher+"_categories.cpl"), categories)
 
         def __dump_application_list(self, publisher, application_list):
@@ -189,7 +180,7 @@
                             application[enumerations.CATEGORY_LIST_COLUMN]
                         app["pkg_authority"] = application[enumerations.AUTHORITY_COLUMN]
                         apps.append(app)
-                self.__dump_cache_file(
+                gui_misc.dump_cache_file(
                     os.path.join(cache_dir, publisher+"_packages.cpl"), apps)
 
         def __dump_section_list(self, publisher, section_list):
@@ -204,21 +195,22 @@
                         sec["subcategory"] = section[enumerations.SECTION_SUBCATEGORY]
                         sec["enabled"] = section[enumerations.SECTION_ENABLED]
                         sections.append(sec)
-                self.__dump_cache_file(os.path.join(cache_dir, publisher+"_sections.cpl"),
+                gui_misc.dump_cache_file(
+                    os.path.join(cache_dir, publisher+"_sections.cpl"),
                     sections)
 
         def __load_cache_info(self, publisher):
                 cache_dir = self.__get_cache_dir()
                 if not cache_dir:
                         return None
-                info = self.__read_cache_file(os.path.join(cache_dir, publisher+".cpl"))
+                info = gui_misc.read_cache_file(os.path.join(cache_dir, publisher+".cpl"))
                 return info
 
         def load_category_list(self, publisher, category_list):
                 cache_dir = self.__get_cache_dir()
                 if not cache_dir:
                         return
-                categories = self.__read_cache_file(
+                categories = gui_misc.read_cache_file(
                     os.path.join(cache_dir, publisher+"_categories.cpl"))
                 cat_count = 0
                 for cat in categories:
@@ -244,7 +236,7 @@
                 cache_dir = self.__get_cache_dir()
                 if not cache_dir:
                         return
-                applications = self.__read_cache_file(
+                applications = gui_misc.read_cache_file(
                     os.path.join(cache_dir, publisher+"_packages.cpl"))
                 app_count = len(application_list)
                 if app_count > 0:
@@ -286,7 +278,7 @@
                 cache_dir = self.__get_cache_dir()
                 if not cache_dir:
                         return
-                sections = self.__read_cache_file(
+                sections = gui_misc.read_cache_file(
                     os.path.join(cache_dir, publisher+"_sections.cpl"))
                 sec_count = 0
                 for sec in sections:
@@ -324,19 +316,6 @@
                                         return name
                 return None
 
-        @staticmethod
-        def __read_cache_file(file_path):
-                fh = open(file_path, 'r')
-                data = cPickle.load(fh)
-                fh.close()
-                return data
-
-        @staticmethod
-        def __dump_cache_file(file_path, data):
-                fh = open(file_path,"w")
-                cPickle.dump(data, fh, True)
-                fh.close()
-
         def __dump_search_completion_info(self, completion_list):
                 cache_dir = self.__get_cache_dir()
                 if not cache_dir:
@@ -347,7 +326,7 @@
                         txt["text"] = text[0]
                         texts.append(txt)
                 try:
-                        self.__dump_cache_file(
+                        gui_misc.dump_cache_file(
                             os.path.join(cache_dir, ".__search__completion.cpl"), texts)
                 except IOError:
                         return
@@ -358,7 +337,7 @@
                         return
                 texts = []
                 try:
-                        texts = self.__read_cache_file(
+                        texts = gui_misc.read_cache_file(
                             os.path.join(cache_dir, ".__search__completion.cpl"))
                 except IOError:
                         return gtk.ListStore(str)
--- a/src/gui/modules/enumerations.py	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/gui/modules/enumerations.py	Thu Jul 16 18:19:39 2009 +0100
@@ -115,4 +115,10 @@
 PUBLISHER_ENABLED,
 ) = range(7)
 
+# Return values from /usr/lib/pm-checkforupdates
+(
+UPDATES_AVAILABLE,
+NO_UPDATES_AVAILABLE,
+UPDATES_UNDETERMINED
+) = range(3)
 
--- a/src/gui/modules/misc.py	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/gui/modules/misc.py	Thu Jul 16 18:19:39 2009 +0100
@@ -28,6 +28,7 @@
                                           # However we need to have an exception rule
                                           # where we will cut all but three last parts.
 
+import cPickle
 import os
 import sys
 try:
@@ -119,6 +120,19 @@
                 return pkg_name[index:].strip("/") + converted_name
         return pkg_name_bk
 
+def get_cache_dir(api_object):
+        img = api_object.img
+        cache_dir = os.path.join(img.imgdir, "gui_cache")
+        try:
+                __mkdir(cache_dir)
+        except OSError:
+                cache_dir = None
+        return cache_dir
+
+def __mkdir(directory_path):
+        if not os.path.isdir(directory_path):
+                os.makedirs(directory_path)
+
 def get_api_object(img_dir, progtrack, parent_dialog):
         api_o = None
         message = None
@@ -135,11 +149,22 @@
                 if parent_dialog != None:
                         error_occurred(parent_dialog,
                             message, _("API Error"))
+                        sys.exit(0)
                 else:
                         print message
-                sys.exit(0)
         return api_o
 
+def read_cache_file(file_path):
+        fh = open(file_path, 'r')
+        data = cPickle.load(fh)
+        fh.close()
+        return data
+
+def dump_cache_file(file_path, data):
+        fh = open(file_path,"w")
+        cPickle.dump(data, fh, True)
+        fh.close()
+
 def error_occurred(parent, error_msg, msg_title = None,
     msg_type=gtk.MESSAGE_ERROR, use_markup = False):
         msgbox = gtk.MessageDialog(parent =
--- a/src/packagemanager.py	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/packagemanager.py	Thu Jul 16 18:19:39 2009 +0100
@@ -62,6 +62,7 @@
 STATUS_COLUMN_INDEX = 2   # Index of Status Column in Application TreeView
 
 PKG_CLIENT_NAME = "packagemanager"
+CHECK_FOR_UPDATES = "/usr/lib/pm-checkforupdates"
 
 # Location for themable icons
 ICON_LOCATION = "usr/share/package-manager/icons"
@@ -89,6 +90,7 @@
 import getopt
 import pwd
 import os
+import subprocess
 import sys
 import time
 import locale
@@ -836,15 +838,6 @@
                 self.api_search_error_dialog.show()
                 self.api_search_button.grab_focus()
 
-        def __get_repo_publishers(self):
-                repo_pub_dict = {}
-                pubs = self.api_o.get_publishers()
-                for pub in pubs:
-                        repo = pub.selected_repository
-                        origin = repo.origins[0]
-                        repo_pub_dict[origin.uri] = pub.prefix
-                return repo_pub_dict
-
         def __on_url(self, view, link):
                 # Handle mouse over events on links and reset when not on link
                 if link == None or link == "":
@@ -3372,7 +3365,6 @@
                 gobject.idle_add(self.w_updateall_menuitem.set_sensitive, False)
                 update_available = self.__check_if_updates_available()
                 gobject.idle_add(self.__g_enable_disable_update_all, update_available)
-                gobject.idle_add(self.__show_info_after_catalog_load)
                 return False
 
         def __show_info_after_catalog_load(self):
@@ -3385,20 +3377,22 @@
                         self.__show_licenses()
 
         def __check_if_updates_available(self):
+                # First we load the catalogs so package info can work
                 try:
                         self.catalog_loaded = False
                         self.api_o.refresh()
                         self.catalog_loaded = True
-                        res = self.__get_inventory_list([], False, False)
-                        for pfmri, state in res:
-                                if state["upgradable"]:
-                                        self.pylintstub = pfmri
-                                        return True
-
                 except api_errors.InventoryException:
                         gobject.idle_add(self.__set_empty_details_panel)
                         return False
-                return False
+                gobject.idle_add(self.__show_info_after_catalog_load)
+
+                return_code = subprocess.call([CHECK_FOR_UPDATES,
+                    self.image_directory])
+                if return_code == enumerations.UPDATES_AVAILABLE:
+                        return True
+                else:
+                        return False
 
         def __g_enable_disable_update_all(self, update_available):
                 self.w_updateall_button.set_sensitive(update_available)
--- a/src/pkgdefs/SUNWipkg-gui/prototype	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/pkgdefs/SUNWipkg-gui/prototype	Thu Jul 16 18:19:39 2009 +0100
@@ -8,6 +8,7 @@
 d none usr/bin 755 root bin
 f none usr/bin/packagemanager 755 root bin
 d none usr/lib 755 root bin
+f none usr/lib/pm-checkforupdates 755 root bin
 f none usr/lib/pm-launch 755 root bin
 d none usr/lib/python2.4 755 root bin
 d none usr/lib/python2.4/vendor-packages 755 root bin
--- a/src/pkgdefs/SUNWipkg-um/prototype	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/pkgdefs/SUNWipkg-um/prototype	Thu Jul 16 18:19:39 2009 +0100
@@ -17,7 +17,6 @@
 d none usr/lib 755 root bin
 d none usr/lib/update-manager 755 root bin
 f none usr/lib/update-manager/update-refresh.sh 755 root bin
-f none usr/lib/um-checkforupdates 755 root bin
 f none usr/lib/updatemanagernotifier 755 root bin
 d none usr/share 755 root sys
 d none usr/share/applications 755 root other
--- a/src/setup.py	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/setup.py	Thu Jul 16 18:19:39 2009 +0100
@@ -163,7 +163,7 @@
         lib_dir: [
                 ['depot.py', 'pkg.depotd'],
                 ['updatemanagernotifier.py', 'updatemanagernotifier'],
-                ['checkforupdates.py', 'um-checkforupdates'],
+                ['checkforupdates.py', 'pm-checkforupdates'],
                 ['launch.py', 'pm-launch'],
                 ],
         svc_method_dir: [
--- a/src/um/update-refresh.sh	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/um/update-refresh.sh	Thu Jul 16 18:19:39 2009 +0100
@@ -26,7 +26,10 @@
 # This script is run as part of a cron job at 0:30, 9:30, 12:30, 18:30, 21:30
 # 1. Refresh the IPS catalog
 #
-# 2. Fetch updates to Packagemanager's Start Page files:
+# 2. Call /usr/lib/pm-checkforupdates to check and cache whether updates
+#    are available
+#
+# 3. Fetch updates to Packagemanager's Start Page files:
 #    startpagebase-<locale prefix>.tar.gz
 #
 # From the URL in the specifed Packagemanager Gconf key:
@@ -58,6 +61,9 @@
 
 pkg refresh 2>/dev/null
 
+# Check and cache whether updates are available
+/usr/lib/pm-checkforupdates --nice $image_dir
+
 # Fetch updates to Packagemanager's Start Page files
 TAR="/usr/gnu/bin/tar"
 WGET="/usr/bin/wget"
--- a/src/updatemanager.py	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/updatemanager.py	Thu Jul 16 18:19:39 2009 +0100
@@ -26,6 +26,7 @@
 
 import getopt
 import os
+import subprocess
 import sys
 import time
 import locale
@@ -63,6 +64,7 @@
 
 PKG_ICON_LOCATION = "usr/share/package-manager/icons"
 ICON_LOCATION = "usr/share/update-manager/icons"
+CHECK_FOR_UPDATES = "/usr/lib/pm-checkforupdates"
 PKG_CLIENT_NAME = "updatemanager" # API client name
 SELECTION_CHANGE_LIMIT = 0.5    # Time limit in seconds to cancel selection updates
 IND_DELAY = 0.05                # Time delay for printing index progress
@@ -372,6 +374,15 @@
                         gobject.TYPE_STRING,      # UM_STEM                        
                         )
 
+                # Use check_for_updates to determine whether updates
+                # are available
+                return_code = subprocess.call([CHECK_FOR_UPDATES,
+                    self.__get_image_path()])
+                if return_code == enumerations.NO_UPDATES_AVAILABLE:
+                        self.progress_stop_thread = True
+                        gobject.idle_add(self.__display_noupdates)
+                        return
+
                 self.api_obj = self.__get_api_obj()
                 image_obj = self.api_obj.img
                 #TODO: This part is required so misc.get_inventory_list
--- a/src/updatemanagernotifier.py	Thu Jul 16 09:35:26 2009 -0700
+++ b/src/updatemanagernotifier.py	Thu Jul 16 18:19:39 2009 +0100
@@ -51,6 +51,7 @@
 import pkg.client.progress as progress
 import pkg.misc as misc
 import pkg.gui.misc as gui_misc
+import pkg.gui.enumerations as enumerations
 
 # Put _() in the global namespace
 import __builtin__
@@ -62,7 +63,7 @@
 IMAGE_DIRECTORY_DEFAULT = "/"
 LASTCHECK_DIR_NAME = os.path.join(os.path.expanduser("~"),'.updatemanager/notify')
 IMAGE_DIR_COMMAND = "svcprop -p update/image_dir svc:/application/pkg/update"
-CHECK_FOR_UPDATES = "/usr/lib/um-checkforupdates"
+CHECK_FOR_UPDATES = "/usr/lib/pm-checkforupdates"
 
 ICON_LOCATION = "/usr/share/update-manager/icons"
 NOTIFY_ICON_NAME = "notify_update"
@@ -285,11 +286,11 @@
                 if len(image_directory) == 0:
                         image_directory = IMAGE_DIRECTORY_DEFAULT
                 return_code = subprocess.call([CHECK_FOR_UPDATES,
-                    image_directory])
+                    '--nice', image_directory])
                 if debug:
                         print "return from subprocess is %d" % return_code
                 self.set_last_check_time()
-                if return_code == 0:
+                if return_code == enumerations.UPDATES_AVAILABLE:
                         self.show_status_icon(True)
                 else:
                         self.show_status_icon(False)