Aidaho 2024-06-05 10:20:43 +03:00
parent 07cdbbfab5
commit 7d47e94160
49 changed files with 1451 additions and 242 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

37
app/modules/db/udp.py Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = []

132
app/modules/service/udp.py Normal file
View File

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

View File

@ -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/<server_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")

View File

@ -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('/<service>/<int:cluster_id>/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)

View File

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

View File

@ -55,6 +55,24 @@ def show_if(server_ip):
return server_mod.ssh_command(server_ip, command)
@bp.route('/show/ip/<server_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/<int:server_id>')
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():

View File

@ -0,0 +1,5 @@
from flask import Blueprint
bp = Blueprint('udp', __name__)
from app.routes.udp import routes

105
app/routes/udp/routes.py Normal file
View File

@ -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('/<service>/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('/<service>/listener/<int:listener_id>')
@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('/<service>/listener/<int:listener_id>/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('/<service>/listener/<int:listener_id>/<action>')
@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)})

View File

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

View File

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

View File

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

View File

@ -0,0 +1,2 @@
keepalived_path_logs: "/var/log/keepalived-udp"
service_dir: "/etc/keepalived"

View File

@ -0,0 +1,4 @@
- name: Restart rsyslog
ansible.builtin.service:
name: rsyslog
state: restarted

View File

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

View File

