2024-08-02 09:50:02 +00:00
|
|
|
from flask.views import MethodView
|
|
|
|
from flask_pydantic import validate
|
|
|
|
from flask_jwt_extended import jwt_required
|
|
|
|
from flask import render_template, request, jsonify, g
|
|
|
|
from playhouse.shortcuts import model_to_dict
|
|
|
|
|
|
|
|
import app.modules.db.ha_cluster as ha_sql
|
2024-11-03 07:12:08 +00:00
|
|
|
import app.modules.db.service as service_sql
|
2024-08-02 09:50:02 +00:00
|
|
|
import app.modules.roxywi.common as roxywi_common
|
|
|
|
import app.modules.common.common as common
|
|
|
|
import app.modules.service.ha_cluster as ha_cluster
|
2024-09-10 08:14:34 +00:00
|
|
|
import app.modules.service.installation as service_mod
|
2024-08-02 09:50:02 +00:00
|
|
|
from app.middleware import get_user_params, page_for_admin, check_group, check_services
|
2024-08-27 09:40:15 +00:00
|
|
|
from app.modules.roxywi.class_models import BaseResponse, IdResponse, HAClusterRequest, HAClusterVIP
|
2024-08-02 09:50:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
class HAView(MethodView):
|
|
|
|
methods = ["GET", "POST", "PUT", "DELETE"]
|
|
|
|
decorators = [jwt_required(), get_user_params(), check_services, page_for_admin(level=3), check_group()]
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.group_id = g.user_params['group_id']
|
|
|
|
|
|
|
|
def get(self, service: str, cluster_id: int):
|
|
|
|
"""
|
|
|
|
This endpoint retrieves information about the specified HA Cluster.
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- HA Cluster
|
|
|
|
parameters:
|
|
|
|
- in: 'path'
|
|
|
|
name: 'service'
|
|
|
|
description: 'Can be only "cluster"'
|
|
|
|
required: true
|
2024-08-02 15:48:11 +00:00
|
|
|
type: 'string'
|
2024-08-02 09:50:02 +00:00
|
|
|
- in: 'path'
|
|
|
|
name: 'cluster_id'
|
|
|
|
description: 'ID of the HA cluster to retrieve information from.'
|
|
|
|
required: true
|
|
|
|
type: 'integer'
|
|
|
|
responses:
|
|
|
|
200:
|
2024-11-03 07:12:08 +00:00
|
|
|
description: HA details retrieved successfully
|
2024-08-02 09:50:02 +00:00
|
|
|
schema:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: object
|
2024-08-02 09:50:02 +00:00
|
|
|
properties:
|
|
|
|
description:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: string
|
|
|
|
description: Description of the HA
|
2024-08-02 09:50:02 +00:00
|
|
|
eth:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: string
|
|
|
|
description: Ethernet interface
|
2024-08-02 09:50:02 +00:00
|
|
|
group_id:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: Group ID
|
2024-08-02 09:50:02 +00:00
|
|
|
id:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: ID of the HA
|
2024-08-02 09:50:02 +00:00
|
|
|
name:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: string
|
|
|
|
description: Name of the listener
|
2024-08-02 09:50:02 +00:00
|
|
|
pos:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: Position
|
|
|
|
return_master:
|
|
|
|
type: integer
|
|
|
|
description: Return master flag
|
|
|
|
servers:
|
|
|
|
type: array
|
|
|
|
items:
|
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
eth:
|
|
|
|
type: string
|
|
|
|
description: Ethernet interface
|
|
|
|
id:
|
|
|
|
type: integer
|
|
|
|
description: Server ID
|
|
|
|
master:
|
|
|
|
type: integer
|
|
|
|
description: Master flag
|
|
|
|
services:
|
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
apache:
|
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
docker:
|
|
|
|
type: integer
|
|
|
|
description: Docker flag for Apache
|
|
|
|
enabled:
|
|
|
|
type: integer
|
|
|
|
description: Enabled flag for Apache
|
|
|
|
haproxy:
|
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
docker:
|
|
|
|
type: integer
|
|
|
|
description: Docker flag for HAProxy
|
|
|
|
enabled:
|
|
|
|
type: integer
|
|
|
|
description: Enabled flag for HAProxy
|
|
|
|
nginx:
|
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
docker:
|
|
|
|
type: integer
|
|
|
|
description: Docker flag for NGINX
|
|
|
|
enabled:
|
|
|
|
type: integer
|
|
|
|
description: Enabled flag for NGINX
|
2024-08-02 09:50:02 +00:00
|
|
|
syn_flood:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: SYN Flood protection flag
|
2024-08-02 09:50:02 +00:00
|
|
|
use_src:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: Use source flag
|
2024-08-02 09:50:02 +00:00
|
|
|
vip:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: string
|
|
|
|
description: Virtual IP address
|
2024-08-02 09:50:02 +00:00
|
|
|
virt_server:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: Virtual server flag
|
2024-08-02 09:50:02 +00:00
|
|
|
"""
|
|
|
|
if not cluster_id:
|
|
|
|
if request.method == 'GET':
|
|
|
|
kwargs = {
|
|
|
|
'clusters': ha_sql.select_clusters(self.group_id),
|
|
|
|
'is_needed_tool': common.is_tool('ansible'),
|
|
|
|
'user_subscription': roxywi_common.return_user_subscription(),
|
|
|
|
'lang': g.user_params['lang'],
|
|
|
|
}
|
|
|
|
|
|
|
|
return render_template('ha_cluster.html', **kwargs)
|
|
|
|
else:
|
|
|
|
settings = {}
|
|
|
|
clusters = ha_sql.select_cluster(cluster_id)
|
|
|
|
router_id = ha_sql.get_router_id(cluster_id, default_router=1)
|
|
|
|
slaves = ha_sql.select_cluster_slaves(cluster_id, router_id)
|
|
|
|
cluster_services = ha_sql.select_cluster_services(cluster_id)
|
|
|
|
vip = ha_sql.select_cluster_vip(cluster_id, router_id)
|
|
|
|
is_virt = ha_sql.check_ha_virt(vip.id)
|
2024-11-03 07:12:08 +00:00
|
|
|
if vip.use_src:
|
|
|
|
use_src = 1
|
|
|
|
else:
|
|
|
|
use_src = 0
|
2024-08-02 09:50:02 +00:00
|
|
|
|
|
|
|
for cluster in clusters:
|
|
|
|
settings = model_to_dict(cluster)
|
|
|
|
settings.setdefault('vip', vip.vip)
|
|
|
|
settings.setdefault('virt_server', is_virt)
|
2024-11-03 07:12:08 +00:00
|
|
|
settings.setdefault('use_src', use_src)
|
2024-08-04 14:44:11 +00:00
|
|
|
settings.setdefault('return_master', vip.return_master)
|
2024-11-03 07:12:08 +00:00
|
|
|
settings['servers'] = []
|
|
|
|
settings['services'] = {
|
|
|
|
'haproxy': {
|
|
|
|
'enabled': 0,
|
|
|
|
'docker': 0
|
|
|
|
},
|
|
|
|
'nginx': {
|
|
|
|
'enabled': 0,
|
|
|
|
'docker': 0
|
|
|
|
},
|
|
|
|
'apache': {
|
|
|
|
'enabled': 0,
|
|
|
|
'docker': 0
|
|
|
|
}
|
|
|
|
}
|
2024-08-02 09:50:02 +00:00
|
|
|
|
|
|
|
for slave in slaves:
|
2024-11-03 07:12:08 +00:00
|
|
|
server_id = slave[0]
|
2024-08-02 09:50:02 +00:00
|
|
|
if slave[31]:
|
|
|
|
settings.setdefault('eth', slave[32])
|
2024-11-03 07:12:08 +00:00
|
|
|
server_settings = {'id': server_id, 'eth': slave[32], 'master': slave[31]}
|
|
|
|
settings['servers'].append(server_settings)
|
2024-08-02 09:50:02 +00:00
|
|
|
|
|
|
|
for c_s in cluster_services:
|
2024-11-03 07:12:08 +00:00
|
|
|
is_dockerized = int(service_sql.select_service_setting(server_id, c_s.service_id, 'dockerized'))
|
2024-08-02 09:50:02 +00:00
|
|
|
if int(c_s.service_id) == 1:
|
2024-11-03 07:12:08 +00:00
|
|
|
settings['services']['haproxy'] = {'enabled': 1, 'docker': is_dockerized}
|
|
|
|
if int(c_s.service_id) == 2:
|
|
|
|
settings['services']['nginx'] = {'enabled': 1, 'docker': is_dockerized}
|
|
|
|
if int(c_s.service_id) == 4:
|
|
|
|
settings['services']['apache'] = {'enabled': 1, 'docker': is_dockerized}
|
2024-08-02 09:50:02 +00:00
|
|
|
|
|
|
|
return jsonify(settings)
|
|
|
|
|
|
|
|
@validate(body=HAClusterRequest)
|
|
|
|
def post(self, service: str, body: HAClusterRequest):
|
|
|
|
"""
|
|
|
|
This endpoint allows to create a new HA cluster.
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- HA Cluster
|
|
|
|
parameters:
|
|
|
|
- in: 'path'
|
|
|
|
name: 'service'
|
|
|
|
description: 'Can be only "cluster"'
|
|
|
|
required: true
|
2024-08-02 15:48:11 +00:00
|
|
|
type: 'string'
|
2024-08-02 09:50:02 +00:00
|
|
|
- in: body
|
|
|
|
name: body
|
|
|
|
required: true
|
|
|
|
schema:
|
2024-08-27 09:40:15 +00:00
|
|
|
required:
|
|
|
|
- name
|
|
|
|
- services
|
2024-08-02 09:50:02 +00:00
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
servers:
|
|
|
|
type: 'array'
|
|
|
|
description: Must be at least 2 servers. One of them must be master = 1
|
|
|
|
items:
|
|
|
|
type: 'object'
|
|
|
|
properties:
|
2024-09-01 17:51:12 +00:00
|
|
|
eth:
|
2024-08-02 09:50:02 +00:00
|
|
|
type: 'string'
|
2024-09-01 17:51:12 +00:00
|
|
|
description: Ethernet interface to bind VIP address
|
2024-08-02 09:50:02 +00:00
|
|
|
master:
|
|
|
|
type: 'integer'
|
2024-08-27 09:40:15 +00:00
|
|
|
id:
|
|
|
|
type: 'integer'
|
2024-08-02 09:50:02 +00:00
|
|
|
name:
|
|
|
|
type: string
|
|
|
|
description:
|
|
|
|
type: string
|
|
|
|
vip:
|
|
|
|
type: string
|
|
|
|
virt_server:
|
|
|
|
type: integer
|
|
|
|
return_to_master:
|
|
|
|
type: string
|
|
|
|
syn_flood:
|
|
|
|
type: integer
|
|
|
|
use_src:
|
|
|
|
type: integer
|
|
|
|
services:
|
|
|
|
type: object
|
|
|
|
additionalProperties:
|
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
enabled:
|
2024-09-01 17:51:12 +00:00
|
|
|
type: integer
|
2024-08-02 09:50:02 +00:00
|
|
|
docker:
|
|
|
|
type: integer
|
|
|
|
router_id:
|
2024-09-01 17:51:12 +00:00
|
|
|
type: integer
|
2024-08-02 09:50:02 +00:00
|
|
|
responses:
|
|
|
|
201:
|
|
|
|
description: HA cluster created successfully
|
|
|
|
400:
|
|
|
|
description: Invalid request data
|
|
|
|
default:
|
|
|
|
description: Unexpected error
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
cluster_id = ha_cluster.create_cluster(body, self.group_id)
|
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot create cluster')
|
|
|
|
|
2024-09-10 08:14:34 +00:00
|
|
|
if body.reconfigure:
|
|
|
|
try:
|
|
|
|
self._install_service(body, cluster_id)
|
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot reconfigure cluster')
|
|
|
|
|
|
|
|
return IdResponse(id=cluster_id).model_dump(mode='json'), 201
|
|
|
|
|
2024-08-02 09:50:02 +00:00
|
|
|
@validate(body=HAClusterRequest)
|
|
|
|
def put(self, service: str, cluster_id: int, body: HAClusterRequest):
|
|
|
|
"""
|
|
|
|
This endpoint allows to update a HA cluster.
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- HA Cluster
|
|
|
|
parameters:
|
|
|
|
- in: 'path'
|
|
|
|
name: 'service'
|
|
|
|
description: 'Can be only "cluster"'
|
|
|
|
required: true
|
|
|
|
type: 'string'
|
|
|
|
- in: 'path'
|
|
|
|
name: 'cluster_id'
|
|
|
|
description: 'ID of the HA cluster to update'
|
|
|
|
required: true
|
|
|
|
type: 'integer'
|
|
|
|
- in: body
|
|
|
|
name: body
|
|
|
|
required: true
|
|
|
|
schema:
|
2024-08-27 09:40:15 +00:00
|
|
|
required:
|
|
|
|
- name
|
|
|
|
- services
|
2024-08-02 09:50:02 +00:00
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
servers:
|
|
|
|
type: 'array'
|
|
|
|
description: Must be at least 2 servers. One of them must be master = 1
|
|
|
|
items:
|
|
|
|
type: 'object'
|
|
|
|
properties:
|
2024-09-01 17:51:12 +00:00
|
|
|
eth:
|
2024-08-02 09:50:02 +00:00
|
|
|
type: 'string'
|
2024-09-01 17:51:12 +00:00
|
|
|
description: Ethernet interface to bind VIP address
|
2024-08-02 09:50:02 +00:00
|
|
|
master:
|
|
|
|
type: 'integer'
|
2024-08-27 09:40:15 +00:00
|
|
|
id:
|
|
|
|
type: 'integer'
|
2024-08-02 09:50:02 +00:00
|
|
|
name:
|
|
|
|
type: string
|
|
|
|
description:
|
|
|
|
type: string
|
|
|
|
vip:
|
|
|
|
type: string
|
|
|
|
virt_server:
|
|
|
|
type: integer
|
|
|
|
return_to_master:
|
|
|
|
type: string
|
|
|
|
syn_flood:
|
|
|
|
type: integer
|
|
|
|
use_src:
|
|
|
|
type: integer
|
|
|
|
services:
|
|
|
|
type: object
|
|
|
|
additionalProperties:
|
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
enabled:
|
2024-09-01 17:51:12 +00:00
|
|
|
type: integer
|
2024-08-02 09:50:02 +00:00
|
|
|
docker:
|
|
|
|
type: integer
|
|
|
|
responses:
|
|
|
|
201:
|
|
|
|
description: HA cluster updated successfully
|
|
|
|
400:
|
|
|
|
description: Invalid request data
|
|
|
|
404:
|
|
|
|
description: HA cluster not found
|
|
|
|
default:
|
|
|
|
description: Unexpected error
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
ha_cluster.update_cluster(body, cluster_id, self.group_id)
|
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot update cluster')
|
|
|
|
|
2024-09-10 08:14:34 +00:00
|
|
|
if body.reconfigure:
|
|
|
|
try:
|
|
|
|
self._install_service(body, cluster_id)
|
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot reconfigure cluster')
|
|
|
|
|
|
|
|
return BaseResponse().model_dump(mode='json'), 201
|
|
|
|
|
2024-08-02 09:50:02 +00:00
|
|
|
@staticmethod
|
|
|
|
def delete(service: str, cluster_id: int):
|
|
|
|
"""
|
|
|
|
Delete a HA cluster
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- HA Cluster
|
|
|
|
parameters:
|
|
|
|
- in: 'path'
|
|
|
|
name: 'service'
|
|
|
|
description: 'Can be only "cluster"'
|
|
|
|
required: true
|
|
|
|
type: 'string'
|
|
|
|
- in: 'path'
|
|
|
|
name: 'cluster_id'
|
|
|
|
description: 'ID of the HA cluster to delete'
|
|
|
|
required: true
|
|
|
|
type: 'integer'
|
|
|
|
responses:
|
|
|
|
204:
|
|
|
|
description: HA cluster deletion successful
|
|
|
|
400:
|
|
|
|
description: Invalid request data
|
|
|
|
404:
|
|
|
|
description: HA cluster not found
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
ha_cluster.delete_cluster(cluster_id)
|
|
|
|
return BaseResponse().model_dump(mode='json'), 204
|
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot delete cluster')
|
|
|
|
|
2024-09-10 08:14:34 +00:00
|
|
|
@staticmethod
|
|
|
|
def _install_service(body: HAClusterRequest, cluster_id: int):
|
|
|
|
try:
|
|
|
|
output = service_mod.install_service('keepalived', body, cluster_id)
|
|
|
|
except Exception as e:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
if len(output['failures']) > 0 or len(output['dark']) > 0:
|
|
|
|
raise Exception('Cannot install Keepalived. Check Apache error log')
|
|
|
|
|
|
|
|
cluster_services = ha_cluster.get_services_dict(body)
|
|
|
|
for service, value in cluster_services.items():
|
|
|
|
if not value['enabled']:
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
output = service_mod.install_service(service, body)
|
|
|
|
except Exception as e:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
if len(output['failures']) > 0 or len(output['dark']) > 0:
|
|
|
|
raise Exception(f'Cannot install {service.title()}. Check Apache error log')
|
|
|
|
|
2024-08-02 09:50:02 +00:00
|
|
|
|
|
|
|
class HAVIPView(MethodView):
|
|
|
|
methods = ["GET", "POST", "PUT", "DELETE"]
|
|
|
|
decorators = [jwt_required(), get_user_params(), check_services, page_for_admin(level=3), check_group()]
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.group_id = g.user_params['group_id']
|
|
|
|
|
|
|
|
@staticmethod
|
2024-08-27 09:40:15 +00:00
|
|
|
def get(service: str, cluster_id: int, vip_id: int):
|
2024-08-02 09:50:02 +00:00
|
|
|
"""
|
|
|
|
This endpoint retrieves information about the specified VIP.
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- HA Cluster VIP
|
|
|
|
parameters:
|
|
|
|
- in: 'path'
|
|
|
|
name: 'service'
|
|
|
|
description: 'Can be only "cluster"'
|
|
|
|
required: true
|
|
|
|
type: 'string'
|
2024-11-03 07:12:08 +00:00
|
|
|
- name: cluster_id
|
|
|
|
in: path
|
|
|
|
type: integer
|
2024-08-02 09:50:02 +00:00
|
|
|
required: true
|
2024-11-03 07:12:08 +00:00
|
|
|
description: ID of the cluster
|
|
|
|
- name: vip_id
|
|
|
|
in: path
|
|
|
|
type: integer
|
2024-08-02 09:50:02 +00:00
|
|
|
required: true
|
2024-11-03 07:12:08 +00:00
|
|
|
description: ID of the VIP
|
2024-08-02 09:50:02 +00:00
|
|
|
responses:
|
|
|
|
200:
|
2024-11-03 07:12:08 +00:00
|
|
|
description: HAVIP details retrieved successfully
|
2024-08-02 09:50:02 +00:00
|
|
|
schema:
|
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
cluster_id:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: ID of the cluster
|
|
|
|
eth:
|
|
|
|
type: string
|
|
|
|
description: Ethernet interface
|
2024-08-02 09:50:02 +00:00
|
|
|
id:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: ID of the HAVIP
|
2024-08-02 09:50:02 +00:00
|
|
|
return_master:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: Return master flag
|
2024-08-02 09:50:02 +00:00
|
|
|
router_id:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: ID of the router
|
|
|
|
servers:
|
|
|
|
type: array
|
|
|
|
items:
|
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
eth:
|
|
|
|
type: string
|
|
|
|
description: Ethernet interface
|
|
|
|
id:
|
|
|
|
type: integer
|
|
|
|
description: Server ID
|
|
|
|
master:
|
|
|
|
type: integer
|
|
|
|
description: Master flag
|
2024-08-02 09:50:02 +00:00
|
|
|
use_src:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: integer
|
|
|
|
description: Use source flag
|
2024-08-02 09:50:02 +00:00
|
|
|
vip:
|
2024-11-03 07:12:08 +00:00
|
|
|
type: string
|
|
|
|
description: Virtual IP address
|
|
|
|
virt_server:
|
|
|
|
type: integer
|
|
|
|
description: Virtual server flag
|
2024-08-02 09:50:02 +00:00
|
|
|
"""
|
|
|
|
try:
|
2024-08-27 09:40:15 +00:00
|
|
|
vip = ha_sql.select_cluster_vip_by_vip_id(cluster_id, vip_id)
|
2024-11-03 07:12:08 +00:00
|
|
|
slaves = ha_sql.select_cluster_slaves(cluster_id, vip.router_id)
|
2024-08-27 09:40:15 +00:00
|
|
|
settings = model_to_dict(vip, recurse=False)
|
2024-08-02 09:50:02 +00:00
|
|
|
is_virt = ha_sql.check_ha_virt(vip.id)
|
|
|
|
settings.setdefault('virt_server', is_virt)
|
2024-11-03 07:12:08 +00:00
|
|
|
settings['servers'] = []
|
|
|
|
for slave in slaves:
|
|
|
|
server_id = slave[0]
|
|
|
|
if slave[31]:
|
|
|
|
settings.setdefault('eth', slave[32])
|
|
|
|
server_settings = {'id': server_id, 'eth': slave[32], 'master': slave[31]}
|
|
|
|
settings['servers'].append(server_settings)
|
2024-08-02 09:50:02 +00:00
|
|
|
return jsonify(settings)
|
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot get VIP')
|
|
|
|
|
|
|
|
@validate(body=HAClusterVIP)
|
|
|
|
def post(self, service: str, cluster_id: int, body: HAClusterVIP):
|
|
|
|
"""
|
|
|
|
This endpoint allows to create a VIP for HA cluster.
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- HA Cluster VIP
|
|
|
|
parameters:
|
|
|
|
- in: 'path'
|
|
|
|
name: 'service'
|
|
|
|
description: 'Can be only "cluster"'
|
|
|
|
required: true
|
|
|
|
type: 'string'
|
|
|
|
- in: 'path'
|
|
|
|
name: 'cluster_id'
|
|
|
|
description: 'ID of the HA cluster to update'
|
|
|
|
required: true
|
|
|
|
type: 'integer'
|
|
|
|
- in: body
|
|
|
|
name: body
|
|
|
|
required: true
|
|
|
|
schema:
|
2024-08-27 09:40:15 +00:00
|
|
|
required:
|
|
|
|
- servers
|
|
|
|
- vip
|
2024-08-02 09:50:02 +00:00
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
servers:
|
2024-08-27 09:40:15 +00:00
|
|
|
type: 'array'
|
|
|
|
description: Must be at least 2 servers. One of them must be master = 1
|
|
|
|
items:
|
|
|
|
type: 'object'
|
2024-08-02 09:50:02 +00:00
|
|
|
properties:
|
2024-08-27 09:40:15 +00:00
|
|
|
id:
|
|
|
|
type: 'integer'
|
2024-08-02 09:50:02 +00:00
|
|
|
master:
|
2024-08-27 09:40:15 +00:00
|
|
|
type: 'integer'
|
2024-08-02 09:50:02 +00:00
|
|
|
vip:
|
|
|
|
type: string
|
|
|
|
virt_server:
|
|
|
|
type: integer
|
|
|
|
return_master:
|
|
|
|
type: integer
|
|
|
|
use_src:
|
|
|
|
type: integer
|
|
|
|
responses:
|
|
|
|
201:
|
|
|
|
description: VIP created successfully
|
|
|
|
400:
|
|
|
|
description: Invalid request data
|
|
|
|
404:
|
|
|
|
description: VIP not found
|
|
|
|
default:
|
|
|
|
description: Unexpected error
|
|
|
|
"""
|
|
|
|
try:
|
2024-08-27 09:40:15 +00:00
|
|
|
vip_id = ha_cluster.insert_vip(cluster_id, body, self.group_id)
|
|
|
|
return IdResponse(id=vip_id).model_dump(mode='json'), 201
|
2024-08-02 09:50:02 +00:00
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot create VIP')
|
|
|
|
|
|
|
|
@validate(body=HAClusterVIP)
|
2024-08-27 09:40:15 +00:00
|
|
|
def put(self, service: str, cluster_id: int, vip_id: int, body: HAClusterVIP):
|
2024-08-02 09:50:02 +00:00
|
|
|
"""
|
|
|
|
This endpoint allows to update a VIP for HA cluster.
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- HA Cluster VIP
|
|
|
|
parameters:
|
|
|
|
- in: 'path'
|
|
|
|
name: 'service'
|
|
|
|
description: 'Can be only "cluster"'
|
|
|
|
required: true
|
|
|
|
type: 'string'
|
|
|
|
- in: 'path'
|
|
|
|
name: 'cluster_id'
|
|
|
|
description: 'ID of the HA cluster to update'
|
|
|
|
required: true
|
|
|
|
type: 'integer'
|
2024-08-27 09:40:15 +00:00
|
|
|
- in: 'path'
|
|
|
|
name: 'vip_id'
|
|
|
|
description: 'ID of the VIP to update'
|
|
|
|
required: true
|
|
|
|
type: 'integer'
|
2024-08-02 09:50:02 +00:00
|
|
|
- in: body
|
|
|
|
name: body
|
|
|
|
required: true
|
|
|
|
schema:
|
2024-08-27 09:40:15 +00:00
|
|
|
required:
|
|
|
|
- servers
|
|
|
|
- vip
|
2024-08-02 09:50:02 +00:00
|
|
|
type: object
|
|
|
|
properties:
|
|
|
|
servers:
|
2024-08-27 09:40:15 +00:00
|
|
|
type: 'array'
|
|
|
|
description: Must be at least 2 servers. One of them must be master = 1
|
|
|
|
items:
|
|
|
|
type: 'object'
|
2024-08-02 09:50:02 +00:00
|
|
|
properties:
|
2024-08-27 09:40:15 +00:00
|
|
|
id:
|
|
|
|
type: 'integer'
|
2024-08-02 09:50:02 +00:00
|
|
|
master:
|
2024-08-27 09:40:15 +00:00
|
|
|
type: 'integer'
|
2024-08-02 09:50:02 +00:00
|
|
|
vip:
|
|
|
|
type: string
|
|
|
|
virt_server:
|
|
|
|
type: integer
|
|
|
|
return_master:
|
|
|
|
type: string
|
|
|
|
use_src:
|
|
|
|
type: integer
|
|
|
|
responses:
|
|
|
|
201:
|
|
|
|
description: VIP updated successfully
|
|
|
|
400:
|
|
|
|
description: Invalid request data
|
|
|
|
404:
|
|
|
|
description: VIP not found
|
|
|
|
default:
|
|
|
|
description: Unexpected error
|
|
|
|
"""
|
2024-08-28 08:30:05 +00:00
|
|
|
try:
|
|
|
|
vip = ha_sql.select_cluster_vip_by_vip_id(cluster_id, vip_id)
|
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot find VIP')
|
2024-08-02 09:50:02 +00:00
|
|
|
try:
|
2024-08-27 09:40:15 +00:00
|
|
|
ha_cluster.update_vip(cluster_id, vip.router_id, body, self.group_id)
|
2024-08-02 09:50:02 +00:00
|
|
|
return BaseResponse().model_dump(mode='json'), 201
|
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot update VIP')
|
|
|
|
|
|
|
|
@staticmethod
|
2024-08-27 09:40:15 +00:00
|
|
|
def delete(service: str, cluster_id: int, vip_id: int):
|
2024-08-02 09:50:02 +00:00
|
|
|
"""
|
|
|
|
Delete a VIP
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- HA Cluster VIP
|
|
|
|
parameters:
|
|
|
|
- in: 'path'
|
|
|
|
name: 'service'
|
|
|
|
description: 'Can be only "cluster"'
|
|
|
|
required: true
|
|
|
|
type: 'string'
|
|
|
|
- in: 'path'
|
2024-08-27 09:40:15 +00:00
|
|
|
name: 'cluster_id'
|
|
|
|
description: 'ID of the HA cluster to delete VIP from'
|
|
|
|
required: true
|
|
|
|
type: 'integer'
|
|
|
|
- in: 'path'
|
|
|
|
name: 'vip_id'
|
2024-08-02 09:50:02 +00:00
|
|
|
description: 'ID of the VIP to delete'
|
|
|
|
required: true
|
|
|
|
type: 'integer'
|
|
|
|
responses:
|
|
|
|
204:
|
|
|
|
description: VIP deletion successful
|
|
|
|
400:
|
|
|
|
description: Invalid request data
|
|
|
|
404:
|
|
|
|
description: VIP not found
|
|
|
|
"""
|
2024-08-27 09:40:15 +00:00
|
|
|
vip = ha_sql.select_cluster_vip_by_vip_id(cluster_id, vip_id)
|
|
|
|
router = ha_sql.get_router(vip.router_id)
|
2024-08-02 09:50:02 +00:00
|
|
|
if router.default == 1:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(Exception(''), 'You cannot delete default VIP')
|
|
|
|
try:
|
2024-11-03 12:45:06 +00:00
|
|
|
if ha_sql.check_ha_virt(vip_id):
|
|
|
|
ha_sql.delete_ha_virt(vip_id)
|
|
|
|
roxywi_common.logging(cluster_id, f'Cluster virtual server for VIP: {vip.vip} has been deleted', keep_history=1, roxywi=1, service='HA cluster')
|
2024-08-27 09:40:15 +00:00
|
|
|
ha_sql.delete_ha_router(vip.router_id)
|
2024-08-02 09:50:02 +00:00
|
|
|
return BaseResponse().model_dump(mode='json'), 204
|
|
|
|
except Exception as e:
|
|
|
|
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot delete VIP')
|
|
|
|
|
|
|
|
|
|
|
|
class HAVIPsView(MethodView):
|
|
|
|
methods = ["GET"]
|
|
|
|
decorators = [jwt_required(), get_user_params(), check_services, page_for_admin(level=3), check_group()]
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get(service: str, cluster_id: int):
|
|
|
|
"""
|
|
|
|
This endpoint retrieves information about the specified VIPs.
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- HA Cluster VIP
|
|
|
|
parameters:
|
|
|
|
- in: 'path'
|
|
|
|
name: 'service'
|
|
|
|
description: 'Can be only "cluster"'
|
|
|
|
required: true
|
|
|
|
type: 'string'
|
|
|
|
- in: 'path'
|
|
|
|
name: 'cluster_id'
|
|
|
|
description: 'ID of the HA cluster to retrieve'
|
|
|
|
required: true
|
|
|
|
type: 'integer'
|
|
|
|
responses:
|
|
|
|
200:
|
|
|
|
description: Successful operation
|
|
|
|
schema:
|
|
|
|
type: 'array'
|
|
|
|
items:
|
|
|
|
type: 'object'
|
|
|
|
properties:
|
|
|
|
cluster_id:
|
|
|
|
type: 'object'
|
|
|
|
properties:
|
|
|
|
description:
|
|
|
|
type: 'string'
|
|
|
|
group_id:
|
|
|
|
type: 'integer'
|
|
|
|
id:
|
|
|
|
type: 'integer'
|
|
|
|
name:
|
|
|
|
type: 'string'
|
|
|
|
pos:
|
|
|
|
type: 'integer'
|
|
|
|
syn_flood:
|
|
|
|
type: 'integer'
|
|
|
|
id:
|
|
|
|
type: 'integer'
|
|
|
|
return_master:
|
|
|
|
type: 'integer'
|
|
|
|
router_id:
|
2024-08-27 09:40:15 +00:00
|
|
|
type: 'integer'
|
2024-08-02 09:50:02 +00:00
|
|
|
use_src:
|
|
|
|
type: 'integer'
|
|
|
|
vip:
|
|
|
|
type: 'string'
|
|
|
|
default:
|
|
|
|
description: Unexpected error
|
|
|
|
"""
|
|
|
|
vips = ha_sql.select_cluster_vips(cluster_id)
|
2024-08-27 09:40:15 +00:00
|
|
|
vips = [model_to_dict(vip, recurse=False) for vip in vips]
|
2024-08-02 09:50:02 +00:00
|
|
|
return jsonify(vips)
|