Example API Training Scenario¶
In this training API scenario, the developer will be driving the following behaviors of the Digital Rebar Platform (DRP)
and features. All example API usage will be documented using curl
calls, so they can be simulated at the command line.
The curl
calls should provide enough detail for a developer to adapt to their preferred language to manipulate the
API.
This example API interaction scenario assumes that the developer has familiarity with the DRP solution, terminology, and basic operational patterns for managing Infrastructure with DRP. Additionally, it is assumed that the DRP Endpoint being manipulated is already fully setup and configured for use with Universal pipelines. DRP Should be at v4.7.0 or newer, with all associated content also at v4.7.0 or newer.
API Documentation can be found in the Swagger UI on an installed DRP Endpoint for interactive exploration and use of the
API at https://DRP_ENDPOINT:8092/swagger-ui>
. Replace DRP_ENDPOINT
with your endpoint address or IP.
In the Swagger UI, authenticate to the DRP Endpoint via the
Authorize
button.
Objectives¶
The following objectives will be described in this document.
- Authentication to the DRP Endpoint, and obtain a JWT Token
- Use of the DRP "Universal" content system, enabling "Pipelines"
- Pre-creation of a Machine object, as opposed to the traditional PXE Discovery mechanism
- Transition a created machine through a Universal pipeline provisioning process
- The machine provisioning process will utilize the "image-deploy" feature to deliver an Operating System payload to the Machine
- Deprovisoin a machine and return it to a waiting state, after wiping the disks
- Destroy the Machine object associated with a machine
- Use the Callback system to send a customized Payload to an external RESTful API endpoint
Detailed API Usage¶
Below are the detailed implementation training instructions as described in the objectives section.
In all following code examples, code will heavily use Variables (shell)
along with the curl
examples. The main Variables are defined as follows:
export RS_ENDPOINT=https://drp-endpoint.example.com:8092
export RS_USER=rocketskates
export RS_PASSWORD=r0cketsk8ts
export RS_KEY=$RS_USER:$RS_PASSWORD
export HEADERS="-H 'Content-Type: application/json' -H 'Accept: application/json'"
In addition, after initial Authentication, the following will be set, and assumed to be used for all subsequent curl
command line calls for authentication.
export RS_TOKEN=<token>
export HEADERS="$HEADERS -H 'Authentication: Bearer $RS_TOKEN"
export CURL="curl -s -X GET -k -u $RS_KEY $HEADERS"
Authentication¶
Username / Password to Obtain a JWT Token¶
Initial authentication is with a traditional User/Password pair. However, a developer should request a Token with an appropriate time-to-live (TTL) value. Tokens can also be highly scoped to provide security controls around Tokens, should one "escape into the wild".
Here is how to submit a User/Pass pair to obtain a JWT Token for subsequent use. This example obtains the token and
assigns it to the
RS_TOKEN
variable for future use in the shell.
TTL=86400 # token good for 24 hours
ROLES=superuser # superuser role
export RS_TOKEN=$(curl -s -X GET -k -u $RS_KEY $HEADERS "$RS_ENDPOINT/api/v3/users/drpadmin/token?ttl=$TTL&roles=$ROLES" | jq -r '.Token')
Example Response¶
Verify the token works with a quick "info" API call:
curl -k -s -X GET -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: Bearer $RS_TOKEN" $RS_ENDPOINT/api/v3/info | jq .version
Should return a JSON string similar to v4.7.0
Token Auth Use¶
Subsequent curl
calls in this documentation may be called out as follows; substitute variables as defined in the above
section, where appropriate:
Where "$CURL" expands to something like:
curl -s -X GET -k -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Bearer <...snip...>'
and <...snip...>
would be replaced with a very long Token string.
Create Machine Object¶
Machine objects are JSON blobs that represent the last recorded state of a given physical device (called "Machine" in DRP parlance; as opposed to a host, node, server or similar). As a Machine passes through Workflow tasks in various pipelines, the state of the Machine is recorded on:
- Machine object fields
- Variable bits of information captured in the Params field of the Machine object (generally all Params are "type defined" for safety)
- Profiles (collections of Param values), in the Profiles field of the Machine object
In addition, the Machine object carries the state of the DRP Runner or Task System. These are recorded in various Fields on the Machine object.
Machine objects are usually dynamically created when a Machine first PXE boots, and if it is unknown to the System, a
new Machine object is created. As the Machine passes through a typical Inventory workflow (eg "universal-discover
"), a
tremendous amount of detailed information about the state of the machine is recorded on the Machine object.
It is possible to "pre-create" a Machine object, which will match the physical characteristics of a device that subsequently PXE boots on the Network. Control of the machine occurs when details of the booting machine match the appropriate signatures in an existing Machine object. Typically the primary values that correlate a Machine object to a physical device is the Network Information Card (NIC) MAC (Media Access Controls) Address of the booting interface.
Creating a minimal Machine object can be done by generating a valid JSON structure with just the Name
field of a
machine. All other fields will be dynamically filled with default values. HOWEVER, to correlate a Machine to a physical
device, additional details (MAC addresses at a minimum) must be provided.
When the Machine object is first created a UUID value will be randomly generated and assigned to the Machine. It is possible during Machine object creation to pre-define a UUID, which can be used to match external infrastructure UUID representations for that machine (e.g. other asset management services UUID references).
Example of creating a minimally defined Machine object:
UUID=$(uuidgen | tr '[:upper:]' '[:lower:]') # must be lowercase, thank you MacOS X
METHOD="PATCH"
curl -k -s -X $METHOD -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: Bearer $RS_TOKEN"
-d ' {
"Name": "test-machine",
"Uuid": "'$UUID'",
"Description": "machine description",
"HardwareAddrs": [ "00:00:00:99:99:00", "00:00:00:99:99:01", "00:00:00:99:99:02", "00:00:00:99:99:02" ]
} ' "$RS_ENDPOINT/api/v3/machines"
Note that the HardwareAddrs
list is the critical connection between the Machine object and the booting physical device
via DHCP/PXE. These must be correct, and they must be unique.
In the sample above, the UUID is dynamically generated, but as long as it is unique, and a validly formed UUID, any value can be used (e.g. existing UUID reference in external asset management database).
- reference: Universally Unique Identifier
Example Response¶
A JSON response with the newly created Machine object will be returned along with an HTTP 200 success code. For error codes (HTTP Response Codes) that you may encounter, see the Swagger UI or API Documentation.
{
"Validated": true,
"Available": true,
"Errors": [],
"ReadOnly": false,
"Meta": {
"feature-flags": "change-stage-v2"
},
"Endpoint": "",
"Bundle": "",
"Partial": false,
"Name": "test-machine",
"Description": "machine description",
"Uuid": "1382ae65-5d95-4ba4-a728-68b0d4eadb46",
"CurrentJob": "",
"Address": "",
"Stage": "discover",
"BootEnv": "sledgehammer",
"Profiles": [],
"Params": {},
"Tasks": [
"stage:discover",
"bootenv:sledgehammer",
"enforce-sledgehammer",
"set-machine-ip-in-sledgehammer",
"reserve-dhcp-address",
"ssh-access",
"stage:sledgehammer-wait"
],
"CurrentTask": -1,
"RetryTaskAttempt": 0,
"TaskErrorStacks": [],
"Runnable": true,
"Secret": "uKu8c_b1-gvZmHe-",
"OS": "",
"HardwareAddrs": [
"00:00:00:99:99:00",
"00:00:00:99:99:01",
"00:00:00:99:99:02",
"00:00:00:99:99:02"
],
"Workflow": "discover-base",
"Arch": "amd64",
"Locked": false,
"Context": "",
"Fingerprint": {
"SSNHash": "",
"CSNHash": "",
"SystemUUID": "",
"MemoryIds": []
},
"Pool": "default",
"PoolAllocated": false,
"PoolStatus": "Free",
"WorkflowComplete": false
}
Note that the machine object will be placed in the defined
defaultWorkflow
according to the DRP Endpoint Info & Preferences
setting. Once the physical machine that matches this Machine object definition PXE boots, it will immediately transition
into this Workflow. If this behavior is not desired, the Developer should reset the Machine runner state and Workflow
settings accordingly, before the Machine PXE boots.
Configuring the Machine¶
Customization of the Machine object is generally performed by manipulating one of three components of the Machine JSON data structure that represents the state and configuration of the device:
- JSON Fields - Generally contain identifying details of the machine, along with the management and control of the Workflow operations
- Params (a top level JSON field on the machine object) - Params are used to record state information and to provide configuration details that Workflow tasks/jobs consume
- Profiles (also a top level JSON field on the machine object) - provides convenient group of Param values
Setting the Universal Application¶
Universal workflow transitions allow for complete zero touch operations, encompassing Discovery, Inventory, Classification, Hardware Lifecycle Management, OS installations, Cluster Configuration, and final application/system configuration. In addition, integrating with external infrastructure services is baked into the Universal workflows.
There are Param values that must be set to define the zero touch journey that will guide the machine's transition to it' s final target destination state. Generally they are:
universal/application
- defines OS selection and optional additional configuration details that will be added to the system based on automatic Profile matchinglinux/install-bootenv
- for Kickstart/Preseed Linux OS deployments; defines the Distro and version of Linux to installimage-deploy/*
- these params define which single artifact Image and accompanying configuration details for a non-Kickstart/Preseed installations; generally a single image is described in a Profile containing the Param values
The following example will assume Universal is being driven to perform the following tasks:
- Perform an image deployment of a Linux operating system
- Installing Ubuntu 18.04
- Using the
universal/application
Param, and the Profile describing theimage-deploy
configuration exists on the system
METHOD="POST"
UUID=<MACHINE_ID>
PARAM=$(printf "%s" ""universal/application" | jq -sRr @uri) # URL encodes the string
VALUE=${VALUE:-"image-deploy"}
curl -k -s -X $METHOD -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: Bearer $RS_TOKEN"
-d ""$VALUE""
"$RS_ENDPOINT/api/v3/machines/$UUID/params/$PARAM" | jq '.'
Example Response¶
Create Profile for Image Deploy¶
In addition to specifying use of the Image Deploy workflow pipeline, a profile describing the image deployment location and configuration details need to be specified. Generally these profiles should be maintained in a curated content pack describing the associated images and configurations for given deployment scenarios.
This example creates an ad-hoc Profile with the Param values necessary to drive deployment in the Universal workflows and pipelines. Normally image profiles should be maintained as Infrastructure-as-Code (IaC) content packs with the descriptive configuration information necessary to deploy the image. The Profiles can be added dynamically by a Classifier Stage/Task in the workflow, or the operator can set the Profile on the Machine prior to transitioning it to an installation/provisioning Workflow.
METHOD="POST"
curl -k -s -X $METHOD -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: Bearer $RS_TOKEN" -d '{
"Name": "id-ubu-18-v1",
"Description": "profile id-ubu-18-v1",
"Meta": {
"color": "blue",
"icon": "linux"
},
"Params": {
"image-deploy/image-file": "/files/images/ubuntu-18.04-5c58265397.tgz",
"image-deploy/image-os": "linux",
"image-deploy/image-os-subtype": "ubuntu",
"image-deploy/image-type": "tgz",
"image-deploy/use-cloud-init": true
}
}' $RS_ENDPOINT/api/v3/profiles | jq '.'
Example Response¶
{
"Validated": true,
"Available": true,
"Errors": [],
"ReadOnly": false,
"Meta": {
"color": "blue",
"icon": "linux"
},
"Endpoint": "",
"Bundle": "",
"Partial": false,
"Name": "id-ubu-18-v1",
"Description": "profile id-ubu-18-v1",
"Documentation": "",
"Params": {
"image-deploy/image-os": "linux",
"image-deploy/image-os-subtype": "ubuntu",
"image-deploy/image-type": "tgz",
"image-deploy/image-file": "/files/images/ubuntu-18.04-5c58265397.tgz",
"image-deploy/use-cloud-init": true
},
"Profiles": []
}
Add the Profile to the Machine¶
Manipulating Profiles on a Machine object requires use of the PATCH operation, and also requires getting the current set of Profiles on the Machine. This method, although a bit more cumbersome to implement, enforces strong guarantees on atomicity and correctness in a multi-writer API environment.
Profile order does matter, if Profiles contain the same Params; based on the parameter precedence, the Param value may
differ. The PATCH
operation allows for setting an explicit order in the list of Profiles and applying that to the
machine.
A PATCH
process to modify the list of Profiles on a Machine generally follows this flow:
- get the current set of Profiles
- insert the new Profile to add to the Machine in the order desired from the original Profiles list
- use PATCH with the correct test and replace operations
- modify the Machine
PROFILE="id-ubu-18-v1"
CURRENT=$(curl -k -s -X GET -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: Bearer $RS_TOKEN"
"$RS_ENDPOINT/api/v3/machines/$UUID?slim=true" | jq -c '.Profiles')
NEW=$(echo "$CURRENT" | jq -c ' .+ ["'$PROFILE'"]')
The above variables CURRENT
will become our test operation to compare against for the PATCH
, while NEW will be
assigned the current Profiles, plus our additional new Profile ($PROFILE
).
The variables will contain a JSON array/list structure (eg
'[ "foo", "bar", "baz" ]
'). To create the very first Profile on a machine, set the test operation to an empty
array/list ('[]
').
Once the new Profile assignment array/list has been constructed, apply it to the Machine:
METHOD=PATCH
UUID=<MACHINE_ID>
curl -k -s -X $METHOD -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: Bearer $RS_TOKEN"
-d '[{"op":"test","path":"/Profiles","value":'$CURRENT'},
{"op":"replace","path":"/Profiles","value":'$NEW'}]'
"$RS_ENDPOINT/api/v3/machines/$UUID" | jq '.'
Example Response¶
A complete Machine object JSON response will be generated on success. Note that this could be a very large blob, if Inventory, BIOS, RAID, or other large data structures have been generated/applied to the Machine previously.
{
"Validated": true,
"Available": true,
"Errors": [],
"ReadOnly": false,
"Meta": {
"color": "black",
"feature-flags": "change-stage-v2",
"icon": "server"
},
"Endpoint": "",
"Bundle": "",
"Partial": false,
"Name": "test-machine",
"Description": "machine description",
"Uuid": "1382ae65-5d95-4ba4-a728-68b0d4eadb46",
"CurrentJob": "",
"Address": "",
"Stage": "none",
"BootEnv": "sledgehammer",
"Profiles": [
"id-ubu-18-v1"
],
"Params": {
"universal/application": "image-deploy"
},
"Tasks": [],
"CurrentTask": 0,
"RetryTaskAttempt": 0,
"TaskErrorStacks": [],
"Runnable": true,
"Secret": "uKu8c_b1-gvZmHe-",
"OS": "",
"HardwareAddrs": [
"00:00:00:99:99:00",
"00:00:00:99:99:01",
"00:00:00:99:99:02",
"00:00:00:99:99:02"
],
"Workflow": "",
"Arch": "amd64",
"Locked": false,
"Context": "",
"Fingerprint": {
"SSNHash": "",
"CSNHash": "",
"SystemUUID": "",
"MemoryIds": []
},
"Pool": "default",
"PoolAllocated": false,
"PoolStatus": "Free",
"WorkflowComplete": true
}
Configure BMC (baseboard management controller)¶
The BMC (baseboard management controller) is responsible for implementing the IPMI, Redfish, and vendor proprietary
hardware protocols on the system (eg: reboot, power on, power off, nextbootpxe, nextbootdisk, etc.). It is important to
be able to regain control of a system if the state of the Runner (Agent)
becomes unavailable; either intentionally (dissolving the Agent/Runner in the OS), or unintentionally (it dies). This
allows setting the DRP Endpoint state to boot the system to Sledgehammer (via some Workflow - like discover-base
), and
then rebooting the system.
The universal-discover
and universal-hardware
workflows will inventory and configure the BMC. With this in mind; if
the BMC has been pre-configured with IP Address, default GW, etc. information, then the only remaining configuration
necessary is setting the Username and Password for authentication of the BMC actions.
If Digital Rebar is going to be responsible for configuration of the BMC values, please consult the documentation for
details on what needs to be set. Documentation is in the IPMI Plugin documentation (it's unfortunately named ipmi
, as
the Plugin actually controls the BMC via IPMI, Redfish, or Vendor specific protocols, not just IPMI functions).
Setting the Username/Password can be performed in a Profile that is ultimately attached to the system automatically (
assuming a group of systems share user/pass authentication credentials). The
ipmi/password
value must be a properly encrypted (using NACL)
value, not a clear text string. There are several methods to perform the encryption, but ultimately it will boil down to
your language This document assumes you have set the Secure Param JSON object correctly with the Key
, Nonce
, and
Payload
.
Setting the ipmi/username:
METHOD="POST"
UUID=<MACHINE_ID>
BMC_USER="root"
BMC_PASS="calvin"
PARAM=$(printf "%s" "$BMC_USER" | jq -sRr @uri | sed 's/%0A//') # strip linefeed on param
curl -k -s -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: Bearer $RS_TOKEN"-d '"root"' $RS_ENDPOINT/api/v3/machines/1382ae65-5d95-4ba4-a728-68b0d4eadb46/params/ipmi%2Fusername
Setting the ipmi/password
- using a pre-generated Secure Param object:
PARAM=$(printf "%s" "$BMC_PASS" | jq -sRr @uri | sed 's/%0A//') # strip line feed on param
curl -k -s -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' --H "Authorization: Bearer $RS_TOKEN" -d '
{
"Key": "sAlABZy90If04Rx0VI2uVZ4HbyIavnEPXOH8EoX4MWA=",
"Nonce": "AEn9DTt+gbtJkmiWteOEdKT8R5xeUT8c",
"Payload": "1w+6NI48KLmm9iJpFS2cM2tE58UFwu/3"
}' $RS_ENDPOINT/api/v3/machines/1382ae65-5d95-4ba4-a728-68b0d4eadb46/params/ipmi%2Fpassword
Example Responses¶
For setting the ipmi/username
, the username is returned as a string:
For setting the ipmi/password
the encrypted object is returned on success:
{
"Key": "sAlABZy90If04Rx0VI2uVZ4HbyIavnEPXOH8EoX4MWA=",
"Nonce": "AEn9DTt+gbtJkmiWteOEdKT8R5xeUT8c",
"Payload": "1w+6NI48KLmm9iJpFS2cM2tE58UFwu/3"
}
Provision the Machine¶
Provision operations are performed by transitioning the machine to an appropriate workflow. For systems using *
Universal* content; all provisioning operations generally should start with the
"universal-discover
" workflow. Newer Universal content systems may have a new decorator starting Workflow (possibly
named
"universal-start
").
Note
Image Deploy chain map support was only added in the Universal content pack at version v4.7.1
or later. If
you do not have this version of Universal installed, please upgrade, or create an appropriate
universal/workflow-chain-map-override
as necessary.
Note
If a Machine is in the same workflow that is being set again, then there is a "no-op" action. To re-run the
same workflow, remove the existing workflow (set Workflow:
field to empty ""
value) from the system, then set the
same workflow again.
Provisioning example:
METHOD="PATCH"
WF="universal-discover"
UUID=<MACHINE_ID>
curl -k -s -X $METHOD -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: Bearer $RS_TOKEN" -d '[{"op": "replace", "path": "/Workflow", "value": "'$WF'"}]' https://66.165.231.2:8092/api/v3/machines/$UUID
This results in setting the new Workflow system which will reset several Fields on the JSON object, including a composed list of the Tasks from the Workflow specification. Once the Machine boots, it will boot into the in-memory Sledgehammer OS, start the Agent (Runner), and begin executing the tasks.
Example Response¶
{
"Validated": true,
"Available": true,
"Errors": [],
"ReadOnly": false,
"Meta": {
"feature-flags": "change-stage-v2"
},
"Endpoint": "",
"Bundle": "",
"Partial": false,
"Name": "d00-00-00-51-01-00.pve-lab.local",
"Description": "",
"Uuid": "715819a9-3c09-445e-86cb-71afa3b0f4bf",
"CurrentJob": "c17af8e6-48ac-414a-a412-7ab99b1c47e9",
"Address": "192.168.1.5",
"Stage": "discover",
"BootEnv": "sledgehammer",
"Profiles": [
"id-ubu-18-v1"
],
"Params": {
"< ... snip ... >": "< ... redacted for brevity ... >"
},
"Tasks": [
"stage:discover",
"bootenv:sledgehammer",
"enforce-sledgehammer",
"set-machine-ip-in-sledgehammer",
"reserve-dhcp-address",
"ssh-access",
"stage:universal-discover-start-callback",
"callback-task",
"stage:universal-discover-pre-flexiflow",
"flexiflow-start",
"flexiflow-stop",
"stage:universal-discover",
"universal-discover",
"stage:raid-reset",
"raid-reset",
"stage:raid-enable-encryption",
"raid-tools-install",
"raid-enable-encryption",
"stage:shred",
"shred",
"stage:raid-reset",
"raid-reset",
"stage:universal-discover-post-flexiflow",
"flexiflow-start",
"flexiflow-stop",
"stage:universal-discover-classification",
"classify-stage-list-start",
"classify-stage-list-stop",
"stage:universal-discover-post-validation",
"validation-start",
"validation-stop",
"stage:universal-discover-complete-callback",
"callback-task",
"stage:universal-chain-workflow",
"universal-chain-workflow",
"stage:complete-nobootenv"
],
"CurrentTask": -1,
"RetryTaskAttempt": 0,
"TaskErrorStacks": [],
"Runnable": true,
"Secret": "tQXvI8wWV2am27fq",
"OS": "centos-8",
"HardwareAddrs": [
"00:00:00:51:01:00",
"3e:6a:da:eb:d8:76"
],
"Workflow": "universal-discover",
"Arch": "amd64",
"Locked": false,
"Context": "",
"Fingerprint": {
"SSNHash": "",
"CSNHash": "",
"CloudInstanceID": "",
"SystemUUID": "",
"MemoryIds": []
},
"Pool": "default",
"PoolAllocated": false,
"PoolStatus": "Free",
"WorkflowComplete": false
}
Deprovision the Machine¶
Deprovision/destroy operations are handled by the
universal-decommission
workflow. Similar to provisioning, simply set the machine Workflow to universal-decommission
.
METHOD="PATCH"
WF=universal-decommission
UUID=<MACHINE_ID>
curl -k -s -X $METHOD -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization: Bearer $RS_TOKEN" -d '[{"op": "replace", "path": "/Workflow", "value": "'$WF'"}]' https://66.165.231.2:8092/api/v3/machines/$UUID
Example Response¶
The following example response has had the "Params" redacted for brevity.
{
"Validated": true,
"Available": true,
"Errors": [],
"ReadOnly": false,
"Meta": {
"feature-flags": "change-stage-v2"
},
"Endpoint": "",
"Bundle": "",
"Partial": false,
"Name": "d00-00-00-51-01-00.pve-lab.local",
"Description": "",
"Uuid": "715819a9-3c09-445e-86cb-71afa3b0f4bf",
"CurrentJob": "c17af8e6-48ac-414a-a412-7ab99b1c47e9",
"Address": "192.168.1.5",
"Stage": "discover",
"BootEnv": "sledgehammer",
"Profiles": [
"id-ubu-18-v1"
],
"Params": {
"< ... snip ... >": "< ... redacted for brevity ... >"
},
"Tasks": [
"stage:discover",
"bootenv:sledgehammer",
"enforce-sledgehammer",
"set-machine-ip-in-sledgehammer",
"reserve-dhcp-address",
"ssh-access",
"stage:universal-decommission-start-callback",
"callback-task",
"stage:universal-decommission-pre-flexiflow",
"flexiflow-start",
"flexiflow-stop",
"stage:universal-decommission",
"universal-decommission",
"stage:raid-reset",
"raid-reset",
"stage:raid-enable-encryption",
"raid-tools-install",
"raid-enable-encryption",
"stage:shred",
"shred",
"stage:raid-reset",
"raid-reset",
"stage:universal-decommission-post-flexiflow",
"flexiflow-start",
"flexiflow-stop",
"stage:universal-decommission-classification",
"classify-stage-list-start",
"classify-stage-list-stop",
"stage:universal-decommission-post-validation",
"validation-start",
"validation-stop",
"stage:universal-decommission-complete-callback",
"callback-task",
"stage:universal-chain-workflow",
"universal-chain-workflow",
"stage:complete-nobootenv"
],
"CurrentTask": -1,
"RetryTaskAttempt": 0,
"TaskErrorStacks": [],
"Runnable": true,
"Secret": "tQXvI8wWV2am27fq",
"OS": "centos-8",
"HardwareAddrs": [
"00:00:00:51:01:00",
"3e:6a:da:eb:d8:76"
],
"Workflow": "universal-decommission",
"Arch": "amd64",
"Locked": false,
"Context": "",
"Fingerprint": {
"SSNHash": "",
"CSNHash": "",
"CloudInstanceID": "",
"SystemUUID": "",
"MemoryIds": []
},
"Pool": "default",
"PoolAllocated": false,
"PoolStatus": "Free",
"WorkflowComplete": false
}