@ -0,0 +1,2 @@
---
- include_tasks: "{{ action }}.yml"

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,10 @@
{{ keepalived_path_logs }}/*.log {
daily
rotate 10
missingok
notifempty
create 0644 root root
dateext
sharedscripts
copytruncate
}

View File

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

View File

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

View File

@ -471,3 +471,8 @@
font-family: "Font Awesome 5 Regular";
content: "\f086";
}
.balance::before {
display: none;
font-family: "Font Awesome 5 Solid";
content: "\f516";
}

View File

@ -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 = '<p id="new_bind_p"><input type="text" name="ip" size="15" placeholder="Any" class="form-control ui-autocomplete-input" autocomplete="off">' +
'<b>:</b> ' +
@ -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,

View File

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

View File

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

472
app/static/js/udp.js Normal file
View File

@ -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('<option value="------" selected>------</option>')
data.forEach(function (obj) {
console.log(obj.vip);
$('#new-udp-vip').append('<option value="' + obj.id + '">' + obj.vip + '</option>')
});
$('#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('<a class="link add-server" title="Add backend server" onclick="createBackendServer()"></a>');
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('<a class="link add-server" title="Add backend server" onclick="createBackendServer()"></a>');
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');
$('<div class="servers">' +
server_word+': <input name="new-udp-server" value="' + server + '" class="form-control" placeholder="10.0.0.1">' +
port_word + ': <input name="new-udp-port" value="' + port + '" type="number" class="form-control" placeholder="443" style="width: 50px">' +
weight_word + ': <input name="new-udp-weight" value="' + weight + '" type="number" class="form-control" style="width: 30px">' +
'<span class="minus minus-style" title="'+delete_word+' '+server_word+'" onclick="$(this).parent().remove()"></span>' +
'</div>').insertBefore('.add-server');
$.getScript("/app/static/js/fontawesome.min.js");
}

View File

@ -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' %}
</head>
<body>
{% if g.user_params['user'] %}
<input type="hidden" id="token" value="{{ g.user_params['token'] }}">
<script>show_version();</script>
{% include 'include/main_menu.html' %}
<div class="container">

View File

@ -43,8 +43,8 @@
{{set.param}}
</td>
<td class="addOption">
{% 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='******') }}

View File

@ -18,7 +18,7 @@
<label for="{{id}}" title="{{title}}" class="{{id}}" style="{{style}}" data-help="{{title}}">{{desc}}</label><input name="{{name}}" type="checkbox" id="{{id}}" value="{{value|e}}" {{checked}} {{disabled}} />
{%- 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() %}
<option value="{{value.2}}" selected>{{value.1}}</option>
{% else %}
<option value="{{ value.2 }}">{{ value.1 }}</option>
{% endif %}
{% if by_id %}
{% if value.0 == selected|string() %}
<option value="{{value.0}}" selected>{{value.1}}</option>
{% else %}
<option value="{{ value.0 }}">{{ value.1 }}</option>
{% endif %}
{% else %}
{% if value.2 == selected|string() %}
<option value="{{value.2}}" selected>{{value.1}}</option>
{% else %}
<option value="{{ value.2 }}">{{ value.1 }}</option>
{% endif %}
{% endif %}
{% endfor %}
{% else %}
{% for v, des in values.items() %}

View File

@ -69,6 +69,11 @@
</ul>
</li>
{% endif %}
{% if '6' in g.user_params['user_services'] %}
{% if g.user_params['role'] <= 2 %}
<li><a href="{{ url_for('udp.listener_funct', service='udp') }}" title="{{lang.menu_links.udp.title}}" class="balance {% if request.url_rule.endpoint == 'udp.listener_funct' %} menu-active{% endif %}">{{lang.menu_links.udp.title}}</a></li>
{% endif %}
{% endif %}
{% if '3' in g.user_params['user_services'] %}
{% if g.user_params['role'] <= 2 %}
<li class="p_menu">

View File

@ -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": "<b>Save</b> - means saving the HA cluster settings for Roxy-WI, without changing the component settings on the cluster members. <br /> <b>Apply</b> - recreate the HA cluster configuration on the cluster member servers and install additional services.",
}
%}
{% set udp_page = {
"save_apply": "<b>Save</b> - means saving the UDP listener settings for Roxy-WI, without changing the component settings on the cluster members or standalone server. <br /> <b>Apply</b> - 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",
}
%}

View File

@ -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": "<b>Enregistrer</b> - signifie enregistrer les paramètres du cluster HA pour Roxy-WI, sans modifier les paramètres des composants sur les membres du cluster. <br /> <b>Appliquer </b>: recréez la configuration du cluster HA sur les serveurs membres du cluster et installez des services supplémentaires.",
}
%}
{% set udp_page = {
"save_apply": "<b>Enregistrer</b> - 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. <br /> <b>Appliquer </b>: 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ù",
}
%}

View File

@ -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": "<b>Salvar</b> - significa salvar as configurações do cluster HA para Roxy-WI, sem alterar as configurações do componente nos membros do cluster. <br /> <b>Aplicar</b> recrie a configuração do cluster HA nos servidores membros do cluster e instale serviços adicionais.",
}
%}
{% set udp_page = {
"save_apply": "<b>Salvar</b> - 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. <br /> <b>Aplicar</b> 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",
}
%}

View File

@ -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": "<b>Сохранить</b> - подразумевает сохранение настроек HА кластера для Roxy-WI, без изменения настроек компонентов на участниках кластера. <br /> <b>Применить</b> - пересоздать конфигурацию HА кластера на серверах участниках кластера и установит дополнительные сервисы."
}
%}
{% set udp_page = {
"save_apply": "<b>Сохранить</b> - подразумевает сохранение настроек UDP слушателя для Roxy-WI, без изменения настроек компонентов на участниках кластера или standalone сервера. <br /> <b>Применить</b> - пересоздать конфигурацию 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": "где",
}
%}

View File

@ -0,0 +1,41 @@
{% import 'languages/'+lang|default('en')+'.html' as lang %}
{% from 'include/input_macros.html' import input, checkbox, copy_to_clipboard %}
<div id="listener-{{listener.id}}" class="div-server-hapwi">
<div class="server-name">
<span id="listener-name-{{listener.id}}">{{listener.name|replace("'", "")}}</span>
<span id="listener-desc-{{listener.id}}">{% if listener.desc != '' %} ({{listener.desc|replace("'", "")}}) {% endif %}</span>
<span class="server-action">
{% if g.user_params['role'] <= 3 %}
<span class="service-start" onclick="confirmUdpBalancerAction('start', '{{listener.id}}')"></span>
<span class="service-reload" onclick="confirmUdpBalancerAction('restart', '{{listener.id}}')"></span>
<span class="service-stop" onclick="confirmUdpBalancerAction('stop', '{{listener.id}}')"></span>
<a class="edit" onclick="createUDPListener(true, '{{listener.id}}')"></a>
<a class="delete" onclick="confirmDeleteListener('{{listener.id}}')"></a>
{% endif %}
{# <a href="{{ url_for('main.service_history', service='cluster', server_ip=listener.id) }}" title="{{lang.words.view|title()}} {{lang.words.history3}} {{listener.name}}" class="history" style="margin: 0 5px 0 10px;"></a>#}
</span>
</div>
<div class="server-desc">
{% if listener.cluster_id %}
{% for cluster in clusters %}
{% if listener.cluster_id == cluster.id %}
{{ input('listener-type-'+listener.id|string(), value='cluster', type='hidden') }}
<b>{{lang.words.cluster|title}} {{ lang.words.name }}</b>:
<a href="/app/ha/cluster/{{ cluster.id }}" title="{{lang.words.open|title()}} {{lang.words.cluster|replace("'", "")}}"> {{ cluster.name }}</a>
{% endif %}
{% endfor %}
<b>VIP</b>:
{% else %}
{{ input('listener-type-'+listener.id|string(), value='server', type='hidden') }}
<b>{{ lang.words.server|title() }}</b>: {{ server.hostname }}
<b>IP</b>:
{% endif %}
{{ copy_to_clipboard(value=listener.vip) }}<br />
<b>{{lang.words.port|title()}}</b>: {{ listener.port }} <br />
<b>{{ lang.words.backend|title() }} {{ lang.words.servers }}</b>: <br />
{% 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 }} <br />
{% endfor %}
</div>
</div>

View File

@ -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 %}
<script src="/app/static/js/install.js"></script>
<script src="/app/static/js/udp.js"></script>
<link href="{{ url_for('static', filename='css/servers.css') }}" rel="stylesheet"/>
<link href="{{ url_for('static', filename='css/smon.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/ha.css') }}" rel="stylesheet">
{% if user_subscription.user_status == 0 or user_subscription.user_plan == 'user' %}
{% include 'include/no_sub.html' %}
{% elif not is_needed_tool %}
<div style="text-align: center;">
<h3>{{lang.admin_page.desc.no_ansible}} Ansible</h3>.
<img src="{{ url_for('static', filename='images/no_servers.png')}}" alt="There is no server">
<h4>
{{lang.words.read|title()}} <a href="https://roxy-wi.org/installation#ansible" title="{{lang.words.install|title()}} Ansible" target="_blank">here</a> {{lang.phrases.how_to_install}} Ansible.
</h4>
</div>
{% else %}
{% if g.user_params['role'] <= 3 %}
<div class="add-button add-button-big" title="{{lang.words.create|title()}} UDP {{ lang.words.listener }}" onclick="createUDPListener();">+ {{lang.words.create|title()}} UDP {{ lang.words.listener }}</div>
{% endif %}
<div class="up-pannel" class="sortable">
{% for listener in listeners %}
<div id="listener-{{listener.id}}" class="div-server-hapwi animated-background"></div>
{% endfor %}
</div>
<div id="create-udp-step-1" style="display: none;">
<table class="overview" id="create-udp-step-1-overview"
title="{{lang.words.create|title()}} UDP {{ lang.words.listener }}"
data-edit="{{lang.words.edit|title()}} UDP {{ lang.words.listener }}">
<tr>
<td class="padding20" style="width: 37%">
{{lang.words.select|title()}} {{ lang.words.where }} {{ lang.words.create }} {{ lang.words.an }} UDP {{ lang.words.listener }}
</td>
<td>
{% set values = {'cluster': 'HA Cluster', 'server': 'Standalone' } %}
{{ select('new-udp-type', values=values) }}
</td>
</tr>
</table>
</div>
<div id="create-udp-step-2" style="display: none;">
<form action="" id="create_udp_listener">
<table class="overview" id="create-udp-step-2-overview"
title="{{lang.words.create|title()}} UDP {{ lang.words.listener }}"
data-edit="{{lang.words.edit|title()}} UDP {{ lang.words.listener }}">
{% include 'include/tr_validate_tips.html' %}
<tr>
<td class="padding20" style="width: 37%">
{{lang.words.name|title()}}
<span class="need-field">*</span>
</td>
<td>
{{ input('new-listener-name', autofocus='autofocus') }}
{{ input('new-listener-type', type='hidden', value='server') }}
</td>
</tr>
<tr class="new-udp-ha-cluster-tr">
<td class="padding20">
{{lang.ha_page.ha}} {{lang.words.cluster}}
<span class="need-field">*</span>
</td>
<td>
<select id="ha-cluster">
<option value="------" selected>------</option>
{% for cluster in clusters %}
<option value="{{ cluster.id }}">{{ cluster.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="new-udp-ha-cluster-tr">
<td class="padding20">
VIP
<span class="need-field">*</span>
</td>
<td>
<select id="new-udp-vip"></select>
</td>
</tr>
<tr class="new-udp-servers-tr">
<td class="padding20">
{{lang.words.server|title()}}
<span class="need-field">*</span>
</td>
<td>
{{ select('serv', values=g.user_params['servers'], is_servers='true', by_id='true') }}
</td>
</tr>
<tr class="new-udp-servers-tr">
<td class="padding20">
IP
<span class="need-field">*</span>
</td>
<td>{{ input('new-udp-ip', title=lang.udp_page.listener_ip) }}</td>
</tr>
<tr>
<td class="padding20">
{{lang.words.port|title()}}
<span class="need-field">*</span>
</td>
<td>
{{ input('new-listener-port', type='number', title=lang.udp_page.listener_port) }}
</td>
</tr>
<tr>
<td class="padding20">
{{lang.words.backend|title()}} {{lang.words.servers|title()}}
<span class="need-field">*</span>
</td>
<td id="new-udp-servers-td">
<a class="link add-server" id="frontend_add_acl" title="{{lang.words.add|title()}} ACL" style="cursor: pointer; display: none;"></a>
</td>
</tr>
<tr>
<td class="padding20">
{{lang.words.desc|title()}}
</td>
<td>
{{ input('new-listener-desc', autofocus='autofocus') }}
</td>
</tr>
</table>
<div class="alert alert-warning">
{{lang.udp_page.save_apply|safe}}
</div>
</form>
</div>
<div id="dialog-confirm" style="display: none;">
<p><span class="ui-icon ui-icon-alert" style="float:left; margin:3px 12px 20px 0;"></span>{{lang.phrases.are_you_sure}}</p>
</div>
<script>
{% for listener in listeners %}
getUDPListener('{{listener.id}}');
{% endfor %}
</script>
{% endif %}
{% endblock %}