Coverage for curator/cli_singletons/utils.py: 51%

1"""Singleton Utils Module"""

2 import json

3 import logging

4 import sys

5 import click

6 from shutil import get_terminal_size

7 from voluptuous import Schema

8 from curator.actions import (

9 Alias, Allocation, Close, ClusterRouting, CreateIndex, DeleteIndices, DeleteSnapshots,

10 ForceMerge, Open, Replicas, Restore, Shrink, Snapshot

11)

12 from curator.exceptions import NoIndices, NoSnapshots

13 from curator.validators import SchemaCheck, config_file, options

14 from curator.utils import ensure_list, filters, prune_nones, validate_filters

15

16

17 CLASS_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

32 EXCLUDED_OPTIONS = [

33 'ignore_empty_list', 'timeout_override',

34 'continue_if_exception', 'disable_action'

35]

36

37 def get_width():

38 """Determine terminal width"""

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

40

41 def 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

50 def 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

61 def 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

72 def 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

82 def 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

105 def 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

129 def 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

136 def 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

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