Refactored many functions to use pointers/references to remove code duplication and improve memory usage. Minor UI changes. Some doxygen comments.

main
xad1 3 years ago
parent 400c838d1c
commit d2f0e9f481

@ -52,11 +52,13 @@ typedef struct {
String health;
} Pool;
Node *getNodeInfo(int *numNodes);
Container *getContainerInfo(int *numContainers, String node);
VM *getVMInfo(int *numVMs, String node);
Disk *getDiskInfo(int *numDisks, String node);
Pool *getPoolInfo(int *numPools, String node);
Node *getNodeInfo(int &numNodes);
Container *getContainerInfo(int &numContainers, const String &node);
VM *getVMInfo(int &numVMs, const String &node);
Disk *getDiskInfo(int &numDisks, const String &node);
Pool *getPoolInfo(int &numPools, const String &node);
typedef void (*ProcessDataCallback)(DynamicJsonDocument&, int&, void*&);
#endif /* JSON_RETRIEVE_H */

@ -1,12 +1,12 @@
#ifndef JSON_SEND_H
#define JSON_SEND_H
#include <Arduino.h>
void restartVM(String node, int vmid);
void restartContainer(String node, int containerid);
void startVM(String node, int vmid);
void startContainer(String node, int containerid);
void stopVM(String node, int vmid);
void stopContainer(String node, int containerid);
void restartVM(const String &node, const int &vmid);
void restartContainer(const String &node, const int &containerid);
void startVM(const String &node, const int &vmid);
void startContainer(const String &node, const int &containerid);
void stopVM(const String &node, const int &vmid);
void stopContainer(const String &node, const int &containerid);
#endif /* JSON_SEND_H */

@ -3,10 +3,10 @@
#include <ArduinoJson.h>
#include <json/retrieve.h>
//JsonObject getNode(String name, JsonArray nodes);
Node getNode(String name);
Container getContainer(int id, String node);
VM getVM(int id, String node);
Disk getDisk(String devpath, String node);
Pool getPool(String name, String node);
Node getNode(const String &name);
Container getContainer(const int &id, const String &node);
VM getVM(const int &id, const String &node);
Disk getDisk(const String &devpath, const String &node);
Pool getPool(const String &name, const String &node);
#endif /* JSON_UTILS_H */

@ -9,15 +9,17 @@ typedef struct {
void (*function)();
} MenuItem;
void listNodes(Node* nodes, int numItems);
void listContainers(Container* containers, int numItems);
void listVMs(VM* vms, int numItems);
void listDisks(Disk* disks, int numItems);
void listPools(Pool* pools, int numItems);
void listNodes(Node* nodes, const int &numItems);
void listContainers(Container* containers, const int &numItems);
void listVMs(VM* vms, const int &numItems);
void listDisks(Disk* disks, const int &numItems);
void listPools(Pool* pools, const int &numItems);
void mainMenu();
void manageContainerMenu();
void manageVMMenu();
typedef void (*MenuPrintCallback)(void*);
extern int selectedItem;
extern int selectedPage;

