11"""Utility functions that get things"""
2+
23import logging
3- import re
44from elasticsearch8 import exceptions as es8exc
55from curator .exceptions import (
6- ConfigurationError , CuratorException , FailedExecution , MissingArgument )
6+ ConfigurationError ,
7+ CuratorException ,
8+ FailedExecution ,
9+ MissingArgument ,
10+ )
11+
712
813def byte_size (num , suffix = 'B' ):
914 """
@@ -23,6 +28,23 @@ def byte_size(num, suffix='B'):
2328 num /= 1024.0
2429 return f'{ num :.1f} Y{ suffix } '
2530
31+
32+ def escape_dots (stringval ):
33+ """
34+ Escape any dots (periods) in ``stringval``.
35+
36+ Primarily used for ``filter_path`` where dots are indicators of path nesting
37+
38+ :param stringval: A string, ostensibly an index name
39+
40+ :type stringval: str
41+
42+ :returns: ``stringval``, but with any periods escaped with a backslash
43+ :retval: str
44+ """
45+ return stringval .replace ('.' , r'\.' )
46+
47+
2648def get_alias_actions (oldidx , newidx , aliases ):
2749 """
2850 :param oldidx: The old index name
@@ -34,7 +56,8 @@ def get_alias_actions(oldidx, newidx, aliases):
3456 :type aliases: dict
3557
3658 :returns: A list of actions suitable for
37- :py:meth:`~.elasticsearch.client.IndicesClient.update_aliases` ``actions`` kwarg.
59+ :py:meth:`~.elasticsearch.client.IndicesClient.update_aliases` ``actions``
60+ kwarg.
3861 :rtype: list
3962 """
4063 actions = []
@@ -43,29 +66,40 @@ def get_alias_actions(oldidx, newidx, aliases):
4366 actions .append ({'add' : {'index' : newidx , 'alias' : alias }})
4467 return actions
4568
69+
4670def get_data_tiers (client ):
4771 """
48- Get all valid data tiers from the node roles of each node in the cluster by polling each node
72+ Get all valid data tiers from the node roles of each node in the cluster by
73+ polling each node
4974
5075 :param client: A client connection object
5176 :type client: :py:class:`~.elasticsearch.Elasticsearch`
5277
5378 :returns: The available data tiers in ``tier: bool`` form.
5479 :rtype: dict
5580 """
81+
5682 def role_check (role , node_info ):
5783 if role in node_info ['roles' ]:
5884 return True
5985 return False
86+
6087 info = client .nodes .info ()['nodes' ]
61- retval = {'data_hot' : False , 'data_warm' : False , 'data_cold' : False , 'data_frozen' : False }
88+ retval = {
89+ 'data_hot' : False ,
90+ 'data_warm' : False ,
91+ 'data_cold' : False ,
92+ 'data_frozen' : False ,
93+ }
6294 for node in info :
6395 for role in ['data_hot' , 'data_warm' , 'data_cold' , 'data_frozen' ]:
64- # This guarantees we don't overwrite a True with a False. We only add True values
96+ # This guarantees we don't overwrite a True with a False.
97+ # We only add True values
6598 if role_check (role , info [node ]):
6699 retval [role ] = True
67100 return retval
68101
102+
69103def get_indices (client , search_pattern = '_all' ):
70104 """
71105 Calls :py:meth:`~.elasticsearch.client.CatClient.indices`
@@ -79,10 +113,14 @@ def get_indices(client, search_pattern='_all'):
79113 logger = logging .getLogger (__name__ )
80114 indices = []
81115 try :
82- # Doing this in two stages because IndexList also calls for these args, and the unit tests
83- # need to Mock this call the same exact way.
116+ # Doing this in two stages because IndexList also calls for these args,
117+ # and the unit tests need to Mock this call the same exact way.
84118 resp = client .cat .indices (
85- index = search_pattern , expand_wildcards = 'open,closed' , h = 'index,status' , format = 'json' )
119+ index = search_pattern ,
120+ expand_wildcards = 'open,closed' ,
121+ h = 'index,status' ,
122+ format = 'json' ,
123+ )
86124 except Exception as err :
87125 raise FailedExecution (f'Failed to get indices. Error: { err } ' ) from err
88126 if not resp :
@@ -92,6 +130,7 @@ def get_indices(client, search_pattern='_all'):
92130 logger .debug ('All indices: %s' , indices )
93131 return indices
94132
133+
95134def get_repository (client , repository = '' ):
96135 """
97136 Calls :py:meth:`~.elasticsearch.client.SnapshotClient.get_repository`
@@ -114,6 +153,7 @@ def get_repository(client, repository=''):
114153 )
115154 raise CuratorException (msg ) from err
116155
156+
117157def get_snapshot (client , repository = None , snapshot = '' ):
118158 """
119159 Calls :py:meth:`~.elasticsearch.client.SnapshotClient.get`
@@ -126,9 +166,10 @@ def get_snapshot(client, repository=None, snapshot=''):
126166 :type repository: str
127167 :type snapshot: str
128168
129- :returns: Information about the provided ``snapshot``, a snapshot (or a comma-separated list of
130- snapshots). If no snapshot specified, it will collect info for all snapshots. If none
131- exist, an empty :py:class:`dict` will be returned.
169+ :returns: Information about the provided ``snapshot``, a snapshot (or a
170+ comma-separated list of snapshots). If no snapshot specified, it will
171+ collect info for all snapshots. If none exist, an empty :py:class:`dict`
172+ will be returned.
132173 :rtype: dict
133174 """
134175 if not repository :
@@ -143,6 +184,7 @@ def get_snapshot(client, repository=None, snapshot=''):
143184 )
144185 raise FailedExecution (msg ) from err
145186
187+
146188def get_snapshot_data (client , repository = None ):
147189 """
148190 Get all snapshots from repository and return a list.
@@ -168,6 +210,7 @@ def get_snapshot_data(client, repository=None):
168210 )
169211 raise FailedExecution (msg ) from err
170212
213+
171214def get_tier_preference (client , target_tier = 'data_frozen' ):
172215 """Do the tier preference thing in reverse order from coldest to hottest
173216 Based on the value of ``target_tier``, build out the list to use.
@@ -194,8 +237,8 @@ def get_tier_preference(client, target_tier='data_frozen'):
194237 if tier in tiers and tiermap [tier ] <= tiermap [target_tier ]:
195238 test_list .insert (0 , tier )
196239 if target_tier == 'data_frozen' :
197- # We're migrating to frozen here. If a frozen tier exists, frozen searchable snapshot
198- # mounts should only ever go to the frozen tier.
240+ # We're migrating to frozen here. If a frozen tier exists, frozen searchable
241+ # snapshot mounts should only ever go to the frozen tier.
199242 if 'data_frozen' in tiers and tiers ['data_frozen' ]:
200243 return 'data_frozen'
201244 # If there are no nodes with the 'data_frozen' role...
@@ -207,9 +250,11 @@ def get_tier_preference(client, target_tier='data_frozen'):
207250 # If all of these are false, then we have no data tiers and must use 'data_content'
208251 if not preflist :
209252 return 'data_content'
210- # This will join from coldest to hottest as csv string, e.g. 'data_cold,data_warm,data_hot'
253+ # This will join from coldest to hottest as csv string,
254+ # e.g. 'data_cold,data_warm,data_hot'
211255 return ',' .join (preflist )
212256
257+
213258def get_write_index (client , alias ):
214259 """
215260 Calls :py:meth:`~.elasticsearch.client.IndicesClient.get_alias`
@@ -220,7 +265,8 @@ def get_write_index(client, alias):
220265 :type client: :py:class:`~.elasticsearch.Elasticsearch`
221266 :type alias: str
222267
223- :returns: The the index name associated with the alias that is designated ``is_write_index``
268+ :returns: The the index name associated with the alias that is designated
269+ ``is_write_index``
224270 :rtype: str
225271 """
226272 try :
@@ -229,17 +275,21 @@ def get_write_index(client, alias):
229275 raise CuratorException (f'Alias { alias } not found' ) from exc
230276 # If there are more than one in the list, one needs to be the write index
231277 # otherwise the alias is a one to many, and can't do rollover.
278+ retval = None
232279 if len (list (response .keys ())) > 1 :
233280 for index in list (response .keys ()):
234281 try :
235282 if response [index ]['aliases' ][alias ]['is_write_index' ]:
236- return index
283+ retval = index
237284 except KeyError as exc :
238285 raise FailedExecution (
239- 'Invalid alias: is_write_index not found in 1 to many alias' ) from exc
286+ 'Invalid alias: is_write_index not found in 1 to many alias'
287+ ) from exc
240288 else :
241289 # There's only one, so this is it
242- return list (response .keys ())[0 ]
290+ retval = list (response .keys ())[0 ]
291+ return retval
292+
243293
244294def index_size (client , idx , value = 'total' ):
245295 """
@@ -256,7 +306,11 @@ def index_size(client, idx, value='total'):
256306 :returns: The sum of either ``primaries`` or ``total`` shards for index ``idx``
257307 :rtype: integer
258308 """
259- return client .indices .stats (index = idx )['indices' ][idx ][value ]['store' ]['size_in_bytes' ]
309+ fpath = f'indices.{ escape_dots (idx )} .{ value } .store.size_in_bytes'
310+ return client .indices .stats (index = idx , filter_path = fpath )['indices' ][idx ][value ][
311+ 'store'
312+ ]['size_in_bytes' ]
313+
260314
261315def meta_getter (client , idx , get = None ):
262316 """Meta Getter
@@ -297,9 +351,10 @@ def meta_getter(client, idx, get=None):
297351 logger .error ('Exception encountered: %s' , exc )
298352 return retval
299353
354+
300355def name_to_node_id (client , name ):
301356 """
302- Calls :py:meth:`~.elasticsearch.client.NodesClient.stats `
357+ Calls :py:meth:`~.elasticsearch.client.NodesClient.info `
303358
304359 :param client: A client connection object
305360 :param name: The node ``name``
@@ -311,17 +366,19 @@ def name_to_node_id(client, name):
311366 :rtype: str
312367 """
313368 logger = logging .getLogger (__name__ )
314- stats = client .nodes .stats ()
315- for node in stats ['nodes' ]:
316- if stats ['nodes' ][node ]['name' ] == name :
369+ fpath = 'nodes'
370+ info = client .nodes .info (filter_path = fpath )
371+ for node in info ['nodes' ]:
372+ if info ['nodes' ][node ]['name' ] == name :
317373 logger .debug ('Found node_id "%s" for name "%s".' , node , name )
318374 return node
319375 logger .error ('No node_id found matching name: "%s"' , name )
320376 return None
321377
378+
322379def node_id_to_name (client , node_id ):
323380 """
324- Calls :py:meth:`~.elasticsearch.client.NodesClient.stats `
381+ Calls :py:meth:`~.elasticsearch.client.NodesClient.info `
325382
326383 :param client: A client connection object
327384 :param node_id: The node ``node_id``
@@ -333,15 +390,17 @@ def node_id_to_name(client, node_id):
333390 :rtype: str
334391 """
335392 logger = logging .getLogger (__name__ )
336- stats = client .nodes .stats ()
393+ fpath = f'nodes.{ node_id } .name'
394+ info = client .nodes .info (filter_path = fpath )
337395 name = None
338- if node_id in stats ['nodes' ]:
339- name = stats ['nodes' ][node_id ]['name' ]
396+ if node_id in info ['nodes' ]:
397+ name = info ['nodes' ][node_id ]['name' ]
340398 else :
341399 logger .error ('No node_id found matching: "%s"' , node_id )
342400 logger .debug ('Name associated with node_id "%s": %s' , node_id , name )
343401 return name
344402
403+
345404def node_roles (client , node_id ):
346405 """
347406 Calls :py:meth:`~.elasticsearch.client.NodesClient.info`
@@ -355,12 +414,14 @@ def node_roles(client, node_id):
355414 :returns: The list of roles assigned to the node identified by ``node_id``
356415 :rtype: list
357416 """
358- return client .nodes .info ()['nodes' ][node_id ]['roles' ]
417+ fpath = f'nodes.{ node_id } .roles'
418+ return client .nodes .info (filter_path = fpath )['nodes' ][node_id ]['roles' ]
419+
359420
360421def single_data_path (client , node_id ):
361422 """
362- In order for a shrink to work, it should be on a single filesystem, as shards cannot span
363- filesystems. Calls :py:meth:`~.elasticsearch.client.NodesClient.stats`
423+ In order for a shrink to work, it should be on a single filesystem, as shards
424+ cannot span filesystems. Calls :py:meth:`~.elasticsearch.client.NodesClient.stats`
364425
365426 :param client: A client connection object
366427 :param node_id: The node ``node_id``
@@ -371,4 +432,6 @@ def single_data_path(client, node_id):
371432 :returns: ``True`` if the node has a single filesystem, else ``False``
372433 :rtype: bool
373434 """
374- return len (client .nodes .stats ()['nodes' ][node_id ]['fs' ]['data' ]) == 1
435+ fpath = f'nodes.{ node_id } .fs.data'
436+ response = client .nodes .stats (filter_path = fpath )
437+ return len (response ['nodes' ][node_id ]['fs' ]['data' ]) == 1
0 commit comments