Vinod Subramaniam

Getting Started with Perl and the HCS Configuration Manager 8.4.1 Rest API

Discussion created by Vinod Subramaniam Employee on Jul 17, 2016
Latest reply on Jul 21, 2016 by Thrinath Seelam



Hitachi Command Suite 8.4.1 ships with the Configuration Manager REST API.

The CM REST API lets you automate common provisioning tasks such as the below


1. Searching for Storage System Information

2. Volume Allocation

3. Pool Management

4. Shadow Image and Thin Image Management

5. Remote Copy Management

Chapter 14 in the Configuration Manager REST API Manual has information on getting started with scripting using Python as a scripting language.

For those of you who are die hard Perl addicts or otherwise forced to use Perl this article will help you get started with the CM REST API and Perl.

The example used in this article is automating Volume Allocation i.e. assigning a 1TB LUN from a HDP Pool to a new Host Group on a specific FC Port.


You can use either REST::Client or LWP::UserAgent to communicate using Perl with the Configuration Manager REST Server.

This article uses REST::Client to illustrate some common programming tasks involved.


If you would like to run the script first and then come back to this article the entire script is attached as a gzipped file.



Below is the usage syntax for the script.

[root@localhost vinod]# ./


The script itself can be broken down into eight stages or steps. This is obvious from the sample dry run below.

The steps are as listed below and are also listed on Page 528 of the manual.


A. Obtain Session Token

B. Query for an unused LDEV ID

C. Create the LDEV on the specified Pool ID with the specified size

D. Create the specified Host Group with the specified host mode

E. Add the specified WWN to the Host Group

F. Add the LDEV to the host group and assign a lun path

G. Verify the LDEV

H. Release the Session Token


[root@localhost vinod]# ./ 0 1T CL3-B AIXSERVR AIX 10000000C9AB531A 5 30
Generating Session Token for 834000400040 and
Obtained Session Token ef76fac3-c36c-4c21-aeb6-ad47ead22e14
Querying for first available LDEV ID
First undefined LDEV ID is 67
Creating LDEV 67 on Pool 0
Job 185 is Initializing
Job 185 is Running
Job 185 is Running
Job 185 is Running
Creating Host Group AIXSERVR on Port CL3-B
Job 186 is Initializing
Job 186 is Completed
Host Group Number is 5
Adding WWN 10000000C9AB531A to AIXSERVR on Port CL3-B
Job 187 is Initializing
Job 187 is Completed
Adding lun paths for LDEV 67 for Host Group AIXSERVR on Port CL3-B
Job 188 is Initializing
Job 188 is Completed
Verifying LDEV 67
  "ldevId" : 67,
  "clprId" : 0,
  "emulationType" : "OPEN-V",
  "byteFormatCapacity" : "1.00 T",
  "blockCapacity" : 2147483648,
  "numOfPorts" : 1,
  "ports" : [ {
    "portId" : "CL3-B",
    "hostGroupNumber" : 5,
    "hostGroupName" : "AIXSERVR",
    "lun" : 0
  } ],
  "attributes" : [ "HDT" ],
  "status" : "NML",
  "mpBladeId" : 0,
  "ssid" : "0000",
  "poolId" : 0,
  "numOfUsedBlock" : 0,
  "isRelocationEnabled" : true,
  "tierLevel" : "all",
  "usedCapacityPerTierLevel1" : 0,
  "usedCapacityPerTierLevel2" : 0,
  "tierLevelForNewPageAllocation" : "M",
  "resourceGroupId" : 0
Releasing Session ef76fac3-c36c-4c21-aeb6-ad47ead22e14


Step 0 : Versions and Constants


Now lets understand the script itself. This article assumes some intermediate level of Perl Scripting expertise.


REST API Version


[16/07/17 21:56:34.422][INFO ][main][13904@WIN-CKA2USBQB02][System][][KART80060-I][VersionEnv#VersionEnv]
[ProductName Configuration Manager REST API, Version 1.2.0.  ][15025KB/23944KB/506816KB]


Below is the OS version and Perl version that this script was tested on.


[root@localhost vinod]# uname -r
[root@localhost vinod]# cat /etc/redhat-release 
CentOS release 5 (Final)
[root@localhost vinod]# perl -v
This is perl, v5.8.8 built for x86_64-linux-thread-multi


To start with we import the required Perl Libraries as below.

The first two are not part of a standard Perl install and will need to be installed using cpan.


use REST::Client;
use JSON;
use Data::Dumper;
use MIME::Base64;


Perl Library Versions are below


Module          REST::Client    (K/KK/KKANE/REST-Client-273.tar.gz)
Module          IO::Socket::SSL (S/SU/SULLR/IO-Socket-SSL-2.033.tar.gz)
Module          IO::Socket::SSL::Utils (S/SU/SULLR/IO-Socket-SSL-2.033.tar.gz)
Module          Net::SSLeay     (M/MI/MIKEM/Net-SSLeay-1.74.tar.gz)
Module          LWP::UserAgent  (E/ET/ETHER/libwww-perl-6.15.tar.gz)
Module          JSON            (M/MA/MAKAMAKA/JSON-2.90.tar.gz)


The next block of code is fairly straight forward and parses the command line arguments.


if ($#ARGV lt 7) {
    print "usage: $0 <POOL_ID> <LDEV_CAPACITY> <PORT_ID> <HG_NAME> <HG_MODE> <WWN> <WAIT> <RETRY>\n";
    exit 1;
my $POOL_ID= $ARGV[0];
my $PORT_ID = $ARGV[2];
my $HG_NAME = $ARGV[3];
my $HG_MODE = $ARGV[4];
my $WWN = $ARGV[5];
my $WAIT_TIME = $ARGV[6];


Following this are the constant declarations that you will need to change in order for the script to run in your environment.

Line 2 is the FQDN of the host running the Configuration Manager REST server.

Line 3 is the serial number of the array padded with 834000. See Page 62 in the manual for an explanation.

Line 4 is the SSL port and Line 7 is the corresponding base URL that will be used throughout.

Lines 5 and 6 are the username and password.



my $RESTHOST = "";
my $SERIALNO = "834000400040";
my $RESTPORT = "23451";
my $RESTUSER = "rest-test";
my $RESTPASS = "rest-api";
my $HOSTURL = "https://" . $RESTHOST . ":" . $RESTPORT;


There are two subroutine declarations following the above section which we will discuss at the end.


Step 1 : Get Session Token


Line 4 shows how to encode the username and password with Mime::Base64 for Basic authentication.

Line 5-9 shows the hash data structure containing the HTTP headers that the CM REST Server expects

Line 13 illustrates the HTTP POST command that will return a JSON output containing the authentication token that will used in the later steps

Lines 15-17 shows how to decode the JSON output and extract the Token and the Session ID

Lines 23-28 shows the hash data structure with HTTP headers that will be used in Steps 2-8 with GET/PUT/POST/DELETE commands


print "\nGenerating Session Token for $SERIALNO and $RESTHOST\n";
my $URI = URI->new($HOSTURL);
my $encoded_auth = encode_base64("$RESTUSER:$RESTPASS", '');
    'Authorization' => "Basic $encoded_auth",
    'Accept' => 'application/json',
    'referer' => $HOSTURL,
    'Content-Type' => 'application/json'
my $CLIENT=REST::Client->new();
$CLIENT->POST("/ConfigurationManager/v1/objects/storages/".$SERIALNO."/sessions/", '', \%SESS_HEADERS);
if ( $CLIENT->responseCode() eq 200 ) {
 $decoded_json = decode_json($CLIENT->responseContent());
 $SESS_TOKEN = $decoded_json->{'token'};
 $SESS_ID = $decoded_json->{'sessionId'};
} else {
 print $CLIENT->responseContent();
 die "Error obtaining Session Token\n";
print "Obtained Session Token $SESS_TOKEN\n";
my %HEADERS= (
 'Authorization' => "Session $SESS_TOKEN",
        'Accept' => 'application/json',
     'referer' => $HOSTURL,
        'Content-Type' => 'application/json'


Step 2 : Query for a unused LDEV ID


Line 3 illustrates the HTTP GET command with a QUERY string in order to obtain the first undefined LDEV ID


print "\nQuerying for first available LDEV ID\n";
if ( $CLIENT->responseCode() eq 200 ) {
 $decoded_json = decode_json($CLIENT->responseContent());
 @LDEV_ARR =@{  $decoded_json->{'data'} };
 $LDEV_ID = $LDEV_ARR[0]->{'ldevId'};
 print "First undefined LDEV ID is $LDEV_ID\n\n";
} else {
 print $CLIENT->responseContent();
 die "Unable to retrieve unused LDEV ID\n";


Step 3 : Create a LDEV on the HDP Pool


Lines 3-7 illustrates the LDEV parameters JSON input hash perl data structure that is input to the subroutine encode_json on Line 8

Line 8 is the POST command that submits the input JSON to the CM REST Server along with the header hash from Step 1

Line 9 calls the wait_for_job subroutine that will first sleep for the specified WAIT passed on the command line before checking the job status again

This subroutine will loop around RETRY times as specified on the command line.

This ensures that Step 3 completes before we move to Step 4. This way we can trace any failure to a particular step.

print "Creating LDEV $LDEV_ID on Pool $POOL_ID\n";
 'ldevId' => $LDEV_ID,
 'poolId' => $POOL_ID,
 'byteFormatCapacity' => $LDEV_CAPACITY
$CLIENT->POST("/ConfigurationManager/v1/objects/storages/".$SERIALNO."/ldevs", encode_json(\%LDEV_INPUT_JSON), \%HEADERS);
wait_for_job( $CLIENT, \%HEADERS );


Step 4 : Create Host Group


Again Lines 3-9 are the input JSON hash with the host group parameters

Line 10 is the HTTP POST command and Line 11 is  the subroutine call that waits for the job to complete.

In this case the subroutine wait_for_hg_job returns the host group number which is used in Step 5


print "\nCreating Host Group $HG_NAME on Port $PORT_ID\n";
 'portId' => $PORT_ID,
 'hostGroupName' => $HG_NAME,
 'hostMode' => $HG_MODE,
 'hostModeOptions' => [ 40,63 ],
 'isQuickCreating' => 'true'
$CLIENT->POST("/ConfigurationManager/v1/objects/storages/".$SERIALNO."/host-groups", encode_json(\%HOST_INPUT_JSON),\%HEADERS);
$hostGroupNumber = wait_for_hg_job($CLIENT, \%HEADERS);
print "Host Group Number is $hostGroupNumber\n";


Step 5 : Add WWN to Host Group


Lines 3-7 is the input JSON and Line 8 is the HTTP POST

Line 9 is the subroutine that waits for the job to end.

print "\nAdding WWN $WWN to $HG_NAME on Port $PORT_ID\n";
 'portId' => $PORT_ID,
 'hostWwn' => $WWN,
 'hostGroupNumber' => $hostGroupNumber 
$CLIENT->POST("/ConfigurationManager/v1/objects/storages/".$SERIALNO."/host-wwns", encode_json(\%WWN_INPUT_JSON), \%HEADERS);
wait_for_job($CLIENT, \%HEADERS);


Step 6 : Add LUN Paths


The block below now reveals a pattern that is beginning to show i.e You build the input JSON and submit it to a URL along with the headers using a POST command to the CM REST Server.

print "\nAdding lun paths for LDEV $LDEV_ID for Host Group $HG_NAME on Port $PORT_ID\n";
 'ldevId' => $LDEV_ID,
 'portId' => $PORT_ID,
 'hostGroupNumber' => $hostGroupNumber 
$CLIENT->POST("/ConfigurationManager/v1/objects/storages/".$SERIALNO."/luns", encode_json(\%LUN_INPUT_JSON), \%HEADERS);
wait_for_job($CLIENT, \%HEADERS);


Step 7 : Verify LDEV addition


This block is a HTTP GET that verifies that Steps 1-6 have been successfully completed.

print "\nVerifying LDEV $LDEV_ID\n";
$CLIENT->GET("/ConfigurationManager/v1/objects/storages/".$SERIALNO."/ldevs/".$LDEV_ID, \%HEADERS);
print $CLIENT->responseContent();


Step 8. Release Session


Since there is a upper limit on the number of concurrent sessions it is a good idea to release the session after you are done.

This is a HTTP DELETE command the destroys the session on the CM REST Server.


print "\nReleasing Session $SESS_TOKEN\n";
my %HEADERS = (
 'Accept' => 'application/json',
 'Content' => 'application/json',
 'Authorization' => "Session $SESS_TOKEN"
$CLIENT->DELETE("/ConfigurationManager/v1/objects/storages/".$SERIALNO."/sessions/".$SESS_ID, \%HEADERS);
if ( $CLIENT->responseCode() eq 200 ) {
} else {
 print $CLIENT->responseContent();
 die "Error destroying Session $SESS_ID\n";


You can get more information and sample Python Scripts from the manual that can be downloaded from

Please note that this script should not be used for testing/learning on a array that is in production.

If you have access to a test/qa array that should be your learning playground.


Update : Using CA certificates and certificates from a registered Certificate Authority


The above code uses a self signed certificate.

With Perl versions greater than 5.8.8 and self signed certificates the above code will fail with a "Certificate Verify Error".

You can head over to and get a server certificate for your test/development server

and use the file which has the below updates to support CA certificate verification.

SSL debug output is also attached as SSLdebug.txt.


Debug output can be obtained using the command below

perl -MIO::Socket::SSL=debug30 ./ 0 1T CL1-A AIXS AIX 10000000C900BA03 5 30 2> SSLdebug.txt


Changes Made to the script are below.


Below are the imports that were added

If you are using a Certificate from VeriSign etc. use Mozilla::CA instead of CACertOrg::CA after you install Mozilla::CA with cpan.


use LWP::UserAgent;
use CACertOrg::CA;


Below lines were added after the %SESSHEADERS declaration

If you are using Mozilla::CA replace CACertOrg with Mozilla in the lines below.


my $UA = LWP::UserAgent->new();
$UA->ssl_opts(timeout => 10);
$UA->ssl_opts(verify_hostname => 'false');
$UA->ssl_opts(SSL_verify_mode => 0x00);
$UA->ssl_opts(SSL_ca_file => CACertOrg::CA::SSL_ca_file());
$UA->protocols_allowed( ['http', 'https'] );
my $CLIENT=REST::Client->new({
 host => $HOSTURL,
 timeout => 300,
 useragent => $UA,
 ca => CACertOrg::CA::SSL_ca_file(), 


Script to debug SSL [ From perlmonks ]


linux-8ons:/home/vinod # perl -MIO::Socket::SSL=debug2 ./
DEBUG: .../IO/Socket/ socket not yet connected
DEBUG: .../IO/Socket/ socket connected
DEBUG: .../IO/Socket/ ssl handshake not started
DEBUG: .../IO/Socket/ using SNI with hostname
DEBUG: .../IO/Socket/ request OCSP stapling
DEBUG: .../IO/Socket/ set socket to non-blocking to enforce timeout=180
DEBUG: .../IO/Socket/ ssl handshake in progress
DEBUG: .../IO/Socket/ waiting for fd to become ready: SSL wants a read first
DEBUG: .../IO/Socket/ socket ready, retrying connect
DEBUG: .../IO/Socket/ ssl handshake in progress
DEBUG: .../IO/Socket/ waiting for fd to become ready: SSL wants a read first
DEBUG: .../IO/Socket/ socket ready, retrying connect
DEBUG: .../IO/Socket/ ssl handshake done


linux-8ons:/home/vinod # cat
use strict;
use warnings;
use CACertOrg::CA;
use LWP::UserAgent;
use Mozilla::CA;
my $ua = LWP::UserAgent->new(
        ssl_opts => {
                verify_hostname => 0,
                SSL_ca_file => CACertOrg::CA::SSL_ca_file(),
                SSL_version => 'TLSv12',
        }) or die;
my $url = $ARGV[0] || '';
my $res = $ua->get($url);
print $res->code . "\n";


Perl and OS Version Tested is below


linux-8ons:/home/vinod # cat /etc/os-release 
PRETTY_NAME="openSUSE Tumbleweed (20160715) (x86_64)"


linux-8ons:/home/vinod # uname -a
Linux linux-8ons 4.6.3-1-default #1 SMP PREEMPT Sun Jun 26 07:34:33 UTC 2016 (d4bcf2a) x86_64 x86_64 x86_64 GNU/Linux


linux-8ons:/home/vinod # perl -v
This is perl 5, version 24, subversion 0 (v5.24.0) built for x86_64-linux-thread-multi


Notes :-


Older OS releases and older Perl versions can negotiate TLSV1 and AES256-SHA which are less secure than TLSV1_2 and AES256-SHA256.

See apache SSL logs below

CentOS 5.1 and Perl 5.8 client


[16/Jul/2016:07:42:04 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/ldevs?ldevOption=undefined&count=1 HTTP/1.1" 134
[16/Jul/2016:07:42:04 -0700] TLSv1 AES256-SHA "POST /ConfigurationManager/v1/objects/storages/834000400040/ldevs HTTP/1.1" 494
[16/Jul/2016:07:42:05 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/167 HTTP/1.1" 494
[16/Jul/2016:07:42:20 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/167 HTTP/1.1" 635
[16/Jul/2016:07:42:35 -0700] TLSv1 AES256-SHA "POST /ConfigurationManager/v1/objects/storages/834000400040/host-groups HTTP/1.1" 566
[16/Jul/2016:07:42:35 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/168 HTTP/1.1" 566
[16/Jul/2016:07:42:50 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/168 HTTP/1.1" 718
[16/Jul/2016:07:43:05 -0700] TLSv1 AES256-SHA "POST /ConfigurationManager/v1/objects/storages/834000400040/host-wwns HTTP/1.1" 513
[16/Jul/2016:07:43:05 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/169 HTTP/1.1" 513
[16/Jul/2016:07:43:20 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/169 HTTP/1.1" 680
[16/Jul/2016:07:43:35 -0700] TLSv1 AES256-SHA "POST /ConfigurationManager/v1/objects/storages/834000400040/luns HTTP/1.1" 493
[16/Jul/2016:07:43:35 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/170 HTTP/1.1" 493
[16/Jul/2016:07:43:50 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/170 HTTP/1.1" 640
[16/Jul/2016:07:44:05 -0700] TLSv1 AES256-SHA "GET /ConfigurationManager/v1/objects/storages/834000400040/ldevs/61 HTTP/1.1" 617
[16/Jul/2016:07:44:06 -0700] TLSv1 AES256-SHA "DELETE /ConfigurationManager/v1/objects/storages/834000400040/sessions/28 HTTP/1.1"


OpenSUSE Tumbleweed and Perl 5.24 client


[17/Jul/2016:21:31:58 -0700] TLSv1.2 AES256-SHA256 "POST /ConfigurationManager/v1/objects/storages/834000400040/sessions/ HTTP/1.1" 76
[17/Jul/2016:21:31:58 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/ldevs?ldevOption=undefined&count=1 HTTP/1.1" 134
[17/Jul/2016:21:32:03 -0700] TLSv1.2 AES256-SHA256 "POST /ConfigurationManager/v1/objects/storages/834000400040/ldevs HTTP/1.1" 494
[17/Jul/2016:21:32:04 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/212 HTTP/1.1" 494
[17/Jul/2016:21:32:09 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/212 HTTP/1.1" 490
[17/Jul/2016:21:32:14 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/212 HTTP/1.1" 635
[17/Jul/2016:21:32:19 -0700] TLSv1.2 AES256-SHA256 "POST /ConfigurationManager/v1/objects/storages/834000400040/host-groups HTTP/1.1" 561
[17/Jul/2016:21:32:19 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/213 HTTP/1.1" 561
[17/Jul/2016:21:32:24 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/213 HTTP/1.1" 713
[17/Jul/2016:21:32:29 -0700] TLSv1.2 AES256-SHA256 "POST /ConfigurationManager/v1/objects/storages/834000400040/host-wwns HTTP/1.1" 513
[17/Jul/2016:21:32:29 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/214 HTTP/1.1" 513
[17/Jul/2016:21:32:34 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/214 HTTP/1.1" 680
[17/Jul/2016:21:32:39 -0700] TLSv1.2 AES256-SHA256 "POST /ConfigurationManager/v1/objects/storages/834000400040/luns HTTP/1.1" 493
[17/Jul/2016:21:32:40 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/215 HTTP/1.1" 493
[17/Jul/2016:21:32:45 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/jobs/215 HTTP/1.1" 640
[17/Jul/2016:21:32:50 -0700] TLSv1.2 AES256-SHA256 "GET /ConfigurationManager/v1/objects/storages/834000400040/ldevs/76 HTTP/1.1" 612
[17/Jul/2016:21:32:50 -0700] TLSv1.2 AES256-SHA256 "DELETE /ConfigurationManager/v1/objects/storages/834000400040/sessions/1 HTTP/1.1" -


Message was edited by: Vinod Subramaniam