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

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 

15 

16 

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} 

31 

32EXCLUDED_OPTIONS = [ 

33 'ignore_empty_list', 'timeout_override', 

34 'continue_if_exception', 'disable_action' 

35] 

36 

37def get_width(): 

38 """Determine terminal width""" 

39 return dict(max_content_width=get_terminal_size()[0]) 

40 

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)) 

49 

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)) 

60 

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 

71 

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) 

81 

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)) 

104 

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) 

127 

128 

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 

135 

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) 

145 

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()