Commit 3a9efa6a authored by Dobli's avatar Dobli
Browse files

Replaced PyInquirer by questionary for cleaner prompt code

parent ef903cc2
......@@ -84,16 +84,14 @@ The setup also expects a working docker-machine environment. To connect all inst
**Python:**
```sh
docker
PyInquirer
pyyaml
bcrypt
pip-tools
docker # Docker client library
questionary # Prompt library
ruamel.yaml # Yaml library that preserves structure
bcrypt # generate bcrypt hashes
pip-tools # manage requirements (Optional)
```
All python requirements managed using `pip-tool` in the `requirements.in` file. The command `pip-compile` generates a `requirements.txt` file that can be used with with `pip install--user -r requirements.txt` to install all needed python dependencies. E.g. on Ubuntu you can execute the following:
All python requirements managed using `pip-tool` in the `requirements.in` file. The command `pip-compile` generates a `requirements.txt` file that can be used with with `pip ` to install all needed python dependencies. E.g. on Ubuntu you can execute the following:
```
pip3 install --user -r requirements.txt
......
......@@ -9,8 +9,9 @@ from subprocess import PIPE, run
import bcrypt
import docker
from PyInquirer import prompt
import questionary as qust
from ruamel.yaml import YAML
from prompt_toolkit.styles import Style
# Configure YAML
yaml = YAML()
......@@ -19,6 +20,21 @@ yaml.indent(mapping=4, sequence=4, offset=2)
# Log level during development is info
logging.basicConfig(level=logging.WARNING)
# Prompt style
st = Style([
('qmark', 'fg:#00c4b4 bold'), # token in front of question
('question', 'bold'), # question text
('answer', 'fg:#00c4b4 bold'), # submitted answer question
('pointer', 'fg:#00c4b4 bold'), # pointer for select and checkbox
('selected', 'fg:#00c4b4'), # selected item checkbox
('separator', 'fg:#00c4b4'), # separator in lists
('instruction', '') # user instructions for selections
])
# ******************************
# Constants <<<
# ******************************
# Directories for config generation
CUSTOM_DIR = 'custom_configs'
TEMPLATE_DIR = 'template_configs'
......@@ -57,12 +73,12 @@ SWARM_PORT = 2377
UID = 9001
# Username for admin
ADMIN_USER = 'ohadmin'
# >>>
# ******************************
# Compose file functions {{{
# Compose file functions <<<
# ******************************
def generate_initial_compose(base_dir):
"""Creates the initial compose using the skeleton
......@@ -296,11 +312,11 @@ def add_or_update_compose_service(compose_path, service_name, service_content):
yaml.dump(compose, compose_f)
# reduce file to new size
compose_f.truncate()
# }}}
# >>>
# ******************************
# Config file functions {{{
# Config file functions <<<
# ******************************
def generate_config_folders(base_dir):
"""Generate folders for configuration files
......@@ -531,11 +547,11 @@ def create_or_replace_config_file(base_dir, config_path, content, json=False):
file.write(content)
# }}}
# >>>
# ******************************
# Docker machine functions {{{
# Docker machine functions <<<
# ******************************
def get_machine_list():
"""Get a list of docker machine names using the docker-machine system command
......@@ -650,11 +666,11 @@ def generate_swarm(machines):
machine, manager=leader)
# }}}
# >>>
# ******************************
# Docker client commands {{{
# Docker client commands <<<
# ******************************
def resolve_service_nodes(service):
"""Returnes nodes running on a specified service
......@@ -752,11 +768,11 @@ def get_docker_client(manager=None):
else:
client = docker.from_env()
return client
# }}}
# >>>
# ******************************
# CLI base commands {{{
# CLI base commands <<<
# ******************************
def init_config_dirs_command(args):
"""Initialize config directories
......@@ -822,11 +838,11 @@ def interactive_command(args):
main_menu(args)
# }}}
# >>>
# ******************************
# Interactive menu entries {{{
# Interactive menu entries <<<
# ******************************
def main_menu(args):
""" Display main menu
......@@ -838,21 +854,15 @@ def main_menu(args):
base_dir = os.getcwd()
# Main menu prompts
questions = [{
'type': 'list',
'name': 'main',
'message': 'Public Building Manager - Main Menu',
'choices': load_main_entires(base_dir)
}]
answers = prompt(questions)
choice = answers['main']
choice = qust.select('Public Building Manager - Main Menu',
choices=load_main_entires(base_dir), style=st).ask()
if 'Create' in choice:
init_menu(args)
elif 'Execute' in choice:
exec_menu(args)
return answers
return choice
def load_main_entires(base_dir):
......@@ -887,42 +897,18 @@ def init_menu(args):
base_dir = os.getcwd()
# Prompts
questions = [
{
'type': 'input',
'name': 'stack_name',
'message': 'Choose a name for your setup'
},
{
'type': 'checkbox',
'name': 'machines',
'message': 'What docker machines will be used?',
'choices': generate_checkbox_choices(get_machine_list())
}
]
answers = prompt(questions)
stack_name = qust.text('Choose a name for your setup', style=st).ask()
hosts = qust.checkbox('What docker machines will be used?',
choices=generate_cb_choices(
get_machine_list()), style=st).ask()
# Ensure passwords match
password_match = False
while not password_match:
password_questions = [{
'type':
'password',
'name':
'password',
'message':
'Choose a password for the ohadmin user:',
},
{
'type':
'password',
'name':
'confirm',
'message':
'Repeat password for the ohadmin user',
}]
password_answers = prompt(password_questions)
if password_answers['password'] == password_answers['confirm']:
password = qust.password(
'Choose a password for the ohadmin user:', style=st).ask()
confirm = qust.password(
'Repeat password for the ohadmin user:', style=st).ask()
if password == confirm:
password_match = True
else:
print("Passwords did not match, try again")
......@@ -932,8 +918,6 @@ def init_menu(args):
generate_initial_compose(base_dir)
# Generate config files based on input
username = ADMIN_USER
password = password_answers['password']
hosts = answers['machines']
generate_sftp_file(base_dir, username, password)
generate_postgres_files(base_dir, username, password)
generate_mosquitto_file(base_dir, username, password)
......@@ -946,19 +930,14 @@ def init_menu(args):
init_machine_menu(base_dir, host, i)
# print(answers)
print(f"Configuration files generated in {base_dir}")
print(f"Configuration files for {stack_name} generated in {base_dir}")
# Check if changes shall be applied to docker environment
generate_questions = [{
'type': 'confirm',
'name': 'generate',
'message': 'Apply changes to docker environment?',
'default': True
}]
generate_answers = prompt(generate_questions)
generate = qust.confirm(
'Apply changes to docker environment?', default=True, style=st).ask()
if generate_answers['generate']:
generate_swarm(answers['machines'])
if generate:
generate_swarm(hosts)
def exec_menu(args):
......@@ -967,23 +946,12 @@ def exec_menu(args):
:args: Passed commandline arguments
"""
machine = docker_client_prompt(" to execute command at")
questions = [
{
'type': 'list',
'name': 'service_name',
'message': 'Which service container shall execute the command?',
'choices': get_container_list(machine)
},
{
'type': 'input',
'name': 'command',
'message': 'What command should be executed?'
}
]
answers = prompt(questions)
run_command_in_service(
answers['service_name'], answers['command'], machine)
print(answers)
service_name = qust.select(
'Which service container shall execute the command?',
choices=get_container_list(machine), style=st).ask()
command = qust.text('What command should be executed?', style=st).ask()
run_command_in_service(service_name, command, machine)
# *** Sub Menu Entries ***
......@@ -995,22 +963,12 @@ def init_machine_menu(base_dir, host, increment):
:increment: incrementing number to ensure ports are unique
"""
# Prompt for services
questions = [
{
'type': 'input',
'name': 'buildingid',
'message': f'Choose a name for building on server {host}',
'default': f'{host}'
},
{
'type': 'checkbox',
'name': 'services',
'message': f'What services shall {host} provide?',
'choices': generate_checkbox_choices(SERVICES.keys(), checked=True)
}
]
answers = prompt(questions)
services = answers['services']
building = qust.text(f'Choose a name for building on server {host}',
default=f'{host}', style=st).ask()
services = qust.checkbox(f'What services shall {host} provide?',
choices=generate_cb_choices(SERVICES.keys(),
checked=True),
style=st).ask()
if 'sftp' in services:
add_sftp_service(base_dir, host, increment)
if 'openhab' in services:
......@@ -1021,11 +979,11 @@ def init_machine_menu(base_dir, host, increment):
add_mqtt_service(base_dir, host, increment)
if 'postgres' in services:
add_postgres_service(base_dir, host)
print(answers)
print(building)
# *** Menu Helper Functions ***
def generate_checkbox_choices(list, checked=False):
def generate_cb_choices(list, checked=False):
"""Generates checkbox entries for lists of strings
:list: pyhton list that shall be converted
......@@ -1041,21 +999,14 @@ def docker_client_prompt(message_details=''):
:manager: Optional machine to use, prompt otherwise
:returns: Docker client instance
"""
questions = [
{
'type': 'list',
'name': 'machine',
'message': f'Choose manager machine{message_details}',
'choices': get_machine_list()
}
]
answers = prompt(questions)
return answers['machine']
# }}}
machine = qust.select(f'Choose manager machine{message_details}',
choices=get_machine_list(), style=st).ask()
return machine
# >>>
# ******************************
# Script main ( entry) {{{
# Script main (entry) <<<
# ******************************
if __name__ == '__main__':
import argparse
......@@ -1123,7 +1074,7 @@ if __name__ == '__main__':
args.func(args)
except AttributeError:
interactive_command(args)
# }}}
# >>>
# --- vim settings ---
# vim:foldmethod=marker:foldlevel=0
# vim:foldmethod=marker:foldlevel=0:foldmarker=<<<,>>>
bcrypt
docker
PyInquirer
pyyaml
questionary
ruamel.yaml
......@@ -11,12 +11,9 @@ chardet==3.0.4 # via requests
docker-pycreds==0.4.0 # via docker
docker==3.6.0
idna==2.7 # via requests
prompt_toolkit==1.0.14 # via pyinquirer
prompt-toolkit==2.0.8 # via questionary
pycparser==2.19 # via cffi
pygments==2.3.1 # via pyinquirer
pyinquirer==1.0.3
pyyaml==3.13
regex==2018.11.22 # via pyinquirer
questionary==1.0.2
requests==2.20.1 # via docker
ruamel.yaml==0.15.86
six==1.11.0 # via bcrypt, docker, docker-pycreds, prompt-toolkit, websocket-client
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment