"Fossies" - the Fresh Open Source Software Archive

Member "unix/plugins/autodiscover" (15 Sep 2021, 18626 Bytes) of package /linux/misc/pandorafms_agent_unix-7.0NG.757.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "autodiscover": 7.0NG.755_vs_7.0NG.756.

    1 #!/usr/bin/env python3
    2 ###################################################
    3 #
    4 # Pandora FMS Autodiscovery plugin.
    5 # Checks the status of the services in list and monitors CPU and Memory for each of them.
    6 #
    7 # (c) A. Kevin Rojas <kevin.rojas@pandorafms.com>
    8 #
    9 # TO DO LIST:
   10 # - Enable child services detection (Windows)
   11 # - Make CPU/Memory usage available for child services (Windows)
   12 #
   13 ###################################################
   14 
   15 from sys import argv
   16 from sys import path
   17 from sys import stderr
   18 from sys import exit
   19 from subprocess import Popen
   20 from subprocess import PIPE
   21 from subprocess import DEVNULL
   22 from subprocess import getstatusoutput
   23 import psutil
   24 
   25 global module_list
   26 module_list = []
   27 version = "1.1"
   28 
   29 
   30 #########################################################################################
   31 # Powershell class
   32 #########################################################################################
   33 class PSCheck:
   34     @staticmethod 
   35     def check_service(servicename, option=False, memcpu=False):
   36         """Check services with powershell by parsing their DisplayName. Returns a dict\
   37         list with the name of the service and a boolean with its status.\n 
   38         Requires service name (case insensitive)."""
   39         pscall = Popen(["powershell", "Get-Service", "-Name", "'*"+ str(servicename) + "*'",
   40                         "|", "Select-Object", "-ExpandProperty", "Name"], 
   41                         stdout=PIPE, stdin=DEVNULL, stderr=DEVNULL, universal_newlines=True)
   42         result = pscall.communicate()
   43         result = str(result[0]).strip().split("\n")
   44         procname = ''
   45         if result != '':
   46             output = []
   47             for element in result:
   48                 if element != '':
   49                     # Get process name
   50                     procname = PSCheck.get_serviceprocess(element)
   51                     # Get process status
   52                     parstatus = PSCheck.getstatus(element)
   53                     if memcpu == True and parstatus == 1:
   54                         usage = get_memcpu(str(procname), str(element))
   55                         output += usage
   56                     # Generate module with name and status
   57                     parent = service_module(str(element), parstatus)
   58                     output += parent
   59                     if option == True:
   60                         children = PSCheck.getchildren(element, memcpu)
   61                         if type(children) == list and len(children) > 1:
   62                             for child in children:
   63                                 output += child
   64                         else:
   65                             output += children
   66                 else:
   67                     next
   68 
   69             #if output != '':
   70             if output and element and procname:
   71                 return ({"name" : element, "process" : procname, "modules": output})
   72             else:
   73                 return (None)
   74 
   75     @staticmethod
   76     def getchildren(servicename, memcpu=False):
   77         """Gets Dependent services of a given Windows service"""
   78         pschild = Popen(["powershell", "Get-Service", "-Name '" + str(servicename) +
   79                         "' -DS", "|", "Select-Object", "-ExpandProperty", "Name"], 
   80                         stdout=PIPE, stdin=DEVNULL, stderr=DEVNULL, universal_newlines=True)
   81         children = pschild.communicate()[0].strip()
   82         kids = []
   83         for child in (children.split("\n") if children != "" else []):
   84             status = PSCheck.getstatus(child)
   85             kids += service_module(str(child), status, "Service " + str(servicename) + " - Status")
   86             if status:
   87                 if memcpu == True:
   88                     kidsusage = get_memcpu(str(child))
   89                     for usage in kidsusage:
   90                         kids += usage
   91             else:
   92                 next
   93         return (kids)
   94 
   95     @staticmethod
   96     def getstatus(servicename):
   97         """Gets the status of a given Windows service"""
   98         running = Popen(["powershell", "Get-Service", "-Name '" + str(servicename) +
   99                     "' |", "Select-Object", "-ExpandProperty", "Status"],
  100                     stdout=PIPE, stdin=DEVNULL, stderr=DEVNULL, universal_newlines=True)
  101         status = running.communicate()[0].strip()
  102         return (int(status == "Running"))
  103 
  104     @staticmethod
  105     def get_serviceprocess(servicename):
  106         """Gets name of the process of the service"""
  107         service = psutil.win_service_get(servicename)
  108         srv_pid = service.pid()
  109         process = psutil.Process(srv_pid)
  110         proc_name = process.name()
  111         return (proc_name)
  112 
  113 
  114 #########################################################################################
  115 # Services creation
  116 #########################################################################################
  117 
  118 def service_module(name, value, parent=None):
  119     #print ("service_module BEGIN "+str(now(0,1)))
  120     module = [{
  121             "name"              :   "Service "+ name + " - Status",
  122             "type"              :   "generic_proc",
  123             "value"             :   value,
  124             "module_parent"     :   parent,
  125         }]
  126     #print ("service_module END "+str(now(0,1)))
  127     return (module)
  128 
  129 def get_memcpu (process, servicename):
  130     """Creates a module for Memory and CPU for a given process. Returns a list of dictionaries."""
  131     modules = []
  132     if process:
  133         if servicename != None:
  134             parentname = servicename
  135         else:
  136             parentname = process
  137         modules += [{
  138                 "name"         :    "Service "+ process + " - Memory usage",
  139                 "type"          :    "generic_data",
  140                 "value"         :     proc_percentbyname(process)[0],
  141                 "unit"          :     "%",
  142                 "module_parent" :    "Service "+ parentname + " - Status",
  143                 },
  144                 {"name"         :    "Service "+ process + " - CPU usage",
  145                 "type"          :    "generic_data",
  146                 "value"         :     proc_percentbyname(process)[1],
  147                 "unit"          :     "%",
  148                 "module_parent" :    "Service "+ parentname + " - Status",
  149                 }]
  150     return (modules)
  151 
  152 def proc_percentbyname(procname): ############# 03/03/2020
  153     """Gets Memory and CPU usage for a given process. Returns a list."""
  154     #print ("proc_percentbyname BEGIN "+str(now(0,1)))
  155     procs = [p for p in psutil.process_iter() if procname in p.name().lower()]
  156     memory = []
  157     cpu = []
  158     try:
  159         for proc in procs:
  160             if proc.name() == procname:
  161                 cpu.append(proc.cpu_percent(interval=0.5))
  162                 memory.append(proc.memory_percent())
  163             else:
  164                 next
  165     except psutil.NoSuchProcess:
  166         next
  167     #print ("proc_percentbyname END "+str(now(0,1)))
  168     return ([sum(memory),sum(cpu)])
  169 
  170 def win_service(servicelist, option=False, memcpu=False):
  171     """Creates modules for Windows servers."""
  172     modules = []
  173     for srvc in servicelist:
  174         if srvc and len(srvc) > 2:
  175             output = PSCheck.check_service(srvc, option, memcpu)
  176             if output != None and output["modules"]:
  177                 modules += PSCheck.check_service(srvc.strip(), option, memcpu)["modules"]
  178                 module_list.append(srvc)
  179                 #winprocess = output["name"]
  180                 #if memcpu == True:
  181                 #    modules += get_memcpu(winprocess) ## Only available for parent service ATM.
  182             else:
  183                 next
  184         else:
  185             next
  186     for module in modules:
  187         print_module(module, 1)
  188 
  189 
  190 def lnx_service(services_list, memcpu=False):
  191     """Creates modules for Linux servers"""
  192     modules = []
  193     sysctl = getstatusoutput("command -v systemctl")[0]
  194     servic = getstatusoutput("command -v service")[0]
  195     for srvc in services_list:
  196         status = None
  197         if sysctl == 0: 
  198             ### Systemd available
  199             syscall = Popen(["systemctl", "show", "-pLoadState", "-pActiveState", srvc], stdout=PIPE,
  200                                 stdin=DEVNULL, universal_newlines=True)
  201             result = syscall.communicate()
  202             srvstatus= result[0].strip().lower().split("\n")
  203             if srvstatus[0] == "loadstate=not-found":
  204                 next
  205             else:
  206                 if srvstatus[1] == "activestate=active":
  207                     modules += service_module(srvc, 1)
  208                     status = 1
  209                 elif srvstatus[1] == "activestate=inactive":
  210                     modules += service_module(srvc, 0)
  211                     status = 0
  212         elif sysctl != 0 and servic == 0: 
  213             ### Systemd not available, switch to service command
  214             syscall = Popen(["service", srvc, "status"], stdout=PIPE,
  215                                 stdin=DEVNULL, stderr=DEVNULL, universal_newlines=True)
  216             result = syscall.communicate()[0].lower()
  217             if "is running" in result:
  218                 modules += service_module(srvc, 1)
  219                 status = 1
  220             elif "is stopped" in result:
  221                 modules += service_module(srvc, 0)
  222                 status = 0
  223             else:
  224                 next
  225         else:
  226             print ("No systemd or service commands available. Exiting...", file=stderr)
  227             exit()
  228         if status:
  229             module_list.append(srvc)
  230             if memcpu == True:
  231                 modules += get_memcpu(srvc, None)
  232     
  233     for m in modules:
  234         print_module (m, 1)
  235 
  236 
  237 #########################################################################################
  238 # print_module function
  239 #########################################################################################
  240 def print_module(module, str_flag=False):
  241     """Returns module in XML format. Accepts only {dict}.\n
  242     + Only works with one module at a time: otherwise iteration is needed.
  243     + Module "value" field accepts str type or [list] for datalists.
  244     + Use not_print_flag to avoid printing the XML (only populates variables).
  245     """
  246     data = dict(module)
  247     module_xml = ("<module>\n"
  248                   "\t<name><![CDATA[" + str(data["name"]) + "]]></name>\n"
  249                   "\t<type>" + str(data["type"]) + "</type>\n"
  250                   )
  251     #### Strip spaces if module not generic_data_string    
  252     if type(data["type"]) is not str and "string" not in data["type"]:
  253         data["value"] = data["value"].strip()
  254     if isinstance(data["value"], list): # Checks if value is a list
  255         module_xml += "\t<datalist>\n"
  256         for value in data["value"]:
  257             if type(value) is dict and "value" in value:
  258                 module_xml += "\t<data>\n"
  259                 module_xml += "\t\t<value><![CDATA[" + str(value["value"]) + "]]></value>\n"
  260                 if "timestamp" in value:
  261                     module_xml += "\t\t<timestamp><![CDATA[" + str(value["timestamp"]) + "]]></timestamp>\n"
  262         module_xml += "\t</data>\n"
  263     else:
  264         module_xml += "\t<data><![CDATA[" + str(data["value"]) + "]]></data>\n"
  265     if "desc" in data:
  266         module_xml += "\t<description><![CDATA[" + str(data["desc"]) + "]]></description>\n"
  267     if "unit" in data:
  268         module_xml += "\t<unit><![CDATA[" + str(data["unit"]) + "]]></unit>\n"
  269     if "interval" in data:
  270         module_xml += "\t<module_interval><![CDATA[" + str(data["interval"]) + "]]></module_interval>\n"
  271     if "tags" in data:
  272         module_xml += "\t<tags>" + str(data["tags"]) + "</tags>\n"
  273     if "module_group" in data:
  274         module_xml += "\t<module_group>" + str(data["module_group"]) + "</module_group>\n"
  275     if "module_parent" in data and data["module_parent"] != None:
  276         module_xml += "\t<module_parent>" + str(data["module_parent"]) + "</module_parent>\n"
  277     if "min_warning" in data:
  278         module_xml += "\t<min_warning><![CDATA[" + str(data["min_warning"]) + "]]></min_warning>\n"
  279     if "max_warning" in data:
  280         module_xml += "\t<max_warning><![CDATA[" + str(data["max_warning"]) + "]]></max_warning>\n"
  281     if "min_critical" in data:
  282         module_xml += "\t<min_critical><![CDATA[" + str(data["min_critical"]) + "]]></min_critical>\n"
  283     if "max_critical" in data:
  284         module_xml += "\t<max_critical><![CDATA[" + str(data["max_critical"]) + "]]></max_critical>\n"
  285     if "str_warning" in data:
  286         module_xml += "\t<str_warning><![CDATA[" + str(data["str_warning"]) + "]]></str_warning>\n"
  287     if "str_critical" in data:
  288         module_xml += "\t<str_critical><![CDATA[" + str(data["str_critical"]) + "]]></str_critical>\n"
  289     if "critical_inverse" in data:
  290         module_xml += "\t<critical_inverse><![CDATA[" + str(data["critical_inverse"]) + "]]></critical_inverse>\n"
  291     if "warning_inverse" in data:
  292         module_xml += "\t<warning_inverse><![CDATA[" + str(data["warning_inverse"]) + "]]></warning_inverse>\n"
  293     if "max" in data:
  294         module_xml += "\t<max><![CDATA[" + str(data["max"]) + "]]></max>\n"
  295     if "min" in data:
  296         module_xml += "\t<min><![CDATA[" + str(data["min"]) + "]]></min>\n"
  297     if "post_process" in data:
  298         module_xml += "\t<post_process><![CDATA[" + str(data["post_process"]) + "]]></post_process>\n"
  299     if "disabled" in data:
  300         module_xml += "\t<disabled><![CDATA[" + str(data["disabled"]) + "]]></disabled>\n"
  301     if "min_ff_event" in data:
  302         module_xml += "\t<min_ff_event><![CDATA[" + str(data["min_ff_event"]) + "]]></min_ff_event>\n"
  303     if "status" in data:
  304         module_xml += "\t<status><![CDATA[" + str(data["status"]) + "]]></status>\n"
  305     if "timestamp" in data:
  306         module_xml += "\t<timestamp><![CDATA[" + str(data["timestamp"]) + "]]></timestamp>\n"
  307     if "custom_id" in data:
  308         module_xml += "\t<custom_id><![CDATA[" + str(data["custom_id"]) + "]]></custom_id>\n"
  309     if "critical_instructions" in data:
  310         module_xml += "\t<critical_instructions><![CDATA[" + str(data["critical_instructions"]) + "]]></critical_instructions>\n"
  311     if "warning_instructions" in data:
  312         module_xml += "\t<warning_instructions><![CDATA[" + str(data["warning_instructions"]) + "]]></warning_instructions>\n"
  313     if "unknown_instructions" in data:
  314         module_xml += "\t<unknown_instructions><![CDATA[" + str(data["unknown_instructions"]) + "]]></unknown_instructions>\n"
  315     if "quiet" in data:
  316         module_xml += "\t<quiet><![CDATA[" + str(data["quiet"]) + "]]></quiet>\n"
  317     if "module_ff_interval" in data:
  318         module_xml += "\t<module_ff_interval><![CDATA[" + str(data["module_ff_interval"]) + "]]></module_ff_interval>\n"
  319     if "crontab" in data:
  320         module_xml += "\t<crontab><![CDATA[" + str(data["crontab"]) + "]]></crontab>\n"
  321     if "min_ff_event_normal" in data:
  322         module_xml += "\t<min_ff_event_normal><![CDATA[" + str(data["min_ff_event_normal"]) + "]]></min_ff_event_normal>\n"
  323     if "min_ff_event_warning" in data:
  324         module_xml += "\t<min_ff_event_warning><![CDATA[" + str(data["min_ff_event_warning"]) + "]]></min_ff_event_warning>\n"
  325     if "min_ff_event_critical" in data:
  326         module_xml += "\t<min_ff_event_critical><![CDATA[" + str(data["min_ff_event_critical"]) + "]]></min_ff_event_critical>\n"
  327     if "ff_type" in data:
  328         module_xml += "\t<ff_type><![CDATA[" + str(data["ff_type"]) + "]]></ff_type>\n"
  329     if "ff_timeout" in data:
  330         module_xml += "\t<ff_timeout><![CDATA[" + str(data["ff_timeout"]) + "]]></ff_timeout>\n"
  331     if "each_ff" in data:
  332         module_xml += "\t<each_ff><![CDATA[" + str(data["each_ff"]) + "]]></each_ff>\n"
  333     if "module_parent_unlink" in data:
  334         module_xml += "\t<module_parent_unlink><![CDATA[" + str(data["parent_unlink"]) + "]]></module_parent_unlink>\n"
  335     if "global_alerts" in data:
  336         for alert in data["alert"]:
  337             module_xml += "\t<alert_template><![CDATA[" + alert + "]]></alert_template>\n"
  338     module_xml += "</module>\n"
  339 
  340     #### Print flag
  341     if str_flag is not False:
  342         print (module_xml)
  343 
  344     return (module_xml)
  345 
  346 
  347 #########################################################################################
  348 # MAIN
  349 #########################################################################################
  350 
  351 def main():
  352     """Checks OS and calls the discover function."""
  353     if psutil.WINDOWS:
  354         OS = "Windows"
  355         service_list = ["MySQL", "postgresql", "pgsql", "oracle", "MSSQL", "IISADMIN",
  356                         "apache", "nginx", "W3svc", "NTDS", "Netlogon", "DNS", "MSExchangeADTopology",
  357                         "MSExchangeServiceHost", "MSExchangeSA", "MSExchangeTransport"]
  358         discover(OS, service_list)
  359     elif psutil.LINUX:
  360         OS = "Linux"
  361         service_list = ["httpd", "apache2", "nginx", "ldap", "docker",
  362                         "postfix", "mysqld", "postgres", "oracle", "mongod"]
  363         discover(OS, service_list)
  364     else:
  365         print ("OS not recognized. Exiting...", file=stderr)
  366         exit()
  367 
  368 def discover(osyst, servicelist):
  369     """Shows help and triggers the creation of service modules"""
  370     if "--usage" in argv:
  371         memcpu = True
  372     else:
  373         memcpu = False
  374     if len(argv) > 2 and argv[1] == "--list":
  375         servicelist = argv[2].split(",")
  376         if osyst == "Windows":
  377             win_service(servicelist, False, memcpu) ## False won't get children
  378         elif osyst == "Linux":
  379             lnx_service(servicelist, memcpu)
  380     elif len(argv) > 1 and argv[1] == "--default":
  381         if osyst == "Windows":
  382             win_service(servicelist, False, memcpu) ## False won't get children
  383         elif osyst == "Linux":
  384             lnx_service(servicelist, memcpu)
  385     else:
  386         print ("\nPandora FMS Autodiscovery plugin v{}".format(version))
  387         print ("Checks the status of the services in list and monitors CPU and Memory for each of them.\n")
  388         print ("Usage:")
  389         print ("{} [options] [--usage]".format(argv[0]))
  390         print ("--help")
  391         print ("\tPrints this help screen")
  392         print ("--default")
  393         print ("\tRuns this tool with default monitoring.")
  394         print ("\tServices monitored by default for {}:".format(osyst))
  395         print ("\t",", ".join(servicelist))
  396         print ("--list \"<srvc1,srvc2,srvc3>\"")
  397         print ("\tReplaces default services for a given list (comma-separated)")
  398         if osyst == "Windows":
  399             print ("\tEach element of the list will be treated as a regexp, but they must be over 2 characters.")
  400             print ("\tElements under 2 characters will be discarded.")
  401         print ("--usage")
  402         print ("\tAdds modules for CPU and Memory usage per service/process (optional, can take some time).\n")
  403 
  404 
  405 ##### RUN ####
  406 main()