Coverage for curator/actions/alias.py: 100%
80 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-20 21:00 -0600
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-20 21:00 -0600
1"""Alias action"""
2import logging
3# pylint: disable=import-error
4from curator.exceptions import ActionError, MissingArgument, NoIndices
5from curator.helpers.date_ops import parse_date_pattern, parse_datemath
6from curator.helpers.testers import verify_index_list
7from curator.helpers.utils import report_failure
9class 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>`_.
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
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.
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)
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`.
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
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 )
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)
127 return self.actions
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)
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)