add systemctl and ufw parsers, support for varying degrees of severity
[logparse.git] / logparse / util.py
index ae27eeefd4260d78a8028b50e4aa67fc4e6bf5b8..e6d905c442a17e78244f055717df98317e05c10e 100644 (file)
-#
-#   utilities.py
-#
-#   Commonly used general functions
-#
+"""
+Commonly used general functions.
 
-import re
+This module provides the following methods:
+    - `hostname`:       get the current machine's hostname
+    - `getlocaldomain`: get the current machine's domain name
+    - `resolve`:        attempt to convert a local/public IP to hostname
+    - `readlog`:        read contents of a log file from disk
+"""
+
+from datetime import datetime, timedelta
+import ipaddress
+import logging
 import os
+from pkg_resources import Requirement, resource_filename
+import re
 import socket
-import inspect
 from systemd import journal
-from datetime import datetime, timedelta
 
-import logging
-logger = logging.getLogger(__name__)
+from logparse import config, formatting
+from logparse.timeparse import timeparse
 
-from pkg_resources import Requirement, resource_filename
 
-from logparse.config import prefs
+logger = logging.getLogger(__name__)
+
 
 def hostname(path): # get the hostname of current server
+    """
+    Get the hostname of the current machine using the file supplied in the
+    `hostname-path` config option.
+    """
+
     hnfile = open(path, 'r')
     hn = re.search('^(\w*)\n*', hnfile.read()).group(1)
     return hn
 
+
 def getlocaldomain(): # get the parent fqdn of current server
-    domain = socket.getfqdn().split('.', 1) # Note: if socket.fetfqdn() returns localhost, make sure the first entry in /etc/hosts contains the fqdn
+    """
+    Get parent domain name (possibly FQDN) of the current machine. Note: if
+    `socket.fetfqdn()` returns localhost, make sure the first entry in the
+    hostname file includes the FQDN.
+    """
+
+    domain = socket.getfqdn().split('.', 1)
     if len(domain) != 2:
-        logger.warning('Could not get domain of this server, only hostname. Please consider updating /etc/hosts')
+        logger.warning("Could not get domain of this server, only hostname. Please consider updating the hostname file at {0}".format(config.prefs.get("logparse", "hostname-path")))
         return 'localdomain'
     else:
         return domain[-1]
 
+
 def resolve(ip, fqdn=None):        # try to resolve an ip to hostname
-    # Possible values for fqdn:
-    #   fqdn            show full hostname and domain
-    #   fqdn-implicit   show hostname and domain unless local
-    #   host-only       only show hostname
-    #   ip              never resolve anything
-    # resolve-domains defined in individual sections of the config take priority over global config
+    """
+    Attempt to resolve an IP into a hostname or FQDN.
+    Possible values for fqdn:
+        - fqdn            show full hostname and domain
+        - fqdn-implicit   show hostname and domain unless local
+        - host-only       only show hostname
+        - ip              never resolve anything
+    Note resolve-domains settings defined in individual sections of the config
+    take priority over the global config (this is enforced in parser modules)
+    """
     
     if not fqdn:
-        fqdn = prefs.getboolean("logparse", "resolve-domains")
+        fqdn = config.prefs.get("logparse", "resolve-domains")
 
     if fqdn == 'ip':
         return(ip)
 
     try:
-        socket.inet_aton(ip)  # succeeds if text contains ip
+        ip_obj = ipaddress.ip_address(ip)
+    except ValueError as err:
+        logger.debug("Invalid format: " + str(err))
+        return ip
+
+    try:
         hn = socket.gethostbyaddr(ip)[0] # resolve ip to hostname
-        if fqdn == 'fqdn-implicit' and hn.split('.', 1)[1] == getlocaldomain():
-            return(hn.split('.')[0])
-        elif fqdn == 'fqdn' or fqdn == 'fqdn-implicit':
-            return(hn)
-        elif fqdn == 'host-only':
-            return(hn.split('.')[0])
-        else:
-            logger.warning("invalid value for fqdn config")
-            return(hn)
     except socket.herror:
         # cannot resolve ip
         logger.debug(ip + " cannot be found, might not exist anymore")
         return(ip)
-    except (OSError, socket.error): # socket.error for Python 2 compatibility
-        # already a hostname
-        logger.debug(ip + " is already a hostname")
-        return(ip)
     except Exception as err:
-        logger.warning("failed to resolve hostname for " + ip + ": " + str(err))
+        logger.warning("Failed to resolve hostname for " + ip + ": " + str(err))
         return(ip)  # return ip if no hostname exists
 
-def readlog(path = None, mode = 'r'):   # read file
+    if (fqdn == "host-only") or (fqdn == "fqdn-implicit" and ip_obj.is_private):
+        return hn.split('.')[0]
+    if fqdn == 'fqdn' or fqdn == 'fqdn-implicit':
+        return hn
+    return hn
+
+
+
+def readlog(path = None, mode = 'r'):
+    """
+    Read a logfile from disk and return string
+    """
+    
     if (path == None):
-        logger.error("no path provided")
-        return
+        logger.error("No path provided")
+        return 1
     else:
         if (os.path.isfile(path) is False):
             logger.error("Log at {0} was requested but does not exist".format(path))
@@ -83,3 +110,14 @@ def readlog(path = None, mode = 'r'):   # read file
             except IOError or OSError as e:
                 logger.warning("Error reading log at {0}: {1}".format(path, e.strerror))
                 return 1
+
+class LogPeriod:
+
+    def __init__(self, section):
+        if config.prefs.get(section.split("_")[0], "period"):
+            self.startdate = datetime.now() - timeparse(config.prefs.get(section.split("_")[0], "period"))
+            logger.debug("Parsing logs for {0} since {1}".format(section, self.startdate.strftime(formatting.DATEFMT + " " + formatting.TIMEFMT)))
+            self.unique = True
+        else:
+            self.startdate = datetime.now() - timeparse(config.prefs.get("logparse", "period"))
+            self.unique = False