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

1"""Alias action"""

2 import logging

3# pylint: disable=import-error

4 from curator.exceptions import ActionError, MissingArgument, NoIndices

5 from curator.helpers.date_ops import parse_date_pattern, parse_datemath

6 from curator.helpers.testers import verify_index_list

7 from curator.helpers.utils import report_failure

8

9 class Alias:

10 """Alias Action Class"""

11 # pylint: disable=unused-argument

12 def __init__(self, name=None, extra_settings=None, **kwargs):

13 """

14 :param name: The alias name

15 :param extra_settings: Extra settings, including filters and routing. For more information

16 see `here </https://www.elastic.co/guide/en/elasticsearch/reference/8.6/indices-aliases.html>`_.

17

18 :type name: str

19 :type extra_settings: dict

20 """

21 if extra_settings is None:

22 extra_settings = {}

23 if not name:

24 raise MissingArgument('No value for "name" provided.')

25 #: The :py:func:`~.curator.helpers.date_ops.parse_date_pattern` rendered

26 #: version of what was passed by param ``name``.

27 self.name = parse_date_pattern(name)

28 #: The list of actions to perform. Populated by :py:meth:`~.curator.actions.Alias.add` and

29 #: :py:meth:`~.curator.actions.Alias.remove`

30 self.actions = []

31 #: The :py:class:`~.elasticsearch.Elasticsearch` client object which will later be set by

32 #: :py:meth:`~.curator.actions.Alias.add` or :py:meth:`~.curator.actions.Alias.remove`

33 self.client = None

34 #: Any extra things to add to the alias, like filters, or routing. Gets the value from

35 #: param ``extra_settings``.

36 self.extra_settings = extra_settings

37 self.loggit = logging.getLogger('curator.actions.alias')

38 #: Preset default value to ``False``.

39 self.warn_if_no_indices = False

40

41 def add(self, ilo, warn_if_no_indices=False):

42 """

43 Create ``add`` statements for each index in ``ilo`` for :py:attr:`name`, then

44 append them to :py:attr:`actions`. Add any :py:attr:`extra_settings` that may be there.

45

46 :param ilo: An IndexList Object

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

48 """

49 verify_index_list(ilo)

50 self.loggit.debug('ADD -> ILO = %s', ilo)

51 if not self.client:

52 self.client = ilo.client

53 self.name = parse_datemath(self.client, self.name)

54 try:

55 ilo.empty_list_check()

56 except NoIndices as exc:

57 # Add a warning if there are no indices to add, if so set in options

58 if warn_if_no_indices:

59 self.warn_if_no_indices = True

60 self.loggit.warning(

61 'No indices found after processing filters. Nothing to add to %s', self.name)

62 return

63 # Re-raise the exceptions.NoIndices so it will behave as before

64 raise NoIndices('No indices to add to alias') from exc

65 for index in ilo.working_list():

66 self.loggit.debug(

67 'Adding index %s to alias %s with extra settings '

68 '%s', index, self.name, self.extra_settings

69 )

70 add_dict = {'add' : {'index' : index, 'alias': self.name}}

71 add_dict['add'].update(self.extra_settings)

72 self.actions.append(add_dict)

73

74 def remove(self, ilo, warn_if_no_indices=False):

75 """

76 Create ``remove`` statements for each index in ``ilo`` for :py:attr:`name`,

77 then append them to :py:attr:`actions`.

78

79 :param ilo: An IndexList Object

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

81 """

82 verify_index_list(ilo)

83 self.loggit.debug('REMOVE -> ILO = %s', ilo)

84 if not self.client:

85 self.client = ilo.client

86 self.name = parse_datemath(self.client, self.name)

87 try:

88 ilo.empty_list_check()

89 except NoIndices as exc:

90 # Add a warning if there are no indices to add, if so set in options

91 if warn_if_no_indices:

92 self.warn_if_no_indices = True

93 self.loggit.warning(

94 'No indices found after processing filters. '

95 'Nothing to remove from %s', self.name

96 )

97 return

98

99 # Re-raise the exceptions.NoIndices so it will behave as before

100 raise NoIndices('No indices to remove from alias') from exc

101 aliases = self.client.indices.get_alias()

102 for index in ilo.working_list():

103 if index in aliases:

104 self.loggit.debug('Index %s in get_aliases output', index)

105 # Only remove if the index is associated with the alias

106 if self.name in aliases[index]['aliases']:

107 self.loggit.debug('Removing index %s from alias %s', index, self.name)

108 self.actions.append(

109 {'remove' : {'index' : index, 'alias': self.name}})

110 else:

111 self.loggit.debug(

112 'Can not remove: Index %s is not associated with alias %s', index, self.name

113 )

114

115 def check_actions(self):

116 """

117 :returns: :py:attr:`actions` for use with the

118 :py:meth:`~.elasticsearch.client.IndicesClient.update_aliases` API call if actions

119 exist, otherwise an exception is raised.

120 """

121 if not self.actions:

122 if not self.warn_if_no_indices:

123 raise ActionError('No "add" or "remove" operations')

124 raise NoIndices('No "adds" or "removes" found. Taking no action')

125 self.loggit.debug('Alias actions: %s', self.actions)

126

127 return self.actions

128

129 def do_dry_run(self):

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

131 self.loggit.info('DRY-RUN MODE. No changes will be made.')

132 for item in self.check_actions():

133 job = list(item.keys())[0]

134 index = item[job]['index']

135 alias = item[job]['alias']

136 # We want our log to look clever, so if job is "remove", strip the

137 # 'e' so "remove" can become "removing". "adding" works already.

138 msg = (

139 f"DRY-RUN: alias: {job.rstrip('e')}ing index \"{index}\" "

140 f"{'to' if job == 'add' else 'from'} alias \"{alias}\""

141 )

142 self.loggit.info(msg)

143

144 def do_action(self):

145 """

146 :py:meth:`~.elasticsearch.client.IndicesClient.update_aliases` for :py:attr:`name` with

147 :py:attr:`actions`

148 """

149 self.loggit.info('Updating aliases...')

150 self.loggit.info('Alias actions: %s', self.actions)

151 try:

152 self.client.indices.update_aliases(actions=self.actions)

153 # pylint: disable=broad-except

154 except Exception as err:

155 report_failure(err)