123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- #!/usr/bin/env python
- # vim:set et sw=4:
- """
- Changes class for dak
- @contact: Debian FTP Master <ftpmaster@debian.org>
- @copyright: 2001 - 2006 James Troup <james@nocrew.org>
- @copyright: 2009 Joerg Jaspert <joerg@debian.org>
- @copyright: 2009 Mark Hymers <mhy@debian.org>
- @license: GNU General Public License version 2 or later
- """
- # 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
- ###############################################################################
- from __future__ import absolute_import, print_function
- import os
- import datetime
- from apt_pkg import TagSection
- from .utils import open_file, poolify, deb_extract_control
- from .config import *
- from .dbconn import *
- ###############################################################################
- __all__ = []
- ###############################################################################
- CHANGESFIELDS_MANDATORY = ["distribution", "source", "architecture",
- "version", "maintainer", "urgency", "fingerprint", "changedby822",
- "changedby2047", "changedbyname", "maintainer822", "maintainer2047",
- "maintainername", "maintaineremail", "closes", "changes"]
- __all__.append('CHANGESFIELDS_MANDATORY')
- CHANGESFIELDS_OPTIONAL = ["changed-by", "filecontents", "format",
- "process-new note", "adv id", "distribution-version", "sponsoremail"]
- __all__.append('CHANGESFIELDS_OPTIONAL')
- CHANGESFIELDS_FILES = ["package", "version", "architecture", "type", "size",
- "md5sum", "sha1sum", "sha256sum", "component", "location id",
- "source package", "source version", "maintainer", "dbtype", "files id",
- "new", "section", "priority", "othercomponents", "pool name",
- "original component"]
- __all__.append('CHANGESFIELDS_FILES')
- CHANGESFIELDS_DSC = ["source", "version", "maintainer", "fingerprint",
- "uploaders", "bts changelog", "dm-upload-allowed"]
- __all__.append('CHANGESFIELDS_DSC')
- CHANGESFIELDS_DSCFILES_MANDATORY = ["size", "md5sum"]
- __all__.append('CHANGESFIELDS_DSCFILES_MANDATORY')
- CHANGESFIELDS_DSCFILES_OPTIONAL = ["files id"]
- __all__.append('CHANGESFIELDS_DSCFILES_OPTIONAL')
- CHANGESFIELDS_ORIGFILES = ["id", "location"]
- __all__.append('CHANGESFIELDS_ORIGFILES')
- ###############################################################################
- class Changes(object):
- """ Convenience wrapper to carry around all the package information """
- def __init__(self, **kwds):
- self.reset()
- def reset(self):
- self.changes_file = ""
- self.changes = {}
- self.dsc = {}
- self.files = {}
- self.dsc_files = {}
- self.orig_files = {}
- def file_summary(self):
- # changes["distribution"] may not exist in corner cases
- # (e.g. unreadable changes files)
- if "distribution" not in self.changes or not \
- isinstance(self.changes["distribution"], dict):
- self.changes["distribution"] = {}
- byhand = False
- new = False
- summary = ""
- override_summary = ""
- for name, entry in sorted(self.files.items()):
- if "byhand" in entry:
- byhand = True
- summary += name + " byhand\n"
- elif "new" in entry:
- new = True
- summary += "(new) %s %s %s\n" % (name, entry["priority"], entry["section"])
- if "othercomponents" in entry:
- summary += "WARNING: Already present in %s distribution.\n" % (entry["othercomponents"])
- if entry["type"] == "deb":
- deb_fh = open_file(name)
- summary += TagSection(deb_extract_control(deb_fh))["Description"] + '\n'
- deb_fh.close()
- else:
- entry["pool name"] = poolify(self.changes.get("source", ""))
- destination = entry["pool name"] + name
- summary += name + "\n to " + destination + "\n"
- if "type" not in entry:
- entry["type"] = "unknown"
- if entry["type"] in ["deb", "udeb", "dsc"]:
- # (queue/unchecked), there we have override entries already, use them
- # (process-new), there we dont have override entries, use the newly generated ones.
- override_prio = entry.get("override priority", entry["priority"])
- override_sect = entry.get("override section", entry["section"])
- override_summary += "%s - %s %s\n" % (name, override_prio, override_sect)
- return (byhand, new, summary, override_summary)
- def check_override(self):
- """
- Checks override entries for validity.
- Returns an empty string if there are no problems
- or the text of a warning if there are
- """
- summary = ""
- # Abandon the check if it's a non-sourceful upload
- if "source" not in self.changes["architecture"]:
- return summary
- for name, entry in sorted(self.files.items()):
- if "new" not in entry and entry["type"] == "deb":
- if entry["section"] != "-":
- if entry["section"].lower() != entry["override section"].lower():
- summary += "%s: package says section is %s, override says %s.\n" % (name,
- entry["section"],
- entry["override section"])
- if entry["priority"] != "-":
- if entry["priority"] != entry["override priority"]:
- summary += "%s: package says priority is %s, override says %s.\n" % (name,
- entry["priority"],
- entry["override priority"])
- return summary
- @session_wrapper
- def remove_known_changes(self, session=None):
- session.delete(get_dbchange(self.changes_file, session))
- def mark_missing_fields(self):
- """add "missing" in fields which we will require for the known_changes table"""
- for key in ['urgency', 'maintainer', 'fingerprint', 'changed-by']:
- if (key not in self.changes) or (not self.changes[key]):
- self.changes[key] = 'missing'
- def __get_file_from_pool(self, filename, entry, session, logger):
- cnf = Config()
- if "Dinstall::SuiteSuffix" in cnf:
- component = cnf["Dinstall::SuiteSuffix"] + entry["component"]
- else:
- component = entry["component"]
- poolname = poolify(entry["source"])
- l = get_location(cnf["Dir::Pool"], component, session=session)
- found, poolfile = check_poolfile(os.path.join(poolname, filename),
- entry['size'],
- entry["md5sum"],
- l.location_id,
- session=session)
- if found is None:
- if logger is not None:
- logger.log(["E: Found multiple files for pool (%s) for %s" % (filename, component)])
- return None
- elif found is False and poolfile is not None:
- if logger is not None:
- logger.log(["E: md5sum/size mismatch for %s in pool" % (filename)])
- return None
- else:
- if poolfile is None:
- if logger is not None:
- logger.log(["E: Could not find %s in pool" % (filename)])
- return None
- else:
- return poolfile
- @session_wrapper
- def add_known_changes(self, dirpath, in_queue=None, session=None, logger=None):
- """add "missing" in fields which we will require for the known_changes table"""
- cnf = Config()
- changesfile = os.path.join(dirpath, self.changes_file)
- filetime = datetime.datetime.fromtimestamp(os.path.getctime(changesfile))
- self.mark_missing_fields()
- multivalues = {}
- for key in ("distribution", "architecture", "binary"):
- if isinstance(self.changes[key], dict):
- multivalues[key] = " ".join(self.changes[key].keys())
- else:
- multivalues[key] = self.changes[key]
- chg = DBChange()
- chg.changesname = self.changes_file
- chg.seen = filetime
- chg.in_queue_id = in_queue
- chg.source = self.changes["source"]
- chg.binaries = multivalues["binary"]
- chg.architecture = multivalues["architecture"]
- chg.version = self.changes["version"]
- chg.distribution = multivalues["distribution"]
- chg.urgency = self.changes["urgency"]
- chg.maintainer = self.changes["maintainer"]
- chg.fingerprint = self.changes["fingerprint"]
- chg.changedby = self.changes["changed-by"]
- chg.date = self.changes["date"]
- session.add(chg)
- files = []
- for chg_fn, entry in self.files.items():
- try:
- f = open(os.path.join(dirpath, chg_fn))
- cpf = ChangePendingFile()
- cpf.filename = chg_fn
- cpf.size = entry['size']
- cpf.md5sum = entry['md5sum']
- if 'sha1sum' in entry:
- cpf.sha1sum = entry['sha1sum']
- else:
- f.seek(0)
- cpf.sha1sum = apt_pkg.sha1sum(f)
- if 'sha256sum' in entry:
- cpf.sha256sum = entry['sha256sum']
- else:
- f.seek(0)
- cpf.sha256sum = apt_pkg.sha256sum(f)
- session.add(cpf)
- files.append(cpf)
- f.close()
- except IOError:
- # Can't find the file, try to look it up in the pool
- poolfile = self.__get_file_from_pool(chg_fn, entry, session)
- if poolfile:
- chg.poolfiles.append(poolfile)
- chg.files = files
- # Add files referenced in .dsc, but not included in .changes
- for name, entry in self.dsc_files.items():
- if name in self.files:
- continue
- entry['source'] = self.changes['source']
- poolfile = self.__get_file_from_pool(name, entry, session, logger)
- if poolfile:
- chg.poolfiles.append(poolfile)
- session.commit()
- chg = session.query(DBChange).filter_by(changesname=self.changes_file).one()
- return chg
- def unknown_files_fields(self, name):
- return sorted(list(set(self.files[name].keys())
- - set(CHANGESFIELDS_FILES)))
- def unknown_changes_fields(self):
- return sorted(list(set(self.changes.keys())
- - set(CHANGESFIELDS_MANDATORY + CHANGESFIELDS_OPTIONAL)))
- def unknown_dsc_fields(self):
- return sorted(list(set(self.dsc.keys())
- - set(CHANGESFIELDS_DSC)))
- def unknown_dsc_files_fields(self, name):
- return sorted(list(set(self.dsc_files[name].keys())
- - set(CHANGESFIELDS_DSCFILES_MANDATORY + CHANGESFIELDS_DSCFILES_OPTIONAL)))
- def str_files(self):
- r = []
- for name, entry in self.files.items():
- r.append(" %s:" % (name))
- for i in CHANGESFIELDS_FILES:
- if i in entry:
- r.append(" %s: %s" % (i.capitalize(), entry[i]))
- xfields = self.unknown_files_fields(name)
- if len(xfields) > 0:
- r.append("files[%s] still has following unrecognised keys: %s" % (name, ", ".join(xfields)))
- return r
- def str_changes(self):
- r = []
- for i in CHANGESFIELDS_MANDATORY:
- val = self.changes[i]
- if isinstance(val, list):
- val = " ".join(val)
- elif isinstance(val, dict):
- val = " ".join(val.keys())
- r.append(' %s: %s' % (i.capitalize(), val))
- for i in CHANGESFIELDS_OPTIONAL:
- if i in self.changes:
- r.append(' %s: %s' % (i.capitalize(), self.changes[i]))
- xfields = self.unknown_changes_fields()
- if len(xfields) > 0:
- r.append("Warning: changes still has the following unrecognised fields: %s" % ", ".join(xfields))
- return r
- def str_dsc(self):
- r = []
- for i in CHANGESFIELDS_DSC:
- if i in self.dsc:
- r.append(' %s: %s' % (i.capitalize(), self.dsc[i]))
- xfields = self.unknown_dsc_fields()
- if len(xfields) > 0:
- r.append("Warning: dsc still has the following unrecognised fields: %s" % ", ".join(xfields))
- return r
- def str_dsc_files(self):
- r = []
- for name, entry in self.dsc_files.items():
- r.append(" %s:" % (name))
- for i in CHANGESFIELDS_DSCFILES_MANDATORY:
- r.append(" %s: %s" % (i.capitalize(), entry[i]))
- for i in CHANGESFIELDS_DSCFILES_OPTIONAL:
- if i in entry:
- r.append(" %s: %s" % (i.capitalize(), entry[i]))
- xfields = self.unknown_dsc_files_fields(name)
- if len(xfields) > 0:
- r.append("dsc_files[%s] still has following unrecognised keys: %s" % (name, ", ".join(xfields)))
- return r
- def __str__(self):
- r = []
- r.append(" Changes:")
- r += self.str_changes()
- r.append("")
- r.append(" Dsc:")
- r += self.str_dsc()
- r.append("")
- r.append(" Files:")
- r += self.str_files()
- r.append("")
- r.append(" Dsc Files:")
- r += self.str_dsc_files()
- return "\n".join(r)
- __all__.append('Changes')
|