mirror of https://github.com/Aidaho12/haproxy-wi
v8.1.6: Add support for editing specific config sections and templates
Enhanced configuration management by introducing support for editing specific sections in HAProxy configurations via `edit_section`. Added server templates with structured validation, improved file encoding handling, and addressed edge cases in multiple components for greater robustness.pull/418/head v8.1.6
parent
32db39fdd5
commit
66f1760ca3
|
@ -460,7 +460,7 @@ def compare_config(service: str, left: str, right: str) -> str:
|
|||
return render_template('ajax/compare.html', stdout=output, lang=lang)
|
||||
|
||||
|
||||
def show_config(server_ip: str, service: str, config_file_name: str, configver: str, claims: dict) -> str:
|
||||
def show_config(server_ip: str, service: str, config_file_name: str, configver: str, claims: dict, edit_section: str) -> str:
|
||||
"""
|
||||
Get and display the configuration file for a given server.
|
||||
|
||||
|
@ -516,7 +516,8 @@ def show_config(server_ip: str, service: str, config_file_name: str, configver:
|
|||
'is_serv_protected': server_sql.is_serv_protected(server_ip),
|
||||
'is_restart': service_sql.select_service_setting(server.server_id, service, 'restart'),
|
||||
'lang': roxywi_common.get_user_lang_for_flask(),
|
||||
'hostname': server.hostname
|
||||
'hostname': server.hostname,
|
||||
'edit_section': edit_section
|
||||
}
|
||||
|
||||
return render_template('ajax/config_show.html', **kwargs)
|
||||
|
|
|
@ -209,6 +209,13 @@ class ConfigRequest(BaseModel):
|
|||
config_local_path: Optional[str] = None
|
||||
config: str
|
||||
|
||||
@root_validator(skip_on_failure=True)
|
||||
@classmethod
|
||||
def decode_config(cls, values):
|
||||
if 'config' in values:
|
||||
values['config'] = values['config'].encode('utf-8')
|
||||
return values
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
login: EscapedString
|
||||
|
@ -407,6 +414,13 @@ class HaproxyServersCheck(BaseModel):
|
|||
inter: Optional[int] = 2000
|
||||
|
||||
|
||||
class HaproxyServersTemplate(BaseModel):
|
||||
prefix: int
|
||||
count: int
|
||||
servers: Union[IPvAnyAddress, DomainName]
|
||||
port: Annotated[int, Gt(1), Le(65535)]
|
||||
|
||||
|
||||
class HaproxyCircuitBreaking(BaseModel):
|
||||
observe: Literal['layer7', 'layer4']
|
||||
error_limit: int
|
||||
|
@ -415,7 +429,7 @@ class HaproxyCircuitBreaking(BaseModel):
|
|||
|
||||
class HaproxyConfigRequest(BaseModel):
|
||||
balance: Optional[Literal['roundrobin', 'source', 'leastconn', 'first', 'rdp-cookie', 'uri', 'uri whole', 'static-rr']] = None
|
||||
mode: Literal['tcp', 'http'] = 'http'
|
||||
mode: Literal['tcp', 'http', 'log'] = 'http'
|
||||
type: Literal['listen', 'frontend', 'backend']
|
||||
name: EscapedString
|
||||
option: Optional[str] = None
|
||||
|
@ -425,6 +439,7 @@ class HaproxyConfigRequest(BaseModel):
|
|||
headers: Optional[List[HaproxyHeaders]] = None
|
||||
acls: Optional[List[HaproxyAcls]] = None
|
||||
backend_servers: Optional[List[HaproxyBackendServer]] = None
|
||||
servers_template: Optional[HaproxyServersTemplate] = None
|
||||
blacklist: Optional[str] = ''
|
||||
whitelist: Optional[str] = ''
|
||||
ssl: Optional[HaproxySSL] = None
|
||||
|
|
|
@ -89,7 +89,7 @@ def show_roxy_log(
|
|||
if syslog_server is None or syslog_server == '':
|
||||
raise Exception('error: Syslog server is enabled, but there is no IP for syslog server')
|
||||
|
||||
if waf:
|
||||
if waf and service == 'haproxy':
|
||||
local_path_logs = '/var/log/waf.log'
|
||||
commands = "sudo cat %s |tail -%s %s %s" % (local_path_logs, rows, grep_act, exgrep_act)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ def ssh_command(server_ip: str, commands: str, **kwargs):
|
|||
|
||||
def subprocess_execute(cmd):
|
||||
import subprocess
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, universal_newlines=True)
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, universal_newlines=True, errors='backslashreplace')
|
||||
stdout, stderr = p.communicate()
|
||||
output = stdout.splitlines()
|
||||
return output, stderr
|
||||
|
|
|
@ -39,9 +39,10 @@ def show_config(service):
|
|||
configver = request.json.get('configver')
|
||||
server_ip = request.json.get('serv')
|
||||
claims = get_jwt()
|
||||
edit_section = request.json.get('edit_section')
|
||||
|
||||
try:
|
||||
data = config_mod.show_config(server_ip, service, config_file_name, configver, claims)
|
||||
data = config_mod.show_config(server_ip, service, config_file_name, configver, claims, edit_section)
|
||||
return DataStrResponse(data=data).model_dump(mode='json'), 200
|
||||
except Exception as e:
|
||||
return roxywi_common.handler_exceptions_for_json_data(e, '')
|
||||
|
@ -114,8 +115,9 @@ def config(service, serv, edit, config_file_name, new):
|
|||
pass
|
||||
|
||||
try:
|
||||
conf = open(cfg, "r")
|
||||
conf = open(cfg, "rb")
|
||||
config_read = conf.read()
|
||||
config_read = config_read.decode('utf-8')
|
||||
conf.close()
|
||||
except IOError as e:
|
||||
return f'Cannot read imported config file {e}', 200
|
||||
|
|
|
@ -41,7 +41,6 @@ def logs_internal():
|
|||
selects.append(['roxy-wi.access.log', 'access.log'])
|
||||
|
||||
kwargs = {
|
||||
'autorefresh': 1,
|
||||
'selects': selects,
|
||||
'serv': log_file,
|
||||
'lang': g.user_params['lang']
|
||||
|
@ -63,6 +62,8 @@ def logs(service, waf):
|
|||
# hour1 = request.args.get('hour1')
|
||||
# minute1 = request.args.get('minute1')
|
||||
log_file = request.args.get('file')
|
||||
service_desc = service_sql.select_service(service)
|
||||
service_name = service_desc.service
|
||||
|
||||
if rows is None:
|
||||
rows = 10
|
||||
|
@ -70,17 +71,14 @@ def logs(service, waf):
|
|||
grep = ''
|
||||
|
||||
if service in ('haproxy', 'nginx', 'keepalived', 'apache') and not waf:
|
||||
service_desc = service_sql.select_service(service)
|
||||
service_name = service_desc.service
|
||||
servers = roxywi_common.get_dick_permit(service=service_desc.slug)
|
||||
elif waf:
|
||||
service_name = 'WAF'
|
||||
servers = roxywi_common.get_dick_permit(haproxy=1)
|
||||
servers = roxywi_common.get_dick_permit(service=service_desc.slug)
|
||||
else:
|
||||
return redirect(url_for('index'))
|
||||
|
||||
kwargs = {
|
||||
'autorefresh': 1,
|
||||
'servers': servers,
|
||||
'serv': serv,
|
||||
'service': service,
|
||||
|
|
|
@ -130,13 +130,17 @@
|
|||
default-server observe {{ config.circuit_breaking.observe }} error-limit {{ config.circuit_breaking.error_limit }} on-error {{ config.circuit_breaking.on_error }}
|
||||
{% endif -%}
|
||||
|
||||
{% if config.backend_servers != 'None' -%}
|
||||
{% if config.backend_servers != 'None' and config.servers_template == 'None' -%}
|
||||
{% for backend in config.backend_servers -%}
|
||||
server {{ backend.server }} {{ backend.server }}:{{ backend.port }} port {{ backend.port_check }} {{ check_option }} {{ ssl_check_option }} maxconn {{ backend.maxconn }}{% if backend.send_proxy %} send-proxy{% endif %}{% if backend.backup %} backup {% endif %}
|
||||
|
||||
{% endfor -%}
|
||||
{% endif -%}
|
||||
|
||||
{% if config.servers_template != 'None' -%}
|
||||
server-template {{ config.servers_template.prefix }} {{ config.servers_template.count }} {{ config.servers_template.servers }}: {{ config.servers_template.port }} {{ check_option }}
|
||||
{% endif -%}
|
||||
|
||||
{% if config.backends and config.backends != 'None' -%}
|
||||
use_backend {{ config.backends }}
|
||||
{% endif %}
|
||||
|
|
|
@ -40,7 +40,7 @@ function overviewHapserverBackends(serv, hostname, service) {
|
|||
$("#top-" + hostname).empty();
|
||||
for (let i in data.data) {
|
||||
if (service === 'haproxy') {
|
||||
div = `<a href="/config/section/haproxy/${serv}/${data.data[i]}" target="_blank" style="padding-right: 10px;">${data.data[i]}</a> `
|
||||
div = `<a href="/config/haproxy/${serv}/show/?section=${data.data[i]}" target="_blank" style="padding-right: 10px;">${data.data[i]}</a> `
|
||||
} else if (service === 'nginx' || service === 'apache') {
|
||||
div = `<a href="/config/${service}/${serv}/show/${i}" target="_blank" style="padding-right: 10px;">${data.data[i]}</a>`;
|
||||
} else {
|
||||
|
|
|
@ -154,7 +154,7 @@ function showLog() {
|
|||
toastr.warning('Select a log file first')
|
||||
return false;
|
||||
} else {
|
||||
file = file_from_get;
|
||||
file = findGetParameter('file');
|
||||
}
|
||||
}
|
||||
if ((file === undefined || file === null) && waf === '') {
|
||||
|
@ -174,9 +174,9 @@ function showLog() {
|
|||
if (service === 'None') {
|
||||
service = 'haproxy';
|
||||
}
|
||||
if (waf && waf != 'haproxy' && waf != 'nginx' && waf != 'apache' && waf != 'keepalived') {
|
||||
url = "/logs/" + service + "/waf/" + serv + "/" + rows;
|
||||
waf = 1;
|
||||
if (waf && waf != 'haproxy' && waf != 'apache' && waf != 'keepalived') {
|
||||
file = findGetParameter('file');
|
||||
url = "/logs/" + service + "/waf/" + serv + "/" + rows + '?file_from_get=' + file;
|
||||
}
|
||||
$.ajax( {
|
||||
url: url,
|
||||
|
@ -300,6 +300,7 @@ function showCompareConfigs() {
|
|||
});
|
||||
}
|
||||
function showConfig() {
|
||||
let edit_section = '';
|
||||
let service = $('#service').val();
|
||||
let config_file = $('#config_file_name').val()
|
||||
let config_file_name = encodeURI(config_file);
|
||||
|
@ -315,10 +316,16 @@ function showConfig() {
|
|||
}
|
||||
}
|
||||
clearAllAjaxFields();
|
||||
|
||||
if (service === 'haproxy') {
|
||||
edit_section = findGetParameter('section');
|
||||
let edit_section_uri = '?section=' + edit_section;
|
||||
}
|
||||
let json_data = {
|
||||
"serv": $("#serv").val(),
|
||||
"service": service,
|
||||
"config_file_name": config_file_name
|
||||
"config_file_name": config_file_name,
|
||||
"edit_section": edit_section
|
||||
}
|
||||
$.ajax({
|
||||
url: "/config/" + service + "/show",
|
||||
|
@ -332,7 +339,7 @@ function showConfig() {
|
|||
toastr.clear();
|
||||
$("#ajax").html(data.data);
|
||||
$.getScript(configShow);
|
||||
window.history.pushState("Show config", "Show config", "/config/" + service + "/" + $("#serv").val() + "/show/" + config_file_name);
|
||||
window.history.pushState("Show config", "Show config", "/config/" + service + "/" + $("#serv").val() + "/show/" + config_file_name + edit_section_uri);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -331,6 +331,16 @@
|
|||
</span><div>
|
||||
{% continue %}
|
||||
{% endif %}
|
||||
{% if line.startswith('log-forward') %}
|
||||
</div><span class="param">{{ line }}
|
||||
{% if role %}
|
||||
<span class="accordion-link">
|
||||
<a href="/config/section/haproxy/{{serv}}/{{ line }}">{{lang.words.edit|title()}}/{{lang.words.delete|title()}}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span><div>
|
||||
{% continue %}
|
||||
{% endif %}
|
||||
{% if "acl" in line or "option" in line or "server" in line %}
|
||||
{% if "timeout" not in line and "default-server" not in line and "#use_backend" not in line and "#" not in line%}
|
||||
<span class="numRow">
|
||||
|
@ -410,6 +420,10 @@
|
|||
function expand_button(event) {
|
||||
$("#expand_link").click();
|
||||
}
|
||||
console.log("{{edit_section}}");
|
||||
{% if service == 'haproxy' and edit_section != '' %}
|
||||
openSection('{{ edit_section }}');
|
||||
{% endif %}
|
||||
</script>
|
||||
<div id="edit-section" style="display: none;"></div>
|
||||
<div id="dialog-confirm" style="display: none;">
|
||||
|
|
|
@ -388,7 +388,7 @@ class ServiceConfigView(MethodView):
|
|||
return ErrorResponse(error=str(e)).model_dump(mode='json')
|
||||
|
||||
try:
|
||||
with open(cfg, 'r') as file:
|
||||
with open(cfg, 'rb') as file:
|
||||
conf = file.readlines()
|
||||
except Exception as e:
|
||||
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot get config')
|
||||
|
@ -461,9 +461,9 @@ class ServiceConfigView(MethodView):
|
|||
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot get config')
|
||||
|
||||
try:
|
||||
with open(cfg, "a") as conf:
|
||||
with open(cfg, "ab") as conf:
|
||||
conf.write(body.config)
|
||||
except IOError as e:
|
||||
except Exception as e:
|
||||
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot write config')
|
||||
|
||||
try:
|
||||
|
@ -476,7 +476,10 @@ class ServiceConfigView(MethodView):
|
|||
return f'error: {e}', 200
|
||||
|
||||
if body.action != 'test':
|
||||
config_mod.diff_config(body.config_local_path, cfg)
|
||||
try:
|
||||
config_mod.diff_config(body.config_local_path, cfg)
|
||||
except Exception as e:
|
||||
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot compare configs')
|
||||
|
||||
return DataStrResponse(data=stderr).model_dump(mode='json'), 201
|
||||
|
||||
|
|
Loading…
Reference in New Issue