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_cron.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_filter.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()
{
parent::initialize();
if (empty(static::$internalServiceClass)) {
throw new \Exception('cannot instantiate without internalServiceClass defined.');
}
if (empty(static::$internalServiceName)) {
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()
{
if ($this->modelHandle == null) {
if (empty(static::$internalServiceClass)) {
throw new \Exception('cannot get model without internalServiceClass defined.');
}
$this->modelHandle = (new \ReflectionClass(static::$internalServiceClass))->newInstance();
}
@ -166,6 +160,18 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
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
* @return array response message
@ -177,13 +183,9 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
if ($this->request->isPost()) {
$this->sessionClose();
$model = $this->getModel();
$backend = new Backend();
if (
(string)$model->getNodeByReference(static::$internalServiceEnabled) != '1' ||
$this->reconfigureForceRestart()
) {
if (!$this->serviceEnabled() || $this->reconfigureForceRestart()) {
$backend->configdRun(escapeshellarg(static::$internalServiceName) . ' stop');
}
@ -191,9 +193,11 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
$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();
if ($runStatus['status'] != 'running') {
$backend->configdRun(escapeshellarg(static::$internalServiceName) . ' start');
@ -216,18 +220,17 @@ abstract class ApiMutableServiceControllerBase extends ApiControllerBase
public function statusAction()
{
$backend = new Backend();
$model = $this->getModel();
$response = $backend->configdRun(escapeshellarg(static::$internalServiceName) . ' status');
if (strpos($response, 'not running') > 0) {
if ((string)$model->getNodeByReference(static::$internalServiceEnabled) == '1') {
if ($this->serviceEnabled()) {
$status = 'stopped';
} else {
$status = 'disabled';
}
} elseif (strpos($response, 'is running') > 0) {
$status = 'running';
} elseif ((string)$model->getNodeByReference(static::$internalServiceEnabled) == '0') {
} elseif (!$this->serviceEnabled()) {
$status = 'disabled';
} else {
$status = 'unknown';

View File

@ -54,7 +54,7 @@ class LeasesController extends ApiControllerBase
}
/* 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 */
$leases = json_decode($backend->configdpRun('dhcpd list leases', [$inactive]), true);

View File

@ -28,74 +28,23 @@
namespace OPNsense\DHCPv4\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\Core\Backend;
use OPNsense\Base\ApiMutableServiceControllerBase;
use OPNsense\Core\Config;
class ServiceController extends ApiControllerBase
class ServiceController extends ApiMutableServiceControllerBase
{
/**
* 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'));
protected static $internalServiceName = 'dhcpd';
if (strpos($response, 'is running') > 0) {
$status = 'running';
} elseif (strpos($response, 'not running') > 0) {
$status = 'stopped';
} else {
$status = 'disabled';
protected function serviceEnabled()
{
$config = Config::getInstance()->object();
foreach ($config->dhcpd->children() as $dhcpifconf) {
if (!empty((string)$dhcpifconf->enable) && (string)$dhcpifconf->enable == '1') {
return true;
}
}
return [
'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;
return false;
}
}

View File

@ -51,7 +51,7 @@ class LeasesController extends ApiControllerBase
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) {
if (!array_key_exists('addresses', $raw_lease)) {
continue;
@ -89,7 +89,7 @@ class LeasesController extends ApiControllerBase
$leases[] = $lease;
}
$sleases = json_decode($backend->configdRun('dhcpd list static 6 0'), true);
$sleases = json_decode($backend->configdRun('dhcpd6 list static 0'), true);
$statics = [];
foreach ($sleases['dhcpd'] as $slease) {
$static = [
@ -207,7 +207,7 @@ class LeasesController extends ApiControllerBase
$backend = new Backend();
$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) {
if ($raw_lease['lease_type'] === 'ia-pd' && array_key_exists('prefixes', $raw_lease)) {
$prefix = [];
@ -237,7 +237,7 @@ class LeasesController extends ApiControllerBase
if ($this->request->isPost()) {
$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") {
$result["result"] = "deleted";
}

View File

@ -28,74 +28,23 @@
namespace OPNsense\DHCPv6\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\Core\Backend;
use OPNsense\Base\ApiMutableServiceControllerBase;
use OPNsense\Core\Config;
class ServiceController extends ApiControllerBase
class ServiceController extends ApiMutableServiceControllerBase
{
/**
* 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'));
protected static $internalServiceName = 'dhcpd6';
if (strpos($response, 'is running') > 0) {
$status = 'running';
} elseif (strpos($response, 'not running') > 0) {
$status = 'stopped';
} else {
$status = 'disabled';
protected function serviceEnabled()
{
$config = Config::getInstance()->object();
foreach ($config->dhcpdv6->children() as $dhcpifconf) {
if (!empty((string)$dhcpifconf->enable) && (string)$dhcpifconf->enable == '1') {
return true;
}
}
return [
'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;
return false;
}
}

View File

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

View File

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

View File

@ -4,39 +4,41 @@ parameters:--inactive %s
type:script_output
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]
command:/usr/local/sbin/pluginctl -r static_mapping
parameters:%s %s
command:/usr/local/sbin/pluginctl -r static_mapping 4
parameters:%s
type:script_output
message: list dhcp static mappings %s
[update.prefixes]
command:/usr/local/opnsense/scripts/dhcp/prefixes.php
[start]
command:/usr/local/sbin/pluginctl -s dhcpd start
parameters:
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]
command:/usr/local/sbin/pluginctl -c dhcp
command:/usr/local/sbin/pluginctl -s dhcpd restart
parameters:%s
type:script
message:restarting %s dhcpd
message:Restarting %s dhcpd
description:Restart DHCPd
[status]
command:/usr/local/sbin/pluginctl -s dhcpd status
parameters:
type:script_output
message:Request DHCPd status
[remove.lease]
command:/usr/local/opnsense/scripts/dhcp/cleanup_leases4.php
parameters:-d=%s -s
type:script_output
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