You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1221 lines
56 KiB

# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
# Core Python Imports
import datetime
import json
import logging
import logging.handlers
import re
import socket
import sys
import time
from httplib2 import ServerNotFoundError
# Splunkd imports
import splunk
import splunk.rest as rest
import splunk.util as util
import lxml.etree as et
from splunk.clilib.bundle_paths import make_splunkhome_path
from splunk.rest import getWebKeyFile, getWebCertFile
# TA and SA Imports
sys.path.append(make_splunkhome_path(['etc', 'apps', 'SA-Hydra-inframon', 'bin']))
sys.path.append(make_splunkhome_path(['etc', 'apps', 'Splunk_TA_vmware_inframon', 'bin']))
import ta_vmware_inframon.simple_vsphere_utils as vsu
from ta_vmware_inframon.models import TAVMwareCollectionStanza, TAVMwareVCenterForwarderStanza, \
SSLCertificate, PoolStanza, TemplateStanza
from hydra_inframon.models import SplunkStoredCredential, HydraNodeStanza, HydraCacheStanza, HydraSessionStanza
# defining global constants
REST_ROOT_PATH = '/services'
local_host_path = splunk.mergeHostPath()
max_worker_process = 30
STATUS_CODES = {
400: 'Bad Request',
401: 'Unauthorized',
402: 'Payment Required',
403: 'Forbidden',
404: 'Not Found',
405: 'Method Not Allowed',
406: 'Not Acceptable',
407: 'Proxy Authentication Required',
408: 'Request Timeout',
409: 'Conflict',
411: 'Length Required',
500: 'Internal Server Error',
503: 'Service Unavailable'
}
class RestError(Exception):
"""
REST Error.
"""
def __init__(self, status, message):
self.status = status
self.reason = STATUS_CODES.get(
status,
'Unknown Error',
)
self.message = message
err_msg = 'REST Error [%(status)s]: %(reason)s -- %(message)s' % {
'status': self.status,
'reason': self.reason,
'message': self.message
}
super(RestError, self).__init__(err_msg)
def setup_logger(logger=None, log_format='%(asctime)s %(levelname)s [ConfigSetUp] %(message)s',
level=logging.INFO, log_name="splunk_for_vmware_setup.log", logger_name="splunk_for_vmware_setup"):
"""
Setup a logger suitable for splunkd consumption
"""
if logger is None:
logger = logging.getLogger(logger_name)
# Prevent the log messages from being duplicated in the python.log file
logger.propagate = False
logger.setLevel(level)
file_handler = logging.handlers.RotatingFileHandler(make_splunkhome_path(
['var', 'log', 'splunk', log_name]), maxBytes=2500000, backupCount=5)
formatter = logging.Formatter(log_format)
file_handler.setFormatter(formatter)
logger.handlers = []
logger.addHandler(file_handler)
logger.debug("Init splunk for vmware setup logger")
return logger
def splunk_rest_request(path, logger, local_session_key=None, session_key=None, getargs=None, postargs=None,
method='GET',
raise_all_errors=False, raw_result=False, timeout=30, jsonargs=None):
"""
This is mostly a shameful copy of splunk.rest.simpleRequest.
The difference lies in the automagic header/cert attachment that
happens in splunkweb and messes with the splunkweb cherrypy.session.
Also we don't auto magic any session keys
Makes an HTTP call to the main splunk REST endpoint
path: the URI to fetch
If given a relative URI, then the method will normalize to the splunkd
default of "/services/...".
If given an absolute HTTP(S) URI, then the method will use as-is.
If given a 'file://' URI, then the method will attempt to read the file
from the local filesystem. Only files under $SPLUNK_HOME are supported,
so paths are 'chrooted' from $SPLUNK_HOME.
getargs: dict of k/v pairs that are always appended to the URL
postargs: dict of k/v pairs that get placed into the body of the
request. If postargs is provided, then the HTTP method is auto
assigned to POST.
method: the HTTP verb - [GET | POST | DELETE | PUT]
raise_all_errors: indicates if the method should raise an exception
if the server HTTP response code is >= 400
raw_result: don't raise an exception if a non 200 response is received;
return the actual response
Return:
This method will return a tuple of (server_response, server_content)
server_response: a dict of HTTP status information
server_content: the body content
"""
logger.debug("Called splunk_rest_request method of rest_utility()")
# strip spaces
path = path.strip(' ')
# if absolute URI, pass along as-is
if path.startswith('http'):
uri = path
# if file:// protocol, try to read file and return
# the serverStatus is just an empty dict; file contents are in server_response
elif path.startswith('file://'):
raise Exception(
"Not supported for this method, use splunk.rest.simpleRequest instead")
else:
# prepend convenience root path
if not path.startswith(REST_ROOT_PATH):
path = REST_ROOT_PATH + '/' + path.strip('/')
# setup args
host = splunk.getDefault('host')
if ':' in host:
host = '[%s]' % host
uri = '%s://%s:%s/%s' % \
(splunk.getDefault('protocol'), host,
splunk.getDefault('port'), path.strip('/'))
if getargs:
getargs = dict([(k, v) for (k, v) in getargs.items() if v is not None])
uri += '?' + util.urlencodeDict(getargs)
# proxy mode bypasses all header passing
headers = {}
session_source = 'direct'
if session_key:
headers['Authorization'] = 'Splunk %s' % session_key
payload = ''
if postargs or jsonargs and method in ('GET', 'POST', 'PUT'):
if method == 'GET':
method = 'POST'
if jsonargs:
# if a JSON body was given, use it for the payload and ignore the postargs
payload = jsonargs
else:
payload = util.urlencodeDict(postargs)
#
# make request
#
t1 = None
if logger.level <= logging.DEBUG:
if uri.lower().find('login') > -1:
logpayload = '[REDACTED]'
else:
logpayload = payload
logger.debug(
'splunk_rest_request >>>\n\tmethod=%s\n\turi=%s\n\tbody=%s', method, uri, logpayload)
logger.debug('splunk_rest_request > %s %s [%s] session_source=%s' % (
method, uri, logpayload, session_source))
t1 = time.time()
# Certificate validation status from inframon_ta_vmware_config_ssl.conf
stanzas = SSLCertificate.all(sessionKey=local_session_key)
cert_validation = not stanzas[0].validate_ssl_certificate
#VMW-6597 - Add support of requireClientCert
ssl_cert = getWebCertFile()
ssl_key = getWebKeyFile()
if cert_validation:
logger.info(
"SSL certificate validation disabled for collection configuration")
else:
if is_require_client_cert:
logger.error(
"Disable SSL certificate validation or requireClientCert")
else:
logger.info(
"SSL certificate validation enabled for collection configuration")
# Add wait and tries to check if the HTTP server is up and running
tries = 4
wait = 10
server_response = None
server_content = None
try:
import httplib2
for a_try in range(tries):
h = httplib2.Http(
timeout=timeout, disable_ssl_certificate_validation=cert_validation)
if ssl_key and ssl_cert:
h.add_certificate(ssl_key, ssl_cert, '')
server_response, server_content = h.request(
uri, method, headers=headers, body=payload)
if server_response is None:
if a_try < tries:
time.sleep(wait)
else:
break
except socket.error as e:
raise splunk.SplunkdConnectionException(str(e))
except socket.timeout as e:
raise splunk.SplunkdConnectionException('Timed out while waiting for splunkd daemon to respond. Splunkd may be hung. (timeout=30)')
except AttributeError as e:
raise splunk.SplunkdConnectionException('Unable to establish connection with splunkd deamon. (%s)' % e)
server_response.messages = []
if logger.level <= logging.DEBUG:
logger.debug('simpleRequest < server responded status=%s responseTime=%.4fs',
server_response.status, time.time() - t1)
# Don't raise exceptions for different status codes or try and parse the response
if raw_result:
return server_response, server_content
#
# we only throw exceptions in limited cases; for most HTTP errors, splunkd
# will return messages in the body, which we parse, so we don't want to
# halt everything and raise exceptions; it is up to the client to figure
# out the best course of action
#
if server_response.status == 401:
# SPL-20915
logger.debug(
'splunk_rest_request - Authentication failed; session_key=%s', session_key)
raise splunk.AuthenticationFailed
elif server_response.status == 402:
raise splunk.LicenseRestriction
elif server_response.status == 403:
raise splunk.AuthorizationFailed(extendedMessages=uri)
elif server_response.status == 404:
# Some 404 responses, such as those for expired jobs which were originally
# run by the scheduler return extra data about the original resource.
# In this case we add that additional info into the exception object
# as the resource_info parameter so others might use it.
try:
body = et.fromstring(server_content)
resource_info = body.find('dict')
if resource_info is not None:
raise splunk.ResourceNotFound(
uri, format.nodeToPrimitive(resource_info))
else:
raise splunk.ResourceNotFound(
uri, extendedMessages=rest.extractMessages(body))
except et.XMLSyntaxError:
pass
raise splunk.ResourceNotFound(uri)
elif server_response.status == 201:
try:
body = et.fromstring(server_content)
server_response.messages = rest.extractMessages(body)
except et.XMLSyntaxError as e:
# do nothing, just continue, no messages to extract if there is no xml
pass
except Exception as e:
# warn if some other type of error occurred.
logger.warn(
"exception trying to parse server_content returned from a 201 response.")
elif server_response.status < 200 or server_response.status > 299:
# service may return messages in the body; try to parse them
try:
body = et.fromstring(server_content)
server_response.messages = rest.extractMessages(body)
except:
pass
if raise_all_errors and server_response.status > 399:
if server_response.status == 500:
raise splunk.InternalServerError((None,
server_response.messages))
elif server_response.status == 400:
raise splunk.BadRequest((None, server_response.messages))
else:
raise splunk.RESTException((server_response.status,
server_response.messages))
# return the headers and body content
return server_response, server_content
def get_remote_session_key(username, password, local_session_key, host_path, logger):
"""
Get a remote session key from the auth system
If fails return None
"""
uri = splunk.mergeHostPath(host_path) + '/services/auth/login'
args = {'username': username, 'password': password}
server_response = None
server_content = None
try:
server_response, server_content = splunk_rest_request(
uri, logger, local_session_key=local_session_key, postargs=args)
except splunk.AuthenticationFailed:
return None
if server_response and server_response.status != 200:
logger.error(
'get_remote_session_key - unable to login; check credentials')
rest.extractMessages(et.fromstring(server_content))
return None
root = et.fromstring(server_content)
session_key = root.findtext('sessionKey')
return session_key
def validate_dcn(node_path, username, password, heads, pool_name, local_session_key, logger):
"""
:param node_path: dcn managment port url
:param username: dcn splunk username
:param password: dcn splunk password
:param heads: dcn worker process
:param pool_name: pool name in which dcn belongs
:param local_session_key:
:param logger:
:return: result of validation as a response
"""
credential_validation = False
last_connectivity_checked = datetime.datetime.utcnow()
# addon_validation checking condition will be changed latter (as package structure is not decided yet.)
addon_validation = False
if not pool_name == [None]:
if isinstance(pool_name, list):
pool_name = pool_name[0]
pool_name = pool_name.strip()
if len(pool_name) == 0:
logger.error("Pool name cannot be empty for node:{0}.".format(node_path))
response = {"status": "invalid", "message": "Pool name cannot be empty.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
pool_stanza = PoolStanza.from_name(pool_name, "Splunk_TA_vmware_inframon", host_path=local_host_path,
session_key=local_session_key)
if not pool_stanza:
logger.error("[pool={0}]Given pool for node:{1} doesn't exist.".format(pool_name, node_path))
response = {"status": "invalid", "message": "Given pool doesn't exist.",
"addon_validation": addon_validation,
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
else:
logger.error("pool name is not passed for node:{0}".format(node_path))
response = {"status": "invalid", "message": "Empty pool name is not allowed.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
if not node_path == [None]:
# node_path could be list or string (list in case of coming in args , string if coming as target).
if isinstance(node_path, list):
node_path = node_path[0]
validated_node_path = re.search(
"^\s*https?:\/\/[A-Za-z0-9\.\-_]+:\d+\/?\s*$", node_path)
if validated_node_path is None:
logger.error("[pool={0}]Node name passed is not valid.".format(pool_name))
response = {"status": "invalid", "message": "Node name passed is not valid.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
else:
logger.error("[pool={0}]No node name passed in data, cannot save nothing!".format(pool_name))
response = {"status": "invalid", "message": "Empty node name is not allowed.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
if not username == [None]:
if isinstance(username, list):
username = username[0]
username = username.strip()
validated_node_username = re.search("^[^\r\n\t\/\s]+$", username)
if validated_node_username is None:
logger.error(
"[pool={0}]Node username passed for node:{1} is not valid.".format(pool_name, node_path))
response = {"status": "invalid", "message": "Username is not valid.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
else:
logger.error(
"[pool={0}]No username passed for node:{1}".format(pool_name, node_path))
response = {"status": "invalid", "message": "Empty username is not allowed.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
if not heads == [None]:
if isinstance(heads, list):
heads = heads[0]
validated_heads = re.search("^(([1-2][0-9])|[1-9]|30)$", heads)
if validated_heads is None:
logger.error("[pool={0}]Heads is not valid for node:{1}.".format(pool_name, node_path))
response = {"status": "invalid", "message": "Heads is not valid.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
else:
logger.error(
"[pool={0}]No heads passed for node:{1}".format(pool_name, node_path))
response = {"status": "invalid", "message": "Empty heads is not allowed.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
if not password == [None]:
if isinstance(password, list):
password = password[0]
password = password.strip()
if len(password) == 0:
logger.error("[pool={0}]Password is not valid for node:{1}.".format(pool_name, node_path))
response = {"status": "invalid", "message": "Password is not valid.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
else:
logger.error("[pool={0}]No password passed for node:{1}.".format(pool_name, node_path))
response = {"status": "invalid", "message": "Empty password is not allowed.",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
last_connectivity_checked = datetime.datetime.utcnow()
try:
remote_session_key = get_remote_session_key(
username, password, local_session_key, node_path, logger)
if remote_session_key is None:
response = {"status": "valid", "message": "Could reach host, but login failed",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
else:
# Okay credentials are good, now we can check that the apps are there
server_response, server_content = splunk_rest_request(
path=node_path + '/services/apps/local', logger=logger, local_session_key=local_session_key,
session_key=remote_session_key, getargs={'count': '0'})
apps = rest.format.parseFeedDocument(server_content)
# required_apps is to be changed latter.
required_apps = ["SA-Hydra-inframon", "Splunk_TA_vmware_inframon"]
installed_count = 0
installed_apps = []
for app in apps:
contents = app.toPrimitive()
if 'label' in contents:
installed_apps.append(contents['label'])
if app.title in required_apps:
installed_count += 1
credential_validation = True
if installed_count == len(required_apps):
addon_validation = True
response = {"status": "valid", "message": "Everything is valid",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
else:
logger.error(
"[pool={2}]Username/password are good for node:{0}, but apps are not there, it had installed_apps='{1}'".
format(node_path, str(installed_apps), pool_name))
addon_validation = False
response = {"status": "badapps", "message": "Username/password are good but apps are not there",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
except ServerNotFoundError:
logger.error("[pool=%s]Could not reach host=%s", pool_name, node_path)
response = {"status": "unreachable", "message": "Could not reach host",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
except splunk.SplunkdConnectionException:
logger.error("[pool=%s]Could not find splunkd on node=%s", pool_name, node_path)
response = {"status": "unreachable", "message": "Could not reach host",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
except splunk.AuthenticationFailed:
logger.error(
"[pool=%s]Could not log into splunkd on node=%s, credentials are definitely bad", pool_name, node_path)
response = {"status": "invalid_creds", "message": "Could not authenticate with remote splunkd",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
except Exception:
logger.exception("[pool={1}]Could not reach host={0}.".format(node_path, pool_name))
response = {"status": "unreachable", "message": "Could not reach host",
"credential_validation": credential_validation, "addon_validation": addon_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
def validate_vcenter(vc_path, username, password, pool_name, local_session_key, logger, check_connection_only=False):
"""
:param vc_path: vCenter path
:param username: vCenter username
:param password: vCenter password
:param pool_name: vCenter pool_name
:param local_session_key: local session key of current Splunk session
:param check_connection_only: flag denoting whether to validate vc using stanzas from conf or check connection using the provided credentials
:return: returns the result of validation as a response
"""
credential_validation = False
last_connectivity_checked = datetime.datetime.utcnow()
if check_connection_only:
dbg_info = 'Check_connection_only'
else:
if not pool_name == [None]:
if isinstance(pool_name, list):
pool_name = pool_name[0]
pool_name = pool_name.strip()
if len(pool_name) == 0:
logger.error("for vCenter:{0} Pool name cannot be empty.".format(vc_path))
response = {"status": "invalid", "message": "Pool name cannot be empty.",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
pool_stanza = PoolStanza.from_name(pool_name, "Splunk_TA_vmware_inframon", host_path=local_host_path,
session_key=local_session_key)
if not pool_stanza:
logger.error("for vCenter:{0} Given pool doesn't exist.".format(vc_path))
response = {"status": "invalid", "message": "Given pool doesn't exist.",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
else:
logger.error("for vCenter:{0} No pool_name passed".format(vc_path))
response = {"status": "invalid", "message": "Empty pool name is not allowed.",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
dbg_info = 'pool={0}'.format(pool_name)
if not vc_path == [None]:
# vc_path could be list or string based on from where is comming
if isinstance(vc_path, list):
vc_path = vc_path[0]
validate_vc_domain = re.search("^[A-Za-z0-9\.\-_]+$", vc_path)
if validate_vc_domain is None:
logger.error("[{0}]vCenter domain passed is not valid.".format(dbg_info))
response = {"status": "invalid", "message": "vCenter domain passed is not valid.",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
else:
logger.error("[{0}]No vCenter domain passed".format(dbg_info))
response = {"status": "invalid", "message": "No vCenter domain passed , cannot validate nothing!",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
if not username == [None] and not username == None:
if isinstance(username, list):
username = username[0]
username = username.strip()
validated_username = re.search("^[^\r\n\t\/\s]+$", username)
if validated_username is None:
logger.error("[{0}] for vCenter:{1} username passed is not valid.".format(dbg_info, vc_path))
response = {"status": "invalid", "message": "vCenter username passed is not valid.",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
else:
logger.error("[{0}] for vCenter:{1} Username is not passed".format(dbg_info, vc_path))
response = {"status": "invalid", "message": "Username is not passed to validate_vcenter, cannot validate!",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
if not password == [None] and not password == None:
if isinstance(password, list):
password = password[0]
password = password.strip()
if len(password) == 0:
logger.error("[{0}] for vCenter:{1} Password is not valid.".format(dbg_info, vc_path))
response = {"status": "invalid", "message": "Password is not valid.",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
else:
logger.error("[{0}] for vCenter:{1} Password is not passed".format(dbg_info, vc_path))
response = {"status": "invalid", "message": "Password is not passed, cannot validate!",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
logger.info("Checking vc={0} with username={1}".format(vc_path, username))
last_connectivity_checked = datetime.datetime.utcnow()
try:
vs = vsu.vSphereService(vc_path, username, password)
credential_validation = True
response = {"status": "valid", "message": "Everyting is valid.",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
if vs.logout():
logger.debug("User={0} successfully logout from {1}.".format(username, vc_path))
else:
logger.warn("User={0} failed to logout from {1}.".format(username, vc_path))
except vsu.ConnectionFailure:
credential_validation = False
logger.error("[{1}]Could not reach the vc:{0} to test credentials".format(vc_path, dbg_info))
response = {"status": "unreachable", "message": "Could not reach the vc to test credentials",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
except vsu.LoginFailure:
credential_validation = False
logger.error("[{1}]Could reach vc:{0}, but login failed.".format(vc_path, dbg_info))
response = {"status": "loginFailed", "message": "Could reach vc, but login failed",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
except Exception:
logger.exception("[{1}]Exception occurred while validating vc:{0}".format(vc_path, dbg_info))
credential_validation = False
response = {"status": "unreachable", "message": "Could not reach the vc to test creds",
"credential_validation": credential_validation,
"last_connectivity_checked": last_connectivity_checked}
return response
def validate_vcenter_forwarder(vc_path, username, password, local_session_key, logger):
"""
:param vc_path: path of vCenter
:param username: username of Splunk forwarder on vCenter
:param password: password of Splunk forwarder on vCenter
:param local_session_key: local session key of current session.
:return: It returns the validation result as a s response
"""
credential_validation = False
addon_validation = False
if not vc_path == [None]:
# vc_path could be list or string based on from where is comming
if isinstance(vc_path, list):
vc_path = vc_path[0]
validate_vc_domain = re.search("^\s*https?:\/\/[A-Za-z0-9\.\-_]+:\d+\/?\s*$", vc_path)
if validate_vc_domain is None:
logger.error("vCenter domain passed is not valid.")
response = {"status": "invalid", "message": "vc forwarder uri passed to validate_vcenter is not valid.",
"credential_validation": credential_validation, "addon_validation": addon_validation}
return response
else:
logger.error("No vc domain passed, cannot validate nothing!")
response = {"status": "invalid", "message": "No vc domain passed to validate_vcenter, cannot validate nothing!",
"credential_validation": credential_validation, "addon_validation": addon_validation}
return response
if not username == [None]:
if isinstance(username, list):
username = username[0]
username = username.strip()
validated_username = re.search("^[^\r\n\t\/\s]+$", username)
if validated_username is None:
logger.error("vCenter forwarder username is not valid.")
response = {"status": "invalid", "message": "vCenter forwarder username is not valid.",
"credential_validation": credential_validation, "addon_validation": addon_validation}
return response
else:
logger.error("Username is not passed , cannot validate!")
response = {"status": "invalid", "message": "Username is not passed to validate_vcenter, cannot validate!",
"credential_validation": credential_validation, "addon_validation": addon_validation}
return response
if not password == [None]:
if isinstance(password, list):
password = password[0]
password = password.strip()
if len(password) == 0:
logger.error("Password is not valid.")
response = {"status": "invalid", "message": "Password is not valid.",
"credential_validation": credential_validation, "addon_validation": addon_validation}
return response
else:
logger.error("Password is not passed, cannot validate!")
response = {"status": "invalid",
"message": "Password is not passed to validate_vcenter_forwrder,cannot validate!",
"credential_validation": credential_validation, "addon_validation": addon_validation}
return response
logger.info("Checking vc=%s forwarder with username=%s", vc_path, username)
# validating credentials.
try:
remote_session_key = get_remote_session_key(username, password, local_session_key, host_path=vc_path,
logger=logger)
if remote_session_key is None:
credential_validation = False
addon_validation = False
response = {"status": "LoginFailed", "message": "Could reach host, but login failed",
"credential_validation": credential_validation, "addon_validation": addon_validation}
else:
# Okay credentials are good, now we can check that the apps are there
server_response, server_content = splunk_rest_request(path=vc_path + '/services/apps/local', logger=logger,
local_session_key=local_session_key,
session_key=remote_session_key,
getargs={'count': '0'})
apps = rest.format.parseFeedDocument(server_content)
logger.info("Accessing apps")
required_apps = ["Splunk_TA_vcenter"]
installed_count = 0
installed_apps = []
for app in apps:
contents = app.toPrimitive()
if 'label' in contents:
installed_apps.append(contents['label'])
if app.title in required_apps:
installed_count += 1
credential_validation = True
if installed_count == len(required_apps):
addon_validation = True
response = {"status": "valid", "message": "Everything is valid",
"credential_validation": credential_validation,
"addon_validation": addon_validation}
else:
logger.warning("vc forwarder did not have the required app- Splunk_TA_vcenter.")
addon_validation = False
response = {"status": "badapps", "message": "Username/password are good but app is not there",
"credential_validation": credential_validation,
"addon_validation": addon_validation}
except ServerNotFoundError:
response = {"status": "unreachable", "message": "Could not reach host",
"credential_validation": credential_validation,
"addon_validation": addon_validation}
except splunk.SplunkdConnectionException:
logger.error("Could not find splunkd on host=%s", vc_path)
response = {"status": "unreachable", "message": "Could not reach host",
"credential_validation": credential_validation,
"addon_validation": addon_validation}
except splunk.AuthenticationFailed:
logger.error("Could not log into splunkd on host=%s, credentials are definitely bad", vc_path)
response = {"status": "LoginFailed", "message": "Could not authenticate with remote splunkd",
"credential_validation": credential_validation,
"addon_validation": addon_validation}
except Exception:
logger.error("Could not log into splunkd on host=%s, due to an exception", vc_path)
response = {"status": "LoginFailed", "message": "Could not reach host",
"credential_validation": credential_validation,
"addon_validation": addon_validation}
return response
def toggle_vc_inputs(host_path, username, password, vc, local_session_key, pool_name, logger, disable=True):
"""
toggle on or off all of the inputs in Splunk TA vCenter, default is disable
RETURNS nothing
"""
status = True
try:
remote_session_key = get_remote_session_key(username, password, local_session_key, host_path, logger)
except ServerNotFoundError:
logger.error("Could not find vc_splunk_forwarder=%s", host_path)
remote_session_key = None
except splunk.SplunkdConnectionException:
logger.error("Could not find splunkd on vc_splunk_forwarder=%s", host_path)
remote_session_key = None
except splunk.AuthenticationFailed:
logger.error("Could not log into splunkd on vc_splunk_forwarder=%s, credentials are definitely bad", host_path)
remote_session_key = None
except Exception as e:
remote_session_key = None
logger.error("Exception occured:{0}".format(e))
if remote_session_key is None:
logger.error(
"[pool=%s]Could not log into vc_splunk_forwarder=%s with the credentials provided, cannot manage the inputs on that instance",
pool_name, host_path)
status = False
else:
uri = '/services/server/info?output_mode=json'
info_path = host_path.rstrip("/") + uri
server_response, server_content = splunk_rest_request(
info_path, logger, local_session_key=local_session_key, session_key=remote_session_key)
server_content_dict = json.loads(server_content)
os = ""
input_uris = [
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%24ALLUSERSPROFILE%5CVMware%5CvCenterServer%5Clogs%5Cvws',
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%24ALLUSERSPROFILE%5CVMware%5CvCenterServer%5Clogs%5Cvmware-vpx',
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%24ALLUSERSPROFILE%5CVMware%5CvCenterServer%5Clogs%5Cperfcharts',
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%2Fvar%2Flog%2Fvmware%2Fvws',
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%2Fvar%2Flog%2Fvmware%2Fvpxd',
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%2Fvar%2Flog%2Fvmware%2Fperfcharts'
]
try:
if server_content_dict:
os = server_content_dict['entry'][0]['content']['os_name']
logger.info("Splunk Machine OS:{0}".format(os))
except Exception as e:
logger.exception(
"[pool={1}]Exception occurred while determining OS of the machine: {0}".format(e, pool_name))
if os == "Windows":
input_uris = [
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%24ALLUSERSPROFILE%5CVMware%5CvCenterServer%5Clogs%5Cvws',
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%24ALLUSERSPROFILE%5CVMware%5CvCenterServer%5Clogs%5Cvmware-vpx',
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%24ALLUSERSPROFILE%5CVMware%5CvCenterServer%5Clogs%5Cperfcharts']
elif os == "Linux":
input_uris = ['/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%2Fvar%2Flog%2Fvmware%2Fvws',
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%2Fvar%2Flog%2Fvmware%2Fvpxd',
'/servicesNS/nobody/Splunk_TA_vcenter/data/inputs/monitor/%2Fvar%2Flog%2Fvmware%2Fperfcharts']
for uri in input_uris:
path = host_path.rstrip("/") + uri
if disable:
postargs = {"host": vc}
action = "disable"
else:
postargs = {"host": vc}
action = "enable"
try:
logger.info(
"Adjusting input with rest request on path={0} with postargs={1} and secondary action={2}".format(
path, postargs, action))
splunk_rest_request(path, logger, local_session_key=local_session_key, session_key=remote_session_key,
method="POST", postargs=postargs,
raise_all_errors=True)
path = path + "/" + action
splunk_rest_request(path, logger, local_session_key=local_session_key, session_key=remote_session_key,
method="POST", raise_all_errors=True)
except ServerNotFoundError:
logger.exception("[pool={1}]Could not reach vc_splunk_forwarder={0}", host_path, pool_name)
status = False
except Exception as e:
message = "[pool={1}]Problem editing inputs on the remote vc_splunk_forwarder={0}: ".format(host_path,
pool_name) + str(e)
logger.exception(message)
status = False
return status
def delete_vcenter_stanza(vc_path, local_session_key, logger):
"""This function deletes vCenter stanza for given @vc_path.
@vc_path - url of vCenter to delete."""
# getting stanza to delete
vc_stanza = TAVMwareCollectionStanza.from_name(vc_path, "Splunk_TA_vmware_inframon", host_path=local_host_path,
session_key=local_session_key)
if not vc_stanza:
logger.error("Could not find stanza for stanza_name={0} cannot delete".format(vc_path))
raise RestError(404, "Could not find stanza for stanza_name={0} cannot delete".format(vc_path))
else:
vc = vc_stanza.target[0]
username = vc_stanza.username
logger.info("Deleting vc stanza_name=%s credentials for username=%s", vc_path, vc_stanza.username)
pool_name = vc_stanza.pool_name
if not vc_stanza.passive_delete():
logger.exception("[pool={1}]Failed to delete vc collection stanza={0}".format(vc_path, pool_name))
raise RestError(500, "Failed to delete vc collection stanza={0}".format(vc_path))
stored_cred = SplunkStoredCredential.from_name(SplunkStoredCredential.build_name(vc, username),
app="Splunk_TA_vmware_inframon", owner="nobody",
host_path=local_host_path, session_key=local_session_key)
if not stored_cred or not stored_cred.passive_delete():
logger.error("Could not delete obsolete credential it may linger")
return True
def delete_vcenter_forwarder_stanza(vc_path, local_session_key, pool_name, logger):
"""This function deletes forwarder stanza of given @vc_path
@vc_path - url of vCenter to delete."""
vc_forwarder_stanza = TAVMwareVCenterForwarderStanza.from_name(vc_path, "Splunk_TA_vmware_inframon", "nobody",
session_key=local_session_key,
host_path=local_host_path)
if vc_forwarder_stanza:
logger.info("Deleting vCenter forwarder stanza for the vc: {0}".format(vc_path))
vc_splunk_uri = vc_forwarder_stanza.host
vc_splunk_username = vc_forwarder_stanza.user
logger.info("vc_splunk_uri: {0}, vc_splunk_username: {1}".format(vc_splunk_uri, vc_splunk_username))
spl_stored_cred = SplunkStoredCredential.from_name(
SplunkStoredCredential.build_name(vc_splunk_uri, vc_splunk_username), app="Splunk_TA_vmware_inframon",
owner="nobody", host_path=local_host_path, session_key=local_session_key)
vc_splunk_password = ""
if spl_stored_cred:
vc_splunk_password = spl_stored_cred.clear_password
toggle_vc_inputs(vc_splunk_uri, vc_splunk_username, vc_splunk_password, vc_path,
local_session_key, pool_name, logger, disable=True)
logger.info("Deleting forwarder Credentials.")
if not vc_forwarder_stanza.passive_delete():
logger.error(
"[pool={2}]Failed to delete vCenter forwarder stanza:{0} for vc: {1}".format(vc_splunk_uri, vc_path,
pool_name))
else:
logger.info("[pool={2}]Forwarder stanza:{0} deleted for vc: {1}.".format(vc_splunk_uri, vc_path, pool_name))
if not spl_stored_cred:
logger.error("Credential Stanza of Forwarder for vc:{0} not found".format(vc_path))
return False
if not spl_stored_cred.passive_delete():
logger.error(
"Could not delete obsolete credential for vCenter forwarder:{0} it may linger".format(vc_splunk_uri))
else:
logger.info("Credential deleted for vCenter forwarder:{0}".format(vc_splunk_uri))
else:
logger.debug("No vCenter forwarder stanza found for vc: {0}.".format(vc_path))
return False
return True
def delete_dcn_stanza(node_path, local_session_key, logger):
"""
:param node_path:
:param local_session_key:
:param logger:
:return: It will return true in case of successful deletion of dcn stanza
"""
node_stanza = HydraNodeStanza.from_name(node_path, "Splunk_TA_vmware_inframon", host_path=local_host_path,
session_key=local_session_key)
if node_stanza:
node_username = node_stanza.user
pool_name = node_stanza.pool_name
stored_cred = SplunkStoredCredential.from_name(
SplunkStoredCredential.build_name(node_stanza.host, node_username), app="Splunk_TA_vmware_inframon",
owner="nobody", host_path=local_host_path,
session_key=local_session_key)
if stored_cred:
node_password = stored_cred.clear_password
logger.info("Deleting obsolete credential for node:{0}".format(node_stanza.host))
if not stored_cred.passive_delete():
logger.error(
"Could not delete obsolete credential it may linger")
else:
logger.info(
"Credentials deleted successfully of node {0}:".format(node_stanza.host))
else:
node_password = None
if not node_stanza.passive_delete():
logger.error("[pool={1}]Failed to delete node {0}".format(node_path, pool_name))
raise RestError(500, "Failed to delete node {0}".format(node_path))
else:
logger.info("[pool={1}]Successfully deleted node: {0}".format(node_path, pool_name))
# disabling of inputs only takes place when node is successfully deleted.
# It will change after adding exceptions.
# for disabling all the inputs(heads), heads is given as Zero.
logger.info("Disabling inputs of deleted stanza: {0}".format(node_path))
enable_heads_on_dcn(node_username, node_password, 0, local_session_key, node_path, pool_name, logger)
return True
else:
logger.error(
"Failed to find node {0}, cannot delete it".format(node_path))
raise RestError(404, "Failed to find node {0}".format(node_path))
def set_conf_modification_time(pool_name, entity_type, local_session_key, logger):
""""""
pool_stanza = PoolStanza.from_name(pool_name, "Splunk_TA_vmware_inframon", host_path=local_host_path,
session_key=local_session_key)
if pool_stanza:
if entity_type == "node":
pool_stanza.node_modification_time = datetime.datetime.utcnow()
elif entity_type == "collection":
pool_stanza.collection_modification_time = datetime.datetime.utcnow()
if not pool_stanza.passive_save():
logger.error(
"[pool={1}]Couldn't set the conf modification time property for type:{0} of the pool:{1}".format(
entity_type,
pool_name))
else:
logger.info(
"[pool={1}]Updated the conf modification time property for type: {0} and pool: {1} ".format(entity_type,
pool_name))
else:
logger.error("Could not find pool stanza for given pool name:{0}".format(pool_name))
def enable_heads_on_dcn(username, password, heads, local_session_key, node_path, pool_name, logger):
"""
This function enables the head on dcn using given username, password, and local session key.
It will enable the number of heads given in @heads parameter and disable the rest of the heads.
So, when heads is given as zero, it will disable all the heads on that dcn.
:param username: splunk username
:param password: splunk password
:param heads: no. of heads to enable on dcn (if heads is given as Zero, then it will disable all the heads on dcn.)
:param local_session_key: session key of current session.
:param node_path: management port url of a dcn.
:return: it returns nothing.
"""
try:
remote_session_key = get_remote_session_key(username, password, local_session_key, node_path, logger)
except ServerNotFoundError:
logger.error("Could not find node=%s", node_path)
remote_session_key = None
except splunk.SplunkdConnectionException:
logger.error("Could not find splunkd on node=%s", node_path)
remote_session_key = None
except splunk.AuthenticationFailed:
logger.error("Could not log into splunkd on node=%s, credentials are definitely bad", node_path)
remote_session_key = None
except Exception as e:
logger.exception(
"could not log in to the node: {0} Exception occurred: {1} ".format(node_path, str(e)))
remote_session_key = None
if remote_session_key is None:
logger.error(
"[pool=%s]Could not log into node=%s with the credentials provided, cannot manage the heads on that node",
pool_name, node_path)
else:
enable_head_status = True
for counter in range(1, max_worker_process + 1):
input_name = "worker_process{0}".format(counter)
if counter <= int(heads):
action = "enable"
else:
action = "disable"
path = node_path.rstrip(
"/") + "/servicesNS/nobody/Splunk_TA_vmware_inframon/data/inputs/ta_vmware_collection_worker_inframon/" + input_name + "/" + action
try:
logger.info("Adjusting input with rest request on path=%s with session_key=%s", path,
remote_session_key)
splunk_rest_request(path, logger, local_session_key=local_session_key,
session_key=remote_session_key, method="POST",
raise_all_errors=True)
except ServerNotFoundError:
enable_head_status = False
logger.exception(
"Problem editing the number of worker inputs on the remote node={0}, Could not reach node.".format(
node_path))
except Exception as e:
enable_head_status = False
message = "Problem editing the number of worker inputs on the remote node={0}: ".format(
node_path) + str(e)
logger.exception(message)
if enable_head_status:
logger.info("[pool={0}]Successfully adjusted input on node:{1}".format(pool_name, node_path))
else:
logger.error("[pool={0}]Problem editing the number of worker inputs on the remote node={1}.".format(
pool_name, node_path))
def delete_template_stanza(template_name, local_session_key, logger):
"""
:param template_name: name of the template to be deleted.
:param local_session_key: session key of the current session
:return: returns true if everything goes fine, It will delete the template stanza from inframon_ta_vmware_template.conf, if failed then it will throw an exception.
"""
template_stanza = TemplateStanza.from_name(template_name, "Splunk_TA_vmware_inframon", host_path=local_host_path,
session_key=local_session_key)
if template_stanza:
if not template_stanza.passive_delete():
logger.error("[pool={0}]Failed to delete the template stanza={0}".format(template_name))
raise RestError(500, "Failed to delete the template stanza={0}".format(template_name))
else:
logger.info("[pool={0}]template stanza successfully deleted for: {0}".format(template_name))
else:
logger.error("[Delete Pool] Failed to find the template stanza={0}, cannot delete it", template_name)
raise RestError(404, "Failed to find the template stanza={0}".format(template_name))
return True
def clear_session(node_path, username, password, local_session_key, logger):
"""
It clears all the stanzas of inframon_hydra_session.conf for given node_path(uri of Node/DCN)
:param node_path: node uri for clearing cache and session
:param username: username of node
:param password: password of node
:param local_session_key: session key of current session
:param logger:
:return: it returns nothing
"""
try:
remote_session_key = get_remote_session_key(username, password, local_session_key, node_path, logger)
except ServerNotFoundError:
logger.error("Could not find node=%s", node_path)
remote_session_key = None
except splunk.SplunkdConnectionException:
logger.error("Could not find splunkd on node=%s", node_path)
remote_session_key = None
except splunk.AuthenticationFailed:
logger.error("Could not log into splunkd on node=%s, credentials are definitely bad", node_path)
remote_session_key = None
except Exception as e:
remote_session_key = None
logger.exception(e)
if remote_session_key is None:
logger.error(
"Could not log into node=%s with the credentials provided, cannot clear session on a node.",
node_path)
else:
session_stanzas = HydraSessionStanza.all(host_path=node_path, sessionKey=remote_session_key)
session_stanzas = session_stanzas.filter_by_app("Splunk_TA_vmware_inframon")
session_stanzas._owner = "nobody"
logger.info("No. of session:{0} on Node:{1}".format(len(session_stanzas), node_path))
for session_stanza in session_stanzas:
session_name = session_stanza.name
if not session_stanza.passive_delete():
logger.error("Could not able to delete session_stanza={0}".format(str(session_stanza)))
else:
logger.info("session of: {0} for node: {1} successfully deleted.".format(session_name, node_path))
def get_node_password(node_stanza, local_session_key, logger):
"""
:param node_stanza: stanza of node
:param local_session_key:
:return: it returns the credentials for the node of given node stanza
"""
stored_cred = SplunkStoredCredential.from_name(
SplunkStoredCredential.build_name(node_stanza.host, node_stanza.user), app="Splunk_TA_vmware_inframon",
owner="nobody", host_path=local_host_path, session_key=local_session_key)
password = ""
if stored_cred:
password = stored_cred.clear_password
else:
logger.error("Failed to find password for: {0} ".format(node_stanza.host))
return password
def prepare_field_list(field_dict, ui_fields, field_type, logger):
"""
:param field_dict: dictionary of field mapping
:param ui_fields: List of UI fields
:param field_type: Type of field
:return: it maps the ui field with appropriate backend inv fields, and will return the backend names of
corresponding UI fields.
"""
inv_fields_set = set()
for field in ui_fields:
field_detail = field_dict[field_type][field]
if field_detail["type"] == "inv":
inv_fields_set.update(field_detail["req_metrics"])
inv_fields = sorted(inv_fields_set)
return inv_fields