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.

155 lines
5.9 KiB

#!/usr/bin/env python
# coding=utf-8
#
# Copyright © Splunk, Inc. All Rights Reserved.
from __future__ import absolute_import, division, print_function, unicode_literals
from os import path
import sys
import json
import slim.app
import slim.command
import slim.utils
# Argument parser definition
parser = slim.command.SlimArgumentParser(
description='split an app source package into a set of targeted deployment packages',
epilog='The targeted deployment packages created are based on user-defined deployment specifications. A deployment '
'specification can contain any combination of three different types of Splunk workloads: indexer (named as '
'"_indexers"), search head (named as "_search_heads") and forwarder (named as "_forwarders").'
)
parser.add_app_package()
parser.add_argument_help()
parser.add_installation()
parser.add_output_directory(description='deployment packages')
parser.add_repository()
parser.add_combine_search_head_indexer_workloads()
parser.add_forwarder_workloads()
parser.add_deployment_packages()
parser.add_target_os()
# Command-specific arguments
# TODO: Make this the default state for 'partition' and remove the 'install' options to split logically (?)
# What's the use case for not updating the input installation graph, if there is one? Is the output installation graph
# not always potentially useful to customers using the CLI to partition an app? Why force customers to run another
# command to achieve the same end: partitioning an app and then updating an installation graph?
parser.add_argument(
'-p', '--partition-only',
action='store_const', const=True, default=False,
help='verify installation graph to ensure it is unchanged after partitioning; useful when you are partitioning an '
'app for re-deployment')
def main(args):
slim.utils.SlimLogger.step('Extracting source from ', slim.utils.encode_filename(args.source), '...')
app_source = get_app_source(args.source, None)
server_collection = get_app_server_class_collection(args.installation, args.repository)
if args.partition_only:
if args.target_os:
slim.utils.SlimLogger.information('"target-os: %s" is ignored because --partition-only is used.'
% args.target_os)
else:
slim.utils.SlimLogger.step('Adding ', app_source.qualified_id, ' to installation graph...')
server_collection.add(app_source, get_deployment_specifications(
args.deployment_packages, args.combine_search_head_indexer_workloads, args.forwarder_workloads
), args.target_os)
slim.utils.SlimLogger.exit_on_error()
# Save the resulting installation graph, even if there are no changes; when len(deployment_packages) == 0
filename = path.join(args.output_dir, 'installation-update.json')
server_collection.save(filename)
slim.utils.SlimLogger.information('Saved updated installation graph to ', slim.utils.encode_filename(filename))
_partition(app_source, server_collection, args.output_dir, partition_all=True)
def partition(source, installation_graph, output_dir):
if isinstance(installation_graph, dict):
installation_graph = json.dumps(installation_graph)
string_io_type = slim.command.SlimStringIOArgument(name="installation_graph.json")
installation = string_io_type(value=installation_graph)
slim.utils.SlimLogger.step('Extracting source from ', slim.utils.encode_filename(source), '...')
app_source = get_app_source(source, None)
server_collection = get_app_server_class_collection(installation, slim.utils.slim_configuration.repository_path)
_partition(app_source, server_collection, output_dir, partition_all=False)
def _partition(app_source, server_collection, output_dir, partition_all):
""" Partition an app into deployment packages targeting a collection of server classes.
:param app_source: Represents the app to be partitioned.
:type app_source: AppSource
:param server_collection: Represents the set of server classes.
:type server_collection: ServerClassCollections
:param output_dir: Path to output directory
:type output_dir: string
:param partition_all:
:type partition_all: bool
"""
slim.utils.SlimLogger.step('Partitioning ', app_source.qualified_id, '...')
deployment_packages = server_collection.partition(app_source, output_dir, partition_all)
if len(deployment_packages) > 0:
app_id = app_source.qualified_id
slim.utils.SlimLogger.information('Generated deployment packages for ', app_id,
':\n ', '\n '.join(deployment_packages))
else:
slim.utils.SlimLogger.information(
'No deployment packages generated for ', app_source.id, ' because there is nothing meaningful to deploy'
)
# Wrapper functions to handle errors
def get_app_source(package, configuration):
app_source = slim.app.AppSource(package, configuration)
slim.utils.SlimLogger.exit_on_error()
return app_source
def get_app_server_class_collection(installation, repository):
server_classes = slim.app.AppServerClassCollection.load(installation, repository)
slim.utils.SlimLogger.exit_on_error()
return server_classes
def get_deployment_specifications(
deployment_specifications, combine_search_head_indexer_workloads, forwarder_deployment_specifications
):
deployment_specifications = slim.app.AppDeploymentSpecification.get_deployment_specifications(
deployment_specifications, combine_search_head_indexer_workloads, forwarder_deployment_specifications)
slim.utils.SlimLogger.exit_on_error()
return deployment_specifications
if __name__ == '__main__':
# noinspection PyBroadException
try:
main(parser.parse_args(sys.argv[1:]))
except SystemExit:
raise
except:
slim.utils.SlimLogger.set_debug(True)
slim.utils.SlimLogger.fatal(exception_info=sys.exc_info())

Powered by BW's shoe-string budget.