Compare commits

..

No commits in common. "main" and "cli_core" have entirely different histories.

59 changed files with 426 additions and 3314 deletions

View File

@ -1,5 +0,0 @@
docker-compose.yml
.gitignore
.env
.git
.cache

4
.gitignore vendored
View File

@ -158,9 +158,7 @@ cython_debug/
# 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
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
.vscode/
#.idea/
# Project specific
config.json
**/dummy_platform

View File

@ -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

View File

@ -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"]

View File

@ -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
View File

@ -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
## 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
```

View File

@ -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

View 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')

View 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

View 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')

View 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

View 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)

View File

View File

View File

View File

View File

View File

21
isp_maintenance/ispmgr.py Executable file
View 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()

View 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
)

View 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', '')
)

View File

@ -6,9 +6,10 @@ BASE_DIR = pathlib.Path(__file__).resolve().parent.parent
INSTALLED_APPS = {
'vm6': {
'auth': 'mgrctl.apps.vm6.auth.commands.cli',
'access': 'apps.vm6.access.commands.cli',
'nodes': 'apps.vm6.nodes.commands.cli',
},
'dci6': {
'auth': 'mgrctl.apps.dci6.auth.commands.cli',
'access': 'apps.dci6.access.commands.cli',
},
}

View 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')}"
)

View File

View File

@ -1,9 +1,8 @@
import json
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
@ -17,7 +16,5 @@ def parse_json_file(file_path: str) -> dict:
with open(file_path, 'r') as f:
return json.load(f)
except Exception as error:
click.echo(error)
click.echo("Required: /opt/ispsystem/PLATFORM_TYPE/config.json")
click.echo("Note: don't forget to mount this file into the container")
print(error)
sys.exit(1)

View File

@ -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"

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -1,7 +0,0 @@
from mgrctl.settings.general import BASE_DIR
from mgrctl.settings.platform import (
PLATFORM_TYPE,
PLATFORM_URL,
PLATFORM_CONFIG
)

View File

@ -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

View File

@ -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', '')

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +1,22 @@
[tool.poetry]
name = "mgrctl"
version = "0.1.1"
name = "isp-maintenance"
version = "0.1.0"
description = "Maintenance service for ISPsystem platforms"
authors = [
"MOIS3Y <s.zhukovskii@ispsystem.com>",
"a.garaev <a.garaev@ispsystem.com>",
]
authors = ["MOIS3Y <s.zhukovskii@ispsystem.com>", "Failak3 <v.shmidt@ispsystem.com>", "a.garaev <a.garaev@ispsystem.com>", "Ann_M <a.moskovkina@ispsystem.com>"]
license = "MIT"
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.11"
peewee = "^3.17.0"
click = "^8.1.7"
requests = "^2.31.0"
environs = "^12.0.0"
sh = "^2.0.7"
tabulate = "^0.9.0"
environs = "^10.3.0"
[tool.poetry.group.dev.dependencies]
flake8 = "^7.0.0"
poetry-plugin-export = "^1.6.0"
[tool.poetry.scripts]
mgrctl = 'mgrctl.mgrctl:cli'
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

@ -1,126 +1,123 @@
certifi==2025.4.26 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6 \
--hash=sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3
charset-normalizer==3.4.2 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4 \
--hash=sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45 \
--hash=sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7 \
--hash=sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0 \
--hash=sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7 \
--hash=sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d \
--hash=sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d \
--hash=sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0 \
--hash=sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184 \
--hash=sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db \
--hash=sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b \
--hash=sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64 \
--hash=sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b \
--hash=sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8 \
--hash=sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff \
--hash=sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344 \
--hash=sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58 \
--hash=sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e \
--hash=sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471 \
--hash=sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148 \
--hash=sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a \
--hash=sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836 \
--hash=sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e \
--hash=sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63 \
--hash=sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c \
--hash=sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1 \
--hash=sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01 \
--hash=sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366 \
--hash=sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58 \
--hash=sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5 \
--hash=sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c \
--hash=sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2 \
--hash=sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a \
--hash=sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597 \
--hash=sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b \
--hash=sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5 \
--hash=sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb \
--hash=sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f \
--hash=sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0 \
--hash=sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941 \
--hash=sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0 \
--hash=sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86 \
--hash=sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7 \
--hash=sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7 \
--hash=sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455 \
--hash=sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6 \
--hash=sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4 \
--hash=sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0 \
--hash=sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3 \
--hash=sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1 \
--hash=sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6 \
--hash=sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981 \
--hash=sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c \
--hash=sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980 \
--hash=sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645 \
--hash=sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7 \
--hash=sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12 \
--hash=sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa \
--hash=sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd \
--hash=sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef \
--hash=sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f \
--hash=sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2 \
--hash=sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d \
--hash=sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5 \
--hash=sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02 \
--hash=sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3 \
--hash=sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd \
--hash=sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e \
--hash=sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214 \
--hash=sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd \
--hash=sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a \
--hash=sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c \
--hash=sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681 \
--hash=sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba \
--hash=sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f \
--hash=sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a \
--hash=sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28 \
--hash=sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691 \
--hash=sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82 \
--hash=sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a \
--hash=sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027 \
--hash=sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7 \
--hash=sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518 \
--hash=sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf \
--hash=sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b \
--hash=sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9 \
--hash=sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544 \
--hash=sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da \
--hash=sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509 \
--hash=sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f \
--hash=sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a \
--hash=sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f
click==8.1.8 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \
--hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a
certifi==2024.2.2 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
charset-normalizer==3.3.2 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \
--hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \
--hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \
--hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \
--hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \
--hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \
--hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \
--hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \
--hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \
--hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \
--hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \
--hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \
--hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \
--hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \
--hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \
--hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \
--hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \
--hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \
--hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \
--hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \
--hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \
--hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \
--hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \
--hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \
--hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \
--hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \
--hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \
--hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \
--hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \
--hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \
--hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \
--hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \
--hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \
--hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \
--hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \
--hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \
--hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \
--hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \
--hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \
--hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \
--hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \
--hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \
--hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \
--hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \
--hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \
--hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \
--hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \
--hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \
--hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \
--hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \
--hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \
--hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \
--hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \
--hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \
--hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \
--hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \
--hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \
--hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \
--hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \
--hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \
--hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \
--hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \
--hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \
--hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \
--hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \
--hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \
--hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \
--hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \
--hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \
--hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \
--hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \
--hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \
--hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \
--hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \
--hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \
--hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \
--hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \
--hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \
--hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \
--hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \
--hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \
--hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \
--hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \
--hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \
--hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \
--hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \
--hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \
--hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \
--hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \
--hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561
click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
--hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and platform_system == "Windows" \
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
environs==12.0.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:152068ba62185b99d87df5f6d6044c0e533261777119037e718c1397fc7d697f \
--hash=sha256:6150ddeb05562a80cc789b308c650d33b98a5f9c3f4c63d9ed465c433f1cb9e2
idna==3.10 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
marshmallow==4.0.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:3b6e80aac299a7935cfb97ed01d1854fb90b5079430969af92118ea1b12a8d55 \
--hash=sha256:e7b0528337e9990fd64950f8a6b3a1baabed09ad17a0dfb844d701151f92d203
python-dotenv==1.1.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5 \
--hash=sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d
requests==2.32.3 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
sh==2.2.2 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:653227a7c41a284ec5302173fbc044ee817c7bad5e6e4d8d55741b9aeb9eb65b \
--hash=sha256:e0b15b4ae8ffcd399bc8ffddcbd770a43c7a70a24b16773fbb34c001ad5d52af
tabulate==0.9.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c \
--hash=sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f
urllib3==2.4.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466 \
--hash=sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813
environs==10.3.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:cc421ddb143fa30183568164755aa113a160e555cd19e97e664c478662032c24 \
--hash=sha256:feeaf28f17fd0499f9cd7c0fcf408c6d82c308e69e335eb92d09322fc9ed8138
idna==3.6 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
marshmallow==3.20.2 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd \
--hash=sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9
packaging==23.2 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \
--hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7
peewee==3.17.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:e009ac4227c4fdc0058a56e822ad5987684f0a1fbb20fed577200785102581c3
python-dotenv==1.0.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca \
--hash=sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a
requests==2.31.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
urllib3==2.2.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20 \
--hash=sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224

View File

@ -1,10 +0,0 @@
# █▀▄▀█ █▀▀ ▀█▀ ▄▀█ ▀
# █░▀░█ ██▄ ░█░ █▀█ ▄
# -- -- -- -- -- -- -
__author__ = "MOIS3Y"
__credits__ = ["Stepan Zhukovsky"]
__license__ = "MIT"
__version__ = "0.1.0"
__maintainer__ = "Stepan Zhukovsky"
__email__ = "stepan@zhukovsky.me"
__status__ = "Development"

View File

@ -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'
)

View File

@ -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

View File

@ -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'
)

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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 $@