Compare commits

...

7 commits

Author SHA1 Message Date
matthewalanpenning
31f878a586 v3.8.0 2023-08-31 05:02:29 -04:00
matthewalanpenning
2c2acd2c4c v3.7.0 2023-03-20 05:12:22 -04:00
Matthew Penning
74b2a1ab3f
Merge pull request #61 from danieldean/master
Close exec websockets on exit.
2023-01-07 13:04:43 -05:00
Daniel Dean
dfffdb7efe Close exec websockets on exit. 2022-12-24 18:26:19 +00:00
matthewalanpenning
7dd0561e41 v3.6.0 2022-12-09 10:17:03 -05:00
Matthew Penning
031554e100
Merge pull request #44 from fthorns/master
Cloud-init support for LXC containers
2022-12-09 09:59:37 -05:00
Fabian Thorns
426b3eb451 Cloud-init support for LXC containers 2022-08-22 11:15:35 +02:00
13 changed files with 136 additions and 19 deletions

View file

@ -1,3 +1,19 @@
# v3.8.0
- Added a check for a null config in storage pools
- Added image token cleanup with downloading images while in a cluster
# v3.7.0
- Set console/exec terminal height to 25 rows because xterm.js defaults to 24 and lxd defaults to 25 there was a single row mismatch
- Added qemu.conf to virtual machine configuration options
- Changed cloud-init user data to use POST method rather than GET
- Merged pull request improving websocket close on exec/console websockets
- Added check for empty storage pool source configuration array item
# v3.6.0
- Merged pull request adding cloud init user data option for new containers via form
- Fixed typo preventing OS version number from showing in remotes-single.php page, under LXD Information
# v3.5.0
- Updated container image catalog based on results from from https://us.lxd.images
- Merged pull request updating MySQL statements for auto increment and now time

View file

@ -43,7 +43,7 @@ if (isset($_SESSION['username'])) {
<body class="">
<p>The open source LXD Dashboard is developed by LXDWARE and provides a web-based user interface capable of managing multiple LXD servers from a single location.</p>
<p>
<strong>Version</strong>: <span id="versionNumber">v3.5.0</span> <br />
<strong>Version</strong>: <span id="versionNumber">v3.8.0</span> <br />
<strong>License</strong>: AGPL-3.0 <br />
<strong>URL</strong>: https://lxdware.com <br />
</p>

View file

@ -650,10 +650,10 @@ if (isset($_SESSION['username'])) {
case "establishInstanceWebSocketExecConnection":
$url = $base_url . "/1.0/containers/".$instance."/exec?project=" . $project;
if ($shell == "sh"){
$data = '{ "command": ["/bin/sh"], "wait-for-websocket": true, "environment":{"HOME": "/root", "TERM": "xterm", "USER": "root"}, "interactive": true}';
$data = '{ "command": ["/bin/sh"], "wait-for-websocket": true, "environment":{"HOME": "/root", "TERM": "xterm", "USER": "root"}, "interactive": true, "width": 80, "height": 25}';
}
else {
$data = '{ "command": ["/bin/bash"], "wait-for-websocket": true, "environment":{"HOME": "/root", "TERM": "xterm", "USER": "root"}, "interactive": true}';
$data = '{ "command": ["/bin/bash"], "wait-for-websocket": true, "environment":{"HOME": "/root", "TERM": "xterm", "USER": "root"}, "interactive": true, "width": 80, "height": 25}';
}
$results = sendCurlRequest($action, "POST", $url, $data);

View file

@ -107,6 +107,7 @@ if (isset($_SESSION['username'])) {
//Declare and instantiate POST variables
$json = (isset($_POST['json'])) ? $_POST['json'] : "";
$cloud_init_user_data = (isset($_POST['cloud_init_user_data'])) ? $_POST['cloud_init_user_data'] : "";
//Require code from lxd-dashboard/backend/config/curl.php
require_once('../config/curl.php');
@ -286,6 +287,8 @@ if (isset($_SESSION['username'])) {
if (!empty($boot_host_shutdown_timeout)){ $instance_array['config']['boot.host_shutdown_timeout'] = $boot_host_shutdown_timeout;}
if (!empty($boot_stop_priority)){ $instance_array['config']['boot.stop.priority'] = $boot_stop_priority;}
if (!empty($cloud_init_user_data)){ $instance_array['config']['cloud-init.user-data'] = $cloud_init_user_data;}
if (!empty($limits_cpu)){ $instance_array['config']['limits.cpu'] = $limits_cpu;}
if (!empty($limits_cpu_allowance)){ $instance_array['config']['limits.cpu.allowance'] = $limits_cpu_allowance;}
if (!empty($limits_cpu_priority)){ $instance_array['config']['limits.cpu.priority'] = $limits_cpu_priority;}

View file

@ -144,6 +144,13 @@ if (isset($_SESSION['username'])) {
case "Updating instance":
$results .= " " . $instance;
break;
case "Image download token":
#Remove image tokens left over from image downloads when in a cluster
if ($running_task['may_cancel'] == true){
$url = $base_url . "/1.0/operations/" . $running_task['id'];
$results = sendCurlRequest($action, "DELETE", $url);
}
break;
}
}
}

View file

@ -452,7 +452,12 @@ if (isset($_SESSION['username'])) {
echo '"' . htmlentities($storage_pool['description']) . '",';
echo '"' . htmlentities($storage_pool['driver']) . '",';
echo '"' . htmlentities($storage_pool['status']) . '",';
echo '"' . htmlentities($storage_pool['config']['source']) . '",';
if(isset($storage_pool['config']) && array_key_exists('source', $storage_pool['config']))
$storage_pool_source = (isset($storage_pool['config']['source'])) ? $storage_pool['config']['source'] : "N/A";
else
$storage_pool_source = "N/A";
echo '"' . htmlentities($storage_pool_source) . '",';
$storage_pool_size = (isset($storage_pool['config']['size'])) ? $storage_pool['config']['size'] : "N/A";
echo '"' . htmlentities($storage_pool_size) . '",';

View file

@ -599,10 +599,10 @@ if (isset($_SESSION['username'])) {
case "establishInstanceWebSocketExecConnection":
$url = $base_url . "/1.0/instances/".$instance."/exec?project=" . $project;
if ($shell == "sh"){
$data = '{ "command": ["/bin/sh"], "wait-for-websocket": true, "environment":{"HOME": "/root", "TERM": "xterm", "USER": "root"}, "interactive": true}';
$data = '{ "command": ["/bin/sh"], "wait-for-websocket": true, "environment":{"HOME": "/root", "TERM": "xterm", "USER": "root"}, "interactive": true, "width": 80, "height": 25}';
}
else {
$data = '{ "command": ["/bin/bash"], "wait-for-websocket": true, "environment":{"HOME": "/root", "TERM": "xterm", "USER": "root"}, "interactive": true}';
$data = '{ "command": ["/bin/bash"], "wait-for-websocket": true, "environment":{"HOME": "/root", "TERM": "xterm", "USER": "root"}, "interactive": true, "width": 80, "height": 25}';
}
$results = sendCurlRequest($action, "POST", $url, $data);

View file

@ -61,6 +61,7 @@ if (isset($_SESSION['username'])) {
$migration_stateful = (isset($_GET['migration_stateful'])) ? filter_var(urldecode($_GET['migration_stateful']), FILTER_SANITIZE_STRING) : "";
$raw_apparmor = (isset($_GET['raw_apparmor'])) ? filter_var(urldecode($_GET['raw_apparmor']), FILTER_SANITIZE_STRING) : "";
$raw_qemu = (isset($_GET['raw_qemu'])) ? filter_var(urldecode($_GET['raw_qemu']), FILTER_SANITIZE_STRING) : "";
$raw_qemu_conf = (isset($_GET['raw_qemu_conf'])) ? filter_var(urldecode($_GET['raw_qemu_conf']), FILTER_SANITIZE_STRING) : "";
$security_devlxd = (isset($_GET['security_devlxd'])) ? filter_var(urldecode($_GET['security_devlxd']), FILTER_SANITIZE_STRING) : "";
$security_protection_delete = (isset($_GET['security_protection_delete'])) ? filter_var(urldecode($_GET['security_protection_delete']), FILTER_SANITIZE_STRING) : "";
$security_secureboot = (isset($_GET['security_secureboot'])) ? filter_var(urldecode($_GET['security_secureboot']), FILTER_SANITIZE_STRING) : "";
@ -257,6 +258,7 @@ if (isset($_SESSION['username'])) {
if (!empty($migration_stateful)){ $instance_array['config']['migration.stateful'] = $migration_stateful;}
if (!empty($raw_apparmor)){ $instance_array['config']['raw.apparmor'] = $raw_apparmor;}
if (!empty($raw_qemu)){ $instance_array['config']['raw.qemu'] = $raw_qemu;}
if (!empty($raw_qemu_conf)){ $instance_array['config']['raw.qemu.conf'] = $raw_qemu_conf;}
if (!empty($security_devlxd)){ $instance_array['config']['security.devlxd'] = $security_devlxd;}
if (!empty($security_protection_delete)){ $instance_array['config']['security.protection.delete'] = $security_protection_delete;}
if (!empty($security_secureboot)){ $instance_array['config']['security.secureboot'] = $security_secureboot;}
@ -899,6 +901,7 @@ if (isset($_SESSION['username'])) {
$instance_array['config']['migration.stateful'] = $migration_stateful;
$instance_array['config']['raw.apparmor'] = $raw_apparmor;
$instance_array['config']['raw.qemu'] = $raw_qemu;
$instance_array['config']['raw.qemu.conf'] = $raw_qemu_conf;
$instance_array['config']['security.devlxd'] = $security_devlxd;
$instance_array['config']['security.protection.delete'] = $security_protection_delete;
$instance_array['config']['security.secureboot'] = $security_secureboot;

View file

@ -607,6 +607,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
<!-- End Security Syscalls Card -->
<!-- Cloud-Init Card -->
<div class="card col-12 border-0 mb-2">
<!-- Card Header - Dropdown -->
<div class="card-header border-0 bg-transparent py-1 d-flex flex-row align-items-center justify-content-between">
<h5 class="m-0 font-weight-bold text-primary">Cloud-Init</h5>
</div>
<!-- Card Body -->
<div class="card-body pt-1">
<div class="row">
<div class="col-12">
<table class="table table-sm">
<tr><td class="pr-3 font-weight-bold">User-Data:</td> <td><span id="cloudInitUserData"></span></td></tr>
</table>
</div>
</div>
</div>
</div>
<!-- End Security Syscalls Card -->
</div>
</div>
<!-- End Config List -->
@ -3718,6 +3737,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
if (dataConfig.hasOwnProperty('boot.host_shutdown_timeout')) { $("#bootHostShutdownTimeout").text(dataConfig['boot.host_shutdown_timeout']); } else { $("#bootHostShutdownTimeout").text(""); }
if (dataConfig.hasOwnProperty('boot.stop.priority')) { $("#bootStopPriority").text(dataConfig['boot.stop.priority']); } else { $("#bootStopPriority").text(""); }
if (dataConfig.hasOwnProperty('cloud-init.user-data')) { $("#cloudInitUserData").text(dataConfig['cloud-init.user-data']); } else { $("#cloudInitUserData").text(""); }
if (dataConfig.hasOwnProperty('cluster.evacuate')) { $("#clusterEvacuate").text(dataConfig['cluster.evacuate']); } else { $("#clusterEvacuate").text(""); }
if (dataConfig.hasOwnProperty('limits.cpu')) { $("#limitsCpu").text(dataConfig['limits.cpu']); } else { $("#limitsCpu").text(""); }
@ -5792,9 +5813,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//Listen for "data" websocket messages
execDataSocket.onmessage = function (e) {
if (e.data instanceof ArrayBuffer) {
if (convertArrayBuffer2String(e.data) != null){
execTerminal.write(convertArrayBuffer2String(e.data));
if (e.data.length == 0) {
execControlSocket.close();
execDataSocket.close();
} else {
if (e.data instanceof ArrayBuffer) {
if (convertArrayBuffer2String(e.data) != null){
execTerminal.write(convertArrayBuffer2String(e.data));
}
}
}
};
@ -5927,7 +5953,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//Initialize xterm for Console
consoleTerminal = new Terminal({
cursorBlink: "block"
cursorBlink: "block",
rows: 25
});
//Setup listener for Console terminal
@ -5939,7 +5966,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//Initialize xterm for Exec
execTerminal = new Terminal({
cursorBlink: "block"
cursorBlink: "block",
rows: 25
});
//Setup listener for Exec terminal

View file

@ -514,6 +514,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<a class="nav-link" id="nav-nvidia-tab" data-toggle="tab" href="#nav-nvidia" role="tab" aria-controls="nav-nvidia" aria-selected="false">Nvidia</a>
<a class="nav-link" id="nav-other-tab" data-toggle="tab" href="#nav-other" role="tab" aria-controls="nav-other" aria-selected="false">Other</a>
<a class="nav-link" id="nav-raw-tab" data-toggle="tab" href="#nav-raw" role="tab" aria-controls="nav-raw" aria-selected="false">Raw</a>
<a class="nav-link" id="nav-cloud-init-tab" data-toggle="tab" href="#nav-cloud-init" role="tab" aria-controls="nav-cloud-init" aria-selected="false">Cloud-Init</a>
<a class="nav-link" id="nav-security-tab" data-toggle="tab" href="#nav-security" role="tab" aria-controls="nav-security" aria-selected="false">Security</a>
<a class="nav-link" id="nav-snapshots-tab" data-toggle="tab" href="#nav-snapshots" role="tab" aria-controls="nav-snapshots" aria-selected="false">Snapshots</a>
</div>
@ -917,6 +918,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
<div class="tab-pane fade" id="nav-cloud-init" role="tabpanel" aria-labelledby="nav-cloud-init">
<div class="row">
<label class="col-4 col-form-label text-right">User-Data: </label>
<div class="col-6">
<div class="form-group">
<textarea id="containerCloudInitUserDataInput" class="form-control" name="containerCloudInitUserDataInput"></textarea>
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title="Enter the user-data configuration for cloud-init. Default: (not set)."></i>
</div>
</div>
</div>
<div class="tab-pane fade" id="nav-security" role="tabpanel" aria-labelledby="nav-security-tab">
<br>
<div class="row">
@ -1443,6 +1457,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
var bootHostShutdownTimeout = $("#containerBootHostShutdownTimeoutInput").val();
var bootStopPriority = $("#containerBootStopPriorityInput").val();
var cloudInitUserData = $("#containerCloudInitUserDataInput").val();
var limitsCpu = $("#containerLimitsCpuInput").val();
var limitsCpuAllowance = $("#containerLimitsCpuAllowanceInput").val();
var limitsCpuPriority = $("#containerLimitsCpuPriorityInput").val();
@ -1501,7 +1517,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
var snapshotsExpiry = $("#containerSnapshotsExpiryInput").val();
console.log("Info: creating container " + instanceName);
$.get("./backend/lxd/containers.php?remote=" + encodeURI(remoteId) + "&project=" + encodeURI(projectName) +
$.post("./backend/lxd/containers.php?remote=" + encodeURI(remoteId) + "&project=" + encodeURI(projectName) +
"&name=" + encodeURI(instanceName) +
"&description=" + encodeURI(instanceDescription) +
"&fingerprint=" + encodeURI(fingerprint) +
@ -1573,7 +1589,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"&snapshots_pattern=" + encodeURI(snapshotsPattern) +
"&snapshots_expiry=" + encodeURI(snapshotsExpiry) +
"&action=createInstanceUsingForm", function (data) {
"&action=createInstanceUsingForm", {
//Sending cloud_init_user_data over POST due to encoding. Shoul work to convert all variables to POST as well in future release.
cloud_init_user_data: cloudInitUserData
}, function (data) {
//Sync type
var operationData = JSON.parse(data);
console.log(operationData);

View file

@ -804,7 +804,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
$("#kernelArchitecture").text(stats.kernelArchitecture);
$("#kernelVersion").text(stats.kernelVersion);
$("#osName").text(stats.osName);
$("#osVersion").css("width", stats.osVersion)
$("#osVersion").text(stats.osVersion)
$("#serverVersion").text(stats.serverVersion);

View file

@ -472,6 +472,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<table class="table table-sm">
<tr><td class="pr-3 font-weight-bold">Apparmor:</td> <td><span id="rawApparmor"></span></td></tr>
<tr><td class="pr-3 font-weight-bold">Qemu:</td> <td><span id="rawQemu"></span></td></tr>
<tr><td class="pr-3 font-weight-bold">Qemu.conf:</td> <td><span id="rawQemuConf"></span></td></tr>
</table>
</div>
</div>
@ -2583,6 +2584,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<i class="far fa-sm fa-question-circle" title="Enter in qemu configuration to be appended to the profile. Default: (not set)."></i>
</div>
</div>
<div class="row">
<label class="col-4 col-form-label text-right">Qemu.conf: </label>
<div class="col-6">
<div class="form-group">
<input type="text" id="instanceRawQemuConfInput" class="form-control" placeholder="" name="instanceRawQemuConfInput">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title="Enter in qemu.conf configuration to override the generated qemu.conf file. Default: (not set)."></i>
</div>
</div>
</div>
<div class="tab-pane fade" id="nav-security" role="tabpanel" aria-labelledby="nav-security-tab">
@ -2985,6 +2997,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
if (dataConfig.hasOwnProperty('raw.apparmor')) { $("#rawApparmor").text(dataConfig['raw.apparmor']); } else { $("#rawApparmor").text(""); }
if (dataConfig.hasOwnProperty('raw.qemu')) { $("#rawQemu").text(dataConfig['raw.qemu']); } else { $("#rawQemu").text(""); }
if (dataConfig.hasOwnProperty('raw.qemu.conf')) { $("#rawQemuConf").text(dataConfig['raw.qemu.conf']); } else { $("#rawQemuConf").text(""); }
if (dataConfig.hasOwnProperty('security.devlxd')) { $("#securityDevLxd").text(dataConfig['security.devlxd']); } else { $("#securityDevLxd").text(""); }
if (dataConfig.hasOwnProperty('security.protection.delete')) { $("#securityProtectionDelete").text(dataConfig['security.protection.delete']); } else { $("#securityProtectionDelete").text(""); }
@ -3092,6 +3105,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
if (dataConfig.hasOwnProperty('raw.apparmor')) { $("#rawApparmor").text(dataConfig['raw.apparmor']); }
if (dataConfig.hasOwnProperty('raw.qemu')) { $("#rawQemu").text(dataConfig['raw.qemu']); }
if (dataConfig.hasOwnProperty('raw.qemu.conf')) { $("#rawQemuConf").text(dataConfig['raw.qemu.conf']); }
if (dataConfig.hasOwnProperty('security.devlxd')) { $("#securityDevLxd").text(dataConfig['security.devlxd']); }
if (dataConfig.hasOwnProperty('security.protection.delete')) { $("#securityProtectionDelete").text(dataConfig['security.protection.delete']); }
@ -4499,6 +4513,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
if (dataConfig.hasOwnProperty('migration.stateful')) { $("#instanceMigrationStatefulInput").val(dataConfig['migration.stateful']); }
if (dataConfig.hasOwnProperty('raw.apparmor')) { $("#instanceRawApparmorInput").val(dataConfig['raw.apparmor']); }
if (dataConfig.hasOwnProperty('raw.qemu')) { $("#instanceRawQemuInput").val(dataConfig['raw.qemu']); }
if (dataConfig.hasOwnProperty('raw.qemu.conf')) { $("#instanceRawQemuConfInput").val(dataConfig['raw.qemu.conf']); }
if (dataConfig.hasOwnProperty('security.devlxd')) { $("#instanceSecurityDevLxdInput").val(dataConfig['security.devlxd']); }
if (dataConfig.hasOwnProperty('security.protection.delete')) { $("#instanceSecurityProtectionDeleteInput").val(dataConfig['security.protection.delete']); }
if (dataConfig.hasOwnProperty('security.secureboot')) { $("#instanceSecuritySecurebootInput").val(dataConfig['security.secureboot']); }
@ -4531,6 +4546,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"&migration_stateful=" + encodeURI($("#instanceMigrationStatefulInput").val()) +
"&raw_apparmor=" + encodeURI($("#instanceRawApparmorInput").val()) +
"&raw_qemu=" + encodeURI($("#instanceRawQemuInput").val()) +
"&raw_qemu_conf=" + encodeURI($("#instanceRawQemuConfInput").val()) +
"&security_devlxd=" + encodeURI($("#instanceSecurityDevLxdInput").val()) +
"&security_protection_delete=" + encodeURI($("#instanceSecurityProtectionDeleteInput").val()) +
"&security_secureboot=" + encodeURI($("#instanceSecuritySecurebootInput").val()) +
@ -4759,9 +4775,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//Listen for "data" websocket messages
execDataSocket.onmessage = function (e) {
if (e.data instanceof ArrayBuffer) {
if (convertArrayBuffer2String(e.data) != null){
execTerminal.write(convertArrayBuffer2String(e.data));
if (e.data.length == 0) {
execControlSocket.close();
execDataSocket.close();
} else {
if (e.data instanceof ArrayBuffer) {
if (convertArrayBuffer2String(e.data) != null){
execTerminal.write(convertArrayBuffer2String(e.data));
}
}
}
};
@ -4896,7 +4917,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//Initialize xterm for Console
consoleTerminal = new Terminal({
cursorBlink: "block"
cursorBlink: "block",
rows: 25
});
//Setup listener for Console terminal
@ -4908,7 +4930,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//Initialize xterm for Exec
execTerminal = new Terminal({
cursorBlink: "block"
cursorBlink: "block",
rows: 25
});
//Setup listener for Exec terminal

View file

@ -705,6 +705,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<i class="far fa-sm fa-question-circle" title="Enter in qemu configuration to be appended to the profile. Default: (not set)."></i>
</div>
</div>
<div class="row">
<label class="col-4 col-form-label text-right">Qemu.conf: </label>
<div class="col-6">
<div class="form-group">
<input type="text" id="instanceRawQemuConfInput" class="form-control" placeholder="" name="instanceRawQemuConfInput">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title="Enter in qemu.conf configuration to override the generated qemu.conf file. Default: (not set)."></i>
</div>
</div>
</div>
<div class="tab-pane fade" id="nav-security" role="tabpanel" aria-labelledby="nav-security-tab">
@ -995,6 +1006,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
var migrationStateful = $("#instanceMigrationStatefulInput").val();
var rawApparmor = $("#instanceRawApparmorInput").val();
var rawQemu = $("#instanceRawQemuInput").val();
var rawQemuConf = $("#instanceRawQemuConfInput").val();
var securityDevLxd = $("#instanceSecurityDevLxdInput").val();
var securityProtectionDelete = $("#instanceSecurityProtectionDeleteInput").val();
var securitySecureboot = $("#instanceSecuritySecurebootInput").val();
@ -1026,6 +1038,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"&migration_stateful=" + encodeURI(migrationStateful) +
"&raw_apparmor=" + encodeURI(rawApparmor) +
"&raw_qemu=" + encodeURI(rawQemu) +
"&raw_qemu_conf=" + encodeURI(rawQemuConf) +
"&security_devlxd=" + encodeURI(securityDevLxd) +
"&security_protection_delete=" + encodeURI(securityProtectionDelete) +
"&security_secureboot=" + encodeURI(securitySecureboot) +