Compare commits
	
		
			No commits in common. "main" and "main" have entirely different histories.
		
	
	
		
	
		
| @ -1,5 +0,0 @@ | |||||||
| docker-compose.yml |  | ||||||
| .gitignore |  | ||||||
| .env |  | ||||||
| .git |  | ||||||
| .cache |  | ||||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -158,9 +158,7 @@ cython_debug/ | |||||||
| #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore | #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore | ||||||
| #  and can be added to the global gitignore or merged into this file.  For a more nuclear | #  and can be added to the global gitignore or merged into this file.  For a more nuclear | ||||||
| #  option (not recommended) you can uncomment the following to ignore the entire idea folder. | #  option (not recommended) you can uncomment the following to ignore the entire idea folder. | ||||||
| .idea/ | #.idea/ | ||||||
| .vscode/ |  | ||||||
| 
 | 
 | ||||||
| # Project specific | # Project specific | ||||||
| config.json | config.json | ||||||
| **/dummy_platform |  | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @ -1,21 +0,0 @@ | |||||||
| # Changelog |  | ||||||
| 
 |  | ||||||
| ## [0.1.0] - 2024-06-09 |  | ||||||
| ### Added |  | ||||||
| - mgrctl core mvp |  | ||||||
| - auth apps for vm6/dci6 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ## [0.1.1] - 2025-05-07 |  | ||||||
| ### Updated |  | ||||||
| - dependencies |  | ||||||
| - docker hub latest image |  | ||||||
| 
 |  | ||||||
| ### Fixed |  | ||||||
| - dci input container name |  | ||||||
| 
 |  | ||||||
| ### Changed |  | ||||||
| - remove peewee deps |  | ||||||
| - remove data base management |  | ||||||
| - remove mysql container for dev env |  | ||||||
| - remove \__version__ from sub app |  | ||||||
							
								
								
									
										59
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								Dockerfile
									
									
									
									
									
								
							| @ -1,59 +0,0 @@ | |||||||
| # app/Dockerfile |  | ||||||
| 
 |  | ||||||
| # pull the official docker image |  | ||||||
| FROM python:3.12-alpine AS poetry-base |  | ||||||
| 
 |  | ||||||
| # default build args |  | ||||||
| ARG APP_VERSION=0.1.1 \ |  | ||||||
|     APP_DIR=/app \ |  | ||||||
|     SRC_DIR=./mgrctl \ |  | ||||||
|     PKG_NAME=mgrctl \ |  | ||||||
|     PKG_VERSION=0.1.1 |  | ||||||
| 
 |  | ||||||
| # set env variables |  | ||||||
| ENV APP_NAME=mgrctl \ |  | ||||||
|     # Python3: |  | ||||||
|     PYTHONFAULTHANDLER=1 \ |  | ||||||
|     PYTHONUNBUFFERED=1 \ |  | ||||||
|     PYTHONHASHSEED=random \ |  | ||||||
|     # Pip: |  | ||||||
|     PIP_NO_CACHE_DIR=on \ |  | ||||||
|     PIP_DISABLE_PIP_VERSION_CHECK=on \ |  | ||||||
|     PIP_ROOT_USER_ACTION=ignore \ |  | ||||||
|     PIP_DEFAULT_TIMEOUT=100 \ |  | ||||||
|     # Poetry: |  | ||||||
|     POETRY_NO_INTERACTION=1 \ |  | ||||||
|     POETRY_VIRTUALENVS_CREATE=false \ |  | ||||||
|     POETRY_CACHE_DIR='/var/cache/pypoetry' \ |  | ||||||
|     POETRY_HOME='/usr/local' \ |  | ||||||
|     POETRY_VERSION=2.1.2 |  | ||||||
| 
 |  | ||||||
| # install system deps |  | ||||||
| RUN apk --no-cache add curl \ |  | ||||||
|     && curl -sSL https://install.python-poetry.org | python3 - \ |  | ||||||
|     && mkdir ${APP_DIR} |  | ||||||
| 
 |  | ||||||
| COPY ./pyproject.toml ./poetry.lock ./README.md ${APP_DIR} |  | ||||||
| COPY ${SRC_DIR} ${APP_DIR}/${PKG_NAME} |  | ||||||
| 
 |  | ||||||
| # set workdir |  | ||||||
| WORKDIR ${APP_DIR} |  | ||||||
| 
 |  | ||||||
| # build pkg whl |  | ||||||
| RUN poetry build --format wheel \ |  | ||||||
|     && pip install ${APP_DIR}/dist/${PKG_NAME}-${PKG_VERSION}-py3-none-any.whl \ |  | ||||||
|     && rm -r /usr/local/venv |  | ||||||
| 
 |  | ||||||
| # now multistage builds |  | ||||||
| FROM python:3.12-alpine |  | ||||||
| 
 |  | ||||||
| # copy app and dependencies |  | ||||||
| COPY --from=poetry-base /usr/local/ /usr/local/ |  | ||||||
| 
 |  | ||||||
| # install bash and mgrctl shell completion |  | ||||||
| RUN apk --no-cache add bash \ |  | ||||||
|     && echo 'eval "$(_MGRCTL_COMPLETE=bash_source mgrctl)"' > ~/.bashrc |  | ||||||
| 
 |  | ||||||
| # "demonize" container |  | ||||||
| # use docker attach mgrctl or docker exec -it mgrctl mgrctl --help for example |  | ||||||
| CMD [ "bash"] |  | ||||||
							
								
								
									
										13
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Makefile
									
									
									
									
									
								
							| @ -1,13 +0,0 @@ | |||||||
| build: |  | ||||||
| 	python3 ./scripts/devtools/dev.py builder docker-compose build |  | ||||||
| destroy: |  | ||||||
| 	python3 ./scripts/devtools/dev.py builder docker-compose destroy |  | ||||||
| up: |  | ||||||
| 	python3 ./scripts/devtools/dev.py runner docker-compose up |  | ||||||
| down: |  | ||||||
| 	python3 ./scripts/devtools/dev.py runner docker-compose down |  | ||||||
| restart: |  | ||||||
| 	python3 ./scripts/devtools/dev.py runner docker-compose restart |  | ||||||
| setup: build up |  | ||||||
| 
 |  | ||||||
| .PHONY: exec setup build |  | ||||||
							
								
								
									
										172
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										172
									
								
								README.md
									
									
									
									
									
								
							| @ -6,175 +6,3 @@ Maintenance application for quick access, check and resolve issues VM/DCImanager | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ### See [CONTRIBUTING.md](CONTRIBUTING.md) for a general overview of how to contribute  | ### See [CONTRIBUTING.md](CONTRIBUTING.md) for a general overview of how to contribute  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ## How to use gogo companion: |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #### Dependencies |  | ||||||
| 
 |  | ||||||
| ОС: Linux, Mac, Windows |  | ||||||
| Shell: bash |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #### Installation |  | ||||||
| 
 |  | ||||||
| - download or copy the script from the root of the repository `/scripts/gogo/gogo.sh` |  | ||||||
| - to call the script without specifying the full path, put it in the directory that is in $PATH (in the future, I assume that you will have it `~/.local/bin/`) |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| curl https://git.isptech.ru/ISPsystem/isp-maintenance/raw/branch/main/scripts/gogo/gogo.sh -o ~/.local/bin/gogo && chmod u+x ~/.local/bin/gogo |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| #### Configuration |  | ||||||
| 
 |  | ||||||
| For the script to work, you need a configuration file with settings `~/.config/gogo/gogo.conf` |  | ||||||
| 
 |  | ||||||
| This config contains the following fields that are read every time you start |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| GO_SERVER_ADDR_RUSSIAN= |  | ||||||
| GO_SERVER_ADDR_GERMANY= |  | ||||||
| GO_SERVER_ADDR_TEST= |  | ||||||
| VAULT_SERVER_ADDR= |  | ||||||
| SSH_PRIVATE_KEY_USER= |  | ||||||
| SSH_PRIVATE_KEY_PATH= |  | ||||||
| SSH_PUBLIC_KEY_PATH= |  | ||||||
| SSH_CRT_FILE= |  | ||||||
| MGRCTL_IMAGE=mois3y/mgrctl:latest |  | ||||||
| DEBUG_MODE=false |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Please fill in the fields with current data, write the path to the keys and certificate in full `/home/username/.ssh/id_ecdsa` and so on. |  | ||||||
| For security reasons, server addresses and paths to keys are not indicated here. |  | ||||||
| 
 |  | ||||||
| For your convenience, the config can be filled out interactively |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --init |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #### Usage |  | ||||||
| 
 |  | ||||||
| - Get a temporary ssh certificate |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --crt |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| - Check your connection to test servers in both locations |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --test |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| - If the connection is successful, you can use the script. You are beautiful! |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #### Examples |  | ||||||
| 
 |  | ||||||
| - Connect to BILLmanager 6 via ssh port 22 and interface port 443 |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --bill my.example.com |  | ||||||
| ``` |  | ||||||
| or |  | ||||||
| ```console |  | ||||||
| gogo --bill my.example.com -p 22 -wp 443 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| - Connect to BILLmanager 6 again without generating an access key to the interface |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --bill my.example.com --ssh |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| - Connect to DNSmanager 6 via ssh port 22 and interface port 1501 |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --dns my.example.com -p 22 -wp 1501 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| - Connect to DCImanager 6 via ssh port 2222 and interface 443 port |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --dci vm.example.com -p 2222 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| - Connect to VMmanager 6 via ssh port 2222 and interface port 443 |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --vm 228.228.228.228 -p 2222 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| - Connect to VMmanager 6 via ssh port 22 and interface port 443, indicating the id of a specific user and generate 3 keys |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --vm vm.example.com --mgrctl auth user access --id 1488 --count 1 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| - The same thing, just print the command that is executed on the server side into the console, you can run it later simply by copying it |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --vm vm.example.com --tty --mgrctl auth user access --id 1488 --count 1 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Connect to VMmanager 6 via ssh port 22 and interface port 443 via DE go3 server |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --vm vm.example.com -p 22 --de |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Connect to DCImanager 6 via ssh port 22 and interface port 443 via the old go method |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --dci dci.example.com -p 22 --go |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ##### There are also examples in `help` |  | ||||||
| 
 |  | ||||||
| ```console |  | ||||||
| gogo --help |  | ||||||
| 
 |  | ||||||
| Usage: gogo [options [parameters]] |  | ||||||
| 
 |  | ||||||
| Examples: |  | ||||||
| 
 |  | ||||||
| gogo --init | init config file |  | ||||||
| gogo --crt  | get ssh certificate for go3 connections |  | ||||||
| gogo --test | check go3 connection availability |  | ||||||
| 
 |  | ||||||
| gogo --bill my.example.com |  | ||||||
| gogo --vm my.example.com --de  | connect throw DE go3 server |  | ||||||
| gogo --vm 0.0.0.0 --ssh | only ssh access |  | ||||||
| gogo --vm 0.0.0.0 --tty | use mgrctl interactive |  | ||||||
| 
 |  | ||||||
| gogo --dci 0.0.0.0 --mgrctl auth user access --id 3 --count 5 |  | ||||||
| gogo --dci 0.0.0.0 --mgrctl auth user ls --admins |  | ||||||
| gogo --vm 0.0.0.0 --port 22122 --mgrctl auth user ls --admins |  | ||||||
| gogo --vm 0.0.0.0 --tty --mgrctl auth user ls --admins |  | ||||||
| gogo --dns ns1.example.com --web-port 1501 |  | ||||||
| gogo --dns ns1.example.com --port 22122 --web-port 1501 |  | ||||||
| gogo --bill my.example.com --port 22 --web-port 1501 |  | ||||||
| 
 |  | ||||||
| Options: |  | ||||||
| 
 |  | ||||||
|  --vm[dci|bill|dns|ip] expected ip_addr             required |  | ||||||
|  --port     | -p    ssh port,   default 22 |  | ||||||
|  --web-port | -wp   web port,   default 443 |  | ||||||
|  --go/--go3         go version, default go3 |  | ||||||
|  --de               connect throw DE go3 server |  | ||||||
|  --ssh              open only ssh session |  | ||||||
|  --tty              for vm6/dci6 echo cmd for run container |  | ||||||
|  --mgrctl [args]    for vm6/dci6 customize access params |  | ||||||
| 
 |  | ||||||
| Single options: |  | ||||||
|  --init     | -i    generate configuration |  | ||||||
|  --crt      | -c    generate ssh cert |  | ||||||
|  --test     | -t    check go3 connection availability |  | ||||||
|  --version  | -v    print version |  | ||||||
|  --help     | -h    print this message and exit |  | ||||||
| ``` |  | ||||||
| @ -1,43 +0,0 @@ | |||||||
| # ? docker-compose.yml for development environment |  | ||||||
| 
 |  | ||||||
| # ! To start development you need to create a directory ./dummy_platform. |  | ||||||
| # ? Place files from the test platform into it: |  | ||||||
| # ? VM6: |  | ||||||
| # ?    ./dummy_platform/opt/ispsystem/vm/config.json - configuration file |  | ||||||
| # ? DCI6: |  | ||||||
| # ?    ./dummy_platform/opt/ispsystem/dci/config.json - configuration file |  | ||||||
| 
 |  | ||||||
| # ? Create ./.env file and fill it with required vars: |  | ||||||
| # ? PLATFORM_TYPE='vm' or PLATFORM_TYPE='dci' |  | ||||||
| 
 |  | ||||||
| # ? Launch:  |  | ||||||
| # ?     docker-compose up -d --force-recreate |  | ||||||
| # ?     docker attach mgrctl |  | ||||||
| 
 |  | ||||||
| services: |  | ||||||
|   mgrctl: |  | ||||||
|     container_name: mgrctl |  | ||||||
|     restart: unless-stopped |  | ||||||
|     build: |  | ||||||
|       context: . |  | ||||||
|       args: |  | ||||||
|         - APP_VERSION=${APP_VERSION} |  | ||||||
|         - APP_DIR=${APP_DIR} |  | ||||||
|         - SRC_DIR=${SRC_DIR} |  | ||||||
|         - PKG_NAME=${PKG_NAME} |  | ||||||
|         - PKG_VERSION=${PKG_VERSION} |  | ||||||
|     networks: |  | ||||||
|       vm_box_net: null |  | ||||||
|     volumes: |  | ||||||
|       - type: bind |  | ||||||
|         source: ./dummy_platform/opt/ispsystem/${PLATFORM_TYPE}/config.json |  | ||||||
|         target: /opt/ispsystem/${PLATFORM_TYPE}/config.json |  | ||||||
|     env_file: |  | ||||||
|       - ./.env |  | ||||||
|     tty: true |  | ||||||
|     stdin_open: true |  | ||||||
| 
 |  | ||||||
| networks: |  | ||||||
|   vm_box_net: |  | ||||||
|     name: vm_box_net |  | ||||||
|     driver: bridge |  | ||||||
							
								
								
									
										12
									
								
								isp_maintenance/apps/dci6/access/commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								isp_maintenance/apps/dci6/access/commands.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import click | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @click.group(help='access command for lazy example') | ||||||
|  | @click.option('--debug/--no-debug', default=False) | ||||||
|  | def cli(debug): | ||||||
|  |     click.echo(f"Debug mode is {'on' if debug else 'off'}") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @cli.command() | ||||||
|  | def enable(): | ||||||
|  |     click.echo('Access granted') | ||||||
							
								
								
									
										13
									
								
								isp_maintenance/apps/dci6/commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								isp_maintenance/apps/dci6/commands.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | import click | ||||||
|  | 
 | ||||||
|  | from core.cli.lazy_group import LazyGroup | ||||||
|  | from settings.general import INSTALLED_APPS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @click.group( | ||||||
|  |     cls=LazyGroup, | ||||||
|  |     lazy_subcommands=INSTALLED_APPS['dci6'], | ||||||
|  |     help='dci6 command for lazy example', | ||||||
|  | ) | ||||||
|  | def cli(): | ||||||
|  |     pass | ||||||
							
								
								
									
										12
									
								
								isp_maintenance/apps/vm6/access/commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								isp_maintenance/apps/vm6/access/commands.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import click | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @click.group(help='access command for lazy example') | ||||||
|  | @click.option('--debug/--no-debug', default=False) | ||||||
|  | def cli(debug): | ||||||
|  |     click.echo(f"Debug mode is {'on' if debug else 'off'}") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @cli.command() | ||||||
|  | def enable(): | ||||||
|  |     click.echo('Access granted') | ||||||
							
								
								
									
										12
									
								
								isp_maintenance/apps/vm6/commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								isp_maintenance/apps/vm6/commands.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import click | ||||||
|  | from core.cli.lazy_group import LazyGroup | ||||||
|  | from settings.general import INSTALLED_APPS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @click.group( | ||||||
|  |     cls=LazyGroup, | ||||||
|  |     lazy_subcommands=INSTALLED_APPS['vm6'], | ||||||
|  |     help='vm6 command for lazy example', | ||||||
|  | ) | ||||||
|  | def cli(): | ||||||
|  |     pass | ||||||
							
								
								
									
										13
									
								
								isp_maintenance/apps/vm6/nodes/commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								isp_maintenance/apps/vm6/nodes/commands.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | import click | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @click.group(help='nodes command for lazy example') | ||||||
|  | def cli(): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @cli.command(name='list') | ||||||
|  | def nodes_list(): | ||||||
|  |     click.echo('NODES LIST: etc...') | ||||||
|  |     for num in range(1, 10): | ||||||
|  |         click.echo(num) | ||||||
							
								
								
									
										0
									
								
								isp_maintenance/core/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								isp_maintenance/core/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								isp_maintenance/core/cli/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								isp_maintenance/core/cli/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								isp_maintenance/db/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								isp_maintenance/db/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								isp_maintenance/db/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								isp_maintenance/db/base.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								isp_maintenance/db/dci6/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								isp_maintenance/db/dci6/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								isp_maintenance/db/vm6/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								isp_maintenance/db/vm6/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										21
									
								
								isp_maintenance/ispmgr.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										21
									
								
								isp_maintenance/ispmgr.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | import click | ||||||
|  | 
 | ||||||
|  | from core.cli.lazy_group import LazyGroup | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @click.group( | ||||||
|  |     cls=LazyGroup, | ||||||
|  |     lazy_subcommands={ | ||||||
|  |         'vm6': 'apps.vm6.commands.cli', | ||||||
|  |         'dci6': 'apps.dci6.commands.cli', | ||||||
|  |     }, | ||||||
|  |     help='main CLI command for lazy example', | ||||||
|  | ) | ||||||
|  | def cli(): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     cli() | ||||||
							
								
								
									
										15
									
								
								isp_maintenance/settings/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								isp_maintenance/settings/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | from settings.general import BASE_DIR | ||||||
|  | 
 | ||||||
|  | from settings.platform import ( | ||||||
|  |     PLATFORM_TYPE, | ||||||
|  |     PLATFORM_URL, | ||||||
|  |     PLATFORM_CONFIG | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | from settings.db import( | ||||||
|  |     DB_ENGINE, | ||||||
|  |     DB_HOST, | ||||||
|  |     DB_PORT, | ||||||
|  |     DB_USER, | ||||||
|  |     DB_PASSWORD | ||||||
|  | ) | ||||||
							
								
								
									
										23
									
								
								isp_maintenance/settings/db.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								isp_maintenance/settings/db.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | from settings.environment import env | ||||||
|  | from settings.platform import PLATFORM_CONFIG | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # ! Required because some instance use psql db: | ||||||
|  | DB_ENGINE = env.str( | ||||||
|  |     'DB_ENGINE', | ||||||
|  |     PLATFORM_CONFIG.get('DatabaseType', 'mysql') | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | # Connection parameters: | ||||||
|  | DB_HOST = env.str( | ||||||
|  |     'DB_HOST', | ||||||
|  |     PLATFORM_CONFIG.get('DatabaseType', 'mysql') | ||||||
|  | ) | ||||||
|  | DB_PORT = env.int('DB_PORT', 3306) | ||||||
|  | DB_USER = env.str('DB_USER', 'root') | ||||||
|  | 
 | ||||||
|  | # ! Do not pass password on production. Use value from config.json | ||||||
|  | DB_PASSWORD = env.str( | ||||||
|  |     'DB_PASSWORD', | ||||||
|  |     PLATFORM_CONFIG.get('MysqlRootPassword', '') | ||||||
|  | ) | ||||||
| @ -6,9 +6,10 @@ BASE_DIR = pathlib.Path(__file__).resolve().parent.parent | |||||||
| 
 | 
 | ||||||
| INSTALLED_APPS = { | INSTALLED_APPS = { | ||||||
|     'vm6': { |     'vm6': { | ||||||
|         'auth': 'mgrctl.apps.vm6.auth.commands.cli', |         'access': 'apps.vm6.access.commands.cli', | ||||||
|  |         'nodes': 'apps.vm6.nodes.commands.cli', | ||||||
|     }, |     }, | ||||||
|     'dci6': { |     'dci6': { | ||||||
|         'auth': 'mgrctl.apps.dci6.auth.commands.cli', |         'access': 'apps.dci6.access.commands.cli', | ||||||
|     }, |     }, | ||||||
| } | } | ||||||
							
								
								
									
										13
									
								
								isp_maintenance/settings/platform.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								isp_maintenance/settings/platform.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | from settings.environment import env | ||||||
|  | from settings.general import BASE_DIR | ||||||
|  | from utils.helpers import parse_json_file | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | PLATFORM_TYPE = env.str('PLATFORM_TYPE', 'vm') | ||||||
|  | 
 | ||||||
|  | PLATFORM_CONFIG = parse_json_file(f'{BASE_DIR}/config.json') | ||||||
|  | 
 | ||||||
|  | PLATFORM_URL = env.url( | ||||||
|  |     'PLATFORM_URL', | ||||||
|  |     f"https://{PLATFORM_CONFIG.get('DomainName' ,'replace.me')}" | ||||||
|  | ) | ||||||
							
								
								
									
										0
									
								
								isp_maintenance/utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								isp_maintenance/utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -1,9 +1,8 @@ | |||||||
| import json | import json | ||||||
| import sys | import sys | ||||||
| import click |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def parse_json_file(file_path: str) -> dict: | def parse_json_file(file_path):  | ||||||
|     """ |     """ | ||||||
|     Function read json file as usual config.json then parse it to python dict |     Function read json file as usual config.json then parse it to python dict | ||||||
| 
 | 
 | ||||||
| @ -17,7 +16,5 @@ def parse_json_file(file_path: str) -> dict: | |||||||
|         with open(file_path, 'r') as f: |         with open(file_path, 'r') as f: | ||||||
|             return json.load(f) |             return json.load(f) | ||||||
|     except Exception as error: |     except Exception as error: | ||||||
|         click.echo(error) |         print(error) | ||||||
|         click.echo("Required: /opt/ispsystem/PLATFORM_TYPE/config.json") |  | ||||||
|         click.echo("Note: don't forget to mount this file into the container") |  | ||||||
|         sys.exit(1) |         sys.exit(1) | ||||||
| @ -1,13 +0,0 @@ | |||||||
| # █▀▄▀█ █▀▀ ▀█▀ ▄▀█ ▀ |  | ||||||
| # █░▀░█ ██▄ ░█░ █▀█ ▄ |  | ||||||
| # -- -- -- -- -- -- - |  | ||||||
| __author__ = "MOIS3Y, a.garaev" |  | ||||||
| __credits__ = [ |  | ||||||
|     "Stepan Zhukovsky", |  | ||||||
|     "Arthur Garaev", |  | ||||||
| ] |  | ||||||
| __license__ = "MIT" |  | ||||||
| __version__ = "0.1.1" |  | ||||||
| __maintainer__ = "Stepan Zhukovsky" |  | ||||||
| __email__ = "stepan@zhukovsky.me" |  | ||||||
| __status__ = "Development" |  | ||||||
| @ -1,150 +0,0 @@ | |||||||
| import sys |  | ||||||
| import json |  | ||||||
| import click |  | ||||||
| import urllib |  | ||||||
| import requests |  | ||||||
| 
 |  | ||||||
| from time import sleep |  | ||||||
| from mgrctl.settings.api import ( |  | ||||||
|     API_URL, |  | ||||||
|     API_HEADERS, |  | ||||||
|     API_EMAIL, |  | ||||||
|     API_PASSWORD, |  | ||||||
|     API_VERIFY_SSL, |  | ||||||
|     API_COUNT_TRY_CONNECTIONS |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class BaseAPI(object): |  | ||||||
|     def __init__(self): |  | ||||||
|         """Announces required parameters""" |  | ||||||
|         self.API_URL = API_URL |  | ||||||
|         self.API_HEADERS = API_HEADERS |  | ||||||
|         self.API_EMAIL = API_EMAIL |  | ||||||
|         self.API_PASSWORD = API_PASSWORD |  | ||||||
|         self.API_VERIFY_SSL = API_VERIFY_SSL |  | ||||||
|         self.API_VERSION = 'v3' |  | ||||||
|         self.API_DEFINITION = 'api' |  | ||||||
| 
 |  | ||||||
|     def _gen_request_url(self, url): |  | ||||||
|         return f'{self.API_URL}/{self.API_DEFINITION}/{self.API_VERSION}{url}' |  | ||||||
| 
 |  | ||||||
|     def call_api(self, url, method='GET', headers={}, data={}): |  | ||||||
|         # set conn params |  | ||||||
|         attempt = API_COUNT_TRY_CONNECTIONS |  | ||||||
|         uri = self._gen_request_url(url) |  | ||||||
|         headers = self.API_HEADERS if not headers else headers |  | ||||||
|         params_str = urllib.parse.urlencode(data, safe="+'()") |  | ||||||
|         # connect |  | ||||||
|         while attempt: |  | ||||||
|             attempt -= 1 |  | ||||||
|             try: |  | ||||||
|                 if method == 'POST': |  | ||||||
|                     api_request = requests.post( |  | ||||||
|                         url=uri, |  | ||||||
|                         json=data, |  | ||||||
|                         headers=headers, |  | ||||||
|                         verify=self.API_VERIFY_SSL |  | ||||||
|                     ) |  | ||||||
|                 if method == 'GET': |  | ||||||
|                     uri = f'{uri}?{params_str}' if params_str else uri |  | ||||||
|                     api_request = requests.get( |  | ||||||
|                         url=uri, |  | ||||||
|                         headers=headers, |  | ||||||
|                         verify=self.API_VERIFY_SSL |  | ||||||
|                     ) |  | ||||||
|             except Exception as error: |  | ||||||
|                 ConnectionError = requests.exceptions.ConnectionError |  | ||||||
|                 if type(error) is ConnectionError: |  | ||||||
|                     if attempt == 0: |  | ||||||
|                         click.echo(f'Error: {type(error).__name__}') |  | ||||||
|                         click.echo(f'mgrctl cannot connect to {self.API_URL}') |  | ||||||
|                         sys.exit() |  | ||||||
|                     else: |  | ||||||
|                         continue |  | ||||||
|                 else: |  | ||||||
|                     click.echo(f'Error: {type(error).__name__}') |  | ||||||
|                     sys.exit() |  | ||||||
| 
 |  | ||||||
|             # Get response: |  | ||||||
|             response = self._parse_response(api_request) |  | ||||||
| 
 |  | ||||||
|             # Validate response: |  | ||||||
|             if self._error_handler(response): |  | ||||||
|                 continue  # new attempt connection |  | ||||||
| 
 |  | ||||||
|             return response |  | ||||||
| 
 |  | ||||||
|     def _parse_response(self, api_request): |  | ||||||
|         try: |  | ||||||
|             response = json.loads(api_request.text) |  | ||||||
|             return response |  | ||||||
|         except json.decoder.JSONDecodeError: |  | ||||||
|             click.echo('Error: Invalid API response') |  | ||||||
|             raise sys.exit() |  | ||||||
| 
 |  | ||||||
|     def _is_error(self, response): |  | ||||||
|         if response.get('error'): |  | ||||||
|             return True |  | ||||||
| 
 |  | ||||||
|     def _is_error_3004(self, error): |  | ||||||
|         if error.get('code') == 3004: |  | ||||||
|             sleep(2)  # wait 2 second timeout |  | ||||||
|             return True |  | ||||||
| 
 |  | ||||||
|     def _error_handler(self, response): |  | ||||||
|         if self._is_error(response): |  | ||||||
|             error = response.get('error') |  | ||||||
|             if self._is_error_3004(error): |  | ||||||
|                 return True |  | ||||||
|             else: |  | ||||||
|                 click.echo(f'Error: API return {response}') |  | ||||||
|                 raise sys.exit() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class BaseAuthAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_VERSION = 'v4' |  | ||||||
|         self.API_DEFINITION = 'auth' |  | ||||||
| 
 |  | ||||||
|     def get_auth_token(self, email=None, password=None) -> dict: |  | ||||||
|         email = self.API_EMAIL if not email else email |  | ||||||
|         password = self.API_PASSWORD if not password else password |  | ||||||
|         return self.call_api( |  | ||||||
|             url='/public/token', |  | ||||||
|             method='POST', |  | ||||||
|             data={'email': email, 'password': password} |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|     def get_auth_key(self, token=None, user=None) -> dict: |  | ||||||
|         headers = {} |  | ||||||
|         user = self.API_EMAIL if not user else user |  | ||||||
|         if token: |  | ||||||
|             headers = self.make_auth_header(token) |  | ||||||
|         return self.call_api( |  | ||||||
|             url=f'/user/{user}/key', |  | ||||||
|             method='POST', |  | ||||||
|             headers=headers |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|     def make_auth_header(self, token: str) -> dict: |  | ||||||
|         return {'x-xsrf-token': token} |  | ||||||
| 
 |  | ||||||
|     def whoami(self) -> dict: |  | ||||||
|         return self.call_api( |  | ||||||
|             url='/whoami', |  | ||||||
|             method='GET' |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class BaseIpAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_DEFINITION = 'ip' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class BaseDnsProxyAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_DEFINITION = 'dnsproxy' |  | ||||||
| @ -1,51 +0,0 @@ | |||||||
| from mgrctl.api.base import BaseAPI, BaseAuthAPI, BaseDnsProxyAPI, BaseIpAPI |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class AuthAPI(BaseAuthAPI): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class BackupAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_VERSION = 'v4' |  | ||||||
|         self.API_DEFINITION = 'backup' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DnsProxyAPI(BaseDnsProxyAPI): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class EserviceAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_DEFINITION = 'eservice' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class IsoAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_DEFINITION = 'iso' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class IpmiAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_DEFINITION = 'ipmiproxy' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class IpAPI(BaseIpAPI): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ReportAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_VERSION = 'v4' |  | ||||||
|         self.API_DEFINITION = 'report' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class UpdaterAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_DEFINITION = 'updater' |  | ||||||
| @ -1,19 +0,0 @@ | |||||||
| from mgrctl.api.base import BaseAPI, BaseAuthAPI, BaseDnsProxyAPI, BaseIpAPI |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class AuthAPI(BaseAuthAPI): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DnsProxyAPI(BaseDnsProxyAPI): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class IpAPI(BaseIpAPI): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class VmAPI(BaseAPI): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.API_DEFINITION = 'vm' |  | ||||||
| @ -1,93 +0,0 @@ | |||||||
| import click |  | ||||||
| 
 |  | ||||||
| from mgrctl.api.dci6 import AuthAPI |  | ||||||
| from mgrctl.utils.api_users import UserAPI |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| user_cursor = UserAPI(callback_class=AuthAPI) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group(help='auth cmd for auth in DCImanager 6') |  | ||||||
| def cli(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @cli.group(help='Manage users') |  | ||||||
| def user(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @user.command(help='List users') |  | ||||||
| @click.option( |  | ||||||
|     '--all', |  | ||||||
|     is_flag=True, |  | ||||||
|     required=False, |  | ||||||
|     help='Show all users' |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--admins', |  | ||||||
|     is_flag=True, |  | ||||||
|     required=False, |  | ||||||
|     help='Show all active admins', |  | ||||||
| ) |  | ||||||
| def ls(all, admins): |  | ||||||
|     if all: |  | ||||||
|         users = user_cursor.get_users(role='all') |  | ||||||
|     elif admins: |  | ||||||
|         users = user_cursor.get_users(role='admin') |  | ||||||
|     else: |  | ||||||
|         users = user_cursor.get_users(role='all') |  | ||||||
|     # print users: |  | ||||||
|     user_cursor.echo_users(users) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @user.command( |  | ||||||
|     help='Generate an access key and return auth link(s)', |  | ||||||
|     no_args_is_help=True |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--id', |  | ||||||
|     '_id', |  | ||||||
|     required=False, |  | ||||||
|     type=int, |  | ||||||
|     help='User id' |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--count', |  | ||||||
|     required=False, |  | ||||||
|     type=int, |  | ||||||
|     help='Number of access keys generated', |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--random', |  | ||||||
|     is_flag=True, |  | ||||||
|     required=False, |  | ||||||
|     help='Generate access key for the first available admin' |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--interactive', |  | ||||||
|     is_flag=True, |  | ||||||
|     required=False, |  | ||||||
|     help='Interactive mode, ignores other keys' |  | ||||||
| ) |  | ||||||
| def access(_id, count, interactive, random): |  | ||||||
|     if _id and not count: |  | ||||||
|         keys = user_cursor.get_access_keys(user=_id, count=1) |  | ||||||
|     elif _id and count: |  | ||||||
|         keys = user_cursor.get_access_keys(user=_id, count=count) |  | ||||||
|     elif random: |  | ||||||
|         admin = user_cursor.get_first_random_admin() |  | ||||||
|         keys = user_cursor.get_access_keys(user=admin.get('id', 3)) |  | ||||||
|     elif interactive: |  | ||||||
|         user_cursor.gen_access_links_interactive() |  | ||||||
|         return  # exit from func |  | ||||||
|     else: |  | ||||||
|         pass |  | ||||||
|     links = user_cursor.gen_access_links(keys) |  | ||||||
|     user_cursor.echo_access_links(links) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @user.command(help='Generate API token for mgrctl user') |  | ||||||
| def token(): |  | ||||||
|     token = user_cursor.gen_api_token() |  | ||||||
|     user_cursor.echo_api_token(token) |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| import click |  | ||||||
| 
 |  | ||||||
| from mgrctl.cli.lazy_group import LazyGroup |  | ||||||
| from mgrctl.settings.general import INSTALLED_APPS |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group( |  | ||||||
|     cls=LazyGroup, |  | ||||||
|     lazy_subcommands=INSTALLED_APPS['dci6'], |  | ||||||
|     help='dci6 command for DCI6manager management', |  | ||||||
| ) |  | ||||||
| def cli(): |  | ||||||
|     pass |  | ||||||
| @ -1,93 +0,0 @@ | |||||||
| import click |  | ||||||
| 
 |  | ||||||
| from mgrctl.api.vm6 import AuthAPI |  | ||||||
| from mgrctl.utils.api_users import UserAPI |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| user_cursor = UserAPI(callback_class=AuthAPI) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group(help='auth cmd for auth in VMmanager 6') |  | ||||||
| def cli(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @cli.group(help='Manage users') |  | ||||||
| def user(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @user.command(help='List users') |  | ||||||
| @click.option( |  | ||||||
|     '--all', |  | ||||||
|     is_flag=True, |  | ||||||
|     required=False, |  | ||||||
|     help='Show all users' |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--admins', |  | ||||||
|     is_flag=True, |  | ||||||
|     required=False, |  | ||||||
|     help='Show all active admins', |  | ||||||
| ) |  | ||||||
| def ls(all, admins): |  | ||||||
|     if all: |  | ||||||
|         users = user_cursor.get_users(role='all') |  | ||||||
|     elif admins: |  | ||||||
|         users = user_cursor.get_users(role='admin') |  | ||||||
|     else: |  | ||||||
|         users = user_cursor.get_users(role='all') |  | ||||||
|     # print users: |  | ||||||
|     user_cursor.echo_users(users) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @user.command( |  | ||||||
|     help='Generate an access key and return auth link(s)', |  | ||||||
|     no_args_is_help=True |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--id', |  | ||||||
|     '_id', |  | ||||||
|     required=False, |  | ||||||
|     type=int, |  | ||||||
|     help='User id' |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--count', |  | ||||||
|     required=False, |  | ||||||
|     type=int, |  | ||||||
|     help='Number of access keys generated', |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--random', |  | ||||||
|     is_flag=True, |  | ||||||
|     required=False, |  | ||||||
|     help='Generate access key for the first available admin' |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--interactive', |  | ||||||
|     is_flag=True, |  | ||||||
|     required=False, |  | ||||||
|     help='Interactive mode, ignores other keys' |  | ||||||
| ) |  | ||||||
| def access(_id, count, interactive, random): |  | ||||||
|     if _id and not count: |  | ||||||
|         keys = user_cursor.get_access_keys(user=_id, count=1) |  | ||||||
|     elif _id and count: |  | ||||||
|         keys = user_cursor.get_access_keys(user=_id, count=count) |  | ||||||
|     elif random: |  | ||||||
|         admin = user_cursor.get_first_random_admin() |  | ||||||
|         keys = user_cursor.get_access_keys(user=admin.get('id', 3)) |  | ||||||
|     elif interactive: |  | ||||||
|         user_cursor.gen_access_links_interactive() |  | ||||||
|         return  # exit from func |  | ||||||
|     else: |  | ||||||
|         pass |  | ||||||
|     links = user_cursor.gen_access_links(keys) |  | ||||||
|     user_cursor.echo_access_links(links) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @user.command(help='Generate API token for mgrctl user') |  | ||||||
| def token(): |  | ||||||
|     token = user_cursor.gen_api_token() |  | ||||||
|     user_cursor.echo_api_token(token) |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| import click |  | ||||||
| 
 |  | ||||||
| from mgrctl.cli.lazy_group import LazyGroup |  | ||||||
| from mgrctl.settings.general import INSTALLED_APPS |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group( |  | ||||||
|     cls=LazyGroup, |  | ||||||
|     lazy_subcommands=INSTALLED_APPS['vm6'], |  | ||||||
|     help='vm6 command for VM6manager management', |  | ||||||
| ) |  | ||||||
| def cli(): |  | ||||||
|     pass |  | ||||||
| @ -1,32 +0,0 @@ | |||||||
| #!/usr/bin/env python3 |  | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| 
 |  | ||||||
| # █▀▄▀█ █▀▀ █▀█ █▀▀ ▀█▀ █░░ ▀ |  | ||||||
| # █░▀░█ █▄█ █▀▄ █▄▄ ░█░ █▄▄ ▄ |  | ||||||
| # -- -- -- -- -- -- -- -- -- |  | ||||||
| 
 |  | ||||||
| import click |  | ||||||
| 
 |  | ||||||
| from mgrctl import __version__ |  | ||||||
| from mgrctl.cli.lazy_group import LazyGroup |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group( |  | ||||||
|     cls=LazyGroup, |  | ||||||
|     lazy_subcommands={ |  | ||||||
|         'vm6': 'mgrctl.apps.vm6.commands.cli', |  | ||||||
|         'dci6': 'mgrctl.apps.dci6.commands.cli', |  | ||||||
|     }, |  | ||||||
|     help='main CLI command for mgrctl app', |  | ||||||
| ) |  | ||||||
| @click.version_option( |  | ||||||
|     version=__version__, |  | ||||||
|     package_name='mgrctl', |  | ||||||
|     message=__version__ |  | ||||||
| ) |  | ||||||
| def cli(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     cli() |  | ||||||
| @ -1,7 +0,0 @@ | |||||||
| from mgrctl.settings.general import BASE_DIR |  | ||||||
| 
 |  | ||||||
| from mgrctl.settings.platform import ( |  | ||||||
|     PLATFORM_TYPE, |  | ||||||
|     PLATFORM_URL, |  | ||||||
|     PLATFORM_CONFIG |  | ||||||
| ) |  | ||||||
| @ -1,47 +0,0 @@ | |||||||
| from requests.packages import urllib3 |  | ||||||
| 
 |  | ||||||
| from mgrctl.settings.environment import env |  | ||||||
| from mgrctl.settings.platform import ( |  | ||||||
|     PLATFORM_TYPE, |  | ||||||
|     PLATFORM_VERIFY_SSL, |  | ||||||
|     PLATFORM_VERIFY_SSL_WARNING, |  | ||||||
|     PLATFORM_DUMMY, |  | ||||||
|     PLATFORM_DUMMY_API_URL, |  | ||||||
|     PLATFORM_DUMMY_EMAIL, |  | ||||||
|     PLATFORM_DUMMY_PASSWORD, |  | ||||||
|     PLATFORM_DUMMY_TOKEN, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| # Name of nginx container: |  | ||||||
| API_INPUT_HOSTNAME = 'input' |  | ||||||
| 
 |  | ||||||
| # Port that nginx container is listening: |  | ||||||
| API_INPUT_PORT = '1500' |  | ||||||
| 
 |  | ||||||
| # Internal API url: |  | ||||||
| API_URL = f'http://{API_INPUT_HOSTNAME}:{API_INPUT_PORT}' |  | ||||||
| 
 |  | ||||||
| # Headers for internal auth: |  | ||||||
| API_HEADERS = {"Internal-Auth": "on", "Accept": "application/json"} |  | ||||||
| 
 |  | ||||||
| # Alias for import: |  | ||||||
| API_VERIFY_SSL = PLATFORM_VERIFY_SSL |  | ||||||
| 
 |  | ||||||
| # API 3004 Unavailable error handler: |  | ||||||
| API_COUNT_TRY_CONNECTIONS = env.int('API_COUNT_TRY_CONNECTIONS', 3) |  | ||||||
| 
 |  | ||||||
| # Suppress warning from urllib3: |  | ||||||
| if not PLATFORM_VERIFY_SSL_WARNING: |  | ||||||
|     # ! This is not recommended, |  | ||||||
|     # ! the user will not see a message about an untrusted SSL connection |  | ||||||
|     urllib3.disable_warnings( |  | ||||||
|         category=urllib3.exceptions.InsecureRequestWarning |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
| # Development mode: |  | ||||||
| if PLATFORM_DUMMY: |  | ||||||
|     API_URL = PLATFORM_DUMMY_API_URL |  | ||||||
|     API_HEADERS = {'x-xsrf-token': PLATFORM_DUMMY_TOKEN} |  | ||||||
| 
 |  | ||||||
| API_EMAIL = PLATFORM_DUMMY_EMAIL |  | ||||||
| API_PASSWORD = PLATFORM_DUMMY_PASSWORD |  | ||||||
| @ -1,41 +0,0 @@ | |||||||
| from mgrctl.settings.environment import env |  | ||||||
| from mgrctl.utils.helpers import parse_json_file |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| PLATFORM_TYPE = env.str('PLATFORM_TYPE', 'vm') |  | ||||||
| 
 |  | ||||||
| PLATFORM_VERIFY_SSL = env.bool('PLATFORM_VERIFY_SSL', True) |  | ||||||
| PLATFORM_VERIFY_SSL_WARNING = env.bool('PLATFORM_VERIFY_SSL_WARNING', True) |  | ||||||
| 
 |  | ||||||
| PLATFORM_CONFIG = env.str( |  | ||||||
|     'PLATFORM_CONFIG', |  | ||||||
|     f'/opt/ispsystem/{PLATFORM_TYPE}/config.json' |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| PLATFORM_CONFIG = parse_json_file(PLATFORM_CONFIG) |  | ||||||
| 
 |  | ||||||
| PLATFORM_URL = env.str( |  | ||||||
|     'PLATFORM_URL', |  | ||||||
|     f"https://{PLATFORM_CONFIG.get('DomainName' ,'replace.me')}" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| # Development mode: |  | ||||||
| PLATFORM_DUMMY = env.bool('PLATFORM_DUMMY', False) |  | ||||||
| 
 |  | ||||||
| if PLATFORM_TYPE == 'vm': |  | ||||||
|     PLATFORM_DUMMY_API_URL = env.str('PLATFORM_DUMMY_VM6_API_URL', '') |  | ||||||
|     PLATFORM_DUMMY_EMAIL = env.str('PLATFORM_DUMMY_VM6_EMAIL', '') |  | ||||||
|     PLATFORM_DUMMY_PASSWORD = env.str('PLATFORM_DUMMY_VM6_PASSWORD', '') |  | ||||||
|     PLATFORM_DUMMY_TOKEN = env.str('PLATFORM_DUMMY_VM6_TOKEN', '') |  | ||||||
| elif PLATFORM_TYPE == 'dci': |  | ||||||
|     PLATFORM_DUMMY_API_URL = env.str('PLATFORM_DUMMY_DCI6_API_URL', '') |  | ||||||
|     PLATFORM_DUMMY_EMAIL = env.str('PLATFORM_DUMMY_DCI6_EMAIL', '') |  | ||||||
|     PLATFORM_DUMMY_PASSWORD = env.str('PLATFORM_DUMMY_DCI6_PASSWORD', '') |  | ||||||
|     PLATFORM_DUMMY_TOKEN = env.str('PLATFORM_DUMMY_DCI6_TOKEN', '') |  | ||||||
| else: |  | ||||||
|     # ? guarantees that constants exist for import |  | ||||||
|     # ? if the user has set the wrong PLATFORM_TYPE: |  | ||||||
|     PLATFORM_DUMMY_API_URL = env.str('PLATFORM_DUMMY_API_URL', '') |  | ||||||
|     PLATFORM_DUMMY_EMAIL = env.str('PLATFORM_DUMMY_EMAIL', '') |  | ||||||
|     PLATFORM_DUMMY_PASSWORD = env.str('PLATFORM_DUMMY_PASSWORD', '') |  | ||||||
|     PLATFORM_DUMMY_TOKEN = env.str('PLATFORM_DUMMY_TOKEN', '') |  | ||||||
| @ -1,92 +0,0 @@ | |||||||
| import click |  | ||||||
| from tabulate import tabulate |  | ||||||
| 
 |  | ||||||
| from mgrctl.settings.platform import PLATFORM_URL |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class UserAPI(object): |  | ||||||
|     def __init__(self, callback_class): |  | ||||||
|         """Announces required parameters""" |  | ||||||
|         self.callback_class = callback_class |  | ||||||
|         self.callback = callback_class() |  | ||||||
| 
 |  | ||||||
|     def get_users(self, role: str) -> list: |  | ||||||
|         data = {} |  | ||||||
|         if role == 'admin': |  | ||||||
|             data = {"where": "((roles+CP+'%@admin%')+AND+(state+EQ+'active'))"} |  | ||||||
|         response = self.callback.call_api( |  | ||||||
|             url='/user', |  | ||||||
|             method='GET', |  | ||||||
|             data=data |  | ||||||
|         ) |  | ||||||
|         users = self._extract_users(users=response) |  | ||||||
|         return users |  | ||||||
| 
 |  | ||||||
|     def _extract_users(self, users: dict) -> list: |  | ||||||
|         return users.get('list', []) |  | ||||||
| 
 |  | ||||||
|     def _format_users(self, users: list) -> list: |  | ||||||
|         output = [] |  | ||||||
|         for user in users: |  | ||||||
|             output.append({ |  | ||||||
|                 'id': user.get('id', ''), |  | ||||||
|                 'email': user.get('email', ''), |  | ||||||
|                 'roles': user.get('roles', []), |  | ||||||
|                 'state': user.get('state', ''), |  | ||||||
|                 # add more fields here... |  | ||||||
|             }) |  | ||||||
|         return output |  | ||||||
| 
 |  | ||||||
|     def get_first_random_admin(self): |  | ||||||
|         users = self.get_users(role='admin') |  | ||||||
|         admin = {} |  | ||||||
|         for user in users: |  | ||||||
|             if '@admin' in user.get('roles', []): |  | ||||||
|                 admin = user |  | ||||||
|                 break |  | ||||||
|         return admin |  | ||||||
| 
 |  | ||||||
|     def echo_users(self, users: list) -> None: |  | ||||||
|         output = self._format_users(users) |  | ||||||
|         click.echo(tabulate(output, headers='keys')) |  | ||||||
| 
 |  | ||||||
|     def get_access_keys(self, user, count=1): |  | ||||||
|         keys = [] |  | ||||||
|         while count >= 1: |  | ||||||
|             count -= 1 |  | ||||||
|             key = self.callback.get_auth_key(user=user) |  | ||||||
|             check = key.get('key', '') |  | ||||||
|             if check: |  | ||||||
|                 keys.append(key) |  | ||||||
|         return keys |  | ||||||
| 
 |  | ||||||
|     def gen_access_links(self, keys: list) -> list: |  | ||||||
|         links = [] |  | ||||||
|         for key in keys: |  | ||||||
|             _id = key.get('id', '') |  | ||||||
|             link = f"{PLATFORM_URL}/auth/key/{key.get('key', '')}" |  | ||||||
|             links.append({'id': _id, 'link': link}) |  | ||||||
|         return links |  | ||||||
| 
 |  | ||||||
|     def echo_access_links(self, links: list) -> None: |  | ||||||
|         click.echo(tabulate(links, headers='keys')) |  | ||||||
| 
 |  | ||||||
|     def gen_access_links_interactive(self) -> None: |  | ||||||
|         users = self.get_users(role='admin') |  | ||||||
|         self.echo_users(users) |  | ||||||
|         try: |  | ||||||
|             click.echo('Choose user id and count of keys') |  | ||||||
|             _id = int(input('User ID: ')) |  | ||||||
|             count = int(input('Count of keys: ')) |  | ||||||
|             keys = self.get_access_keys(user=_id, count=count) |  | ||||||
|             links = self.gen_access_links(keys) |  | ||||||
|             self.echo_access_links(links) |  | ||||||
|         except ValueError: |  | ||||||
|             click.echo('Error: Invalid, value is not a valid integer') |  | ||||||
| 
 |  | ||||||
|     def gen_api_token(self, email=None, password=None): |  | ||||||
|         token = self.callback.get_auth_token(email, password) |  | ||||||
|         return token |  | ||||||
| 
 |  | ||||||
|     def echo_api_token(self, token: dict) -> None: |  | ||||||
|         click.echo(tabulate([token], headers='keys')) |  | ||||||
							
								
								
									
										1618
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1618
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,29 +1,22 @@ | |||||||
| [tool.poetry] | [tool.poetry] | ||||||
| name = "mgrctl" | name = "isp-maintenance" | ||||||
| version = "0.1.1" | version = "0.1.0" | ||||||
| description = "Maintenance service for ISPsystem platforms" | description = "Maintenance service for ISPsystem platforms" | ||||||
| authors = [ | authors = ["MOIS3Y <s.zhukovskii@ispsystem.com>", "Failak3 <v.shmidt@ispsystem.com>", "a.garaev <a.garaev@ispsystem.com>", "Ann_M <a.moskovkina@ispsystem.com>"] | ||||||
|     "MOIS3Y <s.zhukovskii@ispsystem.com>", |  | ||||||
|     "a.garaev <a.garaev@ispsystem.com>", |  | ||||||
| ] |  | ||||||
| license = "MIT" | license = "MIT" | ||||||
| readme = "README.md" | readme = "README.md" | ||||||
| 
 | 
 | ||||||
| [tool.poetry.dependencies] | [tool.poetry.dependencies] | ||||||
| python = "^3.11" | python = "^3.11" | ||||||
|  | peewee = "^3.17.0" | ||||||
| click = "^8.1.7" | click = "^8.1.7" | ||||||
| requests = "^2.31.0" | requests = "^2.31.0" | ||||||
| environs = "^12.0.0" | environs = "^10.3.0" | ||||||
| sh = "^2.0.7" |  | ||||||
| tabulate = "^0.9.0" |  | ||||||
| 
 | 
 | ||||||
| [tool.poetry.group.dev.dependencies] | [tool.poetry.group.dev.dependencies] | ||||||
| flake8 = "^7.0.0" | flake8 = "^7.0.0" | ||||||
| poetry-plugin-export = "^1.6.0" |  | ||||||
| 
 |  | ||||||
| [tool.poetry.scripts] |  | ||||||
| mgrctl = 'mgrctl.mgrctl:cli' |  | ||||||
| 
 | 
 | ||||||
| [build-system] | [build-system] | ||||||
| requires = ["poetry-core"] | requires = ["poetry-core"] | ||||||
| build-backend = "poetry.core.masonry.api" | build-backend = "poetry.core.masonry.api" | ||||||
|  | 
 | ||||||
|  | |||||||
							
								
								
									
										243
									
								
								requirements.txt
									
									
									
									
									
								
							
							
						
						
									
										243
									
								
								requirements.txt
									
									
									
									
									
								
							| @ -1,126 +1,123 @@ | |||||||
| certifi==2025.4.26 ; python_version >= "3.11" and python_version < "4.0" \ | certifi==2024.2.2 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
|     --hash=sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6 \ |     --hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \ | ||||||
|     --hash=sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3 |     --hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1 | ||||||
| charset-normalizer==3.4.2 ; python_version >= "3.11" and python_version < "4.0" \ | charset-normalizer==3.3.2 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
|     --hash=sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4 \ |     --hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \ | ||||||
|     --hash=sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45 \ |     --hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \ | ||||||
|     --hash=sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7 \ |     --hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \ | ||||||
|     --hash=sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0 \ |     --hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \ | ||||||
|     --hash=sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7 \ |     --hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \ | ||||||
|     --hash=sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d \ |     --hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \ | ||||||
|     --hash=sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d \ |     --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \ | ||||||
|     --hash=sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0 \ |     --hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \ | ||||||
|     --hash=sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184 \ |     --hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \ | ||||||
|     --hash=sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db \ |     --hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \ | ||||||
|     --hash=sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b \ |     --hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \ | ||||||
|     --hash=sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64 \ |     --hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \ | ||||||
|     --hash=sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b \ |     --hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \ | ||||||
|     --hash=sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8 \ |     --hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \ | ||||||
|     --hash=sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff \ |     --hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \ | ||||||
|     --hash=sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344 \ |     --hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \ | ||||||
|     --hash=sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58 \ |     --hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \ | ||||||
|     --hash=sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e \ |     --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \ | ||||||
|     --hash=sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471 \ |     --hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \ | ||||||
|     --hash=sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148 \ |     --hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \ | ||||||
|     --hash=sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a \ |     --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \ | ||||||
|     --hash=sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836 \ |     --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \ | ||||||
|     --hash=sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e \ |     --hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \ | ||||||
|     --hash=sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63 \ |     --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \ | ||||||
|     --hash=sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c \ |     --hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \ | ||||||
|     --hash=sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1 \ |     --hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \ | ||||||
|     --hash=sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01 \ |     --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \ | ||||||
|     --hash=sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366 \ |     --hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \ | ||||||
|     --hash=sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58 \ |     --hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \ | ||||||
|     --hash=sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5 \ |     --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \ | ||||||
|     --hash=sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c \ |     --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \ | ||||||
|     --hash=sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2 \ |     --hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \ | ||||||
|     --hash=sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a \ |     --hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \ | ||||||
|     --hash=sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597 \ |     --hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \ | ||||||
|     --hash=sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b \ |     --hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \ | ||||||
|     --hash=sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5 \ |     --hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \ | ||||||
|     --hash=sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb \ |     --hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \ | ||||||
|     --hash=sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f \ |     --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \ | ||||||
|     --hash=sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0 \ |     --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \ | ||||||
|     --hash=sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941 \ |     --hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \ | ||||||
|     --hash=sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0 \ |     --hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \ | ||||||
|     --hash=sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86 \ |     --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \ | ||||||
|     --hash=sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7 \ |     --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \ | ||||||
|     --hash=sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7 \ |     --hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \ | ||||||
|     --hash=sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455 \ |     --hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \ | ||||||
|     --hash=sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6 \ |     --hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \ | ||||||
|     --hash=sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4 \ |     --hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \ | ||||||
|     --hash=sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0 \ |     --hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \ | ||||||
|     --hash=sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3 \ |     --hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \ | ||||||
|     --hash=sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1 \ |     --hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \ | ||||||
|     --hash=sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6 \ |     --hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \ | ||||||
|     --hash=sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981 \ |     --hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \ | ||||||
|     --hash=sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c \ |     --hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \ | ||||||
|     --hash=sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980 \ |     --hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \ | ||||||
|     --hash=sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645 \ |     --hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \ | ||||||
|     --hash=sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7 \ |     --hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \ | ||||||
|     --hash=sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12 \ |     --hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \ | ||||||
|     --hash=sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa \ |     --hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \ | ||||||
|     --hash=sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd \ |     --hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \ | ||||||
|     --hash=sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef \ |     --hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \ | ||||||
|     --hash=sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f \ |     --hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \ | ||||||
|     --hash=sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2 \ |     --hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \ | ||||||
|     --hash=sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d \ |     --hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \ | ||||||
|     --hash=sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5 \ |     --hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \ | ||||||
|     --hash=sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02 \ |     --hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \ | ||||||
|     --hash=sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3 \ |     --hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \ | ||||||
|     --hash=sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd \ |     --hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \ | ||||||
|     --hash=sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e \ |     --hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \ | ||||||
|     --hash=sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214 \ |     --hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \ | ||||||
|     --hash=sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd \ |     --hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \ | ||||||
|     --hash=sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a \ |     --hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \ | ||||||
|     --hash=sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c \ |     --hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \ | ||||||
|     --hash=sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681 \ |     --hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \ | ||||||
|     --hash=sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba \ |     --hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \ | ||||||
|     --hash=sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f \ |     --hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \ | ||||||
|     --hash=sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a \ |     --hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \ | ||||||
|     --hash=sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28 \ |     --hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \ | ||||||
|     --hash=sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691 \ |     --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \ | ||||||
|     --hash=sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82 \ |     --hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \ | ||||||
|     --hash=sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a \ |     --hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \ | ||||||
|     --hash=sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027 \ |     --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \ | ||||||
|     --hash=sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7 \ |     --hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \ | ||||||
|     --hash=sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518 \ |     --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \ | ||||||
|     --hash=sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf \ |     --hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \ | ||||||
|     --hash=sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b \ |     --hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \ | ||||||
|     --hash=sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9 \ |     --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \ | ||||||
|     --hash=sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544 \ |     --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \ | ||||||
|     --hash=sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da \ |     --hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \ | ||||||
|     --hash=sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509 \ |     --hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \ | ||||||
|     --hash=sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f \ |     --hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561 | ||||||
|     --hash=sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a \ | click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
|     --hash=sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f |     --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ | ||||||
| click==8.1.8 ; python_version >= "3.11" and python_version < "4.0" \ |     --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de | ||||||
|     --hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \ |  | ||||||
|     --hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a |  | ||||||
| colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and platform_system == "Windows" \ | colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and platform_system == "Windows" \ | ||||||
|     --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ |     --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ | ||||||
|     --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 |     --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 | ||||||
| environs==12.0.0 ; python_version >= "3.11" and python_version < "4.0" \ | environs==10.3.0 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
|     --hash=sha256:152068ba62185b99d87df5f6d6044c0e533261777119037e718c1397fc7d697f \ |     --hash=sha256:cc421ddb143fa30183568164755aa113a160e555cd19e97e664c478662032c24 \ | ||||||
|     --hash=sha256:6150ddeb05562a80cc789b308c650d33b98a5f9c3f4c63d9ed465c433f1cb9e2 |     --hash=sha256:feeaf28f17fd0499f9cd7c0fcf408c6d82c308e69e335eb92d09322fc9ed8138 | ||||||
| idna==3.10 ; python_version >= "3.11" and python_version < "4.0" \ | idna==3.6 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
|     --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ |     --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ | ||||||
|     --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 |     --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f | ||||||
| marshmallow==4.0.0 ; python_version >= "3.11" and python_version < "4.0" \ | marshmallow==3.20.2 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
|     --hash=sha256:3b6e80aac299a7935cfb97ed01d1854fb90b5079430969af92118ea1b12a8d55 \ |     --hash=sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd \ | ||||||
|     --hash=sha256:e7b0528337e9990fd64950f8a6b3a1baabed09ad17a0dfb844d701151f92d203 |     --hash=sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9 | ||||||
| python-dotenv==1.1.0 ; python_version >= "3.11" and python_version < "4.0" \ | packaging==23.2 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
|     --hash=sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5 \ |     --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \ | ||||||
|     --hash=sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d |     --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7 | ||||||
| requests==2.32.3 ; python_version >= "3.11" and python_version < "4.0" \ | peewee==3.17.1 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
|     --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ |     --hash=sha256:e009ac4227c4fdc0058a56e822ad5987684f0a1fbb20fed577200785102581c3 | ||||||
|     --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 | python-dotenv==1.0.1 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
| sh==2.2.2 ; python_version >= "3.11" and python_version < "4.0" \ |     --hash=sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca \ | ||||||
|     --hash=sha256:653227a7c41a284ec5302173fbc044ee817c7bad5e6e4d8d55741b9aeb9eb65b \ |     --hash=sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a | ||||||
|     --hash=sha256:e0b15b4ae8ffcd399bc8ffddcbd770a43c7a70a24b16773fbb34c001ad5d52af | requests==2.31.0 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
| tabulate==0.9.0 ; python_version >= "3.11" and python_version < "4.0" \ |     --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ | ||||||
|     --hash=sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c \ |     --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 | ||||||
|     --hash=sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f | urllib3==2.2.0 ; python_version >= "3.11" and python_version < "4.0" \ | ||||||
| urllib3==2.4.0 ; python_version >= "3.11" and python_version < "4.0" \ |     --hash=sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20 \ | ||||||
|     --hash=sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466 \ |     --hash=sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224 | ||||||
|     --hash=sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813 |  | ||||||
|  | |||||||
| @ -1,10 +0,0 @@ | |||||||
| # █▀▄▀█ █▀▀ ▀█▀ ▄▀█ ▀ |  | ||||||
| # █░▀░█ ██▄ ░█░ █▀█ ▄ |  | ||||||
| # -- -- -- -- -- -- - |  | ||||||
| __author__ = "MOIS3Y" |  | ||||||
| __credits__ = ["Stepan Zhukovsky"] |  | ||||||
| __license__ = "MIT" |  | ||||||
| __version__ = "0.1.0" |  | ||||||
| __maintainer__ = "Stepan Zhukovsky" |  | ||||||
| __email__ = "stepan@zhukovsky.me" |  | ||||||
| __status__ = "Development" |  | ||||||
| @ -1,50 +0,0 @@ | |||||||
| #!/usr/bin/env python3 |  | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| import click |  | ||||||
| import sh |  | ||||||
| 
 |  | ||||||
| from .lazy_group import LazyGroup |  | ||||||
| from .utils import abort_if_false |  | ||||||
| from . import settings |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group() |  | ||||||
| def cli1(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group( |  | ||||||
|     cls=LazyGroup, |  | ||||||
|     lazy_subcommands={'docker-compose': 'cli.builder.docker_compose'}, |  | ||||||
| ) |  | ||||||
| def cli2(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group(help='cmd for build/destroy project in docker containers') |  | ||||||
| def docker_compose(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @docker_compose.command(help='cmd for build project in docker containers') |  | ||||||
| def build(): |  | ||||||
|     with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): |  | ||||||
|         sh.docker_compose('-f', settings.COMPOSE_FILE, 'build', _fg=True) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @docker_compose.command(help='cmd for destroy project in docker containers') |  | ||||||
| @click.option( |  | ||||||
|     '--yes', |  | ||||||
|     is_flag=True, |  | ||||||
|     callback=abort_if_false, |  | ||||||
|     expose_value=False, |  | ||||||
|     prompt='Are you sure you want to destroy docker containers') |  | ||||||
| def destroy(): |  | ||||||
|     with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): |  | ||||||
|         sh.docker_compose('-f', settings.COMPOSE_FILE, 'down',  _fg=True) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| cli = click.CommandCollection( |  | ||||||
|     sources=[cli1, cli2], |  | ||||||
|     help='cmd for building the project' |  | ||||||
| ) |  | ||||||
| @ -1,39 +0,0 @@ | |||||||
| import importlib |  | ||||||
| import click |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class LazyGroup(click.Group): |  | ||||||
|     def __init__(self, *args, lazy_subcommands=None, **kwargs): |  | ||||||
|         super().__init__(*args, **kwargs) |  | ||||||
|         # lazy_subcommands is a map of the form: |  | ||||||
|         # |  | ||||||
|         #   {command-name} -> {module-name}.{command-object-name} |  | ||||||
|         # |  | ||||||
|         self.lazy_subcommands = lazy_subcommands or {} |  | ||||||
| 
 |  | ||||||
|     def list_commands(self, ctx): |  | ||||||
|         base = super().list_commands(ctx) |  | ||||||
|         lazy = sorted(self.lazy_subcommands.keys()) |  | ||||||
|         return base + lazy |  | ||||||
| 
 |  | ||||||
|     def get_command(self, ctx, cmd_name): |  | ||||||
|         if cmd_name in self.lazy_subcommands: |  | ||||||
|             return self._lazy_load(cmd_name) |  | ||||||
|         return super().get_command(ctx, cmd_name) |  | ||||||
| 
 |  | ||||||
|     def _lazy_load(self, cmd_name): |  | ||||||
|         # lazily loading a command, |  | ||||||
|         # first get the module name and attribute name |  | ||||||
|         import_path = self.lazy_subcommands[cmd_name] |  | ||||||
|         modname, cmd_object_name = import_path.rsplit(".", 1) |  | ||||||
|         # do the import |  | ||||||
|         mod = importlib.import_module(modname) |  | ||||||
|         # get the Command object from that module |  | ||||||
|         cmd_object = getattr(mod, cmd_object_name) |  | ||||||
|         # check the result to make debugging easier |  | ||||||
|         if not isinstance(cmd_object, click.BaseCommand): |  | ||||||
|             raise ValueError( |  | ||||||
|                 f"Lazy loading of {import_path} failed by returning " |  | ||||||
|                 "a non-command object" |  | ||||||
|             ) |  | ||||||
|         return cmd_object |  | ||||||
| @ -1,57 +0,0 @@ | |||||||
| #!/usr/bin/env python3 |  | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| import click |  | ||||||
| import sh |  | ||||||
| 
 |  | ||||||
| from .lazy_group import LazyGroup |  | ||||||
| from . import settings |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group() |  | ||||||
| def cli1(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group( |  | ||||||
|     cls=LazyGroup, |  | ||||||
|     lazy_subcommands={'docker-compose': 'cli.runner.docker_compose'}, |  | ||||||
| ) |  | ||||||
| def cli2(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group(help='cmd for run/stop/restart project in docker containers') |  | ||||||
| def docker_compose(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @docker_compose.command(help='cmd for run project docker containers') |  | ||||||
| def up(): |  | ||||||
|     with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): |  | ||||||
|         sh.docker_compose('-f', settings.COMPOSE_FILE, 'up', '-d', _fg=True) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @docker_compose.command(help='cmd for down project docker containers') |  | ||||||
| def down(): |  | ||||||
|     with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): |  | ||||||
|         sh.docker_compose('-f', settings.COMPOSE_FILE, 'down', _fg=True) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @docker_compose.command(help='cmd for restart project docker containers') |  | ||||||
| def restart(): |  | ||||||
|     with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): |  | ||||||
|         sh.docker_compose( |  | ||||||
|             '-f', |  | ||||||
|             settings.COMPOSE_FILE, |  | ||||||
|             'up', |  | ||||||
|             '-d', |  | ||||||
|             '--build', |  | ||||||
|             '--force-recreate', |  | ||||||
|             _fg=True |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| cli = click.CommandCollection( |  | ||||||
|     sources=[cli1, cli2], |  | ||||||
|     help='cmd for running the project standalone or docker-compose' |  | ||||||
| ) |  | ||||||
| @ -1,18 +0,0 @@ | |||||||
| import pathlib |  | ||||||
| from environs import Env |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Path: |  | ||||||
| ROOT_DIR = pathlib.Path(__file__).resolve().parent.parent.parent.parent |  | ||||||
| PROJECT_DIR = ROOT_DIR / 'mgrctl' |  | ||||||
| COMPOSE_FILE = ROOT_DIR / 'docker-compose.yml' |  | ||||||
| 
 |  | ||||||
| # Init environment: |  | ||||||
| env = Env() |  | ||||||
| 
 |  | ||||||
| # read .env file, if it exists |  | ||||||
| # reed more about .env file here: https://github.com/sloria/environs |  | ||||||
| env.read_env(path=ROOT_DIR / '.env') |  | ||||||
| 
 |  | ||||||
| # ! insecure save sudo password wherever |  | ||||||
| SUDO_PASSWORD = env.str('SUDO_PASSWORD', None) |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| import click |  | ||||||
| 
 |  | ||||||
| from . import __version__ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def print_version(ctx, param, value): |  | ||||||
|     if not value or ctx.resilient_parsing: |  | ||||||
|         return |  | ||||||
|     click.echo(__version__) |  | ||||||
|     ctx.exit() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def abort_if_false(ctx, param, value): |  | ||||||
|     if not value: |  | ||||||
|         ctx.abort() |  | ||||||
| @ -1,30 +0,0 @@ | |||||||
| #!/usr/bin/env python3 |  | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| import click |  | ||||||
| 
 |  | ||||||
| from cli.lazy_group import LazyGroup |  | ||||||
| from cli.utils import print_version |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @click.group( |  | ||||||
|     cls=LazyGroup, |  | ||||||
|     lazy_subcommands={ |  | ||||||
|         'builder': 'cli.builder.cli', |  | ||||||
|         'runner': 'cli.runner.cli', |  | ||||||
|     }, |  | ||||||
|     help='dev.py CLI tool for dev actions', |  | ||||||
| ) |  | ||||||
| @click.option( |  | ||||||
|     '--version', |  | ||||||
|     is_flag=True, |  | ||||||
|     callback=print_version, |  | ||||||
|     expose_value=False, |  | ||||||
|     is_eager=True, |  | ||||||
|     help='Print version and exit' |  | ||||||
| ) |  | ||||||
| def cli(): |  | ||||||
|     pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     cli() |  | ||||||
| @ -1,512 +0,0 @@ | |||||||
| #!/usr/bin/env bash |  | ||||||
| 
 |  | ||||||
| # █▀▀ █▀█ █▀▀ █▀█ ▀ |  | ||||||
| # █▄█ █▄█ █▄█ █▄█ ▄ |  | ||||||
| # -- -- -- -- -- -- |  | ||||||
| 
 |  | ||||||
| # INIT GLOBAL VARIABLES: |  | ||||||
| _VERSION="0.1.1" |  | ||||||
| _SCRIPT_NAME="$(basename $0)" |  | ||||||
| _GO_CMD="go3" |  | ||||||
| _DEBUG_MODE=false |  | ||||||
| 
 |  | ||||||
| _CONFIG_DIR="${HOME}/.config/gogo" |  | ||||||
| _CONFIG="${_CONFIG_DIR}/gogo.conf" |  | ||||||
| 
 |  | ||||||
| _IS_TTY=false |  | ||||||
| _IS_SSH_ONLY=false |  | ||||||
| _IS_MGRCTL_ARGS=false |  | ||||||
| _MGRCTL_ARGS="" |  | ||||||
| _MGRCTL_BIN="mgrctl" |  | ||||||
| _MGRCTL_CMD="" |  | ||||||
| _MGRCTL_RUN="" |  | ||||||
| _MGRCTL_KEY="" |  | ||||||
| 
 |  | ||||||
| _PLATFORM_TYPE="" |  | ||||||
| _PLATFORM_GENERATION=6 |  | ||||||
| _PLATFORM_SSH_PORT=22 |  | ||||||
| _PLATFORM_WEB_PORT=443 |  | ||||||
| _PLATFORM_IP_ADDR="" |  | ||||||
| _PLATFORM_CONFIG_FILE="" |  | ||||||
| _PLATFORM_NETWORK_NAME="" |  | ||||||
| 
 |  | ||||||
| _SSH_CONNECT_CMD="" |  | ||||||
| _SSH_REMOTE_CMD="" |  | ||||||
| 
 |  | ||||||
| _ACCESS_LINK="" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Colorize output |  | ||||||
| # Usage - $(colorize CYAN "Hello, friend!") |  | ||||||
| colorize() { |  | ||||||
| 	local RED="\033[0;31m" |  | ||||||
| 	local GREEN="\033[0;32m"  # <-- [0 means not bold |  | ||||||
| 	local YELLOW="\033[1;33m" # <-- [1 means bold |  | ||||||
| 	local BLUE="\033[0;34m" |  | ||||||
| 	local MAGNETA="\033[0;35" |  | ||||||
| 	local CYAN="\033[1;36m" |  | ||||||
| 	# ... Add more colors if you like |  | ||||||
| 
 |  | ||||||
| 	local NC="\033[0m" # No Color |  | ||||||
| 
 |  | ||||||
| 	# printf "${(P)1}${2} ${NC}\n" # <-- zsh |  | ||||||
| 	# printf "${!1}${2} ${NC}\n"   # <-- bash |  | ||||||
| 	echo -e "${!1}${2}${NC}"       # <-- all-purpose |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Print help message how used it script |  | ||||||
| help() { |  | ||||||
| 	# colorize value |  | ||||||
| 	local script=$(colorize GREEN "$_SCRIPT_NAME") |  | ||||||
| 	local required=$(colorize RED "required") |  | ||||||
| 	# help message |  | ||||||
| 	printf "Usage: $script [options [parameters]]                              \n" |  | ||||||
| 	printf "                                                                   \n" |  | ||||||
| 	printf "Examples:                                                          \n" |  | ||||||
| 	printf "                                                                   \n" |  | ||||||
| 	printf "${script} --init | init config file                                \n" |  | ||||||
| 	printf "${script} --crt  | get ssh certificate for go3 connections         \n" |  | ||||||
| 	printf "${script} --test | check go3 connection availability               \n" |  | ||||||
| 	printf "                                                                   \n" |  | ||||||
| 	printf "${script} --bill my.example.com                                    \n" |  | ||||||
| 	printf "${script} --vm my.example.com --de  | connect throw DE go3 server  \n" |  | ||||||
| 	printf "${script} --vm 0.0.0.0 --ssh | only ssh access                     \n" |  | ||||||
| 	printf "${script} --vm 0.0.0.0 --tty | use mgrctl interactive              \n" |  | ||||||
| 	printf "                                                                   \n" |  | ||||||
| 	printf "${script} --dci 0.0.0.0 --mgrctl auth user access --id 3 --count 5 \n" |  | ||||||
| 	printf "${script} --dci 0.0.0.0 --mgrctl auth user ls --admins             \n" |  | ||||||
| 	printf "${script} --vm 0.0.0.0 --port 22122 --mgrctl auth user ls --admins \n" |  | ||||||
| 	printf "${script} --vm 0.0.0.0 --tty --mgrctl auth user ls --admins        \n" |  | ||||||
| 	printf "${script} --dns ns1.example.com --web-port 1501                    \n" |  | ||||||
| 	printf "${script} --dns ns1.example.com --port 22122 --web-port 1501       \n" |  | ||||||
| 	printf "${script} --bill my.example.com --port 22 --web-port 1501          \n" |  | ||||||
| 	printf "                                                                   \n" |  | ||||||
| 	printf "Options:                                                           \n" |  | ||||||
| 	printf "                                                                   \n" |  | ||||||
| 	printf " --vm[dci|bill|dns|ip] expected ip_addr             $required      \n" |  | ||||||
| 	printf " --port     | -p    ssh port,   default 22                         \n" |  | ||||||
| 	printf " --web-port | -wp   web port,   default 443                        \n" |  | ||||||
| 	printf " --go/--go3         go version, default go3                        \n" |  | ||||||
| 	printf " --de               connect throw DE go3 server                    \n" |  | ||||||
| 	printf " --ssh              open only ssh session                          \n" |  | ||||||
| 	printf " --tty              for vm6/dci6 echo cmd for run container        \n" |  | ||||||
| 	printf " --mgrctl [args]    for vm6/dci6 customize access params           \n" |  | ||||||
| 	printf "                                                                   \n" |  | ||||||
| 	printf "Single options:                                                    \n" |  | ||||||
| 	printf " --init     | -i    generate configuration                         \n" |  | ||||||
| 	printf " --crt      | -c    generate ssh cert                              \n" |  | ||||||
| 	printf " --test     | -t    check go3 connection availability              \n" |  | ||||||
| 	printf " --version  | -v    print version                                  \n" |  | ||||||
| 	printf " --help     | -h    print this message and exit                    \n" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| # Ask confirmation user if No - exit with 1 state |  | ||||||
| continue_handler() { |  | ||||||
| 	read -p "Continue? (Y/N): " confirm \ |  | ||||||
| 	&& [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| # Init script configuration file: |  | ||||||
| init_config() { |  | ||||||
| 	# Lables: |  | ||||||
| 	local warning=$(colorize RED "WARNING!") |  | ||||||
| 	local success=$(colorize GREEN "SUCCESS!") |  | ||||||
| 	local script_name=$(colorize GREEN "${_SCRIPT_NAME}") |  | ||||||
| 	# check if config file exists: |  | ||||||
| 	if [ -f $_CONFIG ]; then |  | ||||||
| 		echo "${warning} Config file is already exists" |  | ||||||
| 		echo "New initialization rewrites current config" |  | ||||||
| 		continue_handler |  | ||||||
| 	fi |  | ||||||
| 	# get user unputs: |  | ||||||
| 	read -p "Enter russian go server address: "    _GO_SERVER_ADDR_RUSSIAN |  | ||||||
| 	read -p "Enter germany go server address: "    _GO_SERVER_ADDR_GERMANY |  | ||||||
| 	read -p "Enter test go server address: "       _GO_SERVER_ADDR_TEST |  | ||||||
| 	read -p "Enter vault server address: "         _VAULT_SERVER_ADDR |  | ||||||
| 	read -p "Enter username: "                     _SSH_PRIVATE_KEY_USER |  | ||||||
| 	read -p "Enter full path to ssh private key: " _SSH_PRIVATE_KEY_PATH |  | ||||||
| 	read -p "Enter full path to ssh public key: "  _SSH_PUBLIC_KEY_PATH |  | ||||||
| 	read -p "Enter full path to ssh certificate: " _SSH_CRT_FILE |  | ||||||
| 	read -p "Enter mgrctl image name: "            _MGRCTL_IMAGE |  | ||||||
| 	# save config: |  | ||||||
| 	mkdir -p $_CONFIG_DIR |  | ||||||
| 	cat << EOF > "${_CONFIG}" |  | ||||||
| GO_SERVER_ADDR_RUSSIAN=$_GO_SERVER_ADDR_RUSSIAN |  | ||||||
| GO_SERVER_ADDR_GERMANY=$_GO_SERVER_ADDR_GERMANY |  | ||||||
| GO_SERVER_ADDR_TEST=$_GO_SERVER_ADDR_TEST |  | ||||||
| VAULT_SERVER_ADDR=$_VAULT_SERVER_ADDR |  | ||||||
| SSH_PRIVATE_KEY_USER=$_SSH_PRIVATE_KEY_USER |  | ||||||
| SSH_PRIVATE_KEY_PATH=$_SSH_PRIVATE_KEY_PATH |  | ||||||
| SSH_PUBLIC_KEY_PATH=$_SSH_PUBLIC_KEY_PATH |  | ||||||
| SSH_CRT_FILE=$_SSH_CRT_FILE |  | ||||||
| MGRCTL_IMAGE=$_MGRCTL_IMAGE |  | ||||||
| DEBUG_MODE=false |  | ||||||
| EOF |  | ||||||
| 	echo "" |  | ||||||
| 	echo "${success} Config file was created, run ${script_name} again" |  | ||||||
| 	echo "" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Read config file that contains key=value params |  | ||||||
| load_config() { |  | ||||||
| 	local file="$_CONFIG" |  | ||||||
| 
 |  | ||||||
| 	if ! [ -f $_CONFIG ]; then |  | ||||||
| 		help |  | ||||||
| 		local warning=$(colorize RED "WARNING!") |  | ||||||
| 		echo "" |  | ||||||
| 		echo "${warning} Config file doesn't exist" |  | ||||||
| 		echo "Init new config: ${_CONFIG}" |  | ||||||
| 		continue_handler |  | ||||||
| 		init_config |  | ||||||
| 	fi |  | ||||||
| 	 |  | ||||||
| 	while IFS="=" read -r key value; do |  | ||||||
| 		case "$key" in |  | ||||||
| 			"GO_SERVER_ADDR_RUSSIAN") |  | ||||||
| 				_GO_SERVER_ADDR_RUSSIAN="$value" |  | ||||||
| 				_GO_SERVER_ADDR="$value" |  | ||||||
| 				;; |  | ||||||
| 			"GO_SERVER_ADDR_GERMANY") |  | ||||||
| 				_GO_SERVER_ADDR_GERMANY="$value" |  | ||||||
| 				;; |  | ||||||
| 			"GO_SERVER_ADDR_TEST") |  | ||||||
| 				_GO_SERVER_ADDR_TEST="$value" |  | ||||||
| 				;; |  | ||||||
| 			"VAULT_SERVER_ADDR") |  | ||||||
| 				_VAULT_SERVER_ADDR="$value" |  | ||||||
| 				;; |  | ||||||
| 			"SSH_PRIVATE_KEY_USER") |  | ||||||
| 				_SSH_PRIVATE_KEY_USER="$value" |  | ||||||
| 				;; |  | ||||||
| 			"SSH_PRIVATE_KEY_PATH") |  | ||||||
| 				_SSH_PRIVATE_KEY_PATH="$value" |  | ||||||
| 				;; |  | ||||||
| 			"SSH_PUBLIC_KEY_PATH") |  | ||||||
| 				_SSH_PUBLIC_KEY_PATH="$value" |  | ||||||
| 				_VAULT_SSH_PUBLIC_KEY="@$value"  # @ sybol is important |  | ||||||
| 				;; |  | ||||||
| 			"SSH_CRT_FILE") |  | ||||||
| 				_SSH_CRT_FILE="$value" |  | ||||||
| 				;; |  | ||||||
| 			"MGRCTL_IMAGE") |  | ||||||
| 				_MGRCTL_IMAGE="$value" |  | ||||||
| 				;; |  | ||||||
| 			"DEBUG_MODE") |  | ||||||
| 				_DEBUG_MODE="$value" |  | ||||||
| 				;; |  | ||||||
| 		esac |  | ||||||
| 	done < "$file" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| # Generate key for coremgr based platrorms access link: |  | ||||||
| gen_random_key() { |  | ||||||
| 	_MGRCTL_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| gen_coremgr_access_params() { |  | ||||||
| 	# get opt name: |  | ||||||
| 	local opt=$1 |  | ||||||
| 	# gen access key: |  | ||||||
| 	gen_random_key |  | ||||||
| 	# fill current parametrs: |  | ||||||
| 	_PLATFORM_TYPE=$(sed 's~[^[:alpha:]/]\+~~g' <<< "$opt") |  | ||||||
| 	_PLATFORM_GENERATION=5 |  | ||||||
| 	_MGRCTL_BIN="/usr/local/mgr5/sbin/mgrctl" |  | ||||||
| 	_MGRCTL_ARGS="-m ${_PLATFORM_TYPE}mgr session.newkey key=$_MGRCTL_KEY"	 |  | ||||||
| 
 |  | ||||||
| 	# override _PLATFORM_GENERATION for bill6 or dns6 |  | ||||||
| 	if [[ $opt == "--bill" ]] || [[ $opt == "--dns" ]]; then |  | ||||||
| 		_PLATFORM_GENERATION=6 |  | ||||||
| 	fi |  | ||||||
| 	# override _MGRCTL_BIN _MGRCTL_ARGS for dns6 |  | ||||||
| 	if [[ $opt == "--dns" ]]; then |  | ||||||
| 		_MGRCTL_BIN="/opt/ispsystem/${_PLATFORM_TYPE}manager6/sbin/mgrctl" |  | ||||||
| 		_MGRCTL_ARGS="-m ${_PLATFORM_TYPE}mgr session.newkey key=$_MGRCTL_KEY"	 |  | ||||||
| 	fi |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| gen_docker_access_params(){ |  | ||||||
| 	# get opt name: |  | ||||||
| 	local opt=$1 |  | ||||||
| 	# fill current parametrs: |  | ||||||
| 	_PLATFORM_TYPE=$(sed 's~[^[:alpha:]/]\+~~g' <<< "$opt") |  | ||||||
| 	_PLATFORM_GENERATION=6 |  | ||||||
| 	_PLATFORM_CONFIG_FILE="/opt/ispsystem/${_PLATFORM_TYPE}/config.json" |  | ||||||
| 	# set platform docker network name: |  | ||||||
| 	if [[ $_PLATFORM_TYPE == "vm" ]]; then |  | ||||||
| 		_PLATFORM_NETWORK_NAME="vm_vm_box_net" |  | ||||||
| 	else |  | ||||||
| 		_PLATFORM_NETWORK_NAME="dci_auth" |  | ||||||
| 	fi |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| gen_ssh_connect_cmd(){ |  | ||||||
| 	# get params: |  | ||||||
| 	local go_server="${_GO_SERVER_ADDR}" |  | ||||||
| 	local go_cmd="${_GO_CMD}" |  | ||||||
| 	local address="${_PLATFORM_IP_ADDR}" |  | ||||||
| 	local port="${_PLATFORM_SSH_PORT}" |  | ||||||
| 	local key_path="${_SSH_PRIVATE_KEY_PATH}" |  | ||||||
| 	local key_user="${_SSH_PRIVATE_KEY_USER}" |  | ||||||
| 	local ssh_args="${key_user}@${go_server} ${go_cmd} ${address} -p ${port}" |  | ||||||
| 	# generate cmd: |  | ||||||
| 	_SSH_CONNECT_CMD="ssh -A -t -i ${key_path} ${ssh_args}" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| gen_ssh_remote_cmd() { |  | ||||||
| 	# ? VMmanager6 || DCImanager6: |  | ||||||
| 	if [[ $_PLATFORM_TYPE == "vm" ]] || \ |  | ||||||
| 		[[ $_PLATFORM_TYPE == "dci" ]] && \ |  | ||||||
| 		[[ $_PLATFORM_GENERATION -eq 6 ]]; then |  | ||||||
| 		# use default mgrctl cmd if not set args: |  | ||||||
| 		if [ -z "${_MGRCTL_ARGS}" ]; then |  | ||||||
| 			_MGRCTL_ARGS="${_PLATFORM_TYPE}6 auth user access --random" |  | ||||||
| 			_MGRCTL_CMD="${_MGRCTL_BIN} ${_MGRCTL_ARGS}" |  | ||||||
| 		else |  | ||||||
| 			_MGRCTL_CMD="${_MGRCTL_BIN} ${_PLATFORM_TYPE}6 ${_MGRCTL_ARGS}" |  | ||||||
| 		fi |  | ||||||
| 		# silent mode: |  | ||||||
| 		local hide_output=">> /dev/null" |  | ||||||
| 		if $_DEBUG_MODE; then |  | ||||||
| 			hide_output="" |  | ||||||
| 		fi |  | ||||||
| 		# image: |  | ||||||
| 		local image=${_MGRCTL_IMAGE} |  | ||||||
| 		# docker cmd: |  | ||||||
| 		local docker_bin="/usr/bin/docker" |  | ||||||
| 		local docker_pull="${docker_bin} pull ${image} ${hide_output}" |  | ||||||
| 		local docker_rm="${docker_bin} image rm -f ${image} ${hide_output}" |  | ||||||
| 		local docker_run="${docker_bin} run" |  | ||||||
| 		# mount config: |  | ||||||
| 		local mount_src="source=${_PLATFORM_CONFIG_FILE}" |  | ||||||
| 		local mount_trg="target=${_PLATFORM_CONFIG_FILE}" |  | ||||||
| 		local mount_opt="type=bind,${mount_src},${mount_trg},readonly" |  | ||||||
| 		local mount="--mount ${mount_opt}" |  | ||||||
| 		# network config: |  | ||||||
| 		local network="--network=${_PLATFORM_NETWORK_NAME}" |  | ||||||
| 		# environment config: |  | ||||||
| 		local envs="-e PLATFORM_TYPE=${_PLATFORM_TYPE}" |  | ||||||
| 		# container args: |  | ||||||
| 		local args="${_MGRCTL_CMD}" |  | ||||||
| 		# mgrctl container params: |  | ||||||
| 		local container="${network} ${mount} ${envs} --rm ${image} ${args}" |  | ||||||
| 		# docker commands: |  | ||||||
| 		local cmd="${docker_pull} && ${docker_run} ${container} && ${docker_rm}" |  | ||||||
| 		# final cmd: |  | ||||||
| 		_SSH_REMOTE_CMD="${cmd}" |  | ||||||
| 		# set cmd for manual start container: |  | ||||||
| 		if $_IS_TTY; then |  | ||||||
| 			# override parammetrs if DEBUG_MODE=false add -it flag: |  | ||||||
| 			docker_pull="${docker_bin} pull ${image}" |  | ||||||
| 			docker_rm="${docker_bin} image rm -f ${image}" |  | ||||||
| 			container="${network} ${mount} ${envs} --rm -i -t ${image}" |  | ||||||
| 			cmd="${docker_pull} && ${docker_run} ${container} && ${docker_rm}" |  | ||||||
| 			_MGRCTL_RUN="${cmd}" |  | ||||||
| 		fi |  | ||||||
| 	# ? BILLmanager6 ||  DNSmanager6 || IP/DNS/DCI/VMmanager5: |  | ||||||
| 	else  |  | ||||||
| 		# final cmd: |  | ||||||
| 		_SSH_REMOTE_CMD="${_MGRCTL_BIN} ${_MGRCTL_ARGS}" |  | ||||||
| 		echo_access_link |  | ||||||
| 	fi |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| gen_access_link() { |  | ||||||
| 	local url="https://${_PLATFORM_IP_ADDR}" |  | ||||||
| 	local port="${_PLATFORM_WEB_PORT}" |  | ||||||
| 	local platform="${_PLATFORM_TYPE}mgr" |  | ||||||
| 	local func="func=auth&key=${_MGRCTL_KEY}" |  | ||||||
| 	_ACCESS_LINK="${url}:${port}/${platform}?${func}" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| echo_access_link() { |  | ||||||
| 	gen_access_link |  | ||||||
| 	echo "mgr    link" |  | ||||||
| 	echo "-----  -------------------------------------------------------------" |  | ||||||
| 	echo "${_PLATFORM_TYPE}${_PLATFORM_GENERATION}  ${_ACCESS_LINK}" |  | ||||||
| 	echo "" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| echo_mgrctl_run_msg() { |  | ||||||
| 	echo "--------------------------------------------------------------------" |  | ||||||
| 	echo "To run the mgrctl container manually on the client server:"  |  | ||||||
| 	echo "copy and paste the command into the terminal." |  | ||||||
| 	echo "This will download the image and run the container interactively." |  | ||||||
| 	echo "After exiting the container and its image will be deleted." |  | ||||||
| 	echo "--------------------------------------------------------------------" |  | ||||||
| 	echo "${_MGRCTL_RUN}" |  | ||||||
| 	echo "--------------------------------------------------------------------" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| get_access() { |  | ||||||
| 	gen_ssh_connect_cmd |  | ||||||
| 	if $_IS_SSH_ONLY; then |  | ||||||
| 		# run connection: |  | ||||||
| 		$_SSH_CONNECT_CMD |  | ||||||
| 	else |  | ||||||
| 		gen_ssh_remote_cmd |  | ||||||
| 		# run connection send remote cmd: |  | ||||||
| 		$_SSH_CONNECT_CMD "${_SSH_REMOTE_CMD}" |  | ||||||
| 		if [[ $_PLATFORM_TYPE == "vm" ]] || \ |  | ||||||
| 			[[ $_PLATFORM_TYPE == "dci" ]] && \ |  | ||||||
| 			[[ $_PLATFORM_GENERATION -eq 6 ]] && \ |  | ||||||
| 			$_IS_TTY; then |  | ||||||
| 			echo_mgrctl_run_msg |  | ||||||
| 		fi |  | ||||||
| 		# use default mgrctl cmd if not set args: |  | ||||||
| 		# run connection again for ssh tty session: |  | ||||||
| 		$_SSH_CONNECT_CMD |  | ||||||
| 	fi |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| get_vault_crt() { |  | ||||||
| 	local public_key=$1 |  | ||||||
| 	local crt_file=$2 |  | ||||||
| 	vault login -method=oidc |  | ||||||
| 	if [ ! -f $crt_file ]; then |  | ||||||
| 		touch $crt_file |  | ||||||
| 	fi |  | ||||||
| 	vault write -field=signed_key ssh/sign/support \ |  | ||||||
| 	public_key=$public_key valid_principals=root > $crt_file |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| set_ssh_agent() { |  | ||||||
| 	local secret_key=$1 |  | ||||||
| 	ssh-add -D |  | ||||||
| 	ssh-add $secret_key |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| renewal_crt() { |  | ||||||
| 	export VAULT_ADDR=$_VAULT_SERVER_ADDR |  | ||||||
| 	get_vault_crt $_VAULT_SSH_PUBLIC_KEY $_SSH_CRT_FILE |  | ||||||
| 	set_ssh_agent $_SSH_PRIVATE_KEY_PATH |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| test_go3_connection() { |  | ||||||
| 	# force only ssh connections without platform features: |  | ||||||
| 	_IS_SSH_ONLY=true |  | ||||||
| 	# set fake client address: |  | ||||||
| 	_PLATFORM_IP_ADDR="${_GO_SERVER_ADDR_TEST}" |  | ||||||
| 	_PLATFORM_SSH_PORT=22 |  | ||||||
| 
 |  | ||||||
| 	echo "Run TEST: $_GO_SERVER_ADDR_RUSSIAN connection" |  | ||||||
| 	get_access |  | ||||||
| 
 |  | ||||||
| 	echo "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --" |  | ||||||
| 
 |  | ||||||
| 	echo "Run TEST: $_GO_SERVER_ADDR_GERMANY connection" |  | ||||||
| 	_GO_SERVER_ADDR="${_GO_SERVER_ADDR_GERMANY}" |  | ||||||
| 	get_access |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Parse user options |  | ||||||
| optparser() { |  | ||||||
| 	# count user-passed options: |  | ||||||
| 	local count_options=$# |  | ||||||
| 	# run help if empty and exit: |  | ||||||
| 	if [[ count_options -eq 0 ]]; then |  | ||||||
| 		help |  | ||||||
| 		exit 2 |  | ||||||
| 	fi |  | ||||||
| 	# run init config if flag --init and exit: |  | ||||||
| 	if [[ "$1" == "--init" ]]; then |  | ||||||
| 		init_config |  | ||||||
| 		exit 0 |  | ||||||
| 	fi |  | ||||||
| 	# load config from config file: |  | ||||||
| 	load_config |  | ||||||
| 	# parse opts: |  | ||||||
| 	while [ ! -z "$1" ]; do |  | ||||||
| 		case "$1" in |  | ||||||
| 			--vm|--dci) |  | ||||||
| 				gen_docker_access_params "$1" |  | ||||||
| 				shift |  | ||||||
| 				_PLATFORM_IP_ADDR="$1" |  | ||||||
| 				;; |  | ||||||
| 			--bill|--dns|--bill5|--ip5|--dns5|--vm5|--dci5) |  | ||||||
| 				gen_coremgr_access_params "$1" |  | ||||||
| 				shift |  | ||||||
| 				_PLATFORM_IP_ADDR="$1" |  | ||||||
| 				;; |  | ||||||
| 			--port|-p) |  | ||||||
| 				shift |  | ||||||
| 				_PLATFORM_SSH_PORT="$1" |  | ||||||
| 				;; |  | ||||||
| 			--web-port|-wp) |  | ||||||
| 				shift |  | ||||||
| 				_PLATFORM_WEB_PORT="$1" |  | ||||||
| 				;; |  | ||||||
| 			--go|--go3) |  | ||||||
| 				_GO_CMD=$(sed 's~[^[:alnum:]/]\+~~g' <<< "$1") |  | ||||||
| 				;; |  | ||||||
| 			--de) |  | ||||||
| 				_GO_SERVER_ADDR="${_GO_SERVER_ADDR_GERMANY}" |  | ||||||
| 				;; |  | ||||||
| 			--mgrctl|--tty|--ssh) |  | ||||||
| 				if [[ "$1" == "--mgrctl" ]]; then |  | ||||||
| 					_IS_MGRCTL_ARGS=true |  | ||||||
| 					shift |  | ||||||
| 					_MGRCTL_ARGS=$@ |  | ||||||
| 				elif [[ "$1" == "--tty" ]]; then |  | ||||||
| 					if $_IS_MGRCTL_ARGS; then |  | ||||||
| 						local error=$(colorize RED "ERROR!") |  | ||||||
| 						echo "${error} $1 must be in before --mgrctl not after" |  | ||||||
| 						exit 1 |  | ||||||
| 					fi |  | ||||||
| 					_IS_TTY=true |  | ||||||
| 				elif [[ "$1" == "--ssh" ]]; then |  | ||||||
| 					_IS_SSH_ONLY=true |  | ||||||
| 				fi |  | ||||||
| 				;; |  | ||||||
| 			--crt|-c) |  | ||||||
| 				renewal_crt |  | ||||||
| 				exit 0 |  | ||||||
| 				;; |  | ||||||
| 			--test|-t) |  | ||||||
| 				test_go3_connection |  | ||||||
| 				exit 0 |  | ||||||
| 				;; |  | ||||||
| 			--help|-h) |  | ||||||
| 				help |  | ||||||
| 				exit 0 |  | ||||||
| 				;; |  | ||||||
| 			--version|-v) |  | ||||||
| 				printf "$_VERSION\n" |  | ||||||
| 				exit 0 |  | ||||||
| 				;; |  | ||||||
| 			*) |  | ||||||
| 				if ! $_IS_MGRCTL_ARGS; then |  | ||||||
| 					help |  | ||||||
| 					exit 1 |  | ||||||
| 				fi |  | ||||||
| 				;; |  | ||||||
| 		esac |  | ||||||
| 	shift |  | ||||||
| 	done |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Entrypoint: |  | ||||||
| main() { |  | ||||||
| 	optparser $@ |  | ||||||
| 	get_access |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # RUN IT: |  | ||||||
| main $@ |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user