From 22bd093ba34fd40ba6eefcb234572e1e173bc4af Mon Sep 17 00:00:00 2001 From: Jannik Schönartz Date: Tue, 9 Mar 2021 21:36:29 +0000 Subject: [server/external-backends] Add error logging of object deletion in the external backends --- server/api/clients.js | 9 ++++++-- server/lib/external-backends/backendhelper.js | 26 ++++++++++++++++------ .../external-backends/backends/idoit-backend.js | 13 +++++++---- .../external-backends/backends/infoblox-backend.js | 12 +++++++--- .../external-backends/backends/template-backend.js | 4 ++++ server/lib/external-backends/index.js | 6 +++++ server/lib/httpresponse.js | 4 ++-- 7 files changed, 56 insertions(+), 18 deletions(-) (limited to 'server') diff --git a/server/api/clients.js b/server/api/clients.js index 1a5c274..66b6a3f 100644 --- a/server/api/clients.js +++ b/server/api/clients.js @@ -52,7 +52,6 @@ router.getAsync('/:id', async (req, res) => { router.postAsync(['', '/:id'], async (req, res) => { if (req.query.delete !== undefined && req.query.delete !== 'false') { if (!Array.isArray(req.body.ids)) return HttpResponse.invalidBodyValue('ids', 'an array').send(res) - await backendHelper.deleteClients(req.body.ids, req.body.backendIds) const user = await db.user.findOne({ where: { id: req.user.id } }) // Only need to log batch request if there is more than one client to delete. @@ -65,6 +64,10 @@ router.postAsync(['', '/:id'], async (req, res) => { }) } + // Delete objects from the selected external backends + const deletedBackendObjects = await backendHelper.deleteClients(req.body.ids, req.body.backendIds) + const externalDeletionErrors = deletedBackendObjects.filter(x => x.error !== undefined) + let deletionCounter = 0 // Delete every client on its own, to get a better log for (let index in req.body.ids) { @@ -97,7 +100,9 @@ router.postAsync(['', '/:id'], async (req, res) => { userId: req.user.id }) } - HttpResponse.successBatch('deleted', 'client', deletionCounter).send(res) + + if (externalDeletionErrors.length <= 0) HttpResponse.successBatch('deleted', 'client', deletionCounter).send(res) + else HttpResponse.warningBatch('deleted', 'client', deletionCounter, req.body.ids.length, externalDeletionErrors).send(res) } else { let client let action = 'updated' diff --git a/server/lib/external-backends/backendhelper.js b/server/lib/external-backends/backendhelper.js index 27f746a..b3c3f21 100644 --- a/server/lib/external-backends/backendhelper.js +++ b/server/lib/external-backends/backendhelper.js @@ -126,16 +126,28 @@ async function deleteClients (clientids, backendIds) { include: ['clientMappings'] }) - backends.forEach(backend => { + let deleteResponses = [] + + for (let backend of backends) { const ba = new ExternalBackends() const instance = ba.getInstance(backend.type) - var objectsToDelete = [] - backend.clientMappings.forEach(mapping => { - objectsToDelete.push(mapping.externalId) - }) + let objectsToDelete = backend.clientMappings.map(x => x.externalId) + // If there are objects to delete -> delete them. - if (objectsToDelete.length > 0) instance.deleteObjects(backend.credentials, objectsToDelete) - }) + if (objectsToDelete.length > 0) { + let backendDeleteErrors = await instance.deleteObjects(backend.credentials, objectsToDelete) + backendDeleteErrors = backendDeleteErrors.map(errors => ({ ...errors, backendId: backend.id, backendType: backend.type })) + deleteResponses = [...deleteResponses, ...backendDeleteErrors] + + for (let err of backendDeleteErrors) { + log({ + category: `ERROR_BACKEND_${err.backendType.toUpperCase()}_OBJECT_DELETION`, + description: `[${err.backendType}] Deletion of object ${err.id} (external backend id) in backend ${err.backendType} failed with error: \n ${err.message}` + }) + } + } + } + return deleteResponses } async function uploadFiles (clientId, files) { diff --git a/server/lib/external-backends/backends/idoit-backend.js b/server/lib/external-backends/backends/idoit-backend.js index 8e1f7d9..6c30891 100644 --- a/server/lib/external-backends/backends/idoit-backend.js +++ b/server/lib/external-backends/backends/idoit-backend.js @@ -123,12 +123,17 @@ class IdoitBackend extends ExternalBackends { if (headers.error) return headers let bodies = [] - objectIds.forEach(oid => { - bodies.push(this.getBody('cmdb.object.quickpurge', { 'id': oid, 'apikey': c.apikey }, oid)) - }) + + bodies.push(this.getBody('cmdb.object.quickpurge', { 'id': objectIds, 'apikey': c.apikey }, 'batch_quickpurge')) let deleteRequest = await this.axiosRequest(c.url, bodies, headers) - return deleteRequest + const errorRegex = /(?<=\s#)[0-9]+/g + + const failedDeletions = deleteRequest[0].result.message.match(errorRegex) + let result = [] + for (let failedObjId of failedDeletions) result.push({ error: true, message: `Deletion of object ${failedObjId} was skipped.`, id: failedObjId }) + + return result } // Function to use the same session for multiple requests diff --git a/server/lib/external-backends/backends/infoblox-backend.js b/server/lib/external-backends/backends/infoblox-backend.js index ca7a7f9..1718ea7 100644 --- a/server/lib/external-backends/backends/infoblox-backend.js +++ b/server/lib/external-backends/backends/infoblox-backend.js @@ -225,10 +225,16 @@ class InfobloxBackend extends ExternalBackends { const login = await ipam.login(c.username, c.password) if (!login) return { error: 'LOGIN_FAILED' } - for (let index in objectIds) { - const ref = objectIds[index] - await ipam.delete(ref) + let errors = [] + for (let id of objectIds) { + const response = await ipam.delete(id) + const parsed = JSON.parse(response) + + if (parsed.Error) { + errors.push({ error: parsed.Code, message: parsed.text, id: id }) + } } + return errors } async getObjects (credentials) { diff --git a/server/lib/external-backends/backends/template-backend.js b/server/lib/external-backends/backends/template-backend.js index f554e69..4320c7e 100644 --- a/server/lib/external-backends/backends/template-backend.js +++ b/server/lib/external-backends/backends/template-backend.js @@ -37,6 +37,10 @@ class TemplateBackend extends ExternalBackends { getSyncTypes () { return ['THERE', 'ARE', 'NO', 'SYNC', 'TYPES'] } + + async deleteObjects (credentials, objectIds) { + return { error: 'DELETION_FAILED', message: 'Deletion of client xy failed' } + } } function x () { diff --git a/server/lib/external-backends/index.js b/server/lib/external-backends/index.js index f16a1e1..f05ba69 100644 --- a/server/lib/external-backends/index.js +++ b/server/lib/external-backends/index.js @@ -88,6 +88,12 @@ class ExternalBackends { * objectIds: [, , ...] * * Deletes the objecs from the backend. + * + * if errors occour, they should be returned in the form: + * [ + * { error: '', message: '', id: }, + * ... + * ] */ async deleteObjects (credentials, objectIds) { return { error: 'NOT_IMPLEMENTED_EXCEPTION', message: 'The provided backend does not have a deleteObject method' } diff --git a/server/lib/httpresponse.js b/server/lib/httpresponse.js index c402e39..61697c9 100644 --- a/server/lib/httpresponse.js +++ b/server/lib/httpresponse.js @@ -29,13 +29,13 @@ HttpResponse.successBatch = (action, type, count) => { // ############################################################################ // ############################## WARNING ##################################### -HttpResponse.warningBatch = (action, type, successfull, count) => { +HttpResponse.warningBatch = (action, type, successfull, count, errors) => { const failed = count - successfull let tmpType = type if (failed > 1) tmpType += 's' if (Array.isArray(type)) type = type[successfull === 1 ? 0 : 1] else if (successfull !== 1) type += 's' - return new HttpResponse(200, action.toUpperCase() + '_MULTIPLE', `Warning: ${action} ${successfull}/${count} ${type}. ${failed} ${tmpType} failed.`, { successfull, count }) + return new HttpResponse(200, action.toUpperCase() + '_MULTIPLE', `Warning: ${action} ${successfull}/${count} ${type}. ${failed} ${tmpType} failed. ${errors.length} external errors occurred.`, { successfull, count, errors }) } // ############################################################################ -- cgit v1.2.3-55-g7522