Skip to main content
Previous sectionNext section

Scripting with ICM

This appendix describes how to issue a series of ICM commands from a script and how to identify and coordinate containers and services across a deployment.

ICM Exit Status

ICM sets the UNIX exit status after each command, providing a simple way to determine whether a given ICM command succeeded. The following examples examine the special variable $? after each command:

# icm inventory
Machine                 IP Address    DNS Name                        Region   Zone
-------                 ----------    --------                        ------   ----
Acme-DATA-TEST-0001 54.191.233.2  ec2-54-191-233-2.amazonaws.com  us-east1 b
Acme-DATA-TEST-0002 54.202.223.57 ec2-54-202-223-57.amazonaws.com us-east1 b
Acme-DATA-TEST-0003 54.202.223.58 ec2-54-202-223-58.amazonaws.com us-east1 b
# echo $?
0

# icm publish
Unrecognized goal: 'publish' (try "icm -help")
# echo $?
1

# icm ps -role QM
Unrecognized role 'QM'
# echo $?
1

# icm session
Error: Interactive commands cannot match more than one instance
# echo $?
1
Copy code to clipboard

ICM Logging

ICM logs its output to a file (default icm.log) and to the console. Whereas all output is sent to the log file, its console output can be captured and split into stdout and stderr. The following example completes without error:

# icm inventory > std.out 2> std.err
# cat std.out
Machine             IP Address    DNS Name                        Region   Zone
-------             ----------    --------                        ------   ----
Acme-DATA-TEST-0001 54.191.233.2  ec2-54-191-233-2.amazonaws.com  us-east1 b
Acme-DATA-TEST-0002 54.202.223.57 ec2-54-202-223-57.amazonaws.com us-east1 b
Acme-DATA-TEST-0003 54.202.223.58 ec2-54-202-223-58.amazonaws.com us-east1 b
# cat std.err
Copy code to clipboard

The following example contains error output:

# icm publish > std.out 2> std.err
# cat std.out
# cat std.err
Unrecognized goal: 'publish' (try "icm -help")
Copy code to clipboard

Remote Script Invocation

Commands can be used in combination to copy scripts to a host or container and remotely invoke them. The following example copies an exported InterSystems IRIS routine into an InterSystems IRIS cluster, then compiles and runs it:

# icm scp -localPath Routine1.xml -remotePath /tmp/
# icm session -command 'Do ##class(%SYSTEM.OBJ).Load("/tmp/Routine1.xml", "c-d")'
# icm session -command 'Do ^Routine1'
Copy code to clipboard

This example copies a shell script to the host, changes its permissions, and executes it:

# icm scp -localPath script1.sh -remotePath /tmp/
# icm ssh -command 'sudo chmod a+x /tmp/script1.sh'
# icm ssh -command '/tmp/script1.sh abc 123'
Copy code to clipboard

This example does the same thing, but within a custom or third-party container:

# icm cp -localPath script2.sh -remotePath /tmp/ -container gracie
# icm exec -command 'chmod a+x /tmp/script2.sh' -container gracie
# icm exec -command '/tmp/script2.sh abc 123' -container gracie
Copy code to clipboard

Using JSON Mode

Your script may need to gather information from ICM about the state of the cluster. Examples would be:

  • What is the IP address of the InterSystems IRIS data server?

  • What is the status of my custom/third-party container?

Parsing the human-readable output of ICM is difficult and prone to breakage. A more reliable solution is to have ICM generate its output in JSON format using the json option. The output is written to a file named response.json in the current working directory.

Normal Output

Most ICM commands do not result in any output upon success, in which case the exit value will be 0, no output will be written to stderr, and the JSON will be the empty array:

# icm exec -command "ls /" -json
# cat response.json
[]
Copy code to clipboard

ICM commands that produce useful output on success are detailed in the following. Note that the order of fields is not guaranteed.

icm provision

The format of the icm provision output is an object containing name-value pairs which describe the input (that is, configuration) and output (that is, state) files used during provisioning. The names, which match their corresponding command-line argument, are defaults, definitions, instances, and stateDir, as shown in this example:

# icm provision -json
...
Machine             IP Address    DNS Name                        Region   Zone
-------             ----------    --------                        ------   ----
Acme-DATA-TEST-0001 54.191.233.2  ec2-54-191-233-2.amazonaws.com  us-east1 b
Acme-DATA-TEST-0002 54.202.223.57 ec2-54-202-223-57.amazonaws.com us-east1 b
Acme-DATA-TEST-0003 54.202.223.58 ec2-54-202-223-58.amazonaws.com us-east1 b
To destroy: icm unprovision [-cleanUp] [-force]

# cat response.json
{
  "defaults" : "defaults.json",
  "definitions" : "definitions.json",
  "instances" : "instances.json",
  "stateDir" : "/Samples/AWS/state/"
}
Copy code to clipboard

icm inventory

The format of the icm inventory command is an array whose elements correspond to each provisioned instance; each element in turn contains a list of the name-value pairs MachineName, Role, IPAddress, and DNSName, as shown in the following:

# icm inventory -json
Machine             IP Address    DNS Name                        Region   Zone
-------             ----------    --------                        ------   ----
Acme-DATA-TEST-0001 54.191.233.2  ec2-54-191-233-2.amazonaws.com  us-east1 b
Acme-DATA-TEST-0002 54.202.223.57 ec2-54-202-223-57.amazonaws.com us-east1 b
Acme-DATA-TEST-0003 54.202.223.58 ec2-54-202-223-58.amazonaws.com us-east1 b

# cat response.json
[
  {
    “Provider”: “AWS”,
    "MachineName":"Acme-DATA-TEST-0001",
    "Role":"DATA",
    "IPAddress":"54.191.233.2",
    "DNSName":"54_191_233_2.amazonaws.com",
    “Region”: “us-east-1”,
    “Zone”: “us-east-1b”
  },
  {
    “Provider”: “AWS”,
    "MachineName ":"Acme-DATA-TEST-0002",
    "Role":"DATA",
    "IPAddress":"54.202.223.57",
    "DNSName":"54_202_223_57.amazonaws.com,
    “Region”: “us-east-1”,
    “Zone”: “us-east-1b”"
  },
  {
    “Provider”: “AWS”,
    "MachineName":"Acme-DATA-TEST-0003",
    "Role":"DATA",
    "IPAddress":"54202.223.58",
    "DNSName":"54_202_223_58.amazonaws.com,
    “Region”: “us-east-1”,
    “Zone”: “us-east-1b”"
  }
]
Copy code to clipboard

icm ps

In container mode, the output of the icm ps command is an array whose elements correspond to each container; each element in turn is a list of the name-value pairs MachineName, Role, IPAddress, DNSName, Container, DockerImage, Status, and MirrorStatus (if applicable):

# icm ps -container iris -json
Machine              IP Address     Container  Status  Health  Image
-------              ----------     ---------  ------  -----   -----
Acme-DATA-TEST-0001  54.191.233.2   iris       Up      healthy isc/iris:stable
Acme-DATA-TEST-0002  54.202.223.57  iris       Up      healthy isc/iris:stable
Acme-DATA-TEST-0003  54.202.223.58  iris       Up      healthy isc/iris:stable
# cat response.json
[
  {
    "MachineName":"Acme-DATA-TEST-0001",
    "Role":"DATA",
    "IPAddress":"54.191.233.2",
    "DNSName":"54_191_233_2.amazonaws.com"
    "Container":"iris",
    "DockerImage":"isc/iris:stable",
    "Status":"Up",
    "Health":"healthy"
  },
  {
    "MachineName ":"Acme-DATA-TEST-0002",
    "Role":"DATA",
    "IPAddress":"54.202.223.57",
    "DNSName":"54_202_223_57.amazonaws.com"
    "Container":"iris",
    "DockerImage":"isc/iris:stable",
    "Status":"Up"",
    "Health":"healthy"
  },
  {
    "MachineName ":"Acme-DATA-TEST-0003",
    "Role":"DATA",
    "IPAddress":"54.202.223.57",
    "DNSName":"54_202_223_57.amazonaws.com"
    "Container":"iris",
    "DockerImage":"isc/iris:stable",
    "Status":"Up",
    "Health":"healthy"
  },
]
Copy code to clipboard

