def _get_cmd_func(name: str) -> Callable[..., Any]: """Returns the function of an enos <command> or panic gracefully If the name does not refer to an enos <command> that exists, this function print an error message and exit. """ # Get the function in charge of `enos <name>` try: return _COMMANDS[name] # Command not found, exit with an error message except KeyError: from difflib import get_close_matches possibilities = get_close_matches(name, _COMMANDS.keys()) if possibilities: CLI.error(f"""\ enos command '{name}' does not exist. The most similar command is '{possibilities[0]}'.""") else: CLI.error(f"""\ enos command '{name}' does not exist. See `enos --help` for a complete list of supported commands.""") sys.exit(1)
def up(**kwargs): """\ USAGE: enos up [-f CONFIG_FILE] [--force-deploy] [-t TAGS] [--pull] [-e ENV] Get and setup resources on the testbed. OPTIONS: -f CONFIG_FILE Path to the configuration file describing the deployment [default: ./reservation.yaml]. --force-deploy Force deployment. -t, --tags TAGS Only run ansible tasks tagged with these values. --pull Only preinstall software (e.g pull docker images). -e, --env ENV Path to the environment directory (Advanced option). Enos creates a directory to track the state of the experiment. Use this option to link enos with a different environment. """ logging.debug('phase[up]: args=%s' % kwargs) from enos import tasks from enos.utils.errors import EnosFilePathError # Get parameters config_file = pathlib.Path(kwargs.get('-f', './reservation.yaml')) is_force_deploy = kwargs.get('--force-deploy', False) is_pull_only = kwargs.get('--pull', False) tags = kwargs.get('--tags', None) # Launch the *up* task try: with _elib_open(kwargs.get('--env'), new=True) as env: config = _load_config(config_file) tasks.up(env, config, is_force_deploy, is_pull_only, tags) CLI.print("""\ The setup of your testbed completed successfully. You may proceed with an `enos os` to deploy OpenStack on it.""") # Nicely handle errors for the user except EnosFilePathError as err: CLI.error(f"""\ The path "{err.filepath}" does not point to a regular file. Please, run `enos new` first or link to an existing configuration file with `-f` option. See `enos help up` for more information.""") sys.exit(1) except yaml.YAMLError as err: error_loc = "" if hasattr(err, 'problem_mark'): loc = getattr(err, 'problem_mark') error_loc = f"at {loc.line+1}:{loc.column+1}" CLI.error(f'Syntax error in the file "{config_file}" ' + error_loc) sys.exit(1) except EnosUnknownProvider as err: CLI.error(str(err)) sys.exit(1) except Exception as e: CLI.critical(str(e)) sys.exit(1)
def destroy(**kwargs): """\ USAGE: enos destroy [--include-images] [--hard] [-e ENV] Destroy the deployment. OPTIONS: --include-images Remove also all the docker images. --hard Destroy the resources from the testbed. -e, --env ENV Path to the environment directory (Advanced option). Enos creates a directory to track the state of the experiment. Use this option to link enos with a different environment [default: ./current]. """ logging.debug('phase[destroy]: args=%s' % kwargs) from enos import tasks # Get parameters include_images = kwargs.get('--include-images', False) try: with _elib_open(kwargs.get('--env')) as env: if kwargs.get('--hard', False): # Destroy the entire infra tasks.destroy_infra(env) CLI.print("""\ Resources acquired from the testbed have been destroyed. You may get new ones with `enos up`.""") else: # Destroy OpenStack and deployed services tasks.destroy_os(env, include_images) CLI.print("""\ OpenStack has been destroyed from the testbed. You may redeployed a new one with `enos os`.""") # Nicely handle errors for the user except MissingEnvState as err: if err.key in ['config', 'kolla-ansible', 'inventory']: CLI.error(f"""\ {err.key} could not be found in your enos environment. Did you run `enos up` and `enos os` first?""") else: CLI.critical(str(err)) sys.exit(1) except Exception as e: CLI.critical(str(e)) sys.exit(1)
def _load_config(config_file: Path) -> Dict[str, Any]: "Load the yaml `config_file`" # Ensure `config_file` points to a file if not config_file.is_file(): raise EnosFilePathError( config_file, f'Configuration file {config_file} does not exist') # Parse it with open(config_file, 'r') as yaml_file: config = yaml.safe_load(yaml_file) CLI.info(f"Loaded the configuration file '{config_file}'") return config
def os(**kwargs): """\ USAGE: enos os [--reconfigure] [-t TAGS] [--pull] [-e ENV] enos os [-e ENV] -- <kolla-cmd> ... Install OpenStack with kolla-ansible. The second command falls back on kolla-ansible to run arbitrary commands, e.g., `enos os -- prechecks`. See `enos os -- --help` for an exhaustive list of supported commands. OPTIONS: --reconfigure Only reconfigure the services (after a first deployment). --pull Only preinstall software (e.g pull docker images). -t, --tags TAGS Only run ansible tasks tagged with these values. -e, --env ENV Path to the environment directory (Advanced option). Enos creates a directory to track the state of the experiment. Use this option to link enos with a different environment [default: ./current]. """ logging.debug('phase[os]: args=%s' % kwargs) from enos import tasks try: with _elib_open(kwargs.get('--env')) as env: if kwargs.get('--'): # Directly call the kolla-ansible executable kolla_cmd = kwargs.get('<kolla-cmd>', []) tasks.kolla_ansible(env, kolla_cmd) else: # Launch the *install os* task is_reconfigure = kwargs.get('--reconfigure', False) is_pull_only = kwargs.get('--pull', False) tags = kwargs.get('--tags', None) tasks.install_os(env, is_reconfigure, is_pull_only, tags) CLI.print("""\ The installation of OpenStack completed successfully. You may proceed with an `enos init` to install images and setup the network.""") # Nicely handle errors for the user except MissingEnvState as err: if err.key == 'kolla-ansible': CLI.error("""\ kolla-ansible could not be found in your enos environment. Did you successfully run `enos up` first?""") else: CLI.critical(err) sys.exit(1) except Exception as e: CLI.critical(str(e)) sys.exit(1)
def info(**kwargs): """\ USAGE: enos info [-o {json,yaml,pickle}] [-e ENV] Show information of the `ENV` deployment. OPTIONS: -o, --out FORMAT Output the result in either json, pickle or yaml FORMAT [default: json]. -e, --env ENV Path to the environment directory (Advanced option). Enos creates a directory to track the state of the experiment. Use this option to link enos with a different environment [default: ./current]. """ logging.debug('phase[info]: args=%s' % kwargs) import json import pickle def json_encoder(o): 'Render pathlib.Path with str' if isinstance(o, Path): return str(o.resolve()) else: return o.__dict__ # Get parameters output_type = kwargs.get('--out', 'json') # Display env with _elib_open(kwargs.get('--env')) as env: if output_type == 'json': print(json.dumps(env.data, default=json_encoder, indent=True)) elif output_type == 'pickle': print(pickle.dumps(env.data)) elif output_type == 'yaml': print(yaml.dump(env.data)) else: CLI.error(f"--out doesn't support {output_type} output format") print(info.__doc__)
def tc(**kwargs): """\ USAGE: enos tc [--test] [--reset] [-e ENV] Enforce network constraints. OPTIONS: --test Test network constraints enforcement. This generates various reports that you can get back on your machine with `enos backup`. --reset Reset the constraints. -e, --env ENV Path to the environment directory (Advanced option). Enos creates a directory to track the state of the experiment. Use this option to link enos with a different environment [default: ./current]. """ logging.debug('phase[tc]: args=%s' % kwargs) from enos import tasks # Get parameters validate = kwargs.get('--test', False) is_reset = kwargs.get('--reset', False) try: # Launch the *tc* task with _elib_open(kwargs.get('--env')) as env: tasks.tc(env, validate, is_reset) if validate: CLI.print("""\ A test for network constraints enforcement has been done. Generated reports could be downloaded on your machine with `enos backup`.""") # Nicely handle errors for the user except MissingEnvState as err: if err.key == 'config': CLI.error(f"""\ {err.key} could not be found in your enos environment. Did you successfully run `enos up` and `enos os` first?""") else: CLI.critical(str(err)) sys.exit(1) except Exception as e: CLI.critical(str(e)) sys.exit(1)
def init(**kwargs): """\ USAGE: enos init [--pull] [-e ENV] Initialise OpenStack with the bare necessities: - Install a 'member' role - Download and install a cirros and a debian image - Install default flavor (m1.tiny, ..., m1.xlarge) - Install default network OPTIONS: --pull Only preinstall software (e.g pull docker images). -e, --env ENV Path to the environment directory (Advanced option). Enos creates a directory to track the state of the experiment. Use this option to link enos with a different environment [default: ./current]. """ logging.debug('phase[init]: args=%s' % kwargs) from enos import tasks # Get params and launch the *init* task try: with _elib_open(kwargs.get('--env')) as env: is_pull_only = kwargs.get('--pull', False) tasks.init_os(env, is_pull_only) os_auth = env['kolla-ansible'].globals_values['openstack_auth'] CLI.print(f"""\ The initialization of OpenStack completed successfully. You may proceed with `source {env.env_name / 'admin-openrc'}` to then run the openstack CLI. You can also access the horizon dashboard at http://{env['rsc']['horizon'][0].address} with user "{os_auth['username']}" and password "{os_auth['password']}".""") # Nicely handle errors for the user except MissingEnvState as err: if err.key in ['kolla-ansible', 'inventory', 'networks']: CLI.error(f"""\ {err.key} could not be found in your enos environment. Did you successfully run `enos up` and `enos os` first?""") else: CLI.critical(err) sys.exit(1) except Exception as e: CLI.critical(str(e)) sys.exit(1)
def _elib_open(path: Optional[pathlib.Path], new: bool = False): "Open and dump enoslib env" from enoslib.task import get_or_create_env from enoslib.errors import EnosFilePathError try: # Get the environment from the file system env = get_or_create_env(new, path) # Let the user update it try: yield env # Save its changes on the file system finally: env.dump() # Nicely handle errors for the user except EnosFilePathError as err: CLI.error(f"""\ The path "{err.filepath}" does not point to a regular file. Please, ensure to link an existing file with the `--env` option or first run `enos up`.""") sys.exit(1)
def _set_logging_level(is_quiet: bool, verbose_level: int): "Set the root logger level" if is_quiet: logging.basicConfig(level=logging.ERROR) CLI.setLevel(logging.ERROR) elif verbose_level == 1: logging.basicConfig(level=logging.INFO) CLI.setLevel(logging.INFO) elif verbose_level == 2: logging.basicConfig(level=logging.DEBUG) CLI.setLevel(logging.DEBUG)
def new(**kwargs): """\ USAGE: enos new [-p TESTBED] Create a basic reservation.yaml file in the current directory. OPTIONS: -p, --provider TESTBED Targeted testbed. One of g5k, vagrant:virtualbox, vagrant:libvirt, chameleonkvm, chameleonbaremetal, openstack, vmong5k, static [default: g5k]. """ logging.debug('phase[new]: args=%s' % kwargs) from enos import tasks # Get parameters provider = kwargs['--provider'] # Launch the *new* task try: tasks.new(provider, Path('./reservation.yaml')) CLI.print(f"""\ A `reservation.yaml` file has been placed in this directory. You are now ready to deploy OpenStack on {provider} with `enos deploy`. Please read comments in the reservation.yaml file as well as the documentation on https://beyondtheclouds.github.io/enos/ for more information on using enos.""") # Nicely handle errors for the user except FileExistsError: CLI.error(f"""\ The `reservation.yaml` file already exists in this directory. Remove it before running `enos new --provider={provider}`.""") sys.exit(1) except Exception as e: CLI.critical(str(e)) sys.exit(1)
def backup(**kwargs): """\ USAGE: enos backup [--backup_dir=BACKUP_DIR] [-e ENV] Backup the environment. OPTIONS: --backup_dir=BACKUP_DIR Backup directory. -e, --env ENV Path to the environment directory (Advanced option). Enos creates a directory to track the state of the experiment. Use this option to link enos with a different environment [default: ./current]. """ logging.debug('phase[backup]: args=%s' % kwargs) from enos import tasks try: with _elib_open(kwargs.get('--env')) as env: # Get parameters backup_dir = pathlib.Path( kwargs.get('--backup_dir') or env.env_name).resolve() CLI.debug(f"Store backups at directory {backup_dir}") # Launch the *bench* task tasks.backup(env, backup_dir) CLI.print(f"""\ The backup of the environment completed successfully. Files has been saved at {backup_dir}.""") # Nicely handle errors for the user except Exception as e: CLI.critical(str(e)) sys.exit(1)
def bench(**kwargs): """\ USAGE: enos bench [--workload=WORKLOAD] [--reset] [--pull] [-e ENV] Run Rally/Shaker on this OpenStack. OPTIONS: --workload=WORKLOAD Path to the workload directory. This directory must contain a run.yml file that contains the description of the different scenarios to launch [default: workload/]. --reset Recreate the benchmark environment. --pull Only preinstall software (e.g pull docker images). -e, --env ENV Path to the environment directory (Advanced option). Enos creates a directory to track the state of the experiment. Use this option to link enos with a different environment [default: ./current]. """ logging.debug('phase[bench]: args=%s' % kwargs) from enos import tasks from enos.utils.extra import seekpath from enos.utils.errors import EnosFilePathError # Get parameters is_reset = kwargs.get('--reset', False) is_pull_only = kwargs.get('--pull', False) workload = pathlib.Path(seekpath(kwargs.get('--workload', 'workload/'))).resolve() CLI.debug(f"Use workload directory at {workload}") # Launch the *bench* task try: with _elib_open(kwargs.get('--env')) as env: tasks.bench(env, workload, is_reset, is_pull_only) CLI.print("""\ The bench phase completed. Generated reports could be downloaded on your machine with `enos backup`.""") # Nicely handle errors for the user except EnosFilePathError as err: CLI.error(f"""\ The path "{err.filepath}" does not point to a regular file. Please, ensure to link an existing file with the `--workload`.""") sys.exit(1) except MissingEnvState as err: if err.key in ['kolla-ansible', 'inventory', 'networks']: CLI.error(f"""\ {err.key} could not be found in your enos environment. Did you successfully run `enos up` and `enos os` first?""") else: CLI.critical(err) sys.exit(1) except yaml.YAMLError as err: error_loc = "" if hasattr(err, 'problem_mark'): loc = getattr(err, 'problem_mark') error_loc = f"at {loc.line+1}:{loc.column+1}" CLI.error(f'Syntax error in the file "{workload / "run.yam"}" ' + error_loc) sys.exit(1) except Exception as e: CLI.critical(str(e)) sys.exit(1)