Improve our parent/children identification with points, fix our testing that was failing with NULLs and asserted out. Added zone:check so that's its easier to identify parent for FTNs

This commit is contained in:
Deon George 2023-12-11 18:31:38 +11:00
parent 247cf614f3
commit 541f612446
4 changed files with 229 additions and 40 deletions

View File

@ -0,0 +1,100 @@
<?php
namespace App\Console\Commands;
use App\Models\{Domain};
use App\Models\Address;
use Illuminate\Console\Command;
class ZoneCheck extends Command
{
protected $signature = 'zone:check'
.' {domain : Domain Name}'
.' {--Z|zone= : Zone}';
protected $description = 'Check that the addresses in a zone are configured correctly';
public function handle()
{
$do = Domain::where('name',$this->argument('domain'))->singleOrFail();
foreach ($do->zones as $zo) {
if ($this->option('zone') && ($this->option('zone') != $zo->zone_id))
continue;
$this->warn('Zone: '.$zo->zone_id);
$this->info(sprintf('- Our address(es): %s',our_address($zo)->pluck('ftn4d')->join(',')));
$this->table(['id','ftn','role','parent','region_id','host_id','hub_id','system','notes'],$zo->addresses()->FTNorder()->active()->with(['system'])->get()->transform(function($item) {
return [
'id'=>$item->id,
'ftn'=>$item->ftn4d,
'role'=>$item->role_name,
'parent'=>$item->parent()?->ftn4d,
'region_id'=>$item->region_id,
'host_id'=>$item->host_id,
'hub_id'=>$item->hub_id,
'system'=>$item->system->name,
'notes'=>$this->check($item),
];
}));
}
return Command::SUCCESS;
}
/**
* Check that an address is defined correctly
*
* @param Address $ao
* @return string
*/
private function check(Address $ao): string
{
// ZC address
if ($ao->role === Address::NODE_ZC) {
if (($ao->region_id === 0) && ($ao->host_id === 0) && ($ao->node_id === 0) && is_null($ao->hub_id) && ($ao->point_id === 0))
return 'OK';
else
return 'INVALID ZC address';
}
// RC address
if ($ao->role === Address::NODE_RC) {
if ($ao->region_id && ($ao->region_id === $ao->host_id) && ($ao->node_id === 0) && is_null($ao->hub_id) && ($ao->point_id === 0))
return 'OK';
else
return 'INVALID RC address';
}
// NC address
if ($ao->role === Address::NODE_NC) {
if (($ao->node_id !== 0) && is_null($ao->hub_id) && ($ao->point_id === 0))
return 'OK';
else
return 'INVALID NC address';
}
// HUB address
if ($ao->role === Address::NODE_HC) {
if (($ao->node_id !== 0) && is_null($ao->hub_id) && ($ao->point_id === 0))
return 'OK';
else
return 'INVALID HUB address';
}
// POINT address
if ($ao->role === Address::NODE_POINT) {
if ($ao->point_id !== 0)
return 'OK';
else
return 'INVALID POINT address';
}
if ($ao->region_id && ($ao->host_id === 0))
return 'INVALID REGION NODE';
return '';
}
}

View File

@ -150,7 +150,6 @@ class Address extends Model
/** /**
* Find children dependent on this record * Find children dependent on this record
* @todo If bosses are defined here, and points, then mail to a point goes to it's boss
*/ */
public function children() public function children()
{ {
@ -194,9 +193,18 @@ class Address extends Model
case self::NODE_PVT: case self::NODE_PVT:
case self::NODE_HOLD: case self::NODE_HOLD:
case self::NODE_DOWN: case self::NODE_DOWN:
case self::NODE_POINT:
case self::NODE_UNKNOWN: case self::NODE_UNKNOWN:
// Nodes dont have children, but must return a relationship instance // Identify our children.
$children = self::select('addresses.*')
->where('zone_id',$this->zone_id)
->where('region_id',$this->region_id)
->where('host_id',$this->host_id)
->where('node_id',$this->node_id)
->where('point_id','<>',0);
break;
case self::NODE_POINT:
// Points dont have children, but must return a relationship instance
return $this->hasOne(self::class,NULL,'void'); return $this->hasOne(self::class,NULL,'void');
default: default:
@ -298,14 +306,17 @@ class Address extends Model
* *
* @return Address|null * @return Address|null
* @throws \Exception * @throws \Exception
* @todo Dont include points in this
*/ */
public function parent(): ?Address public function parent(): ?Address
{ {
// If we are have session password, then we dont have a parent // If we have session password, then we are the parent
if ($this->session('sespass')) if ($this->session('sespass'))
return $this; return $this;
// If it is our address
if (our_address()->contains($this))
return NULL;
switch ($this->role) { switch ($this->role) {
// ZCs dont have parents, but we may have a default // ZCs dont have parents, but we may have a default
case self::NODE_ZC: case self::NODE_ZC:
@ -326,15 +337,15 @@ class Address extends Model
// Hosts // Hosts
case self::NODE_NC: case self::NODE_NC:
// See if we have a RC // See if we have an RC
$parent = self::where('zone_id',$this->zone_id) $parent = self::where('zone_id',$this->zone_id)
->where('region_id',$this->region_id) ->where('region_id',$this->region_id)
->where('host_id',0) ->where('host_id',$this->region_id)
->where('node_id',0) ->where('node_id',0)
->single(); ->single();
if (! $parent) { if (! $parent) {
// See if we have a RC // See if we have an ZC
$parent = self::where('zone_id',$this->zone_id) $parent = self::where('zone_id',$this->zone_id)
->where('region_id',0) ->where('region_id',0)
->where('host_id',0) ->where('host_id',0)
@ -351,22 +362,27 @@ class Address extends Model
case self::NODE_PVT: case self::NODE_PVT:
case self::NODE_HOLD: case self::NODE_HOLD:
case self::NODE_DOWN: case self::NODE_DOWN:
case self::NODE_UNKNOWN:
// If we are a child of a hub, then check our hub // If we are a child of a hub, then check our hub
$parent = ($this->hub_id $parent = ($this->hub_id
? self::where('id',$this->hub_id) ? self::where('id',$this->hub_id)
: self::where('zone_id',$this->zone_id) : self::where('zone_id',$this->zone_id)
->where('region_id',$this->region_id) ->where('region_id',$this->region_id)
->where('host_id',$this->host_id) ->where('host_id',$this->host_id)
->where('node_id',0) ->where('role','<',self::NODE_HC))
->where('role','<',$this->role)) ->active()
->single(); ->single();
break; break;
case self::NODE_UNKNOWN:
case self::NODE_POINT: case self::NODE_POINT:
// @todo Points - if the boss is defined, we should return it. $parent = self::where('zone_id',$this->zone_id)
return NULL; ->where('region_id',$this->region_id)
->where('host_id',$this->host_id)
->where('node_id',$this->node_id)
->where('point_id',0)
->active()
->single();
break;
default: default:
throw new \Exception(sprintf('Unknown role: %s (%d)',serialize($this->role),$this->id)); throw new \Exception(sprintf('Unknown role: %s (%d)',serialize($this->role),$this->id));

View File

@ -190,7 +190,7 @@ class TestNodeHierarchy extends Seeder
'validated'=>TRUE, 'validated'=>TRUE,
'region_id'=>$rid, 'region_id'=>$rid,
'host_id'=>$hostid, 'host_id'=>$hostid,
'node_id'=>0, 'node_id'=>7,
'point_id'=>0, 'point_id'=>0,
'system_id'=>$so->id, 'system_id'=>$so->id,
'role'=>Address::NODE_NC, 'role'=>Address::NODE_NC,

View File

@ -6,8 +6,48 @@ use Illuminate\Database\Eloquent\Collection;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase; use Tests\TestCase;
use App\Models\{Address,Domain,Setup}; use App\Models\{Address,Domain,Setup,System};
/**
* Here we test mail routing.
*
* - COMMON to all routing nodes
* + We have RCs defined - everything for a node in the Region goes to an RC, unless RC's node defined locally, or nodes assigned to ZC
* ie: region_id is the same, find the NC/HUB/NODE address for the system_id that has been assigned 0/0 in the same region.
* nodes assigned to ZC have region_id = 0. Nodes with hub_id = NULL and host_id=0 are RC nodes.
* + We have NCs defined - everything under the Host goes to the NC, unless NC's nodes defined locally or nodes assigned to the RC
* ie: node_id defines the NCs responsible. Nodes with hub_id = NULL are an NC node.
* + We have HUBs defined - everything under the Hub goes to the HUB, unless HUB's nodes defined locally
* ie: hub_id defines who the hub is.
* + We have NODES defined - mail collected locally, including mail for it's point
*
* - Routing scenarios:
* - We are ZC for a domain
* + See COMMON
* + Everything else is collected locally
*
* - We are RC for a domain
* + See COMMON
* + Any nodes in our RC not defined with an NC is collected locally, including points
* + Everything else is sent to the ZC (our uplink)
*
* - We are NC for a domain
* + See COMMON
* + Any nodes in our NC not defined with a HUB is collected locally, including points
* + Everything else is sent to the RC or ZC (our uplink)
*
* - We are a Hub for a domain
* + See COMMON
* + Any nodes in our HUB is collected locally, including points
* + Everything else is sent to the NC or RC (our uplink)
*
* - We are a Node in a domain
* + See COMMON
* + Everything else is sent to the HUB or Host or RC (our uplink)
*
* @see Address::parent()
* @see Address::children()
*/
class RoutingTest extends TestCase class RoutingTest extends TestCase
{ {
use DatabaseTransactions; use DatabaseTransactions;
@ -38,7 +78,7 @@ class RoutingTest extends TestCase
private function session_nc(): void private function session_nc(): void
{ {
// Add session info, and we have 51 children // Add session info, and we have 51 children
$ao = Address::findFTN('100:10/0@a'); $ao = Address::findFTN('100:10/7@a');
$ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]);
} }
@ -93,8 +133,17 @@ class RoutingTest extends TestCase
$this->assertEquals('101:0/0.0@a',$ao->parent()->ftn); $this->assertEquals('101:0/0.0@a',$ao->parent()->ftn);
} }
// Get a list of active addresses in a Region
public function test_rc_session_children()
{
$this->session_rc();
$ao = Address::findFTN('100:1/0@a');
$this->assertEquals(185,$ao->children()->count());
}
// An RCs node still collects mail from the RC // An RCs node still collects mail from the RC
public function test_zc_rc_node_rc() public function test_rc_node_rc()
{ {
$this->session_rc(); $this->session_rc();
@ -105,58 +154,45 @@ class RoutingTest extends TestCase
} }
// An NC collects mail for its children // An NC collects mail for its children
public function test_zc_nc_node_rc() public function test_rc_nc_node_rc()
{ {
return $this->assertTrue(TRUE);
$this->session_rc(); $this->session_rc();
// A NCs parent should still be the RC // A NCs parent should still be the RC
$ao = Address::findFTN('100:10/0@a'); $ao = Address::findFTN('100:10/7@a');
$this->assertEquals($ao->role,Address::NODE_NC); $this->assertEquals($ao->role,Address::NODE_NC);
$this->assertEquals('100:1/0.0@a',$ao->parent()->ftn); // @todo fails, returning NULL? $this->assertEquals('100:1/0.0@a',$ao->parent()->ftn);
} }
// A Hub still collects mail from NC // A Hub still collects mail from NC
public function test_zc_hub_node_nc() public function test_rc_hub_node_nc()
{ {
return $this->assertTrue(TRUE);
$this->session_rc(); $this->session_rc();
// A Hubs parent should still be the NC // A Hubs parent should still be the NC
$ao = Address::findFTN('100:10/20.0@a'); $ao = Address::findFTN('100:10/20.0@a');
$this->assertEquals($ao->role,Address::NODE_HC); $this->assertEquals($ao->role,Address::NODE_HC);
$this->assertEquals('100:1/0.0@a',$ao->parent()->ftn); // @todo fails, returning NULL? $this->assertEquals('100:1/0.0@a',$ao->parent()->ftn);
} }
// A Hub's node still collects mail from Hub // A Hub's node still collects mail from Hub
public function test_zc_hub_node_hub() public function test_rc_hub_node_hub()
{ {
return $this->assertTrue(TRUE);
$this->session_rc(); $this->session_rc();
// A Hubs node should still be the Hub // A Hubs node should still be the Hub
$ao = Address::findFTN('100:10/22.0@a'); $ao = Address::findFTN('100:10/22.0@a');
$this->assertEquals($ao->role,Address::NODE_ACTIVE); $this->assertEquals($ao->role,Address::NODE_ACTIVE);
$this->assertEquals('100:1/0.0@a',$ao->parent()->ftn); // @todo fails, returning NULL? $this->assertEquals('100:1/0.0@a',$ao->parent()->ftn);
}
// When we have an RC with session details, we route to all its children
public function test_rc_session_children()
{
$this->session_rc();
$ao = Address::findFTN('100:1/0@a');
$this->assertCount(185,$ao->children);
} }
// An RCs parent is us even if we have session details for another RC // An RCs parent is us even if we have session details for another RC
public function test_rc_parent() public function test_rc_parent()
{ {
return $this->assertTrue(TRUE);
$this->session_rc(); $this->session_rc();
$ao = Address::findFTN('100:2/0@a'); $ao = Address::findFTN('100:2/0@a');
$this->assertEquals('100:0/0.0@a',$ao->parent()->ftn); // @todo fails, returning NULL? $this->assertNull($ao->parent()?->ftn);
} }
// If we also have session details for an NC, then there are less RC nodes // If we also have session details for an NC, then there are less RC nodes
@ -164,7 +200,7 @@ class RoutingTest extends TestCase
{ {
$this->session_rc(); $this->session_rc();
$ao = Address::findFTN('100:10/0@a'); $ao = Address::findFTN('100:10/7@a');
$ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]);
$ao = Address::findFTN('100:1/0@a'); $ao = Address::findFTN('100:1/0@a');
@ -185,6 +221,7 @@ class RoutingTest extends TestCase
$ao = Address::findFTN('100:10/22@a'); $ao = Address::findFTN('100:10/22@a');
$this->assertEquals('100:10/20.0@a',$ao->parent()->ftn); $this->assertEquals('100:10/20.0@a',$ao->parent()->ftn);
} }
// If we also have session details for an Hub, then there are less RC nodes // If we also have session details for an Hub, then there are less RC nodes
public function test_rc_hub_session_child() public function test_rc_hub_session_child()
{ {
@ -193,4 +230,40 @@ class RoutingTest extends TestCase
$ao = Address::findFTN('100:10/22@a'); $ao = Address::findFTN('100:10/22@a');
$this->assertEquals('100:10/20.0@a',$ao->parent()->ftn); $this->assertEquals('100:10/20.0@a',$ao->parent()->ftn);
} }
// When we have an RC with session details, we route to all its children
public function test_nc_session_children()
{
$this->session_nc();
$ao = Address::findFTN('100:10/7@a');
$this->assertCount(35,$ao->children);
}
// A points parent is the node, if we have traffic for a point and we have session details for the node
public function test_point_session_node()
{
//$this->session_hub();
$so = new System;
$so->name = 'Point System';
$so->sysop = 'Point Sysop';
$so->location = 'Melbourne, AU';
$so->active = TRUE;
$so->save();
// Create a child
$ao = Address::createFTN('100:10/21.2@a',$so);
$ao->region_id = 1; // @todo This should be worked out from the parent node (if exists), or another node on the same host
$ao->save();
$ao = Address::findFTN('100:10/21.0@a');
$ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]);
$ao = Address::findFTN('100:10/21.2@a');
$this->assertEquals('100:10/21.0@a',$ao->parent()?->ftn);
$ao = Address::findFTN('100:10/21@a');
$this->assertCount(1,$ao->children);
}
} }