integ/base/anaconda/centos/patches/0001-TIS-Progress-and-error-handling.patch
Martin, Chen 683d3632bc rebase anaconda patch to CentOS 7.6 version
Test:
Build pass with build pkgs and build iso, and then build rpms for installer,
update installer and rebuild iso again. And deploy multi-node test pass.

Depends-On: https://review.openstack.org/628835/

Story: 2004522
Task: 28413

Change-Id: Iaba803124b9795df206646f6bc24c2862795bbdd
Signed-off-by: Martin, Chen <haochuan.z.chen@intel.com>
2019-01-07 11:04:18 +08:00

395 lines
13 KiB
Diff

From fa37cfcf560506f49bb00b9d216b1e7646a6905b Mon Sep 17 00:00:00 2001
From: Don Penney <don.penney@windriver.com>
Date: Mon, 13 Nov 2017 17:21:05 -0500
Subject: [PATCH] TIS Progress and error handling
---
data/tmux.conf | 3 +-
pyanaconda/errors.py | 24 +++++++--
pyanaconda/flags.py | 1 +
pyanaconda/install.py | 4 ++
pyanaconda/kickstart.py | 3 ++
pyanaconda/packaging/rpmostreepayload.py | 5 ++
pyanaconda/packaging/yumpayload.py | 15 +++++-
pyanaconda/tisnotify.py | 91 ++++++++++++++++++++++++++++++++
pyanaconda/ui/gui/hubs/progress.py | 4 ++
pyanaconda/ui/tui/spokes/progress.py | 4 ++
10 files changed, 147 insertions(+), 7 deletions(-)
create mode 100644 pyanaconda/tisnotify.py
diff --git a/data/tmux.conf b/data/tmux.conf
index 89f788b..7903b06 100644
--- a/data/tmux.conf
+++ b/data/tmux.conf
@@ -1,6 +1,7 @@
# tmux.conf for the anaconda environment
bind -n M-tab next
+bind -n C-o next
bind -n F1 list-keys
set-option -s exit-unattached off
@@ -10,7 +11,7 @@ set-option -g history-limit 10000
new-session -s anaconda -n main "anaconda"
-set-option status-right '#[fg=blue]#(echo -n "Switch tab: Alt+Tab | Help: F1 ")'
+set-option status-right '#[fg=blue]#(echo -n "Switch: Alt+Tab or Ctrl-o ")'
new-window -d -n shell "bash --login"
new-window -d -n log "tail -F /tmp/anaconda.log"
diff --git a/pyanaconda/errors.py b/pyanaconda/errors.py
index 860b228..1d1d34b 100644
--- a/pyanaconda/errors.py
+++ b/pyanaconda/errors.py
@@ -19,6 +19,7 @@
# Author(s): Chris Lumens <clumens@redhat.com>
from pyanaconda.i18n import _
+from pyanaconda.tisnotify import tisnotify
__all__ = ["ERROR_RAISE", "ERROR_CONTINUE", "ERROR_RETRY",
"InvalidImageSizeError", "MissingImageError", "MediaUnmountError",
@@ -81,6 +82,19 @@ ERROR_RAISE = 0
ERROR_CONTINUE = 1
ERROR_RETRY = 2
+#
+# WRS: If a fatal error occurs in a %pre, anaconda hasn't setup the UI yet,
+# and an exception occurs in the error handler. This is a basic dummy UI
+# to avoid this exception and print the error message.
+#
+class DefaultUI(object):
+ def __init__(self):
+ pass
+
+ def showError(self, msg):
+ print "\n\n", msg
+
+
###
### TOP-LEVEL ERROR HANDLING OBJECT
###
@@ -304,12 +318,12 @@ class ErrorHandler(object):
"""
rc = ERROR_RAISE
+ # WRS: Notify the controller installation has failed
+ tisnotify.failed()
+
if not self.ui:
- # While Pylint thinks something else, this should be likely OK
- # for an exception handler.
- #
- # pylint: disable=misplaced-bare-raise
- raise
+ # WRS: Use the basic UI
+ self.ui = DefaultUI()
_map = {"PartitioningError": self._partitionErrorHandler,
"FSResizeError": self._fsResizeHandler,
diff --git a/pyanaconda/flags.py b/pyanaconda/flags.py
index 8a97f95..3d0d2da 100644
--- a/pyanaconda/flags.py
+++ b/pyanaconda/flags.py
@@ -71,6 +71,7 @@ class Flags(object):
self.ksprompt = True
self.rescue_mode = False
self.kexec = False
+ self.tisNotifyPort = "0"
# nosave options
self.nosave_input_ks = False
self.nosave_output_ks = False
diff --git a/pyanaconda/install.py b/pyanaconda/install.py
index 26e1b26..bd8f85b 100644
--- a/pyanaconda/install.py
+++ b/pyanaconda/install.py
@@ -35,6 +35,9 @@ from pyanaconda.ui.lib.entropy import wait_for_entropy
from pyanaconda.kexec import setup_kexec
from pyanaconda.kickstart import runPostScripts, runPreInstallScripts
from pykickstart.constants import SNAPSHOT_WHEN_POST_INSTALL
+
+from pyanaconda.tisnotify import tisnotify
+
import logging
import blivet
log = logging.getLogger("anaconda")
@@ -139,6 +142,7 @@ def doConfiguration(storage, payload, ksdata, instClass):
with progress_report(N_("Creating snapshots")):
ksdata.snapshot.execute(storage, ksdata, instClass)
+ tisnotify.installed()
progress_complete()
def doInstall(storage, payload, ksdata, instClass):
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index 50515c8..d95b2df 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -90,6 +90,8 @@ from pykickstart.sections import NullSection, PackageSection, PostScriptSection,
from pykickstart.version import returnClassForVersion, RHEL7
from pykickstart.options import KSOptionParser
+from pyanaconda.tisnotify import tisnotify
+
import logging
log = logging.getLogger("anaconda")
stderrLog = logging.getLogger("anaconda.stderr")
@@ -2481,6 +2483,7 @@ def runPreScripts(scripts):
if len(preScripts) == 0:
return
+ tisnotify.preinstall()
log.info("Running kickstart %%pre script(s)")
stdoutLog.info(_("Running pre-installation scripts"))
diff --git a/pyanaconda/packaging/rpmostreepayload.py b/pyanaconda/packaging/rpmostreepayload.py
index 7cf59d7..8896ba1 100644
--- a/pyanaconda/packaging/rpmostreepayload.py
+++ b/pyanaconda/packaging/rpmostreepayload.py
@@ -36,6 +36,8 @@ from gi.repository import Gio
from blivet.size import Size
+from pyanaconda.tisnotify import tisnotify
+
import logging
log = logging.getLogger("anaconda")
@@ -69,6 +71,7 @@ class RPMOSTreePayload(ArchivePayload):
"""Like iutil.execWithRedirect, but treat errors as fatal"""
rc = iutil.execWithRedirect(cmd, argv, **kwargs)
if rc != 0:
+ tisnotify.failed()
exn = PayloadInstallError("%s %s exited with code %d" % (cmd, argv, rc))
if errors.errorHandler.cb(exn) == errors.ERROR_RAISE:
raise exn
@@ -183,6 +186,7 @@ class RPMOSTreePayload(ArchivePayload):
GLib.Variant('a{sv}', pull_opts),
progress, cancellable)
except GLib.GError as e:
+ tisnotify.failed()
exn = PayloadInstallError("Failed to pull from repository: %s" % e)
log.error(str(exn))
if errors.errorHandler.cb(exn) == errors.ERROR_RAISE:
@@ -227,6 +231,7 @@ class RPMOSTreePayload(ArchivePayload):
try:
self._copyBootloaderData()
except (OSError, RuntimeError) as e:
+ tisnotify.failed()
exn = PayloadInstallError("Failed to copy bootloader data: %s" % e)
log.error(str(exn))
if errors.errorHandler.cb(exn) == errors.ERROR_RAISE:
diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py
index c6aa234..a0497e0 100644
--- a/pyanaconda/packaging/yumpayload.py
+++ b/pyanaconda/packaging/yumpayload.py
@@ -46,6 +46,8 @@ from pyanaconda.simpleconfig import simple_replace
from functools import wraps
from urlgrabber.grabber import URLGrabber, URLGrabError
+from pyanaconda.tisnotify import tisnotify
+
import logging
log = logging.getLogger("packaging")
@@ -181,6 +183,8 @@ class YumPayload(PackagePayload):
# save repomd metadata
self._repoMD_list = []
+ self.tisNotifyPort = flags.cmdline.get("tisNotifyPort")
+
self.reset()
def reset(self, root=None, releasever=None):
@@ -1347,6 +1351,8 @@ reposdir=%s
if self.data.packages.handleMissing == KS_MISSING_IGNORE:
return
+ tisnotify.failed()
+
# If we're doing non-interactive ks install, raise CmdlineError,
# otherwise the system will just reboot automatically
if flags.automatedInstall and not flags.ksprompt:
@@ -1524,6 +1530,7 @@ reposdir=%s
try:
self.checkSoftwareSelection()
except DependencyError as e:
+ tisnotify.failed()
if errorHandler.cb(e) == ERROR_RAISE:
progressQ.send_quit(1)
while True:
@@ -1578,6 +1585,10 @@ reposdir=%s
key, text = line.split(":", 1)
msg = progress_map[key] + text
progressQ.send_message(msg)
+ if line.startswith("PROGRESS_POST"):
+ tisnotify.postinstall()
+ elif not text.startswith(" error "):
+ tisnotify.installing(text)
log.debug(msg)
elif line.startswith("DEBUG:"):
log.debug(line[6:])
@@ -1590,7 +1601,8 @@ reposdir=%s
install_errors.append(line[6:])
else:
log.debug(line)
- except IOError as e:
+ except (IOError, OSError) as e:
+ tisnotify.failed()
log.error("Error running anaconda-yum: %s", e)
exn = PayloadInstallError(str(e))
if errorHandler.cb(exn) == ERROR_RAISE:
@@ -1612,6 +1624,7 @@ reposdir=%s
shutil.rmtree(iutil.getSysroot()+"/var/tmp/yum.cache")
if install_errors:
+ tisnotify.failed()
exn = PayloadInstallError("\n".join(install_errors))
if errorHandler.cb(exn) == ERROR_RAISE:
progressQ.send_quit(1)
diff --git a/pyanaconda/tisnotify.py b/pyanaconda/tisnotify.py
new file mode 100644
index 0000000..bf5d9bd
--- /dev/null
+++ b/pyanaconda/tisnotify.py
@@ -0,0 +1,91 @@
+"""
+Copyright (c) 2016-2017 Wind River Systems, Inc.
+ SPDX-License-Identifier: Apache-2.0
+
+
+
+"""
+
+import os
+import re
+import subprocess
+import time
+
+from pyanaconda.flags import flags
+
+class TisNotify():
+
+ def __init__(self):
+ self.tisnotify = flags.cmdline.get("tisnotify")
+ self.regex = re.compile(r'\(([\d\/]*)\)$')
+ self.DEVNULL = open(os.devnull, "w")
+ self.last_installing = 0
+
+ def sendNotification(self, data):
+ try:
+ subprocess.call(['/usr/bin/curl',
+ '--data', data,
+ self.tisnotify],
+ stdout=self.DEVNULL,
+ stderr=self.DEVNULL)
+ except:
+ pass
+
+ def preinstall(self):
+ if self.tisnotify is None:
+ return
+
+ data = "install_state=preinstall"
+ self.sendNotification(data)
+
+ def installing(self, text):
+ if self.tisnotify is None:
+ return
+
+ match = self.regex.search(text)
+ if match is None:
+ return
+
+ if (time.time() - self.last_installing) >= 5:
+ self.last_installing = time.time()
+ data = "install_state=installing&install_state_info=%s" % match.groups()[0]
+ self.sendNotification(data)
+
+ def postinstall(self):
+ if self.tisnotify is None:
+ return
+
+ data = "install_state=postinstall"
+ self.sendNotification(data)
+
+ def installed(self):
+ if self.tisnotify is None:
+ return
+
+ data = "install_state=installed"
+ self.sendNotification(data)
+
+ def failed(self):
+ if self.tisnotify is None:
+ return
+
+ data = "install_state=failed"
+ self.sendNotification(data)
+
+ etc_dir = '/mnt/sysimage/etc'
+ platform_dir = etc_dir + '/platform'
+ failed_flag = platform_dir + '/platform/installation_failed'
+ motd_file = etc_dir + '/motd'
+
+ # Set installation_failed flag, if possible and not already done
+ if os.path.exists(platform_dir) and not os.path.exists(failed_flag):
+ try:
+ subprocess.call(['touch', '/mnt/sysimage/etc/platform/installation_failed'])
+ with open(motd_file, 'a') as f:
+ f.write('Installation failure. Please check logs or reinstall.\n\n')
+ except:
+ pass
+
+
+tisnotify = TisNotify()
+
diff --git a/pyanaconda/ui/gui/hubs/progress.py b/pyanaconda/ui/gui/hubs/progress.py
index 0e4dbed..b342bd5 100644
--- a/pyanaconda/ui/gui/hubs/progress.py
+++ b/pyanaconda/ui/gui/hubs/progress.py
@@ -44,6 +44,8 @@ from pykickstart.constants import KS_SHUTDOWN, KS_REBOOT
from pyanaconda.ui.gui.hubs import Hub
from pyanaconda.ui.gui.utils import gtk_action_nowait, gtk_call_once
+from pyanaconda.tisnotify import tisnotify
+
__all__ = ["ProgressHub"]
class ProgressHub(Hub):
@@ -124,6 +126,8 @@ class ProgressHub(Hub):
# to indicate this method should be removed from the idle loop.
return False
elif code == progressQ.PROGRESS_CODE_QUIT:
+ if args[0] != 0:
+ tisnotify.failed()
sys.exit(args[0])
q.task_done()
diff --git a/pyanaconda/ui/tui/spokes/progress.py b/pyanaconda/ui/tui/spokes/progress.py
index 1feeb08..8221e31 100644
--- a/pyanaconda/ui/tui/spokes/progress.py
+++ b/pyanaconda/ui/tui/spokes/progress.py
@@ -31,6 +31,8 @@ from pyanaconda.ui.tui.spokes import StandaloneTUISpoke
from pyanaconda.ui.tui.hubs.summary import SummaryHub
from pyanaconda.ui.tui.simpleline.base import ExitAllMainLoops
+from pyanaconda.tisnotify import tisnotify
+
__all__ = ["ProgressSpoke"]
class ProgressSpoke(StandaloneTUISpoke):
@@ -101,6 +103,8 @@ class ProgressSpoke(StandaloneTUISpoke):
print('')
return True
elif code == progressQ.PROGRESS_CODE_QUIT:
+ if args[0] != 0:
+ tisnotify.failed()
sys.exit(args[0])
q.task_done()
--
1.8.3.1