Repository: Netuitive/netuitive-statsd

Issue:
Support More Flexible Hostname Methods
Ideally this will match the Metricly Diamond project:

https://github.com/Netuitive/netuitive-diamond/blob/f48a0bc7de6038164e0919e2d3d0fd524c83e1d9/conf/diamond.conf.example#L146-L169

Discussion:

Patch:
diff --git a/libs/config.py b/libs/config.py
index c43a9a0..5d68cb9 100644
--- a/libs/config.py
+++ b/libs/config.py
@@ -6,30 +6,18 @@ import socket
 import os
 import configobj
 import sys
+import subprocess
 
 logger = logging.getLogger(__name__)
 
 
 def config(args=None):
 
-    # try to find the hostname
-    hostname = socket.getfqdn().split('.')[0]
-
-    if hostname == 'localhost':
-        hostname = socket.gethostname().split('.')[0]
-
-    if hostname == 'localhost':
-        hostname = os.uname()[1].split('.')[0]
-
-    if hostname == 'localhost':
-        logger.error('could not determine hostname')
-
     # default config
     ret = {
         'enabled': True,
         'url': 'https://api.app.netuitive.com/ingest',
         'api_key': None,
-        'hostname': hostname,
         'interval': 60,
         'element_type': 'SERVER',
         'prefix': 'statsd',
@@ -67,6 +55,7 @@ def config(args=None):
 
             # assemble the config from config file
 
+            ret['hostname'] = get_hostname(cfg)
             ret['configfile'] = configfile
             ret['url'] = cfg['handlers']['NetuitiveHandler']['url']
             ret['api_key'] = cfg['handlers'][
@@ -162,3 +151,98 @@ def config(args=None):
     except Exception as e:
         logger.error(e, exc_info=True)
         raise(e)
+
+
+def get_hostname(fullconfig, method=None):
+    """
+    Returns a hostname as configured by the user
+    """
+    config = fullconfig.get('collectors').get('default')
+    method = method or config.get('hostname_method', 'smart')
+
+    # case insensitive method
+    method = method.lower()
+
+    if 'hostname' in config and method != 'shell':
+        return config['hostname']
+
+    if method == 'shell':
+        if 'hostname' not in config:
+            raise Exception(
+                "hostname must be set to a shell command for"
+                " hostname_method=shell")
+        else:
+            proc = subprocess.Popen(config['hostname'],
+                                    shell=True,
+                                    stdout=subprocess.PIPE)
+            hostname = proc.communicate()[0].strip()
+            if proc.returncode != 0:
+                raise subprocess.CalledProcessError(proc.returncode,
+                                                    config['hostname'])
+            return hostname
+
+    if method == 'smart':
+        hostname = get_hostname(config, 'fqdn_short')
+        if hostname != 'localhost':
+            return hostname
+        hostname = get_hostname(config, 'hostname_short')
+        return hostname
+
+    if method == 'fqdn_short':
+        hostname = socket.getfqdn().split('.')[0]
+        if hostname == '':
+            raise Exception('Hostname is empty?!')
+        return hostname
+
+    if method == 'fqdn':
+        hostname = socket.getfqdn().replace('.', '_')
+        if hostname == '':
+            raise Exception('Hostname is empty?!')
+        return hostname
+
+    if method == 'fqdn_rev':
+        hostname = socket.getfqdn().split('.')
+        hostname.reverse()
+        hostname = '.'.join(hostname)
+        if hostname == '':
+            raise Exception('Hostname is empty?!')
+        return hostname
+
+    if method == 'uname_short':
+        hostname = os.uname()[1].split('.')[0]
+        if hostname == '':
+            raise Exception('Hostname is empty?!')
+        return hostname
+
+    if method == 'uname_rev':
+        hostname = os.uname()[1].split('.')
+        hostname.reverse()
+        hostname = '.'.join(hostname)
+        if hostname == '':
+            raise Exception('Hostname is empty?!')
+        return hostname
+
+    if method == 'hostname':
+        hostname = socket.gethostname()
+        if hostname == '':
+            raise Exception('Hostname is empty?!')
+        return hostname
+
+    if method == 'hostname_short':
+        hostname = socket.gethostname().split('.')[0]
+        if hostname == '':
+            raise Exception('Hostname is empty?!')
+        return hostname
+
+    if method == 'hostname_rev':
+        hostname = socket.gethostname().split('.')
+        hostname.reverse()
+        hostname = '.'.join(hostname)
+        if hostname == '':
+            raise Exception('Hostname is empty?!')
+        return hostname
+
+    if method == 'none':
+        return None
+
+    raise NotImplementedError(config['hostname_method'])
diff --git a/netuitive-statsd.conf.example b/netuitive-statsd.conf.example
index b231aae..eb08f33 100644
--- a/netuitive-statsd.conf.example
+++ b/netuitive-statsd.conf.example
@@ -28,6 +28,26 @@ enabled = True
 [[default]]
 hostname = statsd-test-host
 
+# If you prefer to just use a different way of calculating the hostname
+# Uncomment and set this to one of these values:
+
+# smart             = Default. Tries fqdn_short. If that's localhost, uses hostname_short
+
+# fqdn_short        = Default. Similar to hostname -s
+# fqdn              = hostname output
+# fqdn_rev          = hostname in reverse (com.example.www)
+
+# uname_short       = Similar to uname -n, but only the first part
+# uname_rev         = uname -r in reverse (com.example.www)
+
+# hostname_short    = `hostname -s`
+# hostname          = `hostname`
+# hostname_rev      = `hostname` in reverse (com.example.www)
+
+# shell             = Run the string set in hostname as a shell command and use its
+#                     output(with spaces trimmed off from both ends) as the hostname.
+
+# hostname_method = smart
 
 [logger_root]
 


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
index 197d8cb..9a5512c 100755
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -32,7 +32,6 @@ class Test_Config(unittest.TestCase):
                     'forward': False,
                     'forward_ip': None,
                     'forward_port': None,
-                    'hostname': socket.getfqdn().split('.')[0],
                     'interval': 60,
                     'listen_ip': '127.0.0.1',
                     'listen_port': 8125,