добавил все настройки в веб, сделал cleanup интерфейса

This commit is contained in:
Vladislav Ostapov 2024-11-13 17:44:42 +03:00
parent 9577ac844d
commit 484a6abe08
4 changed files with 362 additions and 34 deletions

View File

@ -87,7 +87,11 @@ namespace http::server {
}
void SslConnection::stop() {
stream_.shutdown();
try {
stream_.shutdown();
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(warning) << "SslConnection::stop(): Can't shutdown ssl socket: " << e.what();
}
}
SslConnection::~SslConnection() = default;

View File

@ -15,6 +15,33 @@ typedef boost::property_tree::ptree::path_type json_path;
static constexpr const char* DEFAULT_QOS_CLASSES = R"({"rt1":[],"rt2":[],"rt3":[],"cd":[]})";
static int calculateSubnetMask(const std::string& subnet_mask) {
int mask = 0;
std::istringstream iss(subnet_mask);
std::string octet;
while (std::getline(iss, octet, '.')) {
int octet_value = std::stoi(octet);
for (int i = 7; i >= 0; i--) {
if (octet_value & (1 << i)) {
mask++;
}
}
}
return mask;
}
class TerminalNetworkSettings {
public:
std::string managementIp, managementGateway, mode, dataIp;
unsigned int dataMtu = 1500;
TerminalNetworkSettings() = default;
TerminalNetworkSettings(const TerminalNetworkSettings& src) = default;
~TerminalNetworkSettings() = default;
TerminalNetworkSettings& operator= (const TerminalNetworkSettings& src) = default;
};
/**
* Этот демон нужен для того, чтобы получать статистику из API, а так же корректно сохранять настройки
*/
@ -67,6 +94,31 @@ private:
}
}
void updateNetworkSettings() {
TerminalNetworkSettings s;
std::string tmp;
std::lock_guard lock(this->cpApiMutex);
CP_GetNetwork(sid, "addr", &s.managementIp);
CP_GetNetwork(sid, "mask", &tmp);
s.managementIp += "/";
s.managementIp += std::to_string(calculateSubnetMask(tmp));
CP_GetNetwork(sid, "gateway", &s.managementGateway);
tmp.clear(); CP_GetNetwork(sid, "mode", &tmp);
if (tmp == "tun") {
s.mode = "l3";
CP_GetNetwork(sid, "addr_data", &s.dataIp);
} else {
s.mode = "l2";
s.dataIp = "0.0.0.0/24";
}
s.dataMtu = 1500;
{
std::lock_guard lock2(this->networkSettingsMutex);
this->networkSettings = s;
}
}
void updateQos() {
bool tmp1; std::string tmp2;
std::scoped_lock lock{this->cpApiMutex};
@ -120,6 +172,15 @@ private:
BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateSettings(): " << e.what();
}
}},
// обновление кеша настроек сети (делается отдельно)
{.lastUpdate = 0, .periodMs = CACHE_SETTINGS_UPDATE_MS, .callback = [this]() {
try {
this->updateSettings();
BOOST_LOG_TRIVIAL(debug) << "api_driver::TerminalApiDaemon::updateSettings(): success update!";
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateSettings(): " << e.what();
}
}},
// обновление кеша QoS
{.lastUpdate = 0, .periodMs = CACHE_QOS_UPDATE_MS, .callback = [this]() {
try {
@ -164,6 +225,9 @@ private:
DPDI_parmeters dpdiSettings{};
buc_lnb_settings bucLnbSettings{};
std::shared_mutex networkSettingsMutex;
TerminalNetworkSettings networkSettings;
std::shared_mutex qosSettingsMutex;
bool qosEnabled;
std::string qosClassesJson;
@ -202,6 +266,11 @@ public:
}
}
void getNetworkSettings(TerminalNetworkSettings& dest) {
std::shared_lock lock(this->networkSettingsMutex);
dest = this->networkSettings;
}
void getQosSettings(bool& isEnabled, std::string& json) {
std::shared_lock lock(this->settingsMutex);
isEnabled = this->qosEnabled;
@ -454,7 +523,9 @@ std::string api_driver::ApiDriver::loadSettings() const {
ACM_parameters_serv_ acmSettings{};
DPDI_parmeters dpdiSettings{};
buc_lnb_settings bucLnb{};
TerminalNetworkSettings network;
daemon->getSettings(&modSettings, &demodSettings, &acmSettings, &dpdiSettings, &bucLnb);
daemon->getNetworkSettings(network);
// uint32_t modulatorModcod;
// {
@ -528,6 +599,13 @@ std::string api_driver::ApiDriver::loadSettings() const {
result << ",\n\"qos.enabled\":" << boolAsStr(qosEnabled);
result << ",\"qos.profile\":" << qosClasses;
// сеть
result << ",\"network.managementIp\":\n" << buildEscapedString(network.managementIp);
result << ",\"network.managementGateway\":\n" << buildEscapedString(network.managementGateway);
result << ",\"network.mode\":\n" << buildEscapedString(network.mode);
result << ",\"network.dataIp\":\n" << buildEscapedString(network.dataIp);
result << ",\"network.dataMtu\":\n" << network.dataMtu;
result << "}";
return result.str();
}

View File

@ -85,11 +85,11 @@
padding: 1em;
}
.tabs-item-flex-container th {
.settings-set-container th {
text-align: left;
padding-right: 1em;
}
.tabs-item-flex-container td {
.settings-set-container td {
min-width: 10em;
}
.tabs-item-flex-container h2 {
@ -124,6 +124,10 @@ label {
border-bottom: solid 2px var(--brand-text);
}
.settings-set-container input:invalid {
border: solid 1px var(--text-bad);
}
/* костыль для браузеров, которые некорректно стилизуют элементы option */
select * {
background: var(--bg-selected);

View File

@ -106,7 +106,7 @@
<table>
<tbody>
<tr><th>Температура ADRV</th><td>{{ stat_device.adrv }} °C</td></tr>
<tr><th>Температура ZYNQ ULTRASUCK</th><td>{{ stat_device.zynq }} °C</td></tr>
<tr><th>Температура ZYNQ <span hidden>ULTRASUCK</span></th><td>{{ stat_device.zynq }} °C</td></tr>
<tr><th>Температура FPGA</th><td>{{ stat_device.fpga }} °C</td></tr>
</tbody>
</table>
@ -421,7 +421,7 @@
</div>
<button class="action-button" @click="settingsSubmitBucLnb()">Сохранить <span class="submit-spinner" v-show="submitStatus.bucLnb"></span></button>
</div>
<div class="tabs-body-item" v-if="activeTab === 'qos'">
<div class="tabs-body-item" v-if="activeTab === 'qos' && settingFetchComplete">
<h2>Настройки QoS</h2>
<div class="settings-set-container">
<label>
@ -478,19 +478,32 @@
</summary>
<label>
<span>VLAN ID</span>
<input v-model="filter.vlan" type="text">
<!-- singleVlanExpr: (([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4}))-->
<!-- expr: ^(((single,)+single)|single)$-->
<input v-model="filter.vlan" type="text" pattern="^((((([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})),)+(([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})))|(([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})))$">
</label>
<label>
<span>Протокол L3</span>
<input v-model="filter.proto" type="text">
<select v-model="filter.proto" multiple>
<option value="ah">AH</option>
<option value="comp">COMP</option>
<option value="dccp">DCCP</option>
<option value="esp">ESP</option>
<option value="icmp">ICMP</option>
<!-- <option value="icmpv6">ICMPv6</option>-->
<option value="sctp">SCTP</option>
<option value="tcp">TCP</option>
<option value="udp">UDP</option>
<option value="udplite">UDP LITE</option>
</select>
</label>
<label>
<span>Порт источника</span>
<input v-model="filter.sport" type="text">
<input v-model="filter.sport" type="text" pattern="^((((([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})),)+(([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})))|(([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})))$">
</label>
<label>
<span>Порт назначения</span>
<input v-model="filter.dport" type="text">
<input v-model="filter.dport" type="text" pattern="^((((([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})),)+(([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})))|(([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})))$">
</label>
<label>
<span>IP источника</span>
@ -512,17 +525,111 @@
</details>
</div>
</template>
<button class="action-button" @click="settingsSubmitQoS()">Применить <span class="submit-spinner" v-show="submitStatus.qos"></span></button>
<h2>Настройки TCP-акселерации</h2>
<div class="settings-set-container">
<label>
<span>Активировать акселерацию</span>
<span class="toggle-input"><input type="checkbox" v-model="tcpAccel.en" /><span class="slider"></span></span>
</label>
<label>
<span>Максимальное количество соединений</span>
<input type="number" v-model="tcpAccel.maxConnections" min="1" max="10000" />
</label>
</div>
<button class="action-button" @click="settingsSubmitTcpAccel()">Применить <span class="submit-spinner" v-show="submitStatus.tcpAccel"></span></button>
</div>
<div class="tabs-body-item" v-if="activeTab === 'admin'">
<p>
Эти настройки пока недоступны, но скоро разработчик это поправит. А пока смотри на крокодила, или купи разработчику банку <span style="text-decoration: line-through;">пива</span> колы для ускорения процесса)
</p>
<img loading="lazy" src="/images/krokodil_vzryvaetsya_hd.gif" alt="krokodil">
<video preload="auto" controls style="max-width: 100%">
<source src="/vid/video_2024-11-06_15-49-35.mp4" type="video/mp4" />
</video>
<div class="tabs-body-item" v-if="activeTab === 'admin' && settingFetchComplete">
<h2>Настройки сети</h2>
<div class="settings-set-container">
<h3>Интерфейс управления</h3>
<label>
<span>IP адрес/маска</span>
<input v-model="param.network.managementIp" required type="text" pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$">
</label>
<label>
<span>Шлюз интерфейса управления</span>
<input v-model="param.network.managementGateway" type="text" pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$">
</label>
<label>
<span>Режим модема</span>
<select v-model="param.network.mode">
<option value="l2">Коммутатор</option>
<option value="l3">Маршрутизатор</option>
</select>
</label>
<h3>Интерфейс данных</h3>
<label v-if="param.network.mode === 'l3'">
<span>IP адрес/маска</span>
<input v-model="param.network.dataIp" required type="text" pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$">
</label>
<label>
<span>MTU</span>
<input v-model="param.network.dataMtu" required type="number" min="1500" max="9000">
</label>
<button class="action-button" @click="settingsSubmitNetwork()">Применить <span class="submit-spinner" v-show="submitStatus.network"></span></button>
</div>
<div class="settings-set-container">
<h3>Отладка</h3>
<label>
<span>Передача отладочной информации</span>
<span class="toggle-input">
<input type="checkbox" v-model="param.debugSend.en" />
<span class="slider"></span>
</span>
</label>
<label v-if="param.debugSend.en">
<span>IP адрес получателя</span>
<input v-model="param.debugSend.receiverIp" required type="text" pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$">
</label>
<label>
<span>Порт для CinC</span>
<input v-model="param.debugSend.portCinC" type="number" pattern="^[0-9]{1,5}$">
</label>
<label>
<span>Порт для CinC</span>
<input v-model="param.debugSend.portData" type="number" pattern="^[0-9]{1,5}$">
</label>
<label>
<span>Таймаут</span>
<input v-model="param.debugSend.timeout" type="number" pattern="^[0-9]+$">
</label>
<button class="action-button" @click="settingsSubmitDebugSend()">Применить <span class="submit-spinner" v-show="submitStatus.network"></span></button>
</div>
<div class="settings-set-container">
<h3>Управление ПО</h3>
<table>
<tbody>
<tr><th>Версия ПО</th><td>{{ param.firmware.firmwareVersion }}</td></tr>
<tr><th>ID модема</th><td>{{ param.firmware.modemUid }}</td></tr>
<tr><th>Серийный номер</th><td>{{ param.firmware.modemSn }}</td></tr>
<tr><th>MAC интерфейса управления</th><td>{{ param.firmware.macManagement }}</td></tr>
<tr><th>MAC интерфейса управления</th><td>{{ param.firmware.macData }}</td></tr>
</tbody>
</table>
<div>
<button class="dangerous-button">Перезагрузить модем</button>
</div>
<div>
<button class="dangerous-button">Сбросить модем до заводских настроек</button>
</div>
<h3>Обновление ПО</h3>
<label>
<span>Порт для CinC</span>
<input type="file" accept="application/zip">
</label>
<button class="dangerous-button" @click="settingsUploadUpdate()">Обновить встроенное ПО</button>
</div>
<div hidden>
<p>
Эти настройки пока недоступны, но скоро разработчик это поправит. А пока смотри на крокодила, или купи разработчику банку <span style="text-decoration: line-through;">пива</span> колы для ускорения процесса)
</p>
<div><img loading="lazy" src="/images/krokodil_vzryvaetsya_hd.gif" alt="krokodil"></div>
<div><video preload="auto" controls style="max-width: 100%"><source src="/vid/video_2024-11-06_15-49-35.mp4" type="video/mp4" /></video></div>
</div>
</div>
<p>Последнее обновление статистики: {{ lastUpdateTime }}</p>
</div>
@ -531,13 +638,12 @@
<!-- Версия для разработки включает в себя возможность вывода в консоль полезных уведомлений -->
<script src="/js/vue.js"></script>
<script>
window.addEventListener('load', updateHeaderHeight);
window.addEventListener('resize', updateHeaderHeight);
function updateHeaderHeight() {
const header = document.querySelector('header');
document.body.style.setProperty('--header-height', `${header.offsetHeight}px`);
}
window.addEventListener('load', updateHeaderHeight);
window.addEventListener('resize', updateHeaderHeight);
// const router = useRouter();
const availableTabs = ['monitoring', 'setup', 'qos', 'admin']
@ -689,6 +795,9 @@
cinc: false,
bucLnb: false,
qos: false,
network: false,
debugSend: false,
tcpAccel: false,
},
stat_rx: {
@ -804,6 +913,29 @@
refClk10M: false, // подача опоры 10MHz
autoStart: false
},
network: {
managementIp: '', // 0.0.0.0/24
managementGateway: '',
mode: String, // l2 | l3
dataIp: '', //
dataMtu: 1500
},
debugSend: {
en: false,
receiverIp: '0.0.0.0', // 0.0.0.0
portCinC: 0,
portData: 0,
timeout: 0
},
// эти "настройки" - read only
firmware: {
firmwareVersion: '?',
modemUid: '?',
modemSn: '?',
macManagement: '?',
macData: '?',
}
},
qos: {
@ -814,6 +946,11 @@
cd: [],
},
tcpAccel: {
en: false,
maxConnections: 128
},
testState: false,
initState: '',
lastUpdateTime: new Date(),
@ -1017,7 +1154,14 @@
for (const fi in qc.filters) {
let filter = {}
if (qc['filters'][fi].vlan !== "") { filter['vlan'] = qc['filters'][fi].vlan }
if (qc['filters'][fi].proto !== "") { filter['proto'] = qc['filters'][fi].proto }
if (qc['filters'][fi].proto !== "") {
let tmp = "";
for (let pid = 0; pid < qc['filters'][fi].proto.length; pid++) {
if (pid !== 0) { tmp += ',' }
tmp += qc['filters'][fi].proto[pid]
}
filter['proto'] = tmp
}
if (qc['filters'][fi].sport !== "") { filter['sport'] = qc['filters'][fi].sport }
if (qc['filters'][fi].dport !== "") { filter['dport'] = qc['filters'][fi].dport }
if (qc['filters'][fi].ip_src !== "") { filter['ip.src'] = qc['filters'][fi].ip_src }
@ -1060,6 +1204,79 @@
})
},
settingsSubmitTcpAccel() {
if (this.submitStatus.tcpAccel) { return }
this.submitStatus.tcpAccel = true
fetch('/api/set/tcpAccel', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"tcpAccel.en": this.tcpAccel.en,
"tcpAccel.maxConnections": this.tcpAccel.maxConnections
})
}).then(async (resp) => {
this.submitStatus.tcpAccel = false
this.updateNetworkSettings(await resp.json())
}).catch((reason) => {
this.submitStatus.tcpAccel = false
alert(`Ошибка при применении настроек: ${reason}`)
})
},
settingsSubmitNetwork() {
if (this.submitStatus.network) { return }
let query = {
"network.managementIp": this.param.network.managementIp,
"network.managementGateway": this.param.network.managementGateway,
"network.mode": this.param.network.mode,
"network.dataIp": this.param.network.dataIp,
"network.dataMtu": this.param.network.dataMtu
}
if (confirm('Вы уверены, что хотите сохранить настройки сети? После этого модем может стать недоступным.')) {
this.submitStatus.network = true
fetch('/api/set/network', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(query)
}).then(async (resp) => {
this.submitStatus.network = false
this.updateNetworkSettings(await resp.json())
}).catch((reason) => {
this.submitStatus.network = false
alert(`Ошибка при применении настроек: ${reason}`)
})
}
},
settingsSubmitDebugSend() {
if (this.submitStatus.debugSend) { return }
let query = {
"debugSend.en": this.param.debugSend.en,
"debugSend.receiverIp": this.param.debugSend.receiverIp,
"debugSend.portCinC": this.param.debugSend.portCinC,
"debugSend.portData": this.param.debugSend.portData,
"debugSend.timeout": this.param.debugSend.timeout
}
this.submitStatus.debugSend = true
fetch('/api/set/debugSend', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(query)
}).then(async (resp) => {
this.submitStatus.debugSend = false
this.updateNetworkSettings(await resp.json())
}).catch((reason) => {
this.submitStatus.debugSend = false
alert(`Ошибка при применении настроек: ${reason}`)
})
},
performUpdateSettings(reloadParts) {
const doFetchSettings = async () => {
let d = await fetch("/api/get/settings")
@ -1176,7 +1393,7 @@
result.filters.push({
isEnabled: !qc['filters'][fi].hasOwnProperty('disabled'),
vlan: qc['filters'][fi].hasOwnProperty('vlan') ? qc['filters'][fi]['vlan'] : '',
proto: qc['filters'][fi].hasOwnProperty('proto') ? qc['filters'][fi]['proto'] : '',
proto: qc['filters'][fi].hasOwnProperty('proto') ? qc['filters'][fi]['proto'].split(',') : [],
sport: qc['filters'][fi].hasOwnProperty('sport') ? qc['filters'][fi]['sport'] : '',
dport: qc['filters'][fi].hasOwnProperty('dport') ? qc['filters'][fi]['dport'] : '',
ip_src: qc['filters'][fi].hasOwnProperty('ip.src') ? qc['filters'][fi]['ip.src'] : '',
@ -1196,12 +1413,39 @@
}
},
updateNetworkSettings(vals) {
this.submitStatus.network = false
this.param.network.managementIp = vals["settings"]["network.managementIp"]
this.param.network.managementGateway = vals["settings"]["network.managementGateway"]
this.param.network.mode = vals["settings"]["network.mode"]
this.param.network.dataIp = vals["settings"]["network.dataIp"]
this.param.network.dataMtu = vals["settings"]["network.dataMtu"]
},
updateDebugSendSettings(vals) {
this.submitStatus.debugSend = false
this.param.debugSend.en = vals["settings"]["debugSend.en"]
this.param.debugSend.receiverIp = vals["settings"]["debugSend.receiverIp"]
this.param.debugSend.portCinC = vals["settings"]["debugSend.portCinC"]
this.param.debugSend.portData = vals["settings"]["debugSend.portData"]
this.param.debugSend.timeout = vals["settings"]["debugSend.timeout"]
},
updateSettings(vals) {
this.settingFetchComplete = true
this.updateRxTxSettings(vals)
this.updateCincSettings(vals)
this.updateBucLnbSettings(vals)
this.updateQosSettings(vals)
this.updateNetworkSettings(vals)
this.updateDebugSendSettings(vals)
// и отдельно тут обновим настройки прошивки
this.param.firmware.firmwareVersion = vals["settings"]["firmware.firmwareVersion"]
this.param.firmware.modemUid = vals["settings"]["firmware.modemUid"]
this.param.firmware.modemSn = vals["settings"]["firmware.modemSn"]
this.param.firmware.macManagement = vals["settings"]["firmware.macManagement"]
this.param.firmware.macData = vals["settings"]["firmware.macData"]
},
// addQosClass()
@ -1225,7 +1469,7 @@
let rule = {
isEnabled: true,
vlan: "",
proto: "",
proto: [],
sport: "",
dport: "",
ip_src: "",
@ -1263,10 +1507,7 @@
let result = ""
let isFirst = true;
for (const key in filter) {
if (key === "isEnabled") {
continue
}
if (!filter[key]) {
if (key === "isEnabled" || !filter[key] || (key === "proto" && filter['proto'].length === 0)) {
continue
}
if (isFirst) {
@ -1287,10 +1528,7 @@
result = ""
isFirst = true;
for (const key in filter) {
if (key === "isEnabled") {
continue
}
if (!filter[key]) {
if (key === "isEnabled" || !filter[key] || (key === "proto" && filter['proto'].length === 0)) {
continue
}
if (isFirst) {
@ -1307,8 +1545,12 @@
},
mounted() {
const doFetchStatistics = async () => {
let d = await fetch("/api/get/statistics")
this.updateStatistics(await d.json())
try {
let d = await fetch("/api/get/statistics")
this.updateStatistics(await d.json())
} catch (e) {
this.initState = "Ошибка обновления статистики"
}
setTimeout(() => {
doFetchStatistics()