Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 1 | From b105e7fe812da3ccaf7155c0fe14c8728b0d39a5 Mon Sep 17 00:00:00 2001 |
| 2 | From: Mark Hatle <mark.hatle@windriver.com> |
| 3 | Date: Mon, 20 Jan 2014 14:30:52 +0000 |
| 4 | Subject: [PATCH] Add mechanism to attempt install without failing |
| 5 | |
| 6 | In OpenEmbedded, for complementary and 'attemptonly' package processing, |
| 7 | we need a way to instruct smart to try to install, but ignore any |
| 8 | failures (usually conflicts). |
| 9 | |
| 10 | This option only works for the install operation. |
| 11 | |
| 12 | If a complementary install fails, an actual error occurred, one that |
| 13 | we can't ignore without losing the entire attempted transaction. Keep |
| 14 | this as an error so that we can catch these cases in the futre. |
| 15 | |
| 16 | Upstream-Status: Pending |
| 17 | |
| 18 | Signed-off-by: Mark Hatle <mark.hatle@windriver.com> |
| 19 | Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com> |
| 20 | --- |
| 21 | backends/rpm/pm.py | 35 ++++++++++++++++++++++++++++++++++- |
| 22 | transaction.py | 50 +++++++++++++++++++++++++++++++++++++------------- |
| 23 | 2 files changed, 71 insertions(+), 14 deletions(-) |
| 24 | |
| 25 | diff --git a/smart/backends/rpm/pm.py b/smart/backends/rpm/pm.py |
| 26 | index 9bbd952..ba6405a 100644 |
| 27 | --- a/smart/backends/rpm/pm.py |
| 28 | +++ b/smart/backends/rpm/pm.py |
| 29 | @@ -241,15 +241,48 @@ class RPMPackageManager(PackageManager): |
| 30 | cb = RPMCallback(prog, upgradednames) |
| 31 | cb.grabOutput(True) |
| 32 | probs = None |
| 33 | + retry = 0 |
| 34 | try: |
| 35 | probs = ts.run(cb, None) |
| 36 | finally: |
| 37 | del getTS.ts |
| 38 | cb.grabOutput(False) |
| 39 | + if probs and sysconf.has("attempt-install", soft=True): |
| 40 | + def remove_conflict(pkgNEVR): |
| 41 | + for key in changeset.keys(): |
| 42 | + if pkgNEVR == str(key): |
| 43 | + del changeset[key] |
| 44 | + del pkgpaths[key] |
| 45 | + iface.warning("Removing %s due to file %s conflicting with %s" % (pkgNEVR, fname, altNEVR)) |
| 46 | + break |
| 47 | + |
| 48 | + retry = 1 |
| 49 | + for prob in probs: |
| 50 | + if prob[1][0] == rpm.RPMPROB_NEW_FILE_CONFLICT: |
| 51 | + msg = prob[0].split() |
| 52 | + fname = msg[1] |
| 53 | + pkgNEVR = msg[7] |
| 54 | + altNEVR = msg[9] |
| 55 | + pkgNEVR = pkgNEVR.rsplit('.', 1)[0] + '@' + pkgNEVR.rsplit('.', 1)[1] |
| 56 | + altNEVR = altNEVR.rsplit('.', 1)[0] + '@' + altNEVR.rsplit('.', 1)[1] |
| 57 | + remove_conflict(pkgNEVR) |
| 58 | + elif prob[1][0] == rpm.RPMPROB_FILE_CONFLICT: |
| 59 | + msg = prob[0].split() |
| 60 | + fname = msg[1] |
| 61 | + pkgNEVR = msg[5] |
| 62 | + altNEVR = msg[11] |
| 63 | + pkgNEVR = pkgNEVR.rsplit('.', 1)[0] + '@' + pkgNEVR.rsplit('.', 1)[1] |
| 64 | + altNEVR = altNEVR.rsplit('.', 1)[0] + '@' + altNEVR.rsplit('.', 1)[1] |
| 65 | + remove_conflict(pkgNEVR) |
| 66 | + else: |
| 67 | + retry = 0 |
| 68 | + |
| 69 | prog.setDone() |
| 70 | - if probs: |
| 71 | + if probs and (not retry): |
| 72 | raise Error, "\n".join([x[0] for x in probs]) |
| 73 | prog.stop() |
| 74 | + if retry and len(changeset): |
| 75 | + self.commit(changeset, pkgpaths) |
| 76 | |
| 77 | class RPMCallback: |
| 78 | def __init__(self, prog, upgradednames): |
| 79 | diff --git a/smart/transaction.py b/smart/transaction.py |
| 80 | index 4b90cb7..3e043e9 100644 |
| 81 | --- a/smart/transaction.py |
| 82 | +++ b/smart/transaction.py |
| 83 | @@ -555,6 +555,8 @@ class Transaction(object): |
| 84 | changeset.set(pkg, INSTALL) |
| 85 | isinst = changeset.installed |
| 86 | |
| 87 | + attempt = sysconf.has("attempt-install", soft=True) |
| 88 | + |
| 89 | # Remove packages conflicted by this one. |
| 90 | for cnf in pkg.conflicts: |
| 91 | for prv in cnf.providedby: |
| 92 | @@ -564,11 +566,16 @@ class Transaction(object): |
| 93 | if not isinst(prvpkg): |
| 94 | locked[prvpkg] = (LOCKED_CONFLICT_BY, pkg) |
| 95 | continue |
| 96 | - if prvpkg in locked: |
| 97 | - raise Failed, _("Can't install %s: conflicted package " |
| 98 | - "%s is locked") % (pkg, prvpkg) |
| 99 | - self._remove(prvpkg, changeset, locked, pending, depth) |
| 100 | - pending.append((PENDING_UPDOWN, prvpkg)) |
| 101 | + if attempt: |
| 102 | + del changeset[pkg] |
| 103 | + raise Failed, _("Can't install %s: it conflicts with package " |
| 104 | + "%s") % (pkg, prvpkg) |
| 105 | + else: |
| 106 | + if prvpkg in locked: |
| 107 | + raise Failed, _("Can't install %s: conflicted package " |
| 108 | + "%s is locked") % (pkg, prvpkg) |
| 109 | + self._remove(prvpkg, changeset, locked, pending, depth) |
| 110 | + pending.append((PENDING_UPDOWN, prvpkg)) |
| 111 | |
| 112 | # Remove packages conflicting with this one. |
| 113 | for prv in pkg.provides: |
| 114 | @@ -579,12 +586,18 @@ class Transaction(object): |
| 115 | if not isinst(cnfpkg): |
| 116 | locked[cnfpkg] = (LOCKED_CONFLICT, pkg) |
| 117 | continue |
| 118 | - if cnfpkg in locked: |
| 119 | + if attempt: |
| 120 | + del changeset[pkg] |
| 121 | raise Failed, _("Can't install %s: it's conflicted by " |
| 122 | - "the locked package %s") \ |
| 123 | - % (pkg, cnfpkg) |
| 124 | - self._remove(cnfpkg, changeset, locked, pending, depth) |
| 125 | - pending.append((PENDING_UPDOWN, cnfpkg)) |
| 126 | + "the package %s") \ |
| 127 | + % (pkg, cnfpkg) |
| 128 | + else: |
| 129 | + if cnfpkg in locked: |
| 130 | + raise Failed, _("Can't install %s: it's conflicted by " |
| 131 | + "the locked package %s") \ |
| 132 | + % (pkg, cnfpkg) |
| 133 | + self._remove(cnfpkg, changeset, locked, pending, depth) |
| 134 | + pending.append((PENDING_UPDOWN, cnfpkg)) |
| 135 | |
| 136 | # Remove packages with the same name that can't |
| 137 | # coexist with this one. |
| 138 | @@ -594,10 +607,15 @@ class Transaction(object): |
| 139 | if not isinst(namepkg): |
| 140 | locked[namepkg] = (LOCKED_NO_COEXIST, pkg) |
| 141 | continue |
| 142 | - if namepkg in locked: |
| 143 | + if attempt: |
| 144 | + del changeset[pkg] |
| 145 | raise Failed, _("Can't install %s: it can't coexist " |
| 146 | "with %s") % (pkg, namepkg) |
| 147 | - self._remove(namepkg, changeset, locked, pending, depth) |
| 148 | + else: |
| 149 | + if namepkg in locked: |
| 150 | + raise Failed, _("Can't install %s: it can't coexist " |
| 151 | + "with %s") % (pkg, namepkg) |
| 152 | + self._remove(namepkg, changeset, locked, pending, depth) |
| 153 | |
| 154 | # Install packages required by this one. |
| 155 | for req in pkg.requires + pkg.recommends: |
| 156 | @@ -1176,6 +1194,8 @@ class Transaction(object): |
| 157 | |
| 158 | self._policy.runStarting() |
| 159 | |
| 160 | + attempt = sysconf.has("attempt-install", soft=True) |
| 161 | + |
| 162 | try: |
| 163 | changeset = self._changeset.copy() |
| 164 | isinst = changeset.installed |
| 165 | @@ -1190,7 +1210,11 @@ class Transaction(object): |
| 166 | locked[pkg] = (LOCKED_KEEP, None) |
| 167 | elif op is INSTALL: |
| 168 | if not isinst(pkg) and pkg in locked: |
| 169 | - raise Failed, _("Can't install %s: it's locked") % pkg |
| 170 | + if attempt: |
| 171 | + iface.warning(_("Can't install %s: it's locked") % pkg) |
| 172 | + del changeset[pkg] |
| 173 | + else: |
| 174 | + raise Failed, _("Can't install %s: it's locked") % pkg |
| 175 | changeset.set(pkg, INSTALL) |
| 176 | locked[pkg] = (LOCKED_INSTALL, None) |
| 177 | elif op is REMOVE: |