DS8000 automation from a jump server: DS CLI scripts and Python

DS8000 automation is how one storage engineer can look after ten systems without typing the same twenty commands every morning. You write small scripts that call the DS CLI, read what it prints, and then either report or make a planned change.

There’s one rule to get straight first: a DS8000 never runs your scripts. They run on your workstation or on a jump server (a small admin server in the data centre) and only reach the storage through the DS CLI talking to the HMC. That keeps the storage clean and puts your code somewhere you can test, log and schedule it.

Here you’ll build the whole kit, which is a script profile, a password file, DS CLI script mode, a read-only Python report and a guarded change script with a plan mode. After that you’ll break it in the two ways it usually breaks at work.

TimeYou needBy the end you can
75 to 90 minutesThe practice shell, Python 3, and Lesson 9 of this course (DS CLI basics)Run DS CLI in script and single-shot mode, keep passwords out of scripts, parse output in Python, and write change scripts that stop on errors

What it is, in one line

DS8000 automation means scripts on a jump server that call the DS CLI with a profile and a password file, read its delimited output, and report on the system or change it in a logged, checked way.

In Simple Words

The school canteen’s order book

Every morning the canteen manager counts the rice, oil and vegetables and writes out the order for the shop. It takes her an hour, and some days she forgets the oil. So she makes a printed checklist that a helper fills in every morning, circling anything that’s running low, and now she only reads the circled lines.

Big orders have a second rule. The helper writes the order first, the manager signs it, and only then does anyone ring the shop, and every order goes into a book with the date.

DS8000 automation is that checklist and that order book. The report script does the morning count, and the change script writes the order, waits for a signature (the change ticket) and records everything it did.

Engineer's View

What IBM gives you to automate with

The DS CLI has three modes (SG24-8559, section 10.1.11, p. 346-348). Single-shot runs one command from the OS shell and exits, which is handy for shell scripts and redirecting output, and Interactive gives you the dscli> prompt.

Script mode (-script file) runs a whole file of DS CLI commands with one login. The file can only hold DS CLI commands, comments and empty lines, and a single shell command in it will make the run fail.

The profile sets hmc1, hmc2, devid, username and pwfile, plus the output controls banner, header and format (default, xml, delim or stanza). The Redbook suggests switching the banner and header off in script mode to make parsing easier, but a header row is handy with Python’s csv module, so our profile leaves it on.

Passwords go into an encrypted file made by managepwfile, which by default is C:\Users\<user>\dscli\security.dat on Windows or $HOME/dscli/security.dat elsewhere. IBM’s advice is simple: don’t put the username and password in the profile, use the password file. The DS CLI also sets a return code when it exits, so a script can tell success from failure (p. 348).

The DS CLI connects to the HMC on TCP port 1751.

The HMC also offers RESTful API services (SG24-8559, section 6.2.3, p. 170), which first appeared on the DS8870 with Release 7.5. This lesson stays with the DS CLI, since every DS8000 you come across will have it.

How it works, step by step

1Put the scripts on a jump server

The jump server needs the DS CLI installed at the same level as your systems, Python 3, and network access to the HMCs on port 1751. Your scripts, profiles and logs live there in a folder only the storage team can read, and nothing gets installed on the DS8000 itself.

The figure follows one script call on its way from the scheduler to the storage and back.

DS8000 automation: scheduler, Python script, DS CLI, HMC port 1751, DS8000

2Give the script its own login

Create a user that exists only for automation. Reports only need the monitor group, so if someone steals the report password they still can’t delete anything, while change scripts need op_volume or op_storage and never a shared admin.

Store each password once with managepwfile and point the profile’s pwfile line at it.

Keep the password file and profiles out of Git, and commit the scripts plus a profile template with a placeholder path instead. Also remember that a new user’s first password is temporary and expires at first login, so log in once by hand before any job uses it.

3Ask for output a machine can read

Set format: delim, delim: , and header: on in the profile. Every list command will then print one header row and one comma-separated row per object, and Python’s csv.DictReader turns those into dictionaries keyed by column name. Reading columns by name keeps working when IBM adds a column in a new release, whereas reading them by position breaks.