@ -5,163 +5,139 @@
#include <json/retrieve.h>
#include <stdexcept>
Node *getNodeInfo(int *numNodes)
/// @brief This is a callback function to process the json data of a node provided by the API into an array of Node structs.
/// @param doc A reference to the json document containing the data from the API.
/// @param numNodes A reference to an integer containing the number of nodes retrieved from the API.
/// @param nodeArray A pointer to the array used to hold the nodes once they have been processed.
void processNodeData(DynamicJsonDocument &doc, int &numNodes, void *&nodeArray)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/";
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.GET();
JsonArray nodes = doc["data"].as<JsonArray>();
Node *array = new Node[nodes.size()];
if (httpCode > 0)
for (int i = 0; i < nodes.size(); i++)
{
String json = http.getString();
DynamicJsonDocument doc(8096);
deserializeJson(doc, json);
array[i].name = nodes[i]["node"].as<String>();
array[i].cpu = nodes[i]["cpu"].as<float>();
array[i].threads = nodes[i]["maxcpu"].as<int>();
array[i].onlineStatus = nodes[i]["status"].as<String>();
array[i].mem = nodes[i]["mem"].as<long long>();
array[i].maxmem = nodes[i]["maxmem"].as<long long>();
array[i].disk = nodes[i]["disk"].as<long long>();
array[i].maxdisk = nodes[i]["maxdisk"].as<long long>();
array[i].uptime = nodes[i]["uptime"].as<long long>();
}
numNodes = nodes.size();
nodeArray = array;
}
JsonArray nodes = doc["data"].as<JsonArray>();
Node *nodeArray = new Node[nodes.size()];
for (int i = 0; i < nodes.size(); i++)
{
nodeArray[i].name = nodes[i]["node"].as<String>();
nodeArray[i].cpu = nodes[i]["cpu"].as<float>();
nodeArray[i].threads = nodes[i]["maxcpu"].as<int>();
nodeArray[i].onlineStatus = nodes[i]["status"].as<String>();
nodeArray[i].mem = nodes[i]["mem"].as<long long>();
nodeArray[i].maxmem = nodes[i]["maxmem"].as<long long>();
nodeArray[i].disk = nodes[i]["disk"].as<long long>();
nodeArray[i].maxdisk = nodes[i]["maxdisk"].as<long long>();
nodeArray[i].uptime = nodes[i]["uptime"].as<long long>();
}
*numNodes = nodes.size();
return nodeArray;
/// @brief This is a callback function to process the json data of a container provided by the api into an array of Container structs.
/// @param doc A reference to the json document containing the data from the API.
/// @param numContainers A reference to an integer containing the number of containers retrieved from the API.
/// @param containerArray A pointer to the array used to hold the containers once they have been processed.
void processContainerData(DynamicJsonDocument &doc, int &numContainers, void *&containerArray)
{
JsonArray containers = doc["data"].as<JsonArray>();
Container *array = new Container[containers.size()];
for (int i = 0; i < containers.size(); i++)
{
array[i].name = containers[i]["name"].as<String>();
array[i].id = containers[i]["vmid"].as<int>();
array[i].onlineStatus = containers[i]["status"].as<String>();
array[i].maxmem = containers[i]["maxmem"].as<long long>();
array[i].maxdisk = containers[i]["maxdisk"].as<long long>();
array[i].uptime = containers[i]["uptime"].as<long long>();
}
throw std::runtime_error("Can't get node info: HTTP request failed with code: " + httpCode);
numContainers = containers.size();
containerArray = array;
}
Container *getContainerInfo(int *numContainers, String node)
/// @brief This is a callback function to process the json data of a VM provided by the api into an array of VM structs.
/// @param doc A reference to the json document containing the data from the API.
/// @param numVMs A reference to an integer containing the number of VMs retrieved from the API.
/// @param vmArray A pointer to the array used to hold the VMs once they have been processed.
void processVMData(DynamicJsonDocument &doc, int &numVMs, void *&vmArray)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/lxc";
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.GET();
JsonArray vms = doc["data"].as<JsonArray>();
VM *array = new VM[vms.size()];
if (httpCode > 0)
for (int i = 0; i < vms.size(); i++)
{
String json = http.getString();
DynamicJsonDocument doc(8096);
deserializeJson(doc, json);
JsonArray containers = doc["data"].as<JsonArray>();
Container *containerArray = new Container[containers.size()];
for (int i = 0; i < containers.size(); i++)
{
containerArray[i].name = containers[i]["name"].as<String>();
containerArray[i].id = containers[i]["vmid"].as<int>();
containerArray[i].onlineStatus = containers[i]["status"].as<String>();
containerArray[i].maxmem = containers[i]["maxmem"].as<long long>();
containerArray[i].maxdisk = containers[i]["maxdisk"].as<long long>();
containerArray[i].uptime = containers[i]["uptime"].as<long long>();
}
*numContainers = containers.size();
return containerArray;
array[i].name = vms[i]["name"].as<String>();
array[i].id = vms[i]["vmid"].as<int>();
array[i].onlineStatus = vms[i]["status"].as<String>();
array[i].maxmem = vms[i]["maxmem"].as<long long>();
array[i].maxdisk = vms[i]["maxdisk"].as<long long>();
array[i].uptime = vms[i]["uptime"].as<long long>();
array[i].netin = vms[i]["netin"].as<long long>();
array[i].netout = vms[i]["netout"].as<long long>();
}
throw std::runtime_error("Can't get container info: HTTP request failed with code: " + httpCode);
numVMs = vms.size();
vmArray = array;
}
VM *getVMInfo(int *numVMs, String node)
/// @brief This is a callback function to process the json data of a disk provided by the api into an array of disk structs.
/// @param doc A reference to the json document containing the data from the API.
/// @param numDisks A reference to an integer containing the number of disks retrieved from the API.
/// @param diskArray A pointer to the array used to hold the disks once they have been processed.
void processDiskData(DynamicJsonDocument &doc, int &numDisks, void *&diskArray)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/qemu";
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.GET();
JsonArray disks = doc["data"].as<JsonArray>();
Disk *array = new Disk[disks.size()];
if (httpCode > 0)
for (int i = 0; i < disks.size(); i++)
{
String json = http.getString();
DynamicJsonDocument doc(8096);
deserializeJson(doc, json);
JsonArray vms = doc["data"].as<JsonArray>();
VM *vmArray = new VM[vms.size()];
for (int i = 0; i < vms.size(); i++)
{
vmArray[i].name = vms[i]["name"].as<String>();
vmArray[i].id = vms[i]["vmid"].as<int>();
vmArray[i].onlineStatus = vms[i]["status"].as<String>();
vmArray[i].maxmem = vms[i]["maxmem"].as<long long>();
vmArray[i].maxdisk = vms[i]["maxdisk"].as<long long>();
vmArray[i].uptime = vms[i]["uptime"].as<long long>();
vmArray[i].netin = vms[i]["netin"].as<long long>();
vmArray[i].netout = vms[i]["netout"].as<long long>();
}
*numVMs = vms.size();
return vmArray;
array[i].devpath = disks[i]["devpath"].as<String>();
array[i].size = disks[i]["size"].as<long long>();
array[i].used = disks[i]["used"].as<String>();
array[i].serial = disks[i]["serial"].as<String>();
array[i].model = disks[i]["model"].as<String>();
array[i].vendor = disks[i]["vendor"].as<String>();
array[i].health = disks[i]["health"].as<String>();
}
throw std::runtime_error("Can't get VM info: HTTP request failed with code: " + httpCode);
numDisks = disks.size();
diskArray = array;
}
Disk *getDiskInfo(int *numDisks, String node)
/// @brief This is a callback function to process the json data of a ZFS pool provided by the api into an array of Pool structs.
/// @param doc A reference to the json document containing the data from the API.
/// @param numPools A reference to an integer containing the number of pools retrieved from the API.
/// @param poolArray A pointer to the array used to hold the pools once they have been processed.
void processPoolData(DynamicJsonDocument &doc, int &numPools, void *&poolArray)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/disks/list";
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.GET();
JsonArray pools = doc["data"].as<JsonArray>();
Pool *array = new Pool[pools.size()];
if (httpCode > 0)
for (int i = 0; i < pools.size(); i++)
{
String json = http.getString();
DynamicJsonDocument doc(32768);
deserializeJson(doc, json);
JsonArray disks = doc["data"].as<JsonArray>();
Disk *diskArray = new Disk[disks.size()];
for (int i = 0; i < disks.size(); i++)
{
Serial.println(disks[i]["devpath"].as<String>());
diskArray[i].devpath = disks[i]["devpath"].as<String>();
diskArray[i].size = disks[i]["size"].as<long long>();
diskArray[i].used = disks[i]["used"].as<String>();
diskArray[i].serial = disks[i]["serial"].as<String>();
diskArray[i].model = disks[i]["model"].as<String>();
diskArray[i].vendor = disks[i]["vendor"].as<String>();
diskArray[i].health = disks[i]["health"].as<String>();
}
*numDisks = disks.size();
return diskArray;
array[i].name = pools[i]["name"].as<String>();
array[i].free = pools[i]["free"].as<long long>();
array[i].size = pools[i]["size"].as<long long>();
array[i].health = pools[i]["health"].as<String>();
}
throw std::runtime_error("Can't get disk info: HTTP request failed with code: " + httpCode);
numPools = pools.size();
poolArray = array;
}
Pool *getPoolInfo(int *numPools, String node)
/// @brief This is the main function to retrieve data from the API. Is is a generic function to handle retrieving all the data for the program.
/// @tparam T The type of the item being retrieved.
/// @param apiAddress The API path to get the appropriate data.
/// @param numItems A reference to an integer to store the number of items retrieved from the API.
/// @param processData A callback function to process the data coming in from the API.
/// @return The array of items populated by the process function is returned in the type defined by T.
template <typename T>
T *apiCall(const String &apiAddress, int &numItems, ProcessDataCallback processData)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/disks/zfs";
http.begin(PROXMOX_ADDRESS + apiAddress);
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.GET();
@ -170,19 +146,60 @@ Pool *getPoolInfo(int *numPools, String node)
String json = http.getString();
DynamicJsonDocument doc(32768);
deserializeJson(doc, json);
JsonArray pools = doc["data"].as<JsonArray>();
Pool *poolArray = new Pool[pools.size()];
for (int i = 0; i < pools.size(); i++)
{
poolArray[i].name = pools[i]["name"].as<String>();
poolArray[i].free = pools[i]["free"].as<long long>();
poolArray[i].size = pools[i]["size"].as<long long>();
poolArray[i].health = pools[i]["health"].as<String>();
}
*numPools = pools.size();
return poolArray;
void *itemArray = nullptr;
processData(doc, numItems, itemArray);
return static_cast<T *>(itemArray);
}
throw std::runtime_error("Can't get pool info: HTTP request failed with code: " + httpCode);
}
throw std::runtime_error("HTTP request failed with code: " + httpCode);
}
/// @brief Function to retrieve node data from the API. Calls the apiCall() function with the appropriate parameters for nodes.
/// @param numNodes A reference to an integer to hold the number of nodes retrieved.
/// @return The array of nodes created by the apiCall() function.
Node *getNodeInfo(int &numNodes)
{
String apiAddress = "/api2/json/nodes/";
return apiCall<Node>(apiAddress, numNodes, processNodeData);
}
/// @brief Function to retrieve container data from the API. Calls the apiCall() function with the appropriate parameters for containers.
/// @param numContainers A reference to an integer to hold the number of containers retrieved.
/// @param node A reference to a string which defines which node we are retrieving container data from.
/// @return The array of containers created by the apiCall() function.
Container *getContainerInfo(int &numContainers, const String &node)
{
String apiAddress = "/api2/json/nodes/" + node + "/lxc";
return apiCall<Container>(apiAddress, numContainers, processContainerData);
}
/// @brief Function to retrieve VM data from the API. Calls the apiCall() function with the appropriate parameters for VMs.
/// @param numVMs A reference to an integer to hold the number of VMs retrieved.
/// @param node A reference to a string which defines which node we are retrieving VM data from.
/// @return The array of VMs created by the apiCall() function.
VM *getVMInfo(int &numVMs, const String &node)
{
String apiAddress = "/api2/json/nodes/" + node + "/qemu";
return apiCall<VM>(apiAddress, numVMs, processVMData);
}
/// @brief Function to retrieve disk data from the API. Calls the apiCall() function with the appropriate parameters for disks.
/// @param numDisks A reference to an integer to hold the number of disks retrieved.
/// @param node A reference to a string which defines which node we are retrieving disk data from.
/// @return The array of disks created by the apiCall() function.
Disk *getDiskInfo(int &numDisks, const String &node)
{
String apiAddress = "/api2/json/nodes/" + node + "/disks/list";
return apiCall<Disk>(apiAddress, numDisks, processDiskData);
}
/// @brief Function to retrieve pool data from the API. Calls the apiCall() function with the appropriate parameters for pools.
/// @param numPools A reference to an integer to hold the number of pools retrieved.
/// @param node A reference to a string which defines which node we are retrieving pool data from.
/// @return The array of pools created by the apiCall() function.
Pool *getPoolInfo(int &numPools, const String &node)
{
String apiAddress = "/api2/json/nodes/" + node + "/disks/zfs";
return apiCall<Pool>(apiAddress, numPools, processPoolData);
}

@ -5,10 +5,12 @@
#include <json/retrieve.h>
#include <stdexcept>
void restartVM(String node, int vmid)
{
/// @brief This is a function to send data to the API. Used for commands such as restarting. It also supports including a post payload to send additional information the API.
/// @param apiPath Refrence to a string which holds the appropriate path on the API to send data.
/// @param post Reference to a string which contains a payload to send to the API.
void apiSend(const String &apiPath, const String &post) {
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/qemu/" + vmid + "/status/reboot";
String apiAddress = PROXMOX_ADDRESS + apiPath;
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
@ -18,91 +20,61 @@ void restartVM(String node, int vmid)
http.end();
if(httpCode == 0) {
throw std::runtime_error("Can't restart VM. Error code: " + httpCode);
throw std::runtime_error("Can't stop. Error code: " + httpCode);
}
}
void restartContainer(String node, int containerid)
/// @brief The function to restart a given VM. Calls the apiSend() function with the appropriate parameters to restart a VM.
/// @param node A reference to a string which contains the name of the node that is running the VM that is being restarted.
/// @param vmid A reference to an integer which holds the ID of the VM being restarted.
void restartVM(const String &node, const int &vmid)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/lxc/" + containerid + "/status/reboot";
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.POST("");
http.end();
if(httpCode == 0) {
throw std::runtime_error("Can't restart container. Error code: " + httpCode);
}
String apiPath = "/api2/json/nodes/" + node + "/qemu/" + vmid + "/status/reboot";
apiSend(apiPath, "");
}
void startVM(String node, int vmid)
/// @brief The function to restart a given container. Calls the apiSend() function with the appropriate parameters to restart a container.
/// @param node A reference to a string which contains the name of the node that is running the container that is being restarted.
/// @param containerid A reference to an integer which holds the ID of the container being restarted.
void restartContainer(const String &node, const int &containerid)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/qemu/" + vmid + "/status/start";
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.POST("");
http.end();
if(httpCode == 0) {
throw std::runtime_error("Can't start VM. Error code: " + httpCode);
}
String apiPath = "/api2/json/nodes/" + node + "/lxc/" + containerid + "/status/reboot";
apiSend(apiPath, "");
}
void startContainer(String node, int containerid)
/// @brief The function to start a given VM. Calls the apiSend() function with the appropriate parameters to start a VM.
/// @param node A reference to a string which contains the name of the node that is running the VM that is being started.
/// @param vmid A reference to an integer which holds the ID of the VM being started.
void startVM(const String &node, const int &vmid)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/lxc/" + containerid + "/status/start";
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.POST("");
http.end();
if(httpCode == 0) {
throw std::runtime_error("Can't start container. Error code: " + httpCode);
}
String apiPath = "/api2/json/nodes/" + node + "/qemu/" + vmid + "/status/start";
apiSend(apiPath, "");
}
void stopVM(String node, int vmid)
/// @brief The function to start a given container. Calls the apiSend() function with the appropriate parameters to start a container.
/// @param node A reference to a string which contains the name of the node that is running the container that is being started.
/// @param containerid A reference to an integer which holds the ID of the container being started.
void startContainer(const String &node, const int &containerid)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/qemu/" + vmid + "/status/stop";
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.POST("");
http.end();
String apiPath = "/api2/json/nodes/" + node + "/lxc/" + containerid + "/status/start";
apiSend(apiPath, "");
if(httpCode == 0) {
throw std::runtime_error("Can't stop VM. Error code: " + httpCode);
}
}
void stopContainer(String node, int containerid)
/// @brief The function to stop a given VM. Calls the apiSend() function with the appropriate parameters to stop a VM.
/// @param node A reference to a string which contains the name of the node that is running the VM that is being stopped.
/// @param vmid A reference to an integer which holds the ID of the VM being stopped.
void stopVM(const String &node, const int &vmid)
{
HTTPClient http;
String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/lxc/" + containerid + "/status/stop";
String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET;
char authc[auth.length() + 1];
auth.toCharArray(authc, auth.length() + 1);
http.begin(apiAddress);
http.setAuthorization(authc);
int httpCode = http.POST("");
http.end();
String apiPath = "/api2/json/nodes/" + node + "/qemu/" + vmid + "/status/stop";
apiSend(apiPath, "");
}
if(httpCode == 0) {
throw std::runtime_error("Can't stop container. Error code: " + httpCode);
}
}
/// @brief The function to stop a given container. Calls the apiSend() function with the appropriate parameters to stop a container.
/// @param node A reference to a string which contains the name of the node that is running the container that is being stopped.
/// @param containerid A reference to an integer which holds the ID of the container being stopped.
void stopContainer(const String &node, const int &containerid)
{
String apiPath = "/api2/json/nodes/" + node + "/lxc/" + containerid + "/status/stop";
apiSend(apiPath, "");
}

