Changelog: https://roxy-wi.org/changelog#7.1.0
pull/372/head
Aidaho 2023-12-16 10:15:59 +03:00
parent cc0edee75a
commit 820b0cd3db
50 changed files with 2774 additions and 1731 deletions

View File

@ -147,6 +147,7 @@ def default_values():
{'service_id': 2, 'service': 'NGINX', 'slug': 'nginx'},
{'service_id': 3, 'service': 'Keepalived', 'slug': 'keepalived'},
{'service_id': 4, 'service': 'Apache', 'slug': 'apache'},
{'service_id': 5, 'service': 'HA cluster', 'slug': 'cluster'},
]
try:
@ -668,7 +669,7 @@ def update_db_v_6_3_18():
def update_ver():
try:
Version.update(version='7.0.4.0').execute()
Version.update(version='7.1.0.0').execute()
except Exception:
print('Cannot update version')

View File

@ -1,4 +1,5 @@
import os
import shutil
import datetime
import distro
@ -78,3 +79,18 @@ def update_owner_on_log():
os.system(f'sudo chown apache:apache -R {log_path}')
except Exception:
pass
@scheduler.task('interval', id='delete_ansible_artifacts', hours=24, misfire_grace_time=None)
def delete_ansible_artifacts():
full_path = get_config.get_config_var('main', 'fullpath')
ansible_path = f'{full_path}/app/scripts/ansible'
folders = ['artifacts', 'env']
for folder in folders:
if os.path.isdir(f'{ansible_path}/{folder}'):
try:
shutil.rmtree(f'{ansible_path}/{folder}')
except Exception as e:
raise Exception(f'error: Cron cannot delete ansible folders: {e}')

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'):
if service not in ('haproxy', 'nginx', 'apache', 'keepalived', 'cluster'):
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

@ -36,7 +36,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"')])
user_services = CharField(constraints=[SQL('DEFAULT "1 2 3 4 5"')])
last_login_date = DateTimeField(constraints=[SQL('DEFAULT "0000-00-00 00:00:00"')])
last_login_ip = CharField(null=True)
@ -649,12 +649,81 @@ class RoxyTool(BaseModel):
constraints = [SQL('UNIQUE (name)')]
class HaCluster(BaseModel):
id = AutoField()
name = CharField()
syn_flood = IntegerField(constraints=[SQL('DEFAULT "0"')])
group_id = IntegerField()
desc = CharField()
pos = IntegerField()
class Meta:
table_name = 'ha_clusters'
class HaClusterRouter(BaseModel):
id = AutoField()
cluster_id = ForeignKeyField(HaCluster, on_delete='Cascade')
default = IntegerField(constraints=[SQL('DEFAULT "0"')])
class Meta:
table_name = 'ha_cluster_routers'
class HaClusterSlave(BaseModel):
id = AutoField()
cluster_id = ForeignKeyField(HaCluster, on_delete='Cascade')
server_id = ForeignKeyField(Server, on_delete='Cascade')
master = IntegerField(constraints=[SQL('DEFAULT "0"')])
eth = CharField(constraints=[SQL('DEFAULT "eth0"')])
router_id = ForeignKeyField(HaClusterRouter, on_delete='Cascade')
class Meta:
table_name = 'ha_cluster_slaves'
constraints = [SQL('UNIQUE (cluster_id, server_id, router_id)')]
class HaClusterVip(BaseModel):
id = AutoField()
cluster_id = ForeignKeyField(HaCluster, on_delete='Cascade')
router_id = ForeignKeyField(HaClusterRouter, on_delete='Cascade')
return_master = IntegerField(constraints=[SQL('DEFAULT "0"')])
vip = CharField()
class Meta:
table_name = 'ha_cluster_vips'
constraints = [SQL('UNIQUE (cluster_id, vip)')]
class HaClusterVirt(BaseModel):
cluster_id = ForeignKeyField(HaCluster, on_delete='Cascade')
virt_id = ForeignKeyField(Server, on_delete='Cascade')
vip_id = ForeignKeyField(HaClusterVip, on_delete='Cascade')
class Meta:
table_name = 'ha_cluster_virts'
primary_key = False
constraints = [SQL('UNIQUE (cluster_id, virt_id)')]
class HaClusterService(BaseModel):
cluster_id = ForeignKeyField(HaCluster, on_delete='Cascade')
service_id = CharField()
class Meta:
table_name = 'ha_cluster_services'
primary_key = False
constraints = [SQL('UNIQUE (cluster_id, service_id)')]
def create_tables():
with conn:
conn.create_tables([User, Server, Role, Telegram, Slack, UUID, Token, 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,
SmonTcpCheck, SmonHttpCheck, SmonPingCheck, SmonDnsCheck, S3Backup, RoxyTool, SmonStatusPage,
SmonStatusPageCheck])
conn.create_tables(
[User, Server, Role, Telegram, Slack, UUID, Token, 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, SmonTcpCheck, SmonHttpCheck, SmonPingCheck, SmonDnsCheck, S3Backup, RoxyTool,
SmonStatusPage, SmonStatusPageCheck, HaCluster, HaClusterSlave, HaClusterVip, HaClusterVirt, HaClusterService,
HaClusterRouter]
)

View File

@ -291,11 +291,11 @@ def update_group(name, descript, group_id):
def add_server(hostname, ip, group, typeip, enable, master, cred, port, desc, haproxy, nginx, apache, firewall):
try:
Server.insert(
server_id = Server.insert(
hostname=hostname, ip=ip, groups=group, type_ip=typeip, enable=enable, master=master, cred=cred,
port=port, desc=desc, haproxy=haproxy, nginx=nginx, apache=apache, firewall_enable=firewall
).execute()
return True
return server_id
except Exception as e:
out_error(e)
return False
@ -4265,3 +4265,257 @@ def delete_status_page(page_id):
SmonStatusPage.delete().where(SmonStatusPage.id == page_id).execute()
except Exception as e:
out_error(e)
def select_clusters(group_id: int):
try:
return HaCluster.select().where(HaCluster.group_id == group_id).execute()
except Exception as e:
out_error(e)
def create_cluster(name: str, syn_flood: int, group_id: int, desc: str) -> int:
try:
last_id = HaCluster.insert(
name=name, syn_flood=syn_flood, group_id=group_id, desc=desc
).execute()
return last_id
except Exception as e:
out_error(e)
def select_cluster(cluster_id: int):
try:
return HaCluster.select().where(HaCluster.id == cluster_id).execute()
except Exception as e:
out_error(e)
def select_cluster_name(cluster_id: int) -> str:
try:
return HaCluster.get(HaCluster.id == cluster_id).name
except Exception as e:
out_error(e)
def select_clusters_slaves():
try:
return HaClusterSlave.select().execute()
except Exception as e:
out_error(e)
def select_clusters_virts():
try:
return HaClusterVirt.select().execute()
except Exception as e:
out_error(e)
def select_clusters_vips():
try:
return HaClusterVip.select().execute()
except Exception as e:
out_error(e)
def select_cluster_vips(cluster_id: int) -> object:
try:
return HaClusterVip.select().where(HaClusterVip.cluster_id == cluster_id).execute()
except Exception as e:
out_error(e)
def select_clusters_vip(cluster_id: int, router_id: int):
try:
return HaClusterVip.get((HaClusterVip.cluster_id == cluster_id) & (HaClusterVip.router_id == router_id)).vip
except Exception as e:
out_error(e)
def select_clusters_vip_return_master(cluster_id: int, router_id: int):
try:
return HaClusterVip.get((HaClusterVip.cluster_id == cluster_id) & (HaClusterVip.router_id == router_id)).return_master
except Exception as e:
out_error(e)
def select_clusters_vip_id(cluster_id: int, router_id):
try:
return HaClusterVip.get((HaClusterVip.cluster_id == cluster_id) & (HaClusterVip.router_id == router_id)).id
except Exception as e:
out_error(e)
def select_cluster_services(cluster_id: int):
try:
return HaClusterService.select().where(HaClusterService.cluster_id == cluster_id).execute()
except Exception as e:
out_error(e)
def delete_cluster_services(cluster_id: int):
try:
return HaClusterService.delete().where(HaClusterService.cluster_id == cluster_id).execute()
except Exception as e:
out_error(e)
def insert_cluster_services(cluster_id: int, service_id: int):
try:
return HaClusterService.insert(cluster_id=cluster_id, service_id=service_id).execute()
except Exception as e:
out_error(e)
def select_cluster_master_slaves(cluster_id: int, group_id: int):
cursor = conn.cursor()
sql = f"select * from servers left join ha_clusters on (servers.id = ha_clusters.master_id) " \
f"left join ha_cluster_slaves on (servers.id = ha_cluster_slaves.server_id) " \
f"left join ha_cluster_virts on (servers.id = ha_cluster_virts.virt_id)" \
f"where (servers.groups = {group_id} and " \
f"(ha_cluster_slaves.cluster_id = {cluster_id} or ha_clusters.id = {cluster_id} or ha_cluster_virts.cluster_id = {cluster_id}));"
try:
cursor.execute(sql)
except Exception as e:
out_error(e)
else:
return cursor.fetchall()
def select_cluster_slaves(cluster_id: int, router_id: int):
cursor = conn.cursor()
sql = f"select * from servers " \
f"left join ha_cluster_slaves on (servers.id = ha_cluster_slaves.server_id) " \
f"where ha_cluster_slaves.cluster_id = {cluster_id} and ha_cluster_slaves.router_id = {router_id};"
try:
cursor.execute(sql)
except Exception as e:
out_error(e)
else:
return cursor.fetchall()
def select_cluster_slaves_for_inv(router_id: int):
try:
return HaClusterSlave.select().where(HaClusterSlave.router_id == router_id).execute()
except Exception as e:
out_error(e)
def delete_ha_cluster_delete_slave(server_id: int) -> None:
try:
HaClusterSlave.delete().where(HaClusterSlave.server_id == server_id).execute()
except Exception as e:
out_error(e)
def delete_ha_cluster_delete_slaves(cluster_id: int) -> None:
try:
HaClusterSlave.delete().where(HaClusterSlave.cluster_id == cluster_id).execute()
except Exception as e:
out_error(e)
def delete_master_from_slave(server_id: int) -> None:
try:
Server.update(master=0).where(Server.server_id == server_id).execute()
except Exception as e:
out_error(e)
def ha_cluster_add_slave(server_id: int, master_id: int) -> None:
try:
HaClusterSlave.insert(
server_id=server_id,
cluster_id=HaCluster.get(HaCluster.master_id == master_id).id
).execute()
except Exception as e:
out_error(e)
def select_ha_cluster_not_masters_not_slaves(group_id: int):
try:
query = Server.select().where(
(Server.type_ip == 0) &
(Server.server_id.not_in(HaClusterSlave.select(HaClusterSlave.server_id))) &
(Server.groups == group_id)
)
return query.execute()
except Exception as e:
out_error(e)
def get_router_id(cluster_id: int, default_router=0) -> int:
try:
return HaClusterRouter.get((HaClusterRouter.cluster_id == cluster_id) & (HaClusterRouter.default == default_router)).id
except Exception as e:
out_error(e)
def create_ha_router(cluster_id: int) -> int:
try:
last_id = HaClusterRouter.insert(cluster_id=cluster_id).execute()
return last_id
except Exception as e:
out_error(e)
def delete_ha_router(router_id: int) -> int:
try:
last_id = HaClusterRouter.delete().where(HaClusterRouter.id == router_id).execute()
return last_id
except Exception as e:
out_error(e)
def insert_or_update_slave(cluster_id: int, server_id: int, eth: str, master: int, router_id) -> None:
try:
HaClusterSlave.insert(cluster_id=cluster_id, server_id=server_id, eth=eth, master=master, router_id=router_id).on_conflict('replace').execute()
except Exception as e:
out_error(e)
def update_slave(cluster_id: int, server_id: int, eth: str, master: int, router_id) -> None:
try:
HaClusterSlave.update(
cluster_id=cluster_id, server_id=server_id, eth=eth, master=master, router_id=router_id
).where((HaClusterSlave.server_id == server_id) & (HaClusterSlave.router_id == router_id)).execute()
except Exception as e:
out_error(e)
def update_cluster(cluster_id: int, name: str, desc: str, syn_flood: int) -> None:
try:
HaCluster.update(name=name, desc=desc, syn_flood=syn_flood).where(HaCluster.id == cluster_id).execute()
except Exception as e:
out_error(e)
def update_ha_cluster_vip(cluster_id: int, router_id: int, vip: str, return_master: int) -> None:
try:
HaClusterVip.update(vip=vip, return_master=return_master).where((HaClusterVip.cluster_id == cluster_id) & (HaClusterVip.router_id == router_id)).execute()
except Exception as e:
out_error(e)
def update_ha_virt_ip(vip_id: int, vip: str) -> None:
try:
Server.update(ip=vip).where(Server.server_id == HaClusterVirt.get(HaClusterVirt.vip_id == vip_id).virt_id).execute()
except Exception as e:
out_error(e)
def delete_ha_virt(vip_id: int) -> None:
try:
Server.delete().where(Server.server_id == HaClusterVirt.get(HaClusterVirt.vip_id == vip_id).virt_id).execute()
except Exception as e:
pass
def check_ha_virt(vip_id: int) -> bool:
try:
HaClusterVirt.get(HaClusterVirt.vip_id == vip_id).virt_id
except Exception:
return False
return True

View File

@ -131,11 +131,6 @@ def logging(server_ip: str, action: str, **kwargs) -> None:
if kwargs.get('roxywi') == 1:
if kwargs.get('login'):
mess = f"{cur_date_in_log} from {ip} user: {login}, group: {user_group}, {action} on: {server_ip}\n"
if kwargs.get('keep_history'):
try:
keep_action_history(kwargs.get('service'), action, server_ip, login, ip)
except Exception as e:
print(str(e))
else:
mess = f"{cur_date_in_log} {action} from {ip}\n"
log_file = f"{log_path}/roxy-wi-{cur_date}.log"
@ -146,11 +141,11 @@ def logging(server_ip: str, action: str, **kwargs) -> None:
mess = f"{cur_date_in_log} from {ip} user: {login}, group: {user_group}, {action} on: {server_ip}\n"
log_file = f"{log_path}/config_edit-{cur_date}.log"
if kwargs.get('keep_history'):
try:
keep_action_history(kwargs.get('service'), action, server_ip, login, ip)
except Exception:
pass
if kwargs.get('keep_history'):
try:
keep_action_history(kwargs.get('service'), action, server_ip, login, ip)
except Exception as e:
print(f'error: Cannot save history: {e}')
try:
with open(log_file, 'a') as log:
@ -160,19 +155,24 @@ def logging(server_ip: str, action: str, **kwargs) -> None:
def keep_action_history(service: str, action: str, server_ip: str, login: str, user_ip: str):
try:
server_id = sql.select_server_id_by_ip(server_ip=server_ip)
hostname = sql.get_hostname_by_server_ip(server_ip)
if login != '':
user_id = sql.get_user_id_by_username(login)
else:
user_id = 0
if user_ip == '':
user_ip = 'localhost'
if login != '':
user_id = sql.get_user_id_by_username(login)
else:
user_id = 0
if user_ip == '':
user_ip = 'localhost'
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)
if service == 'HA cluster':
cluster_name = sql.select_cluster_name(server_ip)
sql.insert_action_history(service, action, server_ip, user_id, user_ip, server_ip, cluster_name)
else:
try:
server_id = sql.select_server_id_by_ip(server_ip=server_ip)
hostname = sql.get_hostname_by_server_ip(server_ip)
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):
@ -253,7 +253,8 @@ def get_users_params(**kwargs):
'servers': servers,
'user_services': user_services,
'lang': user_lang,
'user_id': user_id
'user_id': user_id,
'group_id': group_id
}
return user_params

View File

@ -0,0 +1,278 @@
import modules.db.sql as sql
from modules.db.db_model import HaCluster, HaClusterRouter, HaClusterVip, HaClusterVirt
import modules.common.common as common
import modules.server.server as server_mod
import modules.roxywi.common as roxywi_common
from modules.server.ssh import return_ssh_keys_path
def create_cluster(cluster: object, group_id: int) -> str:
master_ip = None
vip = common.is_ip_or_dns(cluster['vip'])
syn_flood = int(cluster['syn_flood'])
return_master = int(cluster['return_to_master'])
try:
cluster_id = sql.create_cluster(cluster['name'], syn_flood, group_id, cluster['desc'])
roxywi_common.logging(cluster_id, f'New cluster has been created', keep_history=1, roxywi=1, service='HA cluster')
except Exception as e:
return f'error: Cannot create new HA cluster: {e}'
try:
router_id = HaClusterRouter.insert(cluster_id=cluster_id, default=1).on_conflict_ignore().execute()
except Exception as e:
return f'error: Cannon create router: {e}'
try:
vip_id = HaClusterVip.insert(cluster_id=cluster_id, router_id=router_id, vip=vip, return_master=return_master).execute()
roxywi_common.logging(cluster_id, f'New vip {vip} has been created and added to the cluster', keep_history=1, roxywi=1, service='HA cluster')
except Exception as e:
return f'error: Cannon add VIP: {e}'
for slave_id, value in cluster['servers'].items():
if value['master']:
master_ip = value['ip']
for slave_id, value in cluster['servers'].items():
if value['master']:
continue
try:
sql.update_server_master(master_ip, value['ip'])
except Exception as e:
raise Exception(f'error: Cannot update master on slave {value["ip"]: {e}}')
for slave_id, value in cluster['servers'].items():
if value['master']:
slave_id = sql.select_server_id_by_ip(master_ip)
try:
sql.insert_or_update_slave(cluster_id, slave_id, value['eth'], value['master'], router_id)
roxywi_common.logging(cluster_id, f'New server {value["ip"]} has been added to the cluster', keep_history=1, roxywi=1, service='HA cluster')
except Exception as e:
raise Exception(f'error: Cannot update slave server {value["ip"]}: {e}')
for service, value in cluster['services'].items():
if not value['enabled']:
continue
try:
service_id = sql.select_service_id_by_slug(service)
sql.insert_cluster_services(cluster_id, service_id)
roxywi_common.logging(cluster_id, f'Service {service} has been enabled on the cluster', keep_history=1, roxywi=1, service='HA cluster')
except Exception as e:
raise Exception(f'error: Cannot add service {service}: {e}')
if cluster['virt_server']:
add_or_update_virt(cluster, cluster_id, vip_id, group_id)
return str(cluster_id)
def update_cluster(cluster: object, group_id: int) -> str:
cluster_id = int(cluster['cluster_id'])
syn_flood = int(cluster['syn_flood'])
cluster_name = cluster['name']
try:
router_id = sql.get_router_id(cluster_id, default_router=1)
except Exception as e:
raise Exception(f'error: Cannot get router: {e}')
try:
sql.update_cluster(cluster_id, cluster['name'], cluster['desc'], syn_flood)
except Exception as e:
raise Exception(f'error: Cannot update HA cluster: {e}')
try:
update_slaves(cluster, router_id)
except Exception as e:
raise Exception(e)
try:
update_vip(cluster_id, router_id, cluster, group_id)
except Exception as e:
raise Exception(e)
try:
sql.delete_cluster_services(cluster_id)
except Exception as e:
raise Exception(f'error: Cannot delete old services: {e}')
for service, value in cluster['services'].items():
if not value['enabled']:
continue
try:
service_id = sql.select_service_id_by_slug(service)
sql.insert_cluster_services(cluster_id, service_id)
except Exception as e:
raise Exception(f'error: Cannot add service {service}: {e}')
roxywi_common.logging(cluster_id, f'Cluster {cluster_name} has been updated', keep_history=1, roxywi=1, service='HA cluster')
return 'ok'
def delete_cluster(cluster_id: int) -> str:
HaCluster.delete().where(HaCluster.id == cluster_id).execute()
slaves = sql.select_cluster_slaves(cluster_id)
for slave in slaves:
slave_ip = select_server_ip_by_id(slave.server_id)
try:
sql.update_server_master(0, slave_ip)
except Exception as e:
raise Exception(f'error: Cannot update master on slave {slave_ip}: {e}')
roxywi_common.logging(cluster_id, f'Cluster {cluster_name} has been deleted', keep_history=1, roxywi=1, service='HA cluster')
return 'ok'
def update_vip(cluster_id: int, router_id: int, json_data: object, group_id: int) -> None:
return_master = int(json_data['return_to_master'])
vip = common.is_ip_or_dns(json_data['vip'])
vip_id = sql.select_clusters_vip_id(cluster_id, router_id)
try:
sql.update_ha_cluster_vip(cluster_id, router_id, vip, return_master)
except Exception as e:
raise Exception(f'error: Cannot update VIP: {e}')
for slave_id, value in json_data['servers'].items():
try:
sql.update_slave(cluster_id, slave_id, value['eth'], value['master'], router_id)
except Exception as e:
raise Exception(f'error: Cannot add server {value["ip"]}: {e}')
if json_data['virt_server']:
add_or_update_virt(json_data, cluster_id, vip_id, group_id)
else:
try:
if sql.check_ha_virt(vip_id):
sql.delete_ha_virt(vip_id)
roxywi_common.logging(cluster_id, f'Cluster virtual server for VIP: {vip} has been deleted', keep_history=1, roxywi=1, service='HA cluster')
except Exception as e:
roxywi_common.logging(cluster_id, f'Cannot delete cluster virtual server for VIP {vip}: {e}', keep_history=1, roxywi=1, service='HA cluster')
roxywi_common.logging(cluster_id, f'Cluster VIP {vip} has been updated', keep_history=1, roxywi=1, service='HA cluster')
def insert_vip(cluster_id: int, json_data: object, group_id: int) -> None:
vip = common.is_ip_or_dns(json_data['vip'])
return_master = int(json_data['return_to_master'])
try:
router_id = sql.create_ha_router(cluster_id)
except Exception as e:
raise Exception(f'error: Cannot create new router: {e}')
try:
vip_id = HaClusterVip.insert(cluster_id=cluster_id, router_id=router_id, vip=vip, return_master=return_master).execute()
except Exception as e:
raise Exception(f'error: Cannot save VIP {vip}: {e}')
for slave_id, value in json_data['servers'].items():
try:
sql.insert_or_update_slave(cluster_id, slave_id, value['eth'], value['master'], router_id)
except Exception as e:
raise Exception(f'error: Cannot add server {value["ip"]}: {e}')
if json_data['virt_server']:
add_or_update_virt(json_data, cluster_id, vip_id, group_id)
roxywi_common.logging(cluster_id, f'New cluster VIP: {vip} has been created', keep_history=1, roxywi=1, service='HA cluster')
def update_slaves(json_data: object, router_id: int) -> None:
master_ip = None
cluster = json_data
cluster_id = int(json_data['cluster_id'])
all_routers_in_cluster = HaClusterRouter.select(HaClusterRouter.id).where(HaClusterRouter.cluster_id == cluster_id).execute()
server_ids_from_db = sql.select_cluster_slaves(cluster_id, router_id)
server_ids = []
server_ids_from_json = []
for slave_id, value in cluster['servers'].items():
if value['master']:
master_ip = value['ip']
for server in server_ids_from_db:
server_ids.append(server[0])
for slave_id, value in cluster['servers'].items():
if value['master']:
slave_id = sql.select_server_id_by_ip(master_ip)
server_ids_from_json.append(int(slave_id))
server_ids_for_deletion = set(server_ids) - set(server_ids_from_json)
server_ids_for_adding = set(server_ids_from_json) - set(server_ids)
for router in all_routers_in_cluster:
for slave_id, value in cluster['servers'].items():
for server_id_add in server_ids_for_adding:
if int(slave_id) == int(server_id_add):
try:
sql.insert_or_update_slave(cluster_id, slave_id, value['eth'], value['master'], router)
except Exception as e:
raise Exception(f'error: Cannot add new slave {value["name"]}: {e}')
for o_s in server_ids_for_deletion:
sql.delete_master_from_slave(o_s)
try:
sql.delete_ha_cluster_delete_slave(o_s)
except Exception as e:
raise Exception(f'error: Cannot recreate slaves server: {e}')
for slave_id, value in cluster['servers'].items():
if value['master']:
continue
try:
sql.update_server_master(master_ip, value['ip'])
except Exception as e:
raise Exception(f'error: Cannot update master on slave {value["ip"]}: {e}')
for slave_id, value in cluster['servers'].items():
if value['master']:
slave_id = sql.select_server_id_by_ip(master_ip)
try:
sql.insert_or_update_slave(cluster_id, slave_id, value['eth'], value['master'], router_id)
except Exception as e:
raise Exception(f'error: Cannot update server {value["ip"]}: {e}')
def add_or_update_virt(cluster: object, cluster_id: int, vip_id: int, group_id: int) -> None:
haproxy = 0
nginx = 0
apache = 0
master_ip = None
vip = common.is_ip_or_dns(cluster['vip'])
cluster_name = common.checkAjaxInput(cluster['name'])
for slave_id, value in cluster['servers'].items():
if value['master']:
master_ip = common.is_ip_or_dns(value['ip'])
if sql.check_ha_virt(vip_id):
try:
sql.update_ha_virt_ip(vip_id, vip)
roxywi_common.logging(cluster_id, f'Cluster virtual server for VIP {vip} has been updated', keep_history=1, roxywi=1, service='HA cluster')
except Exception as e:
roxywi_common.logging(cluster_id, f'Cannot update cluster virtual server for VIP {vip}: {e}', roxywi=1, service='HA cluster')
else:
services = sql.select_cluster_services(cluster_id)
for service in services:
haproxy = 1 if service.service_id == '1' else 0
nginx = 1 if service.service_id == '2' else 0
apache = 1 if service.service_id == '4' else 0
try:
cred_id = sql.get_cred_id_by_server_ip(master_ip)
firewall = 1 if server_mod.is_service_active(master_ip, 'firewalld') else 0
ssh_settings = return_ssh_keys_path(master_ip)
virt_id = sql.add_server(
f'{vip}-VIP', vip, group_id, '1', '1', '0', cred_id, ssh_settings['port'],
f'VRRP IP for {cluster_name} cluster', haproxy, nginx, apache, firewall
)
HaClusterVirt.insert(cluster_id=cluster_id, virt_id=virt_id, vip_id=vip_id).execute()
roxywi_common.logging(cluster_id, f'New cluster virtual server for VIP: {vip} has been created', keep_history=1, roxywi=1,
service='HA cluster')
except Exception as e:
roxywi_common.logging(cluster_id, f'error: Cannot create new cluster virtual server for VIP: {vip}: {e}', roxywi=1, service='HA cluster')

View File

