From d2f0e9f481d9aaa1b59f6f28b97d8b38646a0857 Mon Sep 17 00:00:00 2001 From: xad1 Date: Fri, 21 Apr 2023 23:43:56 +0100 Subject: [PATCH] Refactored many functions to use pointers/references to remove code duplication and improve memory usage. Minor UI changes. Some doxygen comments. --- include/json/retrieve.h | 12 +- include/json/send.h | 12 +- include/json/utils.h | 10 +- include/menu.h | 12 +- src/json/retrieve.cpp | 299 +++++++++++++++++++++------------------- src/json/send.cpp | 118 ++++++---------- src/json/utils.cpp | 47 +++++-- src/main.cpp | 16 ++- src/manage.cpp | 20 ++- src/menu.cpp | 253 ++++++++++++++++------------------ src/statistics.cpp | 22 +-- 11 files changed, 422 insertions(+), 399 deletions(-) diff --git a/include/json/retrieve.h b/include/json/retrieve.h index d25a8df..4856a27 100644 --- a/include/json/retrieve.h +++ b/include/json/retrieve.h @@ -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 */ diff --git a/include/json/send.h b/include/json/send.h index 1dea592..3ec2ee8 100644 --- a/include/json/send.h +++ b/include/json/send.h @@ -1,12 +1,12 @@ #ifndef JSON_SEND_H #define JSON_SEND_H #include -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 */ diff --git a/include/json/utils.h b/include/json/utils.h index 8d3e0a7..edaa455 100644 --- a/include/json/utils.h +++ b/include/json/utils.h @@ -3,10 +3,10 @@ #include #include //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 */ diff --git a/include/menu.h b/include/menu.h index 42c243d..f368ee1 100644 --- a/include/menu.h +++ b/include/menu.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; diff --git a/src/json/retrieve.cpp b/src/json/retrieve.cpp index 8f85fbf..ac96b86 100644 --- a/src/json/retrieve.cpp +++ b/src/json/retrieve.cpp @@ -5,163 +5,139 @@ #include #include -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(); + 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(); + array[i].cpu = nodes[i]["cpu"].as(); + array[i].threads = nodes[i]["maxcpu"].as(); + array[i].onlineStatus = nodes[i]["status"].as(); + array[i].mem = nodes[i]["mem"].as(); + array[i].maxmem = nodes[i]["maxmem"].as(); + array[i].disk = nodes[i]["disk"].as(); + array[i].maxdisk = nodes[i]["maxdisk"].as(); + array[i].uptime = nodes[i]["uptime"].as(); + } + + numNodes = nodes.size(); + nodeArray = array; +} - JsonArray nodes = doc["data"].as(); - Node *nodeArray = new Node[nodes.size()]; - - for (int i = 0; i < nodes.size(); i++) - { - nodeArray[i].name = nodes[i]["node"].as(); - nodeArray[i].cpu = nodes[i]["cpu"].as(); - nodeArray[i].threads = nodes[i]["maxcpu"].as(); - nodeArray[i].onlineStatus = nodes[i]["status"].as(); - nodeArray[i].mem = nodes[i]["mem"].as(); - nodeArray[i].maxmem = nodes[i]["maxmem"].as(); - nodeArray[i].disk = nodes[i]["disk"].as(); - nodeArray[i].maxdisk = nodes[i]["maxdisk"].as(); - nodeArray[i].uptime = nodes[i]["uptime"].as(); - } - - *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(); + Container *array = new Container[containers.size()]; + + for (int i = 0; i < containers.size(); i++) + { + array[i].name = containers[i]["name"].as(); + array[i].id = containers[i]["vmid"].as(); + array[i].onlineStatus = containers[i]["status"].as(); + array[i].maxmem = containers[i]["maxmem"].as(); + array[i].maxdisk = containers[i]["maxdisk"].as(); + array[i].uptime = containers[i]["uptime"].as(); } - 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(); + 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(); - Container *containerArray = new Container[containers.size()]; - - for (int i = 0; i < containers.size(); i++) - { - containerArray[i].name = containers[i]["name"].as(); - containerArray[i].id = containers[i]["vmid"].as(); - containerArray[i].onlineStatus = containers[i]["status"].as(); - containerArray[i].maxmem = containers[i]["maxmem"].as(); - containerArray[i].maxdisk = containers[i]["maxdisk"].as(); - containerArray[i].uptime = containers[i]["uptime"].as(); - } - - *numContainers = containers.size(); - return containerArray; + array[i].name = vms[i]["name"].as(); + array[i].id = vms[i]["vmid"].as(); + array[i].onlineStatus = vms[i]["status"].as(); + array[i].maxmem = vms[i]["maxmem"].as(); + array[i].maxdisk = vms[i]["maxdisk"].as(); + array[i].uptime = vms[i]["uptime"].as(); + array[i].netin = vms[i]["netin"].as(); + array[i].netout = vms[i]["netout"].as(); } - 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(); + 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(); - VM *vmArray = new VM[vms.size()]; - - for (int i = 0; i < vms.size(); i++) - { - vmArray[i].name = vms[i]["name"].as(); - vmArray[i].id = vms[i]["vmid"].as(); - vmArray[i].onlineStatus = vms[i]["status"].as(); - vmArray[i].maxmem = vms[i]["maxmem"].as(); - vmArray[i].maxdisk = vms[i]["maxdisk"].as(); - vmArray[i].uptime = vms[i]["uptime"].as(); - vmArray[i].netin = vms[i]["netin"].as(); - vmArray[i].netout = vms[i]["netout"].as(); - } - - *numVMs = vms.size(); - return vmArray; + array[i].devpath = disks[i]["devpath"].as(); + array[i].size = disks[i]["size"].as(); + array[i].used = disks[i]["used"].as(); + array[i].serial = disks[i]["serial"].as(); + array[i].model = disks[i]["model"].as(); + array[i].vendor = disks[i]["vendor"].as(); + array[i].health = disks[i]["health"].as(); } - 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(); + 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(); - Disk *diskArray = new Disk[disks.size()]; - - for (int i = 0; i < disks.size(); i++) - { - Serial.println(disks[i]["devpath"].as()); - diskArray[i].devpath = disks[i]["devpath"].as(); - diskArray[i].size = disks[i]["size"].as(); - diskArray[i].used = disks[i]["used"].as(); - diskArray[i].serial = disks[i]["serial"].as(); - diskArray[i].model = disks[i]["model"].as(); - diskArray[i].vendor = disks[i]["vendor"].as(); - diskArray[i].health = disks[i]["health"].as(); - } - - *numDisks = disks.size(); - return diskArray; + array[i].name = pools[i]["name"].as(); + array[i].free = pools[i]["free"].as(); + array[i].size = pools[i]["size"].as(); + array[i].health = pools[i]["health"].as(); } - 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 +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(); - Pool *poolArray = new Pool[pools.size()]; - - for (int i = 0; i < pools.size(); i++) - { - poolArray[i].name = pools[i]["name"].as(); - poolArray[i].free = pools[i]["free"].as(); - poolArray[i].size = pools[i]["size"].as(); - poolArray[i].health = pools[i]["health"].as(); - } - - *numPools = pools.size(); - return poolArray; + + void *itemArray = nullptr; + processData(doc, numItems, itemArray); + + return static_cast(itemArray); } - throw std::runtime_error("Can't get pool info: HTTP request failed with code: " + httpCode); -} \ No newline at end of file + 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(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(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(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(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(apiAddress, numPools, processPoolData); +} diff --git a/src/json/send.cpp b/src/json/send.cpp index 936d8c3..5998aef 100644 --- a/src/json/send.cpp +++ b/src/json/send.cpp @@ -5,10 +5,12 @@ #include #include -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); - } -} \ No newline at end of file +/// @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, ""); +} diff --git a/src/json/utils.cpp b/src/json/utils.cpp index 24310cd..7db3735 100644 --- a/src/json/utils.cpp +++ b/src/json/utils.cpp @@ -3,43 +3,58 @@ #include #include -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) diff --git a/src/main.cpp b/src/main.cpp index af72c84..8c2fdc6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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"); } diff --git a/src/manage.cpp b/src/manage.cpp index 3d59cbf..4adcc1d 100644 --- a/src/manage.cpp +++ b/src/manage.cpp @@ -6,13 +6,14 @@ #include #include +/// @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) diff --git a/src/menu.cpp b/src/menu.cpp index b9f10aa..50209c3 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -6,13 +6,14 @@ #include #include - +// 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); - 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); - 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); - 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); + GO.lcd.println((*d).devpath); } -void listDisks(Disk *disks, int numItems) +void poolMenuPrint(void *pool) +{ + Pool *p = static_cast(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(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(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(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; - } -} \ No newline at end of file +void listVMs(VM *vms, const int &numItems) +{ + void *item; + listItems(vms, &item, "Select VM", numItems, sizeof(VM), true, vmMenuPrint); + VM *vm = static_cast(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(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(item); + selectedPool = (*pool).name; +} diff --git a/src/statistics.cpp b/src/statistics.cpp index aaf24d9..078efcf 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -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;