Start working on an all in one command
This commit is contained in:
parent
5aa5d85f40
commit
2908793ce2
10 changed files with 355 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/vendor/
|
14
bin/pfatt
Executable file
14
bin/pfatt
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
// application.php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Pfatt\Commands\Monitor;
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
$application = new Application();
|
||||
$application->add(new Monitor());
|
||||
// ... register commands
|
||||
|
||||
$application->run();
|
37
composer.json
Normal file
37
composer.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "neclimdul/pfatt",
|
||||
"description": "Bridge tool for PHP stuff.",
|
||||
"type": "project",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Pfatt\\": "src/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "James Gilliland",
|
||||
"email": "neclimdul@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "~7.4",
|
||||
"psr/log": "^1.1",
|
||||
"symfony/console": "^5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"vimeo/psalm": "^4.22"
|
||||
},
|
||||
"scripts": {
|
||||
"psalm": "psalm",
|
||||
"phpstan": "phpstan",
|
||||
"static": [
|
||||
"@phpstan",
|
||||
"@psalm"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
3
phpstan-baseline.neon
Normal file
3
phpstan-baseline.neon
Normal file
|
@ -0,0 +1,3 @@
|
|||
parameters:
|
||||
ignoreErrors: []
|
||||
|
6
phpstan.neon.dist
Normal file
6
phpstan.neon.dist
Normal file
|
@ -0,0 +1,6 @@
|
|||
includes:
|
||||
- phpstan-baseline.neon
|
||||
parameters:
|
||||
level: 9
|
||||
paths:
|
||||
- src
|
15
psalm.xml
Normal file
15
psalm.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="1"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
</psalm>
|
61
src/Commands/Monitor.php
Normal file
61
src/Commands/Monitor.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pfatt\Commands;
|
||||
|
||||
use Pfatt\Config;
|
||||
use Pfatt\Logger;
|
||||
use Pfatt\NgController;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Logger\ConsoleLogger;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class Monitor extends Command
|
||||
{
|
||||
protected static $defaultName = 'monitor';
|
||||
protected static $defaultDescription = 'Monitor connection to trigger 5268AC updates';
|
||||
protected Config $config;
|
||||
protected Logger $logger;
|
||||
protected NgController $ngControl;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
// @todo Inject these. Maybe with a factory or container.
|
||||
$this->config = new Config('', '', '');
|
||||
$this->logger = new Logger('pfatt-5268AC');
|
||||
$this->ngControl = new NgController($this->logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->logger->setOutput($output);
|
||||
|
||||
$this->logger->info('Starting 5268AC ping monitor ...');
|
||||
register_shutdown_function(function () { $this->logger->info('Stopping 5268AC ping monitor ...'); });
|
||||
|
||||
while ($input->isInteractive()) {
|
||||
if ($this->ping()) {
|
||||
$this->ngControl->ngRmHook();
|
||||
}
|
||||
elseif (!$this->ngControl->ngIsConnected()) {
|
||||
$this->ngControl->ngConnect();
|
||||
}
|
||||
sleep(5);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function ping(): int
|
||||
{
|
||||
$process = proc_open('/sbin/ping -t2 -q -c1 ' . $this->config->getPingHost(), [], $pipes);
|
||||
if ($process) {
|
||||
return proc_close($process);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
66
src/Config.php
Normal file
66
src/Config.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pfatt;
|
||||
|
||||
/**
|
||||
* Config wrapper.
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
private string $pingHost = '8.8.8.8';
|
||||
private string $ontInterface;
|
||||
private string $rgInterface;
|
||||
private string $rgEthernetMac;
|
||||
|
||||
public function __construct(string $ontInterface, string $rgInterface, string $rgEthernetMac, ?string $pingHost = null)
|
||||
{
|
||||
$this->ontInterface = $ontInterface;
|
||||
$this->rgInterface = $rgInterface;
|
||||
$this->rgEthernetMac = $rgEthernetMac;
|
||||
if (isset($pingHost)) {
|
||||
$this->pingHost = $pingHost;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Host to ping to check connection.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPingHost(): string
|
||||
{
|
||||
return $this->pingHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network interface connected to the ONT.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOntInterface(): string
|
||||
{
|
||||
return $this->ontInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network interface connected to the Residential Gateway.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRgInterface(): string
|
||||
{
|
||||
return $this->rgInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Residential Gateway MAC address.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRgEthernetMac(): string
|
||||
{
|
||||
return $this->rgEthernetMac;
|
||||
}
|
||||
}
|
63
src/Logger.php
Normal file
63
src/Logger.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pfatt;
|
||||
|
||||
use Psr\Log\AbstractLogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Logger\ConsoleLogger;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* PSR-3 style logger.
|
||||
*/
|
||||
class Logger extends AbstractLogger
|
||||
{
|
||||
private string $channel;
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface|null
|
||||
*/
|
||||
private ?LoggerInterface $logger;
|
||||
|
||||
public function __construct(string $channel, LoggerInterface $logger = null)
|
||||
{
|
||||
$this->channel = $channel;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function log($level, $message, array $context = array())
|
||||
{
|
||||
$full_message = $this->getTimestamp() . ' :: [' . $this->channel . '] :: ' . $message;
|
||||
if (isset($this->logger)) {
|
||||
$this->logger->log($level, $full_message, $context);
|
||||
}
|
||||
else {
|
||||
echo $full_message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current timestamp formatted for logs.
|
||||
*
|
||||
* @return string
|
||||
* ISO8601 formatted timestamp.
|
||||
*/
|
||||
private function getTimestamp(): string
|
||||
{
|
||||
return (new \DateTime())->format(\DateTimeInterface::ISO8601);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add output to use for writing.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* Output service.
|
||||
*/
|
||||
public function setOutput(OutputInterface $output): void {
|
||||
$this->logger = new ConsoleLogger($output);
|
||||
}
|
||||
}
|
89
src/NgController.php
Normal file
89
src/NgController.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pfatt;
|
||||
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class NgController implements LoggerAwareInterface
|
||||
{
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use ngctl to check connection.
|
||||
*/
|
||||
public function ngIsConnected(): int
|
||||
{
|
||||
return $this->ngCtlRun('show laneapfilter:eapout');
|
||||
}
|
||||
|
||||
/**
|
||||
* Use ngctl remove connection.
|
||||
*/
|
||||
public function ngRmHook(): void
|
||||
{
|
||||
$this->logger->info('Disconnecting netgraph node ...');
|
||||
$this->ngCtlRun('rmhook laneapfilter: eapout') ?
|
||||
$this->logger->info('OK!') :
|
||||
$this->logger->error('ERROR!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Use ngctl to establish connection.
|
||||
*/
|
||||
public function ngConnect(): void
|
||||
{
|
||||
$this->logger->info('Connecting netgraph node ...');
|
||||
$this->ngCtlRun('connect waneapfilter: laneapfilter: eapout eapout') ?
|
||||
$this->logger->info('OK!') :
|
||||
$this->logger->error('ERROR!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check filter status.
|
||||
*
|
||||
* @param string $arg
|
||||
* Additional arguments to pass to ngctl.
|
||||
*
|
||||
* @return int
|
||||
* Process return code.
|
||||
*/
|
||||
private function ngCtlRun(string $arg): int
|
||||
{
|
||||
$descriptorspec = [
|
||||
// 0 => ['pipe', 'r'],
|
||||
// 1 => ['pipe', 'w'],
|
||||
// 2 => ['pipe', 'r'],
|
||||
];
|
||||
|
||||
$cwd = '/tmp';
|
||||
$env = [];
|
||||
|
||||
$process = proc_open(
|
||||
'/usr/sbin/ngctl ' . $arg,
|
||||
$descriptorspec,
|
||||
$pipes,
|
||||
$cwd,
|
||||
$env
|
||||
);
|
||||
if ($process) {
|
||||
return proc_close($process);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue