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
* @todo If bosses are defined here, and points, then mail to a point goes to it's boss
*/
public function children()
{
@ -194,9 +193,18 @@ class Address extends Model
case self::NODE_PVT:
case self::NODE_HOLD:
case self::NODE_DOWN:
case self::NODE_POINT:
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');
default:
@ -298,14 +306,17 @@ class Address extends Model
*
* @return Address|null
* @throws \Exception
* @todo Dont include points in this
*/
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'))
return $this;
// If it is our address
if (our_address()->contains($this))
return NULL;
switch ($this->role) {
// ZCs dont have parents, but we may have a default
case self::NODE_ZC:
@ -326,15 +337,15 @@ class Address extends Model
// Hosts
case self::NODE_NC:
// See if we have a RC
// See if we have an RC
$parent = self::where('zone_id',$this->zone_id)
->where('region_id',$this->region_id)
->where('host_id',0)
->where('host_id',$this->region_id)
->where('node_id',0)
->single();
if (! $parent) {
// See if we have a RC
// See if we have an ZC
$parent = self::where('zone_id',$this->zone_id)
->where('region_id',0)
->where('host_id',0)
@ -351,22 +362,27 @@ class Address extends Model
case self::NODE_PVT:
case self::NODE_HOLD:
case self::NODE_DOWN:
case self::NODE_UNKNOWN:
// If we are a child of a hub, then check our hub
$parent = ($this->hub_id
? self::where('id',$this->hub_id)
: self::where('zone_id',$this->zone_id)
->where('region_id',$this->region_id)
->where('host_id',$this->host_id)
->where('node_id',0)
->where('role','<',$this->role))
->where('role','<',self::NODE_HC))
->active()
->single();
break;
case self::NODE_UNKNOWN:
case self::NODE_POINT:
// @todo Points - if the boss is defined, we should return it.
return NULL;
$parent = self::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)
->active()
->single();
break;
default:
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,
'region_id'=>$rid,
'host_id'=>$hostid,
'node_id'=>0,
'node_id'=>7,
'point_id'=>0,
'system_id'=>$so->id,
'role'=>Address::NODE_NC,

View File

@ -6,8 +6,48 @@ use Illuminate\Database\Eloquent\Collection;
use Illuminate\Foundation\Testing\DatabaseTransactions;
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
{
use DatabaseTransactions;
@ -38,7 +78,7 @@ class RoutingTest extends TestCase
private function session_nc(): void
{
// 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']]);
}
@ -93,8 +133,17 @@ class RoutingTest extends TestCase
$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
public function test_zc_rc_node_rc()
public function test_rc_node_rc()
{
$this->session_rc();
@ -105,58 +154,45 @@ class RoutingTest extends TestCase
}
// 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();
// 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('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
public function test_zc_hub_node_nc()
public function test_rc_hub_node_nc()
{
return $this->assertTrue(TRUE);
$this->session_rc();
// A Hubs parent should still be the NC
$ao = Address::findFTN('100:10/20.0@a');
$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
public function test_zc_hub_node_hub()
public function test_rc_hub_node_hub()
{
return $this->assertTrue(TRUE);
$this->session_rc();
// A Hubs node should still be the Hub
$ao = Address::findFTN('100:10/22.0@a');
$this->assertEquals($ao->role,Address::NODE_ACTIVE);
$this->assertEquals('100:1/0.0@a',$ao->parent()->ftn); // @todo fails, returning NULL?
}
// 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);
$this->assertEquals('100:1/0.0@a',$ao->parent()->ftn);
}
// An RCs parent is us even if we have session details for another RC
public function test_rc_parent()
{
return $this->assertTrue(TRUE);
$this->session_rc();
$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
@ -164,7 +200,7 @@ class RoutingTest extends TestCase
{
$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 = Address::findFTN('100:1/0@a');
@ -185,6 +221,7 @@ class RoutingTest extends TestCase
$ao = Address::findFTN('100:10/22@a');
$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
public function test_rc_hub_session_child()
{
@ -193,4 +230,40 @@ class RoutingTest extends TestCase
$ao = Address::findFTN('100:10/22@a');
$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);
}
}