@ -2,6 +2,7 @@ import os
import json
from flask import render_template
import ansible_runner
import modules.db.sql as sql
import modules.service.common as service_common
@ -12,12 +13,12 @@ from modules.server.ssh import return_ssh_keys_path
def show_installation_output(error: str, output: str, service: str, rc=0):
if error and "WARNING" not in error:
if error.read() and "WARNING" not in error.read():
roxywi_common.logging('Roxy-WI server', error, roxywi=1)
raise Exception('error: ' + error)
else:
if rc != 0:
for line in output:
for line in output.read():
if any(s in line for s in ("Traceback", "FAILED", "error", "ERROR", "UNREACHABLE")):
try:
correct_out = line.split('=>')
@ -34,75 +35,6 @@ def show_success_installation(service):
return render_template('include/show_success_installation.html', service=service, lang=lang)
def install_haproxy(server_ip: str, api=0, **kwargs):
script = "install_haproxy.sh"
hap_sock_p = str(sql.get_setting('haproxy_sock_port'))
stats_port = str(sql.get_setting('stats_port'))
server_state_file = sql.get_setting('server_state_file')
stats_user = sql.get_setting('stats_user')
stats_password = sql.get_setting('stats_password')
proxy = sql.get_setting('proxy')
haproxy_dir = sql.get_setting('haproxy_dir')
container_name = sql.get_setting('haproxy_container_name')
haproxy_ver = kwargs.get('hapver')
server_for_installing = kwargs.get('server')
docker = kwargs.get('docker')
m_or_s = kwargs.get('m_or_s')
master = kwargs.get('master')
slave = kwargs.get('slave')
proxy_serv = ''
ssh_settings = return_ssh_keys_path(server_ip)
full_path = '/var/www/haproxy-wi/app'
os.system(f"cp {full_path}/scripts/{script} {full_path}/{script}")
if haproxy_ver is None:
haproxy_ver = '2.8.1-1'
if proxy is not None and proxy != '' and proxy != 'None':
proxy_serv = proxy
syn_flood_protect = '1' if kwargs.get('syn_flood') == "1" else ''
commands = [
f"chmod +x {full_path}/{script} && {full_path}/{script} PROXY={proxy_serv} SOCK_PORT={hap_sock_p} STAT_PORT={stats_port} "
f"STAT_FILE={server_state_file} DOCKER={docker} SSH_PORT={ssh_settings['port']} STATS_USER={stats_user} "
f"CONT_NAME={container_name} HAP_DIR={haproxy_dir} STATS_PASS='{stats_password}' HAPVER={haproxy_ver} "
f"SYN_FLOOD={syn_flood_protect} HOST={server_ip} USER={ssh_settings['user']} PASS='{ssh_settings['password']}' "
f"M_OR_S={m_or_s} MASTER={master} SLAVE={slave} KEY={ssh_settings['key']}"
]
if server_for_installing:
service = server_for_installing + ' HAProxy'
else:
service = ' HAProxy'
return_out = server_mod.subprocess_execute_with_rc(commands[0])
try:
show_installation_output(return_out['error'], return_out['output'], service, rc=return_out['rc'])
except Exception as e:
raise Exception(e)
try:
sql.update_haproxy(server_ip)
except Exception as e:
return str(e)
if docker == '1':
server_id = sql.select_server_id_by_ip(server_ip)
sql.insert_or_update_service_setting(server_id, 'haproxy', 'dockerized', '1')
sql.insert_or_update_service_setting(server_id, 'haproxy', 'restart', '1')
try:
os.remove(f'{full_path}/{script}')
except Exception:
pass
if not api:
return show_success_installation(service)
def waf_install(server_ip: str):
script = "waf.sh"
proxy = sql.get_setting('proxy')
@ -179,77 +111,6 @@ def waf_nginx_install(server_ip: str):
return show_success_installation(service)
def install_service(server_ip: str, service: str, docker: str, syn_flood_protect: int, api=0, **kwargs) -> str:
script = f"install_{service}.sh"
stats_user = sql.get_setting(f'{service}_stats_user')
stats_password = sql.get_setting(f'{service}_stats_password')
stats_port = str(sql.get_setting(f'{service}_stats_port'))
stats_page = sql.get_setting(f'{service}_stats_page')
config_path = sql.get_setting(f'{service}_config_path')
service_dir = sql.get_setting(f'{service}_dir')
server_for_installing = kwargs.get('server')
proxy = sql.get_setting('proxy')
container_name = sql.get_setting(f'{service}_container_name')
proxy_serv = ''
ssh_settings = return_ssh_keys_path(server_ip)
full_path = '/var/www/haproxy-wi/app'
try:
os.system(f"cp {full_path}/scripts/{script} {full_path}/{script}")
except Exception as e:
raise Exception(f'error: {e}')
if proxy is not None and proxy != '' and proxy != 'None':
proxy_serv = proxy
if service == 'apache':
correct_service_name = service_common.get_correct_apache_service_name(server_ip=server_ip, server_id=None)
if service_dir == '/etc/httpd' and correct_service_name == 'apache2':
service_dir = '/etc/apache2'
elif service_dir == '/etc/apache2' and correct_service_name == 'httpd':
service_dir = '/etc/httpd'
commands = [
f"chmod +x {full_path}/{script} && {full_path}/{script} PROXY={proxy_serv} STATS_USER={stats_user} STATS_PASS='{stats_password}' "
f"SSH_PORT={ssh_settings['port']} CONFIG_PATH={config_path} CONT_NAME={container_name} STAT_PORT={stats_port} "
f"STAT_PAGE={stats_page} SYN_FLOOD={syn_flood_protect} DOCKER={docker} service_dir={service_dir} HOST={server_ip} "
f"USER={ssh_settings['user']} PASS='{ssh_settings['password']}' KEY={ssh_settings['key']}"
]
if server_for_installing:
service_name = f'{server_for_installing} {service.title()}'
else:
service_name = service.title()
return_out = server_mod.subprocess_execute_with_rc(commands[0])
try:
show_installation_output(return_out['error'], return_out['output'], service_name, rc=return_out['rc'])
except Exception as e:
raise Exception(e)
if service == 'nginx':
try:
sql.update_nginx(server_ip)
except Exception as e:
return str(e)
elif service == 'apache':
try:
sql.update_apache(server_ip)
except Exception as e:
return str(e)
if docker == '1':
server_id = sql.select_server_id_by_ip(server_ip)
sql.insert_or_update_service_setting(server_id, service, 'dockerized', '1')
sql.insert_or_update_service_setting(server_id, service, 'restart', '1')
os.remove(f'{full_path}/{script}')
if not api:
return show_success_installation(service)
def geoip_installation(serv, geoip_update, service):
proxy = sql.get_setting('proxy')
maxmind_key = sql.get_setting('maxmind_key')
@ -322,165 +183,199 @@ def grafana_install():
return f'success: Grafana and Prometheus servers were installed. You can find Grafana on http://{host}:3000<br>'
def keepalived_master_install(
master: str, eth: str, eth_slave: str, vrrp_ip: str, virt_server: int, syn_flood: int, return_to_master: int,
haproxy: int, nginx: int, router_id: int, api=0
) -> str:
script = "install_keepalived.sh"
proxy = sql.get_setting('proxy')
def generate_kp_inv(json_data: json, install_service) -> object:
json_data = json.loads(json_data)
inv = {"server": {"hosts":{}}}
server_ips = []
cluster_id = int(json_data['cluster_id'])
haproxy = json_data['services']['haproxy']['enabled']
nginx = json_data['services']['nginx']['enabled']
# apache = json_data['apache']
apache = 0
keepalived_path_logs = sql.get_setting('keepalived_path_logs')
proxy_serv = ''
ssh_settings = return_ssh_keys_path(master)
full_path = '/var/www/haproxy-wi/app'
service = 'master Keepalived'
syn_flood_protect = str(json_data['syn_flood'])
routers = {}
vips = sql.select_cluster_vips(cluster_id)
if proxy is not None and proxy != '' and proxy != 'None':
proxy_serv = proxy
for vip in vips:
router_id = str(vip.router_id)
routers[router_id] = {vip.vip: {}}
routers[router_id][vip.vip].setdefault('return_master', vip.return_master)
routers[router_id][vip.vip].setdefault('vip', vip.vip)
slaves = sql.select_cluster_slaves_for_inv(router_id)
for slave in slaves:
routers[router_id][vip.vip].setdefault('master', slave.master)
routers[router_id][vip.vip].setdefault('eth', slave.eth)
try:
os.system(f"cp {full_path}/scripts/{script} {full_path}/{script}")
except Exception as e:
raise Exception(f'error: {e}')
for k, v in json_data['servers'].items():
server_ip = v['ip']
inv['server']['hosts'][server_ip] = {
"HAPROXY": haproxy,
"NGINX": nginx,
"APACHE": apache,
"RESTART": 1,
"SYN_FLOOD": syn_flood_protect,
"keepalived_path_logs": keepalived_path_logs,
"routers": routers
}
server_ips.append(server_ip)
commands = [
f"chmod +x {full_path}/{script} && {full_path}/{script} PROXY={proxy_serv} SSH_PORT={ssh_settings['port']} router_id={router_id} "
f"ETH={eth} IP={vrrp_ip} MASTER=MASTER ETH_SLAVE={eth_slave} keepalived_path_logs={keepalived_path_logs} "
f"RETURN_TO_MASTER={return_to_master} SYN_FLOOD={syn_flood} HOST={master} HAPROXY={haproxy} NGINX={nginx} "
f"USER={ssh_settings['user']} PASS='{ssh_settings['password']}' KEY={ssh_settings['key']}"
]
return_out = server_mod.subprocess_execute_with_rc(commands[0])
try:
show_installation_output(return_out['error'], return_out['output'], service, rc=return_out['rc'])
except Exception as e:
raise Exception(f'error: read output: {e}')
try:
sql.update_keepalived(master)
except Exception as e:
raise Exception(e)
if virt_server:
group_id = sql.get_group_id_by_server_ip(master)
cred_id = sql.get_cred_id_by_server_ip(master)
hostname = sql.get_hostname_by_server_ip(master)
firewall = 1 if server_mod.is_service_active(master, 'firewalld') else 0
sql.add_server(
hostname + '-VIP', vrrp_ip, group_id, '1', '1', '0', cred_id, ssh_settings['port'], f'VRRP IP for {master}',
haproxy, nginx, '0', firewall
)
if not api:
return show_success_installation(service)
return inv, server_ips
def keepalived_slave_install(
master: str, slave: str, eth: str, eth_slave: str, vrrp_ip: str, syn_flood: int, haproxy: int, nginx: int, router_id: int, api=0
) -> str:
script = "install_keepalived.sh"
def generate_haproxy_inv(json_data: json, install_service: str) -> object:
json_data = json.loads(json_data)
inv = {"server": {"hosts": {}}}
slaves = []
server_ips = []
master_ip = 0
hap_sock_p = str(sql.get_setting('haproxy_sock_port'))
stats_port = str(sql.get_setting('stats_port'))
server_state_file = sql.get_setting('server_state_file')
stats_user = sql.get_setting('stats_user')
stats_password = sql.get_setting('stats_password')
haproxy_dir = sql.get_setting('haproxy_dir')
container_name = sql.get_setting('haproxy_container_name')
haproxy_ver = ''
is_docker = json_data['services']['haproxy']['docker']
if haproxy_ver == '':
haproxy_ver = '2.8.1-1'
for k, v in json_data['servers'].items():
if not v['master']:
slaves.append(v['ip'])
else:
master_ip = v['ip']
for k, v in json_data['servers'].items():
server_ip = v['ip']
is_master = v['master']
if 'version' in v:
haproxy_ver = v['version']
inv['server']['hosts'][server_ip] = {
"SOCK_PORT": hap_sock_p,
"STAT_PORT": stats_port,
"STAT_FILE": server_state_file,
"STATS_USER": stats_user,
"CONT_NAME": container_name,
"HAP_DIR": haproxy_dir,
"STATS_PASS": stats_password,
"HAPVER": haproxy_ver,
"SYN_FLOOD": '0',
"M_OR_S": is_master,
"MASTER": master_ip,
"slaves": slaves,
"DOCKER": is_docker
}
server_ips.append(server_ip)
return inv, server_ips
def generate_service_inv(json_data: json, install_service: str) -> object:
json_data = json.loads(json_data)
inv = {"server": {"hosts": {}}}
server_ips = []
stats_user = sql.get_setting(f'{install_service}_stats_user')
stats_password = sql.get_setting(f'{install_service}_stats_password')
stats_port = str(sql.get_setting(f'{install_service}_stats_port'))
stats_page = sql.get_setting(f'{install_service}_stats_page')
config_path = sql.get_setting(f'{install_service}_config_path')
service_dir = sql.get_setting(f'{install_service}_dir')
container_name = sql.get_setting(f'{install_service}_container_name')
is_docker = json_data['services'][install_service]['docker']
if install_service == 'nginx':
os.system('ansible-galaxy collection install community.general')
os.system('ansible-galaxy install nginxinc.nginx,0.23.2 --roles-path /var/www/haproxy-wi/app/scripts/ansible/roles/')
for k, v in json_data['servers'].items():
server_ip = v['ip']
if install_service == 'apache':
correct_service_name = service_common.get_correct_apache_service_name(server_ip=server_ip, server_id=None)
if service_dir == '/etc/httpd' and correct_service_name == 'apache2':
service_dir = '/etc/apache2'
elif service_dir == '/etc/apache2' and correct_service_name == 'httpd':
service_dir = '/etc/httpd'
inv['server']['hosts'][server_ip] = {
"STAT_PORT": stats_port,
"DOCKER": is_docker,
"STATS_USER": stats_user,
"CONT_NAME": container_name,
"STATS_PASS": stats_password,
"service_dir": service_dir,
"SYN_FLOOD": "0",
"CONFIG_PATH": config_path,
"STAT_PAGE": stats_page,
"service": install_service,
}
server_ips.append(server_ip)
return inv, server_ips
def run_ansible(inv: object, server_ips: str, ansible_role: str, service: str) -> object:
inventory = f'/var/www/haproxy-wi/app/scripts/ansible/inventory/{ansible_role}.json'
proxy = sql.get_setting('proxy')
keepalived_path_logs = sql.get_setting('keepalived_path_logs')
proxy_serv = ''
ssh_settings = return_ssh_keys_path(slave)
full_path = '/var/www/haproxy-wi/app'
service = 'slave Keepalived'
tags = ''
for server_ip in server_ips:
ssh_settings = return_ssh_keys_path(server_ip)
inv['server']['hosts'][server_ip]['ansible_ssh_private_key_file'] = ssh_settings['key']
inv['server']['hosts'][server_ip]['ansible_password'] = ssh_settings['password']
inv['server']['hosts'][server_ip]['ansible_user'] = ssh_settings['user']
inv['server']['hosts'][server_ip]['ansible_port'] = ssh_settings['port']
inv['server']['hosts'][server_ip]['ansible_become'] = True
if proxy is not None and proxy != '' and proxy != 'None':
proxy_serv = proxy
if proxy is not None and proxy != '' and proxy != 'None':
proxy_serv = proxy
inv['server']['hosts'][server_ip]['PROXY'] = proxy_serv
if 'DOCKER' in inv['server']['hosts'][server_ip]:
if inv['server']['hosts'][server_ip]['DOCKER']:
tags = 'docker'
else:
tags = 'system'
envvars = {
'ANSIBLE_DISPLAY_OK_HOSTS': 'no',
'ANSIBLE_SHOW_CUSTOM_STATS': 'no',
'ANSIBLE_DISPLAY_SKIPPED_HOSTS': "no",
'ANSIBLE_CALLBACK_PLUGINS': "/var/www/haproxy-wi/app/scripts/ansible/callback_plugins",
'ANSIBLE_CALLBACKS_ENABLED': "roxywi",
'ANSIBLE_STDOUT_CALLBACK': "roxywi",
'ORIGINAL_STDOUT_CALLBACK': "roxywi",
'ANSIBLE_HOST_KEY_CHECKING': "no",
'ACTION_WARNINGS': "no",
'LOCALHOST_WARNING': "no",
'COMMAND_WARNINGS': "no",
'AWX_DISPLAY': False,
}
kwargs = {
'private_data_dir': '/var/www/haproxy-wi/app/scripts/ansible/',
'inventory': inventory,
'envvars': envvars,
'playbook': f'/var/www/haproxy-wi/app/scripts/ansible/roles/{ansible_role}.yml',
'tags': tags
}
if os.path.isfile(inventory):
os.remove(inventory)
try:
os.system(f"cp {full_path}/scripts/{script} {full_path}/{script}")
with open(inventory, 'a') as invent:
invent.write(str(inv))
except Exception as e:
raise Exception(f'error: {e}')
raise Exception(f'error: Cannot save inventory file: {e}')
commands = [
f"chmod +x {full_path}/{script} && {full_path}/{script} PROXY={proxy_serv} SSH_PORT={ssh_settings['port']} router_id={router_id} ETH={eth} "
f"IP={vrrp_ip} MASTER=BACKUP ETH_SLAVE={eth_slave} SYN_FLOOD={syn_flood} keepalived_path_logs={keepalived_path_logs} HAPROXY={haproxy} "
f"NGINX={nginx} HOST={slave} USER={ssh_settings['user']} PASS='{ssh_settings['password']}' KEY={ssh_settings['key']}"
]
result = ansible_runner.run(**kwargs)
stats = result.stats
try:
return_out = server_mod.subprocess_execute_with_rc(commands[0])
except Exception as e:
raise Exception(f'error: {e}')
try:
show_installation_output(return_out['error'], return_out['output'], service, rc=return_out['rc'])
except Exception as e:
raise Exception(f'error: read output: {e}')
try:
sql.update_server_master(master, slave)
sql.update_keepalived(slave)
except Exception as e:
raise Exception(f'{e}')
try:
os.remove(f'{full_path}/{script}')
except Exception:
pass
if not api:
return show_success_installation(service)
def keepalived_masteradd(master, eth, slave_eth, vrrp_ip, router_id, return_to_master, kp):
script = "install_keepalived.sh"
proxy = sql.get_setting('proxy')
keepalived_path_logs = sql.get_setting('keepalived_path_logs')
proxy_serv = ''
ssh_settings = return_ssh_keys_path(master)
full_path = '/var/www/haproxy-wi/app'
if proxy is not None and proxy != '' and proxy != 'None':
proxy_serv = proxy
os.system(f"cp {full_path}/scripts/{script} {full_path}/{script}")
commands = [
f"chmod +x {full_path}/{script} && {full_path}/{script} PROXY={proxy_serv} SSH_PORT={ssh_settings['port']} ETH={eth} ETH_SLAVE={slave_eth} "
f"keepalived_path_logs={keepalived_path_logs} RETURN_TO_MASTER={return_to_master} IP={vrrp_ip} MASTER=MASTER "
f"RESTART={kp} ADD_VRRP=1 HOST={master} router_id={router_id} USER={ssh_settings['user']} "
f"PASS='{ssh_settings['password']}' KEY={ssh_settings['key']}"
]
return_out = server_mod.subprocess_execute_with_rc(commands[0])
try:
show_installation_output(return_out['error'], return_out['output'], 'master VRRP address', rc=return_out['rc'])
except Exception as e:
raise Exception(e)
return show_success_installation('master VRRP address')
def keepalived_slaveadd(slave, eth, slave_eth, vrrp_ip, router_id, kp):
script = "install_keepalived.sh"
proxy = sql.get_setting('proxy')
keepalived_path_logs = sql.get_setting('keepalived_path_logs')
proxy_serv = ''
ssh_settings = return_ssh_keys_path(slave)
full_path = '/var/www/haproxy-wi/app'
if proxy is not None and proxy != '' and proxy != 'None':
proxy_serv = proxy
os.system(f"cp {full_path}/scripts/{script} {full_path}/{script}")
commands = [
f"chmod +x {full_path}/{script} && {full_path}/{script} PROXY={proxy_serv} SSH_PORT={ssh_settings['port']} ETH={eth} ETH_SLAVE={slave_eth} "
f"keepalived_path_logs={keepalived_path_logs} IP={vrrp_ip} MASTER=BACKUP RESTART={kp} ADD_VRRP=1 HOST={slave} "
f"router_id={router_id} USER={ssh_settings['user']} PASS='{ssh_settings['password']}' KEY={ssh_settings['key']}"
]
return_out = server_mod.subprocess_execute_with_rc(commands[0])
try:
show_installation_output(return_out['error'], return_out['output'], 'slave VRRP address', rc=return_out['rc'])
except Exception as e:
raise Exception(e)
os.remove(f'{full_path}/{script}')
return show_success_installation('slave VRRP address')
os.remove(inventory)
return stats

View File

@ -0,0 +1,22 @@
import app.modules.server.server as server_mod
def get_status(server_ip: str) -> tuple:
out1 = []
h = (['', ''],)
try:
cmd = [
"/usr/sbin/keepalived -v 2>&1|head -1|awk '{print $2}' && systemctl status keepalived |"
"grep -e 'Active' |awk '{print $2, $9$10$11$12$13}' && ps ax |grep keepalived|grep -v grep |wc -l"
]
out = server_mod.ssh_command(server_ip, cmd)
for k in out.split():
out1.append(k)
h = (out1,)
servers_with_status1= h
servers_with_status2= h
except Exception:
servers_with_status1 = h
servers_with_status2 = h
return servers_with_status1, servers_with_status2

View File

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

296
app/routes/ha/routes.py Normal file
View File

