From 48b5b7e184a8ca07b0d82ce6ece18036bfe1c78d Mon Sep 17 00:00:00 2001 From: Proskurin Kirill Date: Sat, 6 Jul 2019 13:04:54 +0100 Subject: [PATCH] Added ES index get_settings and put_settings methods --- CHANGELOG.md | 3 +- salt/modules/elasticsearch.py | 111 +++++++++++++++++++++++ tests/unit/modules/test_elasticsearch.py | 85 +++++++++++++++++ 3 files changed, 198 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8ca498675e3..0fd9592b106e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,8 @@ Versions are `MAJOR.PATCH`. ### Added -- [#54917](https://github.com/saltstack/salt/pull/54917) - Added get_settings, put_settings and flush_synced methods for Elasticsearch module. - [@Oloremo](https://github.com/Oloremo) +- [#54505](https://github.com/saltstack/salt/issues/54505) - Added cluster get_settings, put_settings and flush_synced methods for Elasticsearch module. - [@Oloremo](https://github.com/Oloremo) +- [#53736](https://github.com/saltstack/salt/issues/53736) - Added index get_settings, put_settings methods for Elasticsearch module. - [@Oloremo](https://github.com/Oloremo) - [#55418](https://github.com/saltstack/salt/pull/55418) - Added clean_parent argument for the archive state. - [@Oloremo](https://github.com/Oloremo) - [#55593](https://github.com/saltstack/salt/issues/55593) - Added a support for a global proxy to pip module. - [@Oloremo](https://github.com/Oloremo) diff --git a/salt/modules/elasticsearch.py b/salt/modules/elasticsearch.py index 87a9b1a5b1e6..819bc452abd3 100644 --- a/salt/modules/elasticsearch.py +++ b/salt/modules/elasticsearch.py @@ -691,6 +691,117 @@ def index_close(index, allow_no_indices=True, expand_wildcards='open', ignore_un raise CommandExecutionError("Cannot close index {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) +def index_get_settings(hosts=None, profile=None, **kwargs): + ''' + .. versionadded:: Neon + + Check for the existence of an index and if it exists, return its settings + http://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-settings.html + + index + (Optional, string) A comma-separated list of index names; use _all or empty string for all indices. Defaults to '_all'. + name + (Optional, string) The name of the settings that should be included + allow_no_indices + (Optional, boolean) Whether to ignore if a wildcard indices expression resolves into no concrete indices. + (This includes _all string or when no indices have been specified) + expand_wildcards + (Optional, string) Whether to expand wildcard expression to concrete indices that are open, closed or both. + Valid choices are: ‘open’, ‘closed’, ‘none’, ‘all’ + flat_settings + (Optional, boolean) Return settings in flat format + ignore_unavailable + (Optional, boolean) Whether specified concrete indices should be ignored when unavailable (missing or closed) + include_defaults + (Optional, boolean) Whether to return all default setting for each of the indices. + local + (Optional, boolean) Return local information, do not retrieve the state from master node + + The defaults settings for the above parameters depend on the API version being used. + + CLI example:: + + salt myminion elasticsearch.index_get_settings index=testindex + ''' + + es = _get_instance(hosts, profile) + + # Filtering Salt internal keys + filtered_kwargs = kwargs.copy() + for k in kwargs: + if k.startswith('__'): + filtered_kwargs.pop(k) + + try: + return es.indices.get_settings(**filtered_kwargs) + except elasticsearch.exceptions.NotFoundError: + return None + except elasticsearch.TransportError as e: + raise CommandExecutionError("Cannot retrieve index settings {0}, server returned code {1} with message {2}".format(kwargs, e.status_code, e.error)) + + +def index_put_settings(body=None, hosts=None, profile=None, source=None, **kwargs): + ''' + .. versionadded:: Neon + + Update existing index settings + https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html + + body + The index settings to be updated. + source + URL to file specifying index definition. Cannot be used in combination with ``body``. + index + (Optional, string) A comma-separated list of index names; use _all or empty string to perform the operation on all indices + allow_no_indices + (Optional, boolean) Whether to ignore if a wildcard indices expression resolves into no concrete indices. + (This includes _all string or when no indices have been specified) + expand_wildcards + (Optional, string) Whether to expand wildcard expression to concrete indices that are open, closed or both. + Valid choices are: ‘open’, ‘closed’, ‘none’, ‘all’ + flat_settings + (Optional, boolean) Return settings in flat format (default: false) + ignore_unavailable + (Optional, boolean) Whether specified concrete indices should be ignored when unavailable (missing or closed) + master_timeout + (Optional, time units) Explicit operation timeout for connection to master node + preserve_existing + (Optional, boolean) Whether to update existing settings. If set to true existing settings on an index remain unchanged, the default is false + + The defaults settings for the above parameters depend on the API version being used. + + ..note:: + Elasticsearch time units can be found here: + https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units + + CLI example:: + + salt myminion elasticsearch.index_put_settings index=testindex body='{"settings" : {"index" : {"number_of_replicas" : 2}}}' + ''' + es = _get_instance(hosts, profile) + if source and body: + message = 'Either body or source should be specified but not both.' + raise SaltInvocationError(message) + if source: + body = __salt__['cp.get_file_str']( + source, + saltenv=__opts__.get('saltenv', 'base')) + + # Filtering Salt internal keys + filtered_kwargs = kwargs.copy() + for k in kwargs: + if k.startswith('__'): + filtered_kwargs.pop(k) + + try: + result = es.indices.put_settings(body=body, **filtered_kwargs) + return result.get('acknowledged', False) + except elasticsearch.exceptions.NotFoundError: + return None + except elasticsearch.TransportError as e: + raise CommandExecutionError("Cannot update index settings {0}, server returned code {1} with message {2}".format(kwargs, e.status_code, e.error)) + + def mapping_create(index, doc_type, body=None, hosts=None, profile=None, source=None): ''' Create a mapping in a given index diff --git a/tests/unit/modules/test_elasticsearch.py b/tests/unit/modules/test_elasticsearch.py index eb2eee5c4784..f7cdf529220d 100644 --- a/tests/unit/modules/test_elasticsearch.py +++ b/tests/unit/modules/test_elasticsearch.py @@ -996,6 +996,91 @@ class MockElastic(object): MagicMock(return_value=MockElastic())): self.assertRaises(CommandExecutionError, elasticsearch.index_exists, "foo", "bar") + def test_index_get_settings(self): + ''' + Test if settings can be obtained from the index + ''' + + fake_es = MagicMock() + fake_es.indices = MagicMock() + fake_es.indices.get_settings = MagicMock(return_value={"foo": "key"}) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + self.assertDictEqual(elasticsearch.index_get_settings("foo", "bar"), {"foo": "key"}) + + def test_index_get_settings_not_exists(self): + ''' + Test index_get_settings if index doesn't exist + ''' + + fake_es = MagicMock() + fake_es.indices = MagicMock() + fake_es.indices.get_settings = MagicMock() + fake_es.indices.get_settings.side_effect = NotFoundError("custom error", 123) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + self.assertIs(elasticsearch.index_get_settings(index='foo'), None) + + def test_get_settings_failure(self): + ''' + Test if index settings get fails with CommandExecutionError + ''' + + fake_es = MagicMock() + fake_es.indices = MagicMock() + fake_es.indices.get_settings = MagicMock() + fake_es.indices.get_settings.side_effect = TransportError("custom error", 123) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + self.assertRaises(CommandExecutionError, elasticsearch.index_get_settings, index='foo') + + def test_index_put_settings(self): + ''' + Test if we can put settings for the index + ''' + + body = {"settings": {"index": {"number_of_replicas": 2}}} + fake_es = MagicMock() + fake_es.indices = MagicMock() + fake_es.indices.put_settings = MagicMock(return_value={"acknowledged": True}) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + self.assertTrue(elasticsearch.index_put_settings(index='foo', body=body)) + + def test_index_put_settings_not_exists(self): + ''' + Test if settings put executed agains non-existinf index + ''' + + body = {"settings": {"index": {"number_of_replicas": 2}}} + fake_es = MagicMock() + fake_es.indices = MagicMock() + fake_es.indices.put_settings = MagicMock() + fake_es.indices.put_settings.side_effect = NotFoundError("custom error", 123) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + self.assertIs(elasticsearch.index_put_settings(index='foo', body=body), None) + + def test_index_put_settings_failure(self): + ''' + Test if settings put failed with CommandExecutionError + ''' + + body = {"settings": {"index": {"number_of_replicas": 2}}} + fake_es = MagicMock() + fake_es.indices = MagicMock() + fake_es.indices.put_settings = MagicMock() + fake_es.indices.put_settings.side_effect = TransportError("custom error", 123) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + self.assertRaises(CommandExecutionError, elasticsearch.index_put_settings, index='foo', body=body) + # 'index_get' function tests: 3 def test_index_get(self):