rpm-software-management / yum

[DEPRECATED] YUM package manager
GNU General Public License v2.0
127 stars 87 forks source link

Enhancement: create a yum-cron emitter calling all executables in a directory #120

Closed kevin-j-smith closed 4 years ago

kevin-j-smith commented 4 years ago

Basically I am looking for a yum-cron notification system to be extendable. syslog and email are not the only use cases that we have in our super secure environment where monitoring automation is easier through push vs pull. It would be nice to to have a directory like: /etc/yum/yum.cron.d or /etc/yum/cron.emitters.d where we can store executables that take in json/yaml.

Use cases:

#!/usr/local/bin ruby
require 'yaml'
require 'zabbix_sender'

message_from_yum_cron = YAML.load(ARGV[0])

sender = ZabbixSender.new(zabbix_host: "some-zabbix", port: 10051)
sender.post("host", "yum_cron_updates_failed", messages_from_yum_cron['updates_failed'].size())

I would imagine the emitter would look something like:

import os
import subprocess
import yaml

class PluginEmitter(UpdateEmitter):
    """Emitter class to send messages via custom plugins."""

    default_emitters_directory = '/etc/yum/yum-cron.emitters.d'

    def __init__(self, opts, logger):
        super(PluginEmitter, self).__init__(opts)        
        self.logger = logger
        self.message = {}

    def updatesAvailable(self, summary):
        """Appends a message to the output list stating that there are
        updates available
        :param summary: A human-readable summary of the transaction.
        """
        super(PluginEmitter, self).updatesAvailable(summary)
        if 'updatesAvailable' not in self.message:
            self.message['updatesAvailable'] = []
        self.message['updatesAvailable'].append(summary)

    def updatesDownloaded(self):
        """Append a message to the output list stating that updates
        have been downloaded successfully
        """
        super(PluginEmitter, self).updatesDownloaded()
        self.message['updatesDownloaded'] = True

    def updatesInstalled(self):
        """Append a message to the output list stating that updates
        have been installed successfully
        """
        super(PluginEmitter, self).updatesInstalled()
        self.message['updatesInstalled'] = True

    def setupFailed(self, errmsg):
        """Append a message to the output list stating that setup
        failed, and then call sendMessages to emit the output
        :param errmsgs: a string that contains the error message
        """
        super(PluginEmitter, self).setupFailed(errmsg)
        if 'setupFailed' not in self.message:
            self.message['setupFailed'] = []
        self.message['setupFailed'].append(errmsg)

    def checkFailed(self, errmsg):
        """Append a message to the output stating that checking for
        updates failed, then call sendMessages to emit the output
        :param errmsgs: a string that contains the error message
        """
        super(PluginEmitter, self).checkFailed(errmsg)
        self.subject = "Yum: Failed to check for updates on %s" % self.opts.system_name
        if 'checkFailed' not in self.message:
            self.message['checkFailed'] = []
        self.message['checkFailed'].append(errmsg)

    def downloadFailed(self, errmsg):
        """Append a message to the output list stating that checking
        for group updates failed, then call sendMessages to emit the
        output
        :param errmsgs: a string that contains the error message
        """
        super(PluginEmitter, self).downloadFailed(errmsg)
        if 'downloadFailed' not in self.message:
            self.message['downloadFailed'] = []
        self.message['downloadFailed'].append(errmsg)

    def updatesFailed(self, errmsg):
        """Append a message to the output list stating that installing
        updates failed, then call sendMessages to emit the output
        :param errmsgs: a string that contains the error message
        """
        super(PluginEmitter, self).updatesFailed(errmsg)
        if 'updatesFailed' not in self.message:
            self.message['updatesFailed'] = []
        self.message['updatesFailed'].append(errmsg)

    def sendMessages(self):
        """Call each emitter and send the message in as a parameter"""

        super(PluginEmitter, self).sendMessages()
        # Don't send empty messages
        if not self.message:
            return

        # Get emitters directory
        emitters_directory = self.default_emitters_directory
        if self.opts.emitters_dir:
            emitters_directory = self.opts.emitters_dir

        try:
            for emitter_file in os.listdir(emitters_directory):
                emitter_file_path = os.path.join(emitters_directory, emitter_file)
                # We will ignore nested directories, for now and only concern ourselves with executables.
                if not (os.path.isfile(emitter_file_path) and os.access(emitter_file_path, os.X_OK))
                    self.logger.info("Emitter file is not executable '%s'" % (emitter_file_path))
                    continue
                try:
                    subprocess.check_call([emitter_file_path, yaml.dump(self.message)])
                except CalledProcessError, e:
                    self.logger.error("Emitter '%s' failed: %s" % (emitter_file_path, e))

        except Exception, e:
            self.logger.error("Failed to list emitters directory '%s': %s" % (emitters_directory, e))

I am certain better messaging could be created from parsing the yum output before sending to emitters, but that would not be initial use case.

lukash commented 4 years ago

Hello, yum has been superseded by https://github.com/rpm-software-management/dnf, so any effort in this direction should be done over there. The development of yum has stopped.