blob: b3b0f5afcaa2195cf5abd150eacc94c0c35a2e38 [file] [log] [blame]
# Copyright 2013 - 2017 Mirantis, Inc.
#
# 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.
from __future__ import print_function
import argparse
import sys
import yaml
from reclass_tools import walk_models
class Shell(object):
def __init__(self, args):
self.args = args
self.params = self.get_params()
def str2bool(self, v):
return v.lower() in ("true")
def execute(self):
command_name = 'do_{}'.format(self.params.command.replace('-', '_'))
command_method = getattr(self, command_name)
command_method()
def do_get_key(self):
walk_models.remove_reclass_parameter(
self.params.path,
self.params.key_name,
verbose=self.params.verbose,
pretend=True)
def do_add_key(self):
# Try convert to digits
if self.params.str:
add_val = str(self.params.add_value)
else:
try:
add_val = int(self.params.add_value)
except ValueError:
# Try convert to float
try:
add_val = float(self.params.add_value)
except ValueError:
try:
# Try convert to list
if type(eval(self.params.add_value)) == list:
add_val = eval(self.params.add_value)
except (SyntaxError, NameError):
add_val = self.params.add_value
walk_models.add_reclass_parameter(
self.params.path,
self.params.key_name,
add_val,
verbose=self.params.verbose,
merge=self.params.merge)
def do_add_bool_key(self):
add_val = self.str2bool(self.params.add_value)
walk_models.add_reclass_parameter(
self.params.path,
self.params.key_name,
add_val,
verbose=self.params.verbose,
merge=self.params.merge)
def do_del_key(self):
walk_models.remove_reclass_parameter(
self.params.path,
self.params.key_name,
verbose=self.params.verbose,
pretend=False)
def do_list_params(self):
results = walk_models.get_all_reclass_params(
self.params.path,
verbose=self.params.verbose)
print(yaml.dump(results, width=255))
def do_list_domains(self):
try:
from reclass_tools import reclass_models
except ImportError:
sys.exit("Please run this tool on the salt-master node "
"with installed 'reclass'")
inventory = reclass_models.inventory_list()
reclass_storage = reclass_models.reclass_storage(inventory=inventory)
print('\n'.join(sorted(reclass_storage.keys())))
def do_list_nodes(self):
try:
from reclass_tools import reclass_models
except ImportError:
sys.exit("Please run this tool on the salt-master node "
"with installed 'reclass'")
inventory = reclass_models.inventory_list(domain=self.params.domain)
vcp_nodes = reclass_models.vcp_list(domain=self.params.domain,
inventory=inventory)
vcp_node_names = ['{0}.{1}'.format(name, domain)
for name, domain in vcp_nodes]
if self.params.vcp_only:
print('\n'.join(sorted(vcp_node_names)))
elif self.params.non_vcp_only:
print('\n'.join(sorted((node_name for node_name in inventory.keys()
if node_name not in vcp_node_names))))
else:
print('\n'.join(sorted(inventory.keys())))
def do_trace_key(self):
try:
from reclass_tools import reclass_models
except ImportError:
sys.exit("Please run this tool on the salt-master node "
"with installed 'reclass'")
reclass_models.trace_key(key=self.params.key_name,
domain=self.params.domain,
node=self.params.node)
def do_show_context(self):
try:
from reclass_tools import create_inventory
except ImportError:
sys.exit("Please run this tool on the salt-master node "
"with installed 'reclass'")
current_underlay_context = create_inventory.create_inventory_context(
domain=self.params.domain, keys=self.params.keys)
print(yaml.dump(current_underlay_context, default_flow_style=False,
width=255))
def do_render(self):
from reclass_tools import render
if not self.params.template_dir or not self.params.output_dir \
or not self.params.contexts:
sys.exit("Missing parameters, see: reclass-tools render -h")
render.render_dir(template_dir=self.params.template_dir,
output_dir=self.params.output_dir,
contexts=self.params.contexts,
env_name=self.params.env_name)
def do_merge_context(self):
content = yaml.load(open(self.params.yaml).read(), yaml.SafeLoader)
walk_models.merge_dict_to_reclass(
self.params.path,
content)
def get_params(self):
verbose_parser = argparse.ArgumentParser(add_help=False)
verbose_parser.add_argument('--verbose', dest='verbose',
action='store_const', const=True,
help='Show verbosed output', default=False)
merge_parser = argparse.ArgumentParser(add_help=False)
merge_parser.add_argument('--merge', dest='merge',
action='store_const', const=True,
help='Merge value to list or dict',
default=False)
as_str_parser = argparse.ArgumentParser(add_help=False)
as_str_parser.add_argument('--str', dest='str',
action='store_const', const=True,
help='Use value as plain string',
default=False)
key_parser = argparse.ArgumentParser(add_help=False)
key_parser_help = (
'Key name to find in reclass model files, for example:'
' parameters.linux.network.interface')
key_parser.add_argument('key_name', help=key_parser_help, default=None)
keys_parser = argparse.ArgumentParser(add_help=False)
keys_parser.add_argument(
'keys',
help='Key names to find in reclass model files', nargs='*')
add_value_parser = argparse.ArgumentParser(add_help=False)
add_value_parser.add_argument(
'add_value',
help=('Value to add to the reclass model files, can be in the '
'inline YAML format'))
yaml_parser = argparse.ArgumentParser(add_help=False)
yaml_parser.add_argument(
'yaml',
help='Path to YAML file with extra context')
path_parser = argparse.ArgumentParser(add_help=False)
path_parser.add_argument(
'path',
help='Path to search for *.yml files.', nargs='+')
domain_parser = argparse.ArgumentParser(add_help=False)
domain_parser.add_argument(
'--domain', '-d', dest='domain',
help=('Show only the nodes which names are ended with the '
'specified domain, for example: example.local'))
node_parser = argparse.ArgumentParser(add_help=False)
node_parser.add_argument(
'--node', '-n', dest='node',
help=('Show only the specified node, for example: '
'ctl01.example.local'))
env_name_parser = argparse.ArgumentParser(add_help=False)
env_name_parser.add_argument(
'--env-name', '-e', dest='env_name',
help=("Name of the 'environment' to create or use"),
default=None)
vcp_only_parser = argparse.ArgumentParser(add_help=False)
vcp_only_parser.add_argument(
'--vcp-only', dest='vcp_only',
action='store_const', const=True,
help=('Show only VCP nodes (present in '
'parameters.salt.control.cluster.internal.node)'),
default=False)
non_vcp_only_parser = argparse.ArgumentParser(add_help=False)
non_vcp_only_parser.add_argument(
'--non-vcp-only', dest='non_vcp_only',
action='store_const', const=True, default=False,
help=('Show only non-VCP nodes (absent in '
'parameters.salt.control.cluster.internal.node)'))
render_parser = argparse.ArgumentParser(add_help=False)
render_parser.add_argument(
'--template-dir', '-t', dest='template_dir',
help=('Coockiecutter-based template directory'))
render_parser.add_argument(
'--output-dir', '-o', dest='output_dir',
help=('Path to the directory where the rendered '
'template will be placed'))
render_parser.add_argument(
'--context', '-c', dest='contexts', action='append',
help=('YAML/JSON files with context data to render '
'the template'))
parser = argparse.ArgumentParser(
description="Manage virtual environments. "
"For additional help, use with -h/--help option")
subparsers = parser.add_subparsers(title="Operation commands",
help='available commands',
dest='command')
subparsers.add_parser('get-key',
parents=[key_parser, path_parser,
verbose_parser],
help="Find a key in YAMLs found in <path>",
description=("Get a key collected from "
"different YAMLs"))
subparsers.add_parser('add-key',
parents=[key_parser, add_value_parser,
path_parser, verbose_parser,
merge_parser, as_str_parser],
help="Add a key in YAMLs found in <path>",
description=("Add a key to "
"different YAMLs"))
subparsers.add_parser('add-bool-key',
parents=[key_parser, add_value_parser,
path_parser, verbose_parser,
merge_parser],
help="Add a bool key in YAMLs found in <path>. "
"True/False string values are converted to bool",
description=("Add a bool key collected to "
"different YAMLs"))
subparsers.add_parser('del-key',
parents=[key_parser, path_parser,
verbose_parser],
help="Delete a key from YAMLs found in <path>",
description="Delete a key from different YAMLs")
subparsers.add_parser('list-params',
parents=[path_parser, verbose_parser],
help=("Collect all options for "
"'parameters._params' keys from YAMLs "
"found in <path>"))
subparsers.add_parser('list-nodes',
parents=[domain_parser, vcp_only_parser,
non_vcp_only_parser],
help=("List nodes that are available for "
"reclass. Use on salt-master node only!"))
subparsers.add_parser('list-domains',
help=("List domains that are available from "
"reclass models. Use on salt-master "
"node only!"))
subparsers.add_parser('show-context',
parents=[domain_parser, keys_parser],
help=("Show domain nodes with rendered content "
"for specified keys. Use on salt-master "
"node for already generated inventory "
"only!"))
subparsers.add_parser('trace-key',
parents=[domain_parser, node_parser, key_parser],
help=("Use 'reclass' to merge the model and "
"show all the classes where the "
"specified key is overwritten, and updated"
" values during merging and after "
"interpolation. "
"Use on salt-master node only!"))
subparsers.add_parser('render',
parents=[render_parser, env_name_parser],
help=("Render cookiecutter template using "
"multiple metadata sources"))
subparsers.add_parser('merge-context',
parents=[yaml_parser, path_parser],
help=("Merge nested content from YAML file "
"to specified reclass models"))
if len(self.args) == 0:
self.args = ['-h']
return parser.parse_args(self.args)
def main(args=None):
if args is None:
args = sys.argv[1:]
shell = Shell(args)
shell.execute()