Coverage for curator/cli_singletons/utils.py: 51%
96 statements
« prev ^ index » next coverage.py v7.3.0, created at 2023-08-16 15:27 -0600
« prev ^ index » next coverage.py v7.3.0, created at 2023-08-16 15:27 -0600
1"""Singleton Utils Module"""
2import json
3import logging
4import sys
5import click
6from shutil import get_terminal_size
7from voluptuous import Schema
8from curator.actions import (
9 Alias, Allocation, Close, ClusterRouting, CreateIndex, DeleteIndices, DeleteSnapshots,
10 ForceMerge, Open, Replicas, Restore, Shrink, Snapshot
11)
12from curator.exceptions import NoIndices, NoSnapshots
13from curator.validators import SchemaCheck, config_file, options
14from curator.utils import ensure_list, filters, prune_nones, validate_filters
17CLASS_MAP = {
18 'alias' : Alias,
19 'allocation' : Allocation,
20 'close' : Close,
21 'cluster_routing' : ClusterRouting,
22 'create_index' : CreateIndex,
23 'delete_indices' : DeleteIndices,
24 'delete_snapshots' : DeleteSnapshots,
25 'forcemerge' : ForceMerge,
26 'open' : Open,
27 'replicas' : Replicas,
28 'restore' : Restore,
29 'snapshot' : Snapshot,
30}
32EXCLUDED_OPTIONS = [
33 'ignore_empty_list', 'timeout_override',
34 'continue_if_exception', 'disable_action'
35]
37def get_width():
38 """Determine terminal width"""
39 return dict(max_content_width=get_terminal_size()[0])
41def json_to_dict(ctx, param, value):
42 """Convert JSON to dictionary"""
43 if value is None:
44 return {}
45 try:
46 return json.loads(value)
47 except ValueError:
48 raise click.BadParameter('Invalid JSON for {0}: {1}'.format(param, value))
50def validate_filter_json(ctx, param, value):
51 """Validate the JSON provided from the command-line"""
52 # The "None" edge case should only be for optional filters, like alias add/remove
53 if value is None:
54 return value
55 try:
56 filter_list = ensure_list(json.loads(value))
57 return filter_list
58 except ValueError:
59 raise click.BadParameter('Filter list is invalid JSON: {0}'.format(value))
61def false_to_none(ctx, param, value):
62 """Convert Python False to a None"""
63 try:
64 if value:
65 retval = True
66 else:
67 retval = None
68 except ValueError:
69 raise click.BadParameter('Invalid value: {0}'.format(value))
70 return retval
72def filter_schema_check(action, filter_dict):
73 """Validate the provided filters against the filter schema"""
74 valid_filters = SchemaCheck(
75 filter_dict,
76 Schema(filters.Filters(action, location='singleton')),
77 'filters',
78 '{0} singleton action "filters"'.format(action)
79 ).result()
80 return validate_filters(action, valid_filters)
82def actionator(action, action_obj, dry_run=True):
83 """Perform the action or its dry run"""
84 logger = logging.getLogger(__name__)
85 logger.debug('Doing the singleton "{0}" action here.'.format(action))
86 try:
87 if dry_run:
88 action_obj.do_dry_run()
89 else:
90 action_obj.do_action()
91 except Exception as err:
92 if isinstance(err, NoIndices) or isinstance(err, NoSnapshots):
93 logger.error(
94 'Unable to complete action "{0}". No actionable items '
95 'in list: {1}'.format(action, type(err))
96 )
97 else:
98 logger.error(
99 'Failed to complete action: {0}. {1}: '
100 '{2}'.format(action, type(err), err)
101 )
102 sys.exit(1)
103 logger.info('Singleton "{0}" action completed.'.format(action))
105def do_filters(list_object, filters, ignore=False):
106 """Perform filters against the IndexList"""
107 logger = logging.getLogger(__name__)
108 logger.debug('Running filters and testing for empty list object')
109 try:
110 list_object.iterate_filters(filters)
111 list_object.empty_list_check()
112 except (NoIndices, NoSnapshots) as err:
113 if isinstance(err, NoIndices):
114 otype = 'index'
115 else:
116 otype = 'snapshot'
117 if ignore:
118 logger.info(
119 'Singleton action not performed: empty {0} list'.format(otype)
120 )
121 sys.exit(0)
122 else:
123 logger.error(
124 'Singleton action failed due to empty {0} list'.format(otype)
125 )
126 sys.exit(1)
129def prune_excluded(option_dict):
130 """Remove excluded options"""
131 for k in list(option_dict.keys()):
132 if k in EXCLUDED_OPTIONS:
133 del option_dict[k]
134 return option_dict
136def option_schema_check(action, option_dict):
137 """Validate command-line options against the option schema"""
138 clean_options = SchemaCheck(
139 prune_nones(option_dict),
140 options.get_schema(action),
141 'options',
142 '{0} singleton action "options"'.format(action)
143 ).result()
144 return prune_excluded(clean_options)
146def config_override(ctx, config_dict):
147 """Override file-based and default config options with command-line provided ones"""
148 if config_dict is None:
149 config_dict = {}
150 for k in ['client', 'logging']:
151 if not k in config_dict:
152 config_dict[k] = {}
153 for k in list(ctx.params.keys()):
154 if k in ['dry_run', 'config']:
155 pass
156 elif k == 'host':
157 if 'host' in ctx.params and ctx.params['host'] is not None:
158 config_dict['client']['hosts'] = ctx.params[k]
159 elif k in ['loglevel', 'logfile', 'logformat', 'ecs']:
160 if k in ctx.params and ctx.params[k] is not None:
161 config_dict['logging'][k] = ctx.params[k]
162 else:
163 if k in ctx.params and ctx.params[k] is not None:
164 config_dict['client'][k] = ctx.params[k]
165 # After override, prune the nones
166 for k in ['client', 'logging']:
167 config_dict[k] = prune_nones(config_dict[k])
168 return SchemaCheck(
169 config_dict,
170 config_file.client(),
171 'Client Configuration',
172 'full configuration dictionary'
173 ).result()