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.

196 lines
7.2 KiB

'''
Synopsis:
splunk cmd python on_splunk_start.py
Usage:
Called on splunk start. Not intended to be called manually, except for testing.
"Managed" Variable Sync Strategy:
Managed variables like the deployment ID have complex lifecycles, and
require synchronization among multiple nodes in a splunk deployment.
This leads to their abstraction behind "manager" class interfaces.
The strategy for syncing them is as follows:
* On Splunk start (when this script is triggered):
** Pull (or "sync") whatever value is at the cluster master,
overwriting any local value.
*** Since configuring clustering often requires a splunk restart,
this provides immediate sync up when clustering is enabled.
** Call the getter for the managed value.
*** The getters are designed to generate a new value
if one does not yet exist (perhaps this is a new installation,
and there was no cluster master to read from).
* On read (when the value is required to perform a task, or to create an event)
** Only call the getter.
** Typically, it is looked up in the conf file, and created anew if needed.
** Special handling may apply, see the corresponding manager class.
** If a new value is created, it *must* be passed to the telemetry endpoint
to be persisted. Only this endpoint ensures replication to the cluster master.
* On nightly scripted input
** Again, the value is pulled from the cluster master if possible
** This ensures liveness in the system, such that disagreements about
these values can eventually be resolved by conforming to the choice
of a single node. Basically, each night, the last search head to
replicate a choice to the cluster master the prior day will have won,
and all search heads will agree.
** In case there is no cluster master, it's usally a single instance,
or the existing conf file replication is relied upon for syncing
the managed values.
'''
import sys
import time
import logging
from splunk_instrumentation.splunkd import Splunkd
from splunk_instrumentation.service_bundle import ServiceBundle
from splunk_instrumentation.deployment_id_manager import DeploymentIdManager
import splunk_instrumentation.constants as constants
from splunk_instrumentation.salt_manager import SaltManager
NAME = "InstrumentationInit"
logger = logging.getLogger(NAME)
class OnSplunkStart(object):
@staticmethod
def wait_for_kv_store_started(services):
'''
Migration of the deployment ID from V1 of instrumentation
requires waiting until the KV store is ready. We'll give
it 5 minutes, then proceed without out.
'''
t_start = time.time()
status = services.server_info_service.content.get('kvStoreStatus')
while status == 'starting' and (time.time() - t_start) < (5 * 60):
time.sleep(10)
services.server_info_service.fetch()
status = services.server_info_service.content.get('kvStoreStatus')
@staticmethod
def initialize_salt(salt_manager):
'''
Create a new telemetry salt for this deployment, if needed.
'''
salt_manager.sync_with_cluster()
salt_manager.get_salt()
@staticmethod
def initialize_deployment_id(services, deployment_id_manager):
'''
Creates a new deployment ID for this deployment, if needed.
'''
deployment_id_manager.sync_deployment_id()
deployment_id = deployment_id_manager.get_deployment_id()
prefix = deployment_id_manager.get_deployment_id_prefix()
# Ensure the correct prefix is set given the current product type
if prefix and not deployment_id.startswith(prefix + '-'):
stripped_deployment_id = deployment_id
for possible_prefix in [p + '-' for p in constants.DEPLOYMENT_ID_PREFIXES]:
if deployment_id.startswith(possible_prefix):
stripped_deployment_id = deployment_id[len(possible_prefix):]
break
deployment_id_manager.set_deployment_id(
prefix + '-' + stripped_deployment_id)
@staticmethod
def opt_in_for_cloud_instrumentation(services):
'''
Configures cloud instance instrumentation settings
'''
settings = {}
current_opt_in_version = services.telemetry_conf_service.content.get('optInVersion')
swa_endpoint_url = '/splunkd/__raw/servicesNS/nobody/splunk_instrumentation/telemetry-metric'
# Being explicit about all opt-ins. Licensing's default
# is changing to True for on-prem. Need to make sure it
# remains disabled for cloud.
settings.update({
'optInVersionAcknowledged': current_opt_in_version,
'sendAnonymizedWebAnalytics': True,
'sendAnonymizedUsage': True,
'sendLicenseUsage': True,
'onCloudInstance': True,
'swaEndpoint': swa_endpoint_url
})
services.telemetry_conf_service.update(settings)
@staticmethod
def migrate_licensing_opt_in_default(services):
'''
Checks if the current opt-in values have been acknowledged
by the user. If not, they are the previous defaults, so we
can enable license data reporting as the new default.
Note that we could not simply push a new default out in the
default/telemetry.conf file, since splunkd will not write
a value to local conf files if their value is the same as
the default value. This means we would have risked changing
explicit opt-outs to opt-ins without user permissions.
'''
# Check legacy showOptInModal field to see if a user has acked
# the licensing opt-in/opt-out.
if services.telemetry_conf_service.content.get('showOptInModal') == '0':
return
# Check the new optInVersionAcknowledged flag
opt_in_version_acked = services.telemetry_conf_service.content.get('optInVersionAcknowledged')
if opt_in_version_acked is not None and int(opt_in_version_acked) != 0:
return
services.telemetry_conf_service.update({'sendLicenseUsage': True})
def main(services,
salt_manager,
deployment_id_manager,
OnSplunkStart):
OnSplunkStart.wait_for_kv_store_started(services)
OnSplunkStart.initialize_salt(salt_manager)
OnSplunkStart.initialize_deployment_id(services, deployment_id_manager)
if services.server_info_service.is_cloud():
OnSplunkStart.opt_in_for_cloud_instrumentation(services)
else:
# Cloud should never opt-in for license sharing,
# so only apply the default on-prem
OnSplunkStart.migrate_licensing_opt_in_default(services)
if __name__ == '__main__':
try:
token = sys.stdin.read().rstrip()
splunkd = Splunkd(token=token, server_uri=constants.SPLUNKD_URI)
services = ServiceBundle(splunkd)
salt_manager = SaltManager(services)
deployment_id_manager = DeploymentIdManager(
services.splunkd,
telemetry_conf_service=services.telemetry_conf_service,
server_info_service=services.server_info_service
)
main(services, salt_manager, deployment_id_manager, OnSplunkStart)
except Exception as e:
logger.error(e)
exit(0)