summaryrefslogtreecommitdiff
path: root/UpdateManager
diff options
context:
space:
mode:
authorglatzor@ubuntu.com <>2006-08-02 18:37:59 +0200
committerglatzor@ubuntu.com <>2006-08-02 18:37:59 +0200
commit0a5aacc19bfe31eda221e2f6476712097d693a06 (patch)
tree8995ea136686477cc31a4e240b6464ea0c6d6ed0 /UpdateManager
parent70bf03a91044f338aed8acd819c15dc8d5968103 (diff)
parentdde2af913b3f26958f93f604d7a75e61e64ec7f1 (diff)
downloadpython-apt-0a5aacc19bfe31eda221e2f6476712097d693a06.tar.gz
* merge with main
Diffstat (limited to 'UpdateManager')
-rw-r--r--UpdateManager/Common/DistInfo.py76
-rw-r--r--UpdateManager/Common/aptsources.py672
-rw-r--r--UpdateManager/DistUpgradeFetcher.py9
-rw-r--r--UpdateManager/MetaRelease.py10
-rw-r--r--UpdateManager/UpdateManager.py151
5 files changed, 824 insertions, 94 deletions
diff --git a/UpdateManager/Common/DistInfo.py b/UpdateManager/Common/DistInfo.py
index 102b981c..7d2d37ad 100644
--- a/UpdateManager/Common/DistInfo.py
+++ b/UpdateManager/Common/DistInfo.py
@@ -28,21 +28,30 @@ import ConfigParser
_ = gettext.gettext
class Suite:
- name = None
- description = None
- base_uri = None
- repository_type = None
- components = None
+ def __init__(self):
+ self.name = None
+ self.child = False
+ self.match_name = None
+ self.description = None
+ self.base_uri = None
+ self.type = None
+ self.components = {}
+ self.children = []
+ self.match_uri = None
+ self.distribution = None
+ self.available = True
class Component:
- name = None
- description = None
- enabled = None
+ def __init__(self):
+ self.name = ""
+ self.description = ""
+ self.description_long = ""
+ self.enabled = None
class DistInfo:
def __init__(self,
dist = None,
- base_dir = "/usr/share/update-manager/dists"):
+ base_dir = "/usr/share/update-manager/channels"):
self.metarelease_uri = ''
self.suites = []
@@ -73,43 +82,68 @@ class DistInfo:
elif field == 'Suite':
if suite:
if component:
- suite.components.append (component)
+ suite.components["%s" % component.name] = \
+ (component.description, component.enabled,
+ component.description_long)
component = None
self.suites.append (suite)
suite = Suite ()
suite.name = value
- suite.components = []
+ suite.distribution = dist
+ suite.match_name = "^%s$" % value
+ elif field == 'MatchName':
+ suite.match_name = value
+ elif field == 'ParentSuite':
+ suite.child = True
+ for nanny in self.suites:
+ if nanny.name == value:
+ nanny.children.append(suite)
+ elif field == 'Available':
+ suite.available = value
elif field == 'RepositoryType':
- suite.repository_type = value
+ suite.type = value
elif field == 'BaseURI':
suite.base_uri = value
+ suite.match_uri = value
+ elif field == 'MatchURI':
+ suite.match_uri = value
elif field == 'Description':
suite.description = _(value)
elif field == 'Component':
if component:
- suite.components.append (component)
+ suite.components["%s" % component.name] = \
+ (component.description, component.enabled,
+ component.description_long)
component = Component ()
component.name = value
elif field == 'Enabled':
component.enabled = bool(int(value))
elif field == 'CompDescription':
component.description = _(value)
+ elif field == 'CompDescriptionLong':
+ component.description_long = _(value)
if suite:
if component:
- suite.components.append (component)
+ suite.components["%s" % component.name] = \
+ (component.description, component.enabled,
+ component.description_long)
component = None
self.suites.append (suite)
suite = None
if __name__ == "__main__":
- d = DistInfo ("Debian", "../../channels")
+ d = DistInfo ("Ubuntu", "../../channels")
print d.changelogs_uri
for suite in d.suites:
- print suite.name
- print suite.description
- print suite.base_uri
+ print "\nSuite: %s" % suite.name
+ print "Desc: %s" % suite.description
+ print "BaseURI: %s" % suite.base_uri
+ print "MatchURI: %s" % suite.match_uri
for component in suite.components:
- print component.name
- print component.description
- print component.enabled
+ print " %s - %s - %s - %s" % (component,
+ suite.components[component][0],
+ suite.components[component][1],
+ suite.components[component][2])
+ for child in suite.children:
+ print " %s" % child.description
diff --git a/UpdateManager/Common/aptsources.py b/UpdateManager/Common/aptsources.py
new file mode 100644
index 00000000..3d317b15
--- /dev/null
+++ b/UpdateManager/Common/aptsources.py
@@ -0,0 +1,672 @@
+# aptsource.py.in - parse sources.list
+#
+# Copyright (c) 2004,2005 Canonical
+# 2004 Michiel Sikkes
+#
+# Author: Michiel Sikkes <michiel@eyesopened.nl>
+# Michael Vogt <mvo@debian.org>
+#
+# 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 string
+import gettext
+import re
+import apt_pkg
+import glob
+import shutil
+import time
+import os.path
+
+#import pdb
+
+from UpdateManager.Common.DistInfo import DistInfo
+
+
+# some global helpers
+def is_mirror(master_uri, compare_uri):
+ """check if the given add_url is idential or a mirror of orig_uri
+ e.g. master_uri = archive.ubuntu.com
+ compare_uri = de.archive.ubuntu.com
+ -> True
+ """
+ # remove traling spaces and "/"
+ compare_uri = compare_uri.rstrip("/ ")
+ master_uri = master_uri.rstrip("/ ")
+ # uri is identical
+ if compare_uri == master_uri:
+ #print "Identical"
+ return True
+ # add uri is a master site and orig_uri has the from "XX.mastersite"
+ # (e.g. de.archive.ubuntu.com)
+ try:
+ compare_srv = compare_uri.split("//")[1]
+ master_srv = master_uri.split("//")[1]
+ #print "%s == %s " % (add_srv, orig_srv)
+ except IndexError: # ok, somethings wrong here
+ #print "IndexError"
+ return False
+ # remove the leading "<country>." (if any) and see if that helps
+ if "." in compare_srv and \
+ compare_srv[compare_srv.index(".")+1:] == master_srv:
+ #print "Mirror"
+ return True
+ return False
+
+def uniq(s):
+ """ simple and efficient way to return uniq list """
+ return list(set(s))
+
+
+
+# actual source.list entries
+class SourceEntry:
+
+ def __init__(self, line,file=None):
+ self.invalid = False
+ self.disabled = False
+ self.type = ""
+ self.uri = ""
+ self.dist = ""
+ self.comps = []
+ self.comment = ""
+ self.line = line
+ if file == None:
+ file = apt_pkg.Config.FindDir("Dir::Etc")+apt_pkg.Config.Find("Dir::Etc::sourcelist")
+ self.file = file
+ self.parse(line)
+ self.template = None
+ self.children = []
+
+ # works mostely like split but takes [] into account
+ def mysplit(self, line):
+ line = string.strip(line)
+ pieces = []
+ tmp = ""
+ # we are inside a [..] block
+ p_found = False
+ space_found = False
+ for i in range(len(line)):
+ if line[i] == "[":
+ p_found=True
+ tmp += line[i]
+ elif line[i] == "]":
+ p_found=False
+ tmp += line[i]
+ elif space_found and not line[i].isspace(): # we skip one or more space
+ space_found = False
+ pieces.append(tmp)
+ tmp = line[i]
+ elif line[i].isspace() and not p_found: # found a whitespace
+ space_found = True
+ else:
+ tmp += line[i]
+ # append last piece
+ if len(tmp) > 0:
+ pieces.append(tmp)
+ return pieces
+
+
+ # parse a given source line and split it into the fields we need
+ def parse(self,line):
+ line = string.strip(self.line)
+ #print line
+ # check if the source is enabled/disabled
+ if line == "" or line == "#": # empty line
+ self.invalid = True
+ return
+ if line[0] == "#":
+ self.disabled = True
+ pieces = string.split(line[1:])
+ # if it looks not like a disabled deb line return
+ if not (pieces[0] == "deb" or pieces[0] == "deb-src"):
+ self.invalid = True
+ return
+ else:
+ line = line[1:]
+ # check for another "#" in the line (this is treated as a comment)
+ i = line.find("#")
+ if i > 0:
+ self.comment = line[i+1:]
+ line = line[:i]
+ # source is ok, split it and see what we have
+ pieces = self.mysplit(line)
+ # Sanity check
+ if len(pieces) < 3:
+ self.invalid = True
+ return
+ # Type, deb or deb-src
+ self.type = string.strip(pieces[0])
+ # Sanity check
+ if self.type not in ("deb", "deb-src"):
+ self.invalid = True
+ return
+ # URI
+ self.uri = string.strip(pieces[1])
+ if len(self.uri) < 1:
+ self.invalid = True
+ # distro and components (optional)
+ # Directory or distro
+ self.dist = string.strip(pieces[2])
+ if len(pieces) > 3:
+ # List of components
+ self.comps = pieces[3:]
+ else:
+ self.comps = []
+
+ #print self.__dict__
+
+
+ # set enabled/disabled
+ def set_enabled(self, new_value):
+ self.disabled = not new_value
+ # enable, remove all "#" from the start of the line
+ if new_value == True:
+ i=0
+ self.line = string.lstrip(self.line)
+ while self.line[i] == "#":
+ i += 1
+ self.line = self.line[i:]
+ else:
+ # disabled, add a "#"
+ if string.strip(self.line)[0] != "#":
+ self.line = "#" + self.line
+
+ def __str__(self):
+ """ debug helper """
+ return self.str().strip()
+
+ def str(self):
+ """ return the current line as string """
+ if self.invalid:
+ return self.line
+ line = ""
+ if self.disabled:
+ line = "# "
+ line += "%s %s %s" % (self.type, self.uri, self.dist)
+ if len(self.comps) > 0:
+ line += " " + " ".join(self.comps)
+ if self.comment != "":
+ line += " #"+self.comment
+ line += "\n"
+ return line
+
+# the SourceList file as a class
+class NullMatcher(object):
+ def match(self, s):
+ return True
+
+class SourcesList:
+ def __init__(self, withMatcher=True):
+ self.list = [] # of Type SourceEntries
+ if withMatcher:
+ self.matcher = SourceEntryMatcher()
+ else:
+ self.matcher = NullMatcher()
+ self.refresh()
+
+ def refresh(self):
+ self.list = []
+ # read sources.list
+ dir = apt_pkg.Config.FindDir("Dir::Etc")
+ file = apt_pkg.Config.Find("Dir::Etc::sourcelist")
+ self.load(dir+file)
+ # read sources.list.d
+ partsdir = apt_pkg.Config.FindDir("Dir::Etc::sourceparts")
+ for file in glob.glob("%s/*.list" % partsdir):
+ self.load(file)
+ # check if the source item fits a predefined template
+ for source in self.list:
+ if source.invalid == False:
+ self.matcher.match(source)
+
+ def __iter__(self):
+ for entry in self.list:
+ yield entry
+ raise StopIteration
+
+ def add(self, type, uri, dist, comps, comment="", pos=-1, file=None):
+ """
+ Add a new source to the sources.list.
+ The method will search for existing matching repos and will try to
+ reuse them as far as possible
+ """
+ for source in self.list:
+ # if there is a repo with the same (type, uri, dist) just add the
+ # components
+ if source.disabled == False and source.invalid == False and \
+ source.type == type and uri == source.uri and \
+ source.dist == dist:
+ comps = uniq(source.comps + comps)
+ source.comps = comps
+ return source
+ # if there is a corresponding repo which is disabled, enable it
+ elif source.disabled == True and source.invalid == False and \
+ source.type == type and uri == source.uri and \
+ source.dist == dist and \
+ len(set(source.comps) & set(comps)) == len(comps):
+ source.disabled = False
+ return source
+ # there isn't any matching source, so create a new line and parse it
+ line = "%s %s %s" % (type,uri,dist)
+ for c in comps:
+ line = line + " " + c;
+ if comment != "":
+ line = "%s #%s\n" %(line,comment)
+ line = line + "\n"
+ new_entry = SourceEntry(line)
+ if file != None:
+ new_entry.file = file
+ self.matcher.match(new_entry)
+ self.list.insert(pos, new_entry)
+ return new_entry
+
+ def remove(self, source_entry):
+ self.list.remove(source_entry)
+
+ def restoreBackup(self, backup_ext):
+ " restore sources.list files based on the backup extension "
+ dir = apt_pkg.Config.FindDir("Dir::Etc")
+ file = apt_pkg.Config.Find("Dir::Etc::sourcelist")
+ if os.path.exists(dir+file+backup_ext):
+ shutil.copy(dir+file+backup_ext,dir+file)
+ # now sources.list.d
+ partsdir = apt_pkg.Config.FindDir("Dir::Etc::sourceparts")
+ for file in glob.glob("%s/*.list" % partsdir):
+ if os.path.exists(file+backup_ext):
+ shutil.copy(file+backup_ext,file)
+
+ def backup(self, backup_ext=None):
+ """ make a backup of the current source files, if no backup extension
+ is given, the current date/time is used (and returned) """
+ already_backuped = set()
+ if backup_ext == None:
+ backup_ext = time.strftime("%y%m%d.%H%M")
+ for source in self.list:
+ if not source.file in already_backuped:
+ shutil.copy(source.file,"%s%s" % (source.file,backup_ext))
+ return backup_ext
+
+ def load(self,file):
+ """ (re)load the current sources """
+ try:
+ f = open(file, "r")
+ lines = f.readlines()
+ for line in lines:
+ source = SourceEntry(line,file)
+ self.list.append(source)
+ except:
+ print "could not open file '%s'" % file
+ else:
+ f.close()
+
+ def save(self):
+ """ save the current sources """
+ files = {}
+ for source in self.list:
+ if not files.has_key(source.file):
+ files[source.file]=open(source.file,"w")
+ files[source.file].write(source.str())
+ for f in files:
+ files[f].close()
+
+ def check_for_relations(self, sources_list):
+ """get all parent and child channels in the sources list"""
+ parents = []
+ used_child_templates = {}
+ for source in sources_list:
+ # try to avoid checking uninterressting sources
+ if source.template == None:
+ continue
+ # set up a dict with all used child templates and corresponding
+ # source entries
+ if source.template.child == True:
+ key = source.template
+ if not used_child_templates.has_key(key):
+ used_child_templates[key] = []
+ temp = used_child_templates[key]
+ temp.append(source)
+ else:
+ # store each source with children aka. a parent :)
+ if len(source.template.children) > 0:
+ parents.append(source)
+ #print self.used_child_templates
+ #print self.parents
+ return (parents, used_child_templates)
+
+# templates for the add dialog
+class SourceEntryTemplate(SourceEntry):
+ def __init__(self,a_type,uri,dist,description,comps):
+ self.comps_descriptions = []
+ self.type = a_type
+ self.uri = uri
+ self.dist = dist
+ self.description = description
+ self.comps = comps
+
+ def matches(self,source_entry):
+ """ check if a given source_entry matches this one """
+ if (self.type != source_entry.type):
+ return False
+ if (self.dist != source_entry.dist):
+ return False
+ if not is_mirror(self.uri,source_entry.uri):
+ return False
+ for e_comp in source_entry.comps:
+ for t_comp in self.comps:
+ if e_comp == t_comp.name: break
+ else:
+ return False
+ return True
+
+class SourceCompTemplate:
+ def __init__(self, name, description, on_by_default):
+ self.name = name
+ self.description = description
+ self.on_by_default = on_by_default
+
+class SourceEntryTemplates:
+ def __init__(self,datadir):
+ _ = gettext.gettext
+ self.templates = []
+
+ dinfo = DistInfo (base_dir=datadir+"channels/")
+
+ for suite in dinfo.suites:
+ comps = []
+ for comp in suite.components:
+ comps.append(SourceCompTemplate(comp.name, _(comp.description),
+ comp.enabled))
+ self.templates.append (SourceEntryTemplate(suite.repository_type,
+ suite.base_uri,
+ suite.name,
+ suite.description,
+ comps))
+
+# matcher class to make a source entry look nice
+# lots of predefined matchers to make it i18n/gettext friendly
+class SourceEntryMatcher:
+ class MatchType:
+ def __init__(self, a_type,a_descr):
+ self.type = a_type
+ self.description = a_descr
+
+ class MatchDist:
+ def __init__(self,a_uri,a_dist, a_descr,l_comps, l_comps_descr):
+ self.uri = a_uri
+ self.dist = a_dist
+ self.description = a_descr
+ self.comps = l_comps
+ self.comps_descriptions = l_comps_descr
+
+ def __init__(self):
+ self.templates = []
+ # Get the human readable channel and comp names from the channel .infos
+ spec_files = glob.glob("/usr/share/update-manager/channels/*.info")
+ for f in spec_files:
+ f = os.path.basename(f)
+ i = f.find(".info")
+ f = f[0:i]
+ dist = DistInfo(f)
+ for suite in dist.suites:
+ if suite.match_uri != None:
+ self.templates.append(suite)
+ return
+
+ def match(self, source):
+ """Add a matching template to the source"""
+ _ = gettext.gettext
+ found = False
+ for template in self.templates:
+ #print "'%s'" %source.uri
+ if re.search(template.match_uri, source.uri) and \
+ re.match(template.match_name, source.dist):
+ found = True
+ source.template = template
+ break
+ return found
+
+class Distribution:
+ def __init__(self):
+ """"
+ Container for distribution specific informations
+ """
+ # LSB information
+ self.id = ""
+ self.codename = ""
+ self.description = ""
+ self.release = ""
+
+ # get the LSB information
+ lsb_info = []
+ for lsb_option in ["-i", "-c", "-d", "-r"]:
+ pipe = os.popen("lsb_release %s | cut -d : -f 2-" % lsb_option)
+ lsb_info.append(pipe.read().strip())
+ del pipe
+ (self.id, self.codename, self.description, self.release) = lsb_info
+
+ # get a list of country codes and real names
+ self.countries = {}
+ try:
+ f = open("/usr/share/iso-codes/iso_3166.tab", "r")
+ lines = f.readlines()
+ for line in lines:
+ parts = line.split("\t")
+ self.countries[parts[0].lower()] = parts[1]
+ except:
+ print "could not open file '%s'" % file
+ else:
+ f.close()
+
+ def get_sources(self, sources_list):
+ """
+ Find the corresponding template, main and child sources
+ for the distribution
+ """
+ # corresponding sources
+ self.source_template = None
+ self.child_sources = []
+ self.main_sources = []
+ self.disabled_sources = []
+ self.cdrom_sources = []
+ self.download_comps = []
+ self.enabled_comps = []
+ self.cdrom_comps = []
+ self.used_media = []
+ self.get_source_code = False
+ self.source_code_sources = []
+
+ # location of the sources
+ self.default_server = ""
+ self.main_server = ""
+ self.nearest_server = ""
+ self.used_servers = []
+
+ # find the distro template
+ for template in sources_list.matcher.templates:
+ if template.name == self.codename and\
+ template.distribution == self.id:
+ #print "yeah! found a template for %s" % self.description
+ #print template.description, template.base_uri, template.components
+ self.source_template = template
+ break
+ if self.source_template == None:
+ print "Error: could not find a distribution template"
+ # FIXME: will go away - only for debugging issues
+ sys.exit(1)
+
+ # find main and child sources
+ media = []
+ comps = []
+ cdrom_comps = []
+ enabled_comps = []
+ source_code = []
+ for source in sources_list.list:
+ if source.invalid == False and\
+ source.dist == self.codename and\
+ source.template and\
+ source.template.name == self.codename:
+ #print "yeah! found a distro repo: %s" % source.line
+ # cdroms need do be handled differently
+ if source.uri.startswith("cdrom:") and \
+ source.disabled == False:
+ self.cdrom_sources.append(source)
+ cdrom_comps.extend(source.comps)
+ elif source.uri.startswith("cdrom:") and \
+ source.disabled == True:
+ self.cdrom_sources.append(source)
+ elif source.type == "deb" and source.disabled == False:
+ self.main_sources.append(source)
+ comps.extend(source.comps)
+ media.append(source.uri)
+ elif source.type == "deb" and source.disabled == True:
+ self.disabled_sources.append(source)
+ elif source.type.endswith("-src") and source.disabled == False:
+ self.source_code_sources.append(source)
+ elif source.type.endswith("-src") and source.disabled == True:
+ self.disabled_sources.append(source)
+ if source.template in self.source_template.children:
+ #print "yeah! child found: %s" % source.template.name
+ if source.type == "deb":
+ self.child_sources.append(source)
+ elif source.type == "deb-src":
+ self.source_code_sources.append(source)
+ self.download_comps = set(comps)
+ self.cdrom_comps = set(cdrom_comps)
+ enabled_comps.extend(comps)
+ enabled_comps.extend(cdrom_comps)
+ self.enabled_comps = set(enabled_comps)
+ self.used_media = set(media)
+
+ self.get_mirrors()
+
+ def get_mirrors(self):
+ """
+ Provide a set of mirrors where you can get the distribution from
+ """
+ # the main server is stored in the template
+ self.main_server = self.source_template.base_uri
+
+ # try to guess the nearest mirror from the locale
+ # FIXME: for debian we need something different
+ if self.id == "Ubuntu":
+ locale = os.getenv("LANG", default="en.UK")
+ a = locale.find("_")
+ z = locale.find(".")
+ if z == -1:
+ z = len(locale)
+ country_code = locale[a+1:z].lower()
+ self.nearest_server = "http://%s.archive.ubuntu.com/ubuntu/" % \
+ country_code
+ if self.countries.has_key(country_code):
+ self.country = self.countries[country_code]
+ else:
+ self.country = None
+
+ # other used servers
+ for medium in self.used_media:
+ if not medium.startswith("cdrom:"):
+ # seems to be a network source
+ self.used_servers.append(medium)
+
+ if len(self.main_sources) == 0:
+ self.default_server = self.main_server
+ else:
+ self.default_server = self.main_sources[0].uri
+
+ def add_source(self, sources_list, type=None,
+ uri=None, dist=None, comps=None, comment=""):
+ """
+ Add distribution specific sources
+ """
+ if uri == None:
+ # FIXME: Add support for the server selector
+ uri = self.default_server
+ if dist == None:
+ dist = self.codename
+ if comps == None:
+ comps = list(self.enabled_comps)
+ if type == None:
+ type = "deb"
+ if comment == "":
+ comment == "Added by software-properties"
+ new_source = sources_list.add(type, uri, dist, comps, comment)
+ # if source code is enabled add a deb-src line after the new
+ # source
+ if self.get_source_code == True and not type.endswith("-src"):
+ sources_list.add("%s-src" % type, uri, dist, comps, comment,
+ file=new_source.file,
+ pos=sources_list.list.index(new_source)+1)
+
+ def enable_component(self, sourceslist, comp):
+ """
+ Disable a component in all main, child and source code sources
+ (excluding cdrom based sources)
+
+ sourceslist: an aptsource.sources_list
+ comp: the component that should be enabled
+ """
+ sources = []
+ sources.extend(self.main_sources)
+ sources.extend(self.child_sources)
+ sources.extend(self.source_code_sources)
+ # check if there is a main source at all
+ if len(self.main_sources) < 1:
+ # create a new main source
+ self.add_source(sourceslist, comps=["%s"%comp])
+ else:
+ # add the comp to all main, child and source code sources
+ for source in sources:
+ if comp not in source.comps:
+ source.comps.append(comp)
+ if self.get_source_code == True:
+ for source in self.source_code_sources:
+ if comp not in source.comps: source.comps.append(comp)
+
+ def disable_component(self, sourceslist, comp):
+ """
+ Disable a component in all main, child and source code sources
+ (excluding cdrom based sources)
+ """
+ sources = []
+ sources.extend(self.main_sources)
+ sources.extend(self.child_sources)
+ sources.extend(self.source_code_sources)
+ if comp in self.cdrom_comps:
+ sources = []
+ sources.extend(self.main_sources)
+
+ for source in sources:
+ if comp in source.comps:
+ source.comps.remove(comp)
+ if len(source.comps) < 1:
+ sourceslist.remove(source)
+
+
+# some simple tests
+if __name__ == "__main__":
+ apt_pkg.InitConfig()
+ sources = SourcesList()
+
+ for entry in sources:
+ print entry.str()
+ #print entry.uri
+
+ mirror = is_mirror("http://archive.ubuntu.com/ubuntu/",
+ "http://de.archive.ubuntu.com/ubuntu/")
+ print "is_mirror(): %s" % mirror
+
+ print is_mirror("http://archive.ubuntu.com/ubuntu",
+ "http://de.archive.ubuntu.com/ubuntu/")
diff --git a/UpdateManager/DistUpgradeFetcher.py b/UpdateManager/DistUpgradeFetcher.py
index aa51066a..af07cfb4 100644
--- a/UpdateManager/DistUpgradeFetcher.py
+++ b/UpdateManager/DistUpgradeFetcher.py
@@ -105,15 +105,13 @@ class DistUpgradeFetcher(object):
return True
def gpgauthenticate(self, file, signature,
- keyring='/etc/apt/trusted.gpg',
- trustdb='/etc/apt/trustdb.gpg'):
+ keyring='/etc/apt/trusted.gpg'):
""" authenticated a file against a given signature, if no keyring
is given use the apt default keyring
"""
gpg = GnuPGInterface.GnuPG()
gpg.options.extra_args = ['--no-options',
'--no-default-keyring',
- '--trustdb-name',trustdb,
'--keyring', keyring]
proc = gpg.run(['--verify', signature, file],
create_fhs=['status','logger','stderr'])
@@ -190,7 +188,10 @@ class DistUpgradeFetcher(object):
def runDistUpgrader(self):
#print "runing: %s" % script
- os.execv(self.script,[])
+ if os.getuid() != 0:
+ os.execv("/usr/bin/gksu",["gksu",self.script])
+ else:
+ os.execv(self.script,[self.script])
def cleanup(self):
# cleanup
diff --git a/UpdateManager/MetaRelease.py b/UpdateManager/MetaRelease.py
index 57a808ee..70993eaf 100644
--- a/UpdateManager/MetaRelease.py
+++ b/UpdateManager/MetaRelease.py
@@ -60,8 +60,15 @@ class MetaRelease(gobject.GObject):
def __init__(self, useDevelopmentRelase=False):
gobject.GObject.__init__(self)
+ # check what uri to use
if useDevelopmentRelase:
self.METARELEASE_URI = self.METARELEASE_URI_UNSTABLE
+ # check if we can access the METARELEASE_FILE
+ if not os.access(self.METARELEASE_FILE, os.F_OK|os.W_OK|os.R_OK):
+ path = os.path.expanduser("~/.update-manager/")
+ if not os.path.exists(path):
+ os.mkdir(path)
+ self.METARELEASE_FILE = os.path.join(path,"meta-release")
self.metarelease_information = None
self.downloading = True
# we start the download thread here and we have a timeout
@@ -168,6 +175,3 @@ class MetaRelease(gobject.GObject):
if os.path.exists(self.METARELEASE_FILE):
f=open(self.METARELEASE_FILE,"r")
-# register in the gobject system, needed for older versions of pygtk,
-# never ones do this automatically
-gobject.type_register(MetaRelease)
diff --git a/UpdateManager/UpdateManager.py b/UpdateManager/UpdateManager.py
index d4cc7a59..2e2433a8 100644
--- a/UpdateManager/UpdateManager.py
+++ b/UpdateManager/UpdateManager.py
@@ -69,8 +69,7 @@ from MetaRelease import Dist, MetaRelease
# - kill "all_changes" and move the changes into the "Update" class
# list constants
-(LIST_INSTALL, LIST_CONTENTS, LIST_NAME, LIST_SHORTDESC,
- LIST_VERSION, LIST_LONG_DESCR, LIST_PKG) = range(7)
+(LIST_INSTALL, LIST_CONTENTS, LIST_NAME, LIST_PKG) = range(4)
# actions for "invoke_manager"
(INSTALL, UPDATE) = range(2)
@@ -83,11 +82,19 @@ CHANGELOGS_URI="http://changelogs.ubuntu.com/changelogs/pool/%s/%s/%s/%s_%s/chan
class MyCache(apt.Cache):
def __init__(self, progress):
apt.Cache.__init__(self, progress)
+ self._initDepCache()
assert self._depcache.BrokenCount == 0 and self._depcache.DelCount == 0
self.all_changes = {}
+ def _initDepCache(self):
+ #apt_pkg.Config.Set("Debug::pkgPolicy","1")
+ #self.depcache = apt_pkg.GetDepCache(self.cache)
+ #self._depcache = apt_pkg.GetDepCache(self._cache)
+ self._depcache.ReadPinFile()
+ if os.path.exists(SYNAPTIC_PINFILE):
+ self._depcache.ReadPinFile(SYNAPTIC_PINFILE)
+ self._depcache.Init()
def clean(self):
- for pkg in self:
- pkg.markKeep()
+ self._initDepCache()
def saveDistUpgrade(self):
""" this functions mimics a upgrade but will never remove anything """
self._depcache.Upgrade(True)
@@ -193,23 +200,44 @@ class MyCache(apt.Cache):
lock.release()
class UpdateList:
+ ORIGIN_MAPPING = { ("edgy-security","Ubuntu"): _("Ubuntu security updates"),
+ ("edgy-updates","Ubuntu"): _("Ubuntu important updates"),
+ ("edgy-backports","Ubuntu"): _("Ubuntu backports"),
+ ("edgy","Ubuntu"): _("Ubuntu updates")
+ }
+
def __init__(self, parent_window):
- self.pkgs = []
+ # a map of packages under their origin
+ self.pkgs = {}
self.num_updates = 0
self.parent_window = parent_window
def update(self, cache):
held_back = []
broken = []
+
+ # do the upgrade
cache.saveDistUpgrade()
+
+ # sort by origin
for pkg in cache:
if pkg.markedUpgrade or pkg.markedInstall:
- self.pkgs.append(pkg)
+ originstr = _("Unknown")
+ for aorigin in pkg.candidateOrigin:
+ archive = aorigin.archive
+ origin = aorigin.origin
+ if self.ORIGIN_MAPPING.has_key((archive,origin)) and aorigin.trusted:
+ originstr = self.ORIGIN_MAPPING[(archive,origin)]
+ if not self.pkgs.has_key(originstr):
+ self.pkgs[originstr] = []
+ self.pkgs[originstr].append(pkg)
self.num_updates = self.num_updates + 1
elif pkg.isUpgradable:
- #print "MarkedKeep: %s " % pkg.name
held_back.append(pkg.name)
- self.pkgs.sort(lambda x,y: cmp(x.name,y.name))
+ for l in self.pkgs.keys():
+ self.pkgs[l].sort(lambda x,y: cmp(x.name,y.name))
+
+ # check if we have held-back something
if cache._depcache.KeepCount > 0:
#print "WARNING, keeping packages"
msg = ("<big><b>%s</b></big>\n\n%s" % \
@@ -287,8 +315,7 @@ class UpdateManager(SimpleGladeApp):
self.button_close.connect("clicked", lambda w: self.exit())
# the treeview (move into it's own code!)
- self.store = gtk.ListStore(gobject.TYPE_BOOLEAN, str, str, str, str, str,
- gobject.TYPE_PYOBJECT)
+ self.store = gtk.ListStore(gobject.TYPE_BOOLEAN, str, str, gobject.TYPE_PYOBJECT)
self.treeview_update.set_model(self.store)
self.treeview_update.set_headers_clickable(True);
@@ -299,20 +326,23 @@ class UpdateManager(SimpleGladeApp):
cr.set_property("activatable", True)
cr.set_property("xpad", 10)
cr.connect("toggled", self.toggled)
- self.cb = gtk.TreeViewColumn("Install", cr, active=LIST_INSTALL)
- c0 = gtk.TreeViewColumn("Name", tr, markup=LIST_CONTENTS)
- c0.set_resizable(True)
+
+ column_install = gtk.TreeViewColumn("Install", cr)
+ column_install.set_cell_data_func (cr, self.install_column_view_func)
+ column = gtk.TreeViewColumn("Name", tr, markup=LIST_CONTENTS)
+ column.set_cell_data_func (tr, self.package_column_view_func)
+ column.set_resizable(True)
major,minor,patch = gtk.pygtk_version
if (major >= 2) and (minor >= 5):
- self.cb.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
- self.cb.set_fixed_width(30)
- c0.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
- c0.set_fixed_width(100)
+ column_install.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ column_install.set_fixed_width(30)
+ column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ column.set_fixed_width(100)
#self.treeview_update.set_fixed_height_mode(True)
- self.treeview_update.append_column(self.cb)
- self.cb.set_visible(True)
- self.treeview_update.append_column(c0)
+ self.treeview_update.append_column(column_install)
+ column_install.set_visible(True)
+ self.treeview_update.append_column(column)
self.treeview_update.set_search_column(LIST_NAME)
@@ -348,6 +378,24 @@ class UpdateManager(SimpleGladeApp):
self.restore_state()
self.window_main.show()
+ def header_column_func(self, cell_layot, renderer, model, iter):
+ pkg = model.get_value(iter, LIST_PKG)
+ if pkg == None:
+ renderer.set_property("cell-background","yellow")
+ else:
+ renderer.set_property("cell-background", None)
+
+ def install_column_view_func(self, cell_layout, renderer, model, iter):
+ self.header_column_func(cell_layout, renderer, model, iter)
+ pkg = model.get_value(iter, LIST_PKG)
+ to_install = model.get_value(iter, LIST_INSTALL)
+ renderer.set_property("active", to_install)
+ # hide it if we are only a header line
+ renderer.set_property("visible", pkg != None)
+
+ def package_column_view_func(self, cell_layout, renderer, model, iter):
+ self.header_column_func(cell_layout, renderer, model, iter)
+
def setupDbus(self):
""" this sets up a dbus listener if none is installed alread """
# check if there is another g-a-i already and if not setup one
@@ -389,24 +437,17 @@ class UpdateManager(SimpleGladeApp):
return
for line in lines:
-
end_iter = changes_buffer.get_end_iter()
-
- version_match = re.match(r'^%s \((.*)\)(.*)$' % re.escape(srcpkg), line)
+ version_match = re.match(r'^%s \((.*)\)(.*)\;.*$' % re.escape(srcpkg), line)
#bullet_match = re.match("^.*[\*-]", line)
author_match = re.match("^.*--.*<.*@.*>.*$", line)
if version_match:
version = version_match.group(1)
+ upload_archive = version_match.group(2).strip()
version_text = _("Version %s: \n") % version
changes_buffer.insert_with_tags_by_name(end_iter, version_text, "versiontag")
- # mvo: disabled for now as it does not catch multi line entries
- # (see ubuntu #7034 for rational)
- #elif bullet_match and not author_match:
- # bullet_text = " " + line + "\n"
- # changes_buffer.insert(end_iter, bullet_text)
elif (author_match):
pass
- #chanages_buffer.insert(end_iter, "\n")
else:
changes_buffer.insert(end_iter, line+"\n")
@@ -421,7 +462,10 @@ class UpdateManager(SimpleGladeApp):
iter = model.get_iter(path)
# set descr
- long_desc = model.get_value(iter, LIST_LONG_DESCR)
+ pkg = model.get_value(iter, LIST_PKG)
+ if pkg == None:
+ return
+ long_desc = pkg.description
if long_desc == None:
return
# Skip the first line - it's a duplicate of the summary
@@ -518,7 +562,7 @@ class UpdateManager(SimpleGladeApp):
self.notebook_details.set_sensitive(True)
self.treeview_update.set_sensitive(True)
self.button_install.grab_default()
- self.treeview_update.set_cursor(0)
+ self.treeview_update.set_cursor(1)
self.label_header.set_markup(text_header)
self.label_downsize.set_markup(text_download)
@@ -649,17 +693,16 @@ class UpdateManager(SimpleGladeApp):
self.list.update(self.cache)
if self.list.num_updates > 0:
i=0
- for pkg in self.list.pkgs:
-
- name = xml.sax.saxutils.escape(pkg.name)
- summary = xml.sax.saxutils.escape(pkg.summary)
- contents = "<big><b>%s</b></big>\n<small>%s\n\n" % (name, summary)
- contents = contents + _("New version: %s (Size: %s)") % (pkg.candidateVersion,apt.SizeToStr(pkg.packageSize)) + "</small>"
-
- iter = self.store.append([True, contents, pkg.name, pkg.summary,
- pkg.candidateVersion, pkg.description, pkg])
- self.add_update(pkg)
- i = i + 1
+ for origin in self.list.pkgs.keys():
+ self.store.append([False, '<span weight="bold" size="large">%s</span>' % origin, origin, None])
+ for pkg in self.list.pkgs[origin]:
+ name = xml.sax.saxutils.escape(pkg.name)
+ summary = xml.sax.saxutils.escape(pkg.summary)
+ contents = "<big><b>%s</b></big>\n<small>%s\n\n" % (name, summary)
+ contents = contents + _("New version: %s (Size: %s)") % (pkg.candidateVersion,apt.SizeToStr(pkg.packageSize)) + "</small>"
+ iter = self.store.append([True, contents, pkg.name, pkg])
+ self.add_update(pkg)
+ i = i + 1
self.update_count()
# use the normal cursor
@@ -687,23 +730,6 @@ class UpdateManager(SimpleGladeApp):
fetcher.run()
def new_dist_available(self, meta_release, upgradable_to):
- #print "new_dist_available: %s" % upgradable_to.name
- # check if the user already knowns about this dist
- #seen = self.gconfclient.get_string("/apps/update-manager/seen_dist")
- #if name == seen:
- # return
-
- #msg = "<big><b>%s</b></big>\n\n%s" % (_("There is a new release of Ubuntu available!"), _("A new release with the codename '%s' is available. Please see http://www.ubuntulinux.org/ for upgrade instructions.") % name)
- #dialog = gtk.MessageDialog(self.window_main, 0, gtk.MESSAGE_INFO,
- # gtk.BUTTONS_CLOSE, "")
- #dialog.set_markup(msg)
- #check = gtk.CheckButton(_("Never show this message again"))
- #check.show()
- #dialog.vbox.pack_start(check)
- #dialog.run()
- #if check.get_active():
- # self.gconfclient.set_string("/apps/update-manager/seen_dist",name)
- #dialog.destroy()
self.frame_new_release.show()
self.label_new_release.set_markup(_("<b>New distribution release '%s' is available</b>") % upgradable_to.version)
self.new_dist = upgradable_to
@@ -755,13 +781,6 @@ class UpdateManager(SimpleGladeApp):
sys.exit(1)
else:
progress.hide()
- #apt_pkg.Config.Set("Debug::pkgPolicy","1")
- #self.depcache = apt_pkg.GetDepCache(self.cache)
- self.cache._depcache.ReadPinFile()
- if os.path.exists(SYNAPTIC_PINFILE):
- self.cache._depcache.ReadPinFile(SYNAPTIC_PINFILE)
- self.cache._depcache.Init()
-
def check_auto_update(self):
# Check if automatic update is enabled. If not show a dialog to inform