mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
9773a1f289
* Add a Singleton class, use it with Display * update six import * Move remaining failes to display singleton * Fix rebase issues * Singleton improvements * Add code-smell for 'from __main__ import display'. ci_complete * s/self/cls/g * Add docs for no-main-display * Address linting issues * Add changelog fragment. ci_complete * Implement reentrant lock for class instantiation in Singleton * Add Display singleton porting guide
170 lines
6.5 KiB
Python
170 lines
6.5 KiB
Python
# Copyright: (c) 2017, Ansible Project
|
|
# 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
|
|
|
|
import os
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import yaml
|
|
|
|
from ansible.cli import CLI
|
|
from ansible.config.manager import ConfigManager, Setting, find_ini_config_file
|
|
from ansible.errors import AnsibleError, AnsibleOptionsError
|
|
from ansible.module_utils._text import to_native, to_text
|
|
from ansible.parsing.yaml.dumper import AnsibleDumper
|
|
from ansible.utils.color import stringc
|
|
from ansible.utils.display import Display
|
|
from ansible.utils.path import unfrackpath
|
|
|
|
display = Display()
|
|
|
|
|
|
class ConfigCLI(CLI):
|
|
""" Config command line class """
|
|
|
|
VALID_ACTIONS = ("view", "dump", "list") # TODO: edit, update, search
|
|
|
|
def __init__(self, args, callback=None):
|
|
|
|
self.config_file = None
|
|
self.config = None
|
|
super(ConfigCLI, self).__init__(args, callback)
|
|
|
|
def parse(self):
|
|
|
|
self.parser = CLI.base_parser(
|
|
usage="usage: %%prog [%s] [--help] [options] [ansible.cfg]" % "|".join(self.VALID_ACTIONS),
|
|
epilog="\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0]),
|
|
desc="View, edit, and manage ansible configuration.",
|
|
)
|
|
self.parser.add_option('-c', '--config', dest='config_file', help="path to configuration file, defaults to first file found in precedence.")
|
|
|
|
self.set_action()
|
|
|
|
# options specific to self.actions
|
|
if self.action == "list":
|
|
self.parser.set_usage("usage: %prog list [options] ")
|
|
if self.action == "dump":
|
|
self.parser.add_option('--only-changed', dest='only_changed', action='store_true',
|
|
help="Only show configurations that have changed from the default")
|
|
elif self.action == "update":
|
|
self.parser.add_option('-s', '--setting', dest='setting', help="config setting, the section defaults to 'defaults'")
|
|
self.parser.set_usage("usage: %prog update [options] [-c ansible.cfg] -s '[section.]setting=value'")
|
|
elif self.action == "search":
|
|
self.parser.set_usage("usage: %prog update [options] [-c ansible.cfg] <search term>")
|
|
|
|
self.options, self.args = self.parser.parse_args()
|
|
display.verbosity = self.options.verbosity
|
|
|
|
def run(self):
|
|
|
|
super(ConfigCLI, self).run()
|
|
|
|
if self.options.config_file:
|
|
self.config_file = unfrackpath(self.options.config_file, follow=False)
|
|
self.config = ConfigManager(self.config_file)
|
|
else:
|
|
self.config = ConfigManager()
|
|
self.config_file = find_ini_config_file()
|
|
|
|
if self.config_file:
|
|
try:
|
|
if not os.path.exists(self.config_file):
|
|
raise AnsibleOptionsError("%s does not exist or is not accessible" % (self.config_file))
|
|
elif not os.path.isfile(self.config_file):
|
|
raise AnsibleOptionsError("%s is not a valid file" % (self.config_file))
|
|
|
|
os.environ['ANSIBLE_CONFIG'] = to_native(self.config_file)
|
|
except:
|
|
if self.action in ['view']:
|
|
raise
|
|
elif self.action in ['edit', 'update']:
|
|
display.warning("File does not exist, used empty file: %s" % self.config_file)
|
|
|
|
elif self.action == 'view':
|
|
raise AnsibleError('Invalid or no config file was supplied')
|
|
|
|
self.execute()
|
|
|
|
def execute_update(self):
|
|
'''
|
|
Updates a single setting in the specified ansible.cfg
|
|
'''
|
|
raise AnsibleError("Option not implemented yet")
|
|
|
|
# pylint: disable=unreachable
|
|
if self.options.setting is None:
|
|
raise AnsibleOptionsError("update option requries a setting to update")
|
|
|
|
(entry, value) = self.options.setting.split('=')
|
|
if '.' in entry:
|
|
(section, option) = entry.split('.')
|
|
else:
|
|
section = 'defaults'
|
|
option = entry
|
|
subprocess.call([
|
|
'ansible',
|
|
'-m', 'ini_file',
|
|
'localhost',
|
|
'-c', 'local',
|
|
'-a', '"dest=%s section=%s option=%s value=%s backup=yes"' % (self.config_file, section, option, value)
|
|
])
|
|
|
|
def execute_view(self):
|
|
'''
|
|
Displays the current config file
|
|
'''
|
|
try:
|
|
with open(self.config_file, 'rb') as f:
|
|
self.pager(to_text(f.read(), errors='surrogate_or_strict'))
|
|
except Exception as e:
|
|
raise AnsibleError("Failed to open config file: %s" % to_native(e))
|
|
|
|
def execute_edit(self):
|
|
'''
|
|
Opens ansible.cfg in the default EDITOR
|
|
'''
|
|
raise AnsibleError("Option not implemented yet")
|
|
|
|
# pylint: disable=unreachable
|
|
try:
|
|
editor = shlex.split(os.environ.get('EDITOR', 'vi'))
|
|
editor.append(self.config_file)
|
|
subprocess.call(editor)
|
|
except Exception as e:
|
|
raise AnsibleError("Failed to open editor: %s" % to_native(e))
|
|
|
|
def execute_list(self):
|
|
'''
|
|
list all current configs reading lib/constants.py and shows env and config file setting names
|
|
'''
|
|
self.pager(to_text(yaml.dump(self.config.get_configuration_definitions(), Dumper=AnsibleDumper), errors='surrogate_or_strict'))
|
|
|
|
def execute_dump(self):
|
|
'''
|
|
Shows the current settings, merges ansible.cfg if specified
|
|
'''
|
|
# FIXME: deal with plugins, not just base config
|
|
text = []
|
|
defaults = self.config.get_configuration_definitions().copy()
|
|
for setting in self.config.data.get_settings():
|
|
if setting.name in defaults:
|
|
defaults[setting.name] = setting
|
|
|
|
for setting in sorted(defaults):
|
|
if isinstance(defaults[setting], Setting):
|
|
if defaults[setting].origin == 'default':
|
|
color = 'green'
|
|
else:
|
|
color = 'yellow'
|
|
msg = "%s(%s) = %s" % (setting, defaults[setting].origin, defaults[setting].value)
|
|
else:
|
|
color = 'green'
|
|
msg = "%s(%s) = %s" % (setting, 'default', defaults[setting].get('default'))
|
|
if not self.options.only_changed or color == 'yellow':
|
|
text.append(stringc(msg, color))
|
|
|
|
self.pager(to_text('\n'.join(text), errors='surrogate_or_strict'))
|