diff --git a/app/__init__.py b/app/__init__.py index e8f545d1..ac06e144 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -36,6 +36,7 @@ from app.routes.user import bp as user_bp from app.routes.server import bp as server_bp from app.routes.admin import bp as admin_bp from app.routes.ha import bp as ha_bp +from app.routes.udp import bp as udp_bp app.register_blueprint(main_bp) app.register_blueprint(overview_bp) @@ -55,6 +56,7 @@ app.register_blueprint(user_bp, url_prefix='/user') app.register_blueprint(server_bp, url_prefix='/server') app.register_blueprint(admin_bp, url_prefix='/admin') app.register_blueprint(ha_bp, url_prefix='/ha') +app.register_blueprint(udp_bp) from app import login from app import jobs diff --git a/app/create_db.py b/app/create_db.py index 9c7257c7..9b5ee494 100644 --- a/app/create_db.py +++ b/app/create_db.py @@ -15,6 +15,7 @@ def default_values(): apache_dir = 'httpd' data_source = [ {'param': 'time_zone', 'value': 'UTC', 'section': 'main', 'desc': 'Time Zone', 'group': '1'}, + {'param': 'license', 'value': '', 'section': 'main', 'desc': 'License key', 'group': '1'}, {'param': 'proxy', 'value': '', 'section': 'main', 'desc': 'IP address and port of the proxy server. Use proto://ip:port', 'group': '1'}, {'param': 'session_ttl', 'value': '5', 'section': 'main', 'desc': 'TTL for a user session (in days)', 'group': '1'}, {'param': 'token_ttl', 'value': '5', 'section': 'main', 'desc': 'TTL for a user token (in days)', 'group': '1'}, @@ -154,6 +155,7 @@ def default_values(): {'service_id': 3, 'service': 'Keepalived', 'slug': 'keepalived'}, {'service_id': 4, 'service': 'Apache', 'slug': 'apache'}, {'service_id': 5, 'service': 'HA cluster', 'slug': 'cluster'}, + {'service_id': 6, 'service': 'UDP balancing', 'slug': 'udp'}, ] try: @@ -465,85 +467,6 @@ def update_db_v_4_3_0(): print("An error occurred:", e) -def update_db_v_6_3_4(): - cursor = conn.cursor() - sql = list() - sql.append("alter table smon add column ssl_expire_warning_alert integer default 0") - sql.append("alter table smon add column ssl_expire_critical_alert integer default 0") - for i in sql: - try: - cursor.execute(i) - except Exception: - pass - else: - print('Updating... DB has been updated to version 6.3.4.0') - - -def update_db_v_6_3_5(): - cursor = conn.cursor() - sql = list() - sql.append("ALTER TABLE `action_history` ADD COLUMN server_ip varchar(64);") - sql.append("ALTER TABLE `action_history` ADD COLUMN hostname varchar(64);") - for i in sql: - try: - cursor.execute(i) - except Exception: - pass - else: - print("Updating... DB has been updated to version 6.3.5.0") - - -def update_db_v_6_3_6(): - cursor = conn.cursor() - sql = list() - sql.append("ALTER TABLE `user_groups` ADD COLUMN user_role_id integer;") - if mysql_enable == '1': - sql.append("update user_groups u_g inner join user as u on u_g.user_id = u.id inner join role as r on r.name = u.role set user_role_id = r.id where u_g.user_role_id is NULL;") - sql.append("update user u inner join role as r on r.name = u.role set u.role = r.id;") - else: - sql.append("update user_groups as u_g set user_role_id = (select r.id from role as r inner join user as u on u.role = r.name where u_g.user_id = u.id) where user_role_id is null;") - sql.append("update user as u set role = (select r.id from role as r where r.name = u.role);") - for i in sql: - try: - cursor.execute(i) - except Exception: - pass - else: - print("Updating... DB has been updated to version 6.3.6.0") - - -def update_db_v_6_3_8(): - cursor = conn.cursor() - sql = """ - ALTER TABLE `smon` ADD COLUMN ssl_expire_date varchar(64); - """ - try: - cursor.execute(sql) - except Exception as e: - if e.args[0] == 'duplicate column name: ssl_expire_date' or str(e) == '(1060, "Duplicate column name \'ssl_expire_date\'")': - print('Updating... DB has been updated to version 6.3.8') - else: - print("An error occurred:", e) - else: - print("Updating... DB has been updated to version 6.3.8") - - -def update_db_v_6_3_9(): - cursor = conn.cursor() - sql = """ - ALTER TABLE `checker_setting` ADD COLUMN pd_id integer default 0; - """ - try: - cursor.execute(sql) - except Exception as e: - if e.args[0] == 'duplicate column name: pd_id' or str(e) == '(1060, "Duplicate column name \'pd_id\'")': - print('Updating... DB has been updated to version 6.3.9') - else: - print("An error occurred:", e) - else: - print("Updating... DB has been updated to version 6.3.9") - - def update_db_v_6_3_11(): cursor = conn.cursor() sql = """ @@ -755,7 +678,7 @@ def update_db_v_7_2_3(): def update_ver(): try: - Version.update(version='7.2.6.0').execute() + Version.update(version='7.3.0.0').execute() except Exception: print('Cannot update version') @@ -773,11 +696,6 @@ def update_all(): if check_ver() is None: update_db_v_3_4_5_22() update_db_v_4_3_0() - update_db_v_6_3_4() - update_db_v_6_3_5() - update_db_v_6_3_6() - update_db_v_6_3_8() - update_db_v_6_3_9() update_db_v_6_3_11() update_db_v_6_3_13() update_db_v_6_3_13_1() diff --git a/app/login.py b/app/login.py index adcd647e..41e54079 100644 --- a/app/login.py +++ b/app/login.py @@ -24,7 +24,7 @@ def check_login(): return redirect(login_url('login_page', next_url=request.url)) try: - roxywi_auth.check_login(user_params['user_uuid'], user_params['token']) + roxywi_auth.check_login(user_params['user_uuid']) except Exception: return redirect(login_url('login_page', next_url=request.url)) @@ -69,13 +69,13 @@ def login_page(): if user.ldap_user == 1: if login in user.username: if roxywi_auth.check_in_ldap(login, password): - user_uuid, user_token = roxywi_auth.create_uuid_and_token(login) + user_uuid = roxywi_auth.create_uuid_and_token(login) return roxywi_auth.do_login(user_uuid, str(user.groups), user, next_url) else: hashed_password = roxy_wi_tools.Tools.get_hash(password) if login in user.username and hashed_password == user.password: - user_uuid, user_token = roxywi_auth.create_uuid_and_token(login) + user_uuid = roxywi_auth.create_uuid_and_token(login) return roxywi_auth.do_login(user_uuid, str(user.groups), user, next_url) else: return 'ban', 200 diff --git a/app/middleware.py b/app/middleware.py index 35fbff50..5f8ea12b 100644 --- a/app/middleware.py +++ b/app/middleware.py @@ -10,7 +10,7 @@ def check_services(fn): @wraps(fn) def decorated_view(*args, **kwargs): service = kwargs['service'] - if service not in ('haproxy', 'nginx', 'apache', 'keepalived', 'cluster', 'waf'): + if service not in ('haproxy', 'nginx', 'apache', 'keepalived', 'cluster', 'waf', 'udp'): abort(405, 'Wrong service') if not roxywi_auth.is_access_permit_to_service(service): abort(403, f'You do not have needed permissions to access to {service.title()} service') diff --git a/app/modules/config/config.py b/app/modules/config/config.py index 17aff40b..79dde53a 100644 --- a/app/modules/config/config.py +++ b/app/modules/config/config.py @@ -235,7 +235,7 @@ def upload_and_restart(server_ip: str, cfg: str, just_save: str, service: str, * except Exception as e: roxywi_common.logging('Roxy-WI server', str(e), roxywi=1) - # If master then save version of config in a new way + # If master then save a version of config in a new way if not kwargs.get('slave') and service != 'waf': _create_config_version(server_id, server_ip, service, config_path, kwargs.get('login'), cfg, kwargs.get('oldcfg'), tmp_file) diff --git a/app/modules/db/db_model.py b/app/modules/db/db_model.py index 2e5c43a0..0d797106 100644 --- a/app/modules/db/db_model.py +++ b/app/modules/db/db_model.py @@ -52,7 +52,7 @@ class User(BaseModel, UserMixin): groups = CharField() ldap_user = IntegerField(constraints=[SQL('DEFAULT "0"')]) activeuser = IntegerField(constraints=[SQL('DEFAULT "1"')]) - user_services = CharField(constraints=[SQL('DEFAULT "1 2 3 4 5"')]) + user_services = CharField(constraints=[SQL('DEFAULT "1 2 3 4 5 6"')]) last_login_date = DateTimeField(constraints=[SQL('DEFAULT "0000-00-00 00:00:00"')]) last_login_ip = CharField(null=True) @@ -153,16 +153,6 @@ class UUID(BaseModel): primary_key = False -class Token(BaseModel): - user_id = IntegerField() - token = CharField() - exp = DateTimeField(default=datetime.now) - - class Meta: - table_name = 'token' - primary_key = False - - class ApiToken(BaseModel): token = CharField() user_name = CharField() @@ -765,15 +755,36 @@ class HaClusterService(BaseModel): constraints = [SQL('UNIQUE (cluster_id, service_id)')] +class UDPBalancer(BaseModel): + id = AutoField() + name = CharField() + cluster_id = IntegerField(null=True) + server_id = IntegerField(null=True) + vip = CharField() + port = IntegerField() + group_id = ForeignKeyField(Groups) + config = CharField() + desc = CharField() + lb_algo = CharField(constraints=[SQL('DEFAULT "rr"')]) + check_enabled = IntegerField(constraints=[SQL('DEFAULT "1"')]) + delay_loop = IntegerField(constraints=[SQL('DEFAULT "10"')]) + delay_before_retry = IntegerField(constraints=[SQL('DEFAULT "10"')]) + retry = IntegerField(constraints=[SQL('DEFAULT "3"')]) + + class Meta: + table_name = 'udp_balancers' + constraints = [SQL('UNIQUE (vip, port)')] + + def create_tables(): conn = connect() with conn: conn.create_tables( - [User, Server, Role, Telegram, Slack, UUID, Token, ApiToken, Groups, UserGroups, ConfigVersion, Setting, + [User, Server, Role, Telegram, Slack, UUID, ApiToken, Groups, UserGroups, ConfigVersion, Setting, Cred, Backup, Metrics, WafMetrics, Version, Option, SavedServer, Waf, ActionHistory, PortScannerSettings, PortScannerPorts, PortScannerHistory, ServiceSetting, MetricsHttpStatus, SMON, WafRules, Alerts, GeoipCodes, NginxMetrics, SystemInfo, Services, UserName, GitSetting, CheckerSetting, ApacheMetrics, WafNginx, ServiceStatus, KeepaliveRestart, PD, SmonHistory, SmonAgent, SmonTcpCheck, SmonHttpCheck, SmonPingCheck, SmonDnsCheck, S3Backup, RoxyTool, SmonStatusPage, SmonStatusPageCheck, HaCluster, HaClusterSlave, HaClusterVip, HaClusterVirt, HaClusterService, - HaClusterRouter, MM] + HaClusterRouter, MM, UDPBalancer] ) diff --git a/app/modules/db/server.py b/app/modules/db/server.py index 77a05cc3..8fa606d0 100644 --- a/app/modules/db/server.py +++ b/app/modules/db/server.py @@ -44,6 +44,13 @@ def get_hostname_by_server_ip(server_ip): return hostname.hostname +def get_server_by_id(server_id): + try: + return Server.get(Server.server_id == server_id) + except Exception as e: + return out_error(e) + + def select_server_by_name(name): try: ip = Server.get(Server.hostname == name) diff --git a/app/modules/db/udp.py b/app/modules/db/udp.py new file mode 100644 index 00000000..7571d03d --- /dev/null +++ b/app/modules/db/udp.py @@ -0,0 +1,37 @@ +from app.modules.db.db_model import mysql_enable, connect, Server, UDPBalancer +from app.modules.db.common import out_error + + +def select_listeners(group_id: int) -> UDPBalancer: + try: + return UDPBalancer.select().where(UDPBalancer.group_id == group_id).execute() + except Exception as e: + out_error(e) + + +def insert_listener(**kwargs) -> int: + try: + return UDPBalancer.insert(**kwargs).execute() + except Exception as e: + out_error(e) + + +def update_listener(listener_id: int, **kwargs) -> int: + try: + return UDPBalancer.update(**kwargs).where(UDPBalancer.id == listener_id).execute() + except Exception as e: + out_error(e) + + +def get_listener(listener_id: int) -> UDPBalancer: + try: + return UDPBalancer.get(UDPBalancer.id == listener_id) + except Exception as e: + out_error(e) + + +def delete_listener(listener_id: int) -> None: + try: + UDPBalancer.delete().where(UDPBalancer.id == listener_id).execute() + except Exception as e: + out_error(e) diff --git a/app/modules/db/user.py b/app/modules/db/user.py index 394f2427..e6bbf683 100644 --- a/app/modules/db/user.py +++ b/app/modules/db/user.py @@ -1,6 +1,6 @@ from peewee import Case, JOIN -from app.modules.db.db_model import User, UserGroups, Groups, UUID, Token, ApiToken +from app.modules.db.db_model import User, UserGroups, Groups, UUID, ApiToken from app.modules.db.sql import get_setting from app.modules.db.common import out_error import app.modules.roxy_wi_tools as roxy_wi_tools @@ -190,21 +190,15 @@ def select_users_roles(): return query_res -def update_last_act_user(uuid: str, token: str, ip: str) -> None: +def update_last_act_user(uuid: str,ip: str) -> None: get_date = roxy_wi_tools.GetDate(get_setting('time_zone')) session_ttl = get_setting('session_ttl') - token_ttl = get_setting('token_ttl') cur_date_session = get_date.return_date('regular', timedelta=session_ttl) - cur_date_token = get_date.return_date('regular', timedelta=token_ttl) cur_date = get_date.return_date('regular') user_id = get_user_id_by_uuid(uuid) - query = UUID.update(exp=cur_date_session).where(UUID.uuid == uuid) - query1 = Token.update(exp=cur_date_token).where(Token.token == token) - query2 = User.update(last_login_date=cur_date, last_login_ip=ip).where(User.user_id == user_id) try: - query.execute() - query1.execute() - query2.execute() + UUID.update(exp=cur_date_session).where(UUID.uuid == uuid).execute() + User.update(last_login_date=cur_date, last_login_ip=ip).where(User.user_id == user_id).execute() except Exception as e: out_error(e) @@ -281,18 +275,6 @@ def write_user_uuid(login, user_uuid): out_error(e) -def write_user_token(login, user_token): - token_ttl = get_setting('token_ttl') - user_id = get_user_id_by_username(login) - get_date = roxy_wi_tools.GetDate() - cur_date = get_date.return_date('regular', timedelta=token_ttl) - - try: - Token.insert(user_id=user_id, token=user_token, exp=cur_date).execute() - except Exception as e: - out_error(e) - - def select_user_services(user_id): try: query_res = User.get(User.user_id == user_id).user_services @@ -405,28 +387,11 @@ def get_username_group_id_from_api_token(token): return user_name.user_name, user_name.user_group_id, user_name.user_role -def get_token(uuid): - query = Token.select().join(UUID, on=(Token.user_id == UUID.user_id)).where(UUID.uuid == uuid).limit(1) - try: - query_res = query.execute() - except Exception as e: - out_error(e) - else: - try: - for i in query_res: - return i.token - except Exception: - return '' - - def delete_old_uuid(): get_date = roxy_wi_tools.GetDate() cur_date = get_date.return_date('regular') - query = UUID.delete().where((UUID.exp < cur_date) | (UUID.exp.is_null(True))) - query1 = Token.delete().where((Token.exp < cur_date) | (Token.exp.is_null(True))) try: - query.execute() - query1.execute() + UUID.delete().where((UUID.exp < cur_date) | (UUID.exp.is_null(True))).execute() except Exception as e: out_error(e) diff --git a/app/modules/roxywi/auth.py b/app/modules/roxywi/auth.py index ca91f063..636c0329 100644 --- a/app/modules/roxywi/auth.py +++ b/app/modules/roxywi/auth.py @@ -11,7 +11,7 @@ import app.modules.db.service as service_sql import app.modules.roxywi.common as roxywi_common -def check_login(user_uuid, token) -> str: +def check_login(user_uuid) -> str: if user_uuid is None: return 'login_page' @@ -24,7 +24,7 @@ def check_login(user_uuid, token) -> str: except Exception: ip = '' - user_sql.update_last_act_user(user_uuid, token, ip) + user_sql.update_last_act_user(user_uuid, ip) return 'ok' return 'login_page' @@ -106,11 +106,9 @@ def check_in_ldap(user, password): def create_uuid_and_token(login: str): user_uuid = str(uuid.uuid4()) - user_token = str(uuid.uuid4()) user_sql.write_user_uuid(login, user_uuid) - user_sql.write_user_token(login, user_token) - return user_uuid, user_token + return user_uuid def do_login(user_uuid: str, user_group: str, user: str, next_url: str): diff --git a/app/modules/roxywi/common.py b/app/modules/roxywi/common.py index 2835bd20..bca94e03 100644 --- a/app/modules/roxywi/common.py +++ b/app/modules/roxywi/common.py @@ -5,6 +5,7 @@ from typing import Any from flask import request from app.modules.db.sql import get_setting +import app.modules.db.udp as udp_sql import app.modules.db.roxy as roxy_sql import app.modules.db.user as user_sql import app.modules.db.group as group_sql @@ -37,18 +38,10 @@ def get_user_group(**kwargs) -> int: return user_group -def check_user_group_for_flask(**kwargs): - if kwargs.get('token') is not None: - return True - - if kwargs.get('user_uuid'): - group_id = kwargs.get('user_group_id') - user_uuid = kwargs.get('user_uuid') - user_id = user_sql.get_user_id_by_uuid(user_uuid) - else: - user_uuid = request.cookies.get('uuid') - group_id = request.cookies.get('group') - user_id = user_sql.get_user_id_by_uuid(user_uuid) +def check_user_group_for_flask(): + user_uuid = request.cookies.get('uuid') + group_id = request.cookies.get('group') + user_id = user_sql.get_user_id_by_uuid(user_uuid) if user_sql.check_user_group(user_id, group_id): return True @@ -163,25 +156,32 @@ def keep_action_history(service: str, action: str, server_ip: str, login: str, u user_ip = 'localhost' if service == 'HA cluster': - cluster_id = server_ip - cluster_name = ha_sql.select_cluster_name(int(cluster_id)) - history_sql.insert_action_history(service, action, int(cluster_id), user_id, user_ip, cluster_id, cluster_name) + try: + server_id = server_ip + hostname = ha_sql.select_cluster_name(int(server_id)) + except Exception as e: + logging('Roxy-WI server', f'Cannot get info about cluster {server_ip} for history: {e}', roxywi=1) + elif service == 'UDP Listener': + try: + server_id = int(server_ip) + listener = udp_sql.get_listener(server_id) + hostname = listener.name + except Exception as e: + logging('Roxy-WI server', f'Cannot get info about Listener {server_ip} for history: {e}', roxywi=1) else: try: server_id = server_sql.select_server_id_by_ip(server_ip=server_ip) hostname = server_sql.get_hostname_by_server_ip(server_ip) - - history_sql.insert_action_history(service, action, server_id, user_id, user_ip, server_ip, hostname) except Exception as e: - logging('Roxy-WI server', f'Cannot save a history: {e}', roxywi=1) + logging('Roxy-WI server', f'Cannot get info about {server_ip} for history: {e}', roxywi=1) + + try: + history_sql.insert_action_history(service, action, server_id, user_id, user_ip, server_ip, hostname) + except Exception as e: + logging('Roxy-WI server', f'Cannot save a history: {e}', roxywi=1) def get_dick_permit(**kwargs): - if kwargs.get('token'): - token = kwargs.get('token') - else: - token = '' - if not kwargs.get('group_id'): try: group_id = get_user_group(id=1) @@ -190,7 +190,7 @@ def get_dick_permit(**kwargs): else: group_id = kwargs.pop('group_id') - if check_user_group_for_flask(token=token): + if check_user_group_for_flask(): try: servers = server_sql.get_dick_permit(group_id, **kwargs) except Exception as e: @@ -236,11 +236,6 @@ def get_users_params(**kwargs): except Exception as e: raise Exception(f'error: Cannot get user services {e}') - try: - token = user_sql.get_token(user_uuid) - except Exception as e: - raise Exception(f'error: Cannot get user token {e}') - if kwargs.get('virt') and kwargs.get('service') == 'haproxy': servers = get_dick_permit(virt=1, haproxy=1) elif kwargs.get('virt'): @@ -258,7 +253,6 @@ def get_users_params(**kwargs): 'user': user, 'user_uuid': user_uuid, 'role': role, - 'token': token, 'servers': servers, 'user_services': user_services, 'lang': user_lang, diff --git a/app/modules/roxywi/roxy.py b/app/modules/roxywi/roxy.py index 5d1b2fb4..68b28a8e 100644 --- a/app/modules/roxywi/roxy.py +++ b/app/modules/roxywi/roxy.py @@ -6,6 +6,7 @@ import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry +import app.modules.db.sql as sql import app.modules.db.roxy as roxy_sql import app.modules.common.common as common import app.modules.roxywi.common as roxywi_common @@ -72,21 +73,20 @@ def check_new_version(service): def update_user_status() -> None: + user_license = sql.get_setting('license') proxy_dict = common.return_proxy_dict() - user_name = roxy_sql.select_user_name() retry_strategy = Retry( total=3, - status_forcelist=[429, 500, 502, 503, 504], - method_whitelist=["HEAD", "GET", "OPTIONS"] + status_forcelist=[429, 500, 502, 503, 504] ) adapter = HTTPAdapter(max_retries=retry_strategy) roxy_wi_get_plan = requests.Session() roxy_wi_get_plan.mount("https://", adapter) - roxy_wi_get_plan = requests.get(f'https://roxy-wi.org/user-name/{user_name}', timeout=5, proxies=proxy_dict) + json_body = {'license': user_license} + roxy_wi_get_plan = requests.post(f'https://roxy-wi.org/user/license', timeout=5, proxies=proxy_dict, json=json_body) try: - status = roxy_wi_get_plan.content.decode(encoding='UTF-8') - status = status.split(' ') - roxy_sql.update_user_status(status[0], status[1].strip(), status[2].strip()) + status = roxy_wi_get_plan.json() + roxy_sql.update_user_status(status['status'], status['plan'], status['method']) except Exception as e: roxywi_common.logging('Roxy-WI server', f'error: Cannot get user status {e}', roxywi=1) @@ -111,12 +111,24 @@ def update_plan(): if distro.id() == 'ubuntu': path_to_repo = '/etc/apt/auth.conf.d/roxy-wi.conf' cmd = "grep login /etc/apt/auth.conf.d/roxy-wi.conf |awk '{print $2}'" + cmd2 = "grep password /etc/apt/auth.conf.d/roxy-wi.conf |awk '{print $2}'" else: path_to_repo = '/etc/yum.repos.d/roxy-wi.repo' cmd = "grep base /etc/yum.repos.d/roxy-wi.repo |grep -v '#' |awk -F\":\" '{print $2}'|awk -F\"/\" '{print $3}'" + cmd2 = "grep base /etc/yum.repos.d/roxy-wi.repo |grep -v '#' |awk -F\":\" '{print $3}'|awk -F\"@\" '{print $1}'" if os.path.exists(path_to_repo): get_user_name, stderr = server_mod.subprocess_execute(cmd) user_name = get_user_name[0] + cur_license = sql.get_setting('license') + if not cur_license: + get_license, stderr = server_mod.subprocess_execute(cmd2) + user_license = get_license[0] + + if user_license: + try: + sql.update_setting('license', user_license, 1) + except Exception as e: + roxywi_common.logging('Roxy-WI server', f'error: Cannot update license {e}', roxywi=1) else: user_name = 'git' diff --git a/app/modules/service/installation.py b/app/modules/service/installation.py index a06e84a9..c4095459 100644 --- a/app/modules/service/installation.py +++ b/app/modules/service/installation.py @@ -9,6 +9,7 @@ import app.modules.db.sql as sql import app.modules.db.ha_cluster as ha_sql import app.modules.db.server as server_sql import app.modules.db.service as service_sql +import app.modules.service.udp as udp_mod import app.modules.service.common as service_common import app.modules.common.common as common import app.modules.server.server as server_mod @@ -34,6 +35,27 @@ def show_installation_output(error: str, output: list, service: str, rc=0): return True +def generate_udp_inv(listener_id: int, action: str) -> object: + inv = {"server": {"hosts": {}}} + server_ips = [] + listener = udp_mod.get_listener_config(listener_id) + if listener['cluster_id']: + server_ips = udp_mod.get_slaves_for_udp_listener(listener['cluster_id'], listener['vip']) + elif listener['server_id']: + server = server_sql.get_server_by_id(listener['server_id']) + server_ips.append(server.ip) + + for server_ip in server_ips: + inv['server']['hosts'][server_ip] = { + 'action': action, + "vip": listener['vip'], + "port": listener['port'], + "id": listener['id'], + "config": listener['config'], + } + return inv, server_ips + + def generate_geoip_inv(server_ip: str, installed_service: str, geoip_update: int) -> object: inv = {"server": {"hosts": {}}} server_ips = [] diff --git a/app/modules/service/udp.py b/app/modules/service/udp.py new file mode 100644 index 00000000..7774b4c7 --- /dev/null +++ b/app/modules/service/udp.py @@ -0,0 +1,132 @@ +import json + +from playhouse.shortcuts import model_to_dict + +import app.modules.db.udp as udp_sql +import app.modules.db.server as server_sql +import app.modules.db.ha_cluster as ha_sql +import app.modules.common.common as common +import app.modules.server.server as server_mod +import app.modules.roxywi.common as roxywi_common + + +def create_listener(json_data: json) -> int: + listener = _validate_form(json_data) + listener_id = udp_sql.insert_listener(**listener) + roxywi_common.logging(listener_id, f'UDP listener {listener["name"]} has been created', keep_history=1, roxywi=1, service='UDP Listener') + return listener_id + + +def update_listener(json_data: json) -> str: + listener = _validate_form(json_data) + listener_id = json_data['listener_id'] + udp_sql.update_listener(listener_id, **listener) + roxywi_common.logging(listener_id, f'UDP listener {listener["name"]} has been updated', keep_history=1, roxywi=1, service='UDP Listener') + return 'ok' + + +def _validate_form(json_data: json) -> dict: + returned_data = {} + if not isinstance(json_data, dict): + raise ValueError("error: Invalid form data") + returned_data['name'] = common.checkAjaxInput(json_data['new-listener-name']) + returned_data['desc'] = common.checkAjaxInput(json_data['new-listener-desc']) + try: + returned_data['port'] = int(json_data['new-listener-port']) + except Exception: + raise ValueError("error: Invalid port number") + returned_data['group_id'] = int(json_data['group_id']) + returned_data['config'] = {} + for k, v in json_data.items(): + if k == 'servers': + _validate_backend_servers(v) + for server, value in v.items(): + server_ip = common.is_ip_or_dns(server) + returned_data['config'][server_ip] = {} + returned_data['config'][server_ip]['port'] = int(value['port']) + returned_data['config'][server_ip]['weight'] = int(value['weight']) + if json_data['new-listener-type'] == 'server': + returned_data['server_id'] = int(json_data['serv']) + try: + returned_data['vip'] = common.is_ip_or_dns(json_data['new-udp-ip']) + except ValueError: + raise ValueError("error: Cannot parse Server and IP") + else: + try: + cluster_id = int(json_data['ha-cluster']) + returned_data['cluster_id'] = cluster_id + except ValueError: + raise ValueError("error: Cannot parse Cluster ID") + returned_data['vip'] = common.is_ip_or_dns(json_data['new-udp-vip']) + + return returned_data + + +def _validate_backend_servers(serves: dict): + if not isinstance(serves, dict): + raise ValueError("error: Invalid backend servers data") + if len(serves) == 0: + raise ValueError("error: Empty backend servers") + for server, value in serves.items(): + server = common.is_ip_or_dns(server) + if server == '': + raise ValueError("error: Cannot parse backend server IP") + try: + port = int(value['port']) + except ValueError: + raise ValueError(f"error: Invalid port for backend server {server}") + if port > 65535 or port < 1: + raise Exception(f'error: Port must be 1-65535 for backend server {server}') + try: + weight = int(value['weight']) + except ValueError: + raise ValueError(f"error: Invalid weight for backend server {server}") + if weight > 65535 or weight < 1: + raise Exception(f'error: Weight must be 1-65535 for backend server {server}') + + +def get_listener_config(listener_id: int) -> dict: + listener = udp_sql.get_listener(listener_id) + listener_json = model_to_dict(listener) + listener_json['config'] = eval(listener_json['config']) + return listener_json + + +def get_slaves_for_udp_listener(cluster_id: int, vip: str) -> list: + servers = [] + vips = ha_sql.select_cluster_vips(cluster_id) + for v in vips: + if v.vip == vip: + router_id = v.router_id + break + else: + raise ValueError("error: Cannot find VIP") + slaves = ha_sql.select_cluster_slaves(cluster_id, router_id) + for slave in slaves: + servers.append(slave[2]) + return servers + + +def listener_actions(listener_id: int, action: str, group_id: int) -> str: + if action not in ('start', 'stop', 'restart'): + raise ValueError("error: Invalid action") + servers = [] + listener = udp_sql.get_listener(listener_id) + if int(listener.group_id.group_id) != group_id: + raise ValueError("error: Invalid group") + if listener.cluster_id: + get_slaves_for_udp_listener(listener.cluster_id, listener.vip) + elif listener.server_id: + server = server_sql.get_server_by_id(listener.server_id) + servers.append(server.ip) + if len(servers) < 1: + raise ValueError("error: Cannot find server") + cmd = f'sudo systemctl {action} keepalived-udp-{listener_id}.service' + print(cmd) + for server_ip in servers: + try: + server_mod.ssh_command(server_ip, cmd) + roxywi_common.logging(listener.id, f'UDP listener {listener.name} has been {action} on {server_ip}', keep_history=1, roxywi=1, service='UDP Listener') + except Exception as e: + roxywi_common.handle_exceptions(e, 'Roxy-WI server', f'Cannot {action} for UDP balancer {listener.name}', roxywi=1) + return 'ok' diff --git a/app/routes/add/routes.py b/app/routes/add/routes.py index ba42489f..a486e48d 100644 --- a/app/routes/add/routes.py +++ b/app/routes/add/routes.py @@ -12,7 +12,6 @@ import app.modules.common.common as common import app.modules.roxywi.auth as roxywi_auth import app.modules.roxywi.common as roxywi_common import app.modules.roxy_wi_tools as roxy_wi_tools -import app.modules.server.server as server_mod get_config = roxy_wi_tools.GetConfigVar() @@ -670,11 +669,3 @@ def add_nginx_upstream(): return add_mod.save_nginx_config(config_add, server_ip, config_name) except Exception as e: return str(e) - - -@bp.route('/show/ip/') -def show_ip(server_ip): - server_ip = common.is_ip_or_dns(server_ip) - commands = 'sudo hostname -I | tr " " "\\n"|sed "/^$/d"' - - return server_mod.ssh_command(server_ip, commands, ip="1") diff --git a/app/routes/ha/routes.py b/app/routes/ha/routes.py index 1c460fab..bc586e5f 100644 --- a/app/routes/ha/routes.py +++ b/app/routes/ha/routes.py @@ -255,3 +255,14 @@ def ha_vip(service, cluster_id): return 'ok' except Exception as e: return f'error: Cannot delete VIP: {e}' + + +@bp.route('///vips', methods=['GET']) +@check_services +@get_user_params() +def get_vips(service, cluster_id): + from playhouse.shortcuts import model_to_dict + if request.method == 'GET': + vips = ha_sql.select_cluster_vips(cluster_id) + vips = [model_to_dict(vip) for vip in vips] + return jsonify(vips) diff --git a/app/routes/install/routes.py b/app/routes/install/routes.py index d728d066..89146732 100644 --- a/app/routes/install/routes.py +++ b/app/routes/install/routes.py @@ -137,3 +137,14 @@ def check_geoip(service, server_ip): service_dir = common.return_nice_path(sql.get_setting(f'{service}_dir')) cmd = f"ls {service_dir}geoip/" return server_mod.ssh_command(server_ip, cmd) + + +@bp.post('/udp') +def install_udp(): + listener_id = int(request.form.get('listener_id')) + try: + inv, server_ips = service_mod.generate_udp_inv(listener_id, 'install') + return service_mod.run_ansible(inv, server_ips, f'udp'), 201 + except Exception as e: + return f'{e}' + diff --git a/app/routes/server/routes.py b/app/routes/server/routes.py index b9d50be6..298fd4b9 100644 --- a/app/routes/server/routes.py +++ b/app/routes/server/routes.py @@ -55,6 +55,24 @@ def show_if(server_ip): return server_mod.ssh_command(server_ip, command) +@bp.route('/show/ip/') +def show_ip_by_id(server_ip): + server_ip = common.is_ip_or_dns(server_ip) + if server_ip == '': + raise Exception('error: Cannot find server ip') + commands = 'sudo hostname -I | tr " " "\\n"|sed "/^$/d"' + + return server_mod.ssh_command(server_ip, commands, ip="1") + + +@bp.route('/show/ip/') +def show_ip(server_id): + server_ip = server_sql.get_server_by_id(server_id) + commands = 'sudo hostname -I | tr " " "\\n"|sed "/^$/d"' + + return server_mod.ssh_command(server_ip.ip, commands, ip="1") + + @bp.post('/create') @get_user_params() def create_server(): diff --git a/app/routes/udp/__init__.py b/app/routes/udp/__init__.py new file mode 100644 index 00000000..1de4e4fd --- /dev/null +++ b/app/routes/udp/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +bp = Blueprint('udp', __name__) + +from app.routes.udp import routes diff --git a/app/routes/udp/routes.py b/app/routes/udp/routes.py new file mode 100644 index 00000000..c7e87740 --- /dev/null +++ b/app/routes/udp/routes.py @@ -0,0 +1,105 @@ +from flask import render_template, request, g, jsonify +from flask_login import login_required + +from app.routes.udp import bp +import app.modules.db.udp as udp_sql +import app.modules.db.server as server_sql +import app.modules.db.ha_cluster as ha_sql +import app.modules.common.common as common +import app.modules.roxywi.auth as roxywi_auth +import app.modules.roxywi.common as roxywi_common +import app.modules.service.udp as udp_mod +import app.modules.service.installation as service_mod +from app.middleware import get_user_params, check_services + + +@bp.before_request +@login_required +def before_request(): + """ Protect all the admin endpoints. """ + pass + + +@bp.route('//listener', methods=['GET', 'POST', 'PUT', 'DELETE']) +@check_services +@get_user_params() +def listener_funct(service): + if request.method != 'GET': + roxywi_auth.page_for_admin(level=2) + if request.method == 'GET': + kwargs = { + 'listeners': udp_sql.select_listeners(g.user_params['group_id']), + 'lang': g.user_params['lang'], + 'clusters': ha_sql.select_clusters(g.user_params['group_id']), + 'is_needed_tool': common.is_tool('ansible'), + 'user_subscription': roxywi_common.return_user_subscription() + } + return render_template('udp/listeners.html', **kwargs) + elif request.method == 'POST': + json_data = request.get_json() + json_data['group_id'] = g.user_params['group_id'] + try: + listener_id = udp_mod.create_listener(json_data) + return jsonify({'status': 'created', 'listener_id': listener_id}) + except Exception as e: + return jsonify({'status': 'failed', 'error': str(e)}) + elif request.method == 'PUT': + json_data = request.get_json() + json_data['group_id'] = g.user_params['group_id'] + try: + udp_mod.update_listener(json_data) + return jsonify({'status': 'updated'}), 201 + except Exception as e: + return jsonify({'status': 'failed', 'error': str(e)}) + elif request.method == 'DELETE': + kwargs = request.get_json() + listener_id = int(kwargs['listener_id']) + try: + inv, server_ips = service_mod.generate_udp_inv(listener_id, 'uninstall') + service_mod.run_ansible(inv, server_ips, f'udp'), 201 + except Exception as e: + return jsonify({'status': 'failed', 'error': str(e)}) + try: + udp_sql.delete_listener(listener_id) + return jsonify({'status': 'deleted'}), 201 + except Exception as e: + return jsonify({'status': 'failed', 'error': str(e)}) + + +@bp.get('//listener/') +@check_services +@get_user_params() +def get_listener(service, listener_id): + listener = udp_sql.get_listener(listener_id) + cluster = dict() + server = dict() + if listener.cluster_id: + cluster = ha_sql.select_cluster(listener.cluster_id) + elif listener.server_id: + server = server_sql.get_server_by_id(listener.server_id) + kwargs = { + 'clusters': cluster, + 'listener': listener, + 'server': server, + 'lang': g.user_params['lang'], + } + return render_template('udp/listener.html', **kwargs) + + +@bp.get('//listener//settings') +@check_services +@get_user_params() +def get_listener_settings(service, listener_id): + listener_config = udp_mod.get_listener_config(listener_id) + return jsonify(listener_config) + + +@bp.get('//listener//') +@check_services +@get_user_params() +def action_with_listener(service, listener_id, action): + try: + udp_mod.listener_actions(listener_id, action, g.user_params['group_id']) + return jsonify({'status': 'done'}) + except Exception as e: + return jsonify({'status': 'failed', 'error': str(e)}) diff --git a/app/routes/user/routes.py b/app/routes/user/routes.py index 643b10b3..03abff6f 100644 --- a/app/routes/user/routes.py +++ b/app/routes/user/routes.py @@ -32,10 +32,9 @@ def create_user(): page = common.checkAjaxInput(request.form.get('page')) activeuser = common.checkAjaxInput(request.form.get('activeuser')) group = common.checkAjaxInput(request.form.get('newgroupuser')) - token = common.checkAjaxInput(request.form.get('token')) lang = roxywi_common.get_user_lang_for_flask() - if not roxywi_common.check_user_group_for_flask(token=token): + if not roxywi_common.check_user_group_for_flask(): return 'error: Wrong group' if page == 'servers': if roxywi_auth.is_admin(level=2, role_id=role): diff --git a/app/scripts/ansible/roles/haproxy/templates/haproxy_rsyslog.conf.j2 b/app/scripts/ansible/roles/haproxy/templates/haproxy_rsyslog.conf.j2 index e152cefe..9f7f7f20 100644 --- a/app/scripts/ansible/roles/haproxy/templates/haproxy_rsyslog.conf.j2 +++ b/app/scripts/ansible/roles/haproxy/templates/haproxy_rsyslog.conf.j2 @@ -4,12 +4,17 @@ $UDPServerRun 514 module(load="builtin:omfile") -if $programname startswith 'haproxy' then { - if $syslogseverity == 6 then - action(type="omfile" file="/var/log/haproxy/access.log") - if $syslogseverity <= 3 then - action(type="omfile" file="/var/log/haproxy/error.log") - if $syslogseverity <= 5 then - action(type="omfile" file="/var/log/haproxy/status.log") - stop +if ($programname startswith 'haproxy' and $inputname == 'imudp') then { + if $syslogseverity == 6 then { + action(type="omfile" file="/var/log/haproxy/access.log") + stop + } + if $syslogseverity <= 3 then { + action(type="omfile" file="/var/log/haproxy/error.log") + stop + } + if $syslogseverity <= 5 then { + action(type="omfile" file="/var/log/haproxy/status.log"]) + stop + } } \ No newline at end of file diff --git a/app/scripts/ansible/roles/udp.yml b/app/scripts/ansible/roles/udp.yml new file mode 100644 index 00000000..019dfbff --- /dev/null +++ b/app/scripts/ansible/roles/udp.yml @@ -0,0 +1,10 @@ +- name: "UDP balancer" + hosts: all + become: yes + become_method: sudo + gather_facts: yes + roles: + - role: udp + environment: + http_proxy: "{{PROXY}}" + https_proxy: "{{PROXY}}" diff --git a/app/scripts/ansible/roles/udp/defaults/main.yml b/app/scripts/ansible/roles/udp/defaults/main.yml new file mode 100644 index 00000000..ee423d0d --- /dev/null +++ b/app/scripts/ansible/roles/udp/defaults/main.yml @@ -0,0 +1,2 @@ +keepalived_path_logs: "/var/log/keepalived-udp" +service_dir: "/etc/keepalived" \ No newline at end of file diff --git a/app/scripts/ansible/roles/udp/handlers/main.yml b/app/scripts/ansible/roles/udp/handlers/main.yml new file mode 100644 index 00000000..812556c0 --- /dev/null +++ b/app/scripts/ansible/roles/udp/handlers/main.yml @@ -0,0 +1,4 @@ +- name: Restart rsyslog + ansible.builtin.service: + name: rsyslog + state: restarted diff --git a/app/scripts/ansible/roles/udp/tasks/install.yml b/app/scripts/ansible/roles/udp/tasks/install.yml new file mode 100644 index 00000000..f8127973 --- /dev/null +++ b/app/scripts/ansible/roles/udp/tasks/install.yml @@ -0,0 +1,104 @@ +--- +- name: check if Keepalived is installed + package_facts: + manager: "auto" + +- name: Creates log directory + file: + path: "{{keepalived_path_logs}}" + state: directory + +- name: Creates UDP check directory + file: + path: "{{service_dir}}/checks" + state: directory + +- name: Copy UDP check file + template: + src: udp_check.sh.j2 + dest: "{{service_dir}}/checks/udp_check.sh" + mode: 0755 + +- name: Copy keepalived configuration for rsyslog + template: + src: rsyslog.conf.j2 + dest: /etc/rsyslog.d/51-keepalived-udp.conf + mode: 0644 + notify: Restart rsyslog + +- name: Copy keepalived configuration for logrotate + template: + src: logrotate.conf.j2 + dest: /etc/logrotate.d/keepalived-udp + mode: 0644 + +- name: Install the latest version of Keepalived + package: + name: + - keepalived + - ipvsadm + - iptables + state: present + when: "'keepalived' not in ansible_facts.packages" + environment: + http_proxy: "{{PROXY}}" + https_proxy: "{{PROXY}}" + +- name: Copy keepalived configuration in place. + template: + src: keepalived-udp.conf.j2 + dest: "{{ service_dir }}/keepalived-udp-{{ id }}.conf" + mode: 0644 + +- name: Copy keepalived UPD service file in place. + template: + src: keepalived.service.j2 + dest: "/etc/systemd/system/keepalived-udp-{{ id }}.service" + mode: 0644 + +- name: Copy iptables restore service file in place. + template: + src: iptables-restore.service.j2 + dest: /etc/systemd/system/iptables-restore.service + mode: 0644 + +- name: Enable and start service keepalived + service: + name: "keepalived-udp-{{ id }}" + daemon_reload: yes + state: restarted + enabled: yes + ignore_errors: yes + +- name: Enable and start service iptables-restore + service: + name: iptables-restore + daemon_reload: yes + state: restarted + enabled: yes + ignore_errors: yes + +- name: Enable sysctl parameters + sysctl: + name: "{{ item }}" + value: '1' + sysctl_set: yes + state: present + reload: yes + with_items: ["net.ipv4.ip_forward", "net.ipv4.conf.all.arp_ignore", "net.ipv4.ip_nonlocal_bind", "net.ipv6.ip_nonlocal_bind"] + +- name: Enable arp_ignore + sysctl: + name: net.ipv4.conf.all.arp_announce + value: '2' + sysctl_set: yes + state: present + reload: yes + +- name: Create NAT in iptables + command: "/sbin/iptables -t nat -A POSTROUTING -m ipvs --vaddr {{ vip }} -j MASQUERADE" + +- name: Save current state of the firewall in system file + community.general.iptables_state: + state: saved + path: /etc/sysconfig/iptables \ No newline at end of file diff --git a/app/scripts/ansible/roles/udp/tasks/main.yml b/app/scripts/ansible/roles/udp/tasks/main.yml new file mode 100644 index 00000000..d36cae2e --- /dev/null +++ b/app/scripts/ansible/roles/udp/tasks/main.yml @@ -0,0 +1,2 @@ +--- +- include_tasks: "{{ action }}.yml" diff --git a/app/scripts/ansible/roles/udp/tasks/uninstall.yml b/app/scripts/ansible/roles/udp/tasks/uninstall.yml new file mode 100644 index 00000000..47825e17 --- /dev/null +++ b/app/scripts/ansible/roles/udp/tasks/uninstall.yml @@ -0,0 +1,29 @@ +--- +- name: Delete NAT in iptables + shell: "/sbin/iptables -t nat -D POSTROUTING -m ipvs --vaddr {{ vip }} -j MASQUERADE" + ignore_errors: yes + +- name: Save current state of the firewall in system file + community.general.iptables_state: + state: saved + path: /etc/sysconfig/iptables + +- name: Delete keepalived configuration in place. + file: + path: "{{ service_dir }}/keepalived-udp-{{ id }}.conf" + state: absent + ignore_errors: yes + +- name: Disable and start service keepalived + service: + name: "keepalived-udp-{{ id }}" + daemon_reload: yes + state: stopped + enabled: no + ignore_errors: yes + +- name: Delete keepalived UPD service file. + file: + path: "/etc/systemd/system/keepalived-udp-{{ id }}.service" + state: absent + ignore_errors: yes diff --git a/app/scripts/ansible/roles/udp/templates/iptables-restore.service.j2 b/app/scripts/ansible/roles/udp/templates/iptables-restore.service.j2 new file mode 100644 index 00000000..af388a0a --- /dev/null +++ b/app/scripts/ansible/roles/udp/templates/iptables-restore.service.j2 @@ -0,0 +1,13 @@ +# {{ ansible_managed }} + +[Unit] +Description=Restore iptables rules +Before=network-pre.target +Conflicts=shutdown.target + +[Service] +Type=oneshot +ExecStart=/sbin/iptables-restore /etc/sysconfig/iptables + +[Install] +WantedBy=basic.target \ No newline at end of file diff --git a/app/scripts/ansible/roles/udp/templates/keepalived-udp.conf.j2 b/app/scripts/ansible/roles/udp/templates/keepalived-udp.conf.j2 new file mode 100644 index 00000000..75e5f993 --- /dev/null +++ b/app/scripts/ansible/roles/udp/templates/keepalived-udp.conf.j2 @@ -0,0 +1,19 @@ +virtual_server {{ vip }} {{ port }} { + lb_algo rr + lb_kind NAT + protocol UDP + delay_loop 10 + delay_before_retry 10 + retry 3 + + +{% for server, value in config.items() %} + real_server {{ server }} {{ value.port }} { + weight {{ value.weight }} + MISC_CHECK { + misc_path "{{ service_dir }}/checks/udp_check.sh {{ server }} {{ value.port }}" + misc_timeout 5 + } + } +{% endfor %} +} diff --git a/app/scripts/ansible/roles/udp/templates/keepalived.service.j2 b/app/scripts/ansible/roles/udp/templates/keepalived.service.j2 new file mode 100644 index 00000000..8755bf0d --- /dev/null +++ b/app/scripts/ansible/roles/udp/templates/keepalived.service.j2 @@ -0,0 +1,19 @@ +[Unit] +Description=LVS and VRRP High Availability Monitor +After=network-online.target syslog.target +Wants=network-online.target + +[Service] +SELinuxContext=system_u:system_r:keepalived_t:s0 +Type=forking +PIDFile=/var/run/keepalived-udp-{{ id }}.pid +KillMode=process +ExecStart=/usr/sbin/keepalived --log-facility=1 -f /etc/keepalived/keepalived-udp-{{ id }}.conf -p /var/run/keepalived-udp-{{ id }}.pid -r /var/run/keepalived-udp-vrrp-{{ id }}.pid -c /var/run/keepalived-udp-check-{{ id }}.pid +ExecReload=/bin/kill -HUP $MAINPID + +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=roxy-wi-udp + +[Install] +WantedBy=multi-user.target diff --git a/app/scripts/ansible/roles/udp/templates/logrotate.conf.j2 b/app/scripts/ansible/roles/udp/templates/logrotate.conf.j2 new file mode 100644 index 00000000..d282cbee --- /dev/null +++ b/app/scripts/ansible/roles/udp/templates/logrotate.conf.j2 @@ -0,0 +1,10 @@ +{{ keepalived_path_logs }}/*.log { + daily + rotate 10 + missingok + notifempty + create 0644 root root + dateext + sharedscripts + copytruncate +} diff --git a/app/scripts/ansible/roles/udp/templates/rsyslog.conf.j2 b/app/scripts/ansible/roles/udp/templates/rsyslog.conf.j2 new file mode 100644 index 00000000..a6f63392 --- /dev/null +++ b/app/scripts/ansible/roles/udp/templates/rsyslog.conf.j2 @@ -0,0 +1,28 @@ +$ModLoad imudp +$UDPServerAddress 127.0.0.1 +$UDPServerRun 514 +module(load="builtin:omfile") + +if $programname startswith 'Keepalived_healthcheckers' then { + if $syslogseverity == 6 then + action(type="omfile" file="{{ keepalived_path_logs }}/status.log") + stop + if $syslogseverity <= 3 then + action(type="omfile" file="{{ keepalived_path_logs }}/error.log") + stop + if $syslogseverity <= 5 then + action(type="omfile" file="{{ keepalived_path_logs }}/status.log") + stop +} + +if $programname startswith 'keepalived' then { + if $syslogseverity == 6 then + action(type="omfile" file="{{ keepalived_path_logs }}/status.log") + stop + if $syslogseverity <= 3 then + action(type="omfile" file="{{ keepalived_path_logs }}/error.log") + stop + if $syslogseverity <= 5 then + action(type="omfile" file="{{ keepalived_path_logs }}/status.log") + stop +} \ No newline at end of file diff --git a/app/scripts/ansible/roles/udp/templates/udp_check.sh.j2 b/app/scripts/ansible/roles/udp/templates/udp_check.sh.j2 new file mode 100644 index 00000000..37443b05 --- /dev/null +++ b/app/scripts/ansible/roles/udp/templates/udp_check.sh.j2 @@ -0,0 +1,16 @@ +#!/bin/bash + +nc_cmd=`which nc` + +nc_flavor=$($nc_cmd --version 2>&1 | grep -o nmap) +case "$nc_flavor" in +nmap) + nc_flavor_opts="-i1" + ;; +*) # default, probably openbsd + nc_flavor_opts="-w1" + ;; +esac + +$nc_cmd -uzv $nc_flavor_opts $1 $2 > /dev/null +exit $? diff --git a/app/static/css/awesome-6.3.9.css b/app/static/css/awesome-6.3.9.css index afa6f3a8..f875beb4 100644 --- a/app/static/css/awesome-6.3.9.css +++ b/app/static/css/awesome-6.3.9.css @@ -471,3 +471,8 @@ font-family: "Font Awesome 5 Regular"; content: "\f086"; } +.balance::before { + display: none; + font-family: "Font Awesome 5 Solid"; + content: "\f516"; +} diff --git a/app/static/js/add.js b/app/static/js/add.js index 94535e8b..530240a9 100644 --- a/app/static/js/add.js +++ b/app/static/js/add.js @@ -264,7 +264,7 @@ $( function() { request.term = 1 } $.ajax({ - url: "/app/add/show/ip/" + $("#serv").val(), + url: "/app/server/show/ip/" + $("#serv").val(), success: function (data) { data = data.replace(/\s+/g, ' '); response(data.split(" ")); @@ -284,7 +284,7 @@ $( function() { request.term = 1 } $.ajax({ - url: "/app/add/show/ip/" + $("#serv2").val(), + url: "/app/server/show/ip/" + $("#serv2").val(), success: function (data) { data = data.replace(/\s+/g, ' '); response(data.split(" ")); @@ -2059,7 +2059,6 @@ function make_actions_for_adding_header(section_id) { $.getScript("/app/static/js/overview.js"); $( "select" ).selectmenu(); $('[name=headers_method]').selectmenu({width: 180}); - // $('[name=acl_then]').selectmenu({width: 180}); } var bind_option = '

' + ': ' + @@ -2084,7 +2083,7 @@ function make_actions_for_adding_bind(section_id) { request.term = 1 } $.ajax({ - url: "/app/add/show/ip/" + $("#" + serv).val(), + url: "/app/server/show/ip/" + $("#" + serv).val(), success: function (data) { data = data.replace(/\s+/g, ' '); response(data.split(" ")); @@ -2099,17 +2098,17 @@ function make_actions_for_adding_bind(section_id) { }); } function makeid(length) { - var result = ''; - var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - var charactersLength = characters.length; - for ( var i = 0; i < length; i++ ) { + let result = ''; + let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let charactersLength = characters.length; + for ( let i = 0; i < length; i++ ) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } function showUserlists() { - var serv = $("#existing_userlist_serv option:selected").val(); - var select_id = $("#existing_userlist_serv"); + let serv = $("#existing_userlist_serv option:selected").val(); + let select_id = $("#existing_userlist_serv"); if (!checkIsServerFiled(select_id)) return false; $.ajax({ url: "/app/add/haproxy/userlist/" + serv, diff --git a/app/static/js/ha.js b/app/static/js/ha.js index bce8b10c..21ee684c 100644 --- a/app/static/js/ha.js +++ b/app/static/js/ha.js @@ -1,9 +1,3 @@ -function ValidateIPaddress(ipaddress) { - if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress)) { - return (true) - } - return (false) -} $( function() { $("select").selectmenu({ width: 180 @@ -244,9 +238,7 @@ function createHaClusterStep1(edited=false, cluster_id=0, clean=true) { dialog_div.dialog('open'); } function createHaClusterStep2(edited=false, cluster_id=0, jsonData='') { - var add_word = $('#translate').attr('data-next'); var cancel_word = $('#translate').attr('data-cancel'); - var next_word = $('#translate').attr('data-next'); var back_word = $('#translate').attr('data-back'); var save_word = $('#translate').attr('data-save'); var apply_word = $('#translate').attr('data-apply'); diff --git a/app/static/js/install.js b/app/static/js/install.js index 439083ab..a0caf9fd 100644 --- a/app/static/js/install.js +++ b/app/static/js/install.js @@ -175,8 +175,7 @@ function installService(service) { showErrorStatus(nice_names[service], $(select_id + ' option:selected').text()); }, data: { - jsonData: JSON.stringify(jsonData), - token: $('#token').val() + jsonData: JSON.stringify(jsonData) }, type: "POST", success: function (data) { @@ -232,10 +231,6 @@ function showExporterVersion(exporter) { var nice_names = {'haproxy': 'HAProxy', 'nginx': 'NGINX', 'apache': 'Apache', 'node': 'Node', 'keepalived': 'Keepalived'}; $.ajax({ url: "/app/install/exporter/"+ exporter +"/version/" + $('#' + exporter + '_exp_addserv option:selected').val(), - // data: { - // token: $('#token').val() - // }, - // type: "POST", success: function (data) { data = data.replace(/^\s+|\s+$/g, ''); if (data.indexOf('error:') != '-1') { @@ -252,10 +247,6 @@ function showExporterVersion(exporter) { function showServiceVersion(service) { $.ajax({ url: "/app/install/" + service + "/version/" + $('#' + service + 'addserv option:selected').val(), - // data: { - // token: $('#token').val() - // }, - // type: "POST", success: function (data) { data = data.replace(/^\s+|\s+$/g, ''); if (data.indexOf('error: ') != '-1') { diff --git a/app/static/js/udp.js b/app/static/js/udp.js new file mode 100644 index 00000000..0e7ebd8f --- /dev/null +++ b/app/static/js/udp.js @@ -0,0 +1,472 @@ +var cancel_word = $('#translate').attr('data-cancel'); +$( function() { + $( "#ha-cluster" ).on('selectmenuchange',function() { + let cluster_id = $( "#ha-cluster option:selected" ).val(); + if (cluster_id != '------') { + getHAClusterVIPS(cluster_id); + } else { + clearUdpVip(); + } + }); + $("#new-udp-ip").autocomplete({ + source: function (request, response) { + if (!checkIsServerFiled('#serv')) return false; + if (request.term == "") { + request.term = 1 + } + $.ajax({ + url: "/app/server/show/ip/" + $("#serv").val(), + success: function (data) { + data = data.replace(/\s+/g, ' '); + response(data.split(" ")); + } + }); + }, + appendTo: "#create-udp-step-2", + autoFocus: true, + minLength: -1, + select: function (event, ui) { + $('#new-listener-port').focus(); + } + }); +}); +function getHAClusterVIPS(cluster_id) { + $.ajax({ + url: `/app/ha/cluster/${cluster_id}/vips`, + async: false, + contentType: "application/json; charset=utf-8", + success: function (data) { + if (data.status === 'failed') { + toastr.error(data.error); + return false; + } else { + clearUdpVip(); + $('#new-udp-vip').append('') + data.forEach(function (obj) { + console.log(obj.vip); + $('#new-udp-vip').append('') + }); + $('#new-udp-vip').selectmenu("refresh"); + } + } + }); + return true; +} +function createUDPListener(edited=false, listener_id=0, clean=true) { + let next_word = $('#translate').attr('data-next'); + let tabel_title = $("#create-udp-step-1-overview").attr('title'); + clearListenerDialog(); + if (edited) { + tabel_title = $("#create-udp-step-1-overview").attr('data-edit'); + } + if (edited && clean) { + let place = $('#listener-type-'+ listener_id).val(); + if (place === 'cluster') { + $('.new-udp-ha-cluster-tr').show(); + $('.new-udp-servers-tr').hide(); + } else { + $('.new-udp-ha-cluster-tr').hide(); + $('.new-udp-servers-tr').show(); + } + $.ajax({ + url: `/app/udp/listener/${listener_id}/settings`, + type: "GET", + async: false, + contentType: "application/json; charset=utf-8", + success: function (data) { + $('#new-listener-name').val(data.name.replaceAll("'", "")); + $('#new-listener-type').val(place); + $('#new-listener-port').val(data.port); + $('#new-listener-desc').val(data.desc.replaceAll("'", "")); + if (place === 'cluster') { + $.when(getHAClusterVIPS(data.cluster_id)).done(function () { + $("#new-udp-vip option").filter(function () { + return $(this).text() == data.vip; + }).attr('selected', true); + $("#new-udp-vip").selectmenu('refresh'); + }); + $("#ha-cluster").val(data.cluster_id).change(); + $("#ha-cluster").attr('disabled', 'disabled'); + $("#ha-cluster").selectmenu('refresh'); + } else { + $("#serv").val(data.server_id).change(); + $("#serv").attr('disabled', 'disabled'); + $("#serv").selectmenu('refresh'); + $('#new-udp-ip').val(data.vip); + } + $('#new-udp-servers-td').empty(); + $('#new-udp-servers-td').append(''); + for(let server in data.config) { + createBackendServer(server, data.config[server]['port'], data.config[server]['weight']); + } + } + }); + createUDPListenerStep2(edited, listener_id, place); + return false; + } + let dialog_div = $("#create-udp-step-1").dialog({ + autoOpen: false, + resizable: false, + height: "auto", + width: 630, + modal: true, + title: tabel_title, + show: { + effect: "fade", + duration: 200 + }, + hide: { + effect: "fade", + duration: 200 + }, + buttons: [{ + text: next_word, + click: function () { + if ($('#new-udp-type option:selected').val().indexOf('--') != '-1') { + toastr.error('error: Select Listener type'); + return false; + } + let place = ''; + if ($('#new-udp-type option:selected').val() == 'cluster') { + $('.new-udp-ha-cluster-tr').show(); + $('.new-udp-servers-tr').hide(); + place = 'cluster'; + } else { + $('.new-udp-ha-cluster-tr').hide(); + $('.new-udp-servers-tr').show(); + place = 'server'; + } + $('#new-listener-type').val(place); + createUDPListenerStep2(edited, listener_id, place); + $(this).dialog("close"); + toastr.clear(); + } + }, { + text: cancel_word, + click: function () { + $(this).dialog("close"); + clearListenerDialog(edited); + } + }] + }); + dialog_div.dialog('open'); +} +function createUDPListenerStep2(edited, listener_id, place) { + let save_word = $('#translate').attr('data-save'); + let apply_word = $('#translate').attr('data-apply'); + let back_word = $('#translate').attr('data-back'); + let tabel_title = $("#create-udp-step-2-overview").attr('title'); + if (edited) { + tabel_title = $("#create-udp-step-1-overview").attr('data-edit'); + } + let dialog_div = $("#create-udp-step-2").dialog({ + autoOpen: false, + resizable: false, + height: "auto", + width: 630, + modal: true, + title: tabel_title, + show: { + effect: "fade", + duration: 200 + }, + hide: { + effect: "fade", + duration: 200 + }, + buttons: [{ + text: save_word, + click: function () { + if (!validateUDPListenerForm(place)) { + return false; + } + jsonData = getFormData($("#create_udp_listener")); + saveUdpListener(jsonData, $(this), listener_id, edited); + } + }, { + text: apply_word, + click: function () { + jsonData = getFormData($("#create_udp_listener")); + saveUdpListener(jsonData, $(this), listener_id, edited, 1); + } + }, { + text: back_word, + click: function () { + $(this).dialog("close"); + createUDPListener(edited, listener_id, false); + } + }, { + text: cancel_word, + click: function () { + $(this).dialog("close"); + clearListenerDialog(edited); + } + }] + }); + dialog_div.dialog('open'); +} +function validateUDPListenerForm(place) { + if ($('#new-listener-name').val() == '') { + toastr.error('error: Fill in the Name field'); + return false; + } + if ($('#new-listener-port').val() == '') { + toastr.error('error: Fill in the Port field'); + return false; + } + if (place == 'server') { + if ($('#new-udp-ip').val() == '') { + toastr.error('error: Fill in the IP field'); + return false; + } + if (!ValidateIPaddress($('#new-udp-ip').val())) { + toastr.error('error: Wrong IP'); + return false; + } + if ($('#serv option:selected').val().indexOf('--') != '-1') { + toastr.error('error: Select a server'); + return false; + } + } else { + if ($('#ha-cluster option:selected').val().indexOf('--') != '-1') { + toastr.error('error: Select a HA cluster'); + return false; + } + if ($('#new-udp-ha-ports').val() == '') { + toastr.error('error: Fill in the Port field'); + return false; + } + if ($('#new-udp-ha-weight').val() == '') { + toastr.error('error: Fill in the Weight field'); + return false; + } + if ($('#new-udp-vip option:selected').val().indexOf('--') != '-1') { + toastr.error('error: Select the VIP address'); + return false; + } + if (!ValidateIPaddress($('#new-udp-vip option:selected').text())) { + toastr.error('error: Wrong VIP'); + return false; + } + } + return true; +} +function getFormData($form) { + $("#serv").attr('disabled', false); + $("#serv").selectmenu('refresh'); + let unindexed_array = $form.serializeArray(); + let indexed_array = {}; + indexed_array['servers'] = {}; + console.log(unindexed_array) + + $.map(unindexed_array, function (n, i) { + indexed_array[n['name']] = n['value']; + }); + $('.servers').each(function () { + let ip = $(this).children("input[name='new-udp-server']").val(); + if (ip === undefined || ip === '') { + return; + } + let port = $(this).children("input[name='new-udp-port']").val(); + let weight = $(this).children("input[name='new-udp-weight']").val(); + indexed_array['servers'][ip] = {port, weight}; + }); + indexed_array['ha-cluster'] = $('#ha-cluster').val(); + indexed_array['new-udp-router_id'] = $('#new-udp-vip').val(); + indexed_array['new-udp-vip'] = $('#new-udp-vip option:selected').text(); + $("#serv").attr('disabled', 'disabled'); + $("#serv").selectmenu('refresh'); + return indexed_array; +} +function saveUdpListener(jsonData, dialog_id, listener_id=0, edited=0, reconfigure=0) { + let req_method = 'POST'; + if (edited) { + req_method = 'PUT'; + jsonData['listener_id'] = listener_id; + } + $.ajax({ + url: "/app/udp/listener", + type: req_method, + data: JSON.stringify(jsonData), + contentType: "application/json; charset=utf-8", + success: function (data) { + if (data.status === 'failed') { + toastr.error(data.error); + } else { + if (edited) { + getUDPListener(listener_id); + $("#listener-" + listener_id).addClass("update", 1000); + setTimeout(function () { + $("#listener-" + listener_id).removeClass("update"); + }, 2500); + } else { + listener_id = data.listener_id; + getUDPListener(data.listener_id, true); + } + if (reconfigure) { + NProgress.start(); + $.when(Reconfigure(listener_id)).done(function () { + dialog_id.dialog("close"); + NProgress.done(); + }); + } else { + dialog_id.dialog("close"); + } + toastr.success('Listener ' + data.status); + } + } + }); +} +function Reconfigure(listener_id) { + return $.ajax({ + url: "/app/install/udp", + data: {listener_id: listener_id}, + async: false, + type: "POST", + success: function (data) { + if (data.status === 'failed') { + toastr.error(data.error); + } else { + parseAnsibleJsonOutput(data, 'UDP listener', ''); + } + } + }); +} +function getUDPListener(listener_id, new_listener=false) { + $.ajax({ + url: "/app/udp/listener/" + listener_id, + success: function (data) { + data = data.replace(/^\s+|\s+$/g, ''); + if (data.indexOf('error:') != '-1') { + toastr.error(data); + } else { + if (new_listener) { + $('.up-pannel').append(data); + } else { + $('#listener-' + listener_id).replaceWith(data); + } + $('#listener-'+listener_id).removeClass('animated-background'); + } + } + }); + $.getScript("/app/static/js/fontawesome.min.js"); +} +function confirmDeleteListener(listener_id) { + let delete_word = $('#translate').attr('data-delete'); + $("#dialog-confirm").dialog({ + resizable: false, + height: "auto", + width: 400, + modal: true, + title: delete_word + " " + $('#listener-name-' + listener_id).text() + "?", + buttons: [{ + text: delete_word, + click: function () { + $(this).dialog("close"); + deleteListener(listener_id); + } + }, { + text: cancel_word, + click: function () { + $(this).dialog("close"); + } + }] + }); +} +function deleteListener(listener_id) { + let jsonData = {'listener_id': listener_id} + $.ajax({ + url: "/app/udp/listener", + type: "DELETE", + data: JSON.stringify(jsonData), + contentType: "application/json; charset=utf-8", + success: function (data) { + if (data.status === 'failed') { + toastr.error(data.error); + } else { + $('#listener-' + listener_id).remove(); + } + } + }); +} +function clearListenerDialog(edited=0) { + $('#new-listener-name').val(''); + $('#new-listener-desc').val(''); + $('#ha-cluster-master-interface').val(''); + $('#new-udp-ip').val(''); + $('#vrrp-ip').prop("readonly", false); + $('#new-listener-port').val(''); + $("#ha-cluster").attr('disabled', false); + $("#serv").attr('disabled', false); + clearUdpVip() + $('#new-udp-servers-td').empty(); + $('#new-udp-servers-td').append(''); + createBackendServer(); + let selects = ['new-udp-type', 'ha-cluster', 'new-udp-vip', 'serv'] + for (let select of selects) { + unselectSelectMenu(select); + } +} +function unselectSelectMenu(select_id) { + $('#' + select_id + ' option').attr('selected', false); + $('#' + select_id + ' option:first').attr('selected', 'selected'); + $('#' + select_id).selectmenu("refresh"); +} +function clearUdpVip() { + $('#new-udp-vip').selectmenu( "destroy" ); + $('#new-udp-vip').empty(); + $('#new-udp-vip').selectmenu(); +} +function confirmUdpBalancerAction(action, listener_id) { + let action_word = $('#translate').attr('data-'+action); + console.log('#litener-name-'+listener_id); + let l_name = $('#listener-name-'+listener_id).text(); + $( "#dialog-confirm" ).dialog({ + resizable: false, + height: "auto", + width: 400, + modal: true, + title: action_word + " " + l_name + "?", + buttons: [{ + text: action_word, + click: function () { + $(this).dialog("close"); + ajaxActionListener(action, listener_id); + } + }, { + text: cancel_word, + click: function() { + $( this ).dialog( "close" ); + } + }] + }); +} +function ajaxActionListener(action, listener_id) { + $.ajax({ + url: `/app/udp/listener/${listener_id}/${action}`, + type: "GET", + contentType: "application/json; charset=utf-8", + success: function (data) { + if (data.status === 'failed') { + toastr.error(data.error); + return false; + } else { + toastr.success(`Listener has been ${action}ed`); + getUDPListener(listener_id); + } + } + }); +} +function createBackendServer(server='', port='', weight='1') { + let server_word = $('#translate').attr('data-server'); + let port_word = $('#translate').attr('data-port'); + let weight_word = $('#translate').attr('data-weight'); + let delete_word = $('#translate').attr('data-delete'); + $('

' + + server_word+': ' + + port_word + ': ' + + weight_word + ': ' + + '' + + '
').insertBefore('.add-server'); + $.getScript("/app/static/js/fontawesome.min.js"); +} \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html index 953289cc..f366268d 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -19,12 +19,12 @@ data-raw="{{lang.words.raw|title()}}" data-resp_time="{{lang.smon_page.desc.resp_time}}" data-next="{{lang.words.next|title()}}" data-back="{{lang.words.back|title()}}" data-installing="{{lang.words.installing|title()}}" data-creating="{{lang.words.creating|title()}}" data-roxywi_timeout="{{lang.ha_page.roxywi_timeout}}" data-check_apache_log="{{lang.ha_page.check_apache_log}}" data-was_installed="{{lang.ha_page.was_installed}}" data-start_enter="{{lang.ha_page.start_enter}}" - data-apply="{{lang.words.apply|title()}}" data-reconfigure="{{lang.words.reconfigure|title()}}" /> + data-apply="{{lang.words.apply|title()}}" data-reconfigure="{{lang.words.reconfigure|title()}}" data-server="{{lang.words.server|title()}}" data-port="{{lang.words.port}}" + data-weight="{{lang.words.weight}}" /> {% include 'include/main_head.html' %} {% if g.user_params['user'] %} - {% include 'include/main_menu.html' %}
diff --git a/app/templates/include/admin_settings.html b/app/templates/include/admin_settings.html index affc831b..655c3594 100644 --- a/app/templates/include/admin_settings.html +++ b/app/templates/include/admin_settings.html @@ -43,8 +43,8 @@ {{set.param}} - {% if set.param in ('ldap_password', 'haproxy_stats_password', 'nginx_stats_password', 'apache_stats_password', 'rabbitmq_password', 'mail_smtp_password') %} - {% if set.value is none %} + {% if set.param in ('ldap_password', 'haproxy_stats_password', 'nginx_stats_password', 'apache_stats_password', 'rabbitmq_password', 'mail_smtp_password', 'license') %} + {% if set.value == '' or set.value is none %} {{ input(set.param, size='25', type='password', style='width: 210px;') }} {% else %} {{ input(set.param, size='25', type='password', style='width: 210px;', placeholder='******') }} diff --git a/app/templates/include/input_macros.html b/app/templates/include/input_macros.html index f02d0419..a62ce4d9 100644 --- a/app/templates/include/input_macros.html +++ b/app/templates/include/input_macros.html @@ -18,7 +18,7 @@ {%- endmacro %} -{%- macro select(id, values, name='', required='', first='', class='', selected='', title='', disabled='true', is_servers='') -%} +{%- macro select(id, values, name='', required='', first='', class='', selected='', title='', disabled='true', is_servers='', by_id='') -%} {% if name == '' %} {% set name = id %} {% endif %} @@ -35,11 +35,19 @@ {% endif %} {% if is_servers %} {% for value in values %} - {% if value.2 == selected|string() %} - - {% else %} - - {% endif %} + {% if by_id %} + {% if value.0 == selected|string() %} + + {% else %} + + {% endif %} + {% else %} + {% if value.2 == selected|string() %} + + {% else %} + + {% endif %} + {% endif %} {% endfor %} {% else %} {% for v, des in values.items() %} diff --git a/app/templates/include/main_menu.html b/app/templates/include/main_menu.html index 3a33b935..463e4117 100644 --- a/app/templates/include/main_menu.html +++ b/app/templates/include/main_menu.html @@ -69,6 +69,11 @@ {% endif %} + {% if '6' in g.user_params['user_services'] %} + {% if g.user_params['role'] <= 2 %} +
  • {{lang.menu_links.udp.title}}
  • + {% endif %} + {% endif %} {% if '3' in g.user_params['user_services'] %} {% if g.user_params['role'] <= 2 %}
  • diff --git a/app/templates/languages/en.html b/app/templates/languages/en.html index 4d389886..f0a91967 100644 --- a/app/templates/languages/en.html +++ b/app/templates/languages/en.html @@ -115,6 +115,9 @@ }, "history": { "title": "History of" + }, + "udp": { + "title": "UDP balancers" } } %} @@ -170,6 +173,7 @@ }, "main": { "time_zone": "Time Zone", + "license": "License key", "proxy": "IP address and port of the proxy server. Use proto://ip:port", "session_ttl": "TTL for a user session (in days)", "token_ttl": "TTL for a user token (in days)", @@ -508,6 +512,12 @@ "save_apply": "Save - means saving the HA cluster settings for Roxy-WI, without changing the component settings on the cluster members.
    Apply - recreate the HA cluster configuration on the cluster member servers and install additional services.", } %} +{% set udp_page = { + "save_apply": "Save - means saving the UDP listener settings for Roxy-WI, without changing the component settings on the cluster members or standalone server.
    Apply - recreate the UDP listener configuration on the cluster member servers or standalone server.", + "listener_ip": "IP for binding UDP Listener. Start typing", + "listener_port": "Port for binding UDP Listener", + } +%} {% set nettools_page = { "ip_or_name": "Enter IP or Name", "dns_name": "Enter a DNS name", @@ -928,5 +938,9 @@ "agent": "agent", "agent2": "agent", "reconfigure": "reconfigure", + "listener": "listener", + "listeners": "listeners", + "weight": "weight", + "where": "where", } %} diff --git a/app/templates/languages/fr.html b/app/templates/languages/fr.html index 3c479aa0..fbcb041d 100644 --- a/app/templates/languages/fr.html +++ b/app/templates/languages/fr.html @@ -115,6 +115,9 @@ }, "history": { "title": "Historique de" + }, + "udp": { + "title": "Équilibreurs UDP" } } %} @@ -170,6 +173,7 @@ }, "main": { "time_zone": "Fuseau horaire", + "license": "Clé de licence", "proxy": "Adresse IP et port du proxy server. format proto://ip:port", "session_ttl": "TTL pour la session utilisateur (en jours)", "token_ttl": "TTL pour le jeton de l\'utilisateur (en jours)", @@ -508,6 +512,12 @@ "save_apply": "Enregistrer - signifie enregistrer les paramètres du cluster HA pour Roxy-WI, sans modifier les paramètres des composants sur les membres du cluster.
    Appliquer : recréez la configuration du cluster HA sur les serveurs membres du cluster et installez des services supplémentaires.", } %} +{% set udp_page = { + "save_apply": "Enregistrer - signifie enregistrer les paramètres du Écouteur UDP pour Roxy-WI, sans modifier les paramètres des composants sur les membres du cluster ou serveur autonome.
    Appliquer : recréez la configuration du Écouteur UDP sur les serveurs membres du cluster et installez des services supplémentaires ou serveur autonome.", + "listener_ip": "IP pour lier l’écouteur UDP. Commencer à écrire", + "listener_port": "Port pour lier l'écouteur UDP", + } +%} {% set nettools_page = { "ip_or_name": "Entrez l'adresse IP ou le nom", "dns_name": "Entrez un nom DNS", @@ -928,5 +938,9 @@ "agent": "agent", "agent2": "agent", "reconfigure": "reconfigurer", + "listener": "auditeur", + "listeners": "les auditeurs", + "weight": "poids", + "where": "où", } %} diff --git a/app/templates/languages/pt-br.html b/app/templates/languages/pt-br.html index f26991ea..5aca3225 100644 --- a/app/templates/languages/pt-br.html +++ b/app/templates/languages/pt-br.html @@ -115,6 +115,9 @@ }, "history": { "title": "Histórico de" + }, + "udp": { + "title": "Balanceadores UDP" } } %} @@ -170,6 +173,7 @@ }, "main": { "time_zone": "Fuso horário.", + "license": "Chave de licença", "proxy": "Endereço e porta de prowy. Utiliza proto://ip:port", "session_ttl": "TTL de sessão de usuario (em dias)", "token_ttl": "TTL de token de usuario (em dias)", @@ -508,6 +512,12 @@ "save_apply": "Salvar - significa salvar as configurações do cluster HA para Roxy-WI, sem alterar as configurações do componente nos membros do cluster.
    Aplicar – recrie a configuração do cluster HA nos servidores membros do cluster e instale serviços adicionais.", } %} +{% set udp_page = { + "save_apply": "Salvar - significa salvar as configurações do Ouvinte UDP para Roxy-WI, sem alterar as configurações do componente nos membros do cluster ou servidor independente.
    Aplicar – recrie a configuração do Ouvinte UDP nos servidores membros do cluster e instale serviços adicionais ou servidor independente.", + "listener_ip": "IP para vincular o ouvinte UDP. Começe a digitar", + "listener_port": "Porta para ligação do ouvinte UDP", + } +%} {% set nettools_page = { "ip_or_name": "Digite IP ou nome", "dns_name": "Insira um nome DNS", @@ -928,5 +938,9 @@ "agent": "agente", "agent2": "agente", "reconfigure": "reconfigurar", + "listener": "ouvinte", + "listeners": "ouvintes", + "weight": "peso", + "where": "onde", } %} diff --git a/app/templates/languages/ru.html b/app/templates/languages/ru.html index eef18071..f91a1983 100644 --- a/app/templates/languages/ru.html +++ b/app/templates/languages/ru.html @@ -115,6 +115,9 @@ }, "history": { "title": "История" + }, + "udp": { + "title": "UDP балансировщики" } } %} @@ -170,6 +173,7 @@ }, "main": { "time_zone": "Временная зона", + "license": "Лицензионный ключ", "proxy": "IP-адрес и порт прокси сервера. Формат: proto://ip:port", "session_ttl": "Время жизни пользовательских сессий (в днях)", "token_ttl": "Время жизни пользовательских токенов (в днях)", @@ -508,6 +512,12 @@ "save_apply": "Сохранить - подразумевает сохранение настроек HА кластера для Roxy-WI, без изменения настроек компонентов на участниках кластера.
    Применить - пересоздать конфигурацию HА кластера на серверах участниках кластера и установит дополнительные сервисы." } %} +{% set udp_page = { + "save_apply": "Сохранить - подразумевает сохранение настроек UDP слушателя для Roxy-WI, без изменения настроек компонентов на участниках кластера или standalone сервера.
    Применить - пересоздать конфигурацию HА кластера на серверах участниках кластера и установит дополнительные сервисы или standalone сервера.", + "listener_ip": "IP для привязки UDP Listener. Начните печатать", + "listener_port": "Порт для привязки UDP Listener", + } +%} {% set nettools_page = { "ip_or_name": "Введите IP или доменное имя", "dns_name": "Введите доменное имя", @@ -928,5 +938,9 @@ "agent": "агент", "agent2": "агента", "reconfigure": "переконфигурировать", + "listener": "слушатель", + "listeners": "слушатели", + "weight": "вес", + "where": "где", } %} diff --git a/app/templates/udp/listener.html b/app/templates/udp/listener.html new file mode 100644 index 00000000..d48084a2 --- /dev/null +++ b/app/templates/udp/listener.html @@ -0,0 +1,41 @@ +{% import 'languages/'+lang|default('en')+'.html' as lang %} +{% from 'include/input_macros.html' import input, checkbox, copy_to_clipboard %} +
    +
    + {{listener.name|replace("'", "")}} + {% if listener.desc != '' %} ({{listener.desc|replace("'", "")}}) {% endif %} + + {% if g.user_params['role'] <= 3 %} + + + + + + {% endif %} +{# #} + +
    +
    + {% if listener.cluster_id %} + {% for cluster in clusters %} + {% if listener.cluster_id == cluster.id %} + {{ input('listener-type-'+listener.id|string(), value='cluster', type='hidden') }} + {{lang.words.cluster|title}} {{ lang.words.name }}: + {{ cluster.name }} + {% endif %} + {% endfor %} + VIP: + {% else %} + {{ input('listener-type-'+listener.id|string(), value='server', type='hidden') }} + {{ lang.words.server|title() }}: {{ server.hostname }} + IP: + {% endif %} + {{ copy_to_clipboard(value=listener.vip) }}
    + {{lang.words.port|title()}}: {{ listener.port }}
    + {{ lang.words.backend|title() }} {{ lang.words.servers }}:
    + {% set config = listener.config|string_to_dict %} + {% for c, v in config.items() %} + {{ lang.words.server|title() }}: {{ copy_to_clipboard(value=c) }}, {{ lang.words.port }}: {{ v.port }}, {{ lang.words.weight }}: {{ v.weight }}
    + {% endfor %} +
    +
    diff --git a/app/templates/udp/listeners.html b/app/templates/udp/listeners.html new file mode 100644 index 00000000..3e63f131 --- /dev/null +++ b/app/templates/udp/listeners.html @@ -0,0 +1,141 @@ +{% extends "base.html" %} +{% block title %}{{ lang.menu_links.udp.title }} {% endblock %} +{% block h2 %}{{ lang.menu_links.udp.title }} {% endblock %} +{% block content %} +{% from 'include/input_macros.html' import input, checkbox, copy_to_clipboard, select %} + + + + + +{% if user_subscription.user_status == 0 or user_subscription.user_plan == 'user' %} + {% include 'include/no_sub.html' %} +{% elif not is_needed_tool %} +
    +

    {{lang.admin_page.desc.no_ansible}} Ansible

    . + There is no server +

    + {{lang.words.read|title()}} here {{lang.phrases.how_to_install}} Ansible. +

    +
    +{% else %} +{% if g.user_params['role'] <= 3 %} +
    + {{lang.words.create|title()}} UDP {{ lang.words.listener }}
    +{% endif %} +
    + {% for listener in listeners %} +
    + {% endfor %} +
    + + + + +{% endif %} +{% endblock %}