The icm ps output fields in containerless mode are MachineName, Role, IPAddress, DNSName, ISCInstance (always IRIS in a container provided by InterSystems), Kit, Status, and MirrorStatus (if applicable):

# icm ps -json
Machine             IP Address    Instance Kit            Status   Health
-------             ----------    -------- ---            ------   ------
Acme-DATA-TEST-0001 54.67.2.117   IRIS     2017.3.0.392.0 running  ok
Acme-DATA-TEST-0002 54.153.96.236 IRIS     2017.3.0.392.0 running  ok
Acme-DATA-TEST-0002 54.153.90.66  IRIS     2017.3.0.392.0 running  ok

# cat response.json
[
  {
    "MachineName":"Acme-DATA-TEST-0001",
    "Role":"DATA",
    "IPAddress":"54.191.233.2",
    "DNSName":"54_191_233_2.amazonaws.com"
    "ISCInstance":"IRIS",
    "Kit":"2017.3.0.392.0",
    "Status":"running",
    "Health":"ok"
  },
  {
    "MachineName ":"Acme-DATA-TEST-0002",
    "Role":"DATA",
    "IPAddress":"54.202.223.57",
    "DNSName":"54_202_223_57.amazonaws.com"
    "ISCInstance":"IRIS",
    "Kit":"2017.3.0.392.0",
    "Status":"running"",
    "Health":"ok"
  },
  {
    "MachineName ":"Acme-DATA-TEST-0003",
    "Role":"DATA",
    "IPAddress":"54.202.223.57",
    "DNSName":"54_202_223_57.amazonaws.com"
    "ISCInstance":"IRIS",
    "Kit":"2017.3.0.392.0",
    "Status":"running"",
    "Health":"ok"
  }
]
Copy code to clipboard

Abnormal Output

When an error occurs, the format of the JSON output depends on whether the error occurred local to the ICM application or was from a target application on the host or instance.

Local Errors

When an ICM command results in an error, the JSON will contain an object and a name-value pair describing the error, as follows:

# icm ps -role QM -json
Unrecognized role 'QM'

# cat response.json
{
    "error": "Unrecognized role 'QM'"
}
Copy code to clipboard

Remote Errors

A remote error is considered to have occurred when one or more of the following is true:

  • Non-zero exit status

  • Output to stderr

When a remote error occurs, the JSON will be an array of objects containing name-value pairs; the name corresponds to that of the target machine, and the value is another object containing a list of name-value pairs including one or more of the following:

  • error: A description of the problem that occurred; most of the text of an exception

  • file: A file containing more detail about the problem

  • exitValue: The (non-zero) exit value of an underlying process

Here is an example:

# icm ssh -command "ls file.txt" -json
Executing command 'ls file.txt' on host Acme-DATA-TEST-0001...
ls: cannot access file.txt: No such file or directory
Error: See tmp/DATA-TEST/DATA-TEST-0001/ssh.err
Errors occurred during execution; aborting operation

# cat response.json
[
  {
    "Acme-DATA-TEST-0001": {
      "file": "tmp/DATA-TEST/DATA-TEST-0001/ssh.err"
    }
  }
]

# cat tmp/DATA-TEST/DATA-TEST-0001/ssh.err
ls: cannot access file.txt: No such file or directory
Copy code to clipboard