Delete ports via eloquent event (#11354)

* Delete ports via eloquent event
Chunk delete during purge all operations so we don't use too much memory.

* protect against missing device

* fix whitespace

* fetch less from the database when deleting a device's ports
fix output
This commit is contained in:
Tony Murray
2020-04-16 09:19:58 -05:00
committed by GitHub
parent e2eea17ee8
commit e4b49f59f6
13 changed files with 212 additions and 57 deletions

View File

@@ -269,6 +269,18 @@ class Rrd extends BaseDatastore
return join('/', [$pmxcdir, self::safeName($vmid . '_netif_' . $vmport . '.rrd')]);
}
/**
* Get the name of the port rrd file. For alternate rrd, specify the suffix.
*
* @param int $port_id
* @param string $suffix
* @return string
*/
public function portName($port_id, $suffix = null)
{
return "port-id$port_id" . (empty($suffix) ? '' : '-' . $suffix);
}
/**
* rename an rrdfile, can only be done on the LibreNMS server hosting the rrd files
*
@@ -433,6 +445,23 @@ class Rrd extends BaseDatastore
}
}
/**
* Remove RRD file(s). Use with care as this permanently deletes rrd data.
* @param string $hostname rrd subfolder (hostname)
* @param string $prefix start of rrd file name all files matching will be deleted
*/
public function purge($hostname, $prefix)
{
if (empty($hostname)) {
Log::error("Could not purge rrd $prefix, empty hostname");
return;
}
foreach (glob($this->name($hostname, $prefix, '*.rrd')) as $rrd) {
unlink($rrd);
}
}
/**
* Generates a graph file at $graph_file using $options
* Opens its own rrdtool pipe.

View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
class MacAccounting extends PortRelatedModel
{
protected $table = 'mac_accounting';
protected $primaryKey = 'ma_id';
public $timestamps = false;
}

View File

@@ -4,6 +4,7 @@ namespace App\Models;
use DB;
use Illuminate\Database\Eloquent\Builder;
use LibreNMS\Data\Store\Rrd;
use LibreNMS\Util\Rewrite;
use Permissions;
@@ -12,7 +13,40 @@ class Port extends DeviceRelatedModel
public $timestamps = false;
protected $primaryKey = 'port_id';
// ---- Helper Functions ----
/**
* Initialize this class
*/
public static function boot()
{
parent::boot();
static::deleting(function (Port $port) {
// delete related data
$port->adsl()->delete();
$port->fdbEntries()->delete();
$port->ipv4()->delete();
$port->ipv6()->delete();
$port->macAccounting()->delete();
$port->macs()->delete();
$port->nac()->delete();
$port->ospfNeighbors()->delete();
$port->ospfPorts()->delete();
$port->pseudowires()->delete();
$port->statistics()->delete();
$port->stp()->delete();
$port->vlans()->delete();
// dont have relationships yet
DB::table('juniAtmVp')->where('port_id', $port->port_id)->delete();
DB::table('ports_perms')->where('port_id', $port->port_id)->delete();
DB::table('links')->where('local_port_id', $port->port_id)->orWhere('remote_port_id', $port->port_id)->delete();
DB::table('ports_stack')->where('port_id_low', $port->port_id)->orWhere('port_id_high', $port->port_id)->delete();
\Rrd::purge(optional($port->device)->hostname, \Rrd::portName($port->port_id)); // purge all port rrd files
});
}
// ---- Helper Functions ----
/**
* Returns a human readable label for this port
@@ -216,6 +250,11 @@ class Port extends DeviceRelatedModel
// ---- Define Relationships ----
public function adsl()
{
return $this->hasMany(PortAdsl::class, 'port_id');
}
public function events()
{
return $this->morphMany(Eventlog::class, 'events', 'type', 'reference');
@@ -226,12 +265,6 @@ class Port extends DeviceRelatedModel
return $this->hasMany('App\Models\PortsFdb', 'port_id', 'port_id');
}
public function users()
{
// FIXME does not include global read
return $this->belongsToMany('App\Models\User', 'ports_perms', 'port_id', 'user_id');
}
public function ipv4()
{
return $this->hasMany('App\Models\Ipv4Address', 'port_id');
@@ -241,4 +274,55 @@ class Port extends DeviceRelatedModel
{
return $this->hasMany('App\Models\Ipv6Address', 'port_id');
}
public function macAccounting()
{
return $this->hasMany(MacAccounting::class, 'port_id');
}
public function macs()
{
return $this->hasMany(Ipv4Mac::class, 'port_id');
}
public function nac()
{
return $this->hasMany(PortsNac::class, 'port_id');
}
public function ospfNeighbors()
{
return $this->hasMany(OspfNbr::class, 'port_id');
}
public function ospfPorts()
{
return $this->hasMany(OspfPort::class, 'port_id');
}
public function pseudowires()
{
return $this->hasMany(Pseudowire::class, 'port_id');
}
public function statistics()
{
return $this->hasMany(PortStatistic::class, 'port_id');
}
public function stp()
{
return $this->hasMany(PortStp::class, 'port_id');
}
public function users()
{
// FIXME does not include global read
return $this->belongsToMany('App\Models\User', 'ports_perms', 'port_id', 'user_id');
}
public function vlans()
{
return $this->hasMany(PortVlan::class, 'port_id');
}
}

10
app/Models/PortAdsl.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
class PortAdsl extends PortRelatedModel
{
protected $table = 'ports_adsl';
protected $primaryKey = 'port_id';
public $timestamps = false;
}

View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
class PortStatistic extends PortRelatedModel
{
protected $table = 'ports_statistics';
protected $primaryKey = 'port_id';
public $timestamps = false;
}

10
app/Models/PortStp.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
class PortStp extends PortRelatedModel
{
protected $table = 'ports_stp';
protected $primaryKey = 'port_stp_id';
public $timestamps = false;
}

10
app/Models/PortVlan.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
class PortVlan extends PortRelatedModel
{
protected $table = 'ports_vlans';
protected $primaryKey = 'port_vlan_id';
public $timestamps = false;
}

View File

@@ -135,10 +135,13 @@ if ($options['f'] === 'ports_purge') {
$ports_purge = Config::get('ports_purge');
if ($ports_purge) {
$interfaces = dbFetchRows('SELECT * from `ports` AS P, `devices` AS D WHERE `deleted` = 1 AND D.device_id = P.device_id');
foreach ($interfaces as $interface) {
delete_port($interface['port_id']);
}
\App\Models\Port::query()->with(['device' => function ($query) {
$query->select('device_id', 'hostname');
}])->isDeleted()->chunk(100, function ($ports) {
foreach ($ports as $port) {
$port->delete();
}
});
echo "All deleted ports now purged\n";
}
} catch (LockException $e) {

View File

@@ -176,22 +176,6 @@ function print_message($text)
}
}
function delete_port($int_id)
{
$interface = dbFetchRow("SELECT * FROM `ports` AS P, `devices` AS D WHERE P.port_id = ? AND D.device_id = P.device_id", array($int_id));
$interface_tables = array('ipv4_addresses', 'ipv4_mac', 'ipv6_addresses', 'juniAtmVp', 'mac_accounting', 'ospf_nbrs', 'ospf_ports', 'ports', 'ports_adsl', 'ports_perms', 'ports_statistics', 'ports_stp', 'ports_vlans', 'pseudowires');
foreach ($interface_tables as $table) {
dbDelete($table, "`port_id` = ?", array($int_id));
}
dbDelete('links', "`local_port_id` = ? OR `remote_port_id` = ?", array($int_id, $int_id));
dbDelete('ports_stack', "`port_id_low` = ? OR `port_id_high` = ?", array($int_id, $int_id));
unlink(get_port_rrdfile_path($interface['hostname'], $interface['port_id']));
}
function get_sensor_rrd($device, $sensor)
{
return rrd_name($device['hostname'], get_sensor_rrd_name($device, $sensor));
@@ -209,11 +193,7 @@ function get_sensor_rrd_name($device, $sensor)
function getPortRrdName($port_id, $suffix = '')
{
if (!empty($suffix)) {
$suffix = '-' . $suffix;
}
return "port-id$port_id$suffix";
return Rrd::portName($port_id, $suffix);
}
function get_port_rrdfile_path($hostname, $port_id, $suffix = '')

View File

@@ -453,12 +453,16 @@ function delete_device($id)
dbQuery("DELETE `ipv4_addresses` FROM `ipv4_addresses` INNER JOIN `ports` ON `ports`.`port_id`=`ipv4_addresses`.`port_id` WHERE `device_id`=?", array($id));
dbQuery("DELETE `ipv6_addresses` FROM `ipv6_addresses` INNER JOIN `ports` ON `ports`.`port_id`=`ipv6_addresses`.`port_id` WHERE `device_id`=?", array($id));
foreach (dbFetch("SELECT * FROM `ports` WHERE `device_id` = ?", array($id)) as $int_data) {
$int_if = $int_data['ifDescr'];
$int_id = $int_data['port_id'];
delete_port($int_id);
$ret .= "Removed interface $int_id ($int_if)\n";
}
\App\Models\Port::where('device_id', $id)
->with('device')
->select(['port_id', 'device_id', 'ifIndex', 'ifName', 'ifAlias', 'ifDescr'])
->chunk(100, function ($ports) use (&$ret) {
foreach ($ports as $port) {
$port->delete();
$ret .= "Removed interface $port->port_id (" . $port->getLabel() . ")\n";
}
});
// Remove sensors manually due to constraints
foreach (dbFetchRows("SELECT * FROM `sensors` WHERE `device_id` = ?", array($id)) as $sensor) {

View File

@@ -14,6 +14,7 @@
*/
use App\Models\Port;
use Illuminate\Database\Eloquent\ModelNotFoundException;
$pagetitle[] = "Ports";
@@ -388,18 +389,18 @@ foreach ($vars as $var => $value) {
break;
case 'purge':
if ($vars['purge'] === 'all') {
$interfaces = dbFetchRows('SELECT * from `ports` AS P, `devices` AS D WHERE `deleted` = 1 AND D.device_id = P.device_id');
foreach ($interfaces as $interface) {
$interface = cleanPort($interface);
if (port_permitted($interface['port_id'], $interface['device_id'])) {
delete_port($interface['port_id']);
Port::hasAccess(Auth::user())->with(['device' => function ($query) {
$query->select('device_id', 'hostname');
}])->isDeleted()->chunk(100, function ($ports) {
foreach ($ports as $port) {
$port->delete();
}
}
});
} else {
$interface = dbFetchRow('SELECT * from `ports` AS P, `devices` AS D WHERE `port_id` = ? AND D.device_id = P.device_id', array($vars['purge']));
$interface = cleanPort($interface);
if (port_permitted($interface['port_id'], $interface['device_id'])) {
delete_port($interface['port_id']);
try {
Port::hasAccess(Auth::user())->where('port_id', $vars['purge'])->firstOrFail()->delete();
} catch (ModelNotFoundException $e) {
echo "<div class='alert alert-danger'>Port ID {$vars['purge']} not found! Could not purge port.</div>";
}
}
break;

View File

@@ -1,7 +0,0 @@
<?php
foreach (dbFetchRows("SELECT * FROM `ports` WHERE `deleted` = '1'") as $port) {
echo "<div style='font-weight: bold;'>Deleting port ".$port['port_id'].' - '.$port['ifDescr'];
delete_port($port['port_id']);
echo '</div>';
}

View File

@@ -26,6 +26,9 @@
*
*/
use App\Models\Port;
use Illuminate\Database\Eloquent\ModelNotFoundException;
chdir(dirname($argv[0]));
$init_modules = array();
@@ -55,7 +58,11 @@ if (! $port_id && ! $port_id_file || ($port_id && $port_id_file)) {
// Purge single port
if ($port_id) {
delete_port($port_id);
try {
Port::findOrFail($port_id)->delete();
} catch (ModelNotFoundException $e) {
echo "Port ID $port_id not found!\n";
}
}
// Delete multiple ports
@@ -72,7 +79,11 @@ if ($port_id_file) {
}
while ($port_id = trim(fgets($fh))) {
delete_port($port_id);
try {
Port::findOrFail($port_id)->delete();
} catch (ModelNotFoundException $e) {
echo "Port ID $port_id not found!\n";
}
}
if ($fh != STDIN) {