support setting custom binary paths for zfs, support custom dataset properties with templating
This commit is contained in:
		
							parent
							
								
									d6dbbbe951
								
							
						
					
					
						commit
						699b65d931
					
				|  | @ -18,6 +18,22 @@ sshConnection: | ||||||
|     ... |     ... | ||||||
|     -----END RSA PRIVATE KEY----- |     -----END RSA PRIVATE KEY----- | ||||||
| zfs: | zfs: | ||||||
|  |   # can be used to override defaults if necessary | ||||||
|  |   # the example below is useful for TrueNAS 12 | ||||||
|  |   #cli: | ||||||
|  |   #  paths: | ||||||
|  |   #    zfs: /usr/local/sbin/zfs | ||||||
|  |   #    zpool: /usr/local/sbin/zpool | ||||||
|  |   #    sudo: /usr/local/bin/sudo | ||||||
|  |   #    chroot: /usr/sbin/chroot | ||||||
|  | 
 | ||||||
|  |   # can be used to set arbitrary values on the dataset/zvol | ||||||
|  |   # can use handlebars templates with the parameters from the storage class/CO | ||||||
|  |   #datasetProperties: | ||||||
|  |   #  "org.freenas:description": "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}/{{ parameters.[csi.storage.k8s.io/pvc/name] }}" | ||||||
|  |   #  "org.freenas:test": "{{ parameters.foo }}" | ||||||
|  |   #  "org.freenas:test2": "some value" | ||||||
|  |    | ||||||
|   # total volume name (zvol/<datasetParentName>/<pvc name>) length cannot exceed 63 chars |   # total volume name (zvol/<datasetParentName>/<pvc name>) length cannot exceed 63 chars | ||||||
|   # https://www.ixsystems.com/documentation/freenas/11.2-U5/storage.html#zfs-zvol-config-opts-tab |   # https://www.ixsystems.com/documentation/freenas/11.2-U5/storage.html#zfs-zvol-config-opts-tab | ||||||
|   # standard volume naming overhead is 46 chars |   # standard volume naming overhead is 46 chars | ||||||
|  |  | ||||||
|  | @ -18,6 +18,22 @@ sshConnection: | ||||||
|     ... |     ... | ||||||
|     -----END RSA PRIVATE KEY----- |     -----END RSA PRIVATE KEY----- | ||||||
| zfs: | zfs: | ||||||
|  |   # can be used to override defaults if necessary | ||||||
|  |   # the example below is useful for TrueNAS 12 | ||||||
|  |   #cli: | ||||||
|  |   #  paths: | ||||||
|  |   #    zfs: /usr/local/sbin/zfs | ||||||
|  |   #    zpool: /usr/local/sbin/zpool | ||||||
|  |   #    sudo: /usr/local/bin/sudo | ||||||
|  |   #    chroot: /usr/sbin/chroot | ||||||
|  |    | ||||||
|  |   # can be used to set arbitrary values on the dataset/zvol | ||||||
|  |   # can use handlebars templates with the parameters from the storage class/CO | ||||||
|  |   #datasetProperties: | ||||||
|  |   #  "org.freenas:description": "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}/{{ parameters.[csi.storage.k8s.io/pvc/name] }}" | ||||||
|  |   #  "org.freenas:test": "{{ parameters.foo }}" | ||||||
|  |   #  "org.freenas:test2": "some value" | ||||||
|  | 
 | ||||||
