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
83a03bfd7b
commit
49192daac6
@ -48,4 +48,4 @@ server {
|
|||||||
2. Start nginx with the new configurations by running `systemctl reload nginx`
|
2. Start nginx with the new configurations by running `systemctl reload nginx`
|
||||||
|
|
||||||
## Result
|
## 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,
|
"compact": true,
|
||||||
"unit": "B/s",
|
"unit": "B/s",
|
||||||
"display": true
|
"display": true
|
||||||
|
},
|
||||||
|
"pci": {
|
||||||
|
"type": "list",
|
||||||
|
"whitelist": true,
|
||||||
|
"display": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
|
60
src/main.js
60
src/main.js
@ -5,7 +5,7 @@ import cors from "cors";
|
|||||||
import morgan from "morgan";
|
import morgan from "morgan";
|
||||||
import api from "../package.json" assert {type: "json"};
|
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 { checkAuth, approveResources, getUserResources } from "./utils.js";
|
||||||
import { db, pveAPIToken, listenPort, hostname, domain } from "./db.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
|
* - vmid: Number - vm id number to destroy
|
||||||
* - hostpci: String - hostpci number
|
* - hostpci: String - hostpci number
|
||||||
* responses:
|
* responses:
|
||||||
* - 200: PVE PCI Device Object
|
* - 200: {device_name: PVE PCI Device Object}
|
||||||
* - 401: {auth: false, path: String}
|
* - 401: {auth: false, path: String}
|
||||||
* - 500: {error: 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];
|
let device = config[`hostpci${req.query.hostpci}`].split(",")[0];
|
||||||
// get node's pci devices
|
// get node's pci devices
|
||||||
let result = (await requestPVE(`/nodes/${req.query.node}/hardware/pci`, "GET", req.cookies, null, pveAPIToken)).data.data;
|
let deviceData = await getDeviceInfo(req.query.node, req.query.type, req.query.vmid, device);
|
||||||
let deviceData = [];
|
if (!deviceData) {
|
||||||
result.forEach((element) => {
|
res.status(500).send({ error: `Could not find hostpci${req.query.hostpci}=${device} in ${req.query.node}.` });
|
||||||
if (element.id.startsWith(device)) {
|
res.end();
|
||||||
deviceData.push(element);
|
return;
|
||||||
}
|
}
|
||||||
});
|
res.status(200).send({ device_name: deviceData });
|
||||||
res.status(200).send(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();
|
res.end();
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
35
src/pve.js
35
src/pve.js
@ -71,12 +71,18 @@ export async function getUsedResources(req, resourceMeta) {
|
|||||||
let used = {};
|
let used = {};
|
||||||
let diskprefixes = [];
|
let diskprefixes = [];
|
||||||
for (let resourceName of Object.keys(resourceMeta)) {
|
for (let resourceName of Object.keys(resourceMeta)) {
|
||||||
used[resourceName] = 0;
|
|
||||||
if (resourceMeta[resourceName].type === "storage") {
|
if (resourceMeta[resourceName].type === "storage") {
|
||||||
|
used[resourceName] = 0;
|
||||||
for (let diskPrefix of resourceMeta[resourceName].disks) {
|
for (let diskPrefix of resourceMeta[resourceName].disks) {
|
||||||
diskprefixes.push(diskPrefix);
|
diskprefixes.push(diskPrefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (resourceMeta[resourceName].type === "list") {
|
||||||
|
used[resourceName] = [];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
used[resourceName] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (let instance of response.data.data) {
|
for (let instance of response.data.data) {
|
||||||
if (instance.type === "lxc" || instance.type === "qemu") {
|
if (instance.type === "lxc" || instance.type === "qemu") {
|
||||||
@ -92,9 +98,13 @@ export async function getUsedResources(req, resourceMeta) {
|
|||||||
used[diskInfo.storage] += Number(diskInfo.size);
|
used[diskInfo.storage] += Number(diskInfo.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (key.startsWith("net")) {
|
else if (key.startsWith("net") && config[key].includes("rate=")) { // only count net instances with a rate limit
|
||||||
if (config[key].includes("rate=")) { // only count instances with a rate limit
|
used.network += Number(config[key].split("rate=")[1].split(",")[0]);
|
||||||
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 {
|
catch {
|
||||||
return null;
|
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 max = db.getUserConfig(username).resources.max;
|
||||||
let avail = {};
|
let avail = {};
|
||||||
Object.keys(max).forEach((k) => {
|
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 };
|
return { used: used, max: max, avail: avail, resources: dbResources };
|
||||||
}
|
}
|
||||||
@ -49,7 +58,7 @@ export async function approveResources(req, username, request) {
|
|||||||
approved = false;
|
approved = false;
|
||||||
}
|
}
|
||||||
else if (resources[key].type === "list") {
|
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;
|
approved = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user