Coverage for curator/actions/index_settings.py: 100%

1"""Index settings action class"""

2 import logging

3# pylint: disable=import-error

4 from curator.exceptions import ActionError, ConfigurationError, MissingArgument

5 from curator.helpers.testers import verify_index_list

6 from curator.helpers.utils import chunk_index_list, report_failure, show_dry_run, to_csv

7

8 class IndexSettings:

9 """Index Settings Action Class"""

10 def __init__(

11 self, ilo, index_settings=None, ignore_unavailable=False, preserve_existing=False):

12 """

13 :param ilo: An IndexList Object

14 :param index_settings: A settings structure with one or more index settings to change.

15 :param ignore_unavailable: Whether specified concrete indices should be ignored when

16 unavailable (missing or closed)

17 :param preserve_existing: Whether to update existing settings. If set to ``True``, existing

18 settings on an index remain unchanged. The default is ``False``

19

20 :type ilo: :py:class:`~.curator.indexlist.IndexList`

21 :type index_settings: dict

22 :type ignore_unavailable: bool

23 :type preserve_existing: bool

24 """

25 if index_settings is None:

26 index_settings = {}

27 verify_index_list(ilo)

28 if not index_settings:

29 raise MissingArgument('Missing value for "index_settings"')

30 #: The :py:class:`~.curator.indexlist.IndexList` object passed from param ``ilo``

31 self.index_list = ilo

32 #: The :py:class:`~.elasticsearch.Elasticsearch` client object derived from

33 #: :py:attr:`index_list`

34 self.client = ilo.client

35 #: Object attribute that gets the value of param ``index_settings``.

36 self.body = index_settings

37 #: Object attribute that gets the value of param ``ignore_unavailable``.

38 self.ignore_unavailable = ignore_unavailable

39 #: Object attribute that gets the value of param ``preserve_existing``.

40 self.preserve_existing = preserve_existing

41

42 self.loggit = logging.getLogger('curator.actions.index_settings')

43 self._body_check()

44

45 def _body_check(self):

46 # The body only passes the skimpiest of requirements by having 'index'

47 # as the only root-level key, and having a 'dict' as its value

48 if len(self.body) == 1:

49 if 'index' in self.body:

50 if isinstance(self.body['index'], dict):

51 return True

52 raise ConfigurationError(f'Bad value for "index_settings": {self.body}')

53

54 def _static_settings(self):

55 return [

56 'number_of_shards',

57 'shard',

58 'codec',

59 'routing_partition_size',

60 ]

61

62 def _dynamic_settings(self):

63 return [

64 'number_of_replicas',

65 'auto_expand_replicas',

66 'refresh_interval',

67 'max_result_window',

68 'max_rescore_window',

69 'blocks',

70 'max_refresh_listeners',

71 'mapping',

72 'merge',

73 'translog',

74 ]

75

76 def _settings_check(self):

77 # Detect if even one index is open. Save all found to open_index_list.

78 open_index_list = []

79 open_indices = False

80 # This action requires index settings and state to be present

81 # Calling these here should not cause undue problems, even if it's a repeat call

82 self.index_list.get_index_state()

83 self.index_list.get_index_settings()

84 for idx in self.index_list.indices:

85 if self.index_list.index_info[idx]['state'] == 'open':

86 open_index_list.append(idx)

87 open_indices = True

88 for k in self.body['index']:

89 if k in self._static_settings():

90 if not self.ignore_unavailable:

91 if open_indices:

92 msg = (

93 f'Static Setting "{k}" detected with open indices: {open_index_list}. '

94 f'Static settings can only be used with closed indices. Recommend '

95 f'filtering out open indices, or setting ignore_unavailable to True'

96 )

97 raise ActionError(msg)

98 elif k in self._dynamic_settings():

99 # Dynamic settings should be appliable to open or closed indices

100 # Act here if the case is different for some settings.

101 pass

102 else:

103 msg = f'"{k}" is not a setting Curator recognizes and may or may not work.'

104 self.loggit.warning(msg)

105

106 def do_dry_run(self):

107 """Log what the output would be, but take no action."""

108 show_dry_run(self.index_list, 'indexsettings', **self.body)

109

110 def do_action(self):

111 """

112 :py:meth:`~.elasticsearch.client.IndicesClient.put_settings` in :py:attr:`body` to indices

113 in :py:attr:`index_list`

114 """

115 self._settings_check()

116 # Ensure that the open indices filter applied in _settings_check()

117 # didn't result in an empty list (or otherwise empty)

118 self.index_list.empty_list_check()

119 msg = (

120 f'Applying index settings to {len(self.index_list.indices)} indices: '

121 f'{self.index_list.indices}'

122 )

123 self.loggit.info(msg)

124 try:

125 index_lists = chunk_index_list(self.index_list.indices)

126 for lst in index_lists:

127 response = self.client.indices.put_settings(

128 index=to_csv(lst), body=self.body,

129 ignore_unavailable=self.ignore_unavailable,

130 preserve_existing=self.preserve_existing

131 )

132 self.loggit.debug('PUT SETTINGS RESPONSE: %s', response)

133 # pylint: disable=broad-except

134 except Exception as err:

135 report_failure(err)