A collection of classes and commands for automated command line scripting using Python.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

374 lines
9.4 KiB

import logging
from commonkit import smart_cast
from commonkit.shell import EXIT
import logging
import os
from ..lib.loaders import load_variables, INILoader, YMLLoader
from ..variables import LOGGER_NAME, PATH_TO_SCRIPT_TEASE
log = logging.getLogger(LOGGER_NAME)
def options_from_cli(options):
_options = dict()
for token in options:
try:
key, value = token.split(":")
_options[key] = smart_cast(value)
except ValueError:
_options[token] = True
return _options
def loader(path, context=None, options=None, template_locations=None):
# The path may have been given as a file name (steps.ini), path, or an inventory name.
input_locations = [
path,
os.path.join(PATH_TO_SCRIPT_TEASE, "data", "inventory", path, "steps.ini"),
os.path.join(PATH_TO_SCRIPT_TEASE, "data", "inventory", path, "steps.yml"),
]
path = None
for location in input_locations:
if os.path.exists(location):
path = location
break
if path is None:
log.warning("Path does not exist: %s" % path)
return None
# Initialize the loader.
if path.endswith(".ini"):
_loader = INILoader(
path,
context=context,
locations=template_locations,
**options
)
elif path.endswith(".yml"):
_loader = YMLLoader(
path,
context=context,
locations=template_locations,
**options
)
else:
log.error("Unsupported file format: %s" % path)
return None
# Load the commands.
if not _loader.load():
log.error("Failed to load the input file: %s" % path)
return None
return _loader
def subcommands(subparsers):
"""Initialize sub-commands.
:param subparsers: The subparsers instance from argparse.
"""
sub = SubCommands(subparsers)
sub.docs()
sub.inventory()
sub.script()
def variables_from_cli(context, variables):
for token in variables:
try:
key, value = token.split(":")
context.add(key, smart_cast(value))
except ValueError:
context.add(token, True)
class SubCommands(object):
def __init__(self, subparsers):
self.subparsers = subparsers
def docs(self):
sub = self.subparsers.add_parser(
"docs",
help="Output documentation instead of code."
)
sub.add_argument(
"-o=",
"--output-format=",
choices=["html", "md", "plain", "rst"],
default="md",
dest="output_format",
help="The output format; HTML, Markdown, plain text, or ReStructuredText."
)
self._add_script_options(sub)
self._add_common_options(sub)
def inventory(self):
sub = self.subparsers.add_parser(
"inventory",
aliases=["inv"],
help="Copy an inventory item to a local directory."
)
sub.add_argument(
"name",
help="The name of the inventory item. Use ? to list available items."
)
sub.add_argument(
"-P=",
"--path=",
dest="to_path",
help="The path to where the item should be copied. Defaults to the current working directory."
)
self._add_common_options(sub)
def script(self):
sub = self.subparsers.add_parser(
"script",
help="Output the commands."
)
sub.add_argument(
"-c",
"--color",
action="store_true",
dest="color_enabled",
help="Enable code highlighting for terminal output."
)
sub.add_argument(
"-s",
"--shebang",
action="store_true",
dest="include_shebang",
help="Add the shebang to the beginning of the output."
)
self._add_script_options(sub)
self._add_common_options(sub)
def _add_common_options(self, sub):
sub.add_argument(
"-D",
"--debug",
action="store_true",
dest="debug_enabled",
help="Enable debug mode. Produces extra output."
)
sub.add_argument(
"-p",
action="store_true",
dest="preview_enabled",
help="Preview mode."
)
def _add_script_options(self, sub):
sub.add_argument(
"-C=",
"--context=",
action="append",
dest="variables",
help="Context variables for use in pre-parsing the config and templates. In the form of: name:value"
)
sub.add_argument(
"-i=",
"--input-file=",
default="steps.ini",
dest="steps_file",
help="The path to the configuration file."
)
# sub.add_argument(
# "-f=",
# "--filter=",
# action="append",
# dest="filters",
# help="Filter the commands in the form of: attribute:value"
# )
# Capture filters.
# if args.filters:
# filters = dict()
# for token in args.filters:
# key, value = token.split(":")
# if key not in filters:
# filters[key] = list()
#
# filters[key].append(value)
sub.add_argument(
"-O=",
"--option=",
action="append",
dest="options",
help="Common command options in the form of: name:value"
)
sub.add_argument(
"-P=",
"--profile=",
choices=["centos", "ubuntu"],
default="ubuntu",
dest="profile",
help="The OS profile to use."
)
sub.add_argument(
"-T=",
"--template-path=",
action="append",
dest="template_locations",
help="The location of template files that may be used with the template command."
)
sub.add_argument(
"-w=",
"--write=",
dest="output_file",
help="Write the output to disk."
)
sub.add_argument(
"-V=",
"--variables-file=",
dest="variables_file",
help="Load variables from a file."
)
# # Imports
#
# from commonkit import smart_cast
# from configparser import ConfigParser
# import logging
# import os
# from ..constants import LOGGER_NAME
#
# log = logging.getLogger(LOGGER_NAME)
#
# # Exports
#
# __all__ = (
# "context_from_cli",
# "filters_from_cli",
# "options_from_cli",
# "variables_from_file",
# )
#
# # Functions
#
#
# def context_from_cli(variables):
# """Takes a list of variables given in the form of ``name:value`` and converts them to a dictionary.
#
# :param variables: A list of strings of ``name:value`` pairs.
# :type variables: list[str]
#
# :rtype: dict
#
# The ``value`` of the pair passes through "smart casting" to convert it to the appropriate Python data type.
#
# """
# context = dict()
# for i in variables:
# key, value = i.split(":")
# context[key] = smart_cast(value)
#
# return context
#
#
# def filters_from_cli(filters):
# """Takes a list of filters given in the form of ``name:value`` and converts them to a dictionary.
#
# :param filters: A list of strings of ``attribute:value`` pairs.
# :type filters: list[str]
#
# :rtype: dict
#
# """
# _filters = dict()
# for i in filters:
# key, value = i.split(":")
# if key not in filters:
# _filters[key] = list()
#
# _filters[key].append(value)
#
# return _filters
#
#
# def options_from_cli(options):
# """Takes a list of variables given in the form of ``name:value`` and converts them to a dictionary.
#
# :param options: A list of strings of ``name:value`` pairs.
# :type options: list[str]
#
# :rtype: dict
#
# The ``value`` of the pair passes through "smart casting" to convert it to the appropriate Python data type.
#
# """
# _options = dict()
# for i in options:
# key, value = i.split(":")
# _options[key] = smart_cast(value)
#
# return _options
#
#
# def variables_from_file(path):
# """Loads variables from a given INI file.
#
# :param path: The path to the INI file.
# :type path: str
#
# :rtype: dict | None
#
# The resulting dictionary flattens the sections and values. For example:
#
# .. code-block:: ini
#
# [copyright]
# name = ACME, Inc.
# year = 2020
#
# [domain]
# name = example.com
# tld = example_com
#
# The dictionary would contain:
#
# .. code-block:: python
#
# {
# 'copyright_name': "ACME, Inc.",
# 'copyright_year': 2020,
# 'domain_name': "example.com",
# 'domain_tld': "example_com",
# }
#
# """
# if not os.path.exists(path):
# log.warning("Variables file does not exist: %s" % path)
# return None
#
# ini = ConfigParser()
# ini.read(path)
#
# variables = dict()
# for section in ini.sections():
# for key, value in ini.items(section):
# key = "%s_%s" % (section, key)
# variables[key] = smart_cast(value)
#
# return variables