add pci device resource to config,
implement endpoints for getting available devices, update used resource calculation to include pci devices
This commit is contained in:
parent
6c434263e3
commit
53c101ac4a
@ -48,4 +48,4 @@ server {
|
||||
2. Start nginx with the new configurations by running `systemctl reload nginx`
|
||||
|
||||
## Result
|
||||
After these steps, the ProxmoxAAS Client should be avaliable and fully functional at `client.<FQDN>`.
|
||||
After these steps, the ProxmoxAAS Client should be available and fully functional at `client.<FQDN>`.
|
@ -76,6 +76,11 @@
|
||||
"compact": true,
|
||||
"unit": "B/s",
|
||||
"display": true
|
||||
},
|
||||
"pci": {
|
||||
"type": "list",
|
||||
"whitelist": true,
|
||||
"display": true
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
60
src/main.js
60
src/main.js
@ -5,7 +5,7 @@ import cors from "cors";
|
||||
import morgan from "morgan";
|
||||
import api from "../package.json" assert {type: "json"};
|
||||
|
||||
import { requestPVE, handleResponse, getDiskInfo } from "./pve.js";
|
||||
import { requestPVE, handleResponse, getDiskInfo, getDeviceInfo, getUsedResources } from "./pve.js";
|
||||
import { checkAuth, approveResources, getUserResources } from "./utils.js";
|
||||
import { db, pveAPIToken, listenPort, hostname, domain } from "./db.js";
|
||||
|
||||
@ -614,7 +614,7 @@ app.delete("/api/instance/network/delete", async (req, res) => {
|
||||
* - vmid: Number - vm id number to destroy
|
||||
* - hostpci: String - hostpci number
|
||||
* responses:
|
||||
* - 200: PVE PCI Device Object
|
||||
* - 200: {device_name: PVE PCI Device Object}
|
||||
* - 401: {auth: false, path: String}
|
||||
* - 500: {error: String}
|
||||
*/
|
||||
@ -632,14 +632,54 @@ app.get("/api/instance/pci", async (req, res) => {
|
||||
}
|
||||
let device = config[`hostpci${req.query.hostpci}`].split(",")[0];
|
||||
// get node's pci devices
|
||||
let result = (await requestPVE(`/nodes/${req.query.node}/hardware/pci`, "GET", req.cookies, null, pveAPIToken)).data.data;
|
||||
let deviceData = [];
|
||||
result.forEach((element) => {
|
||||
if (element.id.startsWith(device)) {
|
||||
deviceData.push(element);
|
||||
}
|
||||
});
|
||||
res.status(200).send(deviceData);
|
||||
let deviceData = await getDeviceInfo(req.query.node, req.query.type, req.query.vmid, device);
|
||||
if (!deviceData) {
|
||||
res.status(500).send({ error: `Could not find hostpci${req.query.hostpci}=${device} in ${req.query.node}.` });
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
res.status(200).send({ device_name: deviceData });
|
||||
res.end();
|
||||
return;
|
||||
});
|
||||
|
||||
/**
|
||||
* GET - get available pcie devices given node and user
|
||||
* request:
|
||||
* - node: String - vm host node id
|
||||
* responses:
|
||||
* - 200: [PVE PCI Device Object]
|
||||
* - 401: {auth: false, path: String}
|
||||
* - 500: {error: String}
|
||||
*/
|
||||
app.get("/api/nodes/pci", async (req, res) => {
|
||||
// check auth
|
||||
let auth = await checkAuth(req.cookies, res);
|
||||
if (!auth) { return; }
|
||||
// get remaining user resources
|
||||
let userAvailPci = (await getUserResources(req, req.cookies.username)).avail.pci;
|
||||
// get node pci devices
|
||||
let nodeAvailPci = (await requestPVE(`/nodes/${req.query.node}/hardware/pci`, "GET", req.body.cookies, null, pveAPIToken)).data.data;
|
||||
// for each node container, get its config and remove devices which are already used
|
||||
let vms = (await requestPVE(`/nodes/${req.query.node}/qemu`, "GET", req.body.cookies, null, pveAPIToken)).data.data;
|
||||
for (let vm of vms) {
|
||||
let config = (await requestPVE(`/nodes/${req.query.node}/qemu/${vm.vmid}/config`, "GET", req.body.cookies, null, pveAPIToken)).data.data;
|
||||
Object.keys(config).forEach((key) => {
|
||||
if (key.startsWith("hostpci")) {
|
||||
let device_id = config[key].split(",")[0];
|
||||
let allfn = !device_id.includes(".");
|
||||
|
||||
if (allfn) { // if allfn, remove all devices which include the same id as already allocated device
|
||||
nodeAvailPci = nodeAvailPci.filter(element => !element.id.includes(device_id));
|
||||
}
|
||||
else { // if not allfn, remove only device with exact id match
|
||||
nodeAvailPci = nodeAvailPci.filter(element => !element.id === device_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
nodeAvailPci = nodeAvailPci.filter(nodeAvail => userAvailPci.some((userAvail) => { return nodeAvail.device_name && nodeAvail.device_name.includes(userAvail) }));
|
||||
res.status(200).send(nodeAvailPci);
|
||||
res.end();
|
||||
return;
|
||||
});
|
||||
|
35
src/pve.js
35
src/pve.js
@ -71,12 +71,18 @@ export async function getUsedResources(req, resourceMeta) {
|
||||
let used = {};
|
||||
let diskprefixes = [];
|
||||
for (let resourceName of Object.keys(resourceMeta)) {
|
||||
used[resourceName] = 0;
|
||||
if (resourceMeta[resourceName].type === "storage") {
|
||||
used[resourceName] = 0;
|
||||
for (let diskPrefix of resourceMeta[resourceName].disks) {
|
||||
diskprefixes.push(diskPrefix);
|
||||
}
|
||||
}
|
||||
else if (resourceMeta[resourceName].type === "list") {
|
||||
used[resourceName] = [];
|
||||
}
|
||||
else {
|
||||
used[resourceName] = 0;
|
||||
}
|
||||
}
|
||||
for (let instance of response.data.data) {
|
||||
if (instance.type === "lxc" || instance.type === "qemu") {
|
||||
@ -92,9 +98,13 @@ export async function getUsedResources(req, resourceMeta) {
|
||||
used[diskInfo.storage] += Number(diskInfo.size);
|
||||
}
|
||||
}
|
||||
else if (key.startsWith("net")) {
|
||||
if (config[key].includes("rate=")) { // only count instances with a rate limit
|
||||
used.network += Number(config[key].split("rate=")[1].split(",")[0]);
|
||||
else if (key.startsWith("net") && config[key].includes("rate=")) { // only count net instances with a rate limit
|
||||
used.network += Number(config[key].split("rate=")[1].split(",")[0]);
|
||||
}
|
||||
else if (key.startsWith("hostpci")) {
|
||||
let deviceInfo = await getDeviceInfo(instance.node, instance.type, instance.vmid, config[key].split(",")[0]);
|
||||
if (deviceInfo) { // only count if device exists
|
||||
used.pci.push(deviceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,4 +125,21 @@ export async function getDiskInfo(node, type, vmid, disk) {
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getDeviceInfo(node, type, vmid, qid) {
|
||||
try {
|
||||
let result = (await requestPVE(`/nodes/${node}/hardware/pci`, "GET", null, null, pveAPIToken)).data.data;
|
||||
let deviceData = [];
|
||||
result.forEach((element) => {
|
||||
if (element.id.startsWith(qid)) {
|
||||
deviceData.push(element);
|
||||
}
|
||||
});
|
||||
deviceData.sort((a, b) => { return a.id < b.id })
|
||||
return deviceData[0].device_name;
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
13
src/utils.js
13
src/utils.js
@ -33,7 +33,16 @@ export async function getUserResources(req, username) {
|
||||
let max = db.getUserConfig(username).resources.max;
|
||||
let avail = {};
|
||||
Object.keys(max).forEach((k) => {
|
||||
avail[k] = max[k] - used[k];
|
||||
if (dbResources[k] && dbResources[k].type === "list") {
|
||||
avail[k] = structuredClone(max[k]);
|
||||
used[k].forEach((usedDeviceName) => {
|
||||
let index = avail[k].findIndex((maxElement) => usedDeviceName.includes(maxElement));
|
||||
avail[k].splice(index, 1);
|
||||
});
|
||||
}
|
||||
else {
|
||||
avail[k] = max[k] - used[k];
|
||||
}
|
||||
});
|
||||
return { used: used, max: max, avail: avail, resources: dbResources };
|
||||
}
|
||||
@ -49,7 +58,7 @@ export async function approveResources(req, username, request) {
|
||||
approved = false;
|
||||
}
|
||||
else if (resources[key].type === "list") {
|
||||
if (max[key].includes(request[key]) != resources[key].whitelist) {
|
||||
if (avail[key].includes(request[key]) != resources[key].whitelist) {
|
||||
approved = false;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user