@ -3,43 +3,58 @@
#include <utils.h>
#include <stdexcept>
Node getNode(String name)
/// @brief Function to retrieve a specific node. Retrieves an array of the nodes and uses a for loop to check the name of each one until it finds one that matches the provided name.
/// @param name A reference to a string containing the name of the node being searched for.
/// @return The node which matches the name provided.
Node getNode(const String &name)
{
int numNodes;
Node *nodes = getNodeInfo(&numNodes);
Node *nodes = getNodeInfo(numNodes);
for (int i = 0; i < numNodes; i++)
{
if (nodes[i].name == name)
{
return nodes[i];
Node node = nodes[i];
delete[] nodes;
return node;
}
}
throw std::runtime_error("Can't find the requested node in the array");
}
Container getContainer(int id, String node)
/// @brief Function to retrieve a specific container. Retrieves an array of the containers and uses a for loop to check the id of each one until it finds one that matches the provided id.
/// @param id A reference to an integer which contains the id of the container being searched for.
/// @param node A reference to a string which contains the name of the node being searched on.
/// @return A container that matches the id provided.
Container getContainer(const int &id, const String &node)
{
int numContainers;
Container *containers = getContainerInfo(&numContainers, node);
Container *containers = getContainerInfo(numContainers, node);
for (int i = 0; i < numContainers; i++)
{
if (containers[i].id == id)
{
return containers[i];
Container container = containers[i];
delete[] containers;
return container;
}
}
throw std::runtime_error("Can't find the requested container in the array");
}
VM getVM(int id, String node)
/// @brief Function to retrieve a specific VM. Retrieves an array of the VMs and uses a for loop to check the id of each one until it finds one that matches the provided id.
/// @param id A reference to an integer which contains the id of the VM being searched for.
/// @param node A reference to a string which contains the name of the node being searched on.
/// @return A VM that matches the id provided.
VM getVM(const int &id, const String &node)
{
int numVMs;
VM *vms = getVMInfo(&numVMs, node);
VM *vms = getVMInfo(numVMs, node);
for (int i = 0; i < numVMs; i++)
{
if (vms[i].id == id)
@ -51,11 +66,15 @@ VM getVM(int id, String node)
throw std::runtime_error("Can't find the requested vm in the array");
}
Disk getDisk(String devpath, String node)
/// @brief Function to retrieve a specific disk. Retrieves an array of the containers and uses a for loop to check the devpath (e.g /dev/sda) of each one until it finds one that matches the provided devpath.
/// @param devpath A reference to a string which contains the devpath of the container being searched for.
/// @param node A reference to a string which contains the name of the node being searched on.
/// @return A disk that matches the devpath provided.
Disk getDisk(const String &devpath, const String &node)
{
int numDisks;
Disk *disks = getDiskInfo(&numDisks, node);
Disk *disks = getDiskInfo(numDisks, node);
for (int i = 0; i < numDisks; i++)
{
if (disks[i].devpath == devpath)
@ -67,11 +86,15 @@ Disk getDisk(String devpath, String node)
throw std::runtime_error("Can't find the requested disk in the array");
}
Pool getPool(String name, String node)
/// @brief Function to retrieve a specific ZFS pool. Retrieves an array of the pools and uses a for loop to check the name of each one until it finds one that matches the provided name.
/// @param name A reference to a string which contains the name of the pool being searched for.
/// @param node A reference to a string which contains the name of the node being searched on.
/// @return A pool that matches the name provided.
Pool getPool(const String &name, const String &node)
{
int numPools;
Pool *pools = getPoolInfo(&numPools, node);
Pool *pools = getPoolInfo(numPools, node);
for (int i = 0; i < numPools; i++)
{
if (pools[i].name == name)

@ -10,21 +10,28 @@
void loop()
{
// Check if still connected to a WiFi network. If not then display this to the user and then attempt to reconnect.
if (WiFi.status() != WL_CONNECTED)
{
displayError("WiFi Connection Lost");
connectWiFi();
}
//Try catch to handle errors in the program. If an error is found it is displayed to the user before the program continues.
try
{
// Display the list of nodes for the user to choose from.
int numNodes;
Node *nodes = getNodeInfo(&numNodes);
Node *nodes = getNodeInfo(numNodes);
listNodes(nodes, numNodes);
delete[] nodes;
// Reset menu selection variables before continuing to the main menu.
selectedItem = 0;
selectedPage = 0;
mainMenu();
}
catch (const std::exception &e)
{
@ -38,6 +45,11 @@ void setup()
Serial.begin(115200);
GO.begin();
GO.lcd.setTextWrap(false);
/**
* Prompt user to enter pin if one is set. Will only continue if the pin is correct.
* If a pin has not been set then warn the user before continuing.
**/
if (LOCK_PIN != "")
{
bool pinCorrect = false;
@ -49,6 +61,6 @@ void setup()
displayError("Warning: No pin set");
}
// Connect to the configured WiFi network.
connectWiFi();
Serial.println("done setup");
}

@ -6,13 +6,14 @@
#include <odroid_go.h>
#include <utils.h>
/// @brief User interface function to restart a VM. Retrieves an array of all VMs on the selected node, calls the list function to allow the user to pick a VM and then calls the restartVM() function on that VM to send the request to the API. Returns to the manage VM menu afterwards.
void vmRestart()
{
Serial.println("vm restart");
selectedItem = 0;
selectedVM = 0;
int numVMs;
VM *vms = getVMInfo(&numVMs, selectedNode);
VM *vms = getVMInfo(numVMs, selectedNode);
listVMs(vms, numVMs);
delete[] vms;
if (selectedVM > 0)
@ -26,13 +27,14 @@ void vmRestart()
manageVMMenu();
}
/// @brief User interface function to restart a container. Retrieves an array of all containers on the selected node, calls the list function to allow the user to pick a container and then calls the restartContainer() function on that container to send the request to the API. Returns to the manage container menu afterwards.
void containerRestart()
{
Serial.println("lxc restart");
selectedItem = 0;
selectedLXC = 0;
int numContainers;
Container *containers = getContainerInfo(&numContainers, selectedNode);
Container *containers = getContainerInfo(numContainers, selectedNode);
listContainers(containers, numContainers);
delete[] containers;
if (selectedLXC > 0)
@ -47,14 +49,14 @@ void containerRestart()
manageContainerMenu();
}
/// @brief User interface function to start a VM. Retrieves an array of all VMs on the selected node, calls the list function to allow the user to pick a VM and then calls the startVM() function on that VM to send the request to the API. Returns to the manage VM menu afterwards.
void vmStart()
{
Serial.println("vm start");
selectedItem = 0;
selectedVM = 0;
int numVMs;
VM *vms = getVMInfo(&numVMs, selectedNode);
VM *vms = getVMInfo(numVMs, selectedNode);
listVMs(vms, numVMs);
delete[] vms;
if (selectedVM > 0)
@ -71,13 +73,14 @@ void vmStart()
manageVMMenu();
}
/// @brief User interface function to start a container. Retrieves an array of all containers on the selected node, calls the list function to allow the user to pick a container and then calls the startContainer() function on that container to send the request to the API. Returns to the manage container menu afterwards.
void containerStart()
{
Serial.println("lxc start");
selectedItem = 0;
selectedLXC = 0;
int numContainers;
Container *containers = getContainerInfo(&numContainers, selectedNode);
Container *containers = getContainerInfo(numContainers, selectedNode);
listContainers(containers, numContainers);
delete[] containers;
if (selectedLXC > 0)
@ -91,13 +94,15 @@ void containerStart()
manageContainerMenu();
}
/// @brief User interface function to stop a VM. Retrieves an array of all VMs on the selected node, calls the list function to allow the user to pick a VM and then calls the startVM() function on that VM to send the request to the API. Returns to the manage VM menu afterwards.
void vmStop()
{
Serial.println("vm stop");
selectedItem = 0;
selectedVM = 0;
int numVMs;
VM *vms = getVMInfo(&numVMs, selectedNode);
VM *vms = getVMInfo(numVMs, selectedNode);
listVMs(vms, numVMs);
delete[] vms;
if (selectedVM > 0)
@ -114,13 +119,14 @@ void vmStop()
manageVMMenu();
}
/// @brief User interface function to stop a container. Retrieves an array of all containers on the selected node, calls the list function to allow the user to pick a container and then calls the stopContainer() function on that container to send the request to the API. Returns to the manage container menu afterwards.
void containerStop()
{
Serial.println("lxc stop");
selectedItem = 0;
selectedLXC = 0;
int numContainers;
Container *containers = getContainerInfo(&numContainers, selectedNode);
Container *containers = getContainerInfo(numContainers, selectedNode);
listContainers(containers, numContainers);
delete[] containers;
if (selectedLXC > 0)

@ -6,13 +6,14 @@
#include <global.h>
#include <manage.h>
// UI constants for the menu system.
const int MAIN_TEXT_COLOR = WHITE;
const int MAIN_TEXT_SIZE = 2;
const int ITEMS_PER_PAGE = 12;
const int OFFLINE_COLOR = RED;
const int ONLINE_COLOR = GREEN;
// Initializing variables which hold the selections.
int selectedItem = 0;
int selectedPage = 0;
String selectedNode = "";
@ -31,20 +32,24 @@ MenuItem mainMenuItems[] = {
{"Manage VM", &manageVMMenu},
{"Manage Container", &manageContainerMenu}};
// Array of the VM managment menu items.
MenuItem manageVMItems[] = {
{"Restart", &vmRestart},
{"Start", &vmStart},
{"Stop", &vmStop},
};
// Array of the container management menu items.
MenuItem manageContainerItems[] = {
{"Restart", &containerRestart},
{"Start", &containerStart},
{"Stop", &containerStop},
};
int buttonListener(int numItems)
/// @brief Function to listen for button presses. Once called it will loop until a button is pressed. For the directional pad the selected page or item is changed and 0 is returned. For the A/B buttons 1/2 is returned to indicate that the user wants to select something or go back.
/// @param numItems A reference an integer which holds the number of items for the user to pick from in the menu.
/// @return An integer indicating what action was taken. 1 for the A button, 2 for the B button, 0 for any other input.
int buttonListener(const int &numItems)
{
delay(300);
while (true)
@ -91,7 +96,15 @@ int buttonListener(int numItems)
return 0;
}
void drawMenu(MenuItem menuItems[], int numItems, String menuTitle)
/** @brief A function to draw a menu on the screen. This is for static menus which have a function tied to each item.
* Loops through the menu items and draws a ">" before the currently selected item.
* After printing the items, listens for button inputs. If the A button is pressed then the function of the currently selected item is run. If the B button is pressed the function stops running (returning the previous menu).
* If the directional pad buttons are pressed it means that the selected item or page has changed, so the function is run again to redraw the menu with the new values.
* @param menuItems An array of menuItems used to draw the menu.
* @param numItems A reference to an int which holds the number of items in the menu.
* @param menuTitle A reference to a string which holds the title of the menu.
**/
void drawMenu(MenuItem menuItems[], const int &numItems, const String &menuTitle)
{
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
@ -105,6 +118,8 @@ void drawMenu(MenuItem menuItems[], int numItems, String menuTitle)
if (selectedItem == i)
{
GO.lcd.print("> ");
} else {
GO.lcd.print(" ");
}
GO.lcd.println(menuItems[i].name);
}
@ -124,6 +139,9 @@ void drawMenu(MenuItem menuItems[], int numItems, String menuTitle)
}
}
/**
* @brief Function to draw the main menu. Calls the draw menu function using the array of main menu items.
*/
void mainMenu()
{
selectedItem = 0;
@ -131,7 +149,9 @@ void mainMenu()
drawMenu(mainMenuItems, numItems, "Main Menu");
}
/**
* @brief Function to draw the VM management menu. Calls the draw menu function using the array of VM mangement menu items.
*/
void manageVMMenu()
{
selectedItem = 0;
@ -140,6 +160,9 @@ void manageVMMenu()
mainMenu();
}
/**
* @brief Function to draw the container management menu. Calls the draw menu function using the array of container management menu items.
*/
void manageContainerMenu()
{
selectedItem = 0;
@ -148,180 +171,146 @@ void manageContainerMenu()
mainMenu();
}
void listNodes(Node *nodes, int numItems)
void containerMenuPrint(void *container)
{
GO.lcd.clearDisplay();
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.setTextSize(MAIN_TEXT_SIZE);
GO.lcd.setCursor(0, 0);
GO.lcd.println("Select Node (Page " + String(selectedPage + 1) + ")");
GO.lcd.println("--------------------------");
Container *cont = static_cast<Container *>(container);
for (int i = selectedPage * ITEMS_PER_PAGE; i < numItems && i < (selectedPage + 1) * ITEMS_PER_PAGE; i++)
if ((*cont).onlineStatus == "running")
{
if (selectedItem == i)
{
GO.lcd.print("> ");
}
if (nodes[i].onlineStatus == "online") {
GO.lcd.setTextColor(ONLINE_COLOR);
} else {
GO.lcd.setTextColor(OFFLINE_COLOR);
}
GO.lcd.print("O");
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.println(nodes[i].name);
GO.lcd.setTextColor(ONLINE_COLOR);
}
switch (buttonListener(numItems))
else
{
case 1:
Serial.println("selected " + selectedItem);
selectedNode = nodes[selectedItem].name;
break;
default:
listNodes(nodes, numItems);
break;
GO.lcd.setTextColor(OFFLINE_COLOR);
}
}
GO.lcd.print("O ");
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.println(String((*cont).id) + ": " + (*cont).name);
};
void listContainers(Container *containers, int numItems)
void vmMenuPrint(void *vm)
{
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.setTextSize(MAIN_TEXT_SIZE);
GO.lcd.println("Select Container (Page " + String(selectedPage + 1) + ")");
GO.lcd.println("--------------------------");
VM *v = static_cast<VM *>(vm);
for (int i = selectedPage * ITEMS_PER_PAGE; i < numItems && i < (selectedPage + 1) * ITEMS_PER_PAGE; i++)
if ((*v).onlineStatus == "running")
{
if (selectedItem == i)
{
GO.lcd.print("> ");
}
if (containers[i].onlineStatus == "running") {
GO.lcd.setTextColor(ONLINE_COLOR);
} else {
GO.lcd.setTextColor(OFFLINE_COLOR);
}
GO.lcd.print("O");
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.println(String(containers[i].id) + ": " + containers[i].name);
GO.lcd.setTextColor(ONLINE_COLOR);
}
switch (buttonListener(numItems))
else
{
case 1:
selectedLXC = containers[selectedItem].id;
break;
case 2:
Serial.println("back");
break;
default:
listContainers(containers, numItems);
break;
GO.lcd.setTextColor(OFFLINE_COLOR);
}
}
GO.lcd.print("O ");
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.println(String((*v).id) + ": " + (*v).name);
};
void listVMs(VM *vms, int numItems)
void nodeMenuPrint(void *node)
{
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.setTextSize(MAIN_TEXT_SIZE);
GO.lcd.println("Select VM (Page " + String(selectedPage + 1) + ")");
GO.lcd.println("--------------------------");
Node *n = static_cast<Node *>(node);
for (int i = selectedPage * ITEMS_PER_PAGE; i < numItems && i < (selectedPage + 1) * ITEMS_PER_PAGE; i++)
if ((*n).onlineStatus == "online")
{
if (selectedItem == i)
{
GO.lcd.print("> ");
}
if (vms[i].onlineStatus == "running") {
GO.lcd.setTextColor(ONLINE_COLOR);
} else {
GO.lcd.setTextColor(OFFLINE_COLOR);
}
GO.lcd.print("O");
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.println(String(vms[i].id) + ": " + vms[i].name);
GO.lcd.setTextColor(ONLINE_COLOR);
}
switch (buttonListener(numItems))
else
{
case 1:
selectedVM = vms[selectedItem].id;
break;
case 2:
Serial.println("back");
break;
default:
listVMs(vms, numItems);
break;
GO.lcd.setTextColor(OFFLINE_COLOR);
}
GO.lcd.print("O ");
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.println((*n).name);
}
void diskMenuPrint(void *disk)
{
Disk *d = static_cast<Disk *>(disk);
GO.lcd.println((*d).devpath);
}
void listDisks(Disk *disks, int numItems)
void poolMenuPrint(void *pool)
{
Pool *p = static_cast<Pool *>(pool);
GO.lcd.println((*p).name);
}
void listItems(void *items, void **item, const String title, const int &numItems, const int &itemSize, const bool &backEnabled, MenuPrintCallback printMachine)
{
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.setTextSize(MAIN_TEXT_SIZE);
GO.lcd.println("Select Disk (Page " + String(selectedPage + 1) + ")");
GO.lcd.println(title + " (Page " + String(selectedPage + 1) + ")");
GO.lcd.println("--------------------------");
// Cast items to char for pointer calculation
char *itemsAsBytes = static_cast<char *>(items);
for (int i = selectedPage * ITEMS_PER_PAGE; i < numItems && i < (selectedPage + 1) * ITEMS_PER_PAGE; i++)
{
if (selectedItem == i)
{
GO.lcd.print("> ");
} else {
GO.lcd.print(" ");
}
GO.lcd.println(disks[i].devpath);
printMachine(itemsAsBytes + i * itemSize);
}
switch (buttonListener(numItems))
{
case 1:
selectedDisk = disks[selectedItem].devpath;
*item = itemsAsBytes + selectedItem * itemSize;
break;
case 2:
Serial.println("back");
break;
if (backEnabled)
{
Serial.println("back");
break;
}
default:
listDisks(disks, numItems);
listItems(items, item, title, numItems, itemSize, backEnabled, printMachine);
break;
}
}
void listPools(Pool *pools, int numItems)
void listNodes(Node *nodes, const int &numItems)
{
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.setTextSize(MAIN_TEXT_SIZE);
GO.lcd.println("Select Pool (Page " + String(selectedPage + 1) + ")");
GO.lcd.println("--------------------------");
void *item;
listItems(nodes, &item, "Select Node", numItems, sizeof(Node), false, nodeMenuPrint);
Node *node = static_cast<Node *>(item);
Serial.println((*node).name);
selectedNode = (*node).name;
}
for (int i = selectedPage * ITEMS_PER_PAGE; i < numItems && i < (selectedPage + 1) * ITEMS_PER_PAGE; i++)
{
if (selectedItem == i)
{
GO.lcd.print("> ");
}
GO.lcd.println(pools[i].name);
}
void listContainers(Container *containers, const int &numItems)
{
void *item;
listItems(containers, &item, "Select Container", numItems, sizeof(Container), true, containerMenuPrint);
Container *container = static_cast<Container *>(item);
selectedLXC = (*container).id;
}
switch (buttonListener(numItems))
{
case 1:
selectedPool = pools[selectedItem].name;
break;
case 2:
Serial.println("back");
break;
default:
listPools(pools, numItems);
break;
}
}
void listVMs(VM *vms, const int &numItems)
{
void *item;
listItems(vms, &item, "Select VM", numItems, sizeof(VM), true, vmMenuPrint);
VM *vm = static_cast<VM *>(item);
selectedVM = (*vm).id;
}
void listDisks(Disk *disks, const int &numItems)
{
void *item;
listItems(disks, &item, "Select Disk", numItems, sizeof(Disk), true, diskMenuPrint);
Disk *disk = static_cast<Disk *>(item);
selectedDisk = (*disk).devpath;
}
void listPools(Pool *pools, const int &numItems)
{
void *item;
listItems(pools, &item, "Select Pool", numItems, sizeof(Pool), true, poolMenuPrint);
Pool *pool = static_cast<Pool *>(item);
selectedPool = (*pool).name;
}

@ -9,7 +9,7 @@ const int BACKGROUND_COLOR = WHITE;
const int TEXT_COLOR = BLACK;
const int TEXT_SIZE = 2;
String convertBytes(long long value)
String convertBytes(const long long &value)
{
if (value > 1099511627776)
{
@ -30,7 +30,7 @@ String convertBytes(long long value)
return String(value) + " Bytes";
}
String convertTime(long long value)
String convertTime(const long long &value)
{
if (value > 3600)
{
@ -43,7 +43,7 @@ String convertTime(long long value)
return String(value) + " secs";
}
void printNodeStats(Node node)
void printNodeStats(const Node &node)
{
GO.lcd.fillScreen(BACKGROUND_COLOR);
GO.lcd.setCursor(0, 0);
@ -63,7 +63,7 @@ void printNodeStats(Node node)
GO.lcd.println("Max Disk: " + convertBytes(node.maxdisk));
}
void printContainerStats(Container container)
void printContainerStats(const Container &container)
{
GO.lcd.fillScreen(BACKGROUND_COLOR);
@ -79,7 +79,7 @@ void printContainerStats(Container container)
GO.lcd.println("Max Disk: " + convertBytes(container.maxdisk));
}
void printVMStats(VM vm)
void printVMStats(const VM &vm)
{
GO.lcd.fillScreen(BACKGROUND_COLOR);
@ -94,7 +94,7 @@ void printVMStats(VM vm)
GO.lcd.println("Max Disk: " + convertBytes(vm.maxdisk));
}
void printDiskStats(Disk disk)
void printDiskStats(const Disk &disk)
{
GO.lcd.fillScreen(BACKGROUND_COLOR);
@ -112,7 +112,7 @@ void printDiskStats(Disk disk)
GO.lcd.println("Health: " + disk.health);
}
void printPoolStats(Pool pool)
void printPoolStats(const Pool &pool)
{
GO.lcd.fillScreen(BACKGROUND_COLOR);
@ -156,7 +156,7 @@ void containerInfo()
selectedPage = 0;
int numContainers;
Container *containers = getContainerInfo(&numContainers, selectedNode);
Container *containers = getContainerInfo(numContainers, selectedNode);
listContainers(containers, numContainers);
delete[] containers;
@ -186,7 +186,7 @@ void vmInfo()
selectedItem = 0;
selectedPage = 0;
int numVMs;
VM *vms = getVMInfo(&numVMs, selectedNode);
VM *vms = getVMInfo(numVMs, selectedNode);
listVMs(vms, numVMs);
delete[] vms;
unsigned long lastUpdate = millis();
@ -213,7 +213,7 @@ void diskInfo()
selectedItem = 0;
selectedPage = 0;
int numDisks;
Disk *disks = getDiskInfo(&numDisks, selectedNode);
Disk *disks = getDiskInfo(numDisks, selectedNode);
listDisks(disks, numDisks);
delete[] disks;
@ -236,7 +236,7 @@ void poolInfo()
selectedItem = 0;
selectedPage = 0;
int numPools;
Pool *pools = getPoolInfo(&numPools, selectedNode);
Pool *pools = getPoolInfo(numPools, selectedNode);
listPools(pools, numPools);
delete[] pools;

Loading…
Cancel
Save