Current Path : /usr/share/pyshared/DistUpgrade/ |
Current File : //usr/share/pyshared/DistUpgrade/MetaRelease.py |
# MetaRelease.py # # Copyright (c) 2004,2005 Canonical # # Author: Michael Vogt <michael.vogt@ubuntu.com> # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA import apt_pkg import ConfigParser import httplib import logging import rfc822 import os import socket import sys import time import thread import urllib2 from utils import get_lang, get_dist, get_ubuntu_flavor class Dist(object): def __init__(self, name, version, date, supported): self.name = name self.version = version self.date = date self.supported = supported self.releaseNotesURI = None self.releaseNotesHtmlUri = None self.upgradeTool = None self.upgradeToolSig = None # the server may report that the upgrade is broken currently self.upgrade_broken = None class MetaReleaseCore(object): """ A MetaReleaseCore object astracts the list of released distributions. """ DEBUG = "DEBUG_UPDATE_MANAGER" in os.environ # some constants CONF = "/etc/update-manager/release-upgrades" CONF_METARELEASE = "/etc/update-manager/meta-release" def __init__(self, useDevelopmentRelease=False, useProposed=False, forceLTS=False, forceDownload=False): self._debug("MetaRelease.__init__() useDevel=%s useProposed=%s" % (useDevelopmentRelease, useProposed)) # force download instead of sending if-modified-since self.forceDownload = forceDownload # information about the available dists self.downloading = True self.new_dist = None self.current_dist_name = get_dist() self.no_longer_supported = None # default (if the conf file is missing) self.METARELEASE_URI = "http://changelogs.ubuntu.com/meta-release" self.METARELEASE_URI_LTS = "http://changelogs.ubuntu.com/meta-release-lts" self.METARELEASE_URI_UNSTABLE_POSTFIX = "-development" self.METARELEASE_URI_PROPOSED_POSTFIX = "-development" # check the meta-release config first parser = ConfigParser.ConfigParser() if os.path.exists(self.CONF_METARELEASE): try: parser.read(self.CONF_METARELEASE) except ConfigParser.Error, e: sys.stderr.write("ERROR: failed to read '%s':\n%s" % ( self.CONF_METARELEASE, e)) return # make changing the metarelease file and the location # for the files easy if parser.has_section("METARELEASE"): sec = "METARELEASE" for k in ["URI", "URI_LTS", "URI_UNSTABLE_POSTFIX", "URI_PROPOSED_POSTFIX"]: if parser.has_option(sec, k): self._debug("%s: %s " % (self.CONF_METARELEASE, parser.get(sec,k))) setattr(self, "%s_%s" % (sec, k), parser.get(sec, k)) # check the config file first to figure if we want lts upgrades only parser = ConfigParser.ConfigParser() if os.path.exists(self.CONF): try: parser.read(self.CONF) except ConfigParser.Error, e: sys.stderr.write("ERROR: failed to read '%s':\n%s" % ( self.CONF, e)) return # now check which specific url to use if parser.has_option("DEFAULT","Prompt"): type = parser.get("DEFAULT","Prompt").lower() if (type == "never" or type == "no"): # nothing to do for this object # FIXME: what about no longer supported? self.downloading = False return elif type == "lts": self.METARELEASE_URI = self.METARELEASE_URI_LTS # needed for the _tryUpgradeSelf() code in DistUpgradeController if forceLTS: self.METARELEASE_URI = self.METARELEASE_URI_LTS # devel and proposed "just" change the postfix if useDevelopmentRelease: self.METARELEASE_URI += self.METARELEASE_URI_UNSTABLE_POSTFIX elif useProposed: self.METARELEASE_URI += self.METARELEASE_URI_PROPOSED_POSTFIX self._debug("metarelease-uri: %s" % self.METARELEASE_URI) self.metarelease_information = None if not self._buildMetaReleaseFile(): self._debug("_buildMetaReleaseFile failed") return # we start the download thread here and we have a timeout thread.start_new_thread(self.download, ()) #t=thread.start_new_thread(self.check, ()) def _buildMetaReleaseFile(self): # build the metarelease_file name self.METARELEASE_FILE = os.path.join("/var/lib/update-manager/", os.path.basename(self.METARELEASE_URI)) # check if we can write to the global location, if not, # write to homedir try: open(self.METARELEASE_FILE,"a") except IOError, e: cache_dir = os.getenv( "XDG_CACHE_HOME", os.path.expanduser("~/.cache")) path = os.path.join(cache_dir, 'update-manager-core') if not os.path.exists(path): try: os.mkdir(path) except OSError, e: sys.stderr.write("mkdir() failed: '%s'" % e) return False self.METARELEASE_FILE = os.path.join(path,os.path.basename(self.METARELEASE_URI)) # if it is empty, remove it to avoid I-M-S hits on empty file try: if os.path.getsize(self.METARELEASE_FILE) == 0: os.unlink(self.METARELEASE_FILE) except Exception, e: pass return True def dist_no_longer_supported(self, dist): """ virtual function that is called when the distro is no longer supported """ self.no_longer_supported = dist def new_dist_available(self, dist): """ virtual function that is called when a new distro release is available """ self.new_dist = dist def parse(self): self._debug("MetaRelease.parse()") current_dist_name = self.current_dist_name self._debug("current dist name: '%s'" % current_dist_name) current_dist = None dists = [] # parse the metarelease_information file index_tag = apt_pkg.TagFile(self.metarelease_information) step_result = index_tag.step() while step_result: if "Dist" in index_tag.section: name = index_tag.section["Dist"] self._debug("found distro name: '%s'" % name) rawdate = index_tag.section["Date"] date = time.mktime(rfc822.parsedate(rawdate)) supported = int(index_tag.section["Supported"]) version = index_tag.section["Version"] # add the information to a new date object dist = Dist(name, version, date,supported) if "ReleaseNotes" in index_tag.section: dist.releaseNotesURI = index_tag.section["ReleaseNotes"] lang = get_lang() if lang: dist.releaseNotesURI += "?lang=%s" % lang if "ReleaseNotesHtml" in index_tag.section: dist.releaseNotesHtmlUri = index_tag.section["ReleaseNotesHtml"] query = self._get_release_notes_uri_query_string(dist) if query: dist.releaseNotesHtmlUri += query if "UpgradeTool" in index_tag.section: dist.upgradeTool = index_tag.section["UpgradeTool"] if "UpgradeToolSignature" in index_tag.section: dist.upgradeToolSig = index_tag.section["UpgradeToolSignature"] if "UpgradeBroken" in index_tag.section: dist.upgrade_broken = index_tag.section["UpgradeBroken"] dists.append(dist) if name == current_dist_name: current_dist = dist step_result = index_tag.step() # first check if the current runing distro is in the meta-release # information. if not, we assume that we run on something not # supported and silently return if current_dist is None: self._debug("current dist not found in meta-release file\n") return False # then see what we can upgrade to upgradable_to = "" for dist in dists: if dist.date > current_dist.date: upgradable_to = dist self._debug("new dist: %s" % upgradable_to) break # only warn if unsupported and a new dist is available (because # the development version is also unsupported) if upgradable_to != "" and not current_dist.supported: self.dist_no_longer_supported(current_dist) if upgradable_to != "": self.new_dist_available(upgradable_to) # parsing done and sucessfully return True # the network thread that tries to fetch the meta-index file # can't touch the gui, runs as a thread def download(self): self._debug("MetaRelease.download()") lastmodified = 0 req = urllib2.Request(self.METARELEASE_URI) # make sure that we always get the latest file (#107716) req.add_header("Cache-Control", "No-Cache") req.add_header("Pragma", "no-cache") if os.access(self.METARELEASE_FILE, os.W_OK): try: lastmodified = os.stat(self.METARELEASE_FILE).st_mtime except OSError, e: pass if lastmodified > 0 and not self.forceDownload: req.add_header("If-Modified-Since", time.asctime(time.gmtime(lastmodified))) try: # open uri=urllib2.urlopen(req, timeout=20) # sometime there is a root owned meta-relase file # there, try to remove it so that we get it # with proper permissions if (os.path.exists(self.METARELEASE_FILE) and not os.access(self.METARELEASE_FILE,os.W_OK)): try: os.unlink(self.METARELEASE_FILE) except OSError,e: print "Can't unlink '%s' (%s)" % (self.METARELEASE_FILE,e) # we may get exception here on e.g. disk full try: f=open(self.METARELEASE_FILE,"w+") for line in uri.readlines(): f.write(line) f.flush() f.seek(0,0) self.metarelease_information=f except IOError, e: pass uri.close() # http error except urllib2.HTTPError, e: # mvo: only reuse local info on "not-modified" if e.code == 304 and os.path.exists(self.METARELEASE_FILE): self._debug("reading file '%s'" % self.METARELEASE_FILE) self.metarelease_information=open(self.METARELEASE_FILE,"r") else: self._debug("result of meta-release download: '%s'" % e) # generic network error except (urllib2.URLError, httplib.BadStatusLine, socket.timeout), e: self._debug("result of meta-release download: '%s'" % e) # now check the information we have if self.metarelease_information != None: self._debug("have self.metarelease_information") try: self.parse() except: logging.exception("parse failed for '%s'" % self.METARELEASE_FILE) # no use keeping a broken file around os.remove(self.METARELEASE_FILE) # we don't want to keep a meta-release file around when it # has a "Broken" flag, this ensures we are not bitten by # I-M-S/cache issues if self.new_dist and self.new_dist.upgrade_broken: os.remove(self.METARELEASE_FILE) else: self._debug("NO self.metarelease_information") self.downloading = False def _get_release_notes_uri_query_string(self, dist): q = "?" # get the lang lang = get_lang() if lang: q += "lang=%s&" % lang # get the os os = get_ubuntu_flavor() q += "os=%s&" % os # get the version to upgrade to q += "ver=%s" % dist.version return q def _debug(self, msg): if self.DEBUG: sys.stderr.write(msg+"\n") if __name__ == "__main__": meta = MetaReleaseCore(False, False)