bugfix/247 Improve Zerotier Start/Stop Behaviour

https://github.com/opnsense/plugins/issues/247

This commit improves upon the way that Zerotier starts and stops and how
networks are added/removed and activated/deactivated. There is now a "Global"
tab on the Zerotier page that enables or disables the service which is
honoured between reboots. Additionally, the service needs to be active in
order for networks to be added and removed. If the service is not active, then
the tab "Networks" will be disabled and the user will not be able to
add/remove or activate/deactivate Zerotier networks.

This should fix the observed problems raised in the issue.

The "Global" tab will be extended later to include further information and
actions, especially when Zerotier API usage is developed.

-=david=-
This commit is contained in:
David Harrigan
2017-09-04 20:59:49 +01:00
committed by Franco Fichtner
parent a2f3413973
commit 3fdca054d0
8 changed files with 105 additions and 69 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
PLUGIN_NAME= zerotier
PLUGIN_VERSION= 1.0
PLUGIN_VERSION= 1.1.0
PLUGIN_COMMENT= Virtual Networks That Just Work
PLUGIN_DEPENDS= zerotier
PLUGIN_MAINTAINER= dharrigan@gmail.com
@@ -29,15 +29,8 @@
function zerotier_enabled()
{
$mdl = new \OPNsense\Zerotier\Zerotier();
foreach ($mdl->networks->network->__items as $network) {
if ($network->enabled == '1') {
return true;
}
}
return false;
$zerotier = new \OPNsense\Zerotier\Zerotier();
return (string)$zerotier->enabled == '1';
}
function zerotier_services()
@@ -45,12 +45,26 @@ class ZerotierController extends ApiMutableModelControllerBase
{
$result = array();
if ($this->request->isGet()) {
$mdlZerotier = new Zerotier();
$mdlZerotier = $this->getModel();
$result['zerotier'] = $mdlZerotier->getNodes();
}
return $result;
}
public function setAction()
{
$result = array("result" => "failed");
if($this->request->isPost()) {
$mdlZerotier = $this->getModel();
$mdlZerotier->setNodes($this->request->getPost("zerotier"));
$mdlZerotier->serializeToConfig();
Config::getInstance()->save();
$enabled = $this->isEnabled($mdlZerotier);
$result["result"] = $this->toggleZerotierService($enabled);
}
return $result;
}
public function searchNetworkAction()
{
$this->sessionClose();
@@ -129,8 +143,12 @@ class ZerotierController extends ApiMutableModelControllerBase
if ($this->request->isPost()) {
$mdlZerotier = $this->getModel();
if ($uuid != null) {
if (!$this->isEnabled($mdlZerotier)) {
$result["result"] = "service_not_enabled";
return $result;
}
$node = $mdlZerotier->getNodeByReference('networks.network.' . $uuid);
if ($node->enabled->__toString() == "1") {
if ($this->isEnabled($node)) {
# Ensure we remove the interface before deleting the network
$this->toggleZerotierNetwork($node->networkId, 0);
}
@@ -139,7 +157,7 @@ class ZerotierController extends ApiMutableModelControllerBase
Config::getInstance()->save();
$result["result"] = "deleted";
} else {
$result["result"] = "not found";
$result["result"] = "not_found";
}
}
}
@@ -152,10 +170,14 @@ class ZerotierController extends ApiMutableModelControllerBase
if ($this->request->isPost()) {
$mdlZerotier = $this->getModel();
if ($uuid != null) {
if (!$this->isEnabled($mdlZerotier)) {
$result["result"] = "service_not_enabled";
return $result;
}
$node = $mdlZerotier->getNodeByReference('networks.network.' . $uuid);
if ($node != null) {
$networkId = $node->networkId;
if ($node->enabled->__toString() == "1") {
if ($this->isEnabled($node)) {
$node->enabled = "0";
$result['result'] = $this->toggleZerotierNetwork($networkId, 0);
} else {
@@ -170,34 +192,13 @@ class ZerotierController extends ApiMutableModelControllerBase
return $result;
}
public function reconfigureZerotierAction()
{
if ($this->request->isPost()) {
$this->sessionClose();
$backend = new Backend();
$backend->configdRun("template reload OPNsense/zerotier");
$mdlZerotier = $this->getModel();
$action = 'stop';
foreach ($mdlZerotier->networks->network->__items as $network) {
if ($network->enabled == '1') {
$action = 'restart';
break;
}
}
$result = trim($backend->configdRun("zerotier $action"));
return array("status" => $result);
} else {
return array("status" => "failed");
}
}
public function statusAction()
{
$mdlZerotier = $this->getModel();
$enabled = false;
foreach ($mdlZerotier->networks->network->__items as $network) {
if ($network->enabled == '1') {
if ($this->isEnabled($network)) {
$enabled = true;
break;
}
@@ -220,7 +221,7 @@ class ZerotierController extends ApiMutableModelControllerBase
$status = "unknown";
}
return array("status" => $status);
return array("result" => $status);
}
private function toggleZerotierNetwork($networkId, $enabled)
@@ -229,4 +230,18 @@ class ZerotierController extends ApiMutableModelControllerBase
$action = $enabled ? 'join' : 'leave';
return trim($backend->configdRun("zerotier $action $networkId"));
}
private function toggleZerotierService($enabled)
{
$backend = new Backend();
$backend->configdRun("template reload OPNsense/zerotier");
$action = $enabled ? "start" : "stop";
return trim($backend->configdRun("zerotier $action"));
}
private function isEnabled($node)
{
return $node->enabled->__toString() == "1";
}
}
@@ -35,6 +35,7 @@ class IndexController extends \OPNsense\Base\IndexController
{
$this->view->title = "VPN: Zerotier";
$this->view->pick('OPNsense/Zerotier/index');
$this->view->formDialogNetwork = $this->getForm("dialogNetwork");
$this->view->globalForm = $this->getForm("global");
$this->view->dialogNetworkForm = $this->getForm("dialogNetwork");
}
}
@@ -0,0 +1,8 @@
<form>
<field>
<id>zerotier.enabled</id>
<label>Enable the Zerotier Service</label>
<type>checkbox</type>
<help>This will activate the Zerotier service</help>
</field>
</form>
@@ -3,7 +3,12 @@
<description>
Zerotier - Virtual Networks That Just Work.
</description>
<version>1.1.0</version>
<items>
<enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enabled>
<networks>
<network type="ArrayField">
<enabled type="BooleanField">
@@ -31,6 +31,13 @@ POSSIBILITY OF SUCH DAMAGE.
$(document).ready(function() {
var zerotierSettings = {'global': '/api/zerotier/zerotier/get'};
mapDataToFormUI(zerotierSettings).done(function(data) {
formatTokenizersUI();
$('select').selectpicker('refresh');
});
$("#grid-networks").UIBootgrid(
{
search: '/api/zerotier/zerotier/searchNetwork',
@@ -42,37 +49,55 @@ POSSIBILITY OF SUCH DAMAGE.
}
);
ajaxCall(url="/api/zerotier/zerotier/status", sendData={}, callback=function(data,status) {
updateServiceStatusUI(data['status']);
ajaxCall(url="/api/zerotier/zerotier/status", sendData={}, callback=function(data, status) {
updateServiceStatusUI(data['result']);
toggleNetworksTab(data['result']);
});
$("#reconfigureZerotier").click(function() {
$("#reconfigureZerotierProgress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/zerotier/zerotier/reconfigureZerotier", sendData={}, callback=function(data, status) {
ajaxCall(url="/api/zerotier/zerotier/status", sendData={}, callback=function(data,status) {
updateServiceStatusUI(data['status']);
$("#save").click(function() {
$("#saveProgress").addClass("fa fa-spinner fa-pulse");
saveFormToEndpoint(url="/api/zerotier/zerotier/set", formid="global", callback_ok=function(data, status) {
ajaxCall(url="/api/zerotier/zerotier/status", sendData={}, callback=function(data, status) {
updateServiceStatusUI(data['result']);
toggleNetworksTab(data['result']);
});
$("#reconfigureZerotierProgress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'OK') {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_WARNING,
title: "{{ lang._('Error reconfiguring Zerotier') }}",
message: data['status'],
draggable: true
});
}
$("#saveProgress").removeClass("fa fa-spinner fa-pulse");
});
});
function toggleNetworksTab(status) {
switch(status) {
case "disabled":
case "service_not_enabled":
$('#ztNetworks').addClass("disabled");
$('#ztNetworksLink').removeAttr("data-toggle");
break;
default:
$('#ztNetworks').removeClass("disabled");
$('#ztNetworksLink').attr("data-toggle", "tab");
}
};
});
</script>
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#networks">{{ lang._('Networks') }}</a></li>
<li id="ztGlobal" class="active"><a data-toggle="tab" href="#global">{{ lang._('Global') }}</a></li>
<li id="ztNetworks"><a id="ztNetworksLink" data-toggle="tab" href="#networks">{{ lang._('Networks') }}</a></li>
</ul>
<div class="tab-content content-box tab-content">
<div id="networks" class="tab-pane fade in active">
<div id="global" class="tab-pane fade in active">
<div class="content-box" style="padding-bottom: 1.5em;">
{{ partial("layout_partials/base_form", ['fields': globalForm, 'id': 'global']) }}
<hr/>
<div class="col-md-12">
<button class="btn btn-primary" id="save" type="button"><b>{{ lang._('Save') }}</b> <i id="saveProgress" class=""></i></button>
</div>
</div>
</div>
<div id="networks" class="tab-pane fade in">
<table id="grid-networks" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="dialogNetwork">
<thead>
<tr>
@@ -95,11 +120,6 @@ POSSIBILITY OF SUCH DAMAGE.
</tfoot>
</table>
</div>
<div class="col-md-12">
<hr/>
<button class="btn btn-primary" id="reconfigureZerotier" type="button"><b>{{ lang._('Apply') }}</b> <i id="reconfigureZerotierProgress" class=""></i></button>
<br/><br/>
</div>
</div>
{{ partial("layout_partials/base_dialog",['fields':formDialogNetwork,'id':'dialogNetwork','label':lang._('Edit Zerotier Network')]) }}
{{ partial("layout_partials/base_dialog", ['fields': dialogNetworkForm, 'id': 'dialogNetwork', 'label': lang._('Edit Zerotier Network')]) }}
@@ -1,10 +1,4 @@
{% set networks = [] %}
{% for network in helpers.toList('OPNsense.zerotier.networks.network') %}
{% if network.enabled == '1' %}
{% do networks.append(network) %}
{% endif %}
{% endfor %}
{% if networks|length > 0 %}
{% if helpers.exists('OPNsense.zerotier.enabled') and OPNsense.zerotier.enabled == '1' %}
zerotier_enable="YES"
{% else %}
zerotier_enable="NO"