|   datasetParentName: tank/k8s/a/vols |   datasetParentName: tank/k8s/a/vols | ||||||
|   detachedSnapshotsDatasetParentName: tank/k8s/a/snaps |   detachedSnapshotsDatasetParentName: tank/k8s/a/snaps | ||||||
|   datasetEnableQuotas: true |   datasetEnableQuotas: true | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ instance_id: | ||||||
| nfs: | nfs: | ||||||
|   shareHost: server address |   shareHost: server address | ||||||
|   shareBasePath: "/some/path" |   shareBasePath: "/some/path" | ||||||
|  |   # shareHost:shareBasePath should be mounted at this location in the controller container | ||||||
|   controllerBasePath: "/storage" |   controllerBasePath: "/storage" | ||||||
|   dirPermissionsMode: "0777" |   dirPermissionsMode: "0777" | ||||||
|   dirPermissionsUser: root |   dirPermissionsUser: root | ||||||
|  |  | ||||||
|  | @ -14,6 +14,22 @@ service: | ||||||
|   controller: {} |   controller: {} | ||||||
|   node: {} |   node: {} | ||||||
| zfs: | zfs: | ||||||
|  |   # can be used to override defaults if necessary | ||||||
|  |   # the example below is useful for TrueNAS 12 | ||||||
|  |   #cli: | ||||||
|  |   #  paths: | ||||||
|  |   #    zfs: /usr/local/sbin/zfs | ||||||
|  |   #    zpool: /usr/local/sbin/zpool | ||||||
|  |   #    sudo: /usr/local/bin/sudo | ||||||
|  |   #    chroot: /usr/sbin/chroot | ||||||
|  |    | ||||||
|  |   # can be used to set arbitrary values on the dataset/zvol | ||||||
|  |   # can use handlebars templates with the parameters from the storage class/CO | ||||||
|  |   #datasetProperties: | ||||||
|  |   #  "org.freenas:description": "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}/{{ parameters.[csi.storage.k8s.io/pvc/name] }}" | ||||||
|  |   #  "org.freenas:test": "{{ parameters.foo }}" | ||||||
|  |   #  "org.freenas:test2": "some value" | ||||||
|  | 
 | ||||||
|   datasetParentName: tank/k8s/test |   datasetParentName: tank/k8s/test | ||||||
|   detachedSnapshotsDatasetParentName: tanks/k8s/test-snapshots |   detachedSnapshotsDatasetParentName: tanks/k8s/test-snapshots | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,6 +14,22 @@ service: | ||||||
|   controller: {} |   controller: {} | ||||||
|   node: {} |   node: {} | ||||||
| zfs: | zfs: | ||||||
|  |   # can be used to override defaults if necessary | ||||||
|  |   # the example below is useful for TrueNAS 12 | ||||||
|  |   #cli: | ||||||
|  |   #  paths: | ||||||
|  |   #    zfs: /usr/local/sbin/zfs | ||||||
|  |   #    zpool: /usr/local/sbin/zpool | ||||||
|  |   #    sudo: /usr/local/bin/sudo | ||||||
|  |   #    chroot: /usr/sbin/chroot | ||||||
|  |    | ||||||
|  |   # can be used to set arbitrary values on the dataset/zvol | ||||||
|  |   # can use handlebars templates with the parameters from the storage class/CO | ||||||
|  |   #datasetProperties: | ||||||
|  |   #  "org.freenas:description": "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}/{{ parameters.[csi.storage.k8s.io/pvc/name] }}" | ||||||
|  |   #  "org.freenas:test": "{{ parameters.foo }}" | ||||||
|  |   #  "org.freenas:test2": "some value" | ||||||
|  | 
 | ||||||
|   datasetParentName: tank/k8s/test |   datasetParentName: tank/k8s/test | ||||||
|   detachedSnapshotsDatasetParentName: tanks/k8s/test-snapshots |   detachedSnapshotsDatasetParentName: tanks/k8s/test-snapshots | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -820,6 +820,18 @@ | ||||||
|         "protobufjs": "^5.0.3" |         "protobufjs": "^5.0.3" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "handlebars": { | ||||||
|  |       "version": "4.7.6", | ||||||
|  |       "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", | ||||||
|  |       "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", | ||||||
|  |       "requires": { | ||||||
|  |         "minimist": "^1.2.5", | ||||||
|  |         "neo-async": "^2.6.0", | ||||||
|  |         "source-map": "^0.6.1", | ||||||
|  |         "uglify-js": "^3.1.4", | ||||||
|  |         "wordwrap": "^1.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "har-schema": { |     "har-schema": { | ||||||
|       "version": "2.0.0", |       "version": "2.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", |       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", | ||||||
|  | @ -1127,6 +1139,11 @@ | ||||||
|       "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", |       "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", | ||||||
|       "optional": true |       "optional": true | ||||||
|     }, |     }, | ||||||
|  |     "neo-async": { | ||||||
|  |       "version": "2.6.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", | ||||||
|  |       "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" | ||||||
|  |     }, | ||||||
|     "number-is-nan": { |     "number-is-nan": { | ||||||
|       "version": "1.0.1", |       "version": "1.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", |       "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", | ||||||
|  | @ -1502,6 +1519,11 @@ | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "source-map": { | ||||||
|  |       "version": "0.6.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", | ||||||
|  |       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" | ||||||
|  |     }, | ||||||
|     "sprintf-js": { |     "sprintf-js": { | ||||||
|       "version": "1.0.3", |       "version": "1.0.3", | ||||||
|       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", |       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", | ||||||
|  | @ -1696,6 +1718,12 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", |       "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", | ||||||
|       "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" |       "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" | ||||||
|     }, |     }, | ||||||
|  |     "uglify-js": { | ||||||
|  |       "version": "3.10.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.3.tgz", | ||||||
|  |       "integrity": "sha512-Lh00i69Uf6G74mvYpHCI9KVVXLcHW/xu79YTvH7Mkc9zyKUeSPz0owW0dguj0Scavns3ZOh3wY63J0Zb97Za2g==", | ||||||
|  |       "optional": true | ||||||
|  |     }, | ||||||
|     "uri-js": { |     "uri-js": { | ||||||
|       "version": "4.2.2", |       "version": "4.2.2", | ||||||
|       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", |       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", | ||||||
|  | @ -1806,6 +1834,11 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", |       "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", | ||||||
|       "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" |       "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" | ||||||
|     }, |     }, | ||||||
|  |     "wordwrap": { | ||||||
|  |       "version": "1.0.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", | ||||||
|  |       "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" | ||||||
|  |     }, | ||||||
|     "wrap-ansi": { |     "wrap-ansi": { | ||||||
|       "version": "6.2.0", |       "version": "6.2.0", | ||||||
|       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", |       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ | ||||||
|     "bunyan": "^1.8.14", |     "bunyan": "^1.8.14", | ||||||
|     "eslint": "^7.4.0", |     "eslint": "^7.4.0", | ||||||
|     "grpc-uds": "^0.1.4", |     "grpc-uds": "^0.1.4", | ||||||
|  |     "handlebars": "^4.7.6", | ||||||
|     "js-yaml": "^3.14.0", |     "js-yaml": "^3.14.0", | ||||||
|     "lru-cache": "^5.1.1", |     "lru-cache": "^5.1.1", | ||||||
|     "request": "^2.88.2", |     "request": "^2.88.2", | ||||||
|  |  | ||||||
|  | @ -3,6 +3,8 @@ const SshClient = require("../../utils/ssh").SshClient; | ||||||
| const { GrpcError, grpc } = require("../../utils/grpc"); | const { GrpcError, grpc } = require("../../utils/grpc"); | ||||||
| 
 | 
 | ||||||
| const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs"); | const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs"); | ||||||
|  | 
 | ||||||
|  | const Handlebars = require("handlebars"); | ||||||
| const uuidv4 = require("uuid").v4; | const uuidv4 = require("uuid").v4; | ||||||
| 
 | 
 | ||||||
| // zfs common properties
 | // zfs common properties
 | ||||||
|  | @ -122,10 +124,18 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
| 
 | 
 | ||||||
|   getZetabyte() { |   getZetabyte() { | ||||||
|     const sshClient = this.getSshClient(); |     const sshClient = this.getSshClient(); | ||||||
|     return new Zetabyte({ |     const options = {}; | ||||||
|       executor: new ZfsSshProcessManager(sshClient), |     options.executor = new ZfsSshProcessManager(sshClient); | ||||||
|       idempotent: true, |     options.idempotent = true; | ||||||
|     }); | 
 | ||||||
|  |     if ( | ||||||
|  |       this.options.zfs.hasOwnProperty("cli") && | ||||||
|  |       this.options.zfs.cli.hasOwnProperty("paths") | ||||||
|  |     ) { | ||||||
|  |       options.paths = this.options.zfs.cli.paths; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return new Zetabyte(options); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getDatasetParentName() { |   getDatasetParentName() { | ||||||
|  | @ -335,6 +345,20 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|     let volume_content_source_volume_id; |     let volume_content_source_volume_id; | ||||||
|     let fullSnapshotName; |     let fullSnapshotName; | ||||||
|     let volumeProperties = {}; |     let volumeProperties = {}; | ||||||
|  | 
 | ||||||
|  |     // user-supplied properties
 | ||||||
|  |     // put early to prevent stupid (user-supplied values overwriting system values)
 | ||||||
|  |     if (driver.options.zfs.datasetProperties) { | ||||||
|  |       for (let property in driver.options.zfs.datasetProperties) { | ||||||
|  |         let value = driver.options.zfs.datasetProperties[property]; | ||||||
|  |         const template = Handlebars.compile(value); | ||||||
|  | 
 | ||||||
|  |         volumeProperties[property] = template({ | ||||||
|  |           parameters: call.request.parameters, | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     volumeProperties[VOLUME_CSI_NAME_PROPERTY_NAME] = name; |     volumeProperties[VOLUME_CSI_NAME_PROPERTY_NAME] = name; | ||||||
|     volumeProperties[MANAGED_PROPERTY_NAME] = "true"; |     volumeProperties[MANAGED_PROPERTY_NAME] = "true"; | ||||||
|     volumeProperties[VOLUME_CONTEXT_PROVISIONER_DRIVER_PROPERTY_NAME] = |     volumeProperties[VOLUME_CONTEXT_PROVISIONER_DRIVER_PROPERTY_NAME] = | ||||||
|  | @ -346,7 +370,6 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
| 
 | 
 | ||||||
|     // TODO: also set access_mode as property?
 |     // TODO: also set access_mode as property?
 | ||||||
|     // TODO: also set fsType as property?
 |     // TODO: also set fsType as property?
 | ||||||
|     // TODO: allow for users to configure arbitrary/custom properties to add
 |  | ||||||
| 
 | 
 | ||||||
|     // zvol enables reservation by default
 |     // zvol enables reservation by default
 | ||||||
|     // this implements 'sparse' zvols
 |     // this implements 'sparse' zvols
 | ||||||
|  | @ -691,8 +714,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
| 
 | 
 | ||||||
|     volume_context = await this.createShare(call, datasetName); |     volume_context = await this.createShare(call, datasetName); | ||||||
|     await zb.zfs.set(datasetName, { |     await zb.zfs.set(datasetName, { | ||||||
|       [SHARE_VOLUME_CONTEXT_PROPERTY_NAME]: |       [SHARE_VOLUME_CONTEXT_PROPERTY_NAME]: JSON.stringify(volume_context), | ||||||
|         "'" + JSON.stringify(volume_context) + "'", |  | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     volume_context["provisioner_driver"] = driver.options.driver; |     volume_context["provisioner_driver"] = driver.options.driver; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,11 @@ | ||||||
| const events = require("events"); | const events = require("events"); | ||||||
| const cp = require("child_process"); | const cp = require("child_process"); | ||||||
| 
 | 
 | ||||||
|  | const escapeShell = function (cmd) { | ||||||
|  |   cmd = String(cmd); | ||||||
|  |   return '"' + cmd.replace(/(["$`\\])/g, "\\$1") + '"'; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class Zetabyte { | class Zetabyte { | ||||||
|   constructor(options = {}) { |   constructor(options = {}) { | ||||||
|     const zb = this; |     const zb = this; | ||||||
|  | @ -283,7 +288,8 @@ class Zetabyte { | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|           if (options.fsProperties) { |           if (options.fsProperties) { | ||||||
|             for (const [key, value] of Object.entries(options.fsProperties)) { |             for (let [key, value] of Object.entries(options.fsProperties)) { | ||||||
|  |               value = escapeShell(value); | ||||||
|               args.push("-O"); |               args.push("-O"); | ||||||
|               args.push(`${key}=${value}`); |               args.push(`${key}=${value}`); | ||||||
|             } |             } | ||||||
|  | @ -774,6 +780,7 @@ class Zetabyte { | ||||||
|        * @param {*} value |        * @param {*} value | ||||||
|        */ |        */ | ||||||
|       set: function (pool, property, value) { |       set: function (pool, property, value) { | ||||||
|  |         value = escapeShell(value); | ||||||
|         return new Promise((resolve, reject) => { |         return new Promise((resolve, reject) => { | ||||||
|           let args = []; |           let args = []; | ||||||
|           args.push("set"); |           args.push("set"); | ||||||
|  | @ -913,7 +920,8 @@ class Zetabyte { | ||||||
|           if (options.unmounted) args.push("-u"); |           if (options.unmounted) 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 (const [key, value] of Object.entries(options.properties)) { |             for (let [key, value] of Object.entries(options.properties)) { | ||||||
|  |               value = escapeShell(value); | ||||||
|               args.push("-o"); |               args.push("-o"); | ||||||
|               args.push(`${key}=${value}`); |               args.push(`${key}=${value}`); | ||||||
|             } |             } | ||||||
|  | @ -1012,7 +1020,8 @@ class Zetabyte { | ||||||
|           args.push("snapshot"); |           args.push("snapshot"); | ||||||
|           if (options.recurse) args.push("-r"); |           if (options.recurse) args.push("-r"); | ||||||
|           if (options.properties) { |           if (options.properties) { | ||||||
|             for (const [key, value] of Object.entries(options.properties)) { |             for (let [key, value] of Object.entries(options.properties)) { | ||||||
|  |               value = escapeShell(value); | ||||||
|               args.push("-o"); |               args.push("-o"); | ||||||
|               args.push(`${key}=${value}`); |               args.push(`${key}=${value}`); | ||||||
|             } |             } | ||||||
|  | @ -1093,7 +1102,8 @@ class Zetabyte { | ||||||
|           args.push("clone"); |           args.push("clone"); | ||||||
|           if (options.parents) args.push("-p"); |           if (options.parents) args.push("-p"); | ||||||
|           if (options.properties) { |           if (options.properties) { | ||||||
|             for (const [key, value] of Object.entries(options.properties)) { |             for (let [key, value] of Object.entries(options.properties)) { | ||||||
|  |               value = escapeShell(value); | ||||||
|               args.push("-o"); |               args.push("-o"); | ||||||
|               args.push(`${key}=${value}`); |               args.push(`${key}=${value}`); | ||||||
|             } |             } | ||||||
|  | @ -1301,7 +1311,8 @@ class Zetabyte { | ||||||
|           args.push("set"); |           args.push("set"); | ||||||
| 
 | 
 | ||||||
|           if (properties) { |           if (properties) { | ||||||
|             for (const [key, value] of Object.entries(properties)) { |             for (let [key, value] of Object.entries(properties)) { | ||||||
|  |               value = escapeShell(value); | ||||||
|               args.push(`${key}=${value}`); |               args.push(`${key}=${value}`); | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue