# # Copyright (C) 2019 Marko Myllynen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # """PCP netcheck PMDA DNS lookup module""" # pylint: disable=too-many-arguments,too-many-positional-arguments # pylint: disable=invalid-name try: from time import perf_counter as time except ImportError: from time import time import sys import socket from multiprocessing import Manager from pcp.pmapi import pmUnits from cpmapi import PM_TYPE_FLOAT, PM_TYPE_STRING, PM_SEM_INSTANT, PM_SEM_DISCRETE, PM_TIME_MSEC from modules.pcpnetcheck import PCPNetcheckModuleBase # Module constants MODULE = 'dns_lookup' BASENS = 'dns.lookup.' units_none = pmUnits(0, 0, 0, 0, 0, 0) units_msecs = pmUnits(0, 1, 0, 0, PM_TIME_MSEC, 0) # Module report types IPV4 = "ipv4" IPV6 = "ipv6" BOTH = "both" class PCPNetcheckModule(PCPNetcheckModuleBase): """PCP netcheck DNS lookup module""" def __init__(self, config, dbg, log, err, params): """Constructor""" PCPNetcheckModuleBase.__init__(self, MODULE, config, dbg, log, err, params) self.family = BOTH self.prereq_check() for opt in self.config.options(MODULE): if opt == 'family': self.family = self.config.get(MODULE, opt).lower() if self.family not in (IPV4, IPV6, BOTH): self.err("Invalid type specification for 'family', aborting.") sys.exit(1) elif opt not in self.common_opts: self.err("Invalid directive '%s' in %s, aborting." % (opt, MODULE)) sys.exit(1) self.log("Module parameters: timeout: %s, family: %s." % (self.timeout, self.family)) socket.setdefaulttimeout(self.timeout) for host in self.hosts: self.hosts[host] = "" if self.family == IPV4: self.family = socket.AF_INET if self.family == IPV6: self.family = socket.AF_INET6 if self.family == BOTH: self.family = 0 self.log("Initialized.") @staticmethod def prereq_check(): """Check module prerequisities""" try: socket.getaddrinfo("localhost", 0) except Exception: # pylint: disable=broad-except raise RuntimeError("Can't resolve localhost!") def metrics(self): """Get metric definitions""" self.items = ( # Name - reserved - type - semantics - units - help (BASENS + 'res', None, PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'dns lookup result'), (BASENS + 'time', None, PM_TYPE_FLOAT, PM_SEM_INSTANT, units_msecs, 'dns lookup time'), ) return True, self.items def _do_lookup(self, hosts, timings, host): """Do DNS lookup""" try: start_time = time() res = socket.getaddrinfo(host, 0, self.family) end_time = time() hosts[host] = ",".join({addr[4][0] for addr in res}) timings[host] = (end_time - start_time) * 1000 except Exception: # pylint: disable=broad-except pass def do_check(self): """Do net check""" hosts = Manager().dict() timings = Manager().dict() self._run_check_methods(self._do_lookup, (hosts, timings)) for host in self.hosts: self.hosts[host] = hosts[host] if host in hosts else self.HOST_FAIL_STR self.timings[host] = timings[host] if host in timings else -2