improve return message when requests do not pass resource approval
This commit is contained in:
@@ -98,7 +98,7 @@ router.get(`/:node(${nodeRegexP})/pci`, async (req, res) => {
|
|||||||
|
|
||||||
// get remaining user resources
|
// get remaining user resources
|
||||||
const userAvailPci = (await getUserResources(req, userObj)).pci.nodes[params.node]; // we assume that the node list is used. TODO support global lists
|
const userAvailPci = (await getUserResources(req, userObj)).pci.nodes[params.node]; // we assume that the node list is used. TODO support global lists
|
||||||
if (userAvailPci === undefined) { // user has no avaliable devices on this node, so send an empty list
|
if (userAvailPci === undefined) { // user has no available devices on this node, so send an empty list
|
||||||
res.status(200).send([]);
|
res.status(200).send([]);
|
||||||
res.end();
|
res.end();
|
||||||
}
|
}
|
||||||
@@ -164,8 +164,8 @@ router.get(`${basePath}`, async (req, res) => {
|
|||||||
* - swap: number, optional - new amount of swap for instance
|
* - swap: number, optional - new amount of swap for instance
|
||||||
* responses:
|
* responses:
|
||||||
* - 200: PVE Task Object
|
* - 200: PVE Task Object
|
||||||
|
* - 400: {request; Object, error: string, reason: Object}
|
||||||
* - 401: {auth: false, path: string}
|
* - 401: {auth: false, path: string}
|
||||||
* - 500: {request: Object, error: string}
|
|
||||||
* - 500: PVE Task Object
|
* - 500: PVE Task Object
|
||||||
*/
|
*/
|
||||||
router.post(`${basePath}/resources`, async (req, res) => {
|
router.post(`${basePath}/resources`, async (req, res) => {
|
||||||
@@ -201,8 +201,9 @@ router.post(`${basePath}/resources`, async (req, res) => {
|
|||||||
request.cpu = params.proctype;
|
request.cpu = params.proctype;
|
||||||
}
|
}
|
||||||
// check resource approval
|
// check resource approval
|
||||||
if (!await approveResources(req, userObj, request, params.node)) {
|
const { approved, reason } = await approveResources(req, userObj, request, params.node);
|
||||||
res.status(500).send({ request, error: "Could not fulfil request." });
|
if (!approved) {
|
||||||
|
res.status(400).send({ request, error: "Not enough resources to satisfy request.", reason });
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -239,9 +240,9 @@ router.post(`${basePath}/resources`, async (req, res) => {
|
|||||||
* - rootfssize: number, optional, - size of lxc instance rootfs
|
* - rootfssize: number, optional, - size of lxc instance rootfs
|
||||||
* responses:
|
* responses:
|
||||||
* - 200: PVE Task Object
|
* - 200: PVE Task Object
|
||||||
|
* - 400: {request: Object, error: string, reason: Object}
|
||||||
* - 401: {auth: false, path: string}
|
* - 401: {auth: false, path: string}
|
||||||
* - 500: {error: string}
|
* - 500: {error: string}
|
||||||
* - 500: {request: Object, error: string}
|
|
||||||
* - 500: PVE Task Object
|
* - 500: PVE Task Object
|
||||||
*/
|
*/
|
||||||
router.post(`${basePath}/create`, async (req, res) => {
|
router.post(`${basePath}/create`, async (req, res) => {
|
||||||
@@ -312,8 +313,9 @@ router.post(`${basePath}/create`, async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check resource approval
|
// check resource approval
|
||||||
if (!await approveResources(req, userObj, request, params.node)) { // check resource approval
|
const { approved, reason } = await approveResources(req, userObj, request, params.node);
|
||||||
res.status(500).send({ request, error: "Not enough resources to satisfy request." });
|
if (!approved) {
|
||||||
|
res.status(400).send({ request, error: "Not enough resources to satisfy request.", reason });
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,8 @@ router.post("/:disk/resize", async (req, res) => {
|
|||||||
const request = {};
|
const request = {};
|
||||||
request[storage] = Number(params.size * 1024 ** 3); // setup request object
|
request[storage] = Number(params.size * 1024 ** 3); // setup request object
|
||||||
// check request approval
|
// check request approval
|
||||||
if (!await approveResources(req, userObj, request, params.node)) {
|
const { approved } = await approveResources(req, userObj, request, params.node);
|
||||||
|
if (!approved) {
|
||||||
res.status(500).send({ request, error: `Storage ${storage} could not fulfill request of size ${params.size}G.` });
|
res.status(500).send({ request, error: `Storage ${storage} could not fulfill request of size ${params.size}G.` });
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
@@ -219,7 +220,8 @@ router.post("/:disk/move", async (req, res) => {
|
|||||||
request[dstStorage] = Number(size); // always decrease destination storage by size
|
request[dstStorage] = Number(size); // always decrease destination storage by size
|
||||||
}
|
}
|
||||||
// check request approval
|
// check request approval
|
||||||
if (!await approveResources(req, userObj, request, params.node)) {
|
const { approved } = await approveResources(req, userObj, request, params.node);
|
||||||
|
if (!approved) {
|
||||||
res.status(500).send({ request, error: `Storage ${params.storage} could not fulfill request of size ${params.size}G.` });
|
res.status(500).send({ request, error: `Storage ${params.storage} could not fulfill request of size ${params.size}G.` });
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
@@ -335,7 +337,8 @@ router.post("/:disk/create", async (req, res) => {
|
|||||||
// setup request
|
// setup request
|
||||||
request[params.storage] = Number(params.size * 1024 ** 3);
|
request[params.storage] = Number(params.size * 1024 ** 3);
|
||||||
// check request approval
|
// check request approval
|
||||||
if (!await approveResources(req, userObj, request, params.node)) {
|
const { approved } = await approveResources(req, userObj, request, params.node);
|
||||||
|
if (!approved) {
|
||||||
res.status(500).send({ request, error: `Storage ${params.storage} could not fulfill request of size ${params.size}G.` });
|
res.status(500).send({ request, error: `Storage ${params.storage} could not fulfill request of size ${params.size}G.` });
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ router.post("/:netid/create", async (req, res) => {
|
|||||||
};
|
};
|
||||||
// check resource approval
|
// check resource approval
|
||||||
const userObj = global.utils.getUserObjFromUsername(req.cookies.username);
|
const userObj = global.utils.getUserObjFromUsername(req.cookies.username);
|
||||||
if (!await approveResources(req, userObj, request, params.node)) {
|
const { approved } = await approveResources(req, userObj, request, params.node);
|
||||||
|
if (!approved) {
|
||||||
res.status(500).send({ request, error: `Could not fulfil network request of ${params.rate}MB/s.` });
|
res.status(500).send({ request, error: `Could not fulfil network request of ${params.rate}MB/s.` });
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
@@ -116,7 +117,8 @@ router.post("/:netid/modify", async (req, res) => {
|
|||||||
};
|
};
|
||||||
// check resource approval
|
// check resource approval
|
||||||
const userObj = global.utils.getUserObjFromUsername(req.cookies.username);
|
const userObj = global.utils.getUserObjFromUsername(req.cookies.username);
|
||||||
if (!await approveResources(req, userObj, request, params.node)) {
|
const { approved } = await approveResources(req, userObj, request, params.node);
|
||||||
|
if (!approved) {
|
||||||
res.status(500).send({ request, error: `Could not fulfil network request of ${params.rate}MB/s.` });
|
res.status(500).send({ request, error: `Could not fulfil network request of ${params.rate}MB/s.` });
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -100,7 +100,8 @@ router.post("/:hostpci/modify", async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check resource approval
|
// check resource approval
|
||||||
if (!await approveResources(req, userObj, request, params.node)) {
|
const { approved } = await approveResources(req, userObj, request, params.node);
|
||||||
|
if (!approved) {
|
||||||
res.status(500).send({ request, error: `Could not fulfil request for ${requestedDevice.device_name}.` });
|
res.status(500).send({ request, error: `Could not fulfil request for ${requestedDevice.device_name}.` });
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
@@ -172,7 +173,8 @@ router.post("/:hostpci/create", async (req, res) => {
|
|||||||
const request = { pci: requestedDevice.device_name };
|
const request = { pci: requestedDevice.device_name };
|
||||||
// check resource approval
|
// check resource approval
|
||||||
const userObj = global.utils.getUserObjFromUsername(req.cookies.username);
|
const userObj = global.utils.getUserObjFromUsername(req.cookies.username);
|
||||||
if (!await approveResources(req, userObj, request, params.node)) {
|
const { approved } = await approveResources(req, userObj, request, params.node);
|
||||||
|
if (!approved) {
|
||||||
res.status(500).send({ request, error: `Could not fulfil request for ${requestedDevice.device_name}.` });
|
res.status(500).send({ request, error: `Could not fulfil request for ${requestedDevice.device_name}.` });
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
|
|||||||
41
src/utils.js
41
src/utils.js
@@ -222,19 +222,24 @@ export async function getUserResources (req, user) {
|
|||||||
* @param {Object} req ProxmoxAAS API request object.
|
* @param {Object} req ProxmoxAAS API request object.
|
||||||
* @param {{id: string, realm: string}} user object of user requesting additional resources.
|
* @param {{id: string, realm: string}} user object of user requesting additional resources.
|
||||||
* @param {Object} request k-v pairs of resources and requested amounts
|
* @param {Object} request k-v pairs of resources and requested amounts
|
||||||
* @returns {boolean} true if the available resources can fullfill the requested resources, false otherwise.
|
* @returns {boolean, Object} true if the available resources can fullfill the requested resources, false otherwise.
|
||||||
*/
|
*/
|
||||||
export async function approveResources (req, user, request, node) {
|
export async function approveResources (req, user, request, node) {
|
||||||
const dbResources = global.config.resources;
|
const dbResources = global.config.resources;
|
||||||
const userResources = await getUserResources(req, user);
|
const userResources = await getUserResources(req, user);
|
||||||
let approved = true;
|
// let approved = true;
|
||||||
Object.keys(request).every((key) => {
|
const reason = {};
|
||||||
|
|
||||||
|
for (const key in request) {
|
||||||
// if requested resource is not specified in user resources, assume it's not allowed
|
// if requested resource is not specified in user resources, assume it's not allowed
|
||||||
if (!(key in userResources)) {
|
if (!(key in userResources)) {
|
||||||
approved = false;
|
// approved = false;
|
||||||
return false;
|
reason[key] = { approved: false, reason: `${key} not allowed` };
|
||||||
|
continue;
|
||||||
|
// return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use node specific quota if there is one available, otherwise use the global resource quota
|
||||||
const inNode = node in userResources[key].nodes;
|
const inNode = node in userResources[key].nodes;
|
||||||
const resourceData = inNode ? userResources[key].nodes[node] : userResources[key].global;
|
const resourceData = inNode ? userResources[key].nodes[node] : userResources[key].global;
|
||||||
|
|
||||||
@@ -244,24 +249,34 @@ export async function approveResources (req, user, request, node) {
|
|||||||
// if no matching resource when index == -1, then remaining is -1 otherwise use the remaining value
|
// if no matching resource when index == -1, then remaining is -1 otherwise use the remaining value
|
||||||
const avail = index === -1 ? false : resourceData[index].avail > 0;
|
const avail = index === -1 ? false : resourceData[index].avail > 0;
|
||||||
if (avail !== dbResources[key].whitelist) {
|
if (avail !== dbResources[key].whitelist) {
|
||||||
approved = false;
|
// approved = false;
|
||||||
return false;
|
reason[key] = { approved: false, reason: `${key} ${dbResources[key].whitelist ? "not in whitelist" : "in blacklist"}` };
|
||||||
|
// return;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if either the requested or avail resource is not strictly a number, block
|
// if either the requested or avail resource is not strictly a number, block
|
||||||
else if (typeof (resourceData.avail) !== "number" || typeof (request[key]) !== "number") {
|
else if (typeof (resourceData.avail) !== "number" || typeof (request[key]) !== "number") {
|
||||||
approved = false;
|
// approved = false;
|
||||||
return false;
|
reason[key] = { approved: false, reason: `expected ${key} to be a number but got ${request[key]}` };
|
||||||
|
continue;
|
||||||
|
// return;
|
||||||
}
|
}
|
||||||
// if the avail resources is less than the requested resources, block
|
// if the avail resources is less than the requested resources, block
|
||||||
else if (resourceData.avail - request[key] < 0) {
|
else if (resourceData.avail - request[key] < 0) {
|
||||||
approved = false;
|
// approved = false;
|
||||||
return false;
|
reason[key] = { approved: false, reason: `${key} requested ${request[key]} which is more than ${resourceData.avail} available` };
|
||||||
|
continue;
|
||||||
|
// return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
reason[key] = { approved: true, reason: "ok" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const approved = Object.values(reason).every((element) => {
|
||||||
|
return element.approved === true;
|
||||||
});
|
});
|
||||||
return approved; // if all requested resources pass, allow
|
return { approved, reason }; // if all requested resources pass, allow
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user