@ -0,0 +1,296 @@
import json
from flask import render_template, g, request, jsonify
from flask_login import login_required
from app import app
from app.routes.ha import bp
from middleware import get_user_params, check_services
import modules.db.sql as sql
import app.modules.common.common as common
import modules.server.server as server_mod
import modules.roxywi.common as roxywi_common
import modules.service.keepalived as keepalived
import modules.service.ha_cluster as ha_cluster
import modules.service.installation as installation
@bp.before_request
@login_required
def before_request():
""" Protect all of the admin endpoints. """
pass
@bp.route('/<service>', methods=['GET', 'POST', 'PUT', 'DELETE'])
@check_services
@get_user_params()
def cluster_function(service):
user_params = g.user_params
group_id = user_params['group_id']
if request.method == 'GET':
clusters = sql.select_clusters(group_id)
is_needed_tool = common.is_tool('ansible')
user_subscription = roxywi_common.return_user_subscription()
return render_template(
'ha_cluster.html', role=user_params['role'], user=user_params['user'], clusters=clusters, is_needed_tool=is_needed_tool,
user_services=user_params['user_services'], token=user_params['token'], lang=user_params['lang'], user_subscription=user_subscription
)
elif request.method == 'PUT':
cluster = json.loads(request.form.get('jsonData'))
try:
return ha_cluster.update_cluster(cluster, group_id)
except Exception as e:
return f'{e}'
elif request.method == 'POST':
cluster = json.loads(request.form.get('jsonData'))
try:
return ha_cluster.create_cluster(cluster, group_id)
except Exception as e:
return f'{e}'
elif request.method == 'DELETE':
cluster_id = int(request.form.get('cluster_id'))
try:
return ha_cluster.delete_cluster(cluster_id)
except Exception as e:
return f'{e}'
@bp.route('/<service>/get/<int:cluster_id>')
@check_services
@get_user_params()
def get_ha_cluster(service, cluster_id):
user_params = g.user_params
group_id = user_params['group_id']
clusters = sql.select_cluster(cluster_id)
router_id = sql.get_router_id(cluster_id, default_router=1)
slaves = sql.select_cluster_slaves(cluster_id, router_id)
virts = sql.select_clusters_virts()
vips = sql.select_cluster_vips(cluster_id)
servers = roxywi_common.get_dick_permit(virt=1)
cluster_services = sql.select_cluster_services(cluster_id)
services = sql.select_services()
return render_template(
'ajax/ha/clusters.html', role=user_params['role'], user=user_params['user'], servers=servers,
user_services=user_params['user_services'], token=user_params['token'], lang=user_params['lang'],
clusters=clusters, slaves=slaves, virts=virts, vips=vips, cluster_services=cluster_services, services=services,
group_id=group_id, router_id=router_id
)
@bp.route('/<service>/settings/<int:cluster_id>')
@check_services
@get_user_params()
def get_cluster_settings(service, cluster_id):
settings = {}
clusters = sql.select_cluster(cluster_id)
router_id = sql.get_router_id(cluster_id, default_router=1)
slaves = sql.select_cluster_slaves(cluster_id, router_id)
cluster_services = sql.select_cluster_services(cluster_id)
vip = sql.select_clusters_vip(cluster_id, router_id)
return_master = sql.select_clusters_vip_return_master(cluster_id, router_id)
vip_id = sql.select_clusters_vip_id(cluster_id, router_id)
is_virt = sql.check_ha_virt(vip_id)
for cluster in clusters:
settings.setdefault('name', cluster.name)
settings.setdefault('desc', cluster.desc)
settings.setdefault('return_to_master', return_master)
settings.setdefault('syn_flood', cluster.syn_flood)
settings.setdefault('vip', vip)
settings.setdefault('virt_server', is_virt)
for slave in slaves:
if slave[31]:
settings.setdefault('eth', slave[32])
for c_s in cluster_services:
if int(c_s.service_id) == 1:
settings.setdefault('haproxy', 1)
elif int(c_s.service_id) == 2:
settings.setdefault('nginx', 1)
elif int(c_s.service_id) == 4:
settings.setdefault('apache', 1)
return jsonify(settings)
@bp.route('/<service>/<int:cluster_id>')
@check_services
@get_user_params()
def show_ha_cluster(service, cluster_id):
user_params = g.user_params
services = []
service = 'keepalived'
service_desc = sql.select_service(service)
servers = sql.select_cluster_master_slaves(cluster_id, user_params['group_id'])
waf_server = ''
cmd = "ps ax |grep -e 'keep_alive.py' |grep -v grep |wc -l"
keep_alive, stderr = server_mod.subprocess_execute(cmd)
servers_with_status1 = []
restart_settings = sql.select_restart_services_settings(service_desc.slug)
for s in servers:
servers_with_status = list()
servers_with_status.append(s[0])
servers_with_status.append(s[1])
servers_with_status.append(s[2])
servers_with_status.append(s[11])
status1, status2 = keepalived.get_status(s[2])
servers_with_status.append(status1)
servers_with_status.append(status2)
servers_with_status.append(s[22])
servers_with_status.append(sql.is_master(s[2]))
servers_with_status.append(sql.select_servers(server=s[2]))
is_keepalived = sql.select_keepalived(s[2])
if is_keepalived:
try:
cmd = ['sudo kill -USR1 `cat /var/run/keepalived.pid` && sudo grep State /tmp/keepalived.data -m 1 |'
'awk -F"=" \'{print $2}\'|tr -d \'[:space:]\' && sudo rm -f /tmp/keepalived.data']
out = server_mod.ssh_command(s[2], cmd)
out1 = ('1', out)
servers_with_status.append(out1)
except Exception as e:
servers_with_status.append(str(e))
else:
servers_with_status.append('')
servers_with_status1.append(servers_with_status)
user_subscription = roxywi_common.return_user_subscription()
return render_template(
'service.html', role=user_params['role'], user=user_params['user'],
servers=servers_with_status1, keep_alive=''.join(keep_alive), service=service, services=services,
user_services=user_params['user_services'], user_status=user_subscription['user_status'],
user_plan=user_subscription['user_plan'], waf_server=waf_server, restart_settings=restart_settings,
service_desc=service_desc, token=user_params['token'], lang=user_params['lang']
)
@bp.route('/<service>/slaves/<int:cluster_id>', methods=['GET', 'POST'])
@check_services
@get_user_params()
def get_slaves(service, cluster_id):
lang = g.user_params['lang']
if request.method == 'GET':
router_id = sql.get_router_id(cluster_id, default_router=1)
else:
router_id = int(request.form.get('router_id'))
slaves = sql.select_cluster_slaves(cluster_id, router_id)
return render_template('ajax/ha/add_vip_slaves.html', lang=lang, slaves=slaves)
@bp.route('/<service>/slaves/servers/<int:cluster_id>')
@check_services
@get_user_params()
def get_server_slaves(service, cluster_id):
group_id = g.user_params['group_id']
lang = g.user_params['lang']
try:
router_id = sql.get_router_id(cluster_id, default_router=1)
slaves = sql.select_cluster_slaves(cluster_id, router_id)
except Exception:
slaves = ''
free_servers = sql.select_ha_cluster_not_masters_not_slaves(group_id)
return render_template('ajax/ha/slave_servers.html', free_servers=free_servers, slaves=slaves, lang=lang)
@bp.route('/<service>/masters')
@check_services
@get_user_params()
def get_masters(service):
group_id = g.user_params['group_id']
free_servers = sql.select_ha_cluster_not_masters_not_slaves(group_id)
return render_template('ajax/ha/masters.html', free_servers=free_servers)
@bp.route('/<service>/settings/<int:cluster_id>/vip/<int:router_id>')
@check_services
def get_vip_settings(service, cluster_id, router_id):
settings = {}
return_master = sql.select_clusters_vip_return_master(cluster_id, router_id)
vip_id = sql.select_clusters_vip_id(cluster_id, router_id)
is_virt = sql.check_ha_virt(vip_id)
settings.setdefault('return_to_master', return_master)
settings.setdefault('virt_server', is_virt)
return jsonify(settings)
@bp.route('/<service>/<int:cluster_id>/vip', methods=['POST', 'PUT', 'DELETE'])
@check_services
@get_user_params()
def ha_vip(service, cluster_id):
user_params = g.user_params
group_id = user_params['group_id']
json_data = json.loads(request.form.get('jsonData'))
if request.method == 'PUT':
router_id = int(json_data['router_id'])
try:
ha_cluster.update_vip(cluster_id, router_id, json_data, group_id)
except Exception as e:
return f'{e}'
return 'ok'
elif request.method == 'POST':
try:
ha_cluster.insert_vip(cluster_id, json_data, group_id)
except Exception as e:
return f'{e}'
return 'ok'
elif request.method == 'DELETE':
router_id = int(json_data['router_id'])
try:
sql.delete_ha_router(router_id)
return 'ok'
except Exception as e:
return f'error: Cannot delete VIP: {e}'
@bp.route('/<service>/reconfigure/<install_service>', methods=['PUT'])
@check_services
def reconfigure_haproxy(service, install_service):
json_data = request.form.get('jsonData')
update_functions = {
'haproxy': sql.update_haproxy,
'nginx': sql.update_nginx,
'apache': sql.update_apache,
}
generate_functions = {
'haproxy': installation.generate_haproxy_inv,
'nginx': installation.generate_service_inv,
'apache': installation.generate_service_inv,
'keepalived': installation.generate_kp_inv,
}
inv, server_ips = generate_functions[install_service](json_data, install_service)
json_data = json.loads(json_data)
is_docker = None
if install_service == 'keepalived':
nice_service_name = 'HA cluster'
else:
service_desc = sql.select_service(install_service)
nice_service_name = service_desc.service
is_docker = json_data['services'][install_service]['docker']
for server_ip in server_ips:
if install_service == 'keepalived':
continue
try:
update_functions[install_service](server_ip)
except Exception as e:
return str(e)
if is_docker == '1':
server_id = sql.select_server_id_by_ip(server_ip)
sql.insert_or_update_service_setting(server_id, install_service, 'dockerized', '1')
sql.insert_or_update_service_setting(server_id, install_service, 'restart', '1')
return installation.run_ansible(inv, server_ips, install_service, nice_service_name), 201

View File

@ -2,7 +2,7 @@ from flask import render_template, request, g, abort
from flask_login import login_required
from app.routes.install import bp
from middleware import get_user_params
from middleware import get_user_params, check_services
import app.modules.db.sql as sql
import app.modules.common.common as common
import app.modules.roxywi.auth as roxywi_auth
@ -36,135 +36,20 @@ def install_monitoring():
)
@bp.route('/ha')
@get_user_params()
def ha():
if not roxywi_auth.is_access_permit_to_service('keepalived'):
abort(403, 'You do not have needed permissions to access to Keepalived service')
roxywi_auth.page_for_admin(level=2)
user_params = g.user_params
is_needed_tool = common.is_tool('ansible')
user_subscription = roxywi_common.return_user_subscription()
return render_template(
'ha.html', h2=1, role=user_params['role'], user=user_params['user'], selects=user_params['servers'],
user_services=user_params['user_services'], user_status=user_subscription['user_status'], lang=user_params['lang'],
user_plan=user_subscription['user_plan'], is_needed_tool=is_needed_tool, token=user_params['token']
)
@bp.post('/keepalived', defaults={'slave_kp': None})
@bp.post('/keepalived/<slave_kp>')
def install_keepalived(slave_kp):
master = request.form.get('master')
slave = request.form.get('slave')
eth = request.form.get('interface')
eth_slave = request.form.get('slave_interface')
vrrp_ip = request.form.get('vrrpip')
syn_flood = request.form.get('syn_flood')
return_to_master = request.form.get('return_to_master')
haproxy = request.form.get('hap')
nginx = request.form.get('nginx')
router_id = request.form.get('router_id')
virt_server = request.form.get('virt_server')
@bp.post('/<service>')
@check_services
def install_service(service):
try:
virt_server = int(virt_server)
except Exception:
pass
if not slave_kp:
try:
return service_mod.keepalived_master_install(
master, eth, eth_slave, vrrp_ip, virt_server, syn_flood, return_to_master, haproxy, nginx, router_id
)
except Exception as e:
return f'{e}'
else:
try:
return service_mod.keepalived_slave_install(
master, slave, eth, eth_slave, vrrp_ip, syn_flood, haproxy, nginx, router_id
)
except Exception as e:
return f'{e}'
@bp.post('/keepalived/add', defaults={'slave_kp': None})
@bp.post('/keepalived/add/<slave_kp>')
def add_extra_vrrp(slave_kp):
master = request.form.get('master')
slave = request.form.get('slave')
eth = request.form.get('interface')
slave_eth = request.form.get('slave_interface')
vrrp_ip = request.form.get('vrrpip')
router_id = request.form.get('router_id')
return_to_master = request.form.get('return_to_master')
kp = request.form.get('kp')
if not slave_kp:
try:
return service_mod.keepalived_masteradd(master, eth, slave_eth, vrrp_ip, router_id, return_to_master, kp)
except Exception as e:
return f'{e}'
else:
try:
return service_mod.keepalived_slaveadd(slave, eth, slave_eth, vrrp_ip, router_id, kp)
except Exception as e:
return f'{e}'
@bp.post('/<service>/<server_ip>')
def install_service(service, server_ip):
server_ip = common.is_ip_or_dns(server_ip)
docker = common.checkAjaxInput(request.form.get('docker'))
syn_flood = request.form.get('syn_flood')
hapver = request.form.get('hapver')
if service in ('nginx', 'apache'):
try:
return service_mod.install_service(server_ip, service, docker, syn_flood)
except Exception as e:
return str(e)
elif service == 'haproxy':
try:
return service_mod.install_haproxy(server_ip, syn_flood=syn_flood, hapver=hapver, docker=docker)
except Exception as e:
return str(e)
else:
return 'warning: Wrong service'
@bp.post('/<service>/master-slave')
def master_slave(service):
master = request.form.get('master')
slave = request.form.get('slave')
server = request.form.get('server')
docker = request.form.get('docker')
if service == 'haproxy':
if server == 'master':
try:
return service_mod.install_haproxy(master, server=server, docker=docker, m_or_s='master', master=master, slave=slave)
except Exception as e:
return f'{e}'
elif server == 'slave':
try:
return service_mod.install_haproxy(slave, server=server, docker=docker, m_or_s='slave', master=master, slave=slave)
except Exception as e:
return f'{e}'
elif service == 'nginx':
syn_flood_protect = '1' if request.form.get('syn_flood') == "1" else ''
if server == 'master':
try:
return service_mod.install_service(master, 'nginx', docker, syn_flood_protect, server=server)
except Exception as e:
return f'{e}'
elif server == 'slave':
try:
return service_mod.install_service(slave, 'nginx', docker, syn_flood_protect, server=server)
except Exception as e:
return f'{e}'
json_data = request.form.get('jsonData')
generate_functions = {
'haproxy': service_mod.generate_haproxy_inv,
'nginx': service_mod.generate_service_inv,
'apache': service_mod.generate_service_inv,
}
inv, server_ips = generate_functions[service](json_data, service)
return service_mod.run_ansible(inv, server_ips, service, service), 201
except Exception as e:
return str(e)
@bp.route('/<service>/version/<server_ip>')

View File

@ -151,11 +151,14 @@ def service_history(service, server_ip):
user_subscription = roxywi_common.return_user_subscription()
user_params = g.user_params
if service in ('haproxy', 'nginx', 'keepalived', 'apache'):
if service in ('haproxy', 'nginx', 'keepalived', 'apache', 'cluster'):
service_desc = sql.select_service(service)
if not roxywi_auth.is_access_permit_to_service(service_desc.slug):
abort(403, f'You do not have needed permissions to access to {service_desc.slug.title()} service')
server_id = sql.select_server_id_by_ip(server_ip)
if service == 'cluster':
server_id = server_ip
else:
server_id = sql.select_server_id_by_ip(server_ip)
history = sql.select_action_history_by_server_id_and_service(server_id, service_desc.service)
elif service == 'server':
if roxywi_common.check_is_server_in_group(server_ip):

View File

@ -1,6 +1,6 @@
---
- name: Install common role
hosts: "{{ variable_host }}"
hosts: all
become: yes
become_method: sudo
gather_facts: yes
@ -15,7 +15,7 @@
- docker
- name: Install docker
hosts: "{{ variable_host }}"
hosts: all
become: yes
gather_facts: yes
roles:
@ -34,7 +34,7 @@
- docker
- name: Install system role
hosts: "{{ variable_host }}"
hosts: all
become: yes
become_method: sudo
tasks:
@ -50,7 +50,7 @@
- system
- name: "Set {{ ansible_user }} owner to {{ service_dir }}"
hosts: "{{ variable_host }}"
hosts: all
become: yes
gather_facts: no
tasks:

View File

@ -1,6 +1,6 @@
---
- name: Install HAProxy as a service
hosts: "{{ variable_host }}"
hosts: all
become: yes
become_method: sudo
gather_facts: yes
@ -14,7 +14,7 @@
- system
- name: Install HAProxy as a Docker
hosts: "{{ variable_host }}"
hosts: all
become: yes
become_method: sudo
gather_facts: yes

View File

@ -1,7 +1,7 @@
---
- name: Set SSH port
set_fact:
ansible_port: "{{SSH_PORT}}"
#- name: Set SSH port
# set_fact:
# ansible_port: "{{SSH_PORT}}"
- name: check if HAProxy is installed
package_facts:

View File

@ -43,10 +43,14 @@ listen stats
peers default_peers
{% if M_OR_S == 'master' %}
peer {{ ansible_hostname }} {{MASTER}}:10000
peer slave_lb {{SLAVE}}:10000
{% for slave in slaves %}
peer slave_lb {{slave}}:10000
{% endfor %}
{% else %}
peer master_lb {{MASTER}}:10000
peer {{ ansible_hostname }} {{SLAVE}}:10000
{% for slave in slaves %}
peer {{ ansible_hostname }} {{slave}}:10000
{% endfor %}
{% endif %}
{% endif %}

View File

@ -1,4 +1,4 @@
/var/log/haproxy.log {
/var/log/haproxy/access.log {
daily
rotate 10
missingok
@ -9,4 +9,30 @@
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
/bin/kill -HUP `cat /var/run/rsyslogd.pid 2> /dev/null` 2> /dev/null || true
endscript
}
}
/var/log/haproxy/error.log {
daily
rotate 10
missingok
notifempty
compress
sharedscripts
postrotate
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
/bin/kill -HUP `cat /var/run/rsyslogd.pid 2> /dev/null` 2> /dev/null || true
endscript
}
/var/log/haproxy/status.log {
daily
rotate 10
missingok
notifempty
compress
sharedscripts
postrotate
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
/bin/kill -HUP `cat /var/run/rsyslogd.pid 2> /dev/null` 2> /dev/null || true
endscript
}

View File

@ -1,4 +1,4 @@
- hosts: "{{ variable_host }}"
- hosts: all
become: yes
become_method: sudo
gather_facts: yes

View File

@ -1,28 +0,0 @@
---
- name: Creation config from template
template:
src: add_vrrp.conf.j2
dest: /etc/keepalived/keepalived.conf_temp
mode: 0644
force: no
- name: "Append keepalived.conf with content from temporary file"
shell: cat keepalived.conf_temp >> keepalived.conf
args:
chdir: "/etc/keepalived/"
- name: "Delete temporary file"
file:
path: /etc/keepalived/keepalived.conf_temp
state: absent
- name: Restart service keepalived
service:
name: keepalived
state: restarted
force: no
ignore_errors: yes
when: (RESTART is defined) and (RESTART|length > 0)

View File

@ -1,111 +0,0 @@
---
- name: check if Keepalived is installed
package_facts:
manager: "auto"
- name: install EPEL Repository
yum:
name: epel-release
state: latest
when:
- ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
- ansible_facts['distribution_major_version'] == '7'
ignore_errors: yes
environment:
http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}"
- name: Creates log directory
file:
path: "{{keepalived_path_logs}}"
state: directory
- name: Copy keepalived configuration for rsyslog.
template:
src: rsyslog.conf.j2
dest: /etc/rsyslog.d/50-keepalived.conf
mode: 0644
notify: restart rsyslog
- name: Install the latest version of Keepalived
package:
name:
- keepalived
- psmisc
state: present
when: "'keepalived' not in ansible_facts.packages"
environment:
http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}"
- name: Ensure group "keepalived_script" exists
group:
name: keepalived_script
state: present
system: yes
- name: Add the user 'keepalived_script'
user:
name: keepalived_script
comment: User for keepalived_script
group: keepalived_script
shell: /sbin/nologin
create_home: no
system: yes
- name: Copy keepalived configuration in place.
template:
src: keepalived.conf.j2
dest: /etc/keepalived/keepalived.conf
mode: 0644
- name: test to see if selinux is running
command: getenforce
register: sestatus
changed_when: false
when: (ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS')
- name: Disble SELINUX in config
template:
src: ../../haproxy/templates/selinux.j2
dest: /etc/selinux/config
ignore_errors: yes
when:
- sestatus.stdout is defined
- '"Enforcing" in sestatus.stdout'
- name: Disble SELINUX in env
shell: setenforce 0 2> /dev/null
ignore_errors: yes
debugger: never
when:
- sestatus.stdout is defined
- '"Enforcing" in sestatus.stdout'
- name: Enable and start service keepalived
service:
name: keepalived
daemon_reload: yes
state: started
enabled: yes
ignore_errors: yes
- name: Enable net.ipv4.ip_forward
sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_set: yes
state: present
reload: yes
- name: Add syn_flood tasks
include: ../../haproxy/tasks/syn_flood.yml
when: (SYN_FLOOD is defined) and (SYN_FLOOD|length > 0)

View File

@ -1,13 +1,111 @@
---
- name: Set SSH port
set_fact:
ansible_port: "{{SSH_PORT}}"
- name: Add installation tasks
include_tasks: install.yml
when: (ADD_VRRP is not defined) or (ADD_VRRP != "1")
- name: Add add vrrp tasks
include_tasks: add_vrrp.yml
when: (ADD_VRRP is defined) and (ADD_VRRP|length > 0)
- name: check if Keepalived is installed
package_facts:
manager: "auto"
- name: install EPEL Repository
yum:
name: epel-release
state: latest
when:
- ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
- ansible_facts['distribution_major_version'] == '7'
ignore_errors: yes
environment:
http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}"
- name: Creates log directory
file:
path: "{{keepalived_path_logs}}"
state: directory
- name: Copy keepalived configuration for rsyslog.
template:
src: rsyslog.conf.j2
dest: /etc/rsyslog.d/50-keepalived.conf
mode: 0644
notify: restart rsyslog
- name: Install the latest version of Keepalived
package:
name:
- keepalived
- psmisc
state: present
when: "'keepalived' not in ansible_facts.packages"
environment:
http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}"
- name: Ensure group "keepalived_script" exists
group:
name: keepalived_script
state: present
system: yes
- name: Add the user 'keepalived_script'
user:
name: keepalived_script
comment: User for keepalived_script
group: keepalived_script
shell: /sbin/nologin
create_home: no
system: yes
- name: Copy keepalived configuration in place.
template:
src: keepalived.conf.j2
dest: /etc/keepalived/keepalived.conf
mode: 0644
- name: test to see if selinux is running
command: getenforce
register: sestatus
changed_when: false
when: (ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS')
- name: Disble SELINUX in config
template:
src: ../../haproxy/templates/selinux.j2
dest: /etc/selinux/config
ignore_errors: yes
when:
- sestatus.stdout is defined
- '"Enforcing" in sestatus.stdout'
- name: Disble SELINUX in env
shell: setenforce 0 2> /dev/null
ignore_errors: yes
debugger: never
when:
- sestatus.stdout is defined
- '"Enforcing" in sestatus.stdout'
- name: Enable and start service keepalived
service:
name: keepalived
daemon_reload: yes
state: started
enabled: yes
ignore_errors: yes
- name: Enable net.ipv4.ip_forward
sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_set: yes
state: present
reload: yes
- name: Add syn_flood tasks
include: ../../haproxy/tasks/syn_flood.yml
when: (SYN_FLOOD is defined) and (SYN_FLOOD|length > 0)

View File

@ -1,21 +0,0 @@
vrrp_instance VI_{{IP}} {
state {{MASTER}}
interface {% if MASTER == 'MASTER' %}{{ETH}} {% else %} {{ETH_SLAVE}} {% endif %}
virtual_router_id {{ router_id }}
priority {% if RETURN_TO_MASTER == 1 and MASTER == 'MASTER' %}152{% elif MASTER == 'MASTER' and RETURN_TO_MASTER == 0 %}102{% else %}101{%endif%}
track_script {
chk_haproxy
}
advert_int 1
authentication {
auth_type PASS
auth_pass VerySecretPass2!
}
virtual_ipaddress {
{{IP}}
}
}

View File

@ -1,27 +1,47 @@
global_defs {
# Managed by Roxy-WI do not edit this file. Use HA cluster configuration instead
router_id LVS_DEVEL
}
{%- if NGINX == '1' %}
{% set check_service = 'nginx' %}
{% else %}
{% set check_service = 'haproxy' %}
{% endif %}
#health-check for keepalive
vrrp_script chk_service {
script "systemctl is-active --quiet {{ check_service }}"
interval 2
weight 3
}
vrrp_instance VI_1 {
state {{MASTER}}
interface {% if MASTER == 'MASTER' %}{{ETH}} {% else %} {{ETH_SLAVE}} {% endif %}
virtual_router_id {{router_id}}
priority {% if RETURN_TO_MASTER == '1' and MASTER == 'MASTER' %}152{% elif MASTER == 'MASTER' and RETURN_TO_MASTER == '0' %}102{% else %}101{%endif%}
{%- if NGINX %}
#NGINX health-check for keepalive
vrrp_script chk_nginx {
script "systemctl is-active --quiet nginx"
interval 2
weight 3
}
{% endif %}
{%- if HAPROXY %}
#HAProxy health-check for keepalive
vrrp_script chk_haproxy {
script "systemctl is-active --quiet haproxy"
interval 2
weight 3
}
{% endif %}
{% for router, vip in routers.items() %}
{% for vrrp, s in vip.items() %}
vrrp_instance VI_{{router}} {
state {{s.master}}
interface {{s.eth}}
virtual_router_id {{router}}
priority {% if s.return_master and s.master %}152{% elif s.master and not s.return_master %}102{% else %}101{%endif%}
#check if we are still running
track_script {
chk_service
{%- if HAPROXY %}
chk_haproxy
{% endif %}
{%- if NGINX %}
chk_nginx
{% endif %}
}
advert_int 1
@ -30,6 +50,9 @@ vrrp_instance VI_1 {
auth_pass VerySecretPass
}
virtual_ipaddress {
{{IP}}
{{vrrp}}
}
}
{% endfor %}
{% endfor %}
# Managed by Roxy-WI do not edit this file. Use HA cluster configuration instead

View File

@ -1,6 +1,6 @@
---
- name: Install common role
hosts: "{{ variable_host }}"
hosts: all
become: yes
become_method: sudo
gather_facts: yes
@ -15,7 +15,7 @@
- docker
- name: Install docker
hosts: "{{ variable_host }}"
hosts: all
become: yes
gather_facts: yes
roles:
@ -34,7 +34,7 @@
- docker
- name: Install system role
hosts: "{{ variable_host }}"
hosts: all
become: yes
become_method: sudo
tasks:
@ -58,13 +58,10 @@
- system
- name: "Set {{ ansible_user }} owner to {{ service_dir }}"
hosts: "{{ variable_host }}"
hosts: all
become: yes
gather_facts: no
tasks:
- name: Set SSH port
set_fact:
ansible_port: "{{SSH_PORT}}"
- name: "Set {{ ansible_user }} owner to {{ service_dir }}"
file:
path: "{{ service_dir }}"

View File

@ -1,56 +0,0 @@
#!/bin/bash
for ARGUMENT in "$@"
do
KEY=$(echo $ARGUMENT | cut -f1 -d=)
VALUE=$(echo $ARGUMENT | cut -f2 -d=)
case "$KEY" in
PROXY) PROXY=${VALUE} ;;
HOST) HOST=${VALUE} ;;
USER) USER=${VALUE} ;;
PASS) PASS=${VALUE} ;;
KEY) KEY=${VALUE} ;;
SYN_FLOOD) SYN_FLOOD=${VALUE} ;;
STAT_PORT) STAT_PORT=${VALUE} ;;
STAT_PAGE) STAT_PAGE=${VALUE} ;;
STATS_USER) STATS_USER=${VALUE} ;;
STATS_PASS) STATS_PASS=${VALUE} ;;
SSH_PORT) SSH_PORT=${VALUE} ;;
CONFIG_PATH) CONFIG_PATH=${VALUE} ;;
DOCKER) DOCKER=${VALUE} ;;
CONT_NAME) CONT_NAME=${VALUE} ;;
service_dir) service_dir=${VALUE} ;;
*)
esac
done
if [[ $DOCKER == '1' ]]; then
tags='docker'
else
tags='system'
fi
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_DISPLAY_SKIPPED_HOSTS=False
export ACTION_WARNINGS=False
export LOCALHOST_WARNING=False
export COMMAND_WARNINGS=False
PWD=/var/www/haproxy-wi/app/scripts/ansible/
echo "$HOST ansible_port=$SSH_PORT" > $PWD/$HOST
if [[ $KEY == "" ]]; then
ansible-playbook $PWD/roles/apache.yml -e "ansible_user=$USER ansible_ssh_pass='$PASS' variable_host=$HOST PROXY=$PROXY CONT_NAME=$CONT_NAME service_dir=$service_dir SYN_FLOOD=$SYN_FLOOD STAT_PAGE=$STAT_PAGE STAT_PORT=$STAT_PORT STATS_USER=$STATS_USER STATS_PASS=$STATS_PASS CONFIG_PATH=$CONFIG_PATH SSH_PORT=$SSH_PORT service=apache" -i $PWD/$HOST -t $tags
else
ansible-playbook $PWD/roles/apache.yml --key-file $KEY -e "ansible_user=$USER variable_host=$HOST PROXY=$PROXY CONT_NAME=$CONT_NAME service_dir=$service_dir SYN_FLOOD=$SYN_FLOOD STAT_PAGE=$STAT_PAGE STAT_PORT=$STAT_PORT STATS_USER=$STATS_USER STATS_PASS=$STATS_PASS CONFIG_PATH=$CONFIG_PATH SSH_PORT=$SSH_PORT service=apache" -i $PWD/$HOST -t $tags
fi
if [ $? -gt 0 ]
then
echo "error: Can't install Apache service <br /><br />"
rm -f $PWD/$HOST
exit 1
else
echo "ok"
fi
rm -f $PWD/$HOST

View File

@ -1,59 +0,0 @@
#!/bin/bash
for ARGUMENT in "$@"
do
KEY=$(echo $ARGUMENT | cut -f1 -d=)
VALUE=$(echo $ARGUMENT | cut -f2 -d=)
case "$KEY" in
PROXY) PROXY=${VALUE} ;;
SOCK_PORT) SOCK_PORT=${VALUE} ;;
STAT_PORT) STAT_PORT=${VALUE} ;;
STAT_FILE) STAT_FILE=${VALUE} ;;
STATS_USER) STATS_USER=${VALUE} ;;
STATS_PASS) STATS_PASS=${VALUE} ;;
HAPVER) HAPVER=${VALUE} ;;
HOST) HOST=${VALUE} ;;
USER) USER=${VALUE} ;;
PASS) PASS=${VALUE} ;;
KEY) KEY=${VALUE} ;;
SYN_FLOOD) SYN_FLOOD=${VALUE} ;;
SSH_PORT) SSH_PORT=${VALUE} ;;
DOCKER) DOCKER=${VALUE} ;;
HAP_DIR) HAP_DIR=${VALUE} ;;
CONT_NAME) CONT_NAME=${VALUE} ;;
M_OR_S) M_OR_S=${VALUE} ;;
MASTER) MASTER=${VALUE} ;;
SLAVE) SLAVE=${VALUE} ;;
*)
esac
done
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_DISPLAY_SKIPPED_HOSTS=False
export ACTION_WARNINGS=False
export LOCALHOST_WARNING=False
export COMMAND_WARNINGS=False
PWD=/var/www/haproxy-wi/app/scripts/ansible/
echo "$HOST ansible_port=$SSH_PORT" > $PWD/$HOST
if [[ $DOCKER == '1' ]]; then
tags='docker'
else
tags='system'
fi
if [[ $KEY == "" ]]; then
ansible-playbook $PWD/roles/haproxy.yml -e "ansible_user=$USER ansible_ssh_pass='$PASS' variable_host=$HOST PROXY=$PROXY HAPVER=$HAPVER HAP_DIR=$HAP_DIR CONT_NAME=$CONT_NAME SOCK_PORT=$SOCK_PORT STAT_PORT=$STAT_PORT STATS_USER=$STATS_USER STATS_PASS='$STATS_PASS' STAT_FILE=$STAT_FILE SSH_PORT=$SSH_PORT SYN_FLOOD=$SYN_FLOOD M_OR_S=$M_OR_S MASTER=$MASTER SLAVE=$SLAVE" -i $PWD/$HOST -t $tags
else
ansible-playbook $PWD/roles/haproxy.yml --key-file $KEY -e "ansible_user=$USER variable_host=$HOST PROXY=$PROXY HAPVER=$HAPVER HAP_DIR=$HAP_DIR CONT_NAME=$CONT_NAME SOCK_PORT=$SOCK_PORT STAT_PORT=$STAT_PORT STATS_USER=$STATS_USER STATS_PASS='$STATS_PASS' STAT_FILE=$STAT_FILE SSH_PORT=$SSH_PORT SYN_FLOOD=$SYN_FLOOD M_OR_S=$M_OR_S MASTER=$MASTER SLAVE=$SLAVE" -i $PWD/$HOST -t $tags
fi
if [ $? -gt 0 ]
then
echo "error: Cannot install Haproxy service"
rm -f $PWD/$HOST
exit 1
fi
rm -f $PWD/$HOST

View File

@ -1,55 +0,0 @@
#!/bin/bash
for ARGUMENT in "$@"
do
KEY=$(echo $ARGUMENT | cut -f1 -d=)
VALUE=$(echo $ARGUMENT | cut -f2 -d=)
case "$KEY" in
PROXY) PROXY=${VALUE} ;;
MASTER) MASTER=${VALUE} ;;
ETH) ETH=${VALUE} ;;
ETH_SLAVE) ETH_SLAVE=${VALUE} ;;
keepalived_path_logs) keepalived_path_logs=${VALUE} ;;
IP) IP=${VALUE} ;;
HOST) HOST=${VALUE} ;;
USER) USER=${VALUE} ;;
router_id) router_id=${VALUE} ;;
PASS) PASS=${VALUE} ;;
KEY) KEY=${VALUE} ;;
SYN_FLOOD) SYN_FLOOD=${VALUE} ;;
RESTART) RESTART=${VALUE} ;;
RETURN_TO_MASTER) RETURN_TO_MASTER=${VALUE} ;;
ADD_VRRP) ADD_VRRP=${VALUE} ;;
SSH_PORT) SSH_PORT=${VALUE} ;;
HAPROXY) HAPROXY=${VALUE} ;;
NGINX) NGINX=${VALUE} ;;
*)
esac
done
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_DISPLAY_SKIPPED_HOSTS=False
export ACTION_WARNINGS=False
export LOCALHOST_WARNING=False
export COMMAND_WARNINGS=False
PWD=/var/www/haproxy-wi/app/scripts/ansible/
echo "$HOST ansible_port=$SSH_PORT" > $PWD/$HOST
if [[ $MASTER == 'BACKUP' ]]; then
sleep 5
fi
if [[ $KEY == "" ]]; then
ansible-playbook $PWD/roles/keepalived.yml -e "ansible_user=$USER ansible_ssh_pass='$PASS' variable_host=$HOST SYN_FLOOD=$SYN_FLOOD PROXY=$PROXY MASTER=$MASTER ETH=$ETH ETH_SLAVE=$ETH_SLAVE keepalived_path_logs=$keepalived_path_logs IP=$IP RESTART=$RESTART RETURN_TO_MASTER=$RETURN_TO_MASTER ADD_VRRP=$ADD_VRRP router_id=$router_id HAPROXY=$HAPROXY NGINX=$NGINX SSH_PORT=$SSH_PORT" -i $PWD/$HOST
else
ansible-playbook $PWD/roles/keepalived.yml --key-file $KEY -e "ansible_user=$USER variable_host=$HOST SYN_FLOOD=$SYN_FLOOD PROXY=$PROXY MASTER=$MASTER ETH=$ETH ETH_SLAVE=$ETH_SLAVE keepalived_path_logs=$keepalived_path_logs IP=$IP RESTART=$RESTART RETURN_TO_MASTER=$RETURN_TO_MASTER ADD_VRRP=$ADD_VRRP router_id=$router_id HAPROXY=$HAPROXY NGINX=$NGINX SSH_PORT=$SSH_PORT" -i $PWD/$HOST
fi
if [ $? -gt 0 ]
then
echo "error: Can't install keepalived service <br />"
rm -f $PWD/$HOST
exit 1
fi
rm -f $PWD/$HOST