4Treat return codes as the truth

When a DS CLI command fails it prints a message and exits with a non-zero return code, and in script mode the whole run ends with a code as well. Your script has to check it after every change command and stop at the first failure.

Carrying on after a failed mkfbvol is exactly how you end up with half-built systems.

Check that the login works before you read anything too, because a report that quietly prints nothing looks like “all fine”.

5Split read-only reports from change scripts

Reports can run every hour without approval, as long as they only use ls and show commands. Change scripts go through the change process: a plan mode that only prints and checks the commands, a second person who reads the plan, an approved ticket number passed to --apply, and a log file named after the ticket. You can see that flow in the figure below.

Guarded DS8000 change script: CSV, plan, review, apply with ticket, log

6Schedule it and act on the exit code

Use Task Scheduler on Windows and cron on Linux. Have the job start in the script’s folder (or use full paths), send its output to a log, and raise an alert on a non-zero exit code; our report exits 1 for warnings and 2 when it can’t log in. A job that fails at 2 a.m. and tells nobody is worse than having no job at all.

Don’t let a scheduler keep retrying a failed login. With chpass -fail 5, five bad logins lock the account, and a retry loop will get there in minutes.

Hands-on lab: a report script and a guarded change script

Before you run any command, double-check it

Read every command twice before you press Enter. Check the target: the system or account, the IDs, the names and the sizes. These commands are for learning and were tested in our lab, not on your system. At work, run the read-only list commands first, follow your change process, make sure a backup exists, and ask a second person to check anything that changes or deletes data. You run them at your own risk.

Your laptop plays the jump server and the practice shell plays the DS8000. Everything you build here is what you’d copy to a real jump server, with one change: the DSCLI line points at the real dscli command rather than ds8sim.py.

Practice shell note: the practice shell is our teaching tool, not IBM software, and real DS CLI output looks very much like this but isn’t identical. The return code values and the CMUN messages marked below belong to the practice shell, so on a real system go by the code and message your DS CLI prints.

1Build a small system to automate

Save this as setup-29.txt in C:\ds8k-lab. It creates a pool pair, two volumes and a host with two paths. Put in your own admin password.

# setup-29.txt: pool pair, two volumes, host saat-host1 with two paths
mkarray -raidtype 6 -arsite S1
mkarray -raidtype 6 -arsite S2
mkrank -array A0 -stgtype fb -keygrp 1
mkrank -array A1 -stgtype fb -keygrp 1
mkextpool -rankgrp 0 -stgtype fb -keygrp 1 pool_0
mkextpool -rankgrp 1 -stgtype fb -keygrp 1 pool_1
chrank -extpool P0 R0
chrank -extpool P1 R1
mkfbvol -extpool P0 -cap 100 -sam ese -name web_#h 2000
mkfbvol -extpool P1 -cap 100 -sam ese -name web_#h 2100
mkhost -type "Linux Server" saat-host1
mkhostport -host saat-host1 21000024FF2D0F86
mkhostport -host saat-host1 21000024FF2D0F87
chhost -action map -volume 2000,2100 saat-host1

Run this:

cd C:\ds8k-lab
python ds8sim.py -hmc1 10.0.0.1 -user admin -passwd <your password> simctl reset
python ds8sim.py -hmc1 10.0.0.1 -user admin -passwd <your password> -script setup-29.txt

You should see this (last lines):

CMUC00530I mkhost: The host saat-host1 is successfully created.
CMUC00542I mkhostport: The host port 21000024FF2D0F86 is successfully created.
CMUC00542I mkhostport: The host port 21000024FF2D0F87 is successfully created.
CMUC00531I chhost: The host saat-host1 is successfully modified.

✅ Checkpoint: the last line is CMUC00531I and no line starts with CMUN.

2Create the password file and the script profile

Store the password once, then write an 8-line profile called auto.profile. On macOS or Linux, use the full path, such as /home/you/dscli/security.dat.

Run this:

python ds8sim.py -hmc1 10.0.0.1 -user admin -passwd <your password> managepwfile -action add -name admin -pw <your password>
notepad auto.profile

Put these lines in auto.profile:

hmc1: 10.0.0.1
username: admin
pwfile: C:\Users\you\dscli\security.dat
devid: IBM.2107-78SV001
banner: off
header: on
format: delim
delim: ,

Then run a single-shot command and look at its return code:

python ds8sim.py -cfg auto.profile lsextpool
echo $LASTEXITCODE
python ds8sim.py -cfg auto.profile showextpool P9
echo $LASTEXITCODE

You should see this:

SaatVedha DS8000 practice shell — not IBM software. Real DS CLI output looks like this but is not identical.
Name,ID,stgtype,rankgrp,status,availstor (2^30B),%allocated,available,reserved,numvols
pool_0,P0,fb,0,below,99576,0,6372864,64,1
pool_1,P1,fb,1,below,99576,0,6372864,64,1
0
SaatVedha DS8000 practice shell — not IBM software. Real DS CLI output looks like this but is not identical.
CMUN02019E showextpool: P9: The extent pool does not exist.
6

There’s no password on the command line, you get one header row and comma-separated data, and the return code is 0 for success. In the practice shell the failed command returns 6. On macOS or Linux, use echo $? instead of $LASTEXITCODE.

✅ Checkpoint: lsextpool returns 0 and prints comma-separated rows; the bad pool returns a non-zero code.

3Run a DS CLI script file

Script mode logs in once and then runs every line. Save daily.txt:

# daily.txt: read-only checks
lsextpool
lshost
lshostport -login

Run this:

python ds8sim.py -cfg auto.profile -script daily.txt

You should see this:

Name,ID,stgtype,rankgrp,status,availstor (2^30B),%allocated,available,reserved,numvols
pool_0,P0,fb,0,below,99576,0,6372864,64,1
pool_1,P1,fb,1,below,99576,0,6372864,64,1
Name,Type,State,Cluster
saat-host1,Linux Server,Online,-
WWNN,WWPN,Host,I/O ports,ESSIOport
20000024FF2D0F86,21000024FF2D0F86,saat-host1,16 (all),I0032
20000024FF2D0F87,21000024FF2D0F87,saat-host1,16 (all),I0042
20000120FA13B914,10000090FA13B914,-,-,I0032
20000120FA13B915,10000090FA13B915,-,-,I0042

(The practice shell’s first line, “SaatVedha DS8000 practice shell…”, is left out here because the real DS CLI doesn’t print it.) Script mode works well for a fixed set of checks you keep in a file, but once you need decisions such as “warn if a pool is above 80%”, it’s time to move to Python.

✅ Checkpoint: three blocks, each with its own header row.

4Write the daily report in Python

Save this as report.py next to ds8sim.py. It runs on your laptop or jump server, not on the DS8000, and it only reads.

# Runs on your workstation or jump server, not on the DS8000.
# It talks to the DS8000 only through the DS CLI (auto.profile: banner off, header on, format delim).
# Read-only daily report: pool capacity and host paths. Exit code 1 = warnings, 2 = no data.
import csv, subprocess, sys
DSCLI = ["python", "ds8sim.py", "-cfg", "auto.profile"]   # at work: ["dscli", "-cfg", "auto.profile"]

def dscli(*args):
    """Run one DS CLI command and return its rows as dictionaries."""
    out = subprocess.run(DSCLI + list(args), capture_output=True, text=True).stdout
    lines = [l for l in out.splitlines() if l and not l.startswith("SaatVedha") and not l.startswith("CMUC")]
    return list(csv.DictReader(lines))

POOL_WARN = 80          # % allocated
MIN_PATHS = 2           # logged-in host ports per host

rc = subprocess.run(DSCLI + ["ver"], capture_output=True, text=True).returncode
if rc != 0:
    print(f"ERROR DS CLI login failed (rc={rc}): check user, password file and profile")
    sys.exit(2)
