622 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			622 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python
 | |
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved.
 | |
| 
 | |
| from __future__ import print_function
 | |
| 
 | |
| import getpass
 | |
| import inspect
 | |
| import os
 | |
| import platform
 | |
| import re
 | |
| import sys
 | |
| import time
 | |
| 
 | |
| from collections import defaultdict
 | |
| 
 | |
| from subprocess import (PIPE, STDOUT, Popen, CalledProcessError)
 | |
| 
 | |
| ###################################################################################################
 | |
| ScriptPath = os.path.dirname(os.path.realpath(__file__))
 | |
| 
 | |
| ###################################################################################################
 | |
| # python 2/3 portability
 | |
| PY3 = (sys.version_info.major >= 3)
 | |
| 
 | |
| # bind raw_input to input in older versions of python
 | |
| try:
 | |
|   input = raw_input
 | |
| except NameError:
 | |
|   pass
 | |
| 
 | |
| try:
 | |
|   FileNotFoundError
 | |
| except NameError:
 | |
|   FileNotFoundError = IOError
 | |
| 
 | |
| ###################################################################################################
 | |
| PLATFORM_WINDOWS = "Windows"
 | |
| PLATFORM_MAC = "Darwin"
 | |
| PLATFORM_LINUX = "Linux"
 | |
| PLATFORM_LINUX_CENTOS = 'centos'
 | |
| PLATFORM_LINUX_DEBIAN = 'debian'
 | |
| PLATFORM_LINUX_FEDORA = 'fedora'
 | |
| PLATFORM_LINUX_UBUNTU = 'ubuntu'
 | |
| 
 | |
| OPERATION_RUN = 'run'
 | |
| OPERATION_CONFIGURE = 'config'
 | |
| 
 | |
| BEAT_ES_HOST = "BEAT_ES_HOST"
 | |
| BEAT_ES_PROTOCOL = "BEAT_ES_PROTOCOL"
 | |
| BEAT_ES_SSL_VERIFY = "BEAT_ES_SSL_VERIFY"
 | |
| BEAT_HTTP_PASSWORD = "BEAT_HTTP_PASSWORD"
 | |
| BEAT_HTTP_USERNAME = "BEAT_HTTP_USERNAME"
 | |
| BEAT_KIBANA_DASHBOARDS_ENABLED = "BEAT_KIBANA_DASHBOARDS_ENABLED"
 | |
| BEAT_KIBANA_DASHBOARDS_PATH = "BEAT_KIBANA_DASHBOARDS_PATH"
 | |
| BEAT_KIBANA_HOST = "BEAT_KIBANA_HOST"
 | |
| BEAT_KIBANA_PROTOCOL = "BEAT_KIBANA_PROTOCOL"
 | |
| BEAT_KIBANA_SSL_VERIFY = "BEAT_KIBANA_SSL_VERIFY"
 | |
| 
 | |
| BEAT_YML_TEMPLATE = """
 | |
| #================================ General ======================================
 | |
| fields_under_root: true
 | |
| 
 | |
| #================================ Outputs ======================================
 | |
| 
 | |
| #-------------------------- Elasticsearch output -------------------------------
 | |
| output.elasticsearch:
 | |
|   enabled: true
 | |
|   hosts: ["${BEAT_ES_HOST}"]
 | |
|   protocol: "${BEAT_ES_PROTOCOL}"
 | |
|   username: "${BEAT_HTTP_USERNAME}"
 | |
|   password: "${BEAT_HTTP_PASSWORD}"
 | |
|   ssl.verification_mode: "${BEAT_ES_SSL_VERIFY}"
 | |
| 
 | |
| setup.template.enabled: true
 | |
| setup.template.overwrite: false
 | |
| setup.template.settings:
 | |
|   index.number_of_shards: 1
 | |
|   index.number_of_replicas: 0
 | |
| 
 | |
| #============================== Dashboards =====================================
 | |
| setup.dashboards.enabled: "${BEAT_KIBANA_DASHBOARDS_ENABLED}"
 | |
| setup.dashboards.directory: "${BEAT_KIBANA_DASHBOARDS_PATH}"
 | |
| 
 | |
| #============================== Kibana =====================================
 | |
| setup.kibana:
 | |
|   host: "${BEAT_KIBANA_HOST}"
 | |
|   protocol: "${BEAT_KIBANA_PROTOCOL}"
 | |
|   username: "${BEAT_HTTP_USERNAME}"
 | |
|   password: "${BEAT_HTTP_PASSWORD}"
 | |
|   ssl.verification_mode: "${BEAT_KIBANA_SSL_VERIFY}"
 | |
| 
 | |
| #================================ Logging ======================================
 | |
| logging.metrics.enabled: false
 | |
| """
 | |
| 
 | |
| ###################################################################################################
 | |
| # print to stderr
 | |
| def eprint(*args, **kwargs):
 | |
|   print(*args, file=sys.stderr, **kwargs)
 | |
| 
 | |
| ###################################################################################################
 | |
| # get interactive user response to Y/N question
 | |
| def YesOrNo(question, default=None, forceInteraction=False, acceptDefault=False):
 | |
| 
 | |
|   if default == True:
 | |
|     questionStr = "\n{} (Y/n): ".format(question)
 | |
|   elif default == False:
 | |
|     questionStr = "\n{} (y/N): ".format(question)
 | |
|   else:
 | |
|     questionStr = "\n{} (y/n): ".format(question)
 | |
| 
 | |
|   if acceptDefault and (default is not None) and (not forceInteraction):
 | |
|     reply = ''
 | |
|   else:
 | |
|     while True:
 | |
|       reply = str(input(questionStr)).lower().strip()
 | |
|       if (len(reply) > 0) or (default is not None):
 | |
|         break
 | |
| 
 | |
|   if (len(reply) == 0):
 | |
|     reply = 'y' if default else 'n'
 | |
| 
 | |
|   if reply[0] == 'y':
 | |
|     return True
 | |
|   elif reply[0] == 'n':
 | |
|     return False
 | |
|   else:
 | |
|     return YesOrNo(question, default=default)
 | |
| 
 | |
| ###################################################################################################
 | |
| # get interactive user response
 | |
| def AskForString(question, default=None, forceInteraction=False, acceptDefault=False):
 | |
| 
 | |
|   if acceptDefault and (default is not None) and (not forceInteraction):
 | |
|     reply = default
 | |
|   else:
 | |
|     reply = str(input('\n{}: '.format(question))).strip()
 | |
| 
 | |
|   return reply
 | |
| 
 | |
| ###################################################################################################
 | |
| # get interactive password (without echoing)
 | |
| def AskForPassword(prompt):
 | |
|   reply = getpass.getpass(prompt=prompt)
 | |
|   return reply
 | |
| 
 | |
| ###################################################################################################
 | |
| # convenient boolean argument parsing
 | |
| def str2bool(v):
 | |
|   if v.lower() in ('yes', 'true', 't', 'y', '1'):
 | |
|     return True
 | |
|   elif v.lower() in ('no', 'false', 'f', 'n', '0'):
 | |
|     return False
 | |
|   else:
 | |
|     raise ValueError('Boolean value expected')
 | |
| 
 | |
| ###################################################################################################
 | |
| # determine if a program/script exists and is executable in the system path
 | |
| def Which(cmd, debug=False):
 | |
|   result = any(os.access(os.path.join(path, cmd), os.X_OK) for path in os.environ["PATH"].split(os.pathsep))
 | |
|   if (not result) and (platform.system() == PLATFORM_WINDOWS):
 | |
|     result = os.access(os.path.join(os.getcwd(), cmd), os.X_OK)
 | |
|   if debug:
 | |
|     eprint("Which {} returned {}".format(cmd, result))
 | |
|   return result
 | |
| 
 | |
| ###################################################################################################
 | |
| # run command with arguments and return its exit code, stdout, and stderr
 | |
| def check_output_input(*popenargs, **kwargs):
 | |
| 
 | |
|   if 'stdout' in kwargs:
 | |