View File

@ -1,64 +0,0 @@
#!/bin/bash
for ARGUMENT in "$@"
do
KEY=$(echo $ARGUMENT | cut -f1 -d=)
VALUE=$(echo $ARGUMENT | cut -f2 -d=)
case "$KEY" in
PROXY) PROXY=${VALUE} ;;
HOST) HOST=${VALUE} ;;
USER) USER=${VALUE} ;;
PASS) PASS=${VALUE} ;;
KEY) KEY=${VALUE} ;;
SYN_FLOOD) SYN_FLOOD=${VALUE} ;;
STAT_PORT) STAT_PORT=${VALUE} ;;
STAT_PAGE) STAT_PAGE=${VALUE} ;;
STATS_USER) STATS_USER=${VALUE} ;;
STATS_PASS) STATS_PASS=${VALUE} ;;
SSH_PORT) SSH_PORT=${VALUE} ;;
CONFIG_PATH) CONFIG_PATH=${VALUE} ;;
DOCKER) DOCKER=${VALUE} ;;
CONT_NAME) CONT_NAME=${VALUE} ;;
service_dir) service_dir=${VALUE} ;;
*)
esac
done
if [ ! -d "/var/www/haproxy-wi/app/scripts/ansible/roles/nginxinc.nginx" ]; then
if [ ! -z $PROXY ];then
export https_proxy="$PROXY"
export http_proxy="$PROXY"
fi
ansible-galaxy install nginxinc.nginx --roles-path /var/www/haproxy-wi/app/scripts/ansible/roles/
fi
if [[ $DOCKER == '1' ]]; then
tags='docker'
else
tags='system'
fi
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_DISPLAY_SKIPPED_HOSTS=False
export ACTION_WARNINGS=False
export LOCALHOST_WARNING=False
export COMMAND_WARNINGS=False
PWD=/var/www/haproxy-wi/app/scripts/ansible/
echo "$HOST ansible_port=$SSH_PORT" > $PWD/$HOST
if [[ $KEY == "" ]]; then
ansible-playbook $PWD/roles/nginx.yml -e "ansible_user=$USER ansible_ssh_pass='$PASS' variable_host=$HOST PROXY=$PROXY CONT_NAME=$CONT_NAME service_dir=$service_dir SYN_FLOOD=$SYN_FLOOD STAT_PAGE=$STAT_PAGE STAT_PORT=$STAT_PORT STATS_USER=$STATS_USER STATS_PASS=$STATS_PASS CONFIG_PATH=$CONFIG_PATH SSH_PORT=$SSH_PORT service=nginx" -i $PWD/$HOST -t $tags
else
ansible-playbook $PWD/roles/nginx.yml --key-file $KEY -e "ansible_user=$USER variable_host=$HOST PROXY=$PROXY CONT_NAME=$CONT_NAME service_dir=$service_dir SYN_FLOOD=$SYN_FLOOD STAT_PAGE=$STAT_PAGE STAT_PORT=$STAT_PORT STATS_USER=$STATS_USER STATS_PASS=$STATS_PASS CONFIG_PATH=$CONFIG_PATH SSH_PORT=$SSH_PORT service=nginx" -i $PWD/$HOST -t $tags
fi
if [ $? -gt 0 ]
then
echo "error: Can't install NGINX service <br /><br />"
rm -f $PWD/$HOST
exit 1
else
echo "ok"
fi
rm -f $PWD/$HOST

View File

@ -0,0 +1,16 @@
{% import 'languages/'+lang|default('en')+'.html' as lang %}
{% from 'include/input_macros.html' import input %}
{% for slave in slaves %}
<div class="{{ loop.cycle('odd', 'even') }}" style="height: 25px; padding-top: 10px; padding-left: 10px;" id="vip_slave-{{slave.0}}">
<div style="float: left; width: 205px">{{slave.1}}</div>
{%- if not slave.31 -%}
<span class="slave_int" id="slave_int_div-{{slave.0}}" data-ip="{{slave.2}}">
{{ input('slave_int-'+slave.0|string(), placeholder='eth0', value=slave.32, size='7', title=lang.phrases.int_vrrp + ' ' +lang.ha_page.start_enter) }}
</span>
{% else %}
<span class="master_int" id="master_int_div-{{slave.0}}" data-ip="{{slave.2}}">
{{ input('master_int-'+slave.0|string(), placeholder='eth0', value=slave.32, size='7', title=lang.phrases.int_vrrp + ' ' +lang.ha_page.start_enter) }}
</span>
{% endif %}
</div>
{% endfor %}

View File

@ -0,0 +1,76 @@
{% import 'languages/'+lang|default('en')+'.html' as lang %}
{% from 'include/input_macros.html' import input, checkbox, copy_to_clipboard %}
{% for cluster in clusters %}
<div id="cluster-{{cluster.id}}" class="div-server-hapwi">
<div class="server-name">
<a href="/app/ha/cluster/{{cluster.id}}" style="color: #5d9ceb" title="{{lang.words.open|title()}} {{lang.words.cluster}}">
<span id="cluster-name-{{cluster.id}}">{{cluster.name}}</span>
<span id="cluster-desc-{{cluster.id}}">{% if cluster.desc != '' %} ({{cluster.desc}}) {% endif %}</span>
</a>
<span class="server-action">
{% if role <= 2 %}
<a class="plus" onclick="add_vip_ha_cluster('{{cluster.id}}', '{{cluster.name}}')"></a>
<a class="edit" onclick="createHaClusterStep1(true, '{{cluster.id}}')"></a>
<a class="delete" onclick="confirmDeleteCluster('{{cluster.id}}')"></a>
<span class="portlet-header" title="{{lang.words.change2|title()}} {{lang.words.position}} {{cluster.name}}">
<i class="fas fa-grip-vertical"></i>
</span>
{% endif %}
</span>
</div>
<div class="server-desc">
{% for slave in slaves %}
{% if slave.31 %}
Master name: {{ copy_to_clipboard(id='master-server-'+cluster.id|string(), value=slave.1) }}<br>
Master IP: {{ copy_to_clipboard(id='master-ip-'+cluster.id|string(), value=slave.2) }}<br>
{% endif %}
{% endfor %}
{{lang.words.slaves|title()}}:
{% for slave in slaves %}
{% if not slave.31 %}
<span class="cluster-server">{{slave.1}}</span> ({{slave.2}})
{% endif %}
{% endfor %}
<div>
{{lang.words.virtual|title()}} {{lang.words.servers}}:
{% for virt in virts %}
{% if virt.cluster_id|string() == cluster.id|string() %}
{% for server in servers %}
{% if virt.virt_id|string() == server.0|string() %}
<span class="cluster-server">{{server.1}}</span> ({{server.2}})
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</div>
<div>
VIP:
<span id="cluster-vip">
{%- for vip in vips %}
{% if role <= 2 %}
<a style="cursor: pointer;" onclick="add_vip_ha_cluster('{{vip.cluster_id}}', '{{cluster.name}}', '{{vip.router_id}}', '{{vip.vip}}', 1)" title="{{lang.words.edit|title()}} VIP">{{vip.vip}}</a>
{% else %}
{{vip.vip}}
{%- endif -%}
{%- endfor -%}
</span>
</div>
<div>
{{lang.words.services|title()}}:
{% for c_s in cluster_services %}
{% if c_s.cluster_id|string() == cluster.id|string() %}
{% for service in services %}
{% if c_s.service_id|string() == service.service_id|string() %}
<a href="/app/service/{{service.slug}}">{{service.service}}</a>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</div>
<!-- <div>-->
<!-- {{lang.ha_page.return_master_desc}}: {% if cluster.return_master %} {{lang.words.true|title()}} {% else %} {{lang.words.false|title()}} {% endif %}-->
<!-- </div>-->
</div>
{{ input('router_id-'+ cluster.id|string(), type='hidden') }}
</div>
{% endfor %}

View File

@ -0,0 +1,4 @@
<option value="------" disabled selected>-------</option>
{% for server in free_servers %}
<option value="{{server.ip}}">{{server.hostname}}</option>
{% endfor %}

View File

@ -0,0 +1,25 @@
{% import 'languages/'+lang|default('en')+'.html' as lang %}
{% from 'include/input_macros.html' import input %}
<div class="enabled-check" id="enabled-check">
{%- for s in slaves -%}
{%- if not s.31 -%}
<div class="{{ loop.cycle('odd', 'even') }}" id="remove_check-{{s.0}}" data-name="{{ s.1 }}">
<div class="check-name">
<div style="width: 150px; display: inline-block;">{{ s.1.replace("'", '') }}</div>
<span class="slave_int" id="slave_int_div-{{s.0}}" data-ip="{{s.2}}">
{{ input('slave_int-'+s.0|string(), value=s.32, size='7', title=lang.phrases.int_vrrp + ' ' +lang.ha_page.start_enter) }}
</span>
</div>
<div class="add_user_group check-button" onclick="removeCheckFromStatus({{s.0}}, '{{s.2}}')">-</div>
</div>
{%- endif -%}
{%- endfor -%}
</div>
<div id="all-checks">
{% for s in free_servers %}
<div class="{{ loop.cycle('odd', 'even') }} all-checks" id="add_check-{{ s.server_id }}" data-name="{{ s.hostname }}">
<div class="check-name" title="{{s.desc}}" data-help="{{s.desc}}">{{ s.hostname.replace("'", '') }}</div>
<div class="add_user_group check-button" title="{{lang.words.add|title()}} {{lang.words.service}}" onclick="addCheckToStatus('{{ s.server_id }}', '{{s.ip}}')">+</div>
</div>
{% endfor %}
</div>

View File

@ -20,7 +20,10 @@
data-stop="{{lang.words.stop|title()}}" data-reload="{{lang.words.reload|title()}}" data-user_groups="{{lang.phrases.user_groups}}" data-settings="{{lang.words.settings|title()}}"
data-for="{{lang.words.for}}" data-show="{{lang.words.show|title()}}" data-hide="{{lang.words.hide|title()}}" data-logs="{{lang.words.logs}}" data-name="{{lang.words.name}}"
data-value="{{lang.words.value}}" data-if-title="{{lang.words.if|title()}}" data-then="{{lang.words.then}}" data-autorefresh="{{lang.words.auto|title()}}-{{lang.words.refresh}}"
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-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()}}" />
{% if title == 'Login page' %}
<meta name="viewport" content="width=device-width, user-scalable=1">
{% endif %}

View File

