Ignore:
Timestamp:
Apr 3, 2017, 3:54:11 PM (7 years ago)
Author:
joergs
Message:

added queries 'listInstalled', 'isInstalled' and 'clientLastSeen'
Nagios NRPE compatible output for some queries
prepared for python3 (print function)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • opsi/server/dass-opsi-tools/usr/bin/opsiclient

    r1177 r1234  
    55"""ospi-client: performs operation for opsi clients on opsi server via JSON-RPC."""
    66
     7from __future__ import print_function
     8
    79__author__ = "Joerg Steffens"
    8 __copyright__ = "Copyright 2012-2015, dass IT GmbH"
     10__copyright__ = "Copyright 2012-2017, dass IT GmbH"
    911__license__ = "GPL"
    10 __version__ = "1.1"
     12__version__ = "1.2"
    1113__email__ = "joerg.steffens@dass-it.de"
    1214
     
    1921
    2022import argparse
     23from datetime import datetime, timedelta
     24from dateutil import parser as dateparser
    2125import jsonrpc
    2226import logging
    2327import os
    2428from pprint import pprint, pformat
     29import sys
    2530import time
    2631
     
    2934HelpEpilog="WARNING: python-json-rpc is known to have problems with HTTP proxies. In case of problems, make sure, the environment variables http_proxy and/or https_proxy are *not* set."
    3035
     36class Nrpe:
     37    OK = 0
     38    WARNING = 1
     39    CRITICAL = 2
     40    UNKNOWN = 3
     41   
     42    def toString(self, status):
     43        if status == self.OK:
     44            return 'OK'
     45        elif status == self.WARNING:
     46            return 'WARNING'
     47        elif status == self.CRITICAL:
     48            return 'CRITICAL'
     49        else:
     50            return 'UNKNOWN'
     51
     52class Output(Nrpe):
     53    def __init__(self, nagios=False):
     54        self.nagios = nagios
     55        self.result = Nrpe.UNKNOWN
     56        self.message = ''
     57
     58    def setStatus(self, status, message = None):
     59        self.result = status
     60        if message is not None:
     61            self.setMessage(message)
     62
     63    def setMessage(self, string):
     64        self.message = string
     65
     66    def finalize(self):
     67        if self.nagios:
     68            print('{0} - {1}'.format(self.toString(self.result), self.message))
     69            sys.exit(self.result)
     70        else:
     71            print('{0} - {1}'.format(self.toString(self.result), self.message))
     72            return (self.result == Nrpe.OK)
     73
    3174class OpsiRpc:
    3275
     
    3578    ProductAttributesCopy = ['actionRequest','actionResult','installationStatus','packageVersion','productVersion']
    3679
    37     def __init__(self, urlJsonRpc = UrlJsonRpcDefault, debug=False ):
     80    def __init__(self, urlJsonRpc = UrlJsonRpcDefault, debug=False, nagios=False):
    3881        self.logger=logging.getLogger(__name__)
    3982        self.debug=debug
     83        self.nagios=nagios
    4084        self.urlJsonRpc=urlJsonRpc
    4185        self.rpc=jsonrpc.ServiceProxy(self.urlJsonRpc)
     
    4488
    4589    def list(self):
    46         print( "\n".join( self.rpc.getClientIds_list() ) )
    47         return True
     90        try:
     91            print( "\n".join( self.rpc.getClientIds_list() ) )
     92        except jsonrpc.json.JSONDecodeException as e:
     93            self.logger.debug( pformat(self.rpc.getClientIds_list()) )
     94            self.logger.exception( "failed" )
     95            return True
    4896
    4997
     
    5199        return self.rpc.productOnClient_getObjects( [], { "productId": product, "installationStatus": "installed" } )
    52100
     101    def getHardwareSerialNumber(self, src):
     102        serialNumbers  = self.rpc.auditHardwareOnHost_getHashes( [], {"hostId":src, "hardwareClass":"BIOS", "serialNumber":"*"} )
     103        serialNumbers += self.rpc.auditHardwareOnHost_getHashes( [], {"hostId":src, "hardwareClass":"CHASSIS", "serialNumber":"*"} )
     104        result = set()
     105        for i in serialNumbers:
     106            if i['serialNumber']:
     107                result.add(i['serialNumber'])
     108        if len(result) == 1:
     109            return result.pop()
     110        elif len(result) > 1:
     111            self.logger.warning("found more then one serial number")
     112            return list(result)
    53113
    54114    def listClients( self, product ):
    55115        if product:
    56116            for client in self.getClientsWithProduct( product ):
    57                 print client['clientId']
     117                print(client['clientId'])
    58118        else:
    59119            return self.list()
     
    65125    def info(self, src):
    66126        if not self.exists( src ):
    67             print "failed: opsi client", src, "does not exist"
     127            print("failed: opsi client", src, "does not exist")
    68128            return False
    69         print src + ":"
     129        print(src + ":")
    70130        host = self.rpc.host_getHashes( [], {"id":src} )[0]
    71         print "  IP:", host["ipAddress"]
    72         print "  MAC:", host["hardwareAddress"]
    73         print "  inventory:", host["inventoryNumber"]
    74         print "  last seen:", host["lastSeen"]
    75         print "  notes:", host["notes"]
    76         print "  depot:", self.clientGetDepot( src )
    77 
    78         print "  products:"
     131        print("  IP:", host["ipAddress"])
     132        print("  MAC:", host["hardwareAddress"])
     133        print("  inventory:", host["inventoryNumber"])
     134        print("  serial number:", self.getHardwareSerialNumber(src))
     135        print("  last seen:", host["lastSeen"])
     136        print("  notes:", host["notes"])
     137        print("  depot:", self.clientGetDepot(src))
     138
     139        print("  products:")
    79140        products = self.getProductOnClient( src, [] )
    80141        for i in products:
    81             print "    " + i['productId'] + ":"
    82             print "      " + i['installationStatus'], "(",
     142            print("    " + i['productId'] + ":")
     143            print("      " + i['installationStatus'], "(", end='')
    83144            if i['actionRequest']:
    84                 print i['actionRequest'],
     145                print(i['actionRequest'], end='')
    85146                if i['actionProgress']:
    86                     print i['actionProgress'],
    87             print ")"
    88             print "      ",
     147                    print(i['actionProgress'], end='')
     148            print(")")
     149            print("      ", end='')
    89150            pprint( i, indent=8 )
    90151        return True
     
    159220    def copyClient( self, src, dst, ipAddress = None, hardwareAddress = None, depot = None, description = "", copyProperties = True ):
    160221
    161         print "create/update", dst, "from template", src + ":",
     222        print("create/update", dst, "from template", src + ":", end='')
    162223        obj = {
    163224          "id" : dst,
     
    185246        if copyProperties:
    186247            if self.debug:
    187                 print "copy product properties"
     248                print("copy product properties")
    188249            if not depot:
    189250                # get default Properties from Master Depot Server (OpsiConfigserver)
    190251                depot = self.getOpsiConfigserverId()
    191252            self.copyProductPropertyState( src, dst, depot )
    192         print "done"
     253        print("done")
    193254        return True
    194255
     
    201262        for i in products_src:
    202263            if self.debug:
    203                 print i['productId']
     264                print(i['productId'])
    204265                pprint( i )
    205266            i['clientId'] = dst
     
    230291                        use_default=True
    231292            if self.debug:
    232                 print i['productId'], "-", i["propertyId"] + ": ", pformat(i["values"]),
     293                print(i['productId'], "-", i["propertyId"] + ": ", pformat(i["values"]), end='')
    233294                if use_default:
    234                     print "(use default)"
     295                    print("(use default)")
    235296                else:
    236                     print "(set, default:", default_value, ")"
     297                    print("(set, default:", default_value, ")")
    237298            if not use_default:
    238299                i['objectId'] = dst
     
    269330        fd.write( '  Address  = "' + client['clientId'] + '"' + "\n" )
    270331        # ipAddress: method host_getObjects [] '{"id":client['clientId']}'
    271         #print "  # Address =", ipAddress
     332        #print("  # Address =", ipAddress)
    272333        fd.write( '  Password = "' + properties['filedaemon_full_password'] + '"' + "\n" )
    273334        try:
     
    348409            return True
    349410
     411    def __getVersionString(self, product):
     412        return '{productVersion}-{packageVersion}'.format(**product)
     413
     414    def getProductCurrentVersion(self, productId):
     415        products = self.rpc.product_getHashes( [], { 'id': productId } )
     416        if products:
     417            return self.__getVersionString(products[0])
     418        else:
     419            return None
     420
     421    def __getClientId(self, d):
     422        return d.get("clientId")
     423
     424    def listInstalled(self, productId):
     425        productVersion = self.getProductCurrentVersion(productId)
     426        self.logger.debug('version: {0}'.format(productVersion))
     427        products = self.rpc.productOnClient_getHashes( [], { 'productId': productId } )
     428        for i in sorted(products, key=self.__getClientId):
     429            i['version'] = self.__getVersionString(i)
     430            if i.get('installationStatus') == 'installed':
     431                if productVersion != i['version']:
     432                    i['proposedAction'] = 'update'
     433                else:
     434                    i['proposedAction'] = 'None'
     435                print('{clientId}: {version} (proposed action={proposedAction})'.format(**i))
     436            else:
     437                i['proposedAction'] = 'install'
     438                print('{clientId}: (proposed action={proposedAction})'.format(**i))
     439                self.logger.debug('{clientId}: {actionRequest} {installationStatus} {version}'.format(**i))
     440            #pprint( i, indent=8 )
     441        return True
     442
     443    def isInstalled(self, clientId, productId):
     444        """
     445        CRITICAL: not installed
     446        WARNING: installed, but not current version
     447        OK: current version is installed
     448        UNKNOWN: otherwise
     449        """
     450        output = Output(self.nagios)
     451        if not self.exists(clientId):
     452            output.setMessage("failed: opsi client {0} does not exist".format(clientId))
     453            return output.finalize()
     454        productVersion = self.getProductCurrentVersion(productId)
     455        if not productVersion:
     456            output.setMessage("failed: product {0} does not exist".format(productId))
     457            return output.finalize()
     458        self.logger.debug('version: {0}'.format(productVersion))
     459        products = self.rpc.productOnClient_getHashes( [], { "clientId": clientId,'productId': productId } )
     460        if len(products) != 1:
     461            print("failed: opsi client ({0}) product ({1}) combination does not exist".format(clientId, productId))
     462            return False           
     463        for i in sorted(products, key=self.__getClientId):
     464            i['version'] = self.__getVersionString(i)
     465            if i.get('installationStatus') == 'installed':
     466                if productVersion != i['version']:
     467                    i['proposedAction'] = 'update'
     468                    output.setStatus(Nrpe.WARNING)
     469                else:
     470                    i['proposedAction'] = 'None'
     471                    output.setStatus(Nrpe.OK)
     472                output.setMessage('{version} (proposed action={proposedAction})'.format(**i))
     473            else:
     474                i['proposedAction'] = 'install'
     475                output.setStatus(Nrpe.CRITICAL, 'not installed (proposed action={proposedAction})'.format(**i))
     476                self.logger.debug('{clientId}: {actionRequest} {installationStatus} {version}'.format(**i))
     477        return output.finalize()
     478
     479
     480    def clientLastSeen(self, clientId):
     481        """
     482        < 1 day: OK
     483        < 2 days: WARNING
     484        otherwise: CRITICAL
     485        """
     486        output = Output(self.nagios)
     487        if not self.exists(clientId):
     488            output.setMessage("failed: opsi client {0} does not exist".format(clientId))
     489            return output.finalize()
     490        host = self.rpc.host_getHashes( [], {"id":clientId} )[0]
     491        lastSeen = dateparser.parse(host["lastSeen"])
     492        output.setMessage(str(lastSeen))
     493        diff = datetime.now() - lastSeen
     494        output.setMessage('{0} ({1} ago)'.format(str(lastSeen), diff))
     495        if diff < timedelta(1):
     496            output.setStatus(Nrpe.OK)
     497        elif diff < timedelta(2):
     498            output.setStatus(Nrpe.WARNING)
     499        else:
     500            output.setStatus(Nrpe.CRITICAL)
     501        return output.finalize()
     502
    350503
    351504
     
    358511
    359512    parser.add_argument( '--debug', action='store_true', help="enable debugging output" )
     513    parser.add_argument('--nagios', action='store_true', help='output in Nagios NRPE format')
    360514
    361515    parser_url = parser.add_mutually_exclusive_group(required=True)
     
    395549    parser_info = subparsers.add_parser('info', help='print information about a opsi client' )
    396550    parser_info.add_argument( 'src', help="opsi client" )
     551   
     552    parser_clientLastSeen = subparsers.add_parser('clientLastSeen', help='print information about a opsi client' )
     553    parser_clientLastSeen.add_argument( 'client', help="opsi client" )
     554
     555    parser_listInstalled = subparsers.add_parser('listInstalled', help='check if product is installed on client')
     556    parser_listInstalled.add_argument('product', help='opsi product')
     557
     558    parser_isInstalled = subparsers.add_parser('isInstalled', help='check if product is installed on client')
     559    parser_isInstalled.add_argument('client', help='opsi client')
     560    parser_isInstalled.add_argument('product', help='opsi product')
    397561
    398562    parser_update = subparsers.add_parser('update', help='update/create a opsi client')
     
    422586            parser.error( "argument --url is required" )
    423587
    424     opsi=OpsiRpc( url, args.debug )
     588    opsi=OpsiRpc(url, args.debug, args.nagios)
    425589
    426590    result = True
     
    443607        elif args.subcommand == "update":
    444608            result = opsi.updateClient( args.src, None, args.description, args.notes, args.inventory, args.mac, args.ip, args.depot )
    445         else:
    446             print "not yet implemented"
     609        elif args.subcommand == 'listInstalled':
     610            result = opsi.listInstalled(args.product)
     611        elif args.subcommand == 'isInstalled':
     612            result = opsi.isInstalled(args.client, args.product)
     613        elif args.subcommand == 'clientLastSeen':
     614            result = opsi.clientLastSeen(args.client)
     615        else:
     616            print("not yet implemented")
    447617    except IOError as e:
    448618        result = False
    449619        # connection refused
    450         print "failed:", e
    451 
    452     if args.debug: print result
     620        print("failed:", e)
     621
     622    if args.debug: print(result)
    453623
    454624    if result:
Note: See TracChangeset for help on using the changeset viewer.