From 023f7e16b2be2d8c43ef21ee8488aebf6a71956b Mon Sep 17 00:00:00 2001 From: Travis Glenn Hansen Date: Sat, 14 Aug 2021 13:58:39 -0600 Subject: [PATCH] more performant proccess for removing all snapshots on a dataset Signed-off-by: Travis Glenn Hansen --- src/driver/freenas/api.js | 34 ++++------------------- src/driver/freenas/http/api.js | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/driver/freenas/api.js b/src/driver/freenas/api.js index ba77af4..75b7acb 100644 --- a/src/driver/freenas/api.js +++ b/src/driver/freenas/api.js @@ -1601,29 +1601,10 @@ class FreeNASApiDriver extends CsiBaseDriver { } } - async removeSnapshotsFromDatatset(datasetName, options = {}) { - // TODO: alter the logic here to not be n+1 - // https://jira.ixsystems.com/browse/NAS-111708 - const httpClient = await this.getHttpClient(); + async removeSnapshotsFromDatatset(datasetName) { const httpApiClient = await this.getTrueNASHttpApiClient(); - - let response; - let endpoint = `/pool/dataset/id/${encodeURIComponent(datasetName)}`; - response = await httpClient.get(endpoint, { "extra.snapshots": 1 }); - - //console.log(response); - - if (response.statusCode == 404) { - return; - } - if (response.statusCode == 200) { - for (let snapshot of response.body.snapshots) { - await httpApiClient.SnapshotDelete(snapshot.name); - } - return; - } - - throw new Error("unhandled statusCode: " + response.statusCode); + let job_id = await httpApiClient.DatasetDestroySnapshots(datasetName); + await httpApiClient.CoreWaitForJob(job_id, 30); } /** @@ -2224,9 +2205,7 @@ class FreeNASApiDriver extends CsiBaseDriver { } // remove snapshots from target - await this.removeSnapshotsFromDatatset(datasetName, { - force: true, - }); + await this.removeSnapshotsFromDatatset(datasetName); } else { try { response = await httpApiClient.CloneCreate( @@ -2377,9 +2356,7 @@ class FreeNASApiDriver extends CsiBaseDriver { ); // remove snapshots from target - await this.removeSnapshotsFromDatatset(datasetName, { - force: true, - }); + await this.removeSnapshotsFromDatatset(datasetName); // remove snapshot from source await httpApiClient.SnapshotDelete(fullSnapshotName, { @@ -2707,7 +2684,6 @@ class FreeNASApiDriver extends CsiBaseDriver { * @param {*} call */ async ControllerExpandVolume(call) { - // TODO: https://jira.ixsystems.com/browse/NAS-111707 const driver = this; const driverZfsResourceType = this.getDriverZfsResourceType(); const httpApiClient = await this.getTrueNASHttpApiClient(); diff --git a/src/driver/freenas/http/api.js b/src/driver/freenas/http/api.js index 8607697..1832928 100644 --- a/src/driver/freenas/http/api.js +++ b/src/driver/freenas/http/api.js @@ -1,3 +1,4 @@ +const { sleep } = require("../../../utils/general"); const { Zetabyte } = require("../../../utils/zfs"); // used for in-memory cache of the version info @@ -491,6 +492,30 @@ class Api { throw new Error(JSON.stringify(response.body)); } + async DatasetDestroySnapshots(datasetName, data = {}) { + const httpClient = await this.getHttpClient(false); + let response; + let endpoint; + + data.name = datasetName; + + endpoint = "/pool/dataset/destroy_snapshots"; + response = await httpClient.post(endpoint, data); + + if (response.statusCode == 200) { + return response.body; + } + + if ( + response.statusCode == 422 && + JSON.stringify(response.body).includes("already exists") + ) { + return; + } + + throw new Error(JSON.stringify(response.body)); + } + async SnapshotSet(snapshotName, properties) { const httpClient = await this.getHttpClient(false); let response; @@ -654,6 +679,31 @@ class Api { throw new Error(JSON.stringify(response.body)); } + async CoreWaitForJob(job_id, timeout = 0) { + if (!job_id) { + throw new Error("invalid job_id"); + } + + const startTime = Date.now() / 1000; + let currentTime; + + let job; + + // wait for job to finish + while (!job || !["SUCCESS", "ABORTED", "FAILED"].includes(job.state)) { + job = await this.CoreGetJobs({ id: job_id }); + job = job[0]; + await sleep(3000); + + currentTime = Date.now() / 1000; + if (timeout > 0 && currentTime > startTime + timeout) { + throw new Error("timeout waiting for job to complete"); + } + } + + return job; + } + async CoreGetJobs(data) { const httpClient = await this.getHttpClient(false);