Python example for TS7785 REST API authentication and clusters endpoint
queries.
The following sample program written in Python can do the following:
Access the token with the provided MI username and password.
Interact with necessary endpoints to configure a virtual library.
Sample Python script
To use this script:
RESTDriver.py provides a helper class and functions to
interact with RESTful API endpoints.
Update server variable in an instance of RESTDriver class.
Update username and password to match the valid Management Interface user ID
and password on the target TS7785.
In order to try other endpoints, refer to create_ and list_ functions how to interact with
endpoints.
Figure 1. Sample 1-VTL Configuration
#!/usr/bin/python3
'''
IBM Doc :
TS7785 -> Managing -> Management interfaces: GUI and REST API -> IBM TS7785 REST API
Demonstrates:
--- VTL Lifecycle (from sample1_VTL.py) ---
- List virtual libraries GET /virtualLibraries
- Create a virtual library POST /virtualLibraries
--- Host / ACL / Mapping ---
- List cluster ports GET /clusterPorts
- List hosts GET /hosts
- List / create host groups GET /hostGroups POST /hostGroups
- List / create virtual ports GET /virtualPorts POST /virtualPorts
- List / create host ports GET /hostPorts POST /hostPorts
- Create an ACL entry POST /accessControlLists
- Map a host group ↔ host port POST /hostGroups/hostPortMappings
- Map a host group ↔ virtual port POST /hostGroups/virtualPortMappings
Prerequisite:
- A valid virtual library exists in the system, OR
- Run the VTL creation section in main() to create one automatically.
'''
from RESTDriver import RESTDriver
from typing import Optional
import sys
import time
# ---------------------------------------------------------------------------
# Sample payloads — replace placeholder UUIDs before enabling POST calls
#
# Where to get UUID values:
# - VIRTUAL_LIBRARY_UUID : list_virtual_libraries() or create_virtual_library()
# - clusterPortUUID : list_cluster_ports()
# - HOST_GROUP_UUID : create_host_group()
# - hostPortUUID : create_host_port()
# - virtualPortUUID : create_virtual_port()
# - accessControlListUUID : create_acl()
#
# - SAMPLE_HOST_PORT_PAYLOAD['wwpn'] : get from your host :)
# ---------------------------------------------------------------------------
# Used only if you skip Section 2 (VTL creation) and supply a pre-existing VTL.
# If you run Section 2, vtl_uuid is set at runtime and the payloads are updated
# in Section 4 — this constant is not used for those fields.
VIRTUAL_LIBRARY_UUID = "00000000-0000-0000-0000-000000000001" # REPLACE if skipping Section 2
# Example for a 2-way Grid: cluster 1 (3 drives) and cluster 3 (5 drives)
SAMPLE_VTL_CREATE_PAYLOAD = {
"name": "testvtl",
"description": "test vtl",
"storageSlots": 100,
"ioSlots": 100,
"virtualDrives": [
{"cluster": 0, "count": 3},
{"cluster": 1, "count": 5},
],
}
SAMPLE_VTL_UPDATE_PAYLOAD = {
"description": "description updated",
"storageSlots": 200,
}
SAMPLE_HOST_GROUP_PAYLOAD = {"name": "sample-host-group-01"}
# Virtual port on cluster 0 — clusterPortUUID must belong to cluster 0
SAMPLE_VIRTUAL_PORT_PAYLOAD_CLUSTER0 = {
"name": "sample-virtual-port-c0-01",
"owningVirtualLibraryUUID": "REPLACE", # updated at runtime in Section 4
"clusterPortUUID": "00000000-0000-0000-0000-000000000009", # REPLACE: cluster 0 port
}
# Virtual port on cluster 1 — clusterPortUUID must belong to cluster 1
SAMPLE_VIRTUAL_PORT_PAYLOAD_CLUSTER1 = {
"name": "sample-virtual-port-c1-01",
"owningVirtualLibraryUUID": "REPLACE", # updated at runtime in Section 4
"clusterPortUUID": "00000000-0000-0000-0000-000000000002", # REPLACE: cluster 1 port
}
SAMPLE_HOST_PORT_PAYLOAD = {
"hostName": "sample-host-01",
"wwpn": "00:00:00:00:00:00:00:FF", # REPLACE
}
SAMPLE_ACL_PAYLOAD_CLUSTER0 = {
"name": "sample-acl-c0-01",
"description": "Sample ACL cluster 0",
"virtualDriveStartIndex": 0, # REPLACE: 0-based index of the first virtual drive
# to expose via this ACL (0 = first drive in the VTL)
"virtualDriveCount": 3, # REPLACE: number of consecutive drives to expose,
# starting from virtualDriveStartIndex
"clusterID": 0, # cluster 0
"virtualLibraryUUID": "REPLACE", # updated at runtime in Section 4
}
SAMPLE_ACL_PAYLOAD_CLUSTER1 = {
"name": "sample-acl-c1-01",
"description": "Sample ACL cluster 1",
"virtualDriveStartIndex": 0, # REPLACE: 0-based index of the first virtual drive
# to expose via this ACL (0 = first drive in the VTL)
"virtualDriveCount": 5, # REPLACE: number of consecutive drives to expose,
# starting from virtualDriveStartIndex
"clusterID": 1, # cluster 1
"virtualLibraryUUID": "REPLACE", # updated at runtime in Section 4
}
SAMPLE_HOST_PORT_MAPPING = {
"hostGroupUUID": "00000000-0000-0000-0000-000000000003",
"hostPortUUID": "00000000-0000-0000-0000-000000000004",
}
# POST /hostGroups/virtualPortMappings accepts one mapping dict per call.
SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER0 = {
"hostGroupUUID": "00000000-0000-0000-0000-000000000003",
"virtualPortUUID": "00000000-0000-0000-0000-000000000005", # filled at runtime (cluster 0)
"accessControlListUUID": "00000000-0000-0000-0000-000000000006", # filled at runtime (cluster 0)
}
SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER1 = {
"hostGroupUUID": "00000000-0000-0000-0000-000000000003",
"virtualPortUUID": "00000000-0000-0000-0000-000000000007", # filled at runtime (cluster 1)
"accessControlListUUID": "00000000-0000-0000-0000-000000000008", # filled at runtime (cluster 1)
}
# ---------------------------------------------------------------------------
# VTL Lifecycle functions
# ---------------------------------------------------------------------------
def list_virtual_libraries(client: RESTDriver) -> list:
"""GET /virtualLibraries — return list of all configured VTLs."""
rc, response = client.get_method("virtualLibraries")
if rc != 0:
print(" Failed to GET virtualLibraries")
return []
vtls = response.json()
vtls = vtls if isinstance(vtls, list) else []
print(f" Virtual Libraries ({len(vtls)}):")
for vtl in vtls:
print(f" uuid={vtl.get('uuid')} name={vtl.get('name')}")
return vtls
def create_virtual_library(client: RESTDriver, payload: dict) -> str:
"""POST /virtualLibraries — create a VTL and return its UUID.
After a successful POST the list is re-queried to obtain the UUID
assigned to the newly created VTL (identified by name).
"""
rc, _ = client.post_method("virtualLibraries", payload)
if rc != 0:
print(" Failed to POST virtualLibraries")
sys.exit(1)
name = payload.get("name", "")
# Re-query to get the assigned UUID
rc, response = client.get_method("virtualLibraries")
if rc != 0:
print(" Failed to GET virtualLibraries after POST")
sys.exit(1)
for vtl in response.json():
if vtl.get("name") == name:
uuid = vtl["uuid"]
desc = vtl.get("description", "")
slots = vtl.get("storageSlots", "")
print(f" Created VTL: uuid={uuid} name={name} "
f"desc={desc} storageSlots={slots}")
return uuid
print(f" VTL '{name}' not found after POST")
sys.exit(1)
def update_virtual_library(client: RESTDriver, uuid: str, payload: dict) -> None:
"""PATCH /virtualLibraries/{uuid} — update attributes of an existing VTL."""
endpoint = f"virtualLibraries/{uuid}"
rc, _ = client.patch_method(endpoint, payload)
if rc != 0:
print(f" Failed to PATCH virtualLibraries/{uuid}")
sys.exit(1)
# Verify the update by re-reading the resource
rc, response = client.get_method(endpoint)
if rc != 0:
print(f" Failed to GET virtualLibraries/{uuid} after PATCH")
sys.exit(1)
vtl = response.json()
print(f" Updated VTL: uuid={vtl.get('uuid')} name={vtl.get('name')} "
f"desc={vtl.get('description')} storageSlots={vtl.get('storageSlots')}")
# ---------------------------------------------------------------------------
# Host / ACL / Mapping functions
# ---------------------------------------------------------------------------
def list_cluster_ports(client: RESTDriver) -> list:
"""GET /clusterPorts."""
rc, response = client.get_method("clusterPorts")
if rc != 0:
print(" Failed to GET clusterPorts")
return []
ports = response.json()
ports = ports if isinstance(ports, list) else []
print(f" Cluster Ports ({len(ports)}):")
for p in ports:
print(f" {p.get('uuid', '')} cluster={p.get('clusterID')} bay={p.get('bay', '')}"
f" slot={p.get('slot', '')} port={p.get('port', '')} wwpn={p.get('wwpn', '')}")
return ports
def list_hosts(client: RESTDriver) -> list:
"""GET /hosts — discovered host initiators."""
rc, response = client.get_method("hosts")
if rc != 0:
print(" Failed to GET hosts")
return []
hosts = response.json()
hosts = hosts if isinstance(hosts, list) else []
print(f" Hosts ({len(hosts)}):")
for h in hosts:
print(f" name={h.get('name')}")
return hosts
def list_host_groups(client: RESTDriver) -> list:
"""GET /hostGroups."""
rc, response = client.get_method("hostGroups")
if rc != 0:
print(" Failed to GET hostGroups")
return []
groups = response.json()
groups = groups if isinstance(groups, list) else []
print(f" Host Groups ({len(groups)}):")
for g in groups:
print(f" {g.get('uuid')} name={g.get('name')}")
return groups
def list_virtual_ports(client: RESTDriver) -> list:
"""GET /virtualPorts."""
rc, response = client.get_method("virtualPorts")
if rc != 0:
print(" Failed to GET virtualPorts")
return []
ports = response.json()
ports = ports if isinstance(ports, list) else []
print(f" Virtual Ports ({len(ports)}):")
for p in ports:
print(f" {p.get('uuid')} name={p.get('name', '')}")
return ports
def list_host_ports(client: RESTDriver) -> list:
"""GET /hostPorts."""
rc, response = client.get_method("hostPorts")
if rc != 0:
print(" Failed to GET hostPorts")
return []
ports = response.json()
ports = ports if isinstance(ports, list) else []
print(f" Host Ports ({len(ports)}):")
for p in ports:
print(f" {p.get('uuid')} name={p.get('hostName')}")
return ports
def create_virtual_port(client: RESTDriver, payload: dict) -> str:
"""POST /virtualPorts — return new UUID (polled until available)."""
rc, _ = client.post_method("virtualPorts", payload)
if rc != 0:
print(" Failed to POST virtualPorts")
sys.exit(1)
name = payload.get("name", "")
uuid = client.wait_for_uuid("virtualPorts", "name", name)
if not uuid:
sys.exit(1)
print(f" Created virtual port: uuid={uuid} name={name}")
return uuid
def create_host_group(client: RESTDriver, payload: dict) -> str:
"""POST /hostGroups — return new UUID (polled until available)."""
rc, _ = client.post_method("hostGroups", payload)
if rc != 0:
print(" Failed to POST hostGroups")
sys.exit(1)
name = payload.get("name", "")
uuid = client.wait_for_uuid("hostGroups", "name", name)
if not uuid:
sys.exit(1)
print(f" Created host group: uuid={uuid} name={name}")
return uuid
def create_host_port(client: RESTDriver, payload: dict) -> str:
"""POST /hostPorts — return new UUID (polled until available)."""
rc, _ = client.post_method("hostPorts", payload)
if rc != 0:
print(" Failed to POST hostPorts")
sys.exit(1)
name = payload.get("hostName", "")
uuid = client.wait_for_uuid("hostPorts", "hostName", name)
if not uuid:
sys.exit(1)
print(f" Created host port: uuid={uuid} name={name}")
return uuid
def create_acl(client: RESTDriver, payload: dict) -> str:
"""POST /accessControlLists — return new UUID (polled until available)."""
rc, _ = client.post_method("accessControlLists", payload)
if rc != 0:
print(" Failed to POST accessControlLists")
sys.exit(1)
name = payload.get("name", "")
uuid = client.wait_for_uuid("accessControlLists", "name", name)
if not uuid:
sys.exit(1)
print(f" Created ACL: uuid={uuid} name={name}")
return uuid
def map_host_port(client: RESTDriver, payload: dict) -> None:
"""POST /hostGroups/hostPortMappings."""
rc, _ = client.post_method("hostGroups/hostPortMappings", payload)
if rc != 0:
print(" Failed to POST hostGroups/hostPortMappings")
return
print(f" Mapped host port {payload.get('hostPortUUID')} "
f"to group {payload.get('hostGroupUUID')}")
def map_virtual_port(client: RESTDriver, payload: dict) -> None:
"""POST /hostGroups/virtualPortMappings — one mapping dict per call."""
rc, _ = client.post_method("hostGroups/virtualPortMappings", payload)
if rc != 0:
print(" Failed to POST hostGroups/virtualPortMappings")
return
print(f" Mapped virtual port {payload.get('virtualPortUUID')} "
f"to group {payload.get('hostGroupUUID')}")
def enable_virtual_library(client: RESTDriver, uuid: str,
target_clusters: Optional[list] = None) -> None:
"""POST /virtualLibraries/{uuid}/enable — bring a VTL online.
Args:
client: RESTDriver instance.
uuid: UUID of the virtual library to enable.
target_clusters: List of integer cluster IDs to enable, e.g. [1, 3].
Pass None (default) to enable on all clusters.
"""
endpoint = f"virtualLibraries/{uuid}/enable"
payload = {"targetClusters": target_clusters}
rc, _ = client.post_method(endpoint, payload)
if rc != 0:
print(f" Failed to POST {endpoint}")
sys.exit(1)
clusters = target_clusters if target_clusters is not None else "all"
print(f" Enabled VTL uuid={uuid} targetClusters={clusters}")
def wait_for_vtl_online(client: RESTDriver, uuid: str,
interval: int = 30, timeout: int = 300) -> bool:
"""Poll GET /virtualLibraries/{uuid}/state until the VTL is online.
Args:
client: RESTDriver instance.
uuid: UUID of the virtual library to monitor.
interval: Seconds between polls (default 30).
timeout: Total seconds to wait before giving up (default 300 = 5 min).
Returns:
True if the VTL reached the 'online' state within the timeout,
False otherwise (a warning is printed for the user to check manually).
"""
endpoint = f"virtualLibraries/{uuid}/state"
attempts = max(1, timeout // interval)
print(f" Monitoring VTL state (every {interval}s, up to {timeout}s) ...")
for attempt in range(1, attempts + 1):
time.sleep(interval)
rc, response = client.get_method(endpoint)
if rc != 0:
print(f" [{attempt}/{attempts}] Failed to GET {endpoint} — skipping poll")
continue
state = response.json()
# The state endpoint may return a plain string or a dict with a 'state' key.
if isinstance(state, dict):
state = state.get("state", "")
state = str(state).lower()
print(f" [{attempt}/{attempts}] VTL uuid={uuid} state={state}")
if state == "online":
print(f" VTL uuid={uuid} is online.")
return True
print(
f"\n WARNING: VTL uuid={uuid} did not reach 'online' state within {timeout}s.\n"
f" Please check the VTL state manually via GET /{endpoint}"
)
return False
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
if __name__ == '__main__':
rest = RESTDriver(server="xxx.xxx.xxx.xxx",
username="xxx",
password="xxx")
print(f"Connecting to {rest.base_url}\n")
# -----------------------------------------------------------------------
# Section 1 — VTL read operations
# -----------------------------------------------------------------------
print("--- VTL read operations ---")
vtls = list_virtual_libraries(rest)
# Comment out the exit() below once you have confirmed the VTL list and
# are ready to run create / update / delete operations.
print("\nDry run (VTL list) completed. Remove or comment the exit() to continue."); exit(0)
# -----------------------------------------------------------------------
# Section 2 — VTL lifecycle (create → update → delete)
# Optional: skip this section if a valid VTL already exists and set
# VIRTUAL_LIBRARY_UUID manually at the top of the script.
# -----------------------------------------------------------------------
print("\n--- VTL lifecycle operations ---")
# Create a new VTL
vtl_uuid = create_virtual_library(rest, SAMPLE_VTL_CREATE_PAYLOAD)
# -----------------------------------------------------------------------
# Section 3 — Mapping read operations
# -----------------------------------------------------------------------
print("\n--- Mapping read operations ---")
list_cluster_ports(rest)
list_virtual_ports(rest)
list_host_groups(rest)
list_host_ports(rest)
list_hosts(rest)
# Comment out the line below, once you have enough data to continue.
print("\nDry run completed. Make sure to set valid data for those marked REPLACE in the script."); exit(0)
# -----------------------------------------------------------------------
# Section 4 — Mapping POST operations
# -----------------------------------------------------------------------
print("\n--- POST operations ---")
# Propagate the VTL UUID into the payloads that need it.
# - If Section 2 ran: use vtl_uuid (the UUID returned by create_virtual_library)
# - If Section 2 was skipped: assign VIRTUAL_LIBRARY_UUID to vtl_uuid manually, e.g.:
# vtl_uuid = VIRTUAL_LIBRARY_UUID
SAMPLE_VIRTUAL_PORT_PAYLOAD_CLUSTER0['owningVirtualLibraryUUID'] = vtl_uuid
SAMPLE_VIRTUAL_PORT_PAYLOAD_CLUSTER1['owningVirtualLibraryUUID'] = vtl_uuid
SAMPLE_ACL_PAYLOAD_CLUSTER0['virtualLibraryUUID'] = vtl_uuid
SAMPLE_ACL_PAYLOAD_CLUSTER1['virtualLibraryUUID'] = vtl_uuid
# 1a. Create virtual port for cluster 0
new_vp_c0_uuid = create_virtual_port(rest, SAMPLE_VIRTUAL_PORT_PAYLOAD_CLUSTER0)
SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER0['virtualPortUUID'] = new_vp_c0_uuid
# 1b. Create virtual port for cluster 1
new_vp_c1_uuid = create_virtual_port(rest, SAMPLE_VIRTUAL_PORT_PAYLOAD_CLUSTER1)
SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER1['virtualPortUUID'] = new_vp_c1_uuid
# 2. Create a host group (shared across both clusters)
new_hg_uuid = create_host_group(rest, SAMPLE_HOST_GROUP_PAYLOAD)
SAMPLE_HOST_PORT_MAPPING['hostGroupUUID'] = new_hg_uuid
SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER0['hostGroupUUID'] = new_hg_uuid
SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER1['hostGroupUUID'] = new_hg_uuid
# 3. Create a host port (shared across both clusters)
new_hp_uuid = create_host_port(rest, SAMPLE_HOST_PORT_PAYLOAD)
SAMPLE_HOST_PORT_MAPPING['hostPortUUID'] = new_hp_uuid
# 4. Map the host port to the host group
map_host_port(rest, SAMPLE_HOST_PORT_MAPPING)
# 5a. Create ACL for cluster 0
new_acl_c0_uuid = create_acl(rest, SAMPLE_ACL_PAYLOAD_CLUSTER0)
SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER0['accessControlListUUID'] = new_acl_c0_uuid
# 5b. Create ACL for cluster 1
new_acl_c1_uuid = create_acl(rest, SAMPLE_ACL_PAYLOAD_CLUSTER1)
SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER1['accessControlListUUID'] = new_acl_c1_uuid
# 6a. Map cluster 0 virtual port to the host group (with ACL)
map_virtual_port(rest, SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER0)
# 6b. Map cluster 1 virtual port to the host group (with ACL)
map_virtual_port(rest, SAMPLE_VIRTUAL_PORT_MAPPING_CLUSTER1)
# 7. Enable the virtual library on all clusters
enable_virtual_library(rest, vtl_uuid)
# 8. Wait for the VTL to come online (polls every 30 s, up to 5 min)
wait_for_vtl_online(rest, vtl_uuid)
Figure 2. RestDriver.py
#!/usr/bin/python3
import pprint
import requests
import json
import time
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
class RESTDriver:
'''
This class includes the
'''
# class variables
server = '' # VTL server IP address/DNS
username = '' # username to access VTL
password = '' # password of the username
base_url = '' # Base RESTful API URL
auth_dict = {} # authenticate data payload
bearer_token_dict = {} # bearer token header
content_type_dict = {} # content type header
token = '' # retrieved bearer token
def __init__(self, server, username='admin', password='admin'):
'''
constructor to set the class variables.
Input:
server: target VTL server IP address or DNS
username: username to access VTL
password: password of the username
'''
self.server = server
self.username = username
self.password = password
self.base_url = 'https://' + server + '/api/' # base URL (https://<VTL address>/api/)
self.content_type_dict = {'Content-Type': 'application/json'} # content type header (always application/json)
self.auth_dict = {'username': self.username, 'password': self.password} # authenticate data payload (for POST /token)
self.token = self.post_token() # POST to get bearer token
self.bearer_token_dict = {'Authorization': 'Bearer ' + self.token} # bearer token header (Need for GET/POST/PATCH/DELETE)
# =============================================================================
# Getter/Setter of class variables
def get_server(self):
return self.server
def get_credentials(self):
return self.username,self.password
def get_current_token(self):
return self.token
def set_token(self, token):
self.token = token
# =============================================================================
# Base RESTful API methods
def get_method(self, endpoint, data_dict={}):
'''
GET method
Input:
endpoint: target endpoint name such as token, virtualLibraries
data_dict: data payload in dictionary. Default is no data payload.
'''
rc = 1 # default rc
# URL
this_url = self.base_url + endpoint
# data payload
data_json = json.dumps(data_dict)
# headers
headers = self.content_type_dict | self.bearer_token_dict # content type + bearer token header
try:
if not data_dict:
response = requests.get(
this_url,
headers=headers,
verify=False
)
else:
response = requests.get(
this_url,
data=data_json,
headers=headers,
verify=False
)
status = response.status_code
if status == 200 or status == 206:
rc = 0
else:
print(f"GET:{this_url} with {data_json} failed. RC:{response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Failed to GET:{this_url} with {data_json}: {e}")
return rc, response
def post_method(self, endpoint, data_dict={}):
'''
POST method
Input:
endpoint: target endpoint name such as token, virtualLibraries
data_dict: data payload in dictionary. Default is no data payload.
'''
rc = 1 # default rc
# URL
this_url = self.base_url + endpoint
# data payload
data_json = json.dumps(data_dict)
# headers
# If this is POST /token with authentication, no more header is required
if endpoint == 'token' and data_dict:
headers = self.content_type_dict # content type header only
else:
headers = self.content_type_dict | self.bearer_token_dict # content type + bearer token header
try:
if not data_dict:
response = requests.post(
this_url,
headers=headers,
verify=False
)
else:
response = requests.post(
this_url,
data=data_json,
headers=headers,
verify=False
)
status = response.status_code
if status == 200 or status == 201 or status == 202 or status == 204:
rc = 0
else:
print(f"POST:{this_url} with {data_json} failed. RC:{response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Failed to POST:{this_url} with {data_json}: {e}")
return rc, response
def patch_method(self, endpoint, data_dict={}):
'''
PATCH method
Input:
endpoint: target endpoint name such as token, virtualLibraries
data_dict: data payload in dictionary. Default is no data payload.
'''
rc = 1 # default rc
# URL
this_url = self.base_url + endpoint
# data payload
data_json = json.dumps(data_dict)
# headers
headers = self.content_type_dict | self.bearer_token_dict # content type + bearer token header
try:
if not data_dict:
response = requests.patch(
this_url,
headers=headers,
verify=False
)
else:
response = requests.patch(
this_url,
data=data_json,
headers=headers,
verify=False
)
status = response.status_code
if status == 200 or status == 202 or status == 204:
rc = 0
else:
print(f"PATCH:{this_url} with {data_json} failed. RC:{response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Failed to PATCH:{this_url} with {data_json}: {e}")
return rc, response
def delete_method(self, endpoint, data_dict={}):
'''
DELETE method
Input:
endpoint: target endpoint name such as token, virtualLibraries
data_dict: data payload in dictionary. Default is no data payload.
'''
rc = 1 # default rc
# URL
this_url = self.base_url + endpoint
# data payload
data_json = json.dumps(data_dict)
# headers
headers = self.content_type_dict | self.bearer_token_dict # content type + bearer token header
try:
if not data_dict:
response = requests.delete(
this_url,
headers=headers,
verify=False
)
else:
response = requests.delete(
this_url,
data=data_json,
headers=headers,
verify=False
)
status = response.status_code
if status == 200 or status == 202 or status == 204:
rc = 0
else:
print(f"DELETE:{this_url} with {data_json} failed. RC:{response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Failed to DELETE:{this_url} with {data_json}: {e}")
return rc, response
# =============================================================================
# Base token function
def post_token(self):
'''
This method gets token by POST with the username and password.
This is called from the constructor then token is saved as a class variable.
'''
endpoint = 'token'
rc, response = self.post_method(endpoint, self.auth_dict)
if rc == 0:
token = response.headers.get('X-Auth-Token')
else:
raise ConnectionError(f"Faild to connect the server {self.server} to POST {endpoint}")
return token
def renew_token(self):
'''
This method renews token by POST with the non-expired token.
'''
endpoint = 'token'
rc, response = self.post_method(endpoint)
if rc == 0:
token = response.headers.get('X-Auth-Token')
else:
raise ConnectionError(f"Faild to connect the server {self.server} to renew {endpoint}")
self.set_token(token) # Renew token of the class variable
# =============================================================================
# Wait for async operations.
def wait_for_uuid(self, endpoint, name_key, name_value, retries=30, interval=5, id_key="uuid"):
'''
Poll GET <endpoint> until an object matching name_key==name_value appears
with a non-empty identifier. Useful after async POST operations.
Input:
endpoint: target endpoint, e.g. "virtualPorts", "hostGroups"
name_key: field name to match on, e.g. "name"
name_value: expected value of that field
retries: maximum number of polling attempts (default 30)
interval: seconds to wait between attempts (default 5)
id_key: field name for the object identifier (default "uuid";
use "id" for endpoints such as cloudRepositories)
Returns the identifier string, or "" if not found within the retry limit.
'''
for attempt in range(retries):
rc, response = self.get_method(endpoint)
if rc == 0:
items = response.json()
if isinstance(items, list):
identifier = next(
(item.get(id_key, "") for item in items
if item.get(name_key) == name_value),
""
)
if identifier:
return identifier
print(f" [{attempt+1}/{retries}] Waiting for {endpoint} '{name_value}'...")
time.sleep(interval)
print(f" Timed out waiting for {endpoint} '{name_value}'")
return ""
Response
- 1st run
Connecting to https://x.x.x.x/api/
--- VTL read operations ---
Virtual Libraries (2):
uuid=72232a25-0921-46bf-847b-3c1d772927a6 name=vtl1
uuid=302aa022-7753-4fb6-9209-4f9f50bd5f09 name=vtl_ibmi4
Dry run (VTL list) completed. Remove or comment the exit() to continue.
- 2nd run
Connecting to https://x.x.x.x/api/
--- VTL read operations ---
Virtual Libraries (2):
uuid=72232a25-0921-46bf-847b-3c1d772927a6 name=vtl1
uuid=302aa022-7753-4fb6-9209-4f9f50bd5f09 name=vtl_ibmi4
--- VTL lifecycle operations ---
Created VTL: uuid=71e84036-96ca-453e-aaef-449fbea1947d name=testvtl desc=test vtl storageSlots=100
--- Mapping read operations ---
Cluster Ports (8):
7cb82860-0825-4f37-a0d9-857fdc11cadb cluster=0 bay=0 slot=3 port=0 wwpn=50:05:07:60:7D:00:40:01
036cb0ae-d7cf-4fb6-af11-5982245017f7 cluster=0 bay=0 slot=9 port=0 wwpn=50:05:07:60:7D:00:40:02
2a03f8bc-6c67-4501-94ed-af4b1dec0567 cluster=0 bay=0 slot=3 port=1 wwpn=50:05:07:60:7D:00:40:03
653fd00f-fb09-4132-91b4-1ca26bee1c21 cluster=0 bay=0 slot=9 port=1 wwpn=50:05:07:60:7D:00:40:04
090cfae3-3f17-48e8-bc7b-5613e1e93fa9 cluster=0 bay=0 slot=3 port=2 wwpn=50:05:07:60:7D:00:40:05
750aa13b-6dc3-43cc-aa9e-bfadc4b37320 cluster=0 bay=0 slot=9 port=2 wwpn=50:05:07:60:7D:00:40:06
cd892111-5819-4581-bdc0-3e20cdcd70fd cluster=0 bay=0 slot=3 port=3 wwpn=50:05:07:60:7D:00:40:07
e95418b1-81cc-4392-8152-b740b5556e05 cluster=0 bay=0 slot=9 port=3 wwpn=50:05:07:60:7D:00:40:08
Virtual Ports (2):
da0b802c-b521-4362-824d-29f47f3c3d1c name=EP_Defaul_vtl_ib_C0_S3P0-10
6aaa3613-bd36-4e16-8d77-81d0ddecfb28 name=EP_Defaul_vtl1_C0_S9P0-9
Host Groups (2):
763f7da3-1dec-4bda-8712-6cceecfca59d name=VTL1_HG1
699970fa-6be5-464d-8fff-2134122db800 name=vtl_ibmi4_hg
Host Ports (3):
f33f5e01-fa76-4e24-adc8-4cb3c79904de name=vtl1_hp1
2362ab03-1540-443a-bd09-a992b398efb3 name=vtl_ibmi4_hp1
48b0ee5c-00f3-45ae-a25e-534f2a10c47f name=vtl_ibmi4_hp2
Hosts (3):
name=vtl1_hp1
name=vtl_ibmi4_hp1
name=vtl_ibmi4_hp2
Dry run completed. Make sure to set valid data for those marked REPLACE in the script.
Make a note of virtual library uuid just created, and comment out create_virtual_library() also.
- 3rd run
Connecting to https://x.x.x.x/api/
--- VTL read operations ---
Virtual Libraries (3):
uuid=72232a25-0921-46bf-847b-3c1d772927a6 name=vtl1
uuid=302aa022-7753-4fb6-9209-4f9f50bd5f09 name=vtl_ibmi4
uuid=71e84036-96ca-453e-aaef-449fbea1947d name=testvtl
--- VTL lifecycle operations ---
--- Mapping read operations ---
Cluster Ports (8):
7cb82860-0825-4f37-a0d9-857fdc11cadb cluster=0 bay=0 slot=3 port=0 wwpn=50:05:07:60:7D:00:40:01
036cb0ae-d7cf-4fb6-af11-5982245017f7 cluster=0 bay=0 slot=9 port=0 wwpn=50:05:07:60:7D:00:40:02
2a03f8bc-6c67-4501-94ed-af4b1dec0567 cluster=0 bay=0 slot=3 port=1 wwpn=50:05:07:60:7D:00:40:03
653fd00f-fb09-4132-91b4-1ca26bee1c21 cluster=0 bay=0 slot=9 port=1 wwpn=50:05:07:60:7D:00:40:04
090cfae3-3f17-48e8-bc7b-5613e1e93fa9 cluster=0 bay=0 slot=3 port=2 wwpn=50:05:07:60:7D:00:40:05
750aa13b-6dc3-43cc-aa9e-bfadc4b37320 cluster=0 bay=0 slot=9 port=2 wwpn=50:05:07:60:7D:00:40:06
cd892111-5819-4581-bdc0-3e20cdcd70fd cluster=0 bay=0 slot=3 port=3 wwpn=50:05:07:60:7D:00:40:07
e95418b1-81cc-4392-8152-b740b5556e05 cluster=0 bay=0 slot=9 port=3 wwpn=50:05:07:60:7D:00:40:08
Virtual Ports (2):
da0b802c-b521-4362-824d-29f47f3c3d1c name=EP_Defaul_vtl_ib_C0_S3P0-10
6aaa3613-bd36-4e16-8d77-81d0ddecfb28 name=EP_Defaul_vtl1_C0_S9P0-9
Host Groups (2):
763f7da3-1dec-4bda-8712-6cceecfca59d name=VTL1_HG1
699970fa-6be5-464d-8fff-2134122db800 name=vtl_ibmi4_hg
Host Ports (3):
f33f5e01-fa76-4e24-adc8-4cb3c79904de name=vtl1_hp1
2362ab03-1540-443a-bd09-a992b398efb3 name=vtl_ibmi4_hp1
48b0ee5c-00f3-45ae-a25e-534f2a10c47f name=vtl_ibmi4_hp2
Hosts (3):
name=vtl1_hp1
name=vtl_ibmi4_hp1
name=vtl_ibmi4_hp2
--- POST operations ---
Created virtual port: uuid=4bdc3064-de71-41f2-b220-0e57ff07d14c name=sample-virtual-port-c0-01
Created host group: uuid=d3c87101-b5a8-43ef-93d9-544b1844e345 name=sample-host-group-01
Created host port: uuid=8e4a6c05-4eb3-4d26-b8cc-c64e07046feb name=sample-host-01
Mapped host port 8e4a6c05-4eb3-4d26-b8cc-c64e07046feb to group d3c87101-b5a8-43ef-93d9-544b1844e345
Created ACL: uuid=18567b30-4b84-4779-adcb-288fe87f9517 name=sample-acl-c0-01
Mapped virtual port 4bdc3064-de71-41f2-b220-0e57ff07d14c to group d3c87101-b5a8-43ef-93d9-544b1844e345
Enabled VTL uuid=71e84036-96ca-453e-aaef-449fbea1947d targetClusters=all
Monitoring VTL state (every 30s, up to 300s) ...
[1/10] VTL uuid=71e84036-96ca-453e-aaef-449fbea1947d state=online
VTL uuid=71e84036-96ca-453e-aaef-449fbea1947d is online.