Skip to main content

iSCSI Connector API Reference

This page lists every phase the iSCSI connector accepts and every config field each phase reads, so you can drive the whole clone cycle over REST instead of the vME console.

It assumes you already know the shape of an agent job. If you have not read it yet, start with Agent Jobs, which covers the request envelope, job states, polling, timeouts, and error handling for all connectors. Installing and enrolling the agent is covered in the connector guide.

Every job in this reference is created the same way:

POST /agent-job

{
"agent_id" : "agt-abc123",
"phase" : "<one of the phases below>",
"config" : { },
"secrets" : { },
"workflow_id" : "wf-abc123",
"wizard_step" : "source_prep",
"role" : "source"
}

The last three are optional and only matter when the job belongs to an onboarding workflow. workflow_id is what lets a phase's results - such as resolvedSqlInstance - persist for later jobs in the sequence. wizard_step records the job as that step's outcome, so omit it for a job that should not count as one, such as a backup-set probe. role is source or clone, and is also accepted as a filter on GET /agent-job.


Phases​

PhaseRuns onWhat it does
DiscoverHostEither hostInventories the host - local vs iSCSI disks, SQL instances and databases, volumes and drive letters in use, portals, targets, and sessions, plus the host's vME folder, its host facts, and the connector's capabilities. Read-only; touches no storage.
ConnectTargetsEither hostConnects the selected iSCSI targets so their disks become visible to later phases. Does not format or mount.
SourcePrepSourcePrepares the source volume (initialize, format NTFS 64K, mount at its folder under the host's vME folder) and then migrates databases onto it if a migrate block is present. A source prepared at drive letters keeps them.
SourcePrepOnlySourceThe prepare half of SourcePrep, with no migration.
SourceMigrateOnlySourceThe migrate half of SourcePrep, against LUNs that are already prepared.
CloneMountCloneConnects the cloned targets, brings only those targets' disks online without formatting, mounts the volume at its folder under the host's vME folder, and attaches the database files. A clone of a source prepared at drive letters can also be given drive letters.
CloneUnmountCloneDetaches the workflow's databases, then disconnects its iSCSI sessions and offlines only the disks behind them. A volume mounted under the vME folder, source or clone, has its mount point released and its folder removed. Can also forget named targets and portals.
QuiesceEither hostBefore a bookmark or rollback of a volume this host has attached: sets every database with files on that volume OFFLINE, flushes the volume and takes its disk offline. Leaves the iSCSI session and the mount point in place. Refuses a database that also has files on another volume.
ResumeEither hostAfter the bookmark or rollback: brings the disk back online, waits for the volume to appear at the same folder (and re-adds the mount point if it does not), and sets the databases Quiesce took offline back ONLINE.

vME runs Quiesce and Resume itself around a snapshot or rollback of an attached volume; you do not normally queue them. Snapshot and clone creation are not agent phases. They are done in vME through the source and clone APIs, between SourceMigrateOnly and CloneMount.

Typical sequence​

Source host                          vME                        Clone host
----------- --- ----------
DiscoverHost
ConnectTargets
SourcePrepOnly
SourceMigrateOnly
snapshot source
create clone
DiscoverHost
CloneMount
... use the databases ...
CloneUnmount

SourcePrep collapses the two source phases into one job. Use the split phases when you want to prepare LUNs well ahead of the migration, or retry a failed migration without re-preparing.

Quiesce and Resume sit outside this sequence. vME queues them itself around a bookmark or a rollback of a volume that a host has attached - see Quiesce and Resume.


Shared config fields​

These sit at the top level of config, alongside the migrate, attach, and unmount blocks rather than inside them.

FieldTypeDescription
portalStringThe vME appliance's iSCSI portal host or IP. Validated and probed on port 3260 before the initiator is touched, so a bad value fails fast.
targetsArray or StringThe targets this job acts on, as whole IQNs: an array of strings, a comma-separated string, or an array of objects that carry the IQN as iqn beside that target's CHAP credentials (see CHAP). Matched outright, never as a fragment, so a job cannot select another workflow's target. Preferred over manualIQN. The console sends the IQNs vME recorded when it created the exports.
manualIQNStringIQN filter - only matching targets are considered. A whole IQN is matched outright. A fragment is matched against whole IQN name components, so a dataset-path fragment such as tank.sources.sales covers both LUNs of a source created before vME 1.10 (...sales.data and ...sales.log). A comma-separated list is accepted. Targets exported by vME 1.10 or later are named by the dataset's guid alone, so scope those with targets or a whole IQN.
datasetStringRestricts targets to those belonging to a named vME dataset. An alternative to manualIQN.
mountRootStringThe host's vME folder, a folder on a local NTFS volume, for example C:\vME. With mountId, the volume is mounted at a folder under it and no drive letter is used: a source at <mountRoot>\sources\<mountId>, a clone at <mountRoot>\clones\<mountId>, each holding MSSQL\Data, MSSQL\Logs and MSSQL\Backups. The drive letter keys are then not needed. On teardown the same pair scopes the detach to that one volume and removes its folder afterwards. The host reports its own setting in DiscoverHost under defaults.vme_root; a job names the folder it wants.
mountIdStringThe volume's folder name under sources or clones. The console sends the dataset's guid. Letters, digits, dot, dash and underscore only. Both keys sit at the top level of config for every phase that places, removes, or pauses a volume: SourcePrep, SourcePrepOnly, SourceMigrateOnly, CloneMount, CloneUnmount, Quiesce and Resume. Send both or neither.
targetSelectionStringWhich of the discovered targets to connect, as a comma-separated list of indexes, or ALL. Optional when manualIQN or dataset is set.
clearOldPortalsBooleanRemoves previously configured portals before connecting. Read by the prep phases only. Default false.
job_timeout_secondsNumberOverrides the job timeout. See Timeouts.

CHAP​

Supply these in secrets, not config, so they are stored encrypted and kept out of logs and job listings.

FieldTypeDescription
chapUserStringCHAP username for one-way CHAP.
chapPasswordStringCHAP secret. Windows enforces 12-16 ASCII characters.
mutualChapPasswordStringSecret for mutual CHAP. Omit for one-way CHAP.
targetsArrayPer-target credentials, for when different targets use different secrets. Each entry carries the whole IQN it belongs to as iqn. An entry without a chapUser only names a target and leaves the top-level credentials in force. The same list also scopes the job, see Shared config fields; when targets is given in both secrets and config, the secrets list is the one used.
CHAP secret length

The Windows iSCSI Initiator rejects secrets outside 12-16 ASCII characters at connection time. Prefer 16 characters of mixed-case alphanumeric - Windows may auto-decode a pure-hex string to fewer bytes and then reject it as too short.

A target's IQN is matched against each targets entry as whole IQN name components. A target with no matching entry falls back to the top-level chapUser / chapPassword, and connects without authentication when neither is set. mutualChapPassword is always global: Windows supports a single initiator reverse CHAP secret system-wide.

A worked secrets example with per-target credentials:

{
"chapUser": "default-user",
"chapPassword": "default-password",
"mutualChapPassword": "global-mutual-secret",
"targets": [
{
"iqn": "iqn.2025-01.com.storage:lun-data",
"chapUser": "user-for-data",
"chapPassword": "secret-for-data"
},
{
"iqn": "iqn.2025-01.com.storage:lun-log",
"chapUser": "user-for-log",
"chapPassword": "secret-for-log"
}
]
}

The targets array is optional: without it, the top-level credentials apply to every connected target.


DiscoverHost​

Requires no config. Send {} and read the inventory back off the completed job - supplying portal refreshes that portal first, so newly created targets appear.

{ "agent_id": "agt-abc123", "phase": "DiscoverHost", "config": {} }

Run this first on any host you are about to act on. Its output gives you the disk numbers, target indexes, and database IDs that the later phases need. Its defaults block also carries vme_root, the folder this host mounts sources and clones under, empty when the host has none set.

The result also carries two blocks that describe the host and the connector:

BlockWhat it holds
host_capabilities.capabilitiesThe names of what this connector can do. See Capabilities.
host_factsFacts about the host. See Host facts.

Capabilities​

The connector names what it can do. The same list is sent when the agent enrolls, with every heartbeat, and in every DiscoverHost result. A name that is missing from the list means the connector on that host cannot do that thing, so check for the name rather than for a version.

NameMeaning
folder_mountSources and clones mount as folders under the host's vME folder, from mountRoot and mountId.
quiesce_resumeThe Quiesce and Resume phases.
forget_named_targetCloneUnmount forgets only the targets named in unmount.removeTargets.
forget_named_portalCloneUnmount forgets only the portals named in unmount.removePortals.
attach_asCloneMount attaches the clone's one database under the name in attach.attachAs. See Attaching under a chosen name.

Host facts​

The agent collects these when its service starts and every 15 minutes after that, and sends the latest set with each heartbeat. A DiscoverHost job collects a fresh set and returns it as host_facts. On GET /agent, both the capabilities and the host facts appear under the agent's attributes.

FieldMeaning
os.caption, os.version, os.build, os.architectureWindows edition, version, build, and architecture.
cpu_logical, memory_gbLogical processors and physical memory.
domain, part_of_domainThe host's domain or workgroup, and whether it is joined to a domain.
ip_addressesThe host's IPv4 addresses, without loopback and link-local addresses.
initiator_iqnThe host's own iSCSI initiator name.
vme_rootThe host's vME folder. Empty when none is set.
sql_instancesOne entry per SQL Server instance: name, service_name, status, database_count, product_name (for example SQL Server 2022), product_version, major_version, product_level, update_level, edition, collation, is_clustered, is_hadr_enabled. The version fields are absent for an instance that could not be reached, such as a stopped one.
warnings, collected_atWhat could not be read, and when the facts were collected (UTC).

ConnectTargets​

Reads portal, clearOldPortals, and targets - the IQNs to connect, as an array or a comma-separated string.

Unlike the prep and clone phases, this one does not honour manualIQN or dataset. If targets is omitted it connects every target discovered on the portal, so name them explicitly unless that is what you want.

{
"agent_id": "agt-abc123",
"phase": "ConnectTargets",
"config": {
"portal": "10.0.0.10",
"targets": [
"iqn.2024-01.com.example:tank.sources.sales.data",
"iqn.2024-01.com.example:tank.sources.sales.log"
]
}
}
targets here is a plain list

For ConnectTargets, targets is only the list of IQNs to connect, as strings. In the other phases the same key scopes the job and can also carry per-target CHAP credentials as objects - see Shared config fields and CHAP.


SourcePrepOnly and SourcePrep​

Prepares the source LUNs. SourcePrep additionally runs the migration when a migrate block is present.

Required: portal, and mountRoot and mountId at the top level of config. The volume is mounted at <mountRoot>\sources\<mountId>, labelled SQL_DB, and takes no drive letter.

Also required unless manualIQN or dataset is set: dataDiskNumber, logDiskNumber, targetSelection. With manualIQN or dataset present the connector finds the disks from the job's targets, so these can be omitted. Send the target's whole IQN in manualIQN as well as in targets.

Only the disks on the job's selected targets are prepared. The phase waits up to diskWaitSeconds for a disk to appear on each of them. If none of the selected targets has a disk by then, the job fails and lists the iSCSI disks it did find; no other disk is partitioned or formatted in its place.

Runs created with drive letters keep them: a source on a data LUN and a log LUN is prepared at dataDriveLetter and logDriveLetter, with no mountRoot or mountId. A folder holds one volume, so a source on two LUNs always uses drive letters.

FieldTypeDescription
diskWaitSecondsNumberSeconds to wait for a disk to appear on each selected target. Default 60.
dataDiskNumberNumberWindows disk number of the data LUN.
logDiskNumberNumberWindows disk number of the log LUN.
confirmPrepBooleanDefault true. Setting false aborts before any destructive step - useful as a dry run.
dataDriveLetterStringDrive-letter layout only. Drive letter for the data LUN, for example E. Labelled SQL_Data.
logDriveLetterStringDrive-letter layout only. Drive letter for the log LUN, for example F. Labelled SQL_Log.
forceDriveLettersBooleanDrive-letter layout only. Reassigns the requested letters even if they are in use elsewhere. Default false.
This phase formats disks

SourcePrep and SourcePrepOnly initialize and format the selected LUNs as NTFS with 64K allocation units. Only new, empty LUNs should be selected.

{
"agent_id": "agt-src01",
"phase": "SourcePrepOnly",
"config": {
"portal": "10.0.0.10",
"manualIQN": "iqn.2026-09.com.enov8:g-10057799540195812700",
"targets": ["iqn.2026-09.com.enov8:g-10057799540195812700"],
"mountRoot": "C:\\vME",
"mountId": "10057799540195812700"
}
}

SourceMigrateOnly and the migrate block​

Moves databases onto the prepared LUNs. SourceMigrateOnly requires a migrate block; SourcePrep accepts one optionally.

Required: the same mountRoot and mountId at the top level of config that the prepare phase was given. The databases are placed in MSSQL\Data, MSSQL\Logs and MSSQL\Backups under the source's folder. For a source prepared at drive letters, name those letters inside migrate as dataDriveLetter and logDriveLetter. A probeOnly run needs neither.

Also required: dbIds, unless one of dbNameFilter, backupPaths, or sourcePaths.dataPath is supplied - those select the databases a different way.

FieldTypeDescription
dataDriveLetterStringDrive-letter layout only. Drive letter holding \MSSQL\Data and \MSSQL\Backups.
logDriveLetterStringDrive-letter layout only. Drive letter holding \MSSQL\Logs.
dbIdsStringComma-separated database IDs to migrate, as reported by DiscoverHost.
dbNameFilterStringSelects databases by name pattern instead of by ID.
dbPrefixStringPrefix for the new database names. Defaults to ISCSI_. Letters, digits, and underscores only.
sqlInstanceStringPins the SQL instance to use. When omitted, the connector auto-detects: the default instance if present, otherwise the first named instance.
selectionInstanceStringThe SQL instance the database selection was made against, normally the instance DiscoverHost reported the dbIds from. If the instance the job resolves to is a different one, the phase fails before any database is backed up, restored or copied, because database IDs differ between instances. Different spellings of the same instance, such as localhost, . and the computer name, count as the same. Leave it out to skip the check.
backupPathsArrayExisting .bak files or folders to restore from, instead of taking a fresh backup. Accepts directories, wildcards and individual files, mixed freely. Files are grouped into sets by the BackupSetGUID in their header.
probeOnlyBooleanReports the backup sets found in backupPaths and exits without restoring. Requires backupPaths, needs no folder or drive letters, and can run before the LUNs are prepared. See Results.
sourcePathsObject{ "dataPath": "...", "logPath": "..." } for raw .mdf/.ldf file copy. Either a directory or a bare drive letter, which is scanned recursively.
confirmStartBooleanDefault true. false aborts before the migration starts.
confirmSelectionBooleanDefault true. false aborts after the database selection is resolved but before work begins.
executeBooleanDefault true. false generates the SQL and reports what it would do without running it.

Migration modes​

The mode is chosen by which selection fields are present:

ModeSetBehaviour
Backup and restoredbIds or dbNameFilterTakes a fresh backup of each selected database and restores it onto the LUNs under the new name.
Restore from existing backupsbackupPathsRestores from .bak files already on disk, with no BACKUP DATABASE step.
Raw file copysourcePathsOfflines the database and copies its .mdf/.ldf files onto the LUNs. Setting this ignores backupPaths.

Worked bodies for each mode are in Bodies for each migration mode.

Which backup set gets restored

Files in backupPaths are grouped into sets by the BackupSetGUID in their header, so a full and a differential for the same database are recognised as separate sets rather than one striped set. The newest full is restored and the newest differential that chains to it is applied on top. Stripes belonging to one set may be spread across several of the supplied paths, and an incomplete set stops the run with the files it found named.

To see what a path holds before restoring, run the job with migrate.probeOnly and read backupSets from the result, then pass the files you want back as migrate.backupPaths.

Results​

On success the phase reports resolvedSqlInstance - the instance it actually used. When the job belongs to a workflow, vME persists this onto the workflow so the teardown job can target the same instance. Read it from GET /agent-job.

A pinned instance can fall back

sqlInstance is probed before it is used. If the pinned instance cannot be reached, the connector falls back to auto-detection rather than failing, so a typo can result in the databases landing on a different instance. Check resolvedSqlInstance on the completed job to confirm where the work went.

A probeOnly run reports backupSets instead of restoring anything. This is response data - read it from the completed job's result with GET /agent-job, not something you send.

Each database found carries its name, whether it can be restored, and the sets the restore would use:

"backupSets": {
"databases": [
{
"name": "Sales",
"restorable": true,
"reason": "",
"full": {
"guid": "A1B2C3D4-...",
"finishDate": "2026-04-01 22:14:07",
"stripes": ["D:\\Backups\\sales_full_stripe1.bak", "D:\\Backups\\sales_full_stripe2.bak"]
},
"diff": {
"guid": "E5F6A7B8-...",
"finishDate": "2026-04-02 22:11:53",
"stripes": ["D:\\Backups\\sales_diff.bak"]
}
}
]
}
FieldMeaning
restorableWhether this database can be restored from what the paths hold.
reasonEmpty when restorable is true. On a failure it names the problem - an incomplete set with the missing stripes listed, or a differential with no matching full.
fullThe full set the restore would use. null when restorable is false.
diffThe differential that chains to that full, or null when there isn't one.
stripesThe file paths belonging to that set. Pass these back as backupPaths to restore this database, including every stripe.

Unlike resolvedSqlInstance, backupSets is not persisted onto the workflow - read it from the job that produced it.

Migrating on already-prepared LUNs, selecting by database ID:

{
"agent_id": "agt-src01",
"phase": "SourceMigrateOnly",
"config": {
"job_timeout_seconds": 86400,
"mountRoot": "C:\\vME",
"mountId": "10057799540195812700",
"migrate": {
"dbIds": "7,8",
"dbPrefix": "ISCSI_"
}
}
}

Omit sqlInstance to let the connector auto-detect, or set it to pin the instance. Bodies for the other two modes are in Bodies for each migration mode.


CloneMount and the attach block​

Required at the top level: portal, attach. Plus targetSelection, unless manualIQN or dataset is set - with a filter present the connector defaults to all matching targets.

Required inside attach: sqlInstance, dbSelection.

Also required at the top level: mountRoot and mountId. The clone is mounted at <mountRoot>\clones\<mountId> and takes no drive letter. A clone on a data LUN and a log LUN is mounted at <mountRoot>\clones\<mountId>\data and <mountRoot>\clones\<mountId>\log. Runs created with drive letters keep them: such a clone is given dataDriveLetter and logDriveLetter inside attach, with no mountRoot or mountId.

Only the disks on the job's selected targets are brought online. The phase waits up to attach.diskWaitSeconds for a disk to appear on each selected target, then waits up to attach.diskOnlineTimeoutSeconds for each of those disks to come online. If none of the selected targets has a disk, the job fails and lists the iSCSI disks attached to the host; no other disk is used in its place. Any other offline iSCSI disk on the host is left offline. A clone carries the same disk signature as its source, so bringing the wrong one online would make Windows rewrite that signature.

Before bringing each of the job's own disks online, the connector clears its read-only flag, so that Windows can give a clone mounted on its source's host a new disk ID. Any other disk with a clashing ID is left offline.

The connector turns Windows automount off while it mounts a clone and turns it back on when the job ends, whether the job succeeds or fails. If a job is stopped before it can do so, the next clone mount on that host turns automount back on when it ends.

FieldTypeDescription
sqlInstanceStringThe SQL instance to attach the databases to.
dbSelectionStringWhich discovered databases to attach - a comma-separated list, or ALL.
dbPrefixStringPrefix applied to the attached database names. Send an empty string to attach the names exactly as they are on the clone.
attachAsStringAttaches the clone's one database under this name instead of its own. Letters, digits and underscores only, 128 characters at most. Leave it out, or send an empty string, to attach under the database's own name. See Attaching under a chosen name.
autoPairBooleanDefault true. Pairs each .mdf with its .ldf automatically. Set false to supply pairs explicitly.
databasesArrayExplicit file pairs, used when autoPair is false.
diskWaitSecondsNumberSeconds to wait for a disk to appear on each selected target. Default 60.
diskOnlineTimeoutSecondsNumberSeconds to wait for each of the job's disks to come online. Default 600. A very large disk can take minutes to come online. A disk still offline at the end fails the job, naming the disk and the reason.
dataDriveLetterStringDrive-letter layout only. Drive letter to assign to the cloned data volume.
logDriveLetterStringDrive-letter layout only. Drive letter to assign to the cloned log volume. A clone of a single-volume source has no separate log volume; send the data letter again.
skipPartitionAssignBooleanDrive-letter layout only. Assigns dataDriveLetter and logDriveLetter to the clone's volumes by their role, without stopping to ask about each partition. Default false.
Prefixing on attach

The prefix is applied only when the name in the database's primary data file does not already start with it, so it is never doubled. A restored database already carries the migration's prefix and is left alone; a raw-copied file keeps its original name and gets the prefix. Sending the same prefix the ingest used is therefore the normal setting.

Send an empty string or NONE to attach names exactly as they appear on the clone. Whichever you choose, make sure the teardown job's unmount.dbPrefix matches what the databases ended up being called.

Names come from the file header

The attached name is read from each database's primary data file, not from the file name on disk. A renamed .mdf still attaches under the name it was created with.

A name already on the instance is skipped

Without attachAs, a database whose name already exists on the SQL instance is skipped: the job log shows a warning and that database is not attached. This is what happens when a clone is mounted on its source's own SQL Server, where the source database still holds the name. To attach such a clone, give it a new name with attachAs.

Mounted at its folder under the host's vME folder:

{
"agent_id": "agt-clone01",
"phase": "CloneMount",
"config": {
"portal": "10.0.1.10",
"manualIQN": "iqn.2026-09.com.enov8:g-10057799540195812700",
"targets": ["iqn.2026-09.com.enov8:g-10057799540195812700"],
"mountRoot": "C:\\vME",
"mountId": "10057799540195812700",
"attach": {
"sqlInstance": "localhost",
"dbSelection": "ALL",
"dbPrefix": "ISCSI_"
}
}
}

targetSelection can be left out here because manualIQN is set; the connector then uses every target that matches.

A clone of a source prepared at drive letters, given drive letters:

{
"agent_id": "agt-clone01",
"phase": "CloneMount",
"config": {
"portal": "10.0.1.10",
"manualIQN": "tank.clones.sales",
"attach": {
"sqlInstance": "localhost",
"dataDriveLetter": "G",
"logDriveLetter": "H",
"skipPartitionAssign": true,
"dbSelection": "ALL",
"dbPrefix": "ISCSI_"
}
}
}

Attaching under a chosen name​

attach.attachAs attaches the clone's database under a name you choose, for example to put a clone on the same SQL Server as its source. The connector reports that it can do this with the attach_as capability.

The name must meet all of these rules:

  • The clone holds exactly one database.
  • The name starts with attach.dbPrefix, compared without regard to case. When dbPrefix is blank or NONE, any name is allowed.
  • No database of that name already exists on the instance, compared without regard to case.
  • Letters, digits and underscores only, 128 characters at most.

A name with other characters, or longer than 128 characters, fails the job before it starts. If any other rule is not met, the phase fails before any database is attached, and the job log says which rule. A name in use is never skipped: unlike a database attached under its own name, it fails the phase.

Send the teardown job an unmount.dbPrefix that the new name starts with, normally the same prefix, so CloneUnmount finds the database under its new name.

A clone mounted on its source's host, attached under a new name:

{
"agent_id": "agt-src01",
"phase": "CloneMount",
"config": {
"portal": "10.0.0.10",
"manualIQN": "iqn.2026-09.com.enov8:g-4185529034771265112",
"targets": ["iqn.2026-09.com.enov8:g-4185529034771265112"],
"mountRoot": "C:\\vME",
"mountId": "4185529034771265112",
"attach": {
"sqlInstance": "localhost",
"dbSelection": "ALL",
"dbPrefix": "ISCSI_",
"attachAs": "ISCSI_Sales_Test"
}
}
}

CloneUnmount and the unmount block​

Required at the top level: unmount.

Required inside unmount: dbSelection, disconnectTargets.

Also required: manualIQN at the top level, if either of those two is set to MATCH - MATCH scopes the work by IQN, and without it the job would silently do nothing, so the job is rejected. Send the whole IQNs in targets as well. For a volume mounted under the vME folder, source or clone, send the same mountRoot and mountId the mount was given: the detach is then scoped to that volume's folder, its mount point is released, and the folder is removed once the disks are gone. Runs created with drive letters keep them: without mountRoot and mountId the detach is scoped by the volume's drive letters.

Before a database is detached, any other session connected to it is closed and its open work rolled back, since SQL Server refuses to detach a database that is in use. A database left attached is returned to multi-user access.

FieldTypeDescription
dbSelectionStringWhich databases to detach - a comma-separated list, ALL, MATCH to scope by manualIQN, or SKIP / NONE to detach nothing.
disconnectTargetsStringWhich iSCSI sessions to disconnect - a comma-separated list, ALL, MATCH, or SKIP / NONE.
dbPrefixStringOnly detach databases with this prefix. Defaults to ISCSI_. Must match the names the databases were actually attached under, or nothing is detached. Letters, digits, and underscores only - it is used as a literal prefix, so SKIP and NONE are treated as prefixes here, not as sentinels.
sqlInstanceStringThe instance to detach from. Optional - when omitted the connector auto-detects, and for workflow jobs vME supplies the instance recorded by the migration.
fullCleanupBooleanDefault true. After the scoped teardown, performs a full iSCSI reset: disconnects every session on the host, not just this workflow's, removes every persistent target and portal, and clears the initiator's list of known targets. Send false for a scoped teardown - vME does on every teardown it queues.
removeTargetsArray or StringWhole IQNs, as an array or a comma-separated string. The host forgets these targets - each one's session is disconnected and its persistent registration removed - and nothing else. Use it for a target whose dataset no longer exists.
removePortalsArray or StringPortal addresses, as an array or a comma-separated string. The host forgets these portals and nothing else. Use it for an appliance that is no longer in use.
Scope your teardown - and send fullCleanup: false

Two separate settings can take other iSCSI workloads offline on a shared server:

  • disconnectTargets: "ALL" disconnects every session on the host. Prefer MATCH with manualIQN, or an explicit list.
  • fullCleanup defaults to true, and does the same thing regardless of what disconnectTargets is set to. Omitting it opts in.

vME sends fullCleanup: false on every teardown it queues, from the console or on its own. A hand-written request that leaves it out does not.

Detach and disconnect are independent

Setting dbSelection to SKIP while disconnecting the targets leaves the databases registered in SQL Server with their files gone, so they show as RECOVERY_PENDING. The same happens if dbPrefix does not match the attached names, because nothing gets detached. Confirm the databases detached before disconnecting the LUNs.

{
"agent_id": "agt-clone01",
"phase": "CloneUnmount",
"config": {
"manualIQN": "iqn.2026-09.com.enov8:g-10057799540195812700",
"targets": ["iqn.2026-09.com.enov8:g-10057799540195812700"],
"mountRoot": "C:\\vME",
"mountId": "10057799540195812700",
"unmount": {
"dbSelection": "MATCH",
"disconnectTargets": "MATCH",
"dbPrefix": "ISCSI_",
"fullCleanup": false
}
}
}

Forget a named target or portal​

A host keeps a target after its dataset is destroyed, and a portal after its appliance is gone, until something removes them. removeTargets and removePortals remove only the entries they name. Name the same targets in manualIQN with dbSelection and disconnectTargets set to MATCH, so any database or session still on them is released first:

{
"agent_id": "agt-clone01",
"phase": "CloneUnmount",
"config": {
"manualIQN": "iqn.2026-09.com.enov8:g-10057799540195812700",
"unmount": {
"dbSelection": "MATCH",
"disconnectTargets": "MATCH",
"fullCleanup": false,
"removeTargets": ["iqn.2026-09.com.enov8:g-10057799540195812700"]
}
}
}

To forget only a portal, set dbSelection and disconnectTargets to NONE and name the address in removePortals. The job log reports each named entry as forgotten, still listed by the host, or not known to this host.


Quiesce and Resume​

These two phases pause a volume that a host has attached, so vME can take a bookmark of it or roll it back without the host holding stale data. vME queues them itself: Quiesce immediately before the bookmark or rollback, and Resume straight after it, whether or not the storage operation succeeded. You do not normally create these jobs by hand.

  • Quiesce finds every database with files on the volume and sets each online one OFFLINE WITH ROLLBACK IMMEDIATE, confirming the state it reaches. It then flushes the volume and takes the disk offline. The iSCSI session stays logged in and the folder mount point stays in place. If a database also has files on other storage, the phase refuses and takes nothing offline, because changing this volume alone would leave that database inconsistent.
  • Resume brings the disk back online, clears its read-only flag, and waits for the volume to come back at its folder, adding the mount point again if it does not. It then sets the databases Quiesce took offline back online. A database that was already offline before the quiesce is left offline.

Required at the top level: targets or manualIQN, naming the target the volume is exported as. Both phases find the disk by its target, never by disk number, since a disk can come back with a different number. mountRoot and mountId are optional, but must be sent together; with them, Quiesce refuses to act unless the disks in scope are mounted under that volume's folder.

quiesce block (optional):

FieldTypeDescription
sqlInstanceStringThe SQL instance to check. Detected automatically when omitted.
databasesArrayDatabase names. Narrows what is taken offline to this list. Without it, every database with files on the volume is taken offline.
allowSpanningDatabasesBooleanTakes a database offline even when it also has files on other storage. Default false, which refuses instead.
flushVolumeCacheBooleanFlushes the volume before the disk goes offline. Default true.
requireSqlBooleanFails the phase if SQL Server cannot be reached. Default false: an unreachable SQL Server cannot be holding the volume open, so the phase goes straight to the disk.
stateTimeoutSecondsNumberSeconds to wait for each database to reach OFFLINE. Default 60.

resume block (optional):

FieldTypeDescription
sqlInstanceStringThe SQL instance to bring the databases back online on.
databasesArrayThe databases to bring back online, as names or as { "name": ..., "wasOnline": ... } objects. Normally the databases the Quiesce job reported; an entry with wasOnline: false is left alone. Every entry needs a name. Without this list, every database with files on the volume that is offline or recovery-pending is brought online.
accessPathsArrayWhere the volume should come back, normally the accessPaths of the disks the Quiesce job reported.
waitSecondsNumberSeconds to wait for the volume to come back at its access paths. Default 60.
repairMountBooleanAdds the folder mount point again if the volume does not come back there on its own. Default true.
stateTimeoutSecondsNumberSeconds to wait for each database to reach ONLINE. Default 600, since recovery on the volume can take a while.

Both phases report what they did on the completed job's result, including when they fail:

PhaseResult fields
QuiescesqlInstance, sqlAvailable; databases, one { name, wasOnline, state, spansOtherVolumes } per database found on the volume; databasesFailed, one { name, reason } per database that would not go offline; disks, one { number, targetIqn, accessPaths, isOffline } per disk; mountFolder; flushed; and error when the phase failed.
ResumesqlInstance, sqlAvailable; databasesOnline, the names brought back or found already online; databasesFailed, one { name, reason } per database that did not come back; disksOnline; accessPaths; repairedMount; waitedSeconds; and error when the phase failed.

A Resume body built from a Quiesce result:

{
"agent_id": "agt-clone01",
"phase": "Resume",
"config": {
"targets": ["iqn.2026-09.com.enov8:g-10057799540195812700"],
"manualIQN": "iqn.2026-09.com.enov8:g-10057799540195812700",
"mountRoot": "C:\\vME",
"mountId": "10057799540195812700",
"resume": {
"sqlInstance": "localhost",
"databases": [{ "name": "ISCSI_Sales", "wasOnline": true }],
"accessPaths": ["C:\\vME\\clones\\10057799540195812700\\"]
}
}
}

Timeouts​

The connector's defaults, on top of the general timeout rules:

PhaseDefault timeout
SourcePrep86400 seconds (24 hours)
SourceMigrateOnly86400 seconds (24 hours)
Quiesce900 seconds (15 minutes)
Resume3600 seconds (1 hour)
All other phases3600 seconds (1 hour)

The migration phases default high because a backup and restore of a large database routinely runs for hours. Quiesce defaults low because taking databases offline is short, bounded work; one still running after 15 minutes is stuck rather than slow. If you are restoring a very large database, or restoring across a slow link, raise job_timeout_seconds above the default rather than relying on it.

A long job can show timed_out before its timeout

Separately from job_timeout_seconds, vME marks a job timed_out when the agent stops reporting for longer than the staleness window - a service restart or a network blip is enough, even though the work on the host continues.

That state is not terminal: a returning agent can still move the job to completed or failed. A client polling for a terminal state must keep polling through it, and raising job_timeout_seconds does not prevent it.


Config field to environment variable​

For seven of the nine phases the agent runs the same PowerShell scripts you would run by hand, passing config through as environment variables. This mapping is useful when you are reading a script, comparing a failing API job against a working manual run, or translating an existing config.json into API calls.

DiscoverHost and ConnectTargets are the exceptions: the agent implements them directly and never invokes the scripts. That is why ConnectTargets takes an explicit targets list rather than honouring manualIQN or dataset - the IQN filtering lives in the PowerShell, which those two phases do not run.

Config fieldEnvironment variableScript
portalISCSI_PORTALUSECASE1, USECASE3
manualIQNISCSI_MANUAL_IQNUSECASE1, USECASE3, UNMOUNT, QUIESCE, RESUME
datasetISCSI_EXPECTED_DATASETUSECASE1, USECASE3, QUIESCE, RESUME
targetSelectionISCSI_TARGET_SELECTIONUSECASE1, USECASE3
clearOldPortalsISCSI_CLEAR_OLD_PORTALSUSECASE1
diskWaitSecondsISCSI_DISK_WAIT_SECONDSUSECASE1
dataDiskNumberISCSI_DATA_DISKUSECASE1
logDiskNumberISCSI_LOG_DISKUSECASE1
dataDriveLetterISCSI_DATA_LETTERUSECASE1
logDriveLetterISCSI_LOG_LETTERUSECASE1
confirmPrepISCSI_CONFIRM_PREPUSECASE1
forceDriveLettersISCSI_FORCE_DRIVE_LETTERSUSECASE1
chapUserISCSI_CHAP_USERUSECASE1, USECASE3
chapPasswordISCSI_CHAP_SECRETUSECASE1, USECASE3
mutualChapPasswordISCSI_MUTUAL_CHAP_SECRETUSECASE1, USECASE3
targetsISCSI_TARGET_IQNSUSECASE1, USECASE3, UNMOUNT, QUIESCE, RESUME
targets (as JSON, for per-target CHAP)ISCSI_CHAP_TARGETSUSECASE1, USECASE3
mountRootISCSI_MOUNT_ROOTUSECASE1, USECASE2, USECASE3, UNMOUNT, QUIESCE, RESUME
mountIdISCSI_MOUNT_IDUSECASE1, USECASE2, USECASE3, UNMOUNT, QUIESCE, RESUME
migrate.dataDriveLetterISCSI_MIGRATE_DATA_DRIVEUSECASE2
migrate.logDriveLetterISCSI_MIGRATE_LOG_DRIVEUSECASE2
migrate.dbIdsISCSI_MIGRATE_DB_IDSUSECASE2
migrate.dbNameFilterISCSI_MIGRATE_DB_NAME_FILTERUSECASE2
migrate.dbPrefixISCSI_MIGRATE_DB_PREFIXUSECASE2
migrate.sqlInstanceISCSI_SQL_INSTANCEUSECASE2
migrate.selectionInstanceISCSI_MIGRATE_SELECTION_INSTANCEUSECASE2
migrate.backupPathsISCSI_MIGRATE_BACKUP_PATHSUSECASE2
migrate.probeOnlyISCSI_MIGRATE_PROBE_ONLYUSECASE2
migrate.sourcePaths.dataPathISCSI_MIGRATE_SOURCE_DATA_PATHUSECASE2
migrate.sourcePaths.logPathISCSI_MIGRATE_SOURCE_LOG_PATHUSECASE2
migrate.confirmStartISCSI_MIGRATE_CONFIRMUSECASE2
migrate.confirmSelectionISCSI_MIGRATE_DB_CONFIRMUSECASE2
migrate.executeISCSI_MIGRATE_EXECUTEUSECASE2
attach.sqlInstanceISCSI_SQL_INSTANCEUSECASE3
attach.dataDriveLetterISCSI_ATTACH_DATA_DRIVEUSECASE3
attach.logDriveLetterISCSI_ATTACH_LOG_DRIVEUSECASE3
attach.dbSelectionISCSI_ATTACH_DB_SELECTIONUSECASE3
attach.dbPrefixISCSI_ATTACH_DB_PREFIXUSECASE3
attach.attachAsISCSI_ATTACH_DB_NAMEUSECASE3
attach.autoPairISCSI_ATTACH_AUTO_PAIRUSECASE3
attach.databasesISCSI_ATTACH_DATABASESUSECASE3
attach.skipPartitionAssignISCSI_SKIP_PARTITION_ASSIGNUSECASE3
attach.diskWaitSecondsISCSI_DISK_WAIT_SECONDSUSECASE3
attach.diskOnlineTimeoutSecondsISCSI_DISK_ONLINE_TIMEOUT_SECONDSUSECASE3
unmount.sqlInstanceISCSI_SQL_INSTANCEUNMOUNT
unmount.dbSelectionISCSI_DETACH_DB_SELECTIONUNMOUNT
unmount.dbPrefixISCSI_DETACH_DB_PREFIXUNMOUNT
unmount.disconnectTargetsISCSI_DISCONNECT_TARGET_SELECTIONUNMOUNT
unmount.fullCleanupISCSI_FULL_CLEANUP_CONFIRMUNMOUNT
unmount.removeTargetsISCSI_REMOVE_TARGETSUNMOUNT
unmount.removePortalsISCSI_REMOVE_PORTALSUNMOUNT
quiesce.sqlInstanceISCSI_SQL_INSTANCEQUIESCE
quiesce.databasesISCSI_QUIESCE_DATABASESQUIESCE
quiesce.allowSpanningDatabasesISCSI_QUIESCE_ALLOW_SPANNINGQUIESCE
quiesce.flushVolumeCacheISCSI_QUIESCE_FLUSHQUIESCE
quiesce.requireSqlISCSI_QUIESCE_REQUIRE_SQLQUIESCE
quiesce.stateTimeoutSecondsISCSI_STATE_TIMEOUT_SECONDSQUIESCE
resume.sqlInstanceISCSI_SQL_INSTANCERESUME
resume.databasesISCSI_RESUME_DATABASESRESUME
resume.accessPathsISCSI_RESUME_ACCESS_PATHSRESUME
resume.waitSecondsISCSI_RESUME_WAIT_SECONDSRESUME
resume.repairMountISCSI_RESUME_REPAIR_MOUNTRESUME
resume.stateTimeoutSecondsISCSI_STATE_TIMEOUT_SECONDSRESUME
Flat versus nested config

config.json in the toolkit nests fields under source and clone containers. The agent job config above uses the flat shape - portal and manualIQN at the top level, with migrate, attach, unmount, quiesce, and resume as blocks beside them. Do not wrap them in source or clone when creating a job.


Worked Examples​

Every job is created the same way: POST /agent-job with the agent, the phase, and a config block, then poll until it settles.

POST /agent-job
GET /agent-job?job_id=<job_id>&include_events=1

A job settles at completed, failed, or cancelled. timed_out means the agent stopped reporting; an agent that comes back can still move the job to its real outcome. CHAP credentials, when needed, are sent as a secrets object beside config on the same POST - see CHAP.

Discover the host​

Inventories the SQL host: instances with their versions, databases with their state, size, and recovery model, the volumes and drive letters in use, and the iSCSI portals, targets, and sessions. The defaults block carries the host's vME folder as vme_root, and the result also returns the connector's capabilities and the host facts.

{
"agent_id": "<source_agent_id>",
"phase": "DiscoverHost",
"config": { "portal": "10.0.0.10" }
}

Prepare LUNs and ingest in one job​

SourcePrep connects the workflow's LUNs, formats them, and runs the migration. Give a long ingest the time it needs with job_timeout_seconds.

{
"agent_id": "<source_agent_id>",
"phase": "SourcePrep",
"config": {
"portal": "10.0.0.10",
"manualIQN": "iqn.2026-09.com.enov8:g-10057799540195812700",
"targets": ["iqn.2026-09.com.enov8:g-10057799540195812700"],
"mountRoot": "C:\\vME",
"mountId": "10057799540195812700",
"confirmPrep": true,
"migrate": {
"dbIds": "ALL",
"confirmStart": true,
"dbPrefix": "ISCSI_"
},
"job_timeout_seconds": 86400
}
}

To run the steps separately, use SourcePrepOnly with the top-level fields only, then SourceMigrateOnly with the migrate block and the same mountRoot and mountId.

Bodies for each migration mode​

The migrate block above used backup and restore (dbIds, or dbNameFilter to select by name pattern). The other two modes swap the selection fields:

Restore from existing .bak files. Inventory the paths first with a probe - no folder or drive letters needed, nothing is restored:

{
"agent_id": "<source_agent_id>",
"phase": "SourceMigrateOnly",
"config": {
"migrate": {
"probeOnly": true,
"backupPaths": ["D:\\Backups\\*.bak"]
}
}
}

The result's backupSets lists each database found, whether it can be restored, and the files its set needs. Pass the files you want back as backupPaths on the real ingest:

{
"agent_id": "<source_agent_id>",
"phase": "SourceMigrateOnly",
"config": {
"mountRoot": "C:\\vME",
"mountId": "10057799540195812700",
"migrate": {
"backupPaths": [
"D:\\Backups\\sales_full_stripe1.bak",
"D:\\Backups\\sales_full_stripe2.bak",
"D:\\Backups\\sales_diff.bak"
],
"confirmStart": true,
"dbPrefix": "ISCSI_",
"sqlInstance": "HOST\\STAGING"
},
"job_timeout_seconds": 86400
}
}

sqlInstance is optional: omit it and the agent detects the instance.

Raw file copy. Offlines the database and copies its files onto the LUNs:

{ "mountRoot": "C:\\vME", "mountId": "10057799540195812700", "migrate": { "sourcePaths": { "dataPath": "D:\\Data\\sales.mdf", "logPath": "L:\\Logs\\sales_log.ldf" }, "confirmStart": true } }

Mount a clone​

Run against the target appliance's agent, pointing manualIQN and targets at the clone's IQN and mountId at the clone's id. The attach applies the same prefix the ingest used, so the clone comes up under the name the ingest created.

{
"agent_id": "<target_agent_id>",
"phase": "CloneMount",
"config": {
"portal": "10.0.0.20",
"manualIQN": "iqn.2026-09.com.enov8:g-4185529034771265112",
"targets": ["iqn.2026-09.com.enov8:g-4185529034771265112"],
"mountRoot": "C:\\vME",
"mountId": "4185529034771265112",
"attach": {
"sqlInstance": "localhost",
"dbSelection": "ALL",
"autoPair": true,
"dbPrefix": "ISCSI_"
}
}
}

Tear down​

CloneUnmount with MATCH scopes everything to the workflow's own LUNs: only its databases are detached and only its sessions disconnected. With mountRoot and mountId, the volume's folder is removed once its disks are gone. The same phase tears down a mounted clone (target agent, clone IQN and id) or the staging databases on the source host (source agent, source IQN and id):

{
"agent_id": "<target_agent_id>",
"phase": "CloneUnmount",
"config": {
"manualIQN": "iqn.2026-09.com.enov8:g-4185529034771265112",
"targets": ["iqn.2026-09.com.enov8:g-4185529034771265112"],
"mountRoot": "C:\\vME",
"mountId": "4185529034771265112",
"unmount": {
"dbSelection": "MATCH",
"disconnectTargets": "MATCH",
"fullCleanup": false,
"dbPrefix": "ISCSI_"
}
}
}

fullCleanup defaults to true when omitted and resets the iSCSI initiator host-wide. Always send it explicitly - false for anything scoped to one workflow.

Chained end to end​

The Onboarding (API) folder in the vME Postman collection runs this whole sequence as ordered, chained requests, carrying job and workflow IDs between steps, including the vME-side snapshot, clone, and send calls between the agent jobs.