reporting on patches using salt

We have recently switched our Susemanager from spacewalk to salt. With spacewalk, I could run spacewalk-channel and grep out patches with a critical status. However, with salt, I can not find a command-line equivalent. I am trying to create a script that will email me a list of any criticals received during the previous night’s upgrade.

Anybody know how to accomplish this with salt?

How exactly do you do that? Isn’t spacewalk-channel rather used to subscribe/unsubscribe to/from channels from within the client? How do you call it to extract the information about patches that you are looking for?

Why not simply use zypper lp? You can grep the output for severity as needed. The zypper module of Salt could be used as well, but it cannot list available patches yet, only package updates via the list_upgrades call. If this is suitable for you though you could call it locally on a minion via salt-call pkg.list_upgrades.

We thought about that but zypper lp only shows “Repository”, “Name”, “Version”, “Category”, and “Status”. It doesn’t show the severity. I thought maybe “Category” would do it but it is only security/optional/recommend. We’re looking for something to display the “low”, “important”, “moderate”, “critical”, and then be able to grep or filter on just the “critical”. But can’t find a CL-switch for zypper to show that.

Hi
What about zypper if, for example;

zypper if -t patch SUSE-SLE-RPI-12-SP2-2017-135 SUSE-SLE-RPI-12-SP2-2017-138 |grep Summary
Summary     : Recommended update for grub2
Summary     : moderate: Security update for openssh


Cheers Malcolm °¿° SUSE Knowledge Partner (Linux Counter #276890)
openSUSE Leap 42.1|GNOME 3.16.2|4.1.36-44-default
If you find this post helpful and are logged into the web interface,
please show your appreciation and click on the star below… Thanks!

You could use the Susemanager API to accomplish that. It should work with traditional Spacewalk Clients and with Salt Clients.

I’ve created a small Example for you :smiley:

#!/usr/bin/python
import xmlrpclib
from optparse import OptionParser, OptionGroup

MANAGER_URL = "http://manager.example.com/rpc/api"
MANAGER_LOGIN = "username"
MANAGER_PASSWORD = "password"


def main():
    if options.server != '0':
        sm = susemanager(options.user, options.passw, options.url)
        sm.patchStatus(options.server)
    else: 
        print "No Parameters specified! Use --help!"


class susemanager(object):
    def __init__(self, user, password, url):
        self.client = xmlrpclib.Server(url, verbose=0)
        self.key = self.client.auth.login(user, password)
        
    def __del__(self):
        self.client.auth.logout(self.key)
        self.client=""
        self.key=""
        
#### SM - FUNCTIONS
### getSysId - Function
### - Parameter is Name of the System in Susemanager (STRING)
### - Returns the internal SystemID from Susemanager
### - SystemID is needed for other Functions
    def getSysId (self, name):
        system = self.client.system.getId(self.key, name)
        systemid = system[0]['id']
        return systemid
     
    def listSystems(self):
       systems = self.client.system.listSystems(self.key)
       return systems     
     
        
### getRelevantPatchesByType - Function
### - Parameter is Name of the System in Susemanager (STRING) and Patch - Type ('Security Advisory', 'Product Enhancement Advisory', 'Bug Fix Advisory') 
### - Gets SystemId, Patch List and returns the Elements on the List
    def getRelevantPatchesByType(self,name, type):
       systemid = self.getSysId (name)
       errata = self.client.system.getRelevantErrataByType(self.key, systemid, type)
       return errata

### patchStatus - Function
### - Parameter is Name of the System in Susemanager (STRING)
### - Prints Current Patchstatus for the specified System as Output
    def patchStatus (self, name):  
       security = self.getRelevantPatchesByType(name, 'Security Advisory')
       crit = 0
       crit_list = []
       mod = 0
       mod_list = []
       imp = 0 
       imp_list = []
       low = 0
       low_list = []
       
       for patch in security: ### Loop over the Result
          if patch['advisory_synopsis'].find('critical') > -1:
              crit += 1
              crit_list.append(patch)
          elif patch['advisory_synopsis'].find('important') > -1:
              imp += 1
              imp_list.append(patch)
          elif patch['advisory_synopsis'].find('moderate') > -1:
              mod += 1
              mod_list.append(patch)
          elif patch['advisory_synopsis'].find('low') > -1:
              low += 1
              low_list.append(patch)
           
       enhancements = self.getRelevantPatchesByType(name, 'Product Enhancement Advisory')
       bugfix = self.getRelevantPatchesByType(name, 'Bug Fix Advisory')
       
       print "############################################################################"
       print "### System Summary - %s " % (name)
       print "############################################################################"
       print "### Critical: %s Important: %s Moderate: %s Low: %s " % (crit, imp, mod, low)
       print "### Bugfix: %s Enhancement: %s " % (len(bugfix), len(enhancements))
       print "############################################################################"
       print ""
       print "Critical Patches:"
       print "############################################################################"
       for critcal in crit_list: 
         print critcal['advisory_name']+' - '+critcal['advisory_synopsis']
       print ""
       print "Important Patches:"
       print "############################################################################"
       for important in imp_list: 
         print important['advisory_name']+' - '+important['advisory_synopsis']
       print ""
      
       print "Moderate Patches:"
       print "############################################################################"
       for moderate in mod_list: 
         print moderate['advisory_name']+' - '+moderate['advisory_synopsis']
       print ""
       print "Low Patches:"
       print "############################################################################"
       for lows in low_list: 
         print lows['advisory_name']+' - '+lows['advisory_synopsis']
       print ""  
       print "Bugfix Patches:"
       print "############################################################################"
       for sbugfix in bugfix: 
         print sbugfix['advisory_name']+' - '+sbugfix['advisory_synopsis']
       print ""  
       print "Enhancement Patches:"
       print "############################################################################"
       for enh in mod_list: 
         print enh['advisory_name']+' - '+enh['advisory_synopsis']
 
 
parser = OptionParser()
usage = "usage: %prog [options]"
parser.add_option("--server", dest="server", default='0',
                  help="Patchstatus for single Server", metavar="SERVER")
parser.add_option("--url", dest="url", default=MANAGER_URL,
                  help="Susemanager API URL", metavar="URL")
parser.add_option("--user", dest="user", default=MANAGER_LOGIN,
                  help="Susemanager API User", metavar="USER")
parser.add_option("--pass", dest="passw", default=MANAGER_PASSWORD,
                  help="Susemanager API Password", metavar="PASS")
(options, args) = parser.parse_args()
#### Default Call to main() Function
if __name__ == "__main__":
  main()         

Example:

USAGE: <scriptname>.py --server=<name> 
XXXXXX:/tmp # ./patchStatus.py --server=XXXXXXXXXXX
############################################################################
### System Summary - XXXXXXXXXXXXX
############################################################################
### Critical: 0 Important: 0 Moderate: 1 Low: 0
### Bugfix: 2 Enhancement: 0
############################################################################

Critical Patches:
############################################################################

Important Patches:
############################################################################

Moderate Patches:
############################################################################
SUSE-12-SP1-2017-150 - moderate: Security update for pcsc-lite

Low Patches:
############################################################################

Bugfix Patches:
############################################################################
SUSE-12-SP1-2017-156 - Recommended update for yast2-ruby-bindings
SUSE-12-SP1-2017-151 - Recommended update for kernel-firmware

Enhancement Patches:
############################################################################
SUSE-12-SP1-2017-150 - moderate: Security update for pcsc-lite