|     raise ValueError('stdout argument not allowed, it will be overridden')
 | |
| 
 | |
|   if 'stderr' in kwargs:
 | |
|     raise ValueError('stderr argument not allowed, it will be overridden')
 | |
| 
 | |
|   if 'input' in kwargs and kwargs['input']:
 | |
|     if 'stdin' in kwargs:
 | |
|       raise ValueError('stdin and input arguments may not both be used')
 | |
|     inputdata = kwargs['input']
 | |
|     kwargs['stdin'] = PIPE
 | |
|   else:
 | |
|     inputdata = None
 | |
|   kwargs.pop('input', None)
 | |
| 
 | |
|   process = Popen(*popenargs, stdout=PIPE, stderr=PIPE, **kwargs)
 | |
|   try:
 | |
|     output, errput = process.communicate(inputdata)
 | |
|   except:
 | |
|     process.kill()
 | |
|     process.wait()
 | |
|     raise
 | |
| 
 | |
|   retcode = process.poll()
 | |
| 
 | |
|   return retcode, output, errput
 | |
| 
 | |
| ###################################################################################################
 | |
| # run command with arguments and return its exit code, stdout, and stderr
 | |
| def run_process(command, stdout=True, stderr=True, stdin=None, retry=0, retrySleepSec=5, cwd=None, env=None, debug=False):
 | |
| 
 | |
|   retcode = -1
 | |
|   output = []
 | |
| 
 | |
|   try:
 | |
|     # run the command
 | |
|     retcode, cmdout, cmderr = check_output_input(command, input=stdin.encode() if (PY3 and stdin) else stdin, cwd=cwd, env=env)
 | |
| 
 | |
|     # split the output on newlines to return a list
 | |
|     if PY3:
 | |
|       if stderr and (len(cmderr) > 0): output.extend(cmderr.decode(sys.getdefaultencoding()).split('\n'))
 | |
|       if stdout and (len(cmdout) > 0): output.extend(cmdout.decode(sys.getdefaultencoding()).split('\n'))
 | |
|     else:
 | |
|       if stderr and (len(cmderr) > 0): output.extend(cmderr.split('\n'))
 | |
|       if stdout and (len(cmdout) > 0): output.extend(cmdout.split('\n'))
 | |
| 
 | |
|   except (FileNotFoundError, OSError, IOError) as e:
 | |
|     if stderr:
 | |
|       output.append("Command {} not found or unable to execute".format(command))
 | |
| 
 | |
|   if debug:
 | |
|     eprint("{}{} returned {}: {}".format(command, "({})".format(stdin[:80] + bool(stdin[80:]) * '...' if stdin else ""), retcode, output))
 | |
| 
 | |
|   if (retcode != 0) and retry and (retry > 0):
 | |
|     # sleep then retry
 | |
|     time.sleep(retrySleepSec)
 | |
|     return run_process(command, stdout, stderr, stdin, retry-1, retrySleepSec, cwd, env, debug)
 | |
|   else:
 | |
|     return retcode, output
 | |
| 
 | |
| ###################################################################################################
 | |
| class Beatbox(object):
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def __init__(self, debug=False, ymlFileSpec=None, beatName=None, acceptDefaults=False):
 | |
|     self.debug = debug
 | |
|     self.acceptDefaults = acceptDefaults
 | |
|     self.platform = platform.system()
 | |
|     self.ymlFileSpec = ymlFileSpec
 | |
|     self.ymlFilePath = os.path.dirname(ymlFileSpec)
 | |
|     self.beatName = beatName
 | |
|     self.beatExe = beatName
 | |
|     self.beatInstallDir = None
 | |
|     self.defaultKibanaDashboardDir = None
 | |
|     self.keystoreItems = defaultdict(str)
 | |