@ -1,180 +0,0 @@
{% extends "base.html" %}
{% block title %}{{ lang.menu_links.ha.title }} {% endblock %}
{% block h2 %}{{ lang.menu_links.ha.title }} {% endblock %}
{% block content %}
{% from 'include/input_macros.html' import input, checkbox %}
<script src="/inc/users.js"></script>
<script src="/inc/ha.js"></script>
<link href="/inc/css/ha.css" rel="stylesheet">
<style>
p {margin: 0;}
</style>
{% if user_status == 0 or user_plan == 'user' %}
{% include 'include/no_sub.html' %}
{% elif not is_needed_tool %}
<div style="text-align: center;">
<h3>You have not installed Ansible</h3>.
<img src="{{ url_for('static', filename='images/no_servers.png')}}" alt="There is no server">
<h4>
Read <a href="https://roxy-wi.org/installation#ansible" title="{{lang.words.installing|title()}} Ansible" target="_blank">here</a> how to install Ansible.
</h4>
</div>
{% else %}
<table class="overview" style="margin-bottom: 20px;">
<caption><h3>{{lang.words.create|title()}} {{lang.words.w_a}} {{lang.words.new}} HA {{lang.words.cluster}}</h3></caption>
<tr class="overviewHead">
<td class="padding10 first-collumn">{{lang.words.current2|title()}} {{lang.words.installation}}</td>
<td class="padding10 first-collumn">{{lang.words.current2|title()}}</td>
<td class="padding10 first-collumn">{{lang.words.current2|title()}} {{lang.words.installation}}</td>
<td>{{lang.words.slave|title()}}</td>
<td><span title="{{lang.phrases.int_vrrp}}">{{lang.words.master|title()}} {{lang.words.interface|title()}}</span></td>
<td><span title="{{lang.phrases.int_vrrp}}">{{lang.words.slave|title()}} {{lang.words.interface|title()}}</span></td>
<td>VRRP IP</td>
<td></td>
</tr>
<tr>
<td class="padding10 first-collumn" id="cur_master_ver"></td>
<td class="padding10 first-collumn">
<select id="master">
<option disabled selected>------</option>
{% for select in selects %}
<option value="{{ select.2 }}">{{ select.1 }}</option>
{% endfor %}
</select>
</td>
<td class="padding10 first-collumn" id="cur_slave_ver"></td>
<td>
<select id="slave">
<option disabled selected>------</option>
{% for select in selects %}
<option value="{{ select.2 }}">{{ select.1 }}</option>
{% endfor %}
</select>
</td>
<td>{{ input('interface', size='7', title=lang.phrases.int_vrrp) }}</td>
<td>{{ input('slave_interface', size='7', title=lang.phrases.int_vrrp) }}</td>
<td>{{ input('vrrp-ip', size='14') }}</td>
</tr>
<tr class="overviewHead">
<td class="padding10 first-collumn"></td>
<td class="help_cursor"><span title="Roxy-WI will add VRRP address as a separated server">{{lang.words.add|title()}} VIRT</span></td>
<td class="checkbox-head help_cursor" style="padding-left: 20px;" title="If checked, the Keepalived master will not release VRRP if the service is down">{{lang.words.stay|title()}} {{lang.words.as}} {{lang.words.master|title()}}</td>
<td class="checkbox-head">SYN-flood {{lang.words.protection}}</td>
<td class="checkbox-head help_cursor" style="padding: 10px 10px 10px 5px;"><span title="Roxy-WI will try to install HAProxy">HAProxy</span></td>
<td class="checkbox-head help_cursor" style="display: none" id="haproxy_docker_td_header"><span title="Roxy-WI will install HAProxy as Docker container">Docker</span></td>
<td class="help_cursor" style="padding-left: 5px;"><span title="Roxy-WI will try to install NGINX">NGINX</span></td>
<td class="checkbox-head help_cursor" style="display: none" id="nginx_docker_td_header"><span title="Roxy-WI will install NGINX as Docker container">Docker</span></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="padding10 first-collumn"></td>
<td class="checkbox help_cursor">{{ checkbox('virt_server', title='Roxy-WI will add VRRP address as a separated server', checked='checked') }}</td>
<td class="syn-flood-protection-field help_cursor">{{ checkbox('return_to_master', title='If checked, the Keepalived master will not release VRRP if the service is down') }}</td>
<td class="syn-flood-protection-field">{{ checkbox('syn_flood') }}</td>
<td class="checkbox help_cursor" style="padding: 10px 10px 10px 15px;">{{ checkbox('hap', title='Roxy-WI will try to install HAProxy') }}</td>
<td class="checkbox help_cursor" style="display: none" id="haproxy_docker_td">{{ checkbox('hap_docker', title='Roxy-WI will install HAProxy as a Docker container') }}</td>
<td class="checkbox help_cursor">{{ checkbox('nginx', title='Roxy-WI will try to install NGINX') }}</td>
<td class="checkbox help_cursor" style="display: none" id="nginx_docker_td">{{ checkbox('nginx_docker', title='Roxy-WI will install NGINX as a Docker container') }}</td>
<td>
<button id="create" title="Create HA cluster">{{lang.words.create|title()}}</button>
</td>
<td></td>
</tr>
</table>
<table class="overview">
<caption><h3>{{lang.words.add|title()}} VRRP {{lang.words.to}} {{lang.words.an}} {{lang.words.existing2}} {{lang.words.cluster}}</h3></caption>
<tr class="overviewHead">
<td class="padding10 first-collumn">{{lang.words.current2|title()}} {{lang.words.installation}}</td>
<td class="padding10 first-collumn">{{lang.words.master|title()}}</td>
<td class="padding10 first-collumn">{{lang.words.current2|title()}} {{lang.words.installation}}</td>
<td>{{lang.words.slave|title()}}</td>
<td><span title="{{lang.phrases.int_vrrp}}">{{lang.words.master|title()}} {{lang.words.interface|title()}}</span></td>
<td><span title="{{lang.phrases.int_vrrp}}">{{lang.words.slave|title()}} {{lang.words.interface|title()}}</span></td>
<td>VRRP IP</td>
</tr>
<tr>
<td class="padding10 first-collumn" id="cur_master_ver-add"></td>
<td class="padding10 first-collumn">
<select id="master-add">
<option disabled selected>------</option>
{% for select in selects %}
<option value="{{ select.2 }}">{{ select.1 }}</option>
{% endfor %}
</select>
</td>
<td class="padding10 first-collumn" id="cur_slave_ver-add"></td>
<td>
<select id="slave-add">
<option disabled selected>------</option>
{% for select in selects %}
<option value="{{ select.2 }}">{{ select.1 }}</option>
{% endfor %}
</select>
</td>
<td>{{ input('interface-add', size='7', title=lang.phrases.int_vrrp) }}</td>
<td>{{ input('slave_interface-add', size='7', title=lang.phrases.int_vrrp) }}</td>
<td>{{ input('vrrp-ip-add', size='14') }}</td>
</tr>
<tr class="overviewHead">
<td></td>
<td></td>
<td></td>
<td></td>
<td class="padding10 first-collumn" style="padding-left: 10px;"><span title="If checked Roxy-WI will restart Keepalived">{{lang.words.restart|title()}}</span></td>
<td class="help_cursor" title="If checked, Keepalived master will reclaim VRRP after recovered">{{lang.words.return|title()}} {{lang.words.to}} {{lang.words.master|title()}} state</td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="padding10 first-collumn checkbox help_cursor">{{ checkbox('kp', title='If checked Roxy-WI will restart Keepalived') }}</td>
<td class="syn-flood-protection-field">{{ checkbox('add_return_to_master', checked='checked') }}</td>
<td>
<button id="add-vrrp" title="Add a HA configuration" style="width: 70px;">{{lang.words.add|title()}}</button>
</td>
</tr>
</table>
<div id="ajax"></div>
<div class="add-note alert addName alert-info" style="width: inherit; margin-right: 15px;">
{{lang.words.read|title()}} <a href="https://roxy-wi.org/howto/ha-cluster" title="How to create high available cluster" target="_blank">{{lang.phrases.howto_ha}}</a>
</div>
<div id="server_creating" style="display: none;" title="{{lang.phrases.creating_ha}}">
<ul style="padding: 20px 20px 0px 20px;font-size: 15px;">
<li id="creating-master" class="server-creating proccessing">{{lang.words.installing|title()}} {{lang.words.master|title()}} Keepalived...</li>
<li id="creating-slave" class="server-creating proccessing">{{lang.words.installing|title()}} {{lang.words.slave|title()}} Keepalived...</li>
<div id="haproxy_installing_div" style="display: none">
<li id="creating-haproxy-master" class="server-creating">{{lang.words.installing|title()}} {{lang.words.master|title()}} Haproxy...</li>
<li id="creating-haproxy-slave" class="server-creating">{{lang.words.installing|title()}} {{lang.words.slave|title()}} Haproxy...</li>
</div>
<div id="nginx_installing_div" style="display: none">
<li id="creating-nginx-master" class="server-creating">{{lang.words.installing|title()}} {{lang.words.master|title()}} NGINX...</li>
<li id="creating-nginx-slave" class="server-creating">{{lang.words.installing|title()}} {{lang.words.slave|title()}} NGINX...</li>
</div>
</ul>
<div id="wait-mess"></div>
<div id="created-mess" class="alert alert-success" style="display:none;"></div>
<div id="creating-error" class="alert alert-danger" style="display:none;"></div>
<div id="creating-warning" class="alert alert-warning" style="display:none;"></div>
<div class="progress-bar-striped">
<div id="creating-progress" style="width: 0%;"></div>
</div>
</div>
<div id="address_creating" style="display: none;" title="{{lang.phrases.adding_vrrp}}">
<ul style="padding: 20px 20px 0px 20px;font-size: 15px;">
<li id="creating-master-add" class="server-creating proccessing">{{lang.words.creating|title()}} {{lang.words.a}} {{lang.words.new}} {{lang.words.address}} {{lang.words.on}} {{lang.words.master|title()}} Keepalived...</li>
<li id="creating-slave-add" class="server-creating proccessing">{{lang.words.creating|title()}} {{lang.words.a}} {{lang.words.new}} {{lang.words.address}} {{lang.words.on}} {{lang.words.slave|title()}} Keepalived...</li>
</ul>
<div id="wait-mess-add"></div>
<div id="created-mess-add" class="alert alert-success" style="display:none;"></div>
<div id="creating-error-add" class="alert alert-danger" style="display:none;"></div>
<div id="creating-warning-add" class="alert alert-warning" style="display:none;"></div>
<div class="progress-bar-striped">
<div id="creating-progress-add" style="width: 0%;"></div>
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,209 @@
{% extends "base.html" %}
{% block title %}{{ lang.ha_page.has }} {% endblock %}
{% block h2 %}{{ lang.ha_page.has }} {% endblock %}
{% block content %}
{% from 'include/input_macros.html' import input, checkbox, copy_to_clipboard %}
<script src="/inc/users.js"></script>
<script src="/inc/ha.js"></script>
<script src="/inc/overview.js"></script>
<link href="/inc/css/servers.css" rel="stylesheet"/>
<link href="/inc/css/smon.css" rel="stylesheet">
<link href="/inc/css/ha.css" rel="stylesheet">
<script>
$( function() {
$( ".sortable" ).sortable({
revert: true,
placeholder: "ui-state-highlight"
});
$( ".sortable" ).disableSelection();
$( ".sortable" ).sortable({
handle: ".portlet-header",
stop: function(event, ui) {
var itemOrder = $('.sortable').sortable("toArray");
for (var i = 0; i < itemOrder.length; i++) {
var pos = i;
var id = itemOrder[i].split('-')[2]
change_pos(pos, id);
}
}
});
});
</script>
{% 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 role <= 2 %}
<div class="add-button add-button-status-page" title="{{lang.phrases.create_ha}}" onclick="createHaClusterStep1();">+ {{lang.ha_page.create_ha}}</div>
{% endif %}
<div class="up-pannel">
{% for cluster in clusters %}
<div id="cluster-{{cluster.id}}" class="div-server-hapwi animated-background"></div>
{% endfor %}
</div>
<div id="create-status-page-step-1" style="display: none;">
<table class="overview" id="create-ha-cluster-step-1-overview"
title="{{lang.words.create|title()}} {{lang.ha_page.ha}} {{lang.words.cluster}}"
data-edit="{{lang.words.edit|title()}} {{lang.ha_page.ha}} {{lang.words.cluster}}">
{% include 'include/tr_validate_tips.html' %}
<tr>
<td class="padding20" style="width: 50%">
{{lang.words.name|title()}}
<span class="need-field">*</span>
</td>
<td>
{{ input('ha-cluster-name', autofocus='autofocus') }}
</td>
</tr>
<tr>
<td class="padding20">
{{lang.words.desc|title()}}
</td>
<td>
{{ input('ha-cluster-desc', autofocus='autofocus') }}
</td>
</tr>
<tr>
<td class="padding20">
{{lang.words.master|title()}}
<span class="need-field">*</span>
</td>
<td>
<select id="ha-cluster-master"></select>
</td>
</tr>
<tr>
<td class="padding10">{{lang.words.current2|title()}} {{lang.words.installation}}</td>
<td id="cur_master_ver"></td>
</tr>
<tr>
<td class="padding20">
{{lang.words.master|title()}} {{lang.words.interface|title()}}
<span class="need-field">*</span>
</td>
<td>{{ input('ha-cluster-master-interface', size='7', title=lang.phrases.int_vrrp, placeholder='eth0') }}</td>
</tr>
<tr>
<td class="padding20">
VIP
<span class="need-field">*</span>
</td>
<td>{{ input('vrrp-ip', size='14') }}</td>
</tr>
<tr>
<td class="padding20">
{{lang.words.all|title()}} {{lang.words.servers}}
</td>
<td>{{lang.words.slaves|title()}} {{lang.words.servers}}</td>
</tr>
</table>
<div class="checks"></div>
</div>
<div id="create-status-page-step-2" style="display: none;">
<table class="overview" id="create-ha-cluster-step-2-overview"
title="{{lang.words.create|title()}} {{lang.ha_page.ha}} {{lang.words.cluster}}"
data-edit="{{lang.words.edit|title()}} {{lang.ha_page.ha}} {{lang.words.cluster}}">
<tr>
<td class="padding20 help_cursor"><span title="{{lang.ha_page.create_virt_server}}">{{lang.words.add|title()}} VIRT {{lang.words.server}}</span></td>
<td class="help_cursor">{{ checkbox('virt_server', title=lang.ha_page.create_virt_server, checked='checked') }}</td>
</tr>
<tr>
<td class="padding20" style="width: 50%" title="{{lang.ha_page.return_master}}">
{{lang.words.stay|title()}} {{lang.words.as}} {{lang.words.master|title()}}
</td>
<td>
{{ checkbox('return_to_master', title=lang.ha_page.return_master) }}
</td>
</tr>
<tr>
<td class="padding20">
SYN-flood {{lang.words.protection}}
</td>
<td>
{{ checkbox('syn_flood') }}
</td>
</tr>
<tr>
<td class="padding20 help_cursor"><span title="{{lang.ha_page.try_install}} HAProxy">HAProxy</span></td>
<td class="help_cursor">{{ checkbox('hap', title=lang.ha_page.try_install+' HAProxy') }}</td>
</tr>
<tr>
<td class="padding20 help_cursor" style="display: none" id="haproxy_docker_td_header">
<span title="{{lang.ha_page.try_install}} HAProxy {{lang.ha_page.as_docker}}">Docker</span>
</td>
<td class="help_cursor" style="display: none" id="haproxy_docker_td">
{{ checkbox('hap_docker', title=lang.ha_page.try_install+' HAProxy '+lang.ha_page.as_docker) }}
</td>
</tr>
<tr>
<td class="padding20 help_cursor"><span title="{{lang.ha_page.try_install}} NGINX">NGINX</span></td>
<td class="help_cursor">{{ checkbox('nginx', title=lang.ha_page.try_install+' NGINX') }}</td>
</tr>
<tr>
<td class="padding20 help_cursor" style="display: none" id="nginx_docker_td_header">
<span title="{{lang.ha_page.try_install}} NGINX {{lang.ha_page.as_docker}}">Docker</span>
</td>
<td class="help_cursor" style="display: none" id="nginx_docker_td">
{{ checkbox('nginx_docker', title=lang.ha_page.try_install+' NGINX '+lang.ha_page.as_docker) }}
</td>
</tr>
</table>
<div class="alert alert-warning">
{{lang.ha_page.save_apply|safe}}
</div>
</div>
<div id="add-vip" style="display: none">
<table class="overview" id="add-vip-table" title="{{lang.ha_page.add_vip}}" data-edit="{{lang.words.edit|title()}} VIP">
<tr>
<td class="padding20">
VIP
<span class="need-field">*</span>
</td>
<td>{{ input('vrrp-ip-add', size='14') }}</td>
</tr>
<tr>
<td class="padding20">{{lang.words.servers|title()}}</td>
<td id="vip_servers"></td>
</tr>
<tr>
<td class="padding20" style="width: 50%" title="{{lang.ha_page.return_master}}">
{{lang.words.stay|title()}} {{lang.words.as}} {{lang.words.master|title()}}
</td>
<td>
{{ checkbox('vrrp-ip-add-return_to_master', title=lang.ha_page.return_master) }}
</td>
</tr>
<tr>
<td class="padding20 help_cursor"><span title="{{lang.ha_page.create_virt_server}}">{{lang.words.add|title()}} VIRT {{lang.words.server}}</span></td>
<td class="help_cursor">{{ checkbox('vrrp-ip-add-virt_server', title=lang.ha_page.create_virt_server, checked='checked') }}</td>
</tr>
</table>
</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>
<div id="server_creating1" style="display: none;" title="{{lang.phrases.creating_ha}}">
<ul style="padding: 20px 20px 0px 20px;font-size: 15px;" id="server_creating_list"></ul>
<div id="created-mess" class="alert alert-success" style="display:none;"></div>
<div id="creating-error" class="alert alert-danger" style="display:none;"></div>
<div id="creating-warning" class="alert alert-warning" style="display:none;"></div>
<div id="wait-mess"></div>
<div class="progress-bar-striped">
<div id="creating-progress" style="width: 5%;"></div>
</div>
</div>
<script>
{% for cluster in clusters %}
getHaCluster('{{cluster.id}}');
{% endfor %}
</script>
{% endif %}
{% endblock %}

View File

@ -10,6 +10,11 @@
<nav id="menu">
<ul class="menu">
<li><a href="{{ url_for('overview.index') }}" title="{{lang.menu_links.overview.title}}" class="overview-link ">{{lang.menu_links.overview.link}}</a></li>
{% if '5' in user_services %}
{% if role <= 3 %}
{% endif %}
<li><a href="{{ url_for('ha.cluster_function', service='cluster') }}" title="{{lang.menu_links.ha.title}}" class="keepalived">HA {{lang.words.cluster}}</a></li>
{% endif %}
{% if '1' in user_services %}
<li class="p_menu">
<a href="{{ url_for('service.services', service='haproxy') }}" title="{{lang.menu_links.hapservers.haproxy.title}}" class="config-show">HAProxy</a>
@ -96,9 +101,6 @@
<li class="p_menu">
<a href="{{ url_for('install.install_monitoring') }}" title="{{lang.menu_links.servers.title}}" class="hap-menu">{{lang.words.installation|title()}}</a>
<ul class="v_menu">
{% if '3' in user_services %}
<li><a href="{{ url_for('install.ha') }}" title="{{lang.menu_links.ha.title}}" class="keepalived head-submenu">HA {{lang.words.cluster}}</a></li>
{% endif %}
<li><a href="{{ url_for('install.install_monitoring') }}#service" title="{{lang.words.servers|title()}}: {{lang.words.proxy|title()}} {{lang.words.services}} {{lang.words.installation}}" class="hap-menu service installproxy head-submenu">{{lang.words.proxy|title()}} {{lang.words.services}}</a> </li>
<li><a href="{{ url_for('install.install_monitoring') }}#monitoring" title="{{lang.words.servers|title()}}: {{lang.words.monitoring|title()}} {{lang.words.services}} {{lang.words.installation}}" class="hap-menu monitoring installmon head-submenu">{{lang.words.monitoring|title()}}</a> </li>
<li><a href="{{ url_for('install.install_monitoring') }}#geolite2" title="{{lang.words.installation|title()}} GeoLite2" class="hap-menu geolite2 installgeo head-submenu">GeoLite2</a> </li>

View File

