Using Python to talk to Archiware P5’s CLI API

I get asked all the time how the Archiware P5 CLI works. It is actually a fairly intuitive CLI, the docs can be found at Archiware’s knowledgebase here. Below is a heavily commented python script for doing some of the basics with P5 archive via the CLI. This code should work with Python 2.6 and greater.

import sys
import os
import time
import hashlib
import subprocess
 
###########################
# user configurable stuff #
###########################
 
#where is p5 installed?
p5_path = '/usr/local/aw'
 
#ip of p5 server
p5_ip = 'p5demo'
 
#user of p5 server - should be root or user allowed to use P5.  More info here at the url below
# http://portal.archiware.com/support/index.php?/Knowledgebase/Article/View/46/0/user-access-rights-in-presstore
p5_user = 'root'
 
#p5 user password
p5_pass = 'password'
 
#p5 server public port - note this is NOT the port of the local nsd instance, otherwise we could sniff it
p5_port = '8000'
 
###########################
# end configurable stuff  #
###########################
 
#derive the nsdchat path
nsdchat = p5_path + '/bin/nsdchat'
 
# derive the API port
api_port = str(int(p5_port) + 1001)
 
#generate a random session ID - only want to do this once per startup of your app
#I used MD5 here and hashed the date the script started, but you could use whatever you want.  You could even
#use a fixed value as only this application will use that session ID
 
session_id = (hashlib.sha224(str(time.time()))).hexdigest()
 
#build our command prefix so we don't have to keep typing stuff
sock = 'awsock:/' + p5_user + ":" + p5_pass + ":" + session_id + "@" + p5_ip + ":" + api_port
 
#build a simple list we can pass our process interpreter to make life easier
#we can just append our classes/methods now to this
cmd = [nsdchat,'-s',sock,'-c']
 
#make calls easier to make - it will always return a list even if only one element is in there.
#it will print out what it sent via the CLI and return the result. Pass it our cmd from above
#as argument 1 and the class/methods/args we need as a list in arg 2 in a list
def p5_api_call(p5_command_prefix, p5_call):
	#build our call
	new_command = p5_command_prefix + p5_call
	#just print it out so it is easy to see what is going on
	print ' '.join(new_command) 
	#run the call and wait until the process completes
	p = subprocess.Popen(new_command,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	p.wait()
	#grab stdout and stderr
	output,error = p.communicate()
	#return stdout formatted as a list.  This isn't 100% perfect, some results are pure text so
	return output
 
# a few quick examples.  To run geterror, sniff your response for expected error based on API
# normally this is an empty line that strips out to nothing in a list (assuming your are building
# lists), but sometimes it isn't.  
#
print p5_api_call(cmd,['ArchivePlan','names']).rstrip().split(' ')
 
# if you know a result never will be more than a single element, you could get rid of the split
print p5_api_call(cmd,['srvinfo','port']).rstrip()
 
# if you want to trap for an error, take a look at your output and filter for expected results
# then you can get the error running the 'geterror' method immediately after an error condition
 
if p5_api_call(cmd,['Volume','1234','mediatype']).rstrip().split(' ')[0] == '':
	print p5_api_call(cmd,['geterror'])
else:
	print
 
# an example of a full archive job - you'll want to error trap all of this
# but for examples we'll just assume everything works
#
# first we need to create an archive selection
my_selection = p5_api_call(cmd,['ArchiveSelection','create','localhost','10001']).rstrip()
print my_selection
 
# then we need to add entries to the selection.  Normally this will be a list of files
# it is important to note that this absolute path to the file on the P5 server, so if there is path
# mapping that has to happen, you'll need to do that before passing the files
# 
# you will also want to check if a file does not return a handle as that means it won't be archived
 
my_file_list = ['/tmp/AlTest1.err','/tmp/AlTest1.out'] #fill this in with real files
my_handles = []
for file in my_file_list:
	this_handle = p5_api_call(cmd,['ArchiveSelection',my_selection,'addentry',file]).rstrip()
	my_handles.append(this_handle)
 
print my_handles # these are all the unique ids of the items that P5 will use in its index
 
 
my_job = p5_api_call(cmd,['ArchiveSelection','submit','now']).rstrip()
print my_job # this is the job that was just submitted
 
my_status = p5_api_call(cmd,['Job',my_job,'status']
print my_status # get back running status of a submitted job