mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-13 08:09:41 +00:00
Interfaces: Diagnostics: Ping - refactor diagnostics tool (https://github.com/opnsense/core/issues/6378)
This commit is contained in:
parent
f7d7dafccd
commit
83ccec4330
8
plist
8
plist
@ -293,6 +293,7 @@
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/NetflowController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/NetworkinsightController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/PacketCaptureController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/PingController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/SystemController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/SystemhealthController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/TrafficController.php
|
||||
@ -303,12 +304,14 @@
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/NetflowController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/NetworkinsightController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/PacketCaptureController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/PingController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/SystemController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/SystemhealthController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/TrafficController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/forms/dns_diagnostics.xml
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/forms/netflow_capture.xml
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/forms/packetcapture.xml
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/forms/ping.xml
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Firewall/AliasController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Firewall/AliasUtilController.php
|
||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/AliasController.php
|
||||
@ -579,6 +582,8 @@
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Diagnostics/Netflow.xml
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Diagnostics/PacketCapture.php
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Diagnostics/PacketCapture.xml
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Diagnostics/Ping.php
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Diagnostics/Ping.xml
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Dnsmasq/ACL/ACL.xml
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Dnsmasq/Menu/Menu.xml
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Firewall/Alias.php
|
||||
@ -688,6 +693,7 @@
|
||||
/usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/netflow.volt
|
||||
/usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/networkinsight.volt
|
||||
/usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/packetcapture.volt
|
||||
/usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/ping.volt
|
||||
/usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/routes.volt
|
||||
/usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/systemactivity.volt
|
||||
/usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/traffic.volt
|
||||
@ -890,6 +896,7 @@
|
||||
/usr/local/opnsense/scripts/interfaces/list_sockstat.py
|
||||
/usr/local/opnsense/scripts/interfaces/macinfo.py
|
||||
/usr/local/opnsense/scripts/interfaces/mpd.script
|
||||
/usr/local/opnsense/scripts/interfaces/ping.py
|
||||
/usr/local/opnsense/scripts/interfaces/ppp-linkdown.sh
|
||||
/usr/local/opnsense/scripts/interfaces/ppp-linkup.sh
|
||||
/usr/local/opnsense/scripts/interfaces/ppp-uptime.sh
|
||||
@ -1898,7 +1905,6 @@
|
||||
/usr/local/www/diag_confbak.php
|
||||
/usr/local/www/diag_defaults.php
|
||||
/usr/local/www/diag_logs_settings.php
|
||||
/usr/local/www/diag_ping.php
|
||||
/usr/local/www/diag_testport.php
|
||||
/usr/local/www/diag_traceroute.php
|
||||
/usr/local/www/fbegin.inc
|
||||
|
||||
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2023 Deciso B.V.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OPNsense\Diagnostics\Api;
|
||||
|
||||
use OPNsense\Base\ApiMutableModelControllerBase;
|
||||
use OPNsense\Core\Backend;
|
||||
|
||||
class PingController extends ApiMutableModelControllerBase
|
||||
{
|
||||
protected static $internalModelName = 'ping';
|
||||
protected static $internalModelClass = 'OPNsense\Diagnostics\Ping';
|
||||
private static $ping_dir = '/tmp/ping';
|
||||
|
||||
/**
|
||||
* set / create ping job
|
||||
*/
|
||||
public function setAction()
|
||||
{
|
||||
$result = parent::setAction();
|
||||
if (empty($result['validations'])) {
|
||||
$mdl = $this->getModel();
|
||||
$result['result'] = 'ok';
|
||||
$result['uuid'] = $mdl->settings->generateUUID();
|
||||
@mkdir(self::$ping_dir);
|
||||
$nodes = $mdl->settings->getNodes();
|
||||
foreach ($nodes as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$items = [];
|
||||
foreach ($value as $itemkey => $itemval) {
|
||||
if (!empty($itemval['selected'])) {
|
||||
$items[] = $itemkey;
|
||||
}
|
||||
}
|
||||
$nodes[$key] = implode(',', $items);
|
||||
}
|
||||
}
|
||||
file_put_contents(
|
||||
sprintf('%s/%s.json', self::$ping_dir, $result['uuid']),
|
||||
json_encode($nodes)
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* start ping job
|
||||
*/
|
||||
public function startAction($jobid)
|
||||
{
|
||||
$result = ['status' => 'failed'];
|
||||
if ($this->request->isPost()) {
|
||||
$this->sessionClose();
|
||||
$payload = json_decode((new Backend())->configdpRun('interface ping start', [$jobid]) ?? '', true);
|
||||
if (!empty($payload)) {
|
||||
$result = $payload;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* stop ping job
|
||||
*/
|
||||
public function stopAction($jobid)
|
||||
{
|
||||
$result = ['status' => 'failed'];
|
||||
if ($this->request->isPost()) {
|
||||
$this->sessionClose();
|
||||
$payload = json_decode((new Backend())->configdpRun('interface ping stop', [$jobid]) ?? '', true);
|
||||
if (!empty($payload)) {
|
||||
$result = $payload;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove ping job
|
||||
*/
|
||||
public function removeAction($jobid)
|
||||
{
|
||||
$result = ['status' => 'failed'];
|
||||
if ($this->request->isPost()) {
|
||||
$this->sessionClose();
|
||||
$payload = json_decode((new Backend())->configdpRun('interface ping remove', [$jobid]) ?? '', true);
|
||||
if (!empty($payload)) {
|
||||
$result = $payload;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* search current ping jobs
|
||||
*/
|
||||
public function searchJobsAction()
|
||||
{
|
||||
$this->sessionClose();
|
||||
$data = json_decode((new Backend())->configdRun('interface ping list') ?? '', true);
|
||||
$records = (!empty($data) && !empty($data['jobs'])) ? $data['jobs'] : [];
|
||||
return $this->searchRecordsetBase($records);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2023 Deciso B.V.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OPNsense\Diagnostics;
|
||||
|
||||
use OPNsense\Base\IndexController;
|
||||
|
||||
/**
|
||||
* Class PingController
|
||||
* @package OPNsense\Diagnostics
|
||||
*/
|
||||
class PingController extends IndexController
|
||||
{
|
||||
public function indexAction()
|
||||
{
|
||||
$this->view->pick('OPNsense/Diagnostics/ping');
|
||||
$this->view->pingForm = $this->getForm("ping");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>ping.settings.hostname</id>
|
||||
<label>Hostname or IP</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Hostname or address to ping.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>ping.settings.fam</id>
|
||||
<label>Address Family</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ping.settings.source_address</id>
|
||||
<label>Source address</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Optional source address.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>ping.settings.packetsize</id>
|
||||
<label>Packet size</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Specify the number of data bytes to be sent.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>ping.settings.disable_frag</id>
|
||||
<label>Do not fragment</label>
|
||||
<type>checkbox</type>
|
||||
<help>
|
||||
Disable fragmentation.
|
||||
Can be helpful to determine the maximum size a transport is able to send.
|
||||
Keep in mind this is the payload size, an IP and ICMP header are added.
|
||||
</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>ping.settings.description</id>
|
||||
<label>Description</label>
|
||||
<type>text</type>
|
||||
<help>Description to be displayed in the "jobs" tab.</help>
|
||||
</field>
|
||||
</form>
|
||||
@ -166,7 +166,8 @@
|
||||
<page-diagnostics-ping>
|
||||
<name>Diagnostics: Ping</name>
|
||||
<patterns>
|
||||
<pattern>diag_ping.php*</pattern>
|
||||
<pattern>ui/diagnostics/ping</pattern>
|
||||
<pattern>api/diagnostics/ping/*</pattern>
|
||||
</patterns>
|
||||
</page-diagnostics-ping>
|
||||
<page-diagnostics-rebootsystem>
|
||||
|
||||
@ -134,7 +134,7 @@
|
||||
<Diagnostics order="970" cssClass="fa fa-medkit fa-fw">
|
||||
<DNSLookup VisibleName="DNS Lookup" url="/ui/diagnostics/dns_diagnostics"/>
|
||||
<PacketCapture VisibleName="Packet Capture" url="/ui/diagnostics/packet_capture"/>
|
||||
<Ping url="/diag_ping.php"/>
|
||||
<Ping url="/ui/diagnostics/ping"/>
|
||||
<TestPort VisibleName="Port Probe" url="/diag_testport.php"/>
|
||||
<Traceroute VisibleName="Trace Route" url="/diag_traceroute.php"/>
|
||||
</Diagnostics>
|
||||
|
||||
36
src/opnsense/mvc/app/models/OPNsense/Diagnostics/Ping.php
Normal file
36
src/opnsense/mvc/app/models/OPNsense/Diagnostics/Ping.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2023 Deciso B.V.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace OPNsense\Diagnostics;
|
||||
|
||||
use OPNsense\Base\BaseModel;
|
||||
|
||||
class Ping extends BaseModel
|
||||
{
|
||||
}
|
||||
40
src/opnsense/mvc/app/models/OPNsense/Diagnostics/Ping.xml
Normal file
40
src/opnsense/mvc/app/models/OPNsense/Diagnostics/Ping.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<model>
|
||||
<mount>:memory:</mount>
|
||||
<version>1.0.0</version>
|
||||
<description>
|
||||
OPNsense Ping Diagnostics
|
||||
</description>
|
||||
<items>
|
||||
<settings>
|
||||
<hostname type="HostnameField">
|
||||
<Required>Y</Required>
|
||||
<ValidationMessage>Provide a valid hostname or address to ping</ValidationMessage>
|
||||
</hostname>
|
||||
<fam type="OptionField">
|
||||
<Required>Y</Required>
|
||||
<default>ip</default>
|
||||
<OptionValues>
|
||||
<ip>IPv4</ip>
|
||||
<ip6>IPv6</ip6>
|
||||
</OptionValues>
|
||||
</fam>
|
||||
<source_address type="NetworkField">
|
||||
<Required>N</Required>
|
||||
<NetMaskAllowed>N</NetMaskAllowed>
|
||||
<ValidationMessage>Provide a valid source address</ValidationMessage>
|
||||
</source_address>
|
||||
<packetsize type="IntegerField">
|
||||
<MinimumValue>1</MinimumValue>
|
||||
<MaximumValue>65535</MaximumValue>
|
||||
</packetsize>
|
||||
<disable_frag type="BooleanField">
|
||||
<default>0</default>
|
||||
</disable_frag>
|
||||
<description type="TextField">
|
||||
<Required>N</Required>
|
||||
<mask>/^(.){1,255}$/u</mask>
|
||||
<ValidationMessage>Description should be a string between 1 and 255 characters</ValidationMessage>
|
||||
</description>
|
||||
</settings>
|
||||
</items>
|
||||
</model>
|
||||
138
src/opnsense/mvc/app/views/OPNsense/Diagnostics/ping.volt
Normal file
138
src/opnsense/mvc/app/views/OPNsense/Diagnostics/ping.volt
Normal file
@ -0,0 +1,138 @@
|
||||
{#
|
||||
# Copyright (c) 2023 Deciso B.V.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or withoutmodification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#}
|
||||
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
var data_get_map = {'frm_PingSettings':"/api/diagnostics/ping/get"};
|
||||
mapDataToFormUI(data_get_map).done(function(data){
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
});
|
||||
|
||||
|
||||
let grid_jobs = $("#grid-jobs").UIBootgrid({
|
||||
search:'/api/diagnostics/ping/search_jobs',
|
||||
options:{
|
||||
formatters: {
|
||||
"commands": function (column, row) {
|
||||
let btns = [];
|
||||
btns.push('<button type="button" data-toggle="tooltip" class="btn btn-xs btn-default command-remove" title="{{ lang._('remove') }}" data-row-id="' + row.id + '"><span class="fa fa-fw fa-remove"></span></button> ');
|
||||
if (row.status === 'stopped') {
|
||||
btns.push('<button type="button" data-toggle="tooltip" class="btn btn-xs btn-default command-start" title="{{ lang._('(re)start') }}" data-row-id="' + row.id + '"><span class="fa fa-fw fa-play"></span></button> ');
|
||||
} else if (row.status === 'running') {
|
||||
btns.push('<button type="button" data-toggle="tooltip" class="btn btn-xs btn-default command-stop" title="{{ lang._('stop') }}" data-row-id="' + row.id + '"><span class="fa fa-fw fa-stop"></span></button> ');
|
||||
}
|
||||
|
||||
return btns.join("");
|
||||
},
|
||||
"status": function (column, row) {
|
||||
if (row.status == 'running') {
|
||||
return '<i class="fa fa-fw fa-spinner fa-pulse"></i>';
|
||||
} else {
|
||||
return '<i class="fa fa-fw fa-stop-circle-o"></i>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
grid_jobs.on('loaded.rs.jquery.bootgrid', function() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$(".command-start").click(function(){
|
||||
let id = $(this).data('row-id');
|
||||
ajaxCall("/api/diagnostics/ping/start/" + id, {}, function(){
|
||||
$("#grid-jobs").bootgrid("reload");
|
||||
});
|
||||
});
|
||||
$(".command-stop").click(function(){
|
||||
let id = $(this).data('row-id');
|
||||
ajaxCall("/api/diagnostics/ping/stop/" + id, {}, function(){
|
||||
$("#grid-jobs").bootgrid("reload");
|
||||
});
|
||||
});
|
||||
$(".command-remove").click(function(){
|
||||
let id = $(this).data('row-id');
|
||||
ajaxCall("/api/diagnostics/ping/remove/" + id, {}, function(){
|
||||
$("#grid-jobs").bootgrid("reload");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
$("#btn_start_new").click(function () {
|
||||
if (!$("#frm_PingSettings_progress").hasClass("fa-spinner")) {
|
||||
$("#frm_PingSettings_progress").addClass("fa fa-spinner fa-pulse");
|
||||
let callb = function (data) {
|
||||
$("#frm_PingSettings_progress").removeClass("fa fa-spinner fa-pulse");
|
||||
if (data.result && data.result === 'ok') {
|
||||
ajaxCall("/api/diagnostics/ping/start/" + data.uuid, {}, function(){
|
||||
$("#ping_jobs_tab").click();
|
||||
$("#grid-jobs").bootgrid("reload");
|
||||
});
|
||||
}
|
||||
}
|
||||
saveFormToEndpoint("/api/diagnostics/ping/set", 'frm_PingSettings', callb, false, callb);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" href="#ping">{{ lang._('Ping') }}</a></li>
|
||||
<li><a data-toggle="tab" id="ping_jobs_tab" href="#ping_jobs">{{ lang._('Jobs') }}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content content-box">
|
||||
<div id="ping" class="tab-pane fade in active">
|
||||
<div id="ping">
|
||||
{{ partial("layout_partials/base_form",['fields':pingForm,'id':'frm_PingSettings', 'apply_btn_id':'btn_start_new'])}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="ping_jobs" class="tab-pane fade in">
|
||||
<table id="grid-jobs" class="table table-condensed table-hover table-striped table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="status" data-width="2em" data-sortable="false" data-formatter="status"> </th>
|
||||
<th data-column-id="id" data-type="string" data-sortable="false" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
|
||||
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
|
||||
<th data-column-id="hostname" data-type="string">{{ lang._('Hostname') }}</th>
|
||||
<th data-column-id="source_address" data-type="string">{{ lang._('Source') }}</th>
|
||||
<th data-column-id="send" data-width="6em" data-type="string">{{ lang._('Send') }}</th>
|
||||
<th data-column-id="received" data-width="6em" data-type="string">{{ lang._('Received') }}</th>
|
||||
<th data-column-id="min" data-width="6em" data-type="string">{{ lang._('Min') }}</th>
|
||||
<th data-column-id="max" data-width="6em" data-type="string">{{ lang._('Max') }}</th>
|
||||
<th data-column-id="avg" data-width="6em" data-type="string">{{ lang._('Avg') }}</th>
|
||||
<th data-column-id="loss" data-width="6em" data-type="string">{{ lang._('loss') }}</th>
|
||||
<th data-column-id="last_error" data-type="string">{{ lang._('Error') }}</th>
|
||||
<th data-column-id="commands" data-width="15em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
190
src/opnsense/scripts/interfaces/ping.py
Executable file
190
src/opnsense/scripts/interfaces/ping.py
Executable file
@ -0,0 +1,190 @@
|
||||
#!/usr/local/bin/python3
|
||||
|
||||
"""
|
||||
Copyright (c) 2023 Ad Schellevis <ad@opnsense.org>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
|
||||
TEMP_DIR = '/tmp/ping/'
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import glob
|
||||
import subprocess
|
||||
import os
|
||||
import ujson
|
||||
import sys
|
||||
sys.path.insert(0, "/usr/local/opnsense/site-python")
|
||||
from log_helper import reverse_log_reader
|
||||
|
||||
|
||||
def ping_pids(jobid):
|
||||
""" search for capture pids using output filename
|
||||
:param jobid: job uuid number
|
||||
:return: list of pids
|
||||
"""
|
||||
pids = []
|
||||
args = ['/bin/pgrep', '-af', "ping_%s" % jobid]
|
||||
for line in subprocess.run(args, capture_output=True, text=True).stdout.split():
|
||||
if line.isdigit():
|
||||
pids.append(line)
|
||||
return pids
|
||||
|
||||
|
||||
def load_settings(filename):
|
||||
try:
|
||||
return ujson.load(open(filename, 'r'))
|
||||
except ValueError:
|
||||
return {}
|
||||
|
||||
|
||||
def read_latest_stats(filename):
|
||||
result = {
|
||||
'loss': None,
|
||||
'send': None,
|
||||
'received': None,
|
||||
'min': None,
|
||||
'max': None,
|
||||
'avg': None,
|
||||
'std-dev': None,
|
||||
'last_error': None
|
||||
}
|
||||
next_break = False
|
||||
if os.path.isfile(filename):
|
||||
items = collections.deque(maxlen=5)
|
||||
for line in reverse_log_reader(filename):
|
||||
line = line['line']
|
||||
if line.startswith('ping:'):
|
||||
result['last_error'] = line[6:].strip()
|
||||
if next_break:
|
||||
break
|
||||
items.append(line)
|
||||
if line.endswith('packet loss'):
|
||||
if next_break:
|
||||
break
|
||||
# IPv6
|
||||
for item in items:
|
||||
parts = item.split()
|
||||
if item.endswith('packet loss') and len(parts) >=6:
|
||||
result['loss'] = parts[6]
|
||||
result['send'] = int(parts[0])
|
||||
result['received'] = int(parts[3])
|
||||
elif item.startswith('round-trip') and len(parts) >= 4 and parts[3].count('/') == 3:
|
||||
stats = parts[3].split('/')
|
||||
result['min'] = float(stats[0])
|
||||
result['avg'] = float(stats[1])
|
||||
result['max'] = float(stats[2])
|
||||
result['std-dev'] = float(stats[3])
|
||||
next_break=True
|
||||
elif line.find('packets received') > -1:
|
||||
if next_break:
|
||||
break
|
||||
# IPv4
|
||||
parts = items[-1].split()
|
||||
if parts[0].find('/') > 0:
|
||||
result['send'] = int(parts[0].split('/')[1])
|
||||
result['received'] = int(parts[0].split('/')[0])
|
||||
if len(parts) >= 12:
|
||||
if parts[5] == 'min':
|
||||
result['min'] = float(parts[4])
|
||||
if parts[8] == 'avg':
|
||||
result['avg'] = float(parts[7])
|
||||
if parts[11] == 'max':
|
||||
result['max'] = float(parts[10])
|
||||
if result['send']:
|
||||
loss = (result['send']-result['received']) / result['send'] * 100.0
|
||||
result['loss'] = "%0.2f %%" % loss
|
||||
next_break=True
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
result = dict()
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--job', help='job id', default=None)
|
||||
parser.add_argument('action', help='action to perfom', choices=['list', 'start', 'stop', 'remove', 'view'])
|
||||
cmd_args = parser.parse_args()
|
||||
|
||||
all_jobs = {}
|
||||
if os.path.exists(TEMP_DIR):
|
||||
for filename in glob.glob("%s*.json" % TEMP_DIR):
|
||||
all_jobs[os.path.basename(filename).split('.')[0]] = filename
|
||||
|
||||
if cmd_args.action == 'list':
|
||||
result['jobs'] = []
|
||||
result['status'] = 'ok'
|
||||
for jobid in all_jobs:
|
||||
this_pids = ping_pids(jobid)
|
||||
if len(this_pids) > 0:
|
||||
with open("%s.pid" % all_jobs[jobid][:-5], 'r') as f_in:
|
||||
subprocess.run(['kill', '-s', 'INFO', f_in.read().strip()])
|
||||
settings = load_settings(all_jobs[jobid])
|
||||
settings['id'] = jobid
|
||||
settings['status'] = "running" if len(this_pids) > 0 else "stopped"
|
||||
# merge stats
|
||||
settings.update(read_latest_stats("%s.log" % all_jobs[jobid][:-5]))
|
||||
result['jobs'].append(settings)
|
||||
elif cmd_args.action == 'start' and cmd_args.job in all_jobs:
|
||||
this_pids = ping_pids(cmd_args.job)
|
||||
if len(this_pids) > 0:
|
||||
result['status'] = 'failed'
|
||||
result['status_msg'] = 'already active (pids: %s)' % ','.join(this_pids)
|
||||
else:
|
||||
result['status'] = 'ok'
|
||||
settings = load_settings(all_jobs[cmd_args.job])
|
||||
log_target = "%s%s.log" % (TEMP_DIR, cmd_args.job)
|
||||
args = [
|
||||
'/usr/sbin/daemon',
|
||||
'-o', log_target,
|
||||
'-p', "%s%s.pid" % (TEMP_DIR, cmd_args.job),
|
||||
'-t', 'ping_%s' % cmd_args.job,
|
||||
'/sbin/ping',
|
||||
'-4' if settings.get('fam', 'ip') == 'ip' else '-6'
|
||||
]
|
||||
if settings.get('packetsize', '') != '':
|
||||
args.append('-s')
|
||||
args.append(settings['packetsize'])
|
||||
if settings.get('disable_frag', '0') == '1':
|
||||
args.append('-D')
|
||||
args.append(settings.get('hostname', ''))
|
||||
if os.path.isfile(log_target):
|
||||
os.remove(log_target)
|
||||
subprocess.run(args)
|
||||
elif cmd_args.action == 'stop' and cmd_args.job in all_jobs:
|
||||
result['status'] = 'ok'
|
||||
result['stopped_processes'] = 0
|
||||
for pid in ping_pids(cmd_args.job):
|
||||
subprocess.run(['kill', pid])
|
||||
result['stopped_processes'] += 1
|
||||
elif cmd_args.action == 'remove' and cmd_args.job in all_jobs:
|
||||
result['status'] = 'ok'
|
||||
result['stopped_processes'] = 0
|
||||
for pid in ping_pids(cmd_args.job):
|
||||
subprocess.run(['kill', pid])
|
||||
result['stopped_processes'] += 1
|
||||
for filename in glob.glob("%s%s*" % (TEMP_DIR, cmd_args.job)):
|
||||
os.remove(filename)
|
||||
|
||||
print (ujson.dumps(result))
|
||||
@ -237,3 +237,33 @@ command:/usr/local/opnsense/scripts/interfaces/macinfo.py
|
||||
parameters: %s
|
||||
type:script_output
|
||||
message:fetch mac info for %s
|
||||
|
||||
[ping.list]
|
||||
command:/usr/local/opnsense/scripts/interfaces/ping.py list
|
||||
parameters:
|
||||
type:script_output
|
||||
message:Show current ping jobs
|
||||
|
||||
[ping.start]
|
||||
command:/usr/local/opnsense/scripts/interfaces/ping.py
|
||||
parameters: --job %s start
|
||||
type:script_output
|
||||
message:Start ping jobid %s
|
||||
|
||||
[ping.stop]
|
||||
command:/usr/local/opnsense/scripts/interfaces/ping.py
|
||||
parameters: --job %s stop
|
||||
type:script_output
|
||||
message:Stop ping jobid %s
|
||||
|
||||
[ping.remove]
|
||||
command:/usr/local/opnsense/scripts/interfaces/ping.py
|
||||
parameters: --job %s remove
|
||||
type:script_output
|
||||
message:Remove ping jobid %s
|
||||
|
||||
[ping.view]
|
||||
command:/usr/local/opnsense/scripts/interfaces/ping.py
|
||||
parameters: --job %s --detail %s view
|
||||
type:script_output
|
||||
message:View ping jobid %s (%s)
|
||||
|
||||
@ -1,157 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Franco Fichtner <franco@opnsense.org>
|
||||
* Copyright (C) 2016 Deciso B.V.
|
||||
* Copyright (C) 2003-2005 Bob Zoller <bob@kludgebox.com>
|
||||
* Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
require_once("guiconfig.inc");
|
||||
require_once("system.inc");
|
||||
require_once("interfaces.inc");
|
||||
|
||||
$cmd_output = false;
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// set form defaults
|
||||
$pconfig = array();
|
||||
$pconfig['count'] = isset($_GET['count']) ? $_GET['count'] : 3;
|
||||
$pconfig['host'] = isset($_GET['host']) ? $_GET['host'] : null;
|
||||
$pconfig['interface'] = isset($_GET['interface']) ? $_GET['interface'] : null;
|
||||
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// validate formdata and schedule action
|
||||
$pconfig = $_POST;
|
||||
$input_errors = array();
|
||||
/* input validation */
|
||||
$reqdfields = explode(" ", "host count");
|
||||
$reqdfieldsn = array(gettext("Host"),gettext("Count"));
|
||||
do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors);
|
||||
|
||||
if (count($input_errors) == 0) {
|
||||
$args = [];
|
||||
switch ($pconfig['ipproto']) {
|
||||
case 'ipv6':
|
||||
list ($ifaddr) = interfaces_primary_address6($pconfig['interface']);
|
||||
$args[] = '-6';
|
||||
break;
|
||||
case 'ipv6-ll':
|
||||
$args[] = '-6';
|
||||
list ($ifaddr) = interfaces_scoped_address6($pconfig['interface']);
|
||||
break;
|
||||
default:
|
||||
$args[] = '-4';
|
||||
list ($ifaddr) = interfaces_primary_address($pconfig['interface']);
|
||||
break;
|
||||
}
|
||||
if (!empty($ifaddr)) {
|
||||
$args[] = exec_safe('-S %s ', $ifaddr);
|
||||
}
|
||||
$args[] = exec_safe('-c %s %s', [$pconfig['count'], $pconfig['host']]);
|
||||
// execute ping command and catch both stdout and stderr
|
||||
$cmd_action = '/sbin/ping ' . implode(' ', $args);
|
||||
$process = proc_open($cmd_action, array(array("pipe", "r"), array("pipe", "w"), array("pipe", "w")), $pipes);
|
||||
if (is_resource($process)) {
|
||||
$cmd_output = "# $cmd_action\n";
|
||||
$cmd_output .= stream_get_contents($pipes[1]);
|
||||
$cmd_output .= stream_get_contents($pipes[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
legacy_html_escape_form_data($pconfig);
|
||||
include("head.inc"); ?>
|
||||
<body>
|
||||
<?php include("fbegin.inc"); ?>
|
||||
|
||||
<section class="page-content-main">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<section class="col-xs-12">
|
||||
<?php if (isset($input_errors) && count($input_errors) > 0) print_input_errors($input_errors); ?>
|
||||
<div class="content-box">
|
||||
<form method="post" name="iform" id="iform">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped __nomb">
|
||||
<tr>
|
||||
<td><?=gettext("Host"); ?></td>
|
||||
<td><input name="host" type="text" value="<?=$pconfig['host'];?>" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?=gettext("IP Protocol"); ?></td>
|
||||
<td>
|
||||
<select name="ipproto" class="selectpicker">
|
||||
<option value="ipv4" <?=$pconfig['ipproto'] == "ipv4" ? "selected=\"selected\"" : "";?>><?= gettext('IPv4') ?></option>
|
||||
<option value="ipv6" <?=$pconfig['ipproto'] == "ipv6" ? "selected=\"selected\"" : "";?>><?= gettext('IPv6') ?></option>
|
||||
<option value="ipv6-ll" <?=$pconfig['ipproto'] == "ipv6-ll" ? "selected=\"selected\"" : "";?>><?= gettext('IPv6 Link-Local') ?></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?=gettext("Source Address"); ?></td>
|
||||
<td>
|
||||
<select name="interface" class="selectpicker">
|
||||
<option value=""><?= gettext('Default') ?></option>
|
||||
<?php foreach (get_configured_interface_with_descr() as $ifname => $ifdescr): ?>
|
||||
<option value="<?= html_safe($ifname) ?>" <?=!link_interface_to_bridge($ifname) && $ifname == $pconfig['interface'] ? 'selected="selected"' : '' ?>>
|
||||
<?= htmlspecialchars($ifdescr) ?>
|
||||
</option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?= gettext("Count"); ?></td>
|
||||
<td>
|
||||
<select name="count" class="selectpicker" id="count">
|
||||
<?php
|
||||
for ($i = 1; $i <= 10; $i++): ?>
|
||||
<option value="<?=$i;?>" <?=$i == $pconfig['count'] ? "selected=\"selected\"" : ""; ?>>
|
||||
<?=$i;?>
|
||||
</option>
|
||||
<?php
|
||||
endfor; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><button name="submit" type="submit" class="btn btn-primary" value="yes"><?= html_safe(gettext('Ping')) ?></button></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
<?php if (!empty($cmd_output)): ?>
|
||||
<section class="col-xs-12">
|
||||
<pre><?=htmlspecialchars($cmd_output);?></pre>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php
|
||||
|
||||
include('foot.inc');
|
||||
Loading…
x
Reference in New Issue
Block a user