summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general/plugins/lookup/etcd3.py
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/community/general/plugins/lookup/etcd3.py')
-rw-r--r--ansible_collections/community/general/plugins/lookup/etcd3.py229
1 files changed, 229 insertions, 0 deletions
diff --git a/ansible_collections/community/general/plugins/lookup/etcd3.py b/ansible_collections/community/general/plugins/lookup/etcd3.py
new file mode 100644
index 000000000..7f0a0cf90
--- /dev/null
+++ b/ansible_collections/community/general/plugins/lookup/etcd3.py
@@ -0,0 +1,229 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2020, SCC France, Eric Belhomme <ebelhomme@fr.scc.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+ author:
+ - Eric Belhomme (@eric-belhomme) <ebelhomme@fr.scc.com>
+ version_added: '0.2.0'
+ name: etcd3
+ short_description: Get key values from etcd3 server
+ description:
+ - Retrieves key values and/or key prefixes from etcd3 server using its native gRPC API.
+ - Try to reuse M(community.general.etcd3) options for connection parameters, but add support for some C(ETCDCTL_*) environment variables.
+ - See U(https://github.com/etcd-io/etcd/tree/master/Documentation/op-guide) for etcd overview.
+
+ options:
+ _terms:
+ description:
+ - The list of keys (or key prefixes) to look up on the etcd3 server.
+ type: list
+ elements: str
+ required: true
+ prefix:
+ description:
+ - Look for key or prefix key.
+ type: bool
+ default: false
+ endpoints:
+ description:
+ - Counterpart of C(ETCDCTL_ENDPOINTS) environment variable.
+ Specify the etcd3 connection with and URL form eg. C(https://hostname:2379) or C(<host>:<port>) form.
+ - The C(host) part is overwritten by I(host) option, if defined.
+ - The C(port) part is overwritten by I(port) option, if defined.
+ env:
+ - name: ETCDCTL_ENDPOINTS
+ default: '127.0.0.1:2379'
+ type: str
+ host:
+ description:
+ - etcd3 listening client host.
+ - Takes precedence over I(endpoints).
+ type: str
+ port:
+ description:
+ - etcd3 listening client port.
+ - Takes precedence over I(endpoints).
+ type: int
+ ca_cert:
+ description:
+ - etcd3 CA authority.
+ env:
+ - name: ETCDCTL_CACERT
+ type: str
+ cert_cert:
+ description:
+ - etcd3 client certificate.
+ env:
+ - name: ETCDCTL_CERT
+ type: str
+ cert_key:
+ description:
+ - etcd3 client private key.
+ env:
+ - name: ETCDCTL_KEY
+ type: str
+ timeout:
+ description:
+ - Client timeout.
+ default: 60
+ env:
+ - name: ETCDCTL_DIAL_TIMEOUT
+ type: int
+ user:
+ description:
+ - Authenticated user name.
+ env:
+ - name: ETCDCTL_USER
+ type: str
+ password:
+ description:
+ - Authenticated user password.
+ env:
+ - name: ETCDCTL_PASSWORD
+ type: str
+
+ notes:
+ - I(host) and I(port) options take precedence over (endpoints) option.
+ - The recommended way to connect to etcd3 server is using C(ETCDCTL_ENDPOINT)
+ environment variable and keep I(endpoints), I(host), and I(port) unused.
+ seealso:
+ - module: community.general.etcd3
+ - ref: ansible_collections.community.general.etcd_lookup
+ description: The etcd v2 lookup.
+
+ requirements:
+ - "etcd3 >= 0.10"
+'''
+
+EXAMPLES = '''
+- name: "a value from a locally running etcd"
+ ansible.builtin.debug:
+ msg: "{{ lookup('community.general.etcd3', 'foo/bar') }}"
+
+- name: "values from multiple folders on a locally running etcd"
+ ansible.builtin.debug:
+ msg: "{{ lookup('community.general.etcd3', 'foo', 'bar', 'baz') }}"
+
+- name: "look for a key prefix"
+ ansible.builtin.debug:
+ msg: "{{ lookup('community.general.etcd3', '/foo/bar', prefix=True) }}"
+
+- name: "connect to etcd3 with a client certificate"
+ ansible.builtin.debug:
+ msg: "{{ lookup('community.general.etcd3', 'foo/bar', cert_cert='/etc/ssl/etcd/client.pem', cert_key='/etc/ssl/etcd/client.key') }}"
+'''
+
+RETURN = '''
+ _raw:
+ description:
+ - List of keys and associated values.
+ type: list
+ elements: dict
+ contains:
+ key:
+ description: The element's key.
+ type: str
+ value:
+ description: The element's value.
+ type: str
+'''
+
+import re
+
+from ansible.errors import AnsibleLookupError
+from ansible.module_utils.basic import missing_required_lib
+from ansible.module_utils.common.text.converters import to_native
+from ansible.plugins.lookup import LookupBase
+from ansible.utils.display import Display
+
+try:
+ import etcd3
+ HAS_ETCD = True
+except ImportError:
+ HAS_ETCD = False
+
+display = Display()
+
+etcd3_cnx_opts = (
+ 'host',
+ 'port',
+ 'ca_cert',
+ 'cert_key',
+ 'cert_cert',
+ 'timeout',
+ 'user',
+ 'password',
+ # 'grpc_options' Etcd3Client() option currently not supported by lookup module (maybe in future ?)
+)
+
+
+def etcd3_client(client_params):
+ try:
+ etcd = etcd3.client(**client_params)
+ etcd.status()
+ except Exception as exp:
+ raise AnsibleLookupError('Cannot connect to etcd cluster: %s' % (to_native(exp)))
+ return etcd
+
+
+class LookupModule(LookupBase):
+
+ def run(self, terms, variables, **kwargs):
+
+ self.set_options(var_options=variables, direct=kwargs)
+
+ if not HAS_ETCD:
+ display.error(missing_required_lib('etcd3'))
+ return None
+
+ # create the etcd3 connection parameters dict to pass to etcd3 class
+ client_params = {}
+
+ # etcd3 class expects host and port as connection parameters, so endpoints
+ # must be mangled a bit to fit in this scheme.
+ # so here we use a regex to extract server and port
+ match = re.compile(
+ r'^(https?://)?(?P<host>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([-_\d\w\.]+))(:(?P<port>\d{1,5}))?/?$'
+ ).match(self.get_option('endpoints'))
+ if match:
+ if match.group('host'):
+ client_params['host'] = match.group('host')
+ if match.group('port'):
+ client_params['port'] = match.group('port')
+
+ for opt in etcd3_cnx_opts:
+ if self.get_option(opt):
+ client_params[opt] = self.get_option(opt)
+
+ cnx_log = dict(client_params)
+ if 'password' in cnx_log:
+ cnx_log['password'] = '<redacted>'
+ display.verbose("etcd3 connection parameters: %s" % cnx_log)
+
+ # connect to etcd3 server
+ etcd = etcd3_client(client_params)
+
+ ret = []
+ # we can pass many keys to lookup
+ for term in terms:
+ if self.get_option('prefix'):
+ try:
+ for val, meta in etcd.get_prefix(term):
+ if val and meta:
+ ret.append({'key': to_native(meta.key), 'value': to_native(val)})
+ except Exception as exp:
+ display.warning('Caught except during etcd3.get_prefix: %s' % (to_native(exp)))
+ else:
+ try:
+ val, meta = etcd.get(term)
+ if val and meta:
+ ret.append({'key': to_native(meta.key), 'value': to_native(val)})
+ except Exception as exp:
+ display.warning('Caught except during etcd3.get: %s' % (to_native(exp)))
+ return ret