Improve error handling when loading app plugins

Improve error handling when loading app plugins on scenarios that DRBD
fails to sync the helm overrides folder.

This commit fixes a behavior that, in such scenarios, triggers the
generic app plugin which, in turn, raises the following cryptic error
message: "Automatic apply is disabled".

That behavior was changed to raising a SysinvException while attempting
to load the plugins. In addition, the exception now contains a more
descriptive message, clearly stating that there was a failure while
loading the application plugins, also mentioning the specific plugin
folders that could not be found. That exception is handled and logged
so that sysinv can keep running. If DRBD succeeds to sync after failing,
plugins are properly loaded and normal operation can resume.

Test Plan:
PASS: build-pkgs and build-image
PASS: AIO-SX full system deploy
PASS: Simulate the DRBD sync failure by renaming /opt/platform/helm and
      restarting sysinv. Then watch the exception being raised and
      logged referencing the correct plugin folder, rather than
      displaying the "Automatic apply is disabled for
      platform-integ-apps" message. Rename the overrides folder back to
      its original name and check if plugins were correctly loaded.
PASS: Same as above but renaming the folder back and forth while
      sysinv is running.

Closes-Bug: 2024491
Change-Id: Iefa4259fd468a9ae582fc1138b1d1022eba36b0d
Signed-off-by: Igor Soares <Igor.PiresSoares@windriver.com>
This commit is contained in:
Igor Soares 2023-06-16 19:40:34 -03:00
parent 2b9ea72538
commit 9c6d1a77eb
2 changed files with 46 additions and 20 deletions

View File

@ -3662,7 +3662,10 @@ class PluginHelper(object):
constants.APP_APPLY_SUCCESS,
constants.APP_APPLY_FAILURE,
constants.APP_RESTORE_REQUESTED]:
self.activate_plugins(AppOperator.Application(app))
try:
self.activate_plugins(AppOperator.Application(app))
except exception.SysinvException:
LOG.exception("Error while loading plugins for {}".format(app.name))
def install_plugins(self, app):
""" Install application plugins. """
@ -3706,30 +3709,44 @@ class PluginHelper(object):
def activate_plugins(self, app):
pth_fqpn = self._get_pth_fqpn(app)
# If this isn't an app with plugins or the plugin path is already
# active, skip activation
if not app.system_app or os.path.isfile(pth_fqpn):
# Check if plugins are available for the given app and already loaded
if app.system_app and app.sync_plugins_dir in site.removeduppaths():
return
# Add a .pth file to a site-packages directory so the plugin is picked
# automatically on a conductor restart
with open(pth_fqpn, 'w') as f:
f.write(app.sync_plugins_dir + '\n')
LOG.info("PluginHelper: Enabled plugin directory %s: created %s" % (
app.sync_plugins_dir, pth_fqpn))
# If a plugin path does not exist but a .pth files does then raise an
# exception because the path was supposed to be available at this point.
# If a plugin path does exist then activate the plugins.
# Otherwise, the app does not have any plugins and activation should
# be skipped.
# Note: If app.system_app equals true that implies that app.sync_plugins_dir
# exists and is readable.
if not app.system_app and os.path.isfile(pth_fqpn):
raise exception.SysinvException(_(
"Error while activating plugins for {}. "
"File {} was found but the required plugin "
"directory {} does not exist."
.format(app.name, pth_fqpn, app.sync_plugins_dir)))
elif app.system_app:
# Add a .pth file to a site-packages directory so the plugin is picked
# automatically on a conductor restart
if not os.path.isfile(pth_fqpn):
with open(pth_fqpn, 'w') as f:
f.write(app.sync_plugins_dir + '\n')
LOG.info("PluginHelper: Enabled plugin directory %s: created %s" % (
app.sync_plugins_dir, pth_fqpn))
# Make sure the sys.path reflects enabled plugins Add the plugin to
# sys.path
site.addsitedir(app.sync_plugins_dir)
# Make sure the sys.path reflects enabled plugins Add the plugin to
# sys.path
site.addsitedir(app.sync_plugins_dir)
# Find the distribution and add it to the resources working set
for d in pkg_resources.find_distributions(app.sync_plugins_dir,
only=True):
pkg_resources.working_set.add(d, entry=None, insert=True,
replace=True)
# Find the distribution and add it to the resources working set
for d in pkg_resources.find_distributions(app.sync_plugins_dir,
only=True):
pkg_resources.working_set.add(d, entry=None, insert=True,
replace=True)
if self._helm_op:
self._helm_op.discover_plugins()
if self._helm_op:
self._helm_op.discover_plugins()
def deactivate_plugins(self, app):
# If the application doesn't have any plugins, skip deactivation

View File

@ -6631,6 +6631,10 @@ class ConductorManager(service.PeriodicService):
except exception.LifecycleSemanticCheckException as e:
LOG.info("Auto-apply failed prerequisites for {}: {}".format(app.name, e))
return
except exception.SysinvException:
LOG.exception("Internal sysinv error while auto applying {}"
.format(app.name))
return
except Exception as e:
LOG.exception("Automatic operation:{} "
"for app {} failed with: {}".format(hook_info,
@ -6669,6 +6673,11 @@ class ConductorManager(service.PeriodicService):
except exception.LifecycleSemanticCheckOperationNotSupported as e:
LOG.debug(e)
return
except exception.SysinvException:
LOG.exception("Internal sysinv error while checking automatic "
"updates for {}"
.format(app.name))
return
except Exception as e:
LOG.exception("Automatic operation:{} "
"for app {} failed with: {}".format(hook_info,