From a5ee8656343aaf2da96ed26591310eb709d74cd0 Mon Sep 17 00:00:00 2001 From: Sam Doran Date: Thu, 7 Sep 2017 11:33:53 -0400 Subject: [PATCH] Update elasticsearch_plugin.py (#28936) * Update elasticsearch_plugin.py Change module to work with Elasticsearch 2.x and 5.x automatically. Update examples and docs. Supersedes #21989 * Check system paths for elasticsearch-plugin binary Use get_bin_path from basic.py for searching paths. * Create a copy of PLUGIN_BIN_PATHS rather than modifying the global * Use provided plugin_bin path first before trying other places Change global PLUGIN_BIN_PATHS to a tuple --- .../database/misc/elasticsearch_plugin.py | 125 +++++++++++++----- test/sanity/pep8/legacy-files.txt | 1 - 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/lib/ansible/modules/database/misc/elasticsearch_plugin.py b/lib/ansible/modules/database/misc/elasticsearch_plugin.py index 8e24ad4eab..2fcfeef4bb 100644 --- a/lib/ansible/modules/database/misc/elasticsearch_plugin.py +++ b/lib/ansible/modules/database/misc/elasticsearch_plugin.py @@ -1,16 +1,18 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - # (c) 2015, Mathew Davies +# (c) 2017, Sam Doran # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} DOCUMENTATION = ''' @@ -20,11 +22,13 @@ short_description: Manage Elasticsearch plugins description: - Manages Elasticsearch plugins. version_added: "2.0" -author: Mathew Davies (@ThePixelDeveloper) +author: + - Mathew Davies (@ThePixelDeveloper) + - Sam Doran (@samdoran) options: name: description: - - Name of the plugin to install. In ES 2.x, the name can be an url or file location + - Name of the plugin to install. In Eleasticsearch >= 2.0, the name can be an URL or file location. required: True state: description: @@ -40,13 +44,15 @@ options: timeout: description: - "Timeout setting: 30s, 1m, 1h..." + - Only valid for Elasticsearch < 5.0. This option is ignored for Elasticsearch > 5.0. required: False default: 1m plugin_bin: description: - - Location of the plugin binary + - Location of the plugin binary. If this file is not found, the default plugin binaries will be used. + - The default changed in Ansible 2.4 to None. required: False - default: /usr/share/elasticsearch/bin/plugin + default: None plugin_dir: description: - Your configured plugin directory specified in Elasticsearch @@ -73,21 +79,25 @@ options: ''' EXAMPLES = ''' -# Install Elasticsearch head plugin +# Install Elasticsearch Head plugin in Elasticsearch 2.x - elasticsearch_plugin: - state: present name: mobz/elasticsearch-head - -# Install specific version of a plugin -- elasticsearch_plugin: state: present - name: com.github.kzwang/elasticsearch-image - version: '1.2.0' -# Uninstall Elasticsearch head plugin +# Install a specific version of Elasticsearch Head in Elasticsearch 2.x - elasticsearch_plugin: + name: mobz/elasticsearch-head + versino: 2.0.0 + +# Uninstall Elasticsearch head plugin in Elasticsearch 2.x +- elasticsearch_plugin: + name: mobz/elasticsearch-head state: absent - name: mobz/elasticsearch-head + +# Install a specific plugin in Elasticsearch >= 5.0 +- elasticsearch_plugin: + name: analysis-icu + state: present ''' import os @@ -100,6 +110,11 @@ PACKAGE_STATE_MAP = dict( absent="remove" ) +PLUGIN_BIN_PATHS = tuple([ + '/usr/share/elasticsearch/bin/elasticsearch-plugin', + '/usr/share/elasticsearch/bin/plugin' +]) + def parse_plugin_repo(string): elements = string.split("/") @@ -119,21 +134,30 @@ def parse_plugin_repo(string): return repo + def is_plugin_present(plugin_dir, working_dir): return os.path.isdir(os.path.join(working_dir, plugin_dir)) + def parse_error(string): - reason = "reason: " + reason = "ERROR: " try: return string[string.index(reason) + len(reason):].strip() except ValueError: return string + def install_plugin(module, plugin_bin, plugin_name, version, url, proxy_host, proxy_port, timeout): cmd_args = [plugin_bin, PACKAGE_STATE_MAP["present"], plugin_name] - if version: - plugin_name = plugin_name + '/' + version + # Timeout and version are only valid for plugin, not elasticsearch-plugin + if os.path.basename(plugin_bin) == 'plugin': + if timeout: + cmd_args.append("--timeout %s" % timeout) + + if version: + plugin_name = plugin_name + '/' + version + cmd_args[2] = plugin_name if proxy_host and proxy_port: cmd_args.append("-DproxyHost=%s -DproxyPort=%s" % (proxy_host, proxy_port)) @@ -141,9 +165,6 @@ def install_plugin(module, plugin_bin, plugin_name, version, url, proxy_host, pr if url: cmd_args.append("--url %s" % url) - if timeout: - cmd_args.append("--timeout %s" % timeout) - cmd = " ".join(cmd_args) if module.check_mode: @@ -153,10 +174,11 @@ def install_plugin(module, plugin_bin, plugin_name, version, url, proxy_host, pr if rc != 0: reason = parse_error(out) - module.fail_json(msg=reason) + module.fail_json(msg='Is %s a valid plugin name?' % plugin_name, err=reason) return True, cmd, out, err + def remove_plugin(module, plugin_bin, plugin_name): cmd_args = [plugin_bin, PACKAGE_STATE_MAP["absent"], parse_plugin_repo(plugin_name)] @@ -173,6 +195,38 @@ def remove_plugin(module, plugin_bin, plugin_name): return True, cmd, out, err + +def get_plugin_bin(module, plugin_bin): + # Use the plugin_bin that was supplied first before trying other options + if plugin_bin: + if os.path.isfile(plugin_bin): + valid_plugin_bin = plugin_bin + + else: + # Add the plugin_bin passed into the module to the top of the list of paths to test, + # testing for that binary name first before falling back to the default paths. + bin_paths = list(PLUGIN_BIN_PATHS) + if plugin_bin and plugin_bin not in bin_paths: + bin_paths.insert(0, plugin_bin) + + # Get separate lists of dirs and binary names from the full paths to the + # plugin binaries. + plugin_dirs = list(set([os.path.dirname(x) for x in bin_paths])) + plugin_bins = list(set([os.path.basename(x) for x in bin_paths])) + + # Check for the binary names in the default system paths as well as the path + # specified in the module arguments. + for bin_file in plugin_bins: + valid_plugin_bin = module.get_bin_path(bin_file, opt_dirs=plugin_dirs) + if valid_plugin_bin: + break + + if not valid_plugin_bin: + module.fail_json(msg='%s does not exist and no other valid plugin installers were found. Make sure Elasticsearch is installed.' % plugin_bin) + + return valid_plugin_bin + + def main(): module = AnsibleModule( argument_spec=dict( @@ -180,7 +234,7 @@ def main(): state=dict(default="present", choices=PACKAGE_STATE_MAP.keys()), url=dict(default=None), timeout=dict(default="1m"), - plugin_bin=dict(default="/usr/share/elasticsearch/bin/plugin", type="path"), + plugin_bin=dict(type="path"), plugin_dir=dict(default="/usr/share/elasticsearch/plugins/", type="path"), proxy_host=dict(default=None), proxy_port=dict(default=None), @@ -189,15 +243,18 @@ def main(): supports_check_mode=True ) - name = module.params["name"] - state = module.params["state"] - url = module.params["url"] - timeout = module.params["timeout"] - plugin_bin = module.params["plugin_bin"] - plugin_dir = module.params["plugin_dir"] - proxy_host = module.params["proxy_host"] - proxy_port = module.params["proxy_port"] - version = module.params["version"] + name = module.params["name"] + state = module.params["state"] + url = module.params["url"] + timeout = module.params["timeout"] + plugin_bin = module.params["plugin_bin"] + plugin_dir = module.params["plugin_dir"] + proxy_host = module.params["proxy_host"] + proxy_port = module.params["proxy_port"] + version = module.params["version"] + + # Search provided path and system paths for valid binary + plugin_bin = get_plugin_bin(module, plugin_bin) present = is_plugin_present(parse_plugin_repo(name), plugin_dir) diff --git a/test/sanity/pep8/legacy-files.txt b/test/sanity/pep8/legacy-files.txt index 868d5a1eca..e905dff232 100644 --- a/test/sanity/pep8/legacy-files.txt +++ b/test/sanity/pep8/legacy-files.txt @@ -185,7 +185,6 @@ lib/ansible/modules/clustering/consul_kv.py lib/ansible/modules/clustering/consul_session.py lib/ansible/modules/clustering/kubernetes.py lib/ansible/modules/clustering/pacemaker_cluster.py -lib/ansible/modules/database/misc/elasticsearch_plugin.py lib/ansible/modules/database/misc/kibana_plugin.py lib/ansible/modules/database/misc/redis.py lib/ansible/modules/database/misc/riak.py