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

61 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-20 21:00 -0600

1"""Index settings action class""" 

2import logging 

3# pylint: disable=import-error 

4from curator.exceptions import ActionError, ConfigurationError, MissingArgument 

5from curator.helpers.testers import verify_index_list 

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

7 

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