# reporting on patches using salt

**URL:** <https://forums.suse.com/t/reporting-on-patches-using-salt/29269>\
**Category:** SUSE Multi Linux Manager\
**Created:** [January 23, 2017, 9:59pm UTC](https://forums.suse.com/t/reporting-on-patches-using-salt/29269 "2017-01-23T21:59:10Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![jgosney](https://avatars.discourse-cdn.com/v4/letter/j/bb73d2/32.png) [@jgosney](https://forums.suse.com/u/jgosney)\
**Post date:** [January 23, 2017, 9:59pm UTC](https://forums.suse.com/t/reporting-on-patches-using-salt/29269/1 "2017-01-23T21:59:10Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![j\_renner](https://avatars.discourse-cdn.com/v4/letter/j/ba9def/32.png) [@j\_renner](https://forums.suse.com/u/j_renner)\
**Post date:** [January 24, 2017, 4:57pm UTC](https://forums.suse.com/t/reporting-on-patches-using-salt/29269/2 "2017-01-24T16:57:03Z")

</div>

> [@jgosney;36192](#):
>
> 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.

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?

> [@jgosney;36192](#):
>
> Anybody know how to accomplish this with salt?

Why not simply use zypper lp? You can grep the output for severity as needed. The [zypper module of Salt](https://docs.saltstack.com/en/latest/ref/modules/all/salt.modules.zypper.html) 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.

---

<div class="post-metadata">

**Author:** ![jgosney](https://avatars.discourse-cdn.com/v4/letter/j/bb73d2/32.png) [@jgosney](https://forums.suse.com/u/jgosney)\
**Post date:** [January 24, 2017, 5:12pm UTC](https://forums.suse.com/t/reporting-on-patches-using-salt/29269/3 "2017-01-24T17:12:52Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![malcolmlewis](https://sea2.discourse-cdn.com/flex022/user_avatar/forums.suse.com/malcolmlewis/32/11375_2.png) [@malcolmlewis](https://forums.suse.com/u/malcolmlewis)\
**Post date:** [January 24, 2017, 5:24pm UTC](https://forums.suse.com/t/reporting-on-patches-using-salt/29269/4 "2017-01-24T17:24:57Z")

</div>

> [@](#):
>
> On Tue 24 Jan 2017 03:14:02 PM CST, jgosney wrote:
> 
> 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;

```auto
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!

---

<div class="post-metadata">

**Author:** ![mal90](https://avatars.discourse-cdn.com/v4/letter/m/dec6dc/32.png) [@mal90](https://forums.suse.com/u/mal90)\
**Post date:** [January 30, 2017, 11:49am UTC](https://forums.suse.com/t/reporting-on-patches-using-salt/29269/5 "2017-01-30T11:49:18Z")

</div>

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 😃

```auto
#!/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:

```auto
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
```
