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
This commit is contained in:
Sam Doran 2017-09-07 11:33:53 -04:00 committed by GitHub
parent c9f2b931db
commit a5ee865634
2 changed files with 91 additions and 35 deletions

View file

@ -1,16 +1,18 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2015, Mathew Davies <thepixeldeveloper@googlemail.com> # (c) 2015, Mathew Davies <thepixeldeveloper@googlemail.com>
# (c) 2017, Sam Doran <sdoran@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {
'status': ['preview'], 'metadata_version': '1.1',
'supported_by': 'community'} 'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = ''' DOCUMENTATION = '''
@ -20,11 +22,13 @@ short_description: Manage Elasticsearch plugins
description: description:
- Manages Elasticsearch plugins. - Manages Elasticsearch plugins.
version_added: "2.0" version_added: "2.0"
author: Mathew Davies (@ThePixelDeveloper) author:
- Mathew Davies (@ThePixelDeveloper)
- Sam Doran (@samdoran)
options: options:
name: name:
description: 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 required: True
state: state:
description: description:
@ -40,13 +44,15 @@ options:
timeout: timeout:
description: description:
- "Timeout setting: 30s, 1m, 1h..." - "Timeout setting: 30s, 1m, 1h..."
- Only valid for Elasticsearch < 5.0. This option is ignored for Elasticsearch > 5.0.
required: False required: False
default: 1m default: 1m
plugin_bin: plugin_bin:
description: 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 required: False
default: /usr/share/elasticsearch/bin/plugin default: None
plugin_dir: plugin_dir:
description: description:
- Your configured plugin directory specified in Elasticsearch - Your configured plugin directory specified in Elasticsearch
@ -73,21 +79,25 @@ options:
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Install Elasticsearch head plugin # Install Elasticsearch Head plugin in Elasticsearch 2.x
- elasticsearch_plugin: - elasticsearch_plugin:
state: present
name: mobz/elasticsearch-head name: mobz/elasticsearch-head
# Install specific version of a plugin
- elasticsearch_plugin:
state: present 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: - 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 state: absent
name: mobz/elasticsearch-head
# Install a specific plugin in Elasticsearch >= 5.0
- elasticsearch_plugin:
name: analysis-icu
state: present
''' '''
import os import os
@ -100,6 +110,11 @@ PACKAGE_STATE_MAP = dict(
absent="remove" absent="remove"
) )
PLUGIN_BIN_PATHS = tuple([
'/usr/share/elasticsearch/bin/elasticsearch-plugin',
'/usr/share/elasticsearch/bin/plugin'
])
def parse_plugin_repo(string): def parse_plugin_repo(string):
elements = string.split("/") elements = string.split("/")
@ -119,21 +134,30 @@ def parse_plugin_repo(string):
return repo return repo
def is_plugin_present(plugin_dir, working_dir): def is_plugin_present(plugin_dir, working_dir):
return os.path.isdir(os.path.join(working_dir, plugin_dir)) return os.path.isdir(os.path.join(working_dir, plugin_dir))
def parse_error(string): def parse_error(string):
reason = "reason: " reason = "ERROR: "
try: try:
return string[string.index(reason) + len(reason):].strip() return string[string.index(reason) + len(reason):].strip()
except ValueError: except ValueError:
return string return string
def install_plugin(module, plugin_bin, plugin_name, version, url, proxy_host, proxy_port, timeout): 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] cmd_args = [plugin_bin, PACKAGE_STATE_MAP["present"], plugin_name]
if version: # Timeout and version are only valid for plugin, not elasticsearch-plugin
plugin_name = plugin_name + '/' + version 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: if proxy_host and proxy_port:
cmd_args.append("-DproxyHost=%s -DproxyPort=%s" % (proxy_host, 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: if url:
cmd_args.append("--url %s" % url) cmd_args.append("--url %s" % url)
if timeout:
cmd_args.append("--timeout %s" % timeout)
cmd = " ".join(cmd_args) cmd = " ".join(cmd_args)
if module.check_mode: if module.check_mode:
@ -153,10 +174,11 @@ def install_plugin(module, plugin_bin, plugin_name, version, url, proxy_host, pr
if rc != 0: if rc != 0:
reason = parse_error(out) 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 return True, cmd, out, err
def remove_plugin(module, plugin_bin, plugin_name): def remove_plugin(module, plugin_bin, plugin_name):
cmd_args = [plugin_bin, PACKAGE_STATE_MAP["absent"], parse_plugin_repo(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 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(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
@ -180,7 +234,7 @@ def main():
state=dict(default="present", choices=PACKAGE_STATE_MAP.keys()), state=dict(default="present", choices=PACKAGE_STATE_MAP.keys()),
url=dict(default=None), url=dict(default=None),
timeout=dict(default="1m"), 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"), plugin_dir=dict(default="/usr/share/elasticsearch/plugins/", type="path"),
proxy_host=dict(default=None), proxy_host=dict(default=None),
proxy_port=dict(default=None), proxy_port=dict(default=None),
@ -189,15 +243,18 @@ def main():
supports_check_mode=True supports_check_mode=True
) )
name = module.params["name"] name = module.params["name"]
state = module.params["state"] state = module.params["state"]
url = module.params["url"] url = module.params["url"]
timeout = module.params["timeout"] timeout = module.params["timeout"]
plugin_bin = module.params["plugin_bin"] plugin_bin = module.params["plugin_bin"]
plugin_dir = module.params["plugin_dir"] plugin_dir = module.params["plugin_dir"]
proxy_host = module.params["proxy_host"] proxy_host = module.params["proxy_host"]
proxy_port = module.params["proxy_port"] proxy_port = module.params["proxy_port"]
version = module.params["version"] 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) present = is_plugin_present(parse_plugin_repo(name), plugin_dir)

View file

@ -185,7 +185,6 @@ lib/ansible/modules/clustering/consul_kv.py
lib/ansible/modules/clustering/consul_session.py lib/ansible/modules/clustering/consul_session.py
lib/ansible/modules/clustering/kubernetes.py lib/ansible/modules/clustering/kubernetes.py
lib/ansible/modules/clustering/pacemaker_cluster.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/kibana_plugin.py
lib/ansible/modules/database/misc/redis.py lib/ansible/modules/database/misc/redis.py
lib/ansible/modules/database/misc/riak.py lib/ansible/modules/database/misc/riak.py