|     for initItem in [BEAT_ES_HOST,
 | |
|                      BEAT_ES_PROTOCOL,
 | |
|                      BEAT_ES_SSL_VERIFY,
 | |
|                      BEAT_HTTP_PASSWORD,
 | |
|                      BEAT_HTTP_USERNAME,
 | |
|                      BEAT_KIBANA_DASHBOARDS_ENABLED,
 | |
|                      BEAT_KIBANA_DASHBOARDS_PATH,
 | |
|                      BEAT_KIBANA_HOST,
 | |
|                      BEAT_KIBANA_PROTOCOL,
 | |
|                      BEAT_KIBANA_SSL_VERIFY]:
 | |
|       self.keystoreItems[initItem] = ''
 | |
|       self.keystorePath = None
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def __del__(self):
 | |
|     # nothing for now
 | |
|     pass
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def run_process(self, command, stdout=True, stderr=True, stdin=None, retry=0, retrySleepSec=5):
 | |
|     return run_process(command, stdout=stdout, stderr=stderr, stdin=stdin, retry=retry, retrySleepSec=retrySleepSec, debug=self.debug)
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def build_beat_command(self, command):
 | |
|     if not Which(self.beatExe, debug=self.debug):
 | |
|       raise Exception("Beat executable {} does not exist".format(self.beatExe))
 | |
| 
 | |
|     if not os.path.isfile(self.ymlFileSpec):
 | |
|       raise Exception("Beat configuration {} does not exist".format(self.ymlFileSpec))
 | |
| 
 | |
|     # convert paths to absolutes
 | |
|     ymlFileSpec = os.path.abspath(self.ymlFileSpec)
 | |
|     ymlFilePath = os.path.dirname(ymlFileSpec)
 | |
| 
 | |
|     beatCmd = [self.beatExe, '--path.home', ymlFilePath, '--path.config', ymlFilePath, '--path.data', ymlFilePath if (self.platform == PLATFORM_WINDOWS) else os.path.join(ymlFilePath, 'data'), '--path.logs', os.path.join(ymlFilePath, 'logs'), '-c', ymlFileSpec, '-E', "keystore.path='{}'".format(self.keystorePath)]
 | |
| 
 | |
|     return beatCmd + command if isinstance(command, list) else beatCmd + [ command ]
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def run_beat_command(self, command, stdout=True, stderr=True, stdin=None, retry=0, retrySleepSec=5):
 | |
|     return self.run_process(self.build_beat_command(command), stdout=stdout, stderr=stderr, stdin=stdin, retry=retry, retrySleepSec=retrySleepSec)
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def configure_beat_yml(self):
 | |
|     if self.debug:
 | |
|       eprint("{}: {}".format(self.__class__.__name__, inspect.currentframe().f_code.co_name))
 | |
| 
 | |
|     if (self.ymlFileSpec is not None):
 | |
| 
 | |
|       if os.path.isfile(self.ymlFileSpec):
 | |
|         # if it doesn't look like connectivity stuff (at last BEAT_ES_PROTOCOL) is in the YML file, offer to append it
 | |
|         if ((len(list(filter(lambda x: BEAT_ES_PROTOCOL in x, [line.rstrip('\n') for line in open(self.ymlFileSpec)]))) == 0) and
 | |
|             YesOrNo("Append connectivity boilerplate to {}?".format(self.ymlFileSpec), default=False, acceptDefault=self.acceptDefaults)):
 | |
|           with open(self.ymlFileSpec, 'a') as ymlFile:
 | |
|             ymlFile.write(BEAT_YML_TEMPLATE)
 | |
| 
 | |
|       else:
 | |
|         # generate a boilerplate spec file (output configured, no modules) if the YML file doesn't exist
 | |
|         with open(self.ymlFileSpec, 'w') as ymlFile:
 | |
|           ymlFile.write(BEAT_YML_TEMPLATE)
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def configure_keystore(self):
 | |
|     if self.debug:
 | |
|       eprint("{}: {}".format(self.__class__.__name__, inspect.currentframe().f_code.co_name))
 | |
| 
 | |
|     # check if keystore already exists
 | |
|     err, out = self.run_beat_command(['keystore', 'list'])
 | |
|     if (err == 0) and (len(out) > 0):
 | |
|       if not YesOrNo("{} keystore already exists, overwrite?".format(self.beatName), default=False, acceptDefault=self.acceptDefaults):
 | |
|         raise Exception("Configuration cancelled by user")
 | |
| 
 | |
|     # create keystore
 | |
|     err, out = self.run_beat_command(['keystore', 'create', '--force'])
 | |
|     if (err == 0):
 | |
|       eprint('\n'.join(out))
 | |
|     else:
 | |
|       raise Exception("Keystore creation failed: {}".format(out))
 | |
| 
 | |
|     # prompt for and store configuration items
 | |
|     for destination in ['Elasticsearch', 'Kibana']:
 | |
| 
 | |
|       if YesOrNo("Configure {} {} connectivity?".format(self.beatName, destination), default=True, acceptDefault=self.acceptDefaults):
 | |
| 
 | |
|         # protocol
 | |
|         tmpVal, tmpDefault = '', 'https'
 | |
|         while tmpVal not in ['http', 'https']:
 | |
|           tmpVal = AskForString("Enter {} connection protocol (http or https) [{}]".format(destination, tmpDefault), default=tmpDefault, acceptDefault=self.acceptDefaults).lower()
 | |
|           if (len(tmpVal) == 0): tmpVal = tmpDefault
 | |
|         self.keystoreItems[BEAT_ES_PROTOCOL.replace('_ES_', '_KIBANA_' if (destination == 'Kibana') else '_ES_')] = tmpVal
 | |
| 
 | |
|         # SSL verification
 | |
|         tmpVal, tmpDefault = '', 'none'
 | |
|         while tmpVal not in ['none', 'full']:
 | |
|           tmpVal = AskForString("Enter {} SSL verification (none (for self-signed certificates) or full) [{}]".format(destination, tmpDefault), default=tmpDefault, acceptDefault=self.acceptDefaults).lower()
 | |
|           if (len(tmpVal) == 0): tmpVal = tmpDefault
 | |
|         self.keystoreItems[BEAT_ES_SSL_VERIFY.replace('_ES_', '_KIBANA_' if (destination == 'Kibana') else '_ES_')] = tmpVal
 | |
| 
 | |
|         # host
 | |
|         tmpVal, tmpDefault = '', ''
 | |
|         while (len(tmpVal) == 0):
 | |
|           tmpVal = AskForString("Enter {} connection host".format(destination), default=tmpDefault, acceptDefault=self.acceptDefaults)
 | |
|         self.keystoreItems[BEAT_ES_HOST.replace('_ES_', '_KIBANA_' if (destination == 'Kibana') else '_ES_')] = tmpVal
 | |
| 
 | |
|     if (BEAT_KIBANA_HOST in self.keystoreItems):
 | |
| 
 | |
|       #  configure kibana dashboards
 | |
|       if YesOrNo("Configure {} Kibana dashboards?".format(self.beatName), default=True, acceptDefault=self.acceptDefaults):
 | |
|         self.keystoreItems[BEAT_KIBANA_DASHBOARDS_ENABLED] = 'true'
 | |
| 
 | |
|         # kibana dashboards
 | |
|         tmpVal, tmpDefault = '', self.defaultKibanaDashboardDir
 | |
|         while (len(tmpVal) == 0):
 | |
|           tmpVal = AskForString("Enter directory containing Kibana dashboards [{}]".format(tmpDefault), default=tmpDefault, acceptDefault=self.acceptDefaults)
 | |
|           if (len(tmpVal) == 0): tmpVal = tmpDefault
 | |
|         self.keystoreItems[BEAT_KIBANA_DASHBOARDS_PATH] = tmpVal
 | |
| 
 | |
|     # username
 | |
|     tmpVal, tmpDefault = '', ''
 | |
|     while (len(tmpVal) == 0):
 | |
|       tmpVal = AskForString("Enter HTTP/HTTPS server username", default=tmpDefault, acceptDefault=self.acceptDefaults)
 | |
|     self.keystoreItems[BEAT_HTTP_USERNAME] = tmpVal
 | |
| 
 | |
|     # password
 | |
|     tmpVal, tmpValConfirm = '', 'xxxx'
 | |
|     while (len(tmpVal) == 0) and (tmpVal != tmpValConfirm):
 | |
|       tmpVal = AskForPassword("Enter password for {}: ".format(self.keystoreItems[BEAT_HTTP_USERNAME]))
 | |
|       tmpValConfirm = AskForPassword("Enter password for {} (again): ".format(self.keystoreItems[BEAT_HTTP_USERNAME]))
 | |
|       if (tmpVal != tmpValConfirm):
 | |
|         eprint('Passwords do not match')
 | |
|         tmpVal, tmpValConfirm = '', 'xxxx'
 | |
|     self.keystoreItems[BEAT_HTTP_PASSWORD] = tmpVal
 | |
| 
 | |
|     # write values to keystore
 | |
|     for key, value in self.keystoreItems.items():
 | |
|       err, out = self.run_beat_command(['keystore', 'add', key, '--stdin', '--force'], stdin=value)
 | |
|       if (err != 0):
 | |
|         raise Exception("Failed to add {} to {} keystore: {}".format(key, self.beatName, out))
 | |
| 
 | |
|     # list keystore
 | |
|     err, out = self.run_beat_command(['keystore', 'list'])
 | |
|     if (err == 0):
 | |
|       eprint('Generated keystore for {}'.format(self.beatName))
 | |
|       eprint('\n'.join(out))
 | |
|     else:
 | |
|       raise Exception("Failed to enumerate keystore: {}".format(out))
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def beat_run(self):
 | |
|     if self.debug:
 | |
|       eprint("{}: {}".format(self.__class__.__name__, inspect.currentframe().f_code.co_name))
 | |
| 
 | |
|     process = Popen(self.build_beat_command(['run', '-e']), stdout=PIPE)
 | |
|     while True:
 | |
|       output = process.stdout.readline()
 | |
|       if (len(output) == 0) and (process.poll() is not None):
 | |
|         break
 | |
|       if output:
 | |
|         print(output.decode().strip())
 | |
|       else:
 | |
|         time.sleep(0.5)
 | |
|     process.poll()
 | |
| 
 | |
| ###################################################################################################
 | |
| class LinuxBeatbox(Beatbox):
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def __init__(self, debug=False, ymlFileSpec=None, beatName=None):
 | |
|     if PY3:
 | |
|       super().__init__(debug=debug, ymlFileSpec=ymlFileSpec, beatName=beatName)
 | |
|     else:
 | |
|       super(LinuxBeatbox, self).__init__(debug=debug, ymlFileSpec=ymlFileSpec, beatName=beatName)
 | |
| 
 | |
|     if not Which(self.beatExe, debug=self.debug):
 | |
|       self.beatExe = self.beatExe.lower() if (self.beatExe is not None) else self.beatName.lower()
 | |
| 
 | |
|     self.beatInstallDir = "/usr/share/{}".format(self.beatName)
 | |
|     self.defaultKibanaDashboardDir = os.path.join(self.beatInstallDir, 'kibana')
 | |
|     self.keystorePath = os.path.join(os.path.join(os.path.dirname(os.path.abspath(self.ymlFileSpec)), 'data'), "{}.keystore".format(self.beatName))
 | |
| 
 | |
|     self.distro = None
 | |
|     self.codename = None
 | |
|     self.release = None
 | |
| 
 | |
|     # determine the distro (e.g., ubuntu) and code name (e.g., bionic) if applicable
 | |
| 
 | |
|     # check /etc/os-release values first
 | |
|     if os.path.isfile('/etc/os-release'):
 | |
|       osInfo = dict()
 | |
| 
 | |
|       with open("/etc/os-release", 'r') as f:
 | |
|         for line in f:
 | |
|           try:
 | |
|             k, v = line.rstrip().split("=")
 | |
|             osInfo[k] = v.strip('"')
 | |
|           except:
 | |
|             pass
 | |
| 
 | |
|       if ('NAME' in osInfo) and (len(osInfo['NAME']) > 0):
 | |
|         distro = osInfo['NAME'].lower().split()[0]
 | |
| 
 | |
|       if ('VERSION_CODENAME' in osInfo) and (len(osInfo['VERSION_CODENAME']) > 0):
 | |
|         codename = osInfo['VERSION_CODENAME'].lower().split()[0]
 | |
| 
 | |
|       if ('VERSION_ID' in osInfo) and (len(osInfo['VERSION_ID']) > 0):
 | |
|         release = osInfo['VERSION_ID'].lower().split()[0]
 | |
| 
 | |
|     # try lsb_release next
 | |
|     if (self.distro is None):
 | |
|       err, out = self.run_process(['lsb_release', '-is'], stderr=False)
 | |
|       if (err == 0) and (len(out) > 0):
 | |
|         self.distro = out[0].lower()
 | |
| 
 | |
|     if (self.codename is None):
 | |
|       err, out = self.run_process(['lsb_release', '-cs'], stderr=False)
 | |
|       if (err == 0) and (len(out) > 0):
 | |
|         self.codename = out[0].lower()
 | |
| 
 | |
|     if (self.release is None):
 | |
|       err, out = self.run_process(['lsb_release', '-rs'], stderr=False)
 | |
|       if (err == 0) and (len(out) > 0):
 | |
|         self.release = out[0].lower()
 | |
| 
 | |
|     # try release-specific files
 | |
|     if (self.distro is None):
 | |
|       if os.path.isfile('/etc/centos-release'):
 | |
|         distroFile = '/etc/centos-release'
 | |
|       if os.path.isfile('/etc/redhat-release'):
 | |
|         distroFile = '/etc/redhat-release'
 | |
|       elif os.path.isfile('/etc/issue'):
 | |
|         distroFile = '/etc/issue'
 | |
|       else:
 | |
|         distroFile = None
 | |
|       if (distroFile is not None):
 | |
|         with open(distroFile, 'r') as f:
 | |
|           distroVals = f.read().lower().split()
 | |
|           distroNums = [x for x in distroVals if x[0].isdigit()]
 | |
|           self.distro = distroVals[0]
 | |
|           if (self.release is None) and (len(distroNums) > 0):
 | |
|             self.release = distroNums[0]
 | |
| 
 | |
|     if (self.distro is None):
 | |
|       self.distro = "linux"
 | |
| 
 | |
|     if self.debug:
 | |
|       eprint("distro: {}{}{}".format(self.distro,
 | |
|                                      " {}".format(self.codename) if self.codename else "",
 | |
|                                      " {}".format(self.release) if self.release else ""))
 | |
| 
 | |
|     if not self.codename: self.codename = self.distro
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def configure_beat_yml(self):
 | |
|     if PY3:
 | |
|       super().configure_beat_yml()
 | |
|     else:
 | |
|       super(LinuxBeatbox, self).configure_beat_yml()
 | |
| 
 | |
|     localModulePath = os.path.join(os.path.abspath(self.ymlFilePath), 'module')
 | |
|     installedModulePath = os.path.join(self.beatInstallDir, 'module')
 | |
|     if ((not os.path.exists(localModulePath)) and
 | |
|         (os.path.isdir(installedModulePath)) and
 | |
|         YesOrNo("Create symlink to module path {} as {}?".format(installedModulePath, localModulePath), default=True, acceptDefault=self.acceptDefaults)):
 | |
|       os.symlink(installedModulePath, localModulePath)
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def configure_keystore(self):
 | |
|     if PY3:
 | |
|       super().configure_keystore()
 | |
|     else:
 | |
|       super(LinuxBeatbox, self).configure_keystore()
 | |
| 
 | |
|     pass
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def beat_run(self):
 | |
|     if PY3:
 | |
|       super().beat_run()
 | |
|     else:
 | |
|       super(LinuxBeatbox, self).beat_run()
 | |
| 
 | |
|     pass
 | |
| 
 | |
| ###################################################################################################
 | |
| class WindowsBeatbox(Beatbox):
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def __init__(self, debug=False, ymlFileSpec=None, beatName=None):
 | |
|     if PY3:
 | |
|       super().__init__(debug=debug, ymlFileSpec=ymlFileSpec, beatName=beatName)
 | |
|     else:
 | |
|       super(WindowsBeatbox, self).__init__(debug=debug, ymlFileSpec=ymlFileSpec, beatName=beatName)
 | |
| 
 | |
|     if not Which(self.beatExe, debug=self.debug):
 | |
|       self.beatExe = self.beatExe + '.exe' if (self.beatExe is not None) else self.beatName + '.exe'
 | |
| 
 | |
|     self.beatInstallDir = os.path.abspath(self.ymlFilePath)
 | |
|     self.defaultKibanaDashboardDir = os.path.join(self.beatInstallDir, 'kibana')
 | |
|     self.keystorePath = os.path.join(os.path.dirname(os.path.abspath(self.ymlFileSpec)), "{}.keystore".format(self.beatName))
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def configure_beat_yml(self):
 | |
|     if PY3:
 | |
|       super().configure_beat_yml()
 | |
|     else:
 | |
|       super(WindowsBeatbox, self).configure_beat_yml()
 | |
| 
 | |
|     pass
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def configure_keystore(self):
 | |
|     if PY3:
 | |
|       super().configure_keystore()
 | |
|     else:
 | |
|       super(WindowsBeatbox, self).configure_keystore()
 | |
| 
 | |
|     pass
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def beat_run(self):
 | |
|     if PY3:
 | |
|       super().beat_run()
 | |
|     else:
 | |
|       super(WindowsBeatbox, self).beat_run()
 | |
| 
 | |
|     pass
 | |
| 
 | |
| ###################################################################################################
 | |
| class MacBeatbox(Beatbox):
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def __init__(self, debug=False, ymlFileSpec=None, beatName=None):
 | |
|     if PY3:
 | |
|       super().__init__(debug=debug, ymlFileSpec=ymlFileSpec, beatName=beatName)
 | |
|     else:
 | |
|       super(MacBeatbox, self).__init__(debug=debug, ymlFileSpec=ymlFileSpec, beatName=beatName)
 | |
| 
 | |
|     if not Which(self.beatExe, debug=self.debug):
 | |
|       self.beatExe = self.beatExe.lower() if (self.beatExe is not None) else self.beatName.lower()
 | |
| 
 | |
|     self.beatInstallDir = "/Library/Application Support/elastic/{}".format(self.beatName)
 | |
|     self.defaultKibanaDashboardDir = os.path.join(self.beatInstallDir, 'kibana')
 | |
|     self.keystorePath = os.path.join(os.path.join(os.path.dirname(os.path.abspath(self.ymlFileSpec)), 'data'), "{}.keystore".format(self.beatName))
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def configure_beat_yml(self):
 | |
|     if PY3:
 | |
|       super().configure_beat_yml()
 | |
|     else:
 | |
|       super(MacBeatbox, self).configure_beat_yml()
 | |
| 
 | |
|     localModulePath = os.path.join(os.path.abspath(self.ymlFilePath), 'module')
 | |
|     installedModulePath = os.path.join(self.beatInstallDir, 'module')
 | |
|     if ((not os.path.exists(localModulePath)) and
 | |
|         (os.path.isdir(installedModulePath)) and
 | |
|         YesOrNo("Create symlink to module path {} as {}?".format(installedModulePath, localModulePath), default=True, acceptDefault=self.acceptDefaults)):
 | |
|       os.symlink(installedModulePath, localModulePath)
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def configure_keystore(self):
 | |
|     if PY3:
 | |
|       super().configure_keystore()
 | |
|     else:
 | |
|       super(MacBeatbox, self).configure_keystore()
 | |
| 
 | |
|     pass
 | |
| 
 | |
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
|   def beat_run(self):
 | |
|     if PY3:
 | |
|       super().beat_run()
 | |
|     else:
 | |
|       super(MacBeatbox, self).beat_run()
 | |
| 
 | |
|     pass |