attempt to fix missing group behavior for iscsi target creation
This commit is contained in:
		
							parent
							
								
									716cde8f29
								
							
						
					
					
						commit
						01113c8270
					
				|  | @ -1,6 +1,7 @@ | ||||||
| const { ControllerZfsSshBaseDriver } = require("../controller-zfs-ssh"); | const { ControllerZfsSshBaseDriver } = require("../controller-zfs-ssh"); | ||||||
| const { GrpcError, grpc } = require("../../utils/grpc"); | const { GrpcError, grpc } = require("../../utils/grpc"); | ||||||
| const HttpClient = require("./http").Client; | const HttpClient = require("./http").Client; | ||||||
|  | const sleep = require("../../utils/general").sleep; | ||||||
| 
 | 
 | ||||||
| const Handlebars = require("handlebars"); | const Handlebars = require("handlebars"); | ||||||
| 
 | 
 | ||||||
|  | @ -955,6 +956,47 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver { | ||||||
|               ); |               ); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             // handle situations/race conditions where groups failed to be added/created on the target
 | ||||||
|  |             // groups":[{"portal":1,"initiator":1,"auth":null,"authmethod":"NONE"},{"portal":2,"initiator":1,"auth":null,"authmethod":"NONE"}]
 | ||||||
|  |             // TODO: this logic could be more intelligent but this should do for now as it appears in the failure scenario no groups are added
 | ||||||
|  |             // in other words, I have never seen them invalid, only omitted so this should be enough
 | ||||||
|  |             if (target.groups.length != targetGroups.length) { | ||||||
|  |               response = await httpClient.put(`/iscsi/target/id/${target.id}`, { | ||||||
|  |                 groups: targetGroups, | ||||||
|  |               }); | ||||||
|  | 
 | ||||||
|  |               if (response.statusCode != 200) { | ||||||
|  |                 throw new GrpcError( | ||||||
|  |                   grpc.status.UNKNOWN, | ||||||
|  |                   `failed setting target groups` | ||||||
|  |                 ); | ||||||
|  |               } else { | ||||||
|  |                 target = response.body; | ||||||
|  | 
 | ||||||
|  |                 // re-run sanity checks
 | ||||||
|  |                 if (!target) { | ||||||
|  |                   throw new GrpcError( | ||||||
|  |                     grpc.status.UNKNOWN, | ||||||
|  |                     `unknown error creating iscsi target` | ||||||
|  |                   ); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (target.name != iscsiName) { | ||||||
|  |                   throw new GrpcError( | ||||||
|  |                     grpc.status.UNKNOWN, | ||||||
|  |                     `mismatch name error creating iscsi target` | ||||||
|  |                   ); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (target.groups.length != targetGroups.length) { | ||||||
|  |                   throw new GrpcError( | ||||||
|  |                     grpc.status.UNKNOWN, | ||||||
|  |                     `failed setting target groups` | ||||||
|  |                   ); | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             this.ctx.logger.verbose("FreeNAS ISCSI TARGET: %j", target); |             this.ctx.logger.verbose("FreeNAS ISCSI TARGET: %j", target); | ||||||
| 
 | 
 | ||||||
|             // set target.id on zvol
 |             // set target.id on zvol
 | ||||||
|  | @ -1524,6 +1566,103 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   async failedAttachHelper(call, err) { | ||||||
|  |     const driverShareType = this.getDriverShareType(); | ||||||
|  |     const sshClient = this.getSshClient(); | ||||||
|  |     let response; | ||||||
|  | 
 | ||||||
|  |     // not fully implemented
 | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |     switch (driverShareType) { | ||||||
|  |       case "iscsi": | ||||||
|  |         const isScale = await this.getIsScale(); | ||||||
|  |         const majorMinor = await this.getSystemVersionMajorMinor(); | ||||||
|  | 
 | ||||||
|  |         // only works for BSD-based and 11.3+
 | ||||||
|  |         if (!isScale && majorMinor >= 11.3) { | ||||||
|  |           const sudoEnabled = this.getSudoEnabled(); | ||||||
|  |           const sudoPath = await this.getSudoPath(); | ||||||
|  |           let command; | ||||||
|  | 
 | ||||||
|  |           //19 - encountered non-retryable iSCSI login failure
 | ||||||
|  |           // ^ could be missing groups on the target
 | ||||||
|  | 
 | ||||||
|  |           //cat /var/run/ctld.pid
 | ||||||
|  |           // ps -p <pid> | grep ctld
 | ||||||
|  |           // ps -p `cat /var/run/ctld.pid` | grep ctld (if 0 exit status it's running, otherwise no)
 | ||||||
|  | 
 | ||||||
|  |           // random settle time
 | ||||||
|  |           // this could be getting invoked by other instances of the same controller
 | ||||||
|  |           // or other deployments of controllers in the same of different clusters
 | ||||||
|  |           // altogether
 | ||||||
|  |           let maxSettleTime = 10000; | ||||||
|  |           let settleTime = Math.floor(Math.random() * maxSettleTime + 1); | ||||||
|  |           await sleep(settleTime); | ||||||
|  | 
 | ||||||
|  |           // test if config is bad
 | ||||||
|  |           // if so regen
 | ||||||
|  |           command = sshClient.buildCommand("/usr/sbin/ctld", ["-d"]); | ||||||
|  |           if (sudoEnabled) { | ||||||
|  |             command = sudoPath + " " + command; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           this.ctx.logger.verbose("FailedAttachHelper command: %s", command); | ||||||
|  | 
 | ||||||
|  |           response = await sshClient.exec(command); | ||||||
|  |           let configError = false; | ||||||
|  |           let serviceRunning = false; | ||||||
|  |           if (response.stderr.includes("configuration error")) { | ||||||
|  |             configError = true; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           // NOTE: this will not be in the output if the config file has an error
 | ||||||
|  |           if (response.stderr.includes("daemon already running")) { | ||||||
|  |             serviceRunning = true; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           if (configError) { | ||||||
|  |             this.ctx.logger.warn( | ||||||
|  |               "FailedAttachHelper: ctld appears to have a bad configuration file, attempting to regenerate" | ||||||
|  |             ); | ||||||
|  |             // regen config
 | ||||||
|  |             // midclt call etc.generate ctld
 | ||||||
|  |             command = sshClient.buildCommand("midclt", [ | ||||||
|  |               "call", | ||||||
|  |               "etc.generate", | ||||||
|  |               "ctld", | ||||||
|  |             ]); | ||||||
|  |             if (sudoEnabled) { | ||||||
|  |               command = sudoPath + " " + command; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.ctx.logger.verbose("FailedAttachHelper command: %s", command); | ||||||
|  |             response = await sshClient.exec(command); | ||||||
|  | 
 | ||||||
|  |             // reload service (may not be enough)
 | ||||||
|  |             command = sshClient.buildCommand("/etc/rc.d/ctld", ["reload"]); | ||||||
|  |             if (sudoEnabled) { | ||||||
|  |               command = sudoPath + " " + command; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.ctx.logger.verbose("FailedAttachHelper command: %s", command); | ||||||
|  |             response = await sshClient.exec(command); | ||||||
|  | 
 | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           // note, when the 'bad' state is entered, the status still shows as running
 | ||||||
|  |           // check if service is running
 | ||||||
|  |           // /etc/rc.d/ctld status ...exits 0 if running
 | ||||||
|  |           //command = sshClient.buildCommand("/etc/rc.d/ctld", ["reload"]);
 | ||||||
|  | 
 | ||||||
|  |           // if service is not running attempt a restart
 | ||||||
|  |           // /etc/rc.d/ctld restart
 | ||||||
|  |           //command = sshClient.buildCommand("/etc/rc.d/ctld", ["reload"]);
 | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   async getApiVersion() { |   async getApiVersion() { | ||||||
|     const systemVersion = await this.getSystemVersion(); |     const systemVersion = await this.getSystemVersion(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -356,7 +356,15 @@ class CsiBaseDriver { | ||||||
|             nodeDB |             nodeDB | ||||||
|           ); |           ); | ||||||
|           // login
 |           // login
 | ||||||
|           await iscsi.iscsiadm.login(volume_context.iqn, portal); |           try { | ||||||
|  |             await iscsi.iscsiadm.login(volume_context.iqn, portal); | ||||||
|  |           } catch (err) { | ||||||
|  |             if (typeof this.failedAttachHelper === "function") { | ||||||
|  |               // no need to await this
 | ||||||
|  |               this.failedAttachHelper(call, err); | ||||||
|  |             } | ||||||
|  |             throw err; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|           // find device name
 |           // find device name
 | ||||||
|           device = `/dev/disk/by-path/ip-${portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`; |           device = `/dev/disk/by-path/ip-${portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`; | ||||||
|  | @ -378,6 +386,16 @@ class CsiBaseDriver { | ||||||
| 
 | 
 | ||||||
|             let current_time = Math.round(new Date().getTime() / 1000); |             let current_time = Math.round(new Date().getTime() / 1000); | ||||||
|             if (!result && current_time - timer_start > timer_max) { |             if (!result && current_time - timer_start > timer_max) { | ||||||
|  |               if (typeof this.failedAttachHelper === "function") { | ||||||
|  |                 // no need to await this
 | ||||||
|  |                 this.failedAttachHelper( | ||||||
|  |                   call, | ||||||
|  |                   new Error( | ||||||
|  |                     `hit timeout waiting for device node to appear: ${device}` | ||||||
|  |                   ) | ||||||
|  |                 ); | ||||||
|  |               } | ||||||
|  | 
 | ||||||
|               driver.ctx.logger.warn( |               driver.ctx.logger.warn( | ||||||
|                 `hit timeout waiting for device node to appear: ${device}` |                 `hit timeout waiting for device node to appear: ${device}` | ||||||
|               ); |               ); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue