Merge 8ef05936d8 into 55c36d62ff
				
					
				
			This commit is contained in:
		
						commit
						e9fe7ea61f
					
				|  | @ -15,6 +15,15 @@ zfs: | ||||||
|   # the example below is useful for TrueNAS 12 |   # the example below is useful for TrueNAS 12 | ||||||
|   #cli: |   #cli: | ||||||
|   #  sudoEnabled: true |   #  sudoEnabled: true | ||||||
|  |   # | ||||||
|  |   # instead of setting sudoEnabled to true, sudo can be | ||||||
|  |   # reduced only to specific (supported) zfs commands | ||||||
|  |   #  sudoEnabledCommands: | ||||||
|  |   #    - mount | ||||||
|  |   #    - unmount | ||||||
|  |   #    - share | ||||||
|  |   #    - unshare | ||||||
|  |   # | ||||||
|   #  paths: |   #  paths: | ||||||
|   #    zfs: /usr/local/sbin/zfs |   #    zfs: /usr/local/sbin/zfs | ||||||
|   #    zpool: /usr/local/sbin/zpool |   #    zpool: /usr/local/sbin/zpool | ||||||
|  |  | ||||||
|  | @ -48,6 +48,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver { | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); |       options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); | ||||||
|  |       options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []); | ||||||
| 
 | 
 | ||||||
|       if (typeof this.setZetabyteCustomOptions === "function") { |       if (typeof this.setZetabyteCustomOptions === "function") { | ||||||
|         await this.setZetabyteCustomOptions(options); |         await this.setZetabyteCustomOptions(options); | ||||||
|  | @ -92,6 +93,37 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver { | ||||||
|     return name; |     return name; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   async setShareProperty(zb, datasetName, propertyDict) { | ||||||
|  |     try { | ||||||
|  |       await zb.zfs.set(datasetName, propertyDict); | ||||||
|  |     } catch (err) { | ||||||
|  |       if ( | ||||||
|  |         err.toString().includes("unable to reshare") && | ||||||
|  |         zb.options.sudoCommands.includes("mount") | ||||||
|  |       ) { | ||||||
|  |         await zb.zfs.share(datasetName); | ||||||
|  |       } else { | ||||||
|  |         throw err; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async unsetShareProperty(zb, datasetName, propertyKey) { | ||||||
|  |     if (zb.options.sudoCommands.includes("unshare")) { | ||||||
|  |       await zb.zfs.unshare(datasetName, { idempotent: true }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       await zb.zfs.inherit(datasetName, propertyKey); | ||||||
|  |     } catch (err) { | ||||||
|  |       if (err.toString().includes("dataset does not exist")) { | ||||||
|  |         // do nothing
 | ||||||
|  |       } else { | ||||||
|  |         throw err; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /** |   /** | ||||||
|    * should create any necessary share resources |    * should create any necessary share resources | ||||||
|    * should set the SHARE_VOLUME_CONTEXT_PROPERTY_NAME propery |    * should set the SHARE_VOLUME_CONTEXT_PROPERTY_NAME propery | ||||||
|  | @ -118,7 +150,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver { | ||||||
|                   key |                   key | ||||||
|                 ] |                 ] | ||||||
|               ) { |               ) { | ||||||
|                 await zb.zfs.set(datasetName, { |                 await this.setShareProperty(zb, datasetName, { | ||||||
|                   [key]: |                   [key]: | ||||||
|                     this.options.nfs.shareStrategySetDatasetProperties |                     this.options.nfs.shareStrategySetDatasetProperties | ||||||
|                       .properties[key], |                       .properties[key], | ||||||
|  | @ -152,7 +184,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver { | ||||||
|                   key |                   key | ||||||
|                 ] |                 ] | ||||||
|               ) { |               ) { | ||||||
|                 await zb.zfs.set(datasetName, { |                 await this.setShareProperty(zb, datasetName, { | ||||||
|                   [key]: |                   [key]: | ||||||
|                     this.options.smb.shareStrategySetDatasetProperties |                     this.options.smb.shareStrategySetDatasetProperties | ||||||
|                       .properties[key], |                       .properties[key], | ||||||
|  | @ -572,15 +604,7 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath} | ||||||
|                   key |                   key | ||||||
|                 ] |                 ] | ||||||
|               ) { |               ) { | ||||||
|                 try { |                 await this.unsetShareProperty(zb, datasetName, key); | ||||||
|                   await zb.zfs.inherit(datasetName, key); |  | ||||||
|                 } catch (err) { |  | ||||||
|                   if (err.toString().includes("dataset does not exist")) { |  | ||||||
|                     // do nothing
 |  | ||||||
|                   } else { |  | ||||||
|                     throw err; |  | ||||||
|                   } |  | ||||||
|                 } |  | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|             await GeneralUtils.sleep(2000); // let things settle
 |             await GeneralUtils.sleep(2000); // let things settle
 | ||||||
|  | @ -602,15 +626,7 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath} | ||||||
|                   key |                   key | ||||||
|                 ] |                 ] | ||||||
|               ) { |               ) { | ||||||
|                 try { |                 await this.unsetShareProperty(zb, datasetName, key); | ||||||
|                   await zb.zfs.inherit(datasetName, key); |  | ||||||
|                 } catch (err) { |  | ||||||
|                   if (err.toString().includes("dataset does not exist")) { |  | ||||||
|                     // do nothing
 |  | ||||||
|                   } else { |  | ||||||
|                     throw err; |  | ||||||
|                   } |  | ||||||
|                 } |  | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|             await GeneralUtils.sleep(2000); // let things settle
 |             await GeneralUtils.sleep(2000); // let things settle
 | ||||||
|  |  | ||||||
|  | @ -66,6 +66,7 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver { | ||||||
|       }; |       }; | ||||||
| 
 | 
 | ||||||
|       options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); |       options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); | ||||||
|  |       options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []); | ||||||
| 
 | 
 | ||||||
|       if (typeof this.setZetabyteCustomOptions === "function") { |       if (typeof this.setZetabyteCustomOptions === "function") { | ||||||
|         await this.setZetabyteCustomOptions(options); |         await this.setZetabyteCustomOptions(options); | ||||||
|  |  | ||||||
|  | @ -159,7 +159,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver { | ||||||
|     driver.ctx.logger.verbose("whoami command: %s", command); |     driver.ctx.logger.verbose("whoami command: %s", command); | ||||||
|     const response = await execClient.exec(command); |     const response = await execClient.exec(command); | ||||||
|     if (response.code !== 0) { |     if (response.code !== 0) { | ||||||
|       throw new Error("failed to run uname to determine max zvol name length"); |       throw new Error("failed to run whoami to determine name of user"); | ||||||
|     } else { |     } else { | ||||||
|       return response.stdout.trim(); |       return response.stdout.trim(); | ||||||
|     } |     } | ||||||
|  | @ -499,9 +499,10 @@ class ControllerZfsBaseDriver extends CsiBaseDriver { | ||||||
|   async setFilesystemMode(path, mode) { |   async setFilesystemMode(path, mode) { | ||||||
|     const driver = this; |     const driver = this; | ||||||
|     const execClient = this.getExecClient(); |     const execClient = this.getExecClient(); | ||||||
|  |     const sudoEnabled = _.get(this.options, "zfs.cli.sudoEnabled", false); | ||||||
| 
 | 
 | ||||||
|     let command = execClient.buildCommand("chmod", [mode, path]); |     let command = execClient.buildCommand("chmod", [mode, path]); | ||||||
|     if ((await driver.getWhoAmI()) != "root") { |     if ((await driver.getWhoAmI()) != "root" && sudoEnabled) { | ||||||
|       command = (await driver.getSudoPath()) + " " + command; |       command = (await driver.getSudoPath()) + " " + command; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -80,6 +80,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver { | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); |       options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); | ||||||
|  |       options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []); | ||||||
| 
 | 
 | ||||||
|       if (typeof this.setZetabyteCustomOptions === "function") { |       if (typeof this.setZetabyteCustomOptions === "function") { | ||||||
|         await this.setZetabyteCustomOptions(options); |         await this.setZetabyteCustomOptions(options); | ||||||
|  |  | ||||||
							
								
								
									
										180
									
								
								src/utils/zfs.js
								
								
								
								
							
							
						
						
									
										180
									
								
								src/utils/zfs.js
								
								
								
								
							|  | @ -904,6 +904,13 @@ class Zetabyte { | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     zb.zfs = { |     zb.zfs = { | ||||||
|  |       SUDO_COMMANDS: [ | ||||||
|  |         "mount", | ||||||
|  |         "unmount", | ||||||
|  |         "share", | ||||||
|  |         "unshare" | ||||||
|  |       ], | ||||||
|  | 
 | ||||||
|       /** |       /** | ||||||
|        * zfs create [-pu] [-o property=value]... filesystem |        * zfs create [-pu] [-o property=value]... filesystem | ||||||
|        * zfs create [-ps] [-b blocksize] [-o property=value]... -V size volume |        * zfs create [-ps] [-b blocksize] [-o property=value]... -V size volume | ||||||
|  | @ -913,6 +920,7 @@ class Zetabyte { | ||||||
|        */ |        */ | ||||||
|       create: function (dataset, options = {}) { |       create: function (dataset, options = {}) { | ||||||
|         if (!(arguments.length >= 1)) throw new (Error("Invalid arguments"))(); |         if (!(arguments.length >= 1)) throw new (Error("Invalid arguments"))(); | ||||||
|  |         const zfs = this; | ||||||
| 
 | 
 | ||||||
|         return new Promise((resolve, reject) => { |         return new Promise((resolve, reject) => { | ||||||
|           const idempotent = |           const idempotent = | ||||||
|  | @ -921,11 +929,12 @@ class Zetabyte { | ||||||
|               : "idempotent" in zb.options |               : "idempotent" in zb.options | ||||||
|               ? zb.options.idempotent |               ? zb.options.idempotent | ||||||
|               : false; |               : false; | ||||||
|  |           const shouldSudoMount = !zb.options.sudo && zb.options.sudoCommands.includes("mount"); | ||||||
| 
 | 
 | ||||||
|           let args = []; |           let args = []; | ||||||
|           args.push("create"); |           args.push("create"); | ||||||
|           if (options.parents) args.push("-p"); |           if (options.parents) args.push("-p"); | ||||||
|           if (options.unmounted) args.push("-u"); |           if (options.unmounted || shouldSudoMount) args.push("-u"); | ||||||
|           if (options.blocksize) args = args.concat(["-b", options.blocksize]); |           if (options.blocksize) args = args.concat(["-b", options.blocksize]); | ||||||
|           if (options.properties) { |           if (options.properties) { | ||||||
|             for (let [key, value] of Object.entries(options.properties)) { |             for (let [key, value] of Object.entries(options.properties)) { | ||||||
|  | @ -945,10 +954,18 @@ class Zetabyte { | ||||||
|               if ( |               if ( | ||||||
|                 error && |                 error && | ||||||
|                 !(idempotent && stderr.includes("dataset already exists")) |                 !(idempotent && stderr.includes("dataset already exists")) | ||||||
|               ) |               ) { | ||||||
|                 return reject(zb.helpers.zfsError(error, stderr)); |                 return reject(zb.helpers.zfsError(error, stderr)); | ||||||
|  |               } | ||||||
|  | 
 | ||||||
|  |               if (shouldSudoMount) { | ||||||
|  |                 zfs.mount(dataset, { idempotent }) | ||||||
|  |                   .then(out => resolve(stdout + "; " + out)) | ||||||
|  |                   .catch(err => reject(err)); | ||||||
|  |               } else { | ||||||
|                 return resolve(stdout); |                 return resolve(stdout); | ||||||
|               } |               } | ||||||
|  |             } | ||||||
|           ); |           ); | ||||||
|         }); |         }); | ||||||
|       }, |       }, | ||||||
|  | @ -964,6 +981,7 @@ class Zetabyte { | ||||||
|        */ |        */ | ||||||
|       destroy: function (dataset, options = {}) { |       destroy: function (dataset, options = {}) { | ||||||
|         if (!(arguments.length >= 1)) throw Error("Invalid arguments"); |         if (!(arguments.length >= 1)) throw Error("Invalid arguments"); | ||||||
|  |         const zfs = this; | ||||||
| 
 | 
 | ||||||
|         return new Promise((resolve, reject) => { |         return new Promise((resolve, reject) => { | ||||||
|           const idempotent = |           const idempotent = | ||||||
|  | @ -985,6 +1003,7 @@ class Zetabyte { | ||||||
|           if (options.defer) args.push("-d"); |           if (options.defer) args.push("-d"); | ||||||
|           args.push(dataset); |           args.push(dataset); | ||||||
| 
 | 
 | ||||||
|  |           const destroyExec = function() { | ||||||
|             zb.exec( |             zb.exec( | ||||||
|               zb.options.paths.zfs, |               zb.options.paths.zfs, | ||||||
|               args, |               args, | ||||||
|  | @ -1002,6 +1021,14 @@ class Zetabyte { | ||||||
|                 return resolve(stdout); |                 return resolve(stdout); | ||||||
|               } |               } | ||||||
|             ); |             ); | ||||||
|  |           }; | ||||||
|  |           if (zb.options.sudoCommands.includes("unmount")) { | ||||||
|  |             zfs.unmount(dataset, { idempotent }) | ||||||
|  |               .then(() => destroyExec()) | ||||||
|  |               .catch(err => reject(err)); | ||||||
|  |           } else { | ||||||
|  |             destroyExec(); | ||||||
|  |           } | ||||||
|         }); |         }); | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|  | @ -1224,7 +1251,7 @@ class Zetabyte { | ||||||
|           let args = []; |           let args = []; | ||||||
|           args.push("rename"); |           args.push("rename"); | ||||||
|           if (options.parents) args.push("-p"); |           if (options.parents) args.push("-p"); | ||||||
|           if (options.unmounted) args.push("-u"); |           if (options.unmounted || (!zb.options.sudo && zb.options.sudoCommands.includes("mount"))) args.push("-u"); | ||||||
|           if (options.force) args.push("-f"); |           if (options.force) args.push("-f"); | ||||||
|           if (options.recurse) args.push("-r"); |           if (options.recurse) args.push("-r"); | ||||||
|           args.push(source); |           args.push(source); | ||||||
|  | @ -1522,6 +1549,145 @@ class Zetabyte { | ||||||
|           ); |           ); | ||||||
|         }); |         }); | ||||||
|       }, |       }, | ||||||
|  | 
 | ||||||
|  |       /** | ||||||
|  |        * zfs mount filesystem | ||||||
|  |        * | ||||||
|  |        * @param {*} dataset | ||||||
|  |        * @param {*} options | ||||||
|  |        */ | ||||||
|  |       mount: function (dataset, options = {}) { | ||||||
|  |         if (!(arguments.length >= 1)) throw Error("Invalid arguments"); | ||||||
|  | 
 | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |           const idempotent = | ||||||
|  |             "idempotent" in options | ||||||
|  |               ? options.idempotent | ||||||
|  |               : "idempotent" in zb.options | ||||||
|  |               ? zb.options.idempotent | ||||||
|  |               : false; | ||||||
|  | 
 | ||||||
|  |           let args = []; | ||||||
|  |           args.push("mount"); | ||||||
|  |           args.push(dataset); | ||||||
|  | 
 | ||||||
|  |           zb.exec( | ||||||
|  |             zb.options.paths.zfs, | ||||||
|  |             args, | ||||||
|  |             { timeout: zb.options.timeout }, | ||||||
|  |             function (error, stdout, stderr) { | ||||||
|  |               if ( | ||||||
|  |                 error && | ||||||
|  |                 !(idempotent && stderr.includes("filesystem already mounted")) | ||||||
|  |               ) { | ||||||
|  |                 return reject(zb.helpers.zfsError(error, stderr)); | ||||||
|  |               } | ||||||
|  |               return resolve(stdout); | ||||||
|  |             } | ||||||
|  |           ); | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       /** | ||||||
|  |        * zfs unmount filesystem | ||||||
|  |        * | ||||||
|  |        * @param {*} dataset | ||||||
|  |        * @param {*} options | ||||||
|  |        */ | ||||||
|  |        unmount: function (dataset, options = {}) { | ||||||
|  |         if (!(arguments.length >= 1)) throw Error("Invalid arguments"); | ||||||
|  | 
 | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |           const idempotent = | ||||||
|  |             "idempotent" in options | ||||||
|  |               ? options.idempotent | ||||||
|  |               : "idempotent" in zb.options | ||||||
|  |               ? zb.options.idempotent | ||||||
|  |               : false; | ||||||
|  | 
 | ||||||
|  |           let args = []; | ||||||
|  |           args.push("unmount"); | ||||||
|  |           args.push(dataset); | ||||||
|  | 
 | ||||||
|  |           zb.exec( | ||||||
|  |             zb.options.paths.zfs, | ||||||
|  |             args, | ||||||
|  |             { timeout: zb.options.timeout }, | ||||||
|  |             function (error, stdout, stderr) { | ||||||
|  |               if ( | ||||||
|  |                 error && | ||||||
|  |                 !(idempotent && stderr.includes("not currently mounted")) | ||||||
|  |               ) { | ||||||
|  |                 return reject(zb.helpers.zfsError(error, stderr)); | ||||||
|  |               } | ||||||
|  |               return resolve(stdout); | ||||||
|  |             } | ||||||
|  |           ); | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       /** | ||||||
|  |        * zfs share filesystem | ||||||
|  |        * | ||||||
|  |        * @param {*} dataset | ||||||
|  |        */ | ||||||
|  |        share: function (dataset) { | ||||||
|  |         if (arguments.length != 1) throw Error("Invalid arguments"); | ||||||
|  | 
 | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |           let args = []; | ||||||
|  |           args.push("share"); | ||||||
|  |           args.push(dataset); | ||||||
|  | 
 | ||||||
|  |           zb.exec( | ||||||
|  |             zb.options.paths.zfs, | ||||||
|  |             args, | ||||||
|  |             { timeout: zb.options.timeout }, | ||||||
|  |             function (error, stdout, stderr) { | ||||||
|  |               if (error) return reject(zb.helpers.zfsError(error, stderr)); | ||||||
|  |               return resolve(stdout); | ||||||
|  |             } | ||||||
|  |           ); | ||||||
|  |         }); | ||||||
|  |        }, | ||||||
|  | 
 | ||||||
|  |        /** | ||||||
|  |        * zfs unshare filesystem | ||||||
|  |        * | ||||||
|  |        * @param {*} dataset | ||||||
|  |        * @param {*} options | ||||||
|  |        */ | ||||||
|  |       unshare: function (dataset, options = {}) { | ||||||
|  |         if (!(arguments.length >= 1)) throw Error("Invalid arguments"); | ||||||
|  | 
 | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |           const idempotent = | ||||||
|  |             "idempotent" in options | ||||||
|  |               ? options.idempotent | ||||||
|  |               : "idempotent" in zb.options | ||||||
|  |               ? zb.options.idempotent | ||||||
|  |               : false; | ||||||
|  | 
 | ||||||
|  |           let args = []; | ||||||
|  |           args.push("unshare"); | ||||||
|  |           args.push(dataset); | ||||||
|  | 
 | ||||||
|  |           zb.exec( | ||||||
|  |             zb.options.paths.zfs, | ||||||
|  |             args, | ||||||
|  |             { timeout: zb.options.timeout }, | ||||||
|  |             function (error, stdout, stderr) { | ||||||
|  |               if ( | ||||||
|  |                 error && | ||||||
|  |                 !(idempotent && stderr.includes("not currently shared")) | ||||||
|  |               ) { | ||||||
|  |                 return reject(zb.helpers.zfsError(error, stderr)); | ||||||
|  |               } | ||||||
|  |               return resolve(stdout); | ||||||
|  |             } | ||||||
|  |           ); | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -1564,7 +1730,13 @@ class Zetabyte { | ||||||
|       use_sudo = options.sudo; |       use_sudo = options.sudo; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (use_sudo) { |     if ( | ||||||
|  |       use_sudo || ( | ||||||
|  |         Array.isArray(args) && args.length > 0 && | ||||||
|  |         zb.zfs.SUDO_COMMANDS.includes(args[0]) && | ||||||
|  |         zb.options.sudoCommands.includes(args[0]) | ||||||
|  |       ) | ||||||
|  |     ) { | ||||||
|       args = args || []; |       args = args || []; | ||||||
|       args.unshift(command); |       args.unshift(command); | ||||||
|       command = zb.options.paths.sudo; |       command = zb.options.paths.sudo; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue