Update: the whole code
This commit is contained in:
parent
31a52f84f7
commit
d4964af1af
@ -7,7 +7,17 @@
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
from pprint import pprint
|
||||
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
|
||||
|
||||
#sys.path.append("/home/jez/prj/bell/training/tiger-ansible/ext/kheops")
|
||||
from ansible_collections.barbu_it.ansible_kheops.plugins.plugin_utils.common import DOCUMENTATION_OPTION_FRAGMENT, AnsibleKheops
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: kheops
|
||||
@ -26,102 +36,12 @@ DOCUMENTATION = '''
|
||||
- inventory_cache
|
||||
- constructed
|
||||
options:
|
||||
|
||||
plugin:
|
||||
description: token that ensures this is a source file for the C(kheops) plugin.
|
||||
required: True
|
||||
choices: ['kheops', 'barbu_it.ansible_kheops.kheops']
|
||||
|
||||
|
||||
# Query configuration
|
||||
# ==========================
|
||||
query_namespace:
|
||||
description:
|
||||
- The Kheops namespace to use
|
||||
default: 'default'
|
||||
env:
|
||||
- name: ANSIBLE_KHEOPS_NAMESPACE
|
||||
query_scope:
|
||||
description:
|
||||
- A hash containing the scope to use for the request, the values will be resolved as Ansible facts.
|
||||
- Use a dot notation to dig deeper into nested hash facts.
|
||||
default: {}
|
||||
query_keys:
|
||||
description:
|
||||
- A list of keys to lookup
|
||||
default: {}
|
||||
|
||||
|
||||
# Instance configuration
|
||||
# ==========================
|
||||
configuration:
|
||||
description:
|
||||
- Path to Kheops configuration yaml file
|
||||
default: 'kheops.yml'
|
||||
env:
|
||||
- name: ANSIBLE_KHEOPS_CONFIG
|
||||
log_level:
|
||||
description:
|
||||
- Khéops logging level
|
||||
choices: ['DEBUG', 'INFO', 'WARNING', 'ERROR']
|
||||
default: 'WARNING'
|
||||
env:
|
||||
- name: ANSIBLE_KHEOPS_LOG_LEVEL
|
||||
|
||||
# turfu # Client configuration
|
||||
# turfu token:
|
||||
# turfu description:
|
||||
# turfu - The Kheops token to use to authenticate against Kheops server.
|
||||
# turfu default: ''
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_TOKEN
|
||||
# turfu host:
|
||||
# turfu description:
|
||||
# turfu - Hostname of the Kheops Server.
|
||||
# turfu default: '127.0.0.1'
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_HOST
|
||||
# turfu port:
|
||||
# turfu description:
|
||||
# turfu - Kheops port to connect to.
|
||||
# turfu default: '9843'
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_PORT
|
||||
# turfu protocol:
|
||||
# turfu description:
|
||||
# turfu - The URL protocol to use.
|
||||
# turfu default: 'http'
|
||||
# turfu choices: ['http', 'https']
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_PROTOCOL
|
||||
# turfu validate_certs:
|
||||
# turfu description:
|
||||
# turfu - Whether or not to verify the TLS certificates of the Kheops server.
|
||||
# turfu type: boolean
|
||||
# turfu default: False
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_VALIDATE_CERTS
|
||||
|
||||
# Uneeded # Misc
|
||||
# Uneeded version:
|
||||
# Uneeded description:
|
||||
# Uneeded - Kheops API version to use.
|
||||
# Uneeded default: 1
|
||||
# Uneeded choices: [1]
|
||||
# Uneeded env:
|
||||
# Uneeded - name: ANSIBLE_KHEOPS_VERSION
|
||||
# Uneeded cache:
|
||||
# Uneeded description:
|
||||
# Uneeded - Enable Kheops inventory cache.
|
||||
# Uneeded default: false
|
||||
# Uneeded type: boolean
|
||||
# Uneeded env:
|
||||
# Uneeded - name: ANSIBLE_KHEOPS_CACHE
|
||||
# Uneeded policy:
|
||||
# Uneeded description:
|
||||
# Uneeded - Kheops policy to use for the lookups.
|
||||
# Uneeded default: 'default'
|
||||
|
||||
'''
|
||||
''' + DOCUMENTATION_OPTION_FRAGMENT
|
||||
|
||||
EXAMPLES = '''
|
||||
# zzz_dev.kheops.yml
|
||||
@ -145,17 +65,7 @@ query_scope:
|
||||
environment: foreman_environment_name
|
||||
'''
|
||||
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
|
||||
|
||||
#import kheops.app as Kheops
|
||||
from kheops.app import Kheops
|
||||
|
||||
sys.path.append("/home/jez/prj/bell/training/tiger-ansible/ext/kheops")
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin, Cacheable, Constructable):
|
||||
@ -172,81 +82,52 @@ class InventoryModule(BaseInventoryPlugin, Cacheable, Constructable):
|
||||
'''Return dynamic inventory from source '''
|
||||
|
||||
super(InventoryModule, self).parse(inventory, loader, path, cache)
|
||||
self._read_config_data(path)
|
||||
|
||||
# Read configuration
|
||||
self.keys = self.get_option('query_keys')
|
||||
self.scope = self.get_option('query_scope')
|
||||
self.namespace = self.get_option('query_namespace')
|
||||
config_data = self._read_config_data(path)
|
||||
#self._consume_options(config_data)
|
||||
|
||||
self.strict = self.get_option('strict')
|
||||
self.configuration = self.get_option('configuration')
|
||||
self.log_level = self.get_option('log_level')
|
||||
|
||||
self.cache_enabled = os.environ.get(
|
||||
'ANSIBLE_KHEOPS_CACHE',
|
||||
str(self.get_option('cache'))
|
||||
).lower() in ('true', '1', 't')
|
||||
self.compose = self.get_option('compose')
|
||||
self.groups = self.get_option('groups')
|
||||
self.keyed_groups = self.get_option('keyed_groups')
|
||||
|
||||
|
||||
# Determine cache behavior
|
||||
#attempt_to_read_cache = self.cache_enabled and cache
|
||||
self.config_file = self.get_option('config')
|
||||
|
||||
# Khéops instance
|
||||
kheops = Kheops(config=self.configuration, namespace=self.namespace)
|
||||
|
||||
# Khéops log support
|
||||
log_level = getattr(logging, self.log_level, 'DEBUG')
|
||||
|
||||
logger = logging.getLogger('kheops')
|
||||
logger.setLevel(log_level)
|
||||
|
||||
# See for logging: https://medium.com/opsops/debugging-requests-1989797736cc
|
||||
class ListLoggerHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
msg = self.format(record)
|
||||
|
||||
list_logging_handler = ListLoggerHandler()
|
||||
main_logger = logging.getLogger()
|
||||
main_logger.addHandler(list_logging_handler)
|
||||
main_logger.setLevel(logging.DEBUG)
|
||||
|
||||
# from pyinstrument import Profiler
|
||||
#profiler = Profiler()
|
||||
#profiler.start()
|
||||
configs = [
|
||||
self.config_file,
|
||||
path,
|
||||
]
|
||||
kheops = AnsibleKheops(configs=configs, display=self.display)
|
||||
|
||||
# Loop over each keys
|
||||
for host_name in inventory.hosts:
|
||||
host = self.inventory.get_host(host_name)
|
||||
|
||||
# Build the scope
|
||||
scope = {}
|
||||
host_vars = host.get_vars()
|
||||
for key, val in self.scope.items():
|
||||
scope[key] = host_vars.get(val, None)
|
||||
self.display.vvv(f"Kheops scope for {host.name}: {scope} for keys: {self.keys}")
|
||||
scope = kheops.get_scope_from_host_inventory(host.get_vars(), scope=None)
|
||||
|
||||
# Fetch the results
|
||||
ret = kheops.lookup(
|
||||
keys=[ key_src for key_dst, key_src in self.keys.items() ],
|
||||
keys=None,
|
||||
scope=scope,
|
||||
#trace=True,
|
||||
#explain=True,
|
||||
)
|
||||
|
||||
# Inject variables into host
|
||||
for ansible_key, kheops_key in self.keys.items():
|
||||
ansible_value = ret.get(kheops_key)
|
||||
host.set_variable(ansible_key, ansible_value)
|
||||
for key, value in ret.items():
|
||||
self.display.vv (f"Set {host_name} var: {key}={value}")
|
||||
host.set_variable(key, value)
|
||||
|
||||
# Call constructed inventory plugin methods
|
||||
hostvars = self.inventory.get_host(host_name).get_vars()
|
||||
self._set_composite_vars(self.get_option('compose'), hostvars, host_name, self.strict)
|
||||
self._add_host_to_composed_groups(self.get_option('groups'), hostvars, host_name, self.strict)
|
||||
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), hostvars, host_name, self.strict)
|
||||
#hostvars = self.inventory.get_host(host_name).get_vars()
|
||||
hostvars = host.get_vars()
|
||||
|
||||
#tutu = hostvars.get('tiger_profiles', "MISSSSINGGGG")
|
||||
#print ("YOOOOO", tutu, self.keyed_groups, host_name, self.strict)
|
||||
|
||||
|
||||
#profiler.stop()
|
||||
##profiler.print()
|
||||
#profiler.open_in_browser()
|
||||
self._set_composite_vars(self.compose, hostvars, host_name, self.strict)
|
||||
self._add_host_to_composed_groups(self.groups, hostvars, host_name, self.strict)
|
||||
self._add_host_to_keyed_groups(self.keyed_groups, hostvars, host_name, self.strict)
|
||||
|
||||
|
||||
221
plugins/lookup/kheops.py
Normal file
221
plugins/lookup/kheops.py
Normal file
@ -0,0 +1,221 @@
|
||||
# Copyright 2021 Robin Cordier <robin.cordier@bell.ca>
|
||||
# Copyright 2017 Craig Dunn <craig@craigdunn.org>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
import yaml
|
||||
|
||||
|
||||
from copy import deepcopy
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible.template import generate_ansible_template_vars, AnsibleEnvironment, USE_JINJA2_NATIVE
|
||||
|
||||
from ansible_collections.barbu_it.ansible_kheops.plugins.plugin_utils.common import DOCUMENTATION_OPTION_FRAGMENT, AnsibleKheops
|
||||
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
if USE_JINJA2_NATIVE:
|
||||
from ansible.utils.native_jinja import NativeJinjaText
|
||||
|
||||
DOCUMENTATION = """
|
||||
lookup: kheops
|
||||
author: Robin Cordier <robin.cordier@bell.ca>
|
||||
version_added: "3"
|
||||
short_description: read key/values from Kheops
|
||||
description:
|
||||
- This lookup returns the contents of a Kheops key
|
||||
- This is an improved fork of https://github.com/kheops/kheops-ansible-lookup-plugin
|
||||
- This fork provides python3 support and more query options
|
||||
options:
|
||||
_terms:
|
||||
description: One or more string terms prefixed by a namespace. Format is `<namespace>/<key>`.
|
||||
required: True
|
||||
|
||||
enable_jinja:
|
||||
description:
|
||||
- Enable or not Jinja rendering
|
||||
default: True
|
||||
version_added: '2.11'
|
||||
type: bool
|
||||
jinja2_native:
|
||||
description:
|
||||
- Controls whether to use Jinja2 native types.
|
||||
- It is off by default even if global jinja2_native is True.
|
||||
- Has no effect if global jinja2_native is False.
|
||||
- This offers more flexibility than the template module which does not use Jinja2 native types at all.
|
||||
- Mutually exclusive with the convert_data option.
|
||||
default: False
|
||||
version_added: '2.11'
|
||||
type: bool
|
||||
notes:
|
||||
- Kheops documentation is available on http://kheops.io/
|
||||
- You can add more parameters as documented in http://kheops.io/server/api
|
||||
|
||||
""" + DOCUMENTATION_OPTION_FRAGMENT
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Return the value of key
|
||||
debug:
|
||||
msg: "{{ lookup('kheops', 'default/key') }}"
|
||||
|
||||
- name: Return a list of values
|
||||
debug:
|
||||
msg: "{{ lookup('kheops', 'ansible/yum_packages', 'ansible/yum_repos') }}"
|
||||
|
||||
- name: Advanced usage
|
||||
debug:
|
||||
msg: "{{ lookup('kheops', 'ansible/yum_packages', merge='deep_hash', lookup_type='cascade') }}"
|
||||
|
||||
- name: Advanced usage with custom parameters
|
||||
debug:
|
||||
msg: "{{ lookup('kheops', 'ansible/yum_packages', policy='ansible') }}"
|
||||
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_data:
|
||||
description:
|
||||
- Value of the key, when only one term is searched
|
||||
_list:
|
||||
description:
|
||||
- List of value of the keys, when more than one term is searched
|
||||
type: list
|
||||
|
||||
"""
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
# Entry point for Ansible starts here with the LookupModule class
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, scope=None, **kwargs):
|
||||
|
||||
|
||||
self.set_options(direct=kwargs)
|
||||
#self.config_file = self.get_option('config')
|
||||
|
||||
self.config_file = self.get_option('config')
|
||||
|
||||
configs = [
|
||||
self.config_file,
|
||||
# {
|
||||
# "instance_log_level": 'DEBUG',
|
||||
# }
|
||||
]
|
||||
kheops = AnsibleKheops(configs=configs, display=self._display)
|
||||
|
||||
|
||||
scope = kheops.get_scope_from_host_inventory(variables, scope=None)
|
||||
scope = kheops.get_scope_from_jinja(variables, self._templar, scope=None)
|
||||
|
||||
#assert False , f"OUTOUT: {scope2}"
|
||||
|
||||
|
||||
#for key, value in self.get_option('scope').items():
|
||||
# scope[key] = variables[value]
|
||||
|
||||
ret = []
|
||||
for term in terms:
|
||||
result = kheops.lookup(
|
||||
keys=term,
|
||||
scope=scope,
|
||||
)
|
||||
ret.append(result)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# if isinstance(terms, str):
|
||||
# terms = terms.split(',')
|
||||
|
||||
# import ansible_collections.barbu_it.ansible_kheops.plugins.plugin_utils.common as kheops_utils
|
||||
# AnsibleKheops = kheops_utils.AnsibleKheops
|
||||
# kheops = AnsibleKheops()
|
||||
|
||||
# return None
|
||||
|
||||
# assert isinstance(terms, list), f"Expected a list, got: {terms}"
|
||||
|
||||
# # Parse arguments
|
||||
# kwargs = kwargs or {}
|
||||
# #enable_jinja = kwargs.pop('enable_jinja', True)
|
||||
# #jinja2_native = kwargs.pop('jinja2_native', False)
|
||||
|
||||
# #enable_jinja = kwargs.pop('enable_jinja', True)
|
||||
# #jinja2_native = kwargs.pop('jinja2_native', False)
|
||||
|
||||
# self.namespace = self.get_option('query_namespace')
|
||||
# self.configuration = self.get_option('configuration')
|
||||
|
||||
# # Instanciate Kheops client
|
||||
# kheops = Kheops(config=self.configuration, namespace=self.namespace)
|
||||
|
||||
# for term in terms:
|
||||
# print ("Lookup for key: ", term)
|
||||
|
||||
|
||||
# print ("WIIIIIIIIPPPPPPPPPPPPPPPPP")
|
||||
|
||||
# # Start jinja template engine
|
||||
# if enable_jinja:
|
||||
# if USE_JINJA2_NATIVE and not jinja2_native:
|
||||
# templar = self._templar.copy_with_new_env(environment_class=AnsibleEnvironment)
|
||||
# else:
|
||||
# templar = self._templar
|
||||
|
||||
# # Look for each terms
|
||||
# ret = []
|
||||
# for term in terms:
|
||||
# lookuppath = term.split('/')
|
||||
# key = lookuppath.pop()
|
||||
# namespace = lookuppath
|
||||
|
||||
# if not namespace:
|
||||
# raise AnsibleError("No namespace given for lookup of key %s" % key)
|
||||
|
||||
# response = kheops.lookup(key=key, namespace=namespace, variables=variables, kwargs=kwargs)
|
||||
|
||||
# # Render data with Jinja
|
||||
# if enable_jinja:
|
||||
# # Build a copy of environment vars
|
||||
# vars = deepcopy(variables)
|
||||
|
||||
# # Render data with Templar
|
||||
# with templar.set_temporary_context(available_variables=vars):
|
||||
# res = templar.template(response['payload'], preserve_trailing_newlines=True,
|
||||
# convert_data=False, escape_backslashes=False)
|
||||
|
||||
# if USE_JINJA2_NATIVE and not jinja2_native:
|
||||
# # jinja2_native is true globally but off for the lookup, we need this text
|
||||
# # not to be processed by literal_eval anywhere in Ansible
|
||||
# res = NativeJinjaText(res)
|
||||
# else:
|
||||
# res = response['payload']
|
||||
|
||||
# # Append response to response array
|
||||
# ret.append(res)
|
||||
|
||||
# return ret
|
||||
|
||||
424
plugins/plugin_utils/common.py
Normal file
424
plugins/plugin_utils/common.py
Normal file
@ -0,0 +1,424 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
|
||||
|
||||
DOCUMENTATION_OPTION_FRAGMENT = '''
|
||||
|
||||
# Plugin configuration
|
||||
# ==========================
|
||||
config:
|
||||
description:
|
||||
- Path to Kheops configuration yaml file
|
||||
- Can be useful to reuse and merge existing configurations
|
||||
- All settings of the target files can be overriden from this file
|
||||
- Ignored if blank or Null
|
||||
default: Null
|
||||
env:
|
||||
- name: ANSIBLE_KHEOPS_CONFIG
|
||||
|
||||
mode:
|
||||
description:
|
||||
- Choose `client` to use a remote Khéops instance
|
||||
- Choose `instance` to use a Khéops directly
|
||||
default: 'instance'
|
||||
choices: ['instance', 'client']
|
||||
env:
|
||||
- name: ANSIBLE_KHEOPS_MODE
|
||||
|
||||
# default_namespace:
|
||||
# description:
|
||||
# - The Kheops namespace to use
|
||||
# default: 'default'
|
||||
# env:
|
||||
# - name: ANSIBLE_KHEOPS_DEFAULT_NAMESPACE
|
||||
|
||||
# default_scope:
|
||||
# description:
|
||||
# - A list of default variables to inject in scope.
|
||||
# default: 'default'
|
||||
# env:
|
||||
# - name: ANSIBLE_KHEOPS_DEFAULT_SCOPE
|
||||
|
||||
|
||||
# Instance configuration (Direct)
|
||||
# ==========================
|
||||
# Instance configuration
|
||||
instance_config:
|
||||
description:
|
||||
- The Kheops configuration file to use.
|
||||
default: 'site/kheops.yml'
|
||||
env:
|
||||
- name: ANSIBLE_KHEOPS_INSTANCE_CONFIG
|
||||
instance_namespace:
|
||||
description:
|
||||
- The Kheops configuration file to use.
|
||||
default: 'default'
|
||||
env:
|
||||
- name: ANSIBLE_KHEOPS_INSTANCE_NAMESPACE
|
||||
instance_log_level:
|
||||
description:
|
||||
- Khéops logging level
|
||||
choices: ['DEBUG', 'INFO', 'WARNING', 'ERROR']
|
||||
default: 'WARNING'
|
||||
env:
|
||||
- name: ANSIBLE_KHEOPS_LOG_LEVEL
|
||||
|
||||
|
||||
# Instance configuration (Client)
|
||||
# ==========================
|
||||
# turfu # Client configuration
|
||||
# turfu client_token:
|
||||
# turfu description:
|
||||
# turfu - The Kheops token to use to authenticate against Kheops server.
|
||||
# turfu default: ''
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_TOKEN
|
||||
# turfu client_host:
|
||||
# turfu description:
|
||||
# turfu - Hostname of the Kheops Server.
|
||||
# turfu default: '127.0.0.1'
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_HOST
|
||||
# turfu client_port:
|
||||
# turfu description:
|
||||
# turfu - Kheops port to connect to.
|
||||
# turfu default: '9843'
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_PORT
|
||||
# turfu client_protocol:
|
||||
# turfu description:
|
||||
# turfu - The URL protocol to use.
|
||||
# turfu default: 'http'
|
||||
# turfu choices: ['http', 'https']
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_PROTOCOL
|
||||
# turfu client_validate_certs:
|
||||
# turfu description:
|
||||
# turfu - Whether or not to verify the TLS certificates of the Kheops server.
|
||||
# turfu type: boolean
|
||||
# turfu default: False
|
||||
# turfu env:
|
||||
# turfu - name: ANSIBLE_KHEOPS_VALIDATE_CERTS
|
||||
|
||||
|
||||
# Query configuration
|
||||
# ==========================
|
||||
namespace:
|
||||
description:
|
||||
- The Kheops namespace to use
|
||||
default: 'default'
|
||||
env:
|
||||
- name: ANSIBLE_KHEOPS_DEFAULT_NAMESPACE
|
||||
scope:
|
||||
description:
|
||||
- A hash containing the scope to use for the request, the values will be resolved as Ansible facts.
|
||||
- Use a dot notation to dig deeper into nested hash facts.
|
||||
default:
|
||||
node: inventory_hostname
|
||||
groups: group_names
|
||||
|
||||
keys:
|
||||
description:
|
||||
- A list of keys to lookup
|
||||
default: Null
|
||||
|
||||
|
||||
# Uneeded # Misc
|
||||
# Uneeded version:
|
||||
# Uneeded description:
|
||||
# Uneeded - Kheops API version to use.
|
||||
# Uneeded default: 1
|
||||
# Uneeded choices: [1]
|
||||
# Uneeded env:
|
||||
# Uneeded - name: ANSIBLE_KHEOPS_VERSION
|
||||
# Uneeded cache:
|
||||
# Uneeded description:
|
||||
# Uneeded - Enable Kheops inventory cache.
|
||||
# Uneeded default: false
|
||||
# Uneeded type: boolean
|
||||
# Uneeded env:
|
||||
# Uneeded - name: ANSIBLE_KHEOPS_CACHE
|
||||
# Uneeded policy:
|
||||
# Uneeded description:
|
||||
# Uneeded - Kheops policy to use for the lookups.
|
||||
# Uneeded default: 'default'
|
||||
|
||||
'''
|
||||
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Union
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
# Devel mode
|
||||
sys.path.append("/home/jez/prj/bell/training/tiger-ansible/ext/kheops")
|
||||
|
||||
from kheops.app import Kheops
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.template import generate_ansible_template_vars, AnsibleEnvironment, USE_JINJA2_NATIVE
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
KEY_NS_SEP = '/'
|
||||
if USE_JINJA2_NATIVE:
|
||||
from ansible.utils.native_jinja import NativeJinjaText
|
||||
|
||||
|
||||
@dataclass
|
||||
class Key:
|
||||
key: str
|
||||
remap: Union[str, type(None)]
|
||||
namespace: Union[type(None), str]
|
||||
|
||||
def show(self):
|
||||
ret = self.key
|
||||
if self.namespace is not None:
|
||||
ret = f"{self.namespace}{KEY_NS_SEP}{ret}"
|
||||
return ret
|
||||
|
||||
|
||||
class AnsibleKheops():
|
||||
|
||||
|
||||
def __init__(self, configs=None, display=None):
|
||||
|
||||
self.configs = configs or []
|
||||
self.display = display or print
|
||||
|
||||
config = self.get_config()
|
||||
# print ("CURRENT CONFIG vv")
|
||||
# pprint (config)
|
||||
# print ("CURRENT CONFIG ^^")
|
||||
|
||||
self.init_templar()
|
||||
|
||||
# Instanciate Kheops
|
||||
if config["mode"] == 'instance':
|
||||
|
||||
# Confiogure logging
|
||||
logger = logging.getLogger('kheops')
|
||||
logger.setLevel(config["instance_log_level"])
|
||||
|
||||
# See for logging: https://medium.com/opsops/debugging-requests-1989797736cc
|
||||
class ListLoggerHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
msg = self.format(record)
|
||||
|
||||
main_logger = logging.getLogger()
|
||||
main_logger.addHandler(ListLoggerHandler())
|
||||
main_logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Start instance
|
||||
self.kheops = Kheops(
|
||||
config=config['instance_config'],
|
||||
namespace=config['instance_namespace']
|
||||
)
|
||||
elif config["mode"] == 'client':
|
||||
raise AnsibleError("Kheops client mode is not implemented")
|
||||
|
||||
self.config = config
|
||||
print ("Kheops instance is OK")
|
||||
|
||||
|
||||
def get_config(self):
|
||||
items = [
|
||||
# We exclude 'config'
|
||||
'mode',
|
||||
'instance_config', 'instance_namespace', 'instance_log_level',
|
||||
'namespace', 'scope', 'keys']
|
||||
|
||||
|
||||
# Extract default value from doc
|
||||
data_doc = yaml.safe_load(DOCUMENTATION_OPTION_FRAGMENT)
|
||||
default_config = {key: value.get("default", None) for key, value in data_doc.items()}
|
||||
|
||||
|
||||
merged_configs = {}
|
||||
for config in self.configs:
|
||||
|
||||
conf_data = None
|
||||
if isinstance(config, str):
|
||||
#print ("Read file", config)
|
||||
if os.path.isfile(config):
|
||||
data = open(config, "r")
|
||||
conf_data = yaml.safe_load(data)
|
||||
else:
|
||||
raise AnsibleError("Unable to find configuration file %s" % config_file)
|
||||
|
||||
elif isinstance(config, dict):
|
||||
#print ("Read Config", config)
|
||||
conf_data = config
|
||||
else:
|
||||
assert False, f"Bad config for: {config}"
|
||||
|
||||
assert isinstance(conf_data, dict), f"Bug with conf_data: {config_data}"
|
||||
if isinstance(conf_data, dict):
|
||||
merged_configs.update(conf_data)
|
||||
|
||||
|
||||
# Get environment config
|
||||
env_config = {}
|
||||
for item in items:
|
||||
envvar = "ANSIBLE_KHEOPS_" + item.upper()
|
||||
try:
|
||||
env_config[item] = os.environ[envvar]
|
||||
except KeyError as err:
|
||||
pass
|
||||
|
||||
# Merge results
|
||||
combined_config = {}
|
||||
combined_config.update(default_config)
|
||||
combined_config.update(env_config)
|
||||
combined_config.update(merged_configs)
|
||||
|
||||
|
||||
# out = {
|
||||
# "0_default": default_config,
|
||||
# "1_env": env_config,
|
||||
# "2_common": merged_configs,
|
||||
# }
|
||||
# print ('=' * 20)
|
||||
# pprint (out)
|
||||
# print ('=' * 20)
|
||||
# pprint (combined_config)
|
||||
# print ('=' * 20)
|
||||
|
||||
|
||||
return combined_config
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def parse_string(item, default_namespace):
|
||||
key = None
|
||||
remap = key
|
||||
namespace = default_namespace
|
||||
|
||||
if isinstance(item, str):
|
||||
|
||||
parts = item.split('/', 3)
|
||||
#print (len(parts))
|
||||
|
||||
if len(parts) > 0:
|
||||
key = parts[0]
|
||||
if len(parts) > 1:
|
||||
namespace = parts[0]
|
||||
key = parts[1]
|
||||
if len(parts) > 2:
|
||||
remap = parts[2]
|
||||
|
||||
elif isinstance(item, dict):
|
||||
key = item.get('key')
|
||||
remap = item.get('remap', key)
|
||||
namespace = item.get('namespace', namespace)
|
||||
|
||||
return Key(key=key,
|
||||
remap=remap,
|
||||
namespace=namespace)
|
||||
|
||||
|
||||
@classmethod
|
||||
def parse_keys(self, data, namespace):
|
||||
|
||||
keys = []
|
||||
|
||||
if isinstance(data, str):
|
||||
keys.append(self.parse_string(data, namespace))
|
||||
|
||||
elif isinstance(data, list):
|
||||
for item in data:
|
||||
keys.append(self.parse_string(item, namespace))
|
||||
elif isinstance(data, dict):
|
||||
for key, value in data.items():
|
||||
item = key
|
||||
if value:
|
||||
assert isinstance(value, str), f"Need a string, got: {value}"
|
||||
item = f"{key}{KEY_NS_SEP}{value}"
|
||||
|
||||
keys.append(self.parse_string(item, namespace))
|
||||
|
||||
else:
|
||||
raise AnsibleError("Unable to process Kheops keys: %s" % keys)
|
||||
|
||||
return keys
|
||||
|
||||
|
||||
def get_scope_from_host_inventory(self, host_vars, scope=None):
|
||||
scope = scope or self.config['scope']
|
||||
ret = {}
|
||||
for key, val in scope.items():
|
||||
# Tofix should this fail silently ?
|
||||
ret[key] = host_vars.get(val, None)
|
||||
|
||||
return ret
|
||||
|
||||
def init_templar(self, enable_jinja=True, jinja2_native=False):
|
||||
pass
|
||||
|
||||
def get_scope_from_jinja(self, host_vars, templar, scope=None, jinja2_native=False):
|
||||
scope = scope or self.config['scope']
|
||||
ret = {}
|
||||
|
||||
if USE_JINJA2_NATIVE and not jinja2_native:
|
||||
_templar = templar.copy_with_new_env(environment_class=AnsibleEnvironment)
|
||||
else:
|
||||
_templar = templar
|
||||
|
||||
_vars = deepcopy(host_vars)
|
||||
with _templar.set_temporary_context(available_variables=_vars):
|
||||
res = _templar.template(scope, preserve_trailing_newlines=True,
|
||||
convert_data=False, escape_backslashes=False)
|
||||
if USE_JINJA2_NATIVE and not jinja2_native:
|
||||
# jinja2_native is true globally but off for the lookup, we need this text
|
||||
# not to be processed by literal_eval anywhere in Ansible
|
||||
res = NativeJinjaText(res)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def lookup(self, keys, namespace=None, scope=None, kwargs=None):
|
||||
|
||||
namespace = namespace or self.config['namespace']
|
||||
scope = scope or self.config['scope']
|
||||
keys = keys or self.config['keys']
|
||||
keys_config = self.parse_keys(keys, namespace)
|
||||
keys = [ i.show() for i in keys_config ]
|
||||
|
||||
# self.base.display.v(f"Kheops keys: {keys}")
|
||||
# self.base.display.vv(f"Kheops scope: {scope}")
|
||||
|
||||
|
||||
print ("LOOKUP QUERY: ", keys, scope)
|
||||
#try:
|
||||
ret = self.kheops.lookup(
|
||||
keys=keys,
|
||||
scope=scope,
|
||||
#trace=True,
|
||||
#explain=True,
|
||||
)
|
||||
#except Exception as err:
|
||||
# assert False, f"{err.__traceback__}"
|
||||
# assert False, f"{dir(err)}"
|
||||
# raise AnsibleError('Something happened, this was original exception: %s' % to_native(err))
|
||||
# assert False, f"MY ERRRROR {err}"
|
||||
|
||||
# self.display.v(err)
|
||||
|
||||
print ("RESULT: ", ret)
|
||||
# Remap output
|
||||
#for key in keys_config:
|
||||
# if key.remap != key.key:
|
||||
# ret[key.remap] = ret[key.key]
|
||||
# del ret[key.key]
|
||||
|
||||
return ret or {}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user