MVC: allow new services to hook into ApiMutableServiceController (#6663)

To allow legacy services without a model to hook into the `ApiMutableServiceController`, we define a protected `serviceEnabled` function that by default checks the given `internalServiceEnabled` property to see if a service is enabled, but allows derived classes to override the functionality. We loosen the property restrictions in `initialize()` by moving the checks to their runtime implementations.

DHCPv4/v6 is modified here to hook into this change, but since the `actions_services` requires the keyword `service`, which isn't used by the mutable service controller, we define start/stop/restart/status actions in the `actions_dhcpd.conf` and the new `actions_dhcpd6.conf` files.
This commit is contained in:
Stephan de Wit 2023-07-12 14:21:28 +02:00 committed by GitHub
parent fc15a2e281
commit 4ea46ea21c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 127 additions and 173 deletions

1
plist
View File

@ -1074,6 +1074,7 @@
/usr/local/opnsense/service/conf/actions.d/actions_configd.conf /usr/local/opnsense/service/conf/actions.d/actions_configd.conf
/usr/local/opnsense/service/conf/actions.d/actions_cron.conf /usr/local/opnsense/service/conf/actions.d/actions_cron.conf
/usr/local/opnsense/service/conf/actions.d/actions_dhcpd.conf /usr/local/opnsense/service/conf/actions.d/actions_dhcpd.conf
/usr/local/opnsense/service/conf/actions.d/actions_dhcpd6.conf
/usr/local/opnsense/service/conf/actions.d/actions_dns.conf /usr/local/opnsense/service/conf/actions.d/actions_dns.conf
/usr/local/opnsense/service/conf/actions.d/actions_filter.conf /usr/local/opnsense/service/conf/actions.d/actions_filter.conf
/usr/local/opnsense/service/conf/actions.d/actions_firmware.conf /usr/local/opnsense/service/conf/actions.d/actions_firmware.conf

View File

@ -72,18 +72,9 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
public function initialize() public function initialize()
{ {
parent::initialize(); parent::initialize();
if (empty(static::$internalServiceClass)) {
throw new \Exception('cannot instantiate without internalServiceClass defined.');
}
if (empty(static::$internalServiceName)) { if (empty(static::$internalServiceName)) {
throw new \Exception('cannot instantiate without internalServiceName defined.'); throw new \Exception('cannot instantiate without internalServiceName defined.');
} }
if (empty(static::$internalServiceTemplate)) {
throw new \Exception('cannot instantiate without internalServiceTemplate defined.');
}
if (empty(static::$internalServiceEnabled)) {
throw new \Exception('cannot instantiate without internalServiceEnabled defined.');
}
} }
/** /**
@ -93,6 +84,9 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
protected function getModel() protected function getModel()
{ {
if ($this->modelHandle == null) { if ($this->modelHandle == null) {
if (empty(static::$internalServiceClass)) {
throw new \Exception('cannot get model without internalServiceClass defined.');
}
$this->modelHandle = (new \ReflectionClass(static::$internalServiceClass))->newInstance(); $this->modelHandle = (new \ReflectionClass(static::$internalServiceClass))->newInstance();
} }
@ -166,6 +160,18 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
return false; return false;
} }
/**
* check if service is enabled according to model
*/
protected function serviceEnabled()
{
if (empty(static::$internalServiceEnabled)) {
throw new \Exception('cannot check if service is enabled without internalServiceEnabled defined.');
}
return (string)($this->getModel())->getNodeByReference(static::$internalServiceEnabled) == '1';
}
/** /**
* reconfigure with optional stop, generate config and start / reload * reconfigure with optional stop, generate config and start / reload
* @return array response message * @return array response message
@ -177,13 +183,9 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
if ($this->request->isPost()) { if ($this->request->isPost()) {
$this->sessionClose(); $this->sessionClose();
$model = $this->getModel();
$backend = new Backend(); $backend = new Backend();
if ( if (!$this->serviceEnabled() || $this->reconfigureForceRestart()) {
(string)$model->getNodeByReference(static::$internalServiceEnabled) != '1' ||
$this->reconfigureForceRestart()
) {
$backend->configdRun(escapeshellarg(static::$internalServiceName) . ' stop'); $backend->configdRun(escapeshellarg(static::$internalServiceName) . ' stop');
} }
@ -191,9 +193,11 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
$backend->configdRun('interface invoke registration'); $backend->configdRun('interface invoke registration');
} }
$backend->configdRun('template reload ' . escapeshellarg(static::$internalServiceTemplate)); if (!empty(static::$internalServiceTemplate)) {
$backend->configdRun('template reload ' . escapeshellarg(static::$internalServiceTemplate));
}
if ((string)$model->getNodeByReference(static::$internalServiceEnabled) == '1') { if ($this->serviceEnabled()) {
$runStatus = $this->statusAction(); $runStatus = $this->statusAction();
if ($runStatus['status'] != 'running') { if ($runStatus['status'] != 'running') {
$backend->configdRun(escapeshellarg(static::$internalServiceName) . ' start'); $backend->configdRun(escapeshellarg(static::$internalServiceName) . ' start');
@ -216,18 +220,17 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
public function statusAction() public function statusAction()
{ {
$backend = new Backend(); $backend = new Backend();
$model = $this->getModel();
$response = $backend->configdRun(escapeshellarg(static::$internalServiceName) . ' status'); $response = $backend->configdRun(escapeshellarg(static::$internalServiceName) . ' status');
if (strpos($response, 'not running') > 0) { if (strpos($response, 'not running') > 0) {
if ((string)$model->getNodeByReference(static::$internalServiceEnabled) == '1') { if ($this->serviceEnabled()) {
$status = 'stopped'; $status = 'stopped';
} else { } else {
$status = 'disabled'; $status = 'disabled';
} }
} elseif (strpos($response, 'is running') > 0) { } elseif (strpos($response, 'is running') > 0) {
$status = 'running'; $status = 'running';
} elseif ((string)$model->getNodeByReference(static::$internalServiceEnabled) == '0') { } elseif (!$this->serviceEnabled()) {
$status = 'disabled'; $status = 'disabled';
} else { } else {
$status = 'unknown'; $status = 'unknown';

View File

@ -54,7 +54,7 @@ class LeasesController extends ApiControllerBase
} }
/* get configured static leases */ /* get configured static leases */
$sleases = json_decode($backend->configdRun('dhcpd list static 4'), true); $sleases = json_decode($backend->configdRun('dhcpd list static 0'), true);
/* include inactive leases if requested */ /* include inactive leases if requested */
$leases = json_decode($backend->configdpRun('dhcpd list leases', [$inactive]), true); $leases = json_decode($backend->configdpRun('dhcpd list leases', [$inactive]), true);

View File

@ -28,74 +28,23 @@
namespace OPNsense\DHCPv4\Api; namespace OPNsense\DHCPv4\Api;
use OPNsense\Base\ApiControllerBase; use OPNsense\Base\ApiMutableServiceControllerBase;
use OPNsense\Core\Backend; use OPNsense\Core\Config;
class ServiceController extends ApiControllerBase class ServiceController extends ApiMutableServiceControllerBase
{ {
/** protected static $internalServiceName = 'dhcpd';
* XXX most of this logic can be replaced when appropriate start/stop/restart/status
* hooks are provided to fit into an ApiMutableServiceControllerBase class. dhcpd being
* 'enabled' isn't as straight-forward however with current legacy config format.
*/
public function statusAction()
{
$response = trim((new Backend())->configdRun('service status dhcpd'));
if (strpos($response, 'is running') > 0) { protected function serviceEnabled()
$status = 'running'; {
} elseif (strpos($response, 'not running') > 0) { $config = Config::getInstance()->object();
$status = 'stopped';
} else { foreach ($config->dhcpd->children() as $dhcpifconf) {
$status = 'disabled'; if (!empty((string)$dhcpifconf->enable) && (string)$dhcpifconf->enable == '1') {
return true;
}
} }
return [ return false;
'status' => $status,
'widget' => [
'caption_stop' => gettext("stop service"),
'caption_start' => gettext("start service"),
'caption_restart' => gettext("restart service")
]
];
}
public function startAction()
{
$result = ['status' => 'failed'];
if ($this->request->isPost()) {
$this->sessionClose();
$response = trim((new Backend())->configdRun('service start dhcpd'));
return ['status' => $response];
}
return $result;
}
public function stopAction()
{
$result = ['status' => 'failed'];
if ($this->request->isPost()) {
$this->sessionClose();
$response = trim((new Backend())->configdRun('service stop dhcpd'));
return ['status' => $response];
}
return $result;
}
public function restartAction()
{
$result = ['status' => 'failed'];
if ($this->request->isPost()) {
$this->sessionClose();
$response = trim((new Backend())->configdRun('service restart dhcpd'));
return ['status' => $response];
}
return $result;
} }
} }

View File

@ -51,7 +51,7 @@ class LeasesController extends ApiControllerBase
array_push($online, $ndp_entry['mac'], $ndp_entry['ip']); array_push($online, $ndp_entry['mac'], $ndp_entry['ip']);
} }
$raw_leases = json_decode($backend->configdpRun('dhcpd list leases6', [$inactive]), true); $raw_leases = json_decode($backend->configdpRun('dhcpd6 list leases', [$inactive]), true);
foreach ($raw_leases as $raw_lease) { foreach ($raw_leases as $raw_lease) {
if (!array_key_exists('addresses', $raw_lease)) { if (!array_key_exists('addresses', $raw_lease)) {
continue; continue;
@ -89,7 +89,7 @@ class LeasesController extends ApiControllerBase
$leases[] = $lease; $leases[] = $lease;
} }
$sleases = json_decode($backend->configdRun('dhcpd list static 6 0'), true); $sleases = json_decode($backend->configdRun('dhcpd6 list static 0'), true);
$statics = []; $statics = [];
foreach ($sleases['dhcpd'] as $slease) { foreach ($sleases['dhcpd'] as $slease) {
$static = [ $static = [
@ -207,7 +207,7 @@ class LeasesController extends ApiControllerBase
$backend = new Backend(); $backend = new Backend();
$prefixes = []; $prefixes = [];
$raw_leases = json_decode($backend->configdpRun('dhcpd list leases6 1'), true); $raw_leases = json_decode($backend->configdpRun('dhcpd6 list leases 1'), true);
foreach ($raw_leases as $raw_lease) { foreach ($raw_leases as $raw_lease) {
if ($raw_lease['lease_type'] === 'ia-pd' && array_key_exists('prefixes', $raw_lease)) { if ($raw_lease['lease_type'] === 'ia-pd' && array_key_exists('prefixes', $raw_lease)) {
$prefix = []; $prefix = [];
@ -237,7 +237,7 @@ class LeasesController extends ApiControllerBase
if ($this->request->isPost()) { if ($this->request->isPost()) {
$this->sessionClose(); $this->sessionClose();
$response = json_decode((new Backend())->configdpRun("dhcpd remove lease6", [$ip]), true); $response = json_decode((new Backend())->configdpRun("dhcpd6 remove lease", [$ip]), true);
if ($response["removed_leases"] != "0") { if ($response["removed_leases"] != "0") {
$result["result"] = "deleted"; $result["result"] = "deleted";
} }

View File

@ -28,74 +28,23 @@
namespace OPNsense\DHCPv6\Api; namespace OPNsense\DHCPv6\Api;
use OPNsense\Base\ApiControllerBase; use OPNsense\Base\ApiMutableServiceControllerBase;
use OPNsense\Core\Backend; use OPNsense\Core\Config;
class ServiceController extends ApiControllerBase class ServiceController extends ApiMutableServiceControllerBase
{ {
/** protected static $internalServiceName = 'dhcpd6';
* XXX most of this logic can be replaced when appropriate start/stop/restart/status
* hooks are provided to fit into an ApiMutableServiceControllerBase class. dhcpd being
* 'enabled' isn't as straight-forward however with current legacy config format.
*/
public function statusAction()
{
$response = trim((new Backend())->configdRun('service status dhcpd6'));
if (strpos($response, 'is running') > 0) { protected function serviceEnabled()
$status = 'running'; {
} elseif (strpos($response, 'not running') > 0) { $config = Config::getInstance()->object();
$status = 'stopped';
} else { foreach ($config->dhcpdv6->children() as $dhcpifconf) {
$status = 'disabled'; if (!empty((string)$dhcpifconf->enable) && (string)$dhcpifconf->enable == '1') {
return true;
}
} }
return [ return false;
'status' => $status,
'widget' => [
'caption_stop' => gettext("stop service"),
'caption_start' => gettext("start service"),
'caption_restart' => gettext("restart service")
]
];
}
public function startAction()
{
$result = ['status' => 'failed'];
if ($this->request->isPost()) {
$this->sessionClose();
$response = trim((new Backend())->configdRun('service start dhcpd6'));
return ['status' => $response];
}
return $result;
}
public function stopAction()
{
$result = ['status' => 'failed'];
if ($this->request->isPost()) {
$this->sessionClose();
$response = trim((new Backend())->configdRun('service stop dhcpd6'));
return ['status' => $response];
}
return $result;
}
public function restartAction()
{
$result = ['status' => 'failed'];
if ($this->request->isPost()) {
$this->sessionClose();
$response = trim((new Backend())->configdRun('service restart dhcpd6'));
return ['status' => $response];
}
return $result;
} }
} }

View File

@ -127,7 +127,7 @@
$("#inactive-selection-wrapper").detach().prependTo('#grid-leases-header > .row > .actionBar'); $("#inactive-selection-wrapper").detach().prependTo('#grid-leases-header > .row > .actionBar');
$("#interface-selection-wrapper").detach().prependTo('#grid-leases-header > .row > .actionBar > .actions'); $("#interface-selection-wrapper").detach().prependTo('#grid-leases-header > .row > .actionBar > .actions');
updateServiceControlUI('dhcp'); updateServiceControlUI('dhcpv4');
}); });
</script> </script>

View File

@ -32,7 +32,7 @@ PREVIOUS=
while :; do while :; do
CURRENT=$(${CHECKSUM} ${LEASES}) CURRENT=$(${CHECKSUM} ${LEASES})
if [ "${CURRENT}" != "${PREVIOUS}" ]; then if [ "${CURRENT}" != "${PREVIOUS}" ]; then
configctl dhcpd update prefixes configctl dhcpd6 update prefixes
PREVIOUS=${CURRENT} PREVIOUS=${CURRENT}
fi fi

View File

@ -4,39 +4,41 @@ parameters:--inactive %s
type:script_output type:script_output
message:list dhcp leases %s message:list dhcp leases %s
[list.leases6]
command:/usr/local/opnsense/scripts/dhcp/get_leases6.py
parameters:--inactive %s
type:script_output
message:list dhcpv6 leases %s
[list.static] [list.static]
command:/usr/local/sbin/pluginctl -r static_mapping command:/usr/local/sbin/pluginctl -r static_mapping 4
parameters:%s %s parameters:%s
type:script_output type:script_output
message: list dhcp static mappings %s message: list dhcp static mappings %s
[update.prefixes] [start]
command:/usr/local/opnsense/scripts/dhcp/prefixes.php command:/usr/local/sbin/pluginctl -s dhcpd start
parameters: parameters:
type:script type:script
message:update IPv6 prefixes message:Starting dhcpd
description:Start DHCPd
[stop]
command:/usr/local/sbin/pluginctl -s dhcpd stop
parameters:
type:script
message:Stopping dhcpd
description:Stop DHCPd
[restart] [restart]
command:/usr/local/sbin/pluginctl -c dhcp command:/usr/local/sbin/pluginctl -s dhcpd restart
parameters:%s parameters:%s
type:script type:script
message:restarting %s dhcpd message:Restarting %s dhcpd
description:Restart DHCPd description:Restart DHCPd
[status]
command:/usr/local/sbin/pluginctl -s dhcpd status
parameters:
type:script_output
message:Request DHCPd status
[remove.lease] [remove.lease]
command:/usr/local/opnsense/scripts/dhcp/cleanup_leases4.php command:/usr/local/opnsense/scripts/dhcp/cleanup_leases4.php
parameters:-d=%s -s parameters:-d=%s -s
type:script_output type:script_output
message:remove lease for %s message:remove lease for %s
[remove.lease6]
command:/usr/local/opnsense/scripts/dhcp/cleanup_leases6.php
parameters:-d=%s -s
type:script_output
message:remove lease6 for %s

View File

@ -0,0 +1,50 @@
[list.leases]
command:/usr/local/opnsense/scripts/dhcp/get_leases6.py
parameters:--inactive %s
type:script_output
message:list dhcpv6 leases %s
[list.static]
command:/usr/local/sbin/pluginctl -r static_mapping 6
parameters:%s
type:script_output
message: list dhcpv6 static mappings %s
[update.prefixes]
command:/usr/local/opnsense/scripts/dhcp/prefixes.php
parameters:
type:script
message:update IPv6 prefixes
[start]
command:/usr/local/sbin/pluginctl -s dhcpd6 start
parameters:
type:script
message:Starting dhcpd6
description:Start DHCPd6
[stop]
command:/usr/local/sbin/pluginctl -s dhcpd6 stop
parameters:
type:script
message:Stopping dhcpd6
description:Stop DHCPd6
[restart]
command:/usr/local/sbin/pluginctl -s dhcpd6 restart
parameters:%s
type:script
message:Restarting %s dhcpd6
description:Restart DHCPd6
[status]
command:/usr/local/sbin/pluginctl -s dhcpd6 status
parameters:
type:script_output
message:Request DHCPd6 status
[remove.lease]
command:/usr/local/opnsense/scripts/dhcp/cleanup_leases6.php
parameters:-d=%s -s
type:script_output
message:remove lease for %s