pools = dscli("lsextpool")
warn = 0
for p in pools:
    pct = int(p["%allocated"])
    state = "WARN" if pct >= POOL_WARN else "OK  "
    warn += state == "WARN"
    print(f"{state} pool {p['ID']} {p['Name']}: {pct}% allocated, {p['availstor (2^30B)']} GiB available")

logins = dscli("lshostport", "-login")
for h in dscli("lshost"):
    paths = sum(1 for l in logins if l["Host"] == h["Name"])
    state = "WARN" if paths < MIN_PATHS else "OK  "
    warn += state == "WARN"
    print(f"{state} host {h['Name']}: {paths} logged-in ports")
sys.exit(1 if warn else 0)

Run this:

python report.py
echo $LASTEXITCODE

You should see this:

OK   pool P0 pool_0: 0% allocated, 99576 GiB available
OK   pool P1 pool_1: 0% allocated, 99576 GiB available
OK   host saat-host1: 2 logged-in ports
0

Now cause some trouble with two practice shell controls: fill pool P1 to 85% and pull one of saat-host1’s cables. Run the report again, and then put everything back.

Run this:

python ds8sim.py -cfg auto.profile simctl poolfill P1 85
python ds8sim.py -cfg auto.profile simctl logout 21000024FF2D0F87
python report.py
echo $LASTEXITCODE
python ds8sim.py -cfg auto.profile simctl poolfill P1 0
python ds8sim.py -cfg auto.profile simctl login 21000024FF2D0F87 I0042

You should see this (report part):

OK   pool P0 pool_0: 0% allocated, 99576 GiB available
WARN pool P1 pool_1: 85% allocated, 14936 GiB available
WARN host saat-host1: 1 logged-in ports
1

✅ Checkpoint: two WARN lines and exit code 1, then all OK again after you undo the changes.

report.py prints WARN lines and exit code 1 in the practice shell lab
report.py on the laptop: two WARN lines and exit code 1 after the pool fill and the pulled cable.

5Create volumes with a guarded change script

The application team sends you a CSV with three new volumes. Save it as new-vols.csv and the change script as mkvols.py. By default it only plans, and it changes nothing unless you give it --apply and a ticket number.

id,pool,cap_gib,name
2001,P0,50,web_2001
2101,P1,50,web_2101
2002,P1,50,web_2002
# Runs on your workstation or jump server, not on the DS8000.
# It talks to the DS8000 only through the DS CLI (auto.profile: banner off, header on, format delim).
# Guarded change script: plan by default, --apply only with a change ticket. Stops on the first error.
import argparse, csv, subprocess, sys, datetime
DSCLI = ["python", "ds8sim.py", "-cfg", "auto.profile"]   # at work: ["dscli", "-cfg", "auto.profile"]

def dscli(*args):
    """Run one DS CLI command and return its rows as dictionaries."""
    out = subprocess.run(DSCLI + list(args), capture_output=True, text=True).stdout
    lines = [l for l in out.splitlines() if l and not l.startswith("SaatVedha") and not l.startswith("CMUC")]
    return list(csv.DictReader(lines))

ap = argparse.ArgumentParser()
ap.add_argument("csvfile")
ap.add_argument("--apply", action="store_true")
ap.add_argument("--ticket", default="")
a = ap.parse_args()

pools = {p["ID"]: p for p in dscli("lsextpool")}
existing = {v["ID"] for v in dscli("lsfbvol")}
plan, problems = [], []
for r in csv.DictReader(open(a.csvfile)):
    vid, pool = r["id"].upper(), r["pool"].upper()
    lss_even = int(vid[:2], 16) % 2 == 0
    if pool not in pools:
        problems.append(f"{vid}: pool {pool} does not exist")
    elif lss_even != (pools[pool]["rankgrp"] == "0"):
        problems.append(f"{vid}: LSS {vid[:2]} and pool {pool} (rank group {pools[pool]['rankgrp']}) are on different servers")
    if vid in existing:
        problems.append(f"{vid}: volume already exists")
    plan.append(["mkfbvol", "-extpool", pool, "-cap", r["cap_gib"], "-sam", "ese", "-name", r["name"], vid])

for cmd in plan:
    print("PLAN", " ".join(cmd))
for p in problems:
    print("STOP", p)
if problems or not a.apply:
    print("Plan only. Nothing was changed." if not problems else "Fix the problems above. Nothing was changed.")
    sys.exit(1 if problems else 0)
if not a.ticket:
    sys.exit("STOP --apply needs --ticket (the approved change number)")

log = open(f"change-{a.ticket}.log", "a")
for cmd in plan:
    res = subprocess.run(DSCLI + cmd, capture_output=True, text=True)
    msg = [l for l in res.stdout.splitlines() if l.startswith("CMU")]
    line = f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S} rc={res.returncode} {' '.join(cmd)} {' '.join(msg)}"
    print(line)
    log.write(line + "\n")
    if res.returncode != 0:
        sys.exit(f"STOP rc={res.returncode}: later commands were not run")
print(f"Done. Log: change-{a.ticket}.log")

Run this (plan only):

python mkvols.py new-vols.csv

You should see this:

PLAN mkfbvol -extpool P0 -cap 50 -sam ese -name web_2001 2001
PLAN mkfbvol -extpool P1 -cap 50 -sam ese -name web_2101 2101
PLAN mkfbvol -extpool P1 -cap 50 -sam ese -name web_2002 2002
STOP 2002: LSS 20 and pool P1 (rank group 1) are on different servers
Fix the problems above. Nothing was changed.

The plan has caught a real rule: a volume in an even LSS must come from a rank group 0 pool. Change the last CSV line to 2002,P0,50,web_2002, show the new plan to a colleague, and then apply it with the ticket number.

Run this:

python mkvols.py new-vols.csv --apply
python mkvols.py new-vols.csv --apply --ticket CHG4711

You should see this:

PLAN mkfbvol -extpool P0 -cap 50 -sam ese -name web_2001 2001
PLAN mkfbvol -extpool P1 -cap 50 -sam ese -name web_2101 2101
PLAN mkfbvol -extpool P0 -cap 50 -sam ese -name web_2002 2002
STOP --apply needs --ticket (the approved change number)
PLAN mkfbvol -extpool P0 -cap 50 -sam ese -name web_2001 2001
PLAN mkfbvol -extpool P1 -cap 50 -sam ese -name web_2101 2101
PLAN mkfbvol -extpool P0 -cap 50 -sam ese -name web_2002 2002
2026-10-24 20:06:37 rc=0 mkfbvol -extpool P0 -cap 50 -sam ese -name web_2001 2001 CMUC00025I mkfbvol: FB volume 2001 successfully created.
2026-10-24 20:06:37 rc=0 mkfbvol -extpool P1 -cap 50 -sam ese -name web_2101 2101 CMUC00025I mkfbvol: FB volume 2101 successfully created.
2026-10-24 20:06:37 rc=0 mkfbvol -extpool P0 -cap 50 -sam ese -name web_2002 2002 CMUC00025I mkfbvol: FB volume 2002 successfully created.
Done. Log: change-CHG4711.log

Your timestamps will be different. If you run the plan once more, every line says “volume already exists”, so running the script twice by mistake doesn’t change anything.

✅ Checkpoint: change-CHG4711.log holds three rc=0 lines and lsfbvol shows 2001, 2002 and 2101.

6Schedule the report

On a jump server you’d run the report every morning, with something like this (you don’t run it in the lab):

# Windows Task Scheduler
schtasks /Create /TN "DS8000 daily report" /SC DAILY /ST 06:30 /TR "cmd /c cd /d C:\ds8k-lab && python report.py > report.log"
# Linux cron (crontab -e)
30 6 * * * cd /home/you/ds8k-lab && python3 report.py > report.log 2>&1 || mail -s "DS8000 report" storage-team < report.log

Both versions start in the script folder, keep the output and raise an alert when the exit code isn’t 0, and neither has a retry loop.

✅ Checkpoint: you can explain what happens on exit codes 0, 1 and 2.

Break It, Then Fix It

Automation usually breaks for dull reasons, like someone editing a profile or a password changing. You’ll try both.

1Break: change the delimiter

A colleague wants the output to open neatly in a spreadsheet, so they change the last profile line to a semicolon.

Run this:

(Get-Content auto.profile) -replace 'delim: ,','delim: ;' | Set-Content auto.profile
python ds8sim.py -cfg auto.profile lsextpool
python report.py

You should see this:

SaatVedha DS8000 practice shell — not IBM software. Real DS CLI output looks like this but is not identical.
Name;ID;stgtype;rankgrp;status;availstor (2^30B);%allocated;available;reserved;numvols
pool_0;P0;fb;0;below;99576;0;6372864;64;3
pool_1;P1;fb;1;below;99576;0;6372864;64;2
Traceback (most recent call last):
  File "C:\ds8k-lab\report.py", line 23, in <module>
    pct = int(p["%allocated"])
              ~^^^^^^^^^^^^^^
KeyError: '%allocated'
report.py KeyError after the profile delimiter was changed to a semicolon
A semicolon in the profile breaks the column names the script expects.

Why: Python’s csv reader splits on commas by default. With semicolons, each line becomes one big column called “Name;ID;stgtype;…”, so there’s no column named %allocated. The DS CLI did nothing wrong here; what broke was the agreement between the profile and the script.

Fix it:

(Get-Content auto.profile) -replace 'delim: ;','delim: ,' | Set-Content auto.profile
python report.py

You should see this:

OK   pool P0 pool_0: 0% allocated, 99576 GiB available
OK   pool P1 pool_1: 0% allocated, 99576 GiB available
OK   host saat-host1: 2 logged-in ports

✅ Checkpoint: the report works again. Keep script profiles apart from personal ones, and let only the script owner change them.

2Break: run the report as a new monitor user

Security says reports mustn’t run as admin, which is fair enough. So you create a monitor user, store its password and switch the profile over to it.

Run this:

python ds8sim.py -cfg auto.profile mkuser -pw Report2026 -group monitor rptuser
python ds8sim.py -cfg auto.profile managepwfile -action add -name rptuser -pw Report2026
(Get-Content auto.profile) -replace 'username: admin','username: rptuser' | Set-Content auto.profile
python report.py
echo $LASTEXITCODE
python ds8sim.py -cfg auto.profile lsextpool

You should see this (after the first two commands):

ERROR DS CLI login failed (rc=5): check user, password file and profile
2
SaatVedha DS8000 practice shell — not IBM software. Real DS CLI output looks like this but is not identical.
CMUN00247W Your password has expired. You must change it now.
Enter a new password: 
Re-enter the new password: 
CMUN00246E The password for user rptuser has expired and must be changed. No new password was entered.

Why: a password set with mkuser is temporary and expires at the first login (the Redbook says the same about admin/admin). A script can’t type in a new password, so the login fails. The CMUN00247W and CMUN00246E texts are practice shell messages, and a real DS CLI refuses in its own words.

Notice that the report behaved properly: it said “login failed” and exited 2 instead of printing an empty “all OK”.

Fix it: log in once by hand, then update the password file

python ds8sim.py
Enter the primary management console IP address: 10.0.0.1
Enter the secondary management console IP address:
Enter your username: rptuser
Enter your password: (type Report2026; it does not show)
(enter a new password twice)
dscli> managepwfile -action change -name rptuser -pw <the new password>
dscli> quit
python report.py

You should see this:

CMUN00247W Your password has expired. You must change it now.
CMUC00249I Your password was changed successfully.
CMUC00206I managepwfile: Record 10.0.0.1/rptuser successfully changed in password file C:\Users\you\dscli\security.dat.
OK   pool P0 pool_0: 0% allocated, 99576 GiB available
OK   pool P1 pool_1: 0% allocated, 99576 GiB available
OK   host saat-host1: 2 logged-in ports

✅ Checkpoint: the report runs as rptuser. Try python ds8sim.py -cfg auto.profile mkfbvol -extpool P0 -cap 10 -sam ese 2003: the monitor user is refused (CMUN00018E, a practice shell message), which is exactly what you want from a report account.

This happened at work

The sale-night report that locked out the on-call engineer

An online retailer ran a capacity and path report against its two DS8000s every 15 minutes during big sale events. It ran as the shared admin user, from a password file on the jump server, and if a run failed the scheduler retried every minute “to be safe”.

Two days before a festival sale, a security review rotated the admin password and nobody updated the password file. At 1:10 a.m. on sale night the first run failed, the retries started, and the fifth failed login locked the admin account.

The script printed nothing useful, and nobody got an alert.

Then at 1:40 a.m. a pool behind the order database crossed its threshold. The on-call engineer tried to log in as admin to add capacity and found the account locked, the second admin was asleep, and it took 50 minutes to get the account reset and a rank added.

For part of that time, order writes slowed down badly.

Root cause: one shared admin login used by both people and scripts, a retry loop, and no alert when a run failed. The team created a monitor user for reports and an op_volume user for change scripts, took out the retries, made the report exit non-zero on a login failure and page the on-call engineer, and added “update the password file” to the rotation checklist.

Common mistakes and troubleshooting

SymptomLikely causeWhat to check / fix
KeyError on a column nameProfile delimiter or format changed, or header offProfile must say format: delim, delim: , and header: on.
Report prints nothing, or only OK lines, when the box is unreachableLogin or connection failure not checkedCheck the return code of a cheap command (ver) first and exit non-zero.
Account locked overnightScheduler retries with an old passwordNo retries on login failure; update the password file in the rotation checklist.
Script works by hand, fails in the schedulerDifferent start folder, user or PATHcd to the script folder or use full paths; run the job as the same service user.
Half the volumes created, the rest missingChange script ignored a failed commandStop at the first non-zero return code and log every command.
Password found in Git or a profileSecrets written into scriptsUse managepwfile and pwfile; commit a template without real paths.
Script file fails with a shell command in itScript mode accepts DS CLI commands onlyKeep shell logic in Python or PowerShell; keep -script files pure DS CLI.

A good test for any script: if you can’t explain its exit codes in one sentence, it isn’t ready for a scheduler yet.

Interview questions

Click a question to open it. They’re worded the way an interviewer would ask them, and under each one you’ll find why they ask it and one way you could answer in your own words.

Fresher level

1. Where do DS8000 automation scripts run, and how do they talk to the storage?

Why they ask this: They want to hear that you understand the management path and don’t picture code running on the array.

How you might answer: They run on a workstation or jump server that has the DS CLI installed. The script calls dscli with a profile and a password file, the DS CLI talks to the HMC on port 1751, and the HMC talks to the storage. Nothing gets installed on the DS8000 itself.

2. How do you avoid putting a DS8000 password in a script?

Why they ask this: It’s a basic security question, and plenty of real incidents begin with a password sitting in a script or a Git repository.

How you might answer: I’d create the password file with managepwfile, which stores it encrypted in security.dat under my user folder, and point the profile’s pwfile line at it, so the script only uses -cfg with the profile. The profile and password file stay out of Git, and I commit a template with a placeholder.

2–5 years experience

3. Walk me through a safe script that creates 50 volumes from a CSV.

Why they ask this: They want to see change discipline, and not just a loop around mkfbvol.

How you might answer: I’d start with a plan mode that reads the CSV and the current state with lsextpool and lsfbvol, checks every line (the pool exists, the ID is free, an even LSS goes to a rank group 0 pool) and prints the commands without changing anything.

Someone else reviews that plan. Then apply mode needs the change ticket, runs one mkfbvol at a time, logs each command with its return code, and stops at the first failure so we never end up half-built.

4. Your nightly report suddenly shows KeyError. How do you troubleshoot?

Why they ask this: It tests whether you work calmly through the layers, DS CLI first and then the parser.

