| # 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 |
| 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) |
| |
| 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], |
| 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() |