@ -90,7 +90,7 @@
"title": "Add proxy: Create and upload whitelists or blacklists"
},
"ha": {
"title": "Create and configure a high availability cluster"
"title": "Create and configure a high availability cluster",
},
"monitoring": {
"link": "Monitoring",
@ -479,7 +479,28 @@
}
}
%}
{% set ha_page = {
"ha": "High availability cluster",
"has": "High availability clusters",
"create_virt_server": "Roxy-WI will add VRRP address as a separate server",
"return_master_desc": "Return to master",
"return_master": "If this flag is checked, the Keepalived master will return VRRP when the service is running again",
"try_install": "Roxy-WI will install",
"as_docker": "as Docker container",
"add_vip": "Add VIP to HA cluster",
"create_ha": "Create high availability cluster",
"start_enter": "Start typing the network interface name to add VIP",
"roxywi_timeout": "Roxy-WI answer timeout",
"check_apache_log": "Check <a href='/app/logs/internal' target='_blank' title='Internal logs'>Apache error log</a> for more information.",
"was_installed": "was installed on",
"start_enter": "Start typing into the network interface to add VIP",
"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 words = {
"apply": "apply",
"true": "true",
"false": "false",
"cache": "cache",
"compression": "compression",
"acceleration": "acceleration",
@ -771,6 +792,7 @@
"diff3": "diff",
"master": "master",
"slave": "slave",
"slaves": "slaves",
"interface": "interface",
"as": "as",
"stay": "stay",

View File

@ -479,7 +479,28 @@
}
}
%}
{% set ha_page = {
"ha": "La haute disponibilité cluster",
"has": "La haute disponibilité clusters",
"create_virt_server": "Roxy-WI ajoutera l'adresse VRRP en tant que serveur distinct",
"return_master": "Si cet indicateur est coché, le maître Keepalived renverra VRRP lorsque le service sera à nouveau exécuté",
"return_master_desc": "Retour au maître",
"try_install": "Roxy-WI va installer",
"as_docker": "comme conteneur Docker",
"add_vip": "Ajouter un VIP au cluster HA",
"create_ha": "Créer un cluster haute disponibilité",
"start_enter": "Commencez à taper le nom de l'interface réseau pour ajouter VIP",
"roxywi_timeout": "Délai d'attente de réponse Roxy-WI",
"check_apache_log": "Consultez <a href='/app/logs/internal' target='_blank' title='Journaux internes'>le journal des erreurs Apache</a> pour plus dinformations.",
"was_installed": "a été installé sur",
"start_enter": "Commencez à taper dans l'interface réseau pour ajouter VIP",
"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 words = {
"apply": "appliquer",
"true": "vrai",
"false": "faux",
"cache": "cache",
"compression": "compression",
"acceleration": "accélération",
@ -770,7 +791,8 @@
"diff2": "diff",
"diff3": "diff",
"master": "master",
"slave": "slave",
"slave": "esclave",
"slaves": "des esclaves",
"interface": "interface",
"as": "comme",
"stay": "rester",

View File

@ -479,7 +479,28 @@
}
}
%}
{% set ha_page = {
"ha": "Alta disponibilidade cluster",
"has": "Alta disponibilidade clusters",
"create_virt_server": "Roxy-WI adicionará endereço VRRP como um servidor separado",
"return_master": "Se este sinalizador estiver marcado, o mestre Keepalived retornará VRRP quando o serviço estiver em execução novamente",
"return_master_desc": "Voltar ao mestre",
"try_install": "Roxy-WI irá instalar",
"as_docker": "como contêiner Docker",
"add_vip": "Adicionar VIP ao cluster HA",
"create_ha": "Criar cluster de alta disponibilidade",
"start_enter": "Comece a digitar o nome da interface de rede para adicionar VIP",
"roxywi_timeout": "Tempo limite de resposta do Roxy-WI",
"check_apache_log": "Verifique <a href='/app/logs/internal' target='_blank' title='Registros internos'>o log de erros do Apache</a> para obter mais informações.",
"was_installed": "foi instalado em",
"start_enter": "Comece a digitar na interface de rede para adicionar VIP",
"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 words = {
"apply": "aplicar",
"true": "verídico",
"false": "falso",
"cache": "cache",
"compression": "compressão",
"acceleration": "aceleração",
@ -771,6 +792,7 @@
"diff3": "diff",
"master": "mestre",
"slave": "escravo",
"slaves": "escravos",
"interface": "interface",
"as": "como",
"stay": "ficar",

View File

@ -479,7 +479,28 @@
}
}
%}
{% set ha_page = {
"ha": "кластер высокой доступности",
"has": "Кластера высокой доступности",
"create_virt_server": "Roxy-WI добавит адрес VRRP в качестве отдельного сервера",
"return_master": "Если этот флаг установлен, мастер Keepalived вернет VRRP, когда служба снова запустится",
"return_master_desc": "Возврат мастера",
"try_install": "Roxy-WI установит",
"as_docker": "как Docker контейнер",
"add_vip": "Добавить VIP в HA кластер",
"create_ha": "Создать кластер высокой доступности",
"start_enter": "Начните вводить имя сетевого интерфейса для добавления VIP",
"roxywi_timeout": "Слишком долгий ответ Roxy-WI",
"check_apache_log": "Проверьте <a href='/app/logs/internal' target='_blank' title='Внутренние логи'>журнал ошибок Apache</a> для получения дополнительной информации.",
"was_installed": "был установлен на",
"start_enter": "Начните вводить в сетевой интерфейс для добавления VIP",
"save_apply": "<b>Сохранить</b> - подразумевает сохранение настроек HА кластера для Roxy-WI, без изменения настроек компонентов на участниках кластера. <br /> <b>Применить</b> - пересоздать конфигурацию HА кластера на серверах участниках кластера и установит дополнительные сервисы."
}
%}
{% set words = {
"apply": "применить",
"true": "да",
"false": "нет",
"cache": "кэш",
"compression": "сжатие",
"acceleration": "ускорение",
@ -771,6 +792,7 @@
"diff3": "разницы",
"master": "мастер",
"slave": "подчиненный",
"slaves": "подчиненные",
"interface": "интерфейс",
"as": "как",
"stay": "оставаться",

View File

@ -16,3 +16,4 @@ python3-nmap<=1.5.1
aio-pika>=7.1.0
pika>=1.2.0
websockets>=9.0
ansible-runner==2.3.2

View File

@ -17,3 +17,4 @@ python3-nmap<=1.5.1
aio-pika>=7.1.0
pika>=1.2.0
websockets>=9.0
ansible-runner==2.3.2

View File

@ -18,3 +18,4 @@ python3-nmap<=1.5.1
aio-pika>=7.1.0
pika>=1.2.0
websockets>=9.0
ansible-runner==2.3.2

View File

@ -18,3 +18,4 @@ aio-pika>=7.1.0
pika>=1.2.0
websockets>=9.0
tzlocal==2.0.0
ansible-runner==2.3.2

View File

@ -1,21 +1,30 @@
.padding20{ width: 160px;}
.server-creating {padding-bottom: 10px;}
.proccessing, .processing_error, .proccessing_done {font-weight: bold; color: var(--blue-color);}
.proccessing_done {color: var(--green-color);}
.proccessing, .processing_error {font-weight: bold; color: var(--green-color);}
.proccessing::before {
display: none;
font-family: "Font Awesome 5 Solid";
content: "\f35a";
}
.proccessing .fa-arrow-alt-circle-right {
.proccessing_done::before {
display: none;
font-family: "Font Awesome 5 Solid";
content: "\f058";
}
/*.proccessing .fa-arrow-alt-circle-right {*/
/* padding-right: 10px !important;*/
/* margin-bottom: -1px !important;*/
/*}*/
.proccessing .fa-arrow-alt-circle-right, .processing_error .svg-inline--fa {
padding-right: 10px !important;
margin-bottom: -1px !important;
}
.processing_error .svg-inline--fa {
.proccessing_done .fa-check-circle {
padding-right: 10px !important;
margin-bottom: -1px !important;
}
.processing_error { color: red;}
.processing_error { color: var(--red-color);}
.processing_warning { color: #efba22;}
.processing_error::before, .processing_warning::before {
display: none;
@ -90,3 +99,24 @@
.first-collumn {
width: 15%;
}
.fa-plus, .fa-edit, .fa-trash-alt {
width: 3px;
cursor: pointer;
margin-bottom: 3px;
}
.fa-plus {
margin-right: 3px;
}
.delete {
float: unset;
margin: 0;
}
.checks {
height: 100px;
}
.add-button-status-page {
margin-left: 20px;
}
.div-server-hapwi {
height: 135px;
}

View File

@ -6,6 +6,7 @@
--light-blue-color: #d1ecf1;
--menu-color: #06212a;
--right-menu-blue-rolor: #5D9CEB;
--yellow-color: #ffcc00;
--border-radius: 3px;
--indent: 15px;
}
@ -412,7 +413,7 @@ pre {
padding-left: var(--indent);
font-size: 15px;
}
.serverUp, .serverDown, .serverNone {
.serverUp, .serverDown, .serverNone, .serverWarn {
padding: 3px;
border-radius: 3px;
color: #fff;
@ -421,6 +422,9 @@ pre {
.serverUp {
background-color: var(--green-color);
}
.serverWarn {
background-color: var(--yellow-color);
}
.serverNone {
background-color: #ddd;
}
@ -1380,3 +1384,23 @@ label {
position: relative;
bottom: 3px;
}
@keyframes placeHolderShimmer {
0% {
background-position: -800px 0
}
100% {
background-position: 800px 0
}
}
.animated-background {
animation-duration: 2s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
animation-name: placeHolderShimmer;
animation-timing-function: linear;
background-color: #f6f7f8;
background: linear-gradient(to right, #eeeeee 8%, #bbbbbb 18%, #eeeeee 33%);
background-size: 800px 104px;
position: relative;
}

1345
inc/ha.js

File diff suppressed because it is too large Load Diff

View File

@ -7,49 +7,43 @@ $( function() {
var cancel_word = $('#translate').attr('data-cancel');
$( "#backup_tabs" ).tabs();
$('#install').click(function() {
$("#ajax").html('')
var syn_flood = 0;
var docker = 0;
if ($('#syn_flood').is(':checked')) {
syn_flood = '1';
}
if ($('#haproxy_docker').is(':checked')) {
docker = '1';
}
if ($('#haproxyaddserv').val() == '------' || $('#haproxyaddserv').val() === null) {
var select_server = $('#translate').attr('data-select_server');
toastr.warning(select_server);
return false
}
$("#ajax").html(wait_mess);
$.ajax({
url: "/app/install/haproxy/" + $('#haproxyaddserv').val(),
data: {
syn_flood: syn_flood,
hapver: $('#hapver option:selected').val(),
docker: docker,
token: $('#token').val()
},
type: "POST",
success: function (data) {
data = data.replace(/\s+/g, ' ');
$("#ajax").html('')
if (data.indexOf('error:') != '-1' || data.indexOf('FAILED') != '-1' || data.indexOf('UNREACHABLE') != '-1') {
var p_err = show_pretty_ansible_error(data);
toastr.error(p_err);
} else if (data.indexOf('success') != '-1') {
toastr.remove();
toastr.success(data);
$("#haproxyaddserv").trigger("selectmenuchange");
} else if (data.indexOf('Info') != '-1') {
toastr.remove();
toastr.info(data);
} else {
toastr.remove();
toastr.info(data);
}
}
});
// $("#ajax").html('')
// var syn_flood = 0;
// var docker = 0;
// var jsonData = {'servers': {'0': {}}, 'services': {'haproxy': {}}};
// if ($('#syn_flood').is(':checked')) {
// syn_flood = '1';
// }
// if ($('#haproxy_docker').is(':checked')) {
// docker = '1';
// }
// if ($('#haproxyaddserv').val() == '------' || $('#haproxyaddserv').val() === null) {
// var select_server = $('#translate').attr('data-select_server');
// toastr.warning(select_server);
// return false
// }
// jsonData['syn_flood'] = syn_flood;
// jsonData['servers']['0']['ip'] = $('#haproxyaddserv').val();
// jsonData['servers']['0']['master'] = '0';
// jsonData['servers']['0']['name'] = $('#haproxyaddserv option:selected').text();
// jsonData['servers']['0']['version'] = $('#hapver option:selected').val();
// jsonData['services']['haproxy']['enabled'] = 1;
// jsonData['services']['haproxy']['docker'] = docker;
// $("#ajax").html(wait_mess);
// $.ajax({
// url: "/app/install/haproxy/" + $('#haproxyaddserv').val(),
// data: {
// jsonData: JSON.stringify(jsonData),
// token: $('#token').val()
// },
// type: "POST",
// success: function (data) {
// data = data.replace(/\s+/g, ' ');
// parseAnsibleJsonOutput(data, 'HAProxy');
// $("#haproxyaddserv").trigger("selectmenuchange");
// }
// });
installService('haproxy')
});
$('#nginx_install').click(function() {
installService('nginx');
@ -2477,8 +2471,8 @@ function uploadOvpn() {
toastr.error(data);
} else if (data.indexOf('success') != '-1') {
toastr.clear();
toastr.success(data)
location.reload()
toastr.success(data);
location.reload();
} else {
toastr.error('Something wrong, check and try again');
}
@ -2882,41 +2876,92 @@ function installService(service) {
if ($('#' + service + '_docker').is(':checked')) {
docker = '1';
}
if ($('#' + service + 'addserv').val() == '------') {
if ($('#haproxyaddserv').val() == '------' || $('#' + service + 'addserv').val() === null) {
var select_server = $('#translate').attr('data-select_server');
toastr.warning(select_server);
return false
}
var jsonData = {};
jsonData['servers'] = {'0': {}}
jsonData['services'] = {};
jsonData['services'][service] = {};
jsonData['syn_flood'] = syn_flood;
jsonData['servers']['0']['ip'] = $('#' + service + 'addserv').val();
jsonData['servers']['0']['master'] = '0';
jsonData['servers']['0']['name'] = $('#' + service + 'addserv option:selected').text();
if (service == 'haproxy') {
jsonData['servers']['0']['version'] = $('#hapver option:selected').val();
}
console.log(jsonData)
jsonData['services'][service]['enabled'] = 1;
jsonData['services'][service]['docker'] = docker;
var nice_names = {'haproxy': 'HAProxy', 'nginx': 'NGINX', 'apache': 'Apache'};
$("#ajax").html(wait_mess);
$.ajax({
url: "/app/install/" + service + "/" + $('#' + service + 'addserv').val(),
url: "/app/install/" + service,
500: function () {
showErrorStatus(ice_names[service], $('#' + service + 'addserv option:selected').text());
},
504: function () {
showErrorStatus(ice_names[service], $('#' + service + 'addserv option:selected').text());
},
data: {
syn_flood: syn_flood,
docker: docker,
jsonData: JSON.stringify(jsonData),
token: $('#token').val()
},
type: "POST",
success: function (data) {
data = data.replace(/\s+/g, ' ');
$("#ajax").html('')
if (data.indexOf('error:') != '-1' || data.indexOf('FAILED') != '-1' || data.indexOf('UNREACHABLE') != '-1') {
toastr.clear();
var p_err = show_pretty_ansible_error(data);
toastr.error(p_err);
} else if (data.indexOf('success') != '-1') {
toastr.clear();
toastr.success(data);
$('#' + service + 'addserv').trigger("selectmenuchange");
} else if (data.indexOf('Info') != '-1') {
toastr.clear();
toastr.info(data);
} else {
toastr.clear();
toastr.info(data);
}
// data = data.replace(/\s+/g, ' ');
parseAnsibleJsonOutput(data, nice_names[service]);
$("#" + service + "yaddserv").trigger("selectmenuchange");
}
});
}
// function installService(service) {
// $("#ajax").html('')
// var syn_flood = 0;
// var docker = 0;
// if ($('#' + service + '_syn_flood').is(':checked')) {
// syn_flood = '1';
// }
// if ($('#' + service + '_docker').is(':checked')) {
// docker = '1';
// }
// if ($('#' + service + 'addserv').val() == '------') {
// var select_server = $('#translate').attr('data-select_server');
// toastr.warning(select_server);
// return false
// }
// $("#ajax").html(wait_mess);
// $.ajax({
// url: "/app/install/" + service + "/" + $('#' + service + 'addserv').val(),
// data: {
// syn_flood: syn_flood,
// docker: docker,
// token: $('#token').val()
// },
// type: "POST",
// success: function (data) {
// data = data.replace(/\s+/g, ' ');
// $("#ajax").html('')
// if (data.indexOf('error:') != '-1' || data.indexOf('FAILED') != '-1' || data.indexOf('UNREACHABLE') != '-1') {
// toastr.clear();
// var p_err = show_pretty_ansible_error(data);
// toastr.error(p_err);
// } else if (data.indexOf('success') != '-1') {
// toastr.clear();
// toastr.success(data);
// $('#' + service + 'addserv').trigger("selectmenuchange");
// } else if (data.indexOf('Info') != '-1') {
// toastr.clear();
// toastr.info(data);
// } else {
// toastr.clear();
// toastr.info(data);
// }
// }
// });
// }
function showServiceVersion(service) {
$.ajax({
url: "/app/install/" + service + "/version/" + $('#' + service + 'addserv option:selected').val(),
@ -3178,3 +3223,21 @@ function changeServerServices(server_id) {
}
});
}
function showErrorStatus(service_name, server) {
var something_wrong = $('#translate').attr('data-something_wrong');
toastr.error(something_wrong + ' ' + service_name + ' ' + server);
}
function parseAnsibleJsonOutput(output, service_name) {
output = JSON.parse(JSON.stringify(output));
var check_apache_log = $('#translate').attr('data-check_apache_log');
var was_installed = $('#translate').attr('data-was_installed');
for (var k in output['ok']) {
toastr.success(service_name + ' ' + was_installed +' ' + k);
}
for (var k in output['failures']) {
showErrorStatus(service_name, k);
}
for (var k in output['dark']) {
showErrorStatus(service_name, k);
}
}

View File

@ -21,3 +21,4 @@ Flask==2.2.5
Flask-Login==0.6.2
Flask-APScheduler==1.13.0
Flask-Caching==2.1.0
ansible-runner==2.3.2