How you might answer: I’d run the same DS CLI command by hand with the same profile and look at the raw output. Usually the format, delimiter or header setting has changed, or the command printed an error instead of rows. Then I’d fix the profile, or add a check for the expected columns so next time the script fails with a clear message.

Senior level

5. Design DS8000 automation for a team that manages 12 systems across three generations.

Why they ask this: Senior engineers have to balance safety, scale and maintainability, so they’re looking for structure and controls.

How you might answer: I’d set up a pair of jump servers with a DS CLI level that supports all our releases, one profile per system, and named service users, monitor for reports and op_volume for changes. Every script starts with ver -l and refuses releases it doesn’t support.

Reports run on a schedule and alert on exit codes. Change scripts have plan and apply modes, need a ticket, log to a central place, and get code-reviewed in Git with no secrets in them. If a tool needed it, I’d also look at the RESTful API on the HMC.

6. A scheduled job locked the admin account during an outage. What do you change?

Why they ask this: They want root-cause thinking about accounts and retries, not just “reset it”.

How you might answer: In the short term I’d get the account reset and stop the job. After that I’d separate people from scripts, with a monitor user for reports and no shared admin. I’d remove automatic retries on login failures, make the job exit non-zero and raise an alert, and add the password file to the password rotation checklist.

Quick quiz

Q1. Where does a DS8000 automation script run?

  1. On the DS8000 CPCs
  2. On the HMC
  3. On a workstation or jump server that runs the DS CLI
  4. Inside the host adapter
Show answer

C — The DS8000 never runs your code. Scripts call the DS CLI from a workstation or jump server.

Q2. Which profile settings make DS CLI output easy to parse with csv.DictReader?

  1. format: xml
  2. format: delim, delim: , and header: on
  3. banner: on
  4. paging: on
Show answer

B — Delimited output with a header row gives one dictionary per object, keyed by column name.

Q3. What can a DS CLI script file (-script) contain?

  1. Any shell commands
  2. DS CLI commands, comments and empty lines only
  3. Python code
  4. Only one command
Show answer

B — IBM says a script file may hold only DS CLI commands; a shell command makes the run fail.

Q4. Why does a new user’s login fail in a scheduled job?

  1. Monitor users cannot log in
  2. The first password is temporary and must be changed at first login
  3. The password file only stores admin
  4. The profile needs -user
Show answer

B — Passwords set with mkuser are temporary. Log in once by hand, set a new one and update the password file.

Q5. What should a change script do when one mkfbvol returns a non-zero code?

  1. Retry until it works
  2. Skip it and continue
  3. Stop and log, so later commands are not run
  4. Delete everything it created
Show answer

C — Stopping at the first failure keeps the system in a known state you can fix by hand.

Quick recap

  • Scripts run on a workstation or jump server and reach the DS8000 only through the DS CLI and the HMC.
  • Use a script profile with format: delim, delim: , and header: on, plus a password file from managepwfile.
  • Single-shot mode suits one command; -script runs a pure DS CLI file with one login.
  • Check return codes: stop on the first failure and alert on non-zero exit codes.
  • Separate read-only reports (monitor user) from change scripts (op_volume, plan mode, ticket, log).
  • Never retry failed logins in a loop; a password rotation must update the password file.

Next lesson

Lesson 30: Troubleshooting blueprint: what, where, how to check → When a report turns red, this is how you find the cause, layer by layer.

Further reading: DS8000 automation

Want to go deeper into DS8000 automation? IBM describes the DS CLI modes, profile settings, password file and return codes in IBM Storage DS8A00 Architecture and Implementation Guide, SG24-8559 (section 10.1, pages 342 to 348), and the RESTful API in section 6.2.3 (page 170). For the profile and password file from first principles, see our lesson DS CLI basics: profile, modes, help, return codes.

You can also see the full plan for this course on the IBM DS8000 Implementation course page.

Scroll to Top

This website uses cookies

We use a few cookies to keep this site working. If you allow it, we also use Google Analytics to count visits so we can improve our lessons. You can accept all, deny the optional ones, or choose what to allow.