1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# Copyright (c) 2016, Thierno IB. BARRY @barryib 5# Sponsored by Polyconseil http://polyconseil.fr. 6# 7# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 8 9from __future__ import absolute_import, division, print_function 10__metaclass__ = type 11 12 13DOCUMENTATION = ''' 14--- 15module: kibana_plugin 16short_description: Manage Kibana plugins 17description: 18 - This module can be used to manage Kibana plugins. 19author: Thierno IB. BARRY (@barryib) 20options: 21 name: 22 description: 23 - Name of the plugin to install. 24 required: True 25 type: str 26 state: 27 description: 28 - Desired state of a plugin. 29 choices: ["present", "absent"] 30 default: present 31 type: str 32 url: 33 description: 34 - Set exact URL to download the plugin from. 35 - For local file, prefix its absolute path with file:// 36 type: str 37 timeout: 38 description: 39 - "Timeout setting: 30s, 1m, 1h etc." 40 default: 1m 41 type: str 42 plugin_bin: 43 description: 44 - Location of the Kibana binary. 45 default: /opt/kibana/bin/kibana 46 type: path 47 plugin_dir: 48 description: 49 - Your configured plugin directory specified in Kibana. 50 default: /opt/kibana/installedPlugins/ 51 type: path 52 version: 53 description: 54 - Version of the plugin to be installed. 55 - If plugin exists with previous version, plugin will NOT be updated unless C(force) is set to yes. 56 type: str 57 force: 58 description: 59 - Delete and re-install the plugin. Can be useful for plugins update. 60 type: bool 61 default: false 62 allow_root: 63 description: 64 - Whether to allow C(kibana) and C(kibana-plugin) to be run as root. Passes the C(--allow-root) flag to these commands. 65 type: bool 66 default: false 67 version_added: 2.3.0 68''' 69 70EXAMPLES = ''' 71- name: Install Elasticsearch head plugin 72 community.general.kibana_plugin: 73 state: present 74 name: elasticsearch/marvel 75 76- name: Install specific version of a plugin 77 community.general.kibana_plugin: 78 state: present 79 name: elasticsearch/marvel 80 version: '2.3.3' 81 82- name: Uninstall Elasticsearch head plugin 83 community.general.kibana_plugin: 84 state: absent 85 name: elasticsearch/marvel 86''' 87 88RETURN = ''' 89cmd: 90 description: the launched command during plugin management (install / remove) 91 returned: success 92 type: str 93name: 94 description: the plugin name to install or remove 95 returned: success 96 type: str 97url: 98 description: the url from where the plugin is installed from 99 returned: success 100 type: str 101timeout: 102 description: the timeout for plugin download 103 returned: success 104 type: str 105stdout: 106 description: the command stdout 107 returned: success 108 type: str 109stderr: 110 description: the command stderr 111 returned: success 112 type: str 113state: 114 description: the state for the managed plugin 115 returned: success 116 type: str 117''' 118 119import os 120from distutils.version import LooseVersion 121from ansible.module_utils.basic import AnsibleModule 122 123 124PACKAGE_STATE_MAP = dict( 125 present="--install", 126 absent="--remove" 127) 128 129 130def parse_plugin_repo(string): 131 elements = string.split("/") 132 133 # We first consider the simplest form: pluginname 134 repo = elements[0] 135 136 # We consider the form: username/pluginname 137 if len(elements) > 1: 138 repo = elements[1] 139 140 # remove elasticsearch- prefix 141 # remove es- prefix 142 for string in ("elasticsearch-", "es-"): 143 if repo.startswith(string): 144 return repo[len(string):] 145 146 return repo 147 148 149def is_plugin_present(plugin_dir, working_dir): 150 return os.path.isdir(os.path.join(working_dir, plugin_dir)) 151 152 153def parse_error(string): 154 reason = "reason: " 155 try: 156 return string[string.index(reason) + len(reason):].strip() 157 except ValueError: 158 return string 159 160 161def install_plugin(module, plugin_bin, plugin_name, url, timeout, allow_root, kibana_version='4.6'): 162 if LooseVersion(kibana_version) > LooseVersion('4.6'): 163 kibana_plugin_bin = os.path.join(os.path.dirname(plugin_bin), 'kibana-plugin') 164 cmd_args = [kibana_plugin_bin, "install"] 165 if url: 166 cmd_args.append(url) 167 else: 168 cmd_args.append(plugin_name) 169 else: 170 cmd_args = [plugin_bin, "plugin", PACKAGE_STATE_MAP["present"], plugin_name] 171 172 if url: 173 cmd_args.extend(["--url", url]) 174 175 if timeout: 176 cmd_args.extend(["--timeout", timeout]) 177 178 if allow_root: 179 cmd_args.append('--allow-root') 180 181 if module.check_mode: 182 return True, " ".join(cmd_args), "check mode", "" 183 184 rc, out, err = module.run_command(cmd_args) 185 if rc != 0: 186 reason = parse_error(out) 187 module.fail_json(msg=reason) 188 189 return True, " ".join(cmd_args), out, err 190 191 192def remove_plugin(module, plugin_bin, plugin_name, allow_root, kibana_version='4.6'): 193 if LooseVersion(kibana_version) > LooseVersion('4.6'): 194 kibana_plugin_bin = os.path.join(os.path.dirname(plugin_bin), 'kibana-plugin') 195 cmd_args = [kibana_plugin_bin, "remove", plugin_name] 196 else: 197 cmd_args = [plugin_bin, "plugin", PACKAGE_STATE_MAP["absent"], plugin_name] 198 199 if allow_root: 200 cmd_args.append('--allow-root') 201 202 if module.check_mode: 203 return True, " ".join(cmd_args), "check mode", "" 204 205 rc, out, err = module.run_command(cmd_args) 206 if rc != 0: 207 reason = parse_error(out) 208 module.fail_json(msg=reason) 209 210 return True, " ".join(cmd_args), out, err 211 212 213def get_kibana_version(module, plugin_bin, allow_root): 214 cmd_args = [plugin_bin, '--version'] 215 216 if allow_root: 217 cmd_args.append('--allow-root') 218 219 rc, out, err = module.run_command(cmd_args) 220 if rc != 0: 221 module.fail_json(msg="Failed to get Kibana version : %s" % err) 222 223 return out.strip() 224 225 226def main(): 227 module = AnsibleModule( 228 argument_spec=dict( 229 name=dict(required=True), 230 state=dict(default="present", choices=list(PACKAGE_STATE_MAP.keys())), 231 url=dict(default=None), 232 timeout=dict(default="1m"), 233 plugin_bin=dict(default="/opt/kibana/bin/kibana", type="path"), 234 plugin_dir=dict(default="/opt/kibana/installedPlugins/", type="path"), 235 version=dict(default=None), 236 force=dict(default=False, type="bool"), 237 allow_root=dict(default=False, type="bool"), 238 ), 239 supports_check_mode=True, 240 ) 241 242 name = module.params["name"] 243 state = module.params["state"] 244 url = module.params["url"] 245 timeout = module.params["timeout"] 246 plugin_bin = module.params["plugin_bin"] 247 plugin_dir = module.params["plugin_dir"] 248 version = module.params["version"] 249 force = module.params["force"] 250 allow_root = module.params["allow_root"] 251 252 changed, cmd, out, err = False, '', '', '' 253 254 kibana_version = get_kibana_version(module, plugin_bin, allow_root) 255 256 present = is_plugin_present(parse_plugin_repo(name), plugin_dir) 257 258 # skip if the state is correct 259 if (present and state == "present" and not force) or (state == "absent" and not present and not force): 260 module.exit_json(changed=False, name=name, state=state) 261 262 if version: 263 name = name + '/' + version 264 265 if state == "present": 266 if force: 267 remove_plugin(module, plugin_bin, name, allow_root, kibana_version) 268 changed, cmd, out, err = install_plugin(module, plugin_bin, name, url, timeout, allow_root, kibana_version) 269 270 elif state == "absent": 271 changed, cmd, out, err = remove_plugin(module, plugin_bin, name, allow_root, kibana_version) 272 273 module.exit_json(changed=changed, cmd=cmd, name=name, state=state, url=url, timeout=timeout, stdout=out, stderr=err) 274 275 276if __name__ == '__main__': 277 main() 278