Skip to content

Commit

Permalink
v1.2.8
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewalanpenning committed Mar 10, 2021
1 parent 6a1a9e5 commit 3ae6f4c
Show file tree
Hide file tree
Showing 20 changed files with 137 additions and 58 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# v1.2.8
- added edit and delete actions for storage volumes
- added action to create new storage volumes, with form options for size and content_type

# v1.2.7
- fixed bug that caused new storage pools to default to size 30GB
- improved list of disk devices displayed on instance page
Expand Down
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
# lxd-dashboard
This LXD dashboard by LXDWARE is a web-based user interface that makes it easy to manage the containers and virtual machines on your LXD servers. Some of the features include:
This LXD dashboard is a web-based user interface allowing users to easily manage LXD/LXC servers. Some of the features include:

- Creating/launching new LXD container and virtual machine instances
- Starting, stopping, renaming, and deleting LXD instances
- Copying instances to create new instances
- Creating, restoring and deleting snapshots of instances
- Creating instances from snaphots
- Migrating instances between hosts on an LXD cluster
- Downloading LXD container and virtual machine images to your host
- Creating, editing and applying LXD profiles
- Creating and editing networks, storage pools, and projects
- Selecting between projects on a host
- Sending shell commands to instances using exec
- Creating and downloading backups of your LXD instance to your local computer
- Connect and manage multiple LXD servers
- Create LXD container and virtual machine instances from either a form or JSON input
- Start, stop, rename, and delete LXD instances
- Copy instances to create new instances
- Create, restore and delete snapshots of instances
- Create instances from snaphots
- Migrate instances between hosts on an LXD cluster
- Download LXD container and virtual machine images to LXD hosts
- Create, edit, apply, and remove LXD profiles
- Create, edit, and delete networks, storage pools, storage volumes, and projects
- Switch between projects on an LXD host
- Send shell commands to instances using exec
- Create and download backups of LXD instance to your local computer

You can deploy the LXD dashboard on either an LXC or Docker container. The software is built primarly using Ubuntu, NGINX, and PHP.
The LXD dashboard can be deployed on either an LXC or Docker container. The software is built primarily using Ubuntu, NGINX, and PHP.

Installation instructions can be found on the LXDWARE web site located at https://lxdware.com/installation
2 changes: 1 addition & 1 deletion admin/about.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<body>
<p><strong>About</strong>: The LXD Dashboard by LXDWARE is open source software that provides a web-based user interface for controlling your LXD infrastructure.</p>
<p><strong>Version</strong>: 1.2.7</p>
<p><strong>Version</strong>: 1.2.8</p>
<p><strong>License</strong>: GPL-3.0</p>
<p><strong>URL</strong>: https://lxdware.com</p>
</body>
Expand Down
5 changes: 5 additions & 0 deletions admin/php/lxd/storage-volume-list.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
echo '",';
echo '"' . htmlentities($storage_volume['name']) . '",';
echo '"' . htmlentities($storage_volume['type']) . '",';
echo '"' . htmlentities($storage_volume['location']) . '",';
echo '"' . htmlentities($storage_volume['content_type']) . '",';

echo '"';
$ii = 0;
Expand All @@ -54,6 +56,9 @@
echo '",';

echo '"';
echo "<a href='#' onclick=loadStorageVolumeJson('".$storage_volume['type']."/".$storage_volume['name']."')><i class='fas fa-edit fa-lg' style='color:#ddd' title='Edit' aria-hidden='true'></i></a>";
echo " &nbsp ";
echo "<a href='#' onclick=deleteStorageVolume('".$storage_volume['type']."/".$storage_volume['name']."')><i class='fas fa-trash-alt fa-lg' style='color:#ddd' title='Delete' aria-hidden='true'></i></a>";
echo '"';

echo " ]";
Expand Down
31 changes: 17 additions & 14 deletions admin/php/lxd/storage-volumes.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@
$action = filter_var(urldecode($_GET['action']), FILTER_SANITIZE_STRING);
if (isset($_GET['storage_pool']))
$storage_pool = filter_var(urldecode($_GET['storage_pool']), FILTER_SANITIZE_STRING);
if (isset($_GET['storage_volume_url']))
$storage_volume_url = filter_var(urldecode($_GET['storage_volume_url']), FILTER_SANITIZE_STRING);


if (isset($_GET['name']))
$image = filter_var(urldecode($_GET['name']), FILTER_SANITIZE_STRING);
if (isset($_GET['repo']))
$repo = filter_var(urldecode($_GET['repo']), FILTER_SANITIZE_STRING);

//Instantiate the POST variable
$name = filter_var(urldecode($_GET['name']), FILTER_SANITIZE_STRING);
if (isset($_GET['content_type']))
$content_type = filter_var(urldecode($_GET['content_type']), FILTER_SANITIZE_STRING);
if (isset($_GET['size']))
$size = filter_var(urldecode($_GET['size']), FILTER_SANITIZE_STRING);

//Instantiate the POST variable
if (isset($_POST['json']))
$json = $_POST['json'];

Expand All @@ -37,23 +35,28 @@

//Run the matching action
switch ($action) {
case "createStorageVolume":
$url = $url . "/1.0/storage-pools?project=" . $project;
case "createStorageVolumeForm":
$url = $url . "/1.0/storage-pools/" . $storage_pool . "/volumes?project=" . $project;
$data = escapeshellarg('{"config": {"size": "'.$size.'GB"}, "name": "'.$name.'", "type": "custom", "content_type": "'.$content_type.'"}');
$results = shell_exec("sudo curl -k -L --connect-timeout 3 --cert $cert --key $key -X POST -d $data '$url'");
break;
case "createStorageVolumeJson":
$url = $url . "/1.0/storage-pools/" . $storage_pool . "/volumes?project=" . $project;
$data = escapeshellarg($json);
$results = shell_exec("sudo curl -k -L --connect-timeout 3 --cert $cert --key $key -X POST -d $data '$url'");
break;
case "deleteStorageVolume":
$url = $url . $storage_volume_url . "?project=" . $project;
$url = $url . "/1.0/storage-pools/" . $storage_pool . "/volumes/" . $name . "?project=" . $project;
$data = escapeshellarg('{}');
$results = shell_exec("sudo curl -k -L --connect-timeout 3 --cert $cert --key $key -X DELETE -d $data '$url'");
break;
case "updateStorageVolume":
$url = $url . $storage_volume_url . "?project=" . $project;
$url = $url . "/1.0/storage-pools/" . $storage_pool . "/volumes/" . $name . "?project=" . $project;
$data = escapeshellarg($json);
$results = shell_exec("sudo curl -k -L --connect-timeout 3 --cert $cert --key $key -X PUT -d $data '$url'");
break;
case "loadStorageVolume":
$url = $url . $storage_volume_url . "?project=" . $project;
$url = $url . "/1.0/storage-pools/" . $storage_pool . "/volumes/" . $name . "?project=" . $project;
$results = shell_exec("sudo curl -k -L --connect-timeout 3 --cert $cert --key $key -X GET '$url'");
break;
}
Expand Down
124 changes: 95 additions & 29 deletions admin/storage-volumes.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Storage Volumes</h6>
<div class="dropdown no-arrow">
<a class="dropdown-toggle mr-2" href="#" data-toggle="modal" data-target="#createStorageVolumeModal" title="New Storage Volume" aria-hidden="true">
<i class="fas fa-plus fa-sm fa-fw"></i> Storage Volume</a>
</div>
</div>
<!-- Card Body -->
Expand Down Expand Up @@ -182,34 +184,81 @@ <h6 class="m-0 font-weight-bold text-primary">Storage Volumes</h6>
<i class="fas fa-angle-up"></i>
</a>

<!-- Create Storage Volume Modal-->
<div class="modal fade" id="createStorageVolumeModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Create Storage Volume</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="jsonCreateInput" rows="16" placeholder="Enter JSON data"></textarea>
</pre>
<!-- Create Storage Volume Modal-->
<div class="modal fade" id="createStorageVolumeModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Create Storage Volume</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="form-tab" data-toggle="tab" href="#form" role="tab" aria-controls="form" aria-selected="true">Form</a>
</li>
<li class="nav-item">
<a class="nav-link" id="json-tab" data-toggle="tab" href="#json" role="tab" aria-controls="json" aria-selected="false">JSON</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="form" role="tabpanel" aria-labelledby="form-tab">
<br />
<div class="row">
<label class="col-3 col-form-label text-right">Name: </label>
<div class="col-7">
<div class="form-group">
<input type="text" id="storageVolumeNameCreate" class="form-control" placeholder="Storage Volume Name" name="storage_volume_name">
</div>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Content Type: </label>
<div class="col-7">
<div class="form-group">
<select id="storageVolumeContentTypeCreate" class="form-control" name="content_type">
<option value="filesystem" selected>filesystem</option>
<option value="block">block</option>
</select>
</div>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Size (GB): </label>
<div class="col-4">
<div class="form-group">
<input type="number" id="storageVolumeSizeCreate" class="form-control" value="10" name="storage_volume_size">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="createStorageVolumeForm()" data-dismiss="modal">Submit</a>
</div>
</div>
<div class="tab-pane fade" id="json" role="tabpanel" aria-labelledby="json-tab">
<br />
<div class="row">
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="jsonCreateInput" rows="16" placeholder="Enter JSON data"></textarea>
</pre>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="createStorageVolumeJson()" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="createStorageVolume()" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>

<!-- Edit Storage Volume Modal-->
<div class="modal fade" id="editStorageVolumeModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
Expand Down Expand Up @@ -338,6 +387,8 @@ <h5 class="modal-title" id="exampleModalLabel">About LXDWARE Dashboard</h5>
{},
{ title: "Name" },
{ title: "Type" },
{ title: "Location" },
{ title: "Content Type" },
{ title: "Used by" },
{ title: "Action" }
],
Expand All @@ -354,11 +405,26 @@ <h5 class="modal-title" id="exampleModalLabel">About LXDWARE Dashboard</h5>
setInterval(() => { reloadPageContent(); }, 5000);
}

//API uses sync, NOT YET SETUP
function createStorageVolume(){
function createStorageVolumeForm(){
var storageVolumeName = $("#storageVolumeNameCreate").val();
var storageVolumeContentType = $("#storageVolumeContentTypeCreate").val();
var storageVolumeSize = $("#storageVolumeSizeCreate").val();
console.log("Info: creating storage volume " + storageVolumeName + " in " + storagePoolName);
$.get("./php/lxd/storage-volumes.php?remote=" + encodeURI(remoteName) + "&project=" + encodeURI(projectName) + "&name=" + encodeURI(storageVolumeName) + "&content_type=" + encodeURI(storageVolumeContentType) + "&size=" + encodeURI(storageVolumeSize) + "&storage_pool=" + encodeURI(storagePoolName) + "&action=createStorageVolumeForm", function (data) {
var operationData = JSON.parse(data);
console.log(operationData);
if (operationData.error_code >= 400){
alert(operationData.error);
}
setTimeout(() => { reloadPageContent(); }, 1000);
});
}

//API uses sync
function createStorageVolumeJson(){
var storageVolumeCreateJSON = $("#jsonCreateInput").val();
console.log("Info: creating storage volume");
$.post("./php/lxd/storage-volumes.php?remote=" + encodeURI(remoteName) + "&project=" + encodeURI(projectName) + "&action=createStorageVolume", {json: storageVolumeCreateJSON}, function (data) {
$.post("./php/lxd/storage-volumes.php?remote=" + encodeURI(remoteName) + "&project=" + encodeURI(projectName) + "&storage_pool=" + encodeURI(storagePoolName) + "&action=createStorageVolumeJson", {json: storageVolumeCreateJSON}, function (data) {
var operationData = JSON.parse(data);
console.log(operationData);
if (operationData.error_code >= 400){
Expand All @@ -372,7 +438,7 @@ <h5 class="modal-title" id="exampleModalLabel">About LXDWARE Dashboard</h5>
function loadStorageVolumeJson(storageVolumeToLoad){
console.log("Info: loading storage volume " + storageVolumeToLoad);
storageVolumeToUpdate = storageVolumeToLoad;
$.get("./php/lxd/storage-volumes.php?remote=" + encodeURI(remoteName) + "&project=" + encodeURI(projectName) + "&storage_volume_url=" + encodeURI(storageVolumeToLoad) + "&action=loadStorageVolume", function (data) {
$.get("./php/lxd/storage-volumes.php?remote=" + encodeURI(remoteName) + "&project=" + encodeURI(projectName) + "&name=" + encodeURI(storageVolumeToLoad) + "&storage_pool=" + encodeURI(storagePoolName) + "&action=loadStorageVolume", function (data) {
var operationData = JSON.parse(data);
console.log(operationData);
if (operationData.error_code >= 400){
Expand All @@ -388,7 +454,7 @@ <h5 class="modal-title" id="exampleModalLabel">About LXDWARE Dashboard</h5>
function updateStorageVolume(){
var storageVolumeUpdateJSON = $("#jsonEditInput").val();
console.log("Info: updating storage volume " + storageVolumeToUpdate);
$.post("./php/lxd/storage-volumes.php?remote=" + encodeURI(remoteName) + "&project=" + encodeURI(projectName) + "&storage_volume_url=" + encodeURI(storageVolumeToUpdate) + "&action=updateStorageVolume", {json: storageVolumeUpdateJSON}, function (data) {
$.post("./php/lxd/storage-volumes.php?remote=" + encodeURI(remoteName) + "&project=" + encodeURI(projectName) + "&name=" + encodeURI(storageVolumeToUpdate) + "&storage_pool=" + encodeURI(storagePoolName) + "&action=updateStorageVolume", {json: storageVolumeUpdateJSON}, function (data) {
var operationData = JSON.parse(data);
console.log(operationData);
if (operationData.error_code >= 400){
Expand All @@ -401,7 +467,7 @@ <h5 class="modal-title" id="exampleModalLabel">About LXDWARE Dashboard</h5>
//API uses sync
function deleteStorageVolume(storageVolumeToDelete){
console.log("Info: deleting storage volume " + storageVolumeToDelete);
$.get("./php/lxd/storage-volumes.php?remote=" + encodeURI(remoteName) + "&project=" + encodeURI(projectName) + "&storage_volume_url=" + encodeURI(storageVolumeToDelete) + "&action=deleteStorageVolume", function (data) {
$.get("./php/lxd/storage-volumes.php?remote=" + encodeURI(remoteName) + "&project=" + encodeURI(projectName) + "&name=" + encodeURI(storageVolumeToDelete) + "&storage_pool=" + encodeURI(storagePoolName) + "&action=deleteStorageVolume", function (data) {
var operationData = JSON.parse(data);
console.log(operationData);
if (operationData.error_code >= 400){
Expand Down
Binary file removed screenshots/Screenshot_2020-08-21 LXDWARE - 1.png
Binary file not shown.
Binary file removed screenshots/Screenshot_2020-08-21 LXDWARE - 2.png
Binary file not shown.
Binary file removed screenshots/Screenshot_2020-08-21 LXDWARE - 3.png
Binary file not shown.
Binary file removed screenshots/Screenshot_2020-08-21 LXDWARE - 4.png
Binary file not shown.
Binary file removed screenshots/Screenshot_2020-08-21 LXDWARE - 5.png
Binary file not shown.
Binary file removed screenshots/Screenshot_2020-08-21 LXDWARE.png
Binary file not shown.
Binary file added screenshots/screenshot_cluster.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/screenshot_host.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/screenshot_hosts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/screenshot_images.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/screenshot_instance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/screenshot_instances.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/screenshot_instances_new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/screenshot_profile_edit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3ae6f4c

Please sign in to comment.