Commit 261d1165 authored by Vladimir Bahyl's avatar Vladimir Bahyl
Browse files

Adding tape-(un)mount

parent 591c5c48
This diff is collapsed.
Summary: CTA CERN specific tape utilities
Name: cta-cern-tape-utils
Version: 1.0
Release: 1
Release: 2
License: GPL
Buildroot: /tmp/%{name}-%{version}
BuildArch: noarch
......@@ -29,7 +29,12 @@ cd $RPM_BUILD_ROOT
mkdir -p usr/local/bin etc/cron.d
name=`echo %{name} |sed 's/CERN-CC-//'`
install -m 755 $RPM_BUILD_DIR/%{name}-%{version}/tape-config-generate usr/local/bin/tape-config-generate
install -m 755 $RPM_BUILD_DIR/%{name}-%{version}/tape-config-generate $RPM_BUILD_ROOT/usr/local/bin/tape-config-generate
install -m 644 -p $RPM_BUILD_DIR/%{name}-%{version}/TapeAdmin.py $RPM_BUILD_ROOT/usr/local/lib/python
install -m 755 -p $RPM_BUILD_DIR/%{name}-%{version}/tape-mount $RPM_BUILD_ROOT/usr/local/bin/tape-mount
ln -s $RPM_BUILD_ROOT/usr/local/bin/tape-mount $RPM_BUILD_ROOT/usr/local/bin/tape-unmount
%post
......@@ -39,3 +44,8 @@ rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
/usr/local/bin/tape-config-generate
/usr/local/lib/python/TapeAdmin.py
/usr/local/lib/python/TapeAdmin.pyc
/usr/local/lib/python/TapeAdmin.pyo
/usr/local/bin/tape-mount
/usr/local/bin/tape-unmount
#!/usr/bin/python36
import argparse
import sys
import os
sys.path.append('/usr/local/lib/python')
import TapeAdmin
opt_verbose, opt_drive, opt_vid, opt_check = None, None, None, None
my_name = sys.argv[0].split('/')[-1] # Get rid of "/usr/local/bin" and take only the final part
mt = '/bin/mt'
args, lib, device = None, None, None
operation = None
cta_smc = '/usr/bin/cta-smc'
def load_args():
usage = None
if operation == 'mount':
usage = '\ntape-mount -V <vid> [-D <drive_name>] [-v] [-c]\n' + \
'tape-mount --vid <vid> [--drivename <drive_name>] [--verbose] [--check]'
elif operation == 'unmount':
usage = '\ntape-unmount [-V <vid>] [-D <drive_name>] [-v] [-c]\n' + \
'tape-unmount [--vid <vid>] [--drivename <drive_name>] [--verbose] [--check]'
parser = argparse.ArgumentParser(
description='A script to mount or unmount a tape on the local drive',
usage=usage
)
# Required argument -V if the operation is to MOUNT
if operation == 'mount':
required_named = parser.add_argument_group('required arguments')
required_named.add_argument('-V', '--vid', type=str, help='The volume ID to mount/unmount')
# Optional argument -V if the operation is to UNMOUNT
elif operation == 'unmount':
parser.add_argument('-V', '--vid', type=str, help='The volume ID to mount/unmount')
# Optional arguments
parser.add_argument('-D', '--drive', type=str, help='The drive to be used. This is an optional parameter, '
'this script will use the first drive from TPCONFIG if none is specified.')
parser.add_argument('-v', '--verbose', action='store_true', help='Be verbose')
parser.add_argument('-c', '--check', action='store_true', help='Check if the correct tape is mounted by reading '
'the VOL1 label.')
global opt_vid, opt_drive, opt_verbose, opt_check
global args
args = parser.parse_args()
opt_vid = args.vid
opt_drive = args.drive
opt_verbose = args.verbose
opt_check = args.check
if not opt_drive:
opt_drive = TapeAdmin.get_first_drive()
if operation == 'mount' and not opt_vid:
parser.print_usage()
exit(0)
if operation == 'unmount' and not opt_vid:
opt_vid = TapeAdmin.get_tape_vid_inside_drive(opt_drive)
# Get the library and the device
global lib, device
lib = TapeAdmin.get_library(opt_drive)
device = TapeAdmin.get_device(opt_drive)
def define_mount_or_unmount_operation():
"""
Defines if the operation to do is mount or unmount, depending on the name of the called script
"""
global operation
if 'unmount' in sys.argv[0]:
operation = 'unmount'
elif 'mount' in sys.argv[0]:
operation = 'mount'
def get_drive():
"""
Checks the drive to exist if the opt_drive was given. Otherwise, assigns the 1st drive found in TPCONFIG
"""
global opt_drive
if opt_drive:
if TapeAdmin.is_drive_known(opt_drive):
if opt_verbose:
TapeAdmin.debug(my_name, 'using specified drive: ' + opt_drive)
return
TapeAdmin.abort(my_name, 'drive ' + opt_drive + ' not found in TPCONFIG')
else:
opt_drive = TapeAdmin.get_first_drive()
if opt_verbose:
TapeAdmin.debug(my_name, 'no drive specified, taking the one found in TPCONFIG: ' + opt_drive)
def do_VOL1_check():
if opt_check and operation == 'mount':
# Rewind
cmd = mt + ' -f ' + device + ' rewind'
if opt_verbose:
TapeAdmin.debug(my_name, 'running ' + cmd)
if TapeAdmin.run_cmd(cmd).returncode != 0:
TapeAdmin.abort(my_name, 'rewind failed')
elif opt_verbose:
TapeAdmin.debug(my_name, 'rewind of ' + opt_vid + ': OK')
# Read VOL1
file = '/tmp/' + opt_vid + '-' + str(os.getuid()) + 'vol1'
# Remove past temp file
cmd = 'rm -f ' + file + ' >/dev/null 2>&1'
TapeAdmin.run_cmd(cmd)
if TapeAdmin.do_VOL1_check(device, file, opt_vid) == 'ok':
if opt_verbose:
TapeAdmin.debug(my_name, 'VOL1 contains ' + opt_vid + ': OK')
else:
TapeAdmin.warning(my_name, 'VOL1 check FAILED for tape ' + opt_vid + ' after mount operation')
TapeAdmin.run_cmd('rm -f ' + file + ' >/dev/null 2>&1') # Remove temp file
def check_tape_location_if_mount():
"""
Checks if the tape is in the proper state for mount
"""
location = TapeAdmin.tape_queryvolume(opt_vid, opt_verbose)
if operation == 'mount' and location != 'HOME':
TapeAdmin.abort(my_name, 'tape ' + opt_vid + ' is NOT in its HOME slot. Its location is ' + location)
def do_previous_drive_checks():
"""
Checks if the current status of the drive is suitable for doing mount or unmount, respectively
"""
# Do previous unmount checks
if operation == 'unmount':
if TapeAdmin.is_drive_free(opt_drive):
TapeAdmin.abort(my_name, 'there is no tape inside ' + opt_drive + ': drive is FREE')
elif opt_vid and not TapeAdmin.get_tape_vid_inside_drive(opt_drive) == opt_vid:
TapeAdmin.abort(my_name, 'the specified tape ' + opt_vid + ' is not in the specified drive ' + opt_drive)
elif not TapeAdmin.is_tape_ejected(opt_drive):
cmd = mt + ' -f ' + device + ' eject'
if opt_verbose:
TapeAdmin.debug(my_name, 'tape drive ' + opt_drive + ' has the tape ' +
TapeAdmin.get_tape_vid_inside_drive(opt_drive) + ' still inside. Ejecting...')
TapeAdmin.debug(my_name, 'running ' + cmd)
TapeAdmin.run_cmd(cmd)
else:
if opt_verbose:
TapeAdmin.debug(my_name, 'tape drive ' + opt_drive + ' has the tape ' +
TapeAdmin.get_tape_vid_inside_drive(opt_drive) + ' in an EJECTED state')
# Do previous mount checks
elif operation == 'mount':
if not TapeAdmin.is_drive_free(opt_drive):
if TapeAdmin.is_tape_ejected(opt_drive):
TapeAdmin.abort(my_name, 'tape drive ' + opt_drive + ' is not free, tape ' +
TapeAdmin.get_tape_vid_inside_drive(opt_drive) + ' is EJECTED in it')
else:
TapeAdmin.abort(my_name, 'tape drive ' + opt_drive + ' is not free, tape ' +
TapeAdmin.get_tape_vid_inside_drive(opt_drive) + ' is MOUNTED inside')
def execute_mount_unmount_command_IBM():
# Finally execute the proper command (mount/unmount)
if operation == 'mount':
cmd = cta_smc + ' -m -V ' + opt_vid + ' -D ' + TapeAdmin.get_ordinal(opt_drive)
if opt_verbose:
TapeAdmin.debug(my_name, 'running ' + cmd)
TapeAdmin.run_cmd(cmd)
if TapeAdmin.is_drive_free(opt_drive):
TapeAdmin.abort(my_name, 'drive ' + opt_drive + ' is still free after mount operation: ERROR')
tape_mounted = TapeAdmin.get_tape_vid_inside_drive(opt_drive)
if tape_mounted != opt_vid:
TapeAdmin.abort(my_name, 'wrong tape mounted inside drive ' + opt_drive +
' (expected: ' + opt_vid + '; mounted instead: ' + tape_mounted + ')')
TapeAdmin.info(my_name, 'mount of tape ' + opt_vid + ' inside drive ' + opt_drive + ': OK')
elif operation == 'unmount':
cmd = cta_smc + ' -d -V ' + opt_vid + ' -D ' + TapeAdmin.get_ordinal(opt_drive) + ' 2>/dev/null'
if opt_verbose:
TapeAdmin.debug(my_name, 'running ' + cmd)
TapeAdmin.run_cmd(cmd)
if TapeAdmin.is_drive_free(opt_drive):
TapeAdmin.info(my_name, 'unmount of tape ' + opt_vid + ' from drive ' + opt_drive + ': OK')
if not TapeAdmin.is_drive_free(opt_drive):
TapeAdmin.info(my_name, 'unmount of tape ' + opt_vid + ' inside drive ' + opt_drive + ': ERROR')
def mount_or_unmount():
if lib.startswith('IBM'):
execute_mount_unmount_command_IBM()
else:
TapeAdmin.abort(my_name, 'unknown library "' + lib + '"')
if __name__ == '__main__':
TapeAdmin.request_kinit_permissions(TapeAdmin.info)
define_mount_or_unmount_operation()
load_args()
get_drive()
do_previous_drive_checks()
if operation == 'mount':
are_tape_and_drive_in_the_same_lib, lib_tape, lib_drive = \
TapeAdmin.are_tape_and_drive_in_the_same_lib(opt_vid, opt_drive, opt_verbose)
if not are_tape_and_drive_in_the_same_lib:
TapeAdmin.abort(my_name, 'different libraries (drive=' + lib_drive + ' | tape=' + lib_tape + ')')
if opt_verbose:
TapeAdmin.debug(my_name, 'same library (' + lib_drive + '): OK')
check_tape_location_if_mount()
mount_or_unmount()
do_VOL1_check()
#!/usr/bin/perl -w
#######################################################################
#
# This script will generate /etc/cta/TPCONFIG file with the data from
# TOMS (Tape Operations Management System) URL:
#
# https://apex.cern.ch/pls/htmldb_castorns/f?p=toms_prod:250:163672298908022::NO::P250_TAPESERVER:HOSTNAME
#
# Vladimir Bahyl - 05/2019
#
#######################################################################
use strict;
use XML::DOM;
use Sys::Hostname;
use LWP::UserAgent;
use LC::Check qw(file);
#use Data::Dumper;
my $today = localtime;
my %TPCONFIG = ();
my $hostname = '';
my $tpconfigfile = '/etc/castor/TPCONFIG';
my $tpconfig = "#######################################################################
#
# CTA Tape Server Configuration file
#
# This tape server is not configured.
#
#######################################################################
#
# Generated on $today by $0
";
my $changes = 0;
($hostname = hostname()) =~ s/\.cern\.ch$//io;
my $configUrl = 'https://apex.cern.ch/pls/htmldb_castorns/f?p=toms_prod:250:163672298908022::NO::P250_TAPESERVER:HOSTNAME';
die ("$0: missing configuration URL") unless ($configUrl);
$configUrl =~ s/HOSTNAME/$hostname/o;
#
# Fetch the data
#
print("$0: Fetching the data over HTTP from the Oracle APEX database ... please be patient ...\n");
%TPCONFIG = &GetData($configUrl);
#
# Prepare the TPCONFIG file
#
my $i = 0;
while (%TPCONFIG and defined($TPCONFIG{$i}{'tapeserver'}) and (lc($TPCONFIG{$i}{'tapeserver'}) eq lc($hostname))) {
$tpconfig = "#######################################################################
#
# CTA Tape Server Configuration file
#
# unit device system control
# name group device method
" if ($i == 0);
$tpconfig .= "$TPCONFIG{$i}{'tapedrive'} $TPCONFIG{$i}{'devicegroup'} $TPCONFIG{$i}{'unixdevice'} $TPCONFIG{$i}{'controlmethod'}
# Tape Drive Comment: $TPCONFIG{$i}{'tapedrivecomment'}
# Tape Service Comment: $TPCONFIG{$i}{'tapeservicecomment'}
# Modified by: $TPCONFIG{$i}{'modifuser'}
# Modify date: $TPCONFIG{$i}{'modifdate'}
";
$i++;
}
$tpconfig .= "#
#######################################################################
#
# Generated on $today by $0
" if (%TPCONFIG and (lc($TPCONFIG{0}{'tapeserver'}) eq lc($hostname)));
# Change the TPCONFIG location if comment mentions CTA
$tpconfigfile = '/etc/cta/TPCONFIG' if (%TPCONFIG and (defined $TPCONFIG{0}{'tapeservicecomment'}) and ($TPCONFIG{0}{'tapeservicecomment'} =~ /CTA/oi));
#
# Configure TPCONFIG and SSI files
#
$changes += &UpdateFile($tpconfigfile, $tpconfig);
LC::Check::link('/etc/TPCONFIG', $tpconfigfile,
backup => '.old',
nocheck => 1,
force => 1
);
##########################################################################
sub GetData {
##########################################################################
my ($url) = @_;
my %TPCONFIG = ();
my $xmlParser = new XML::DOM::Parser;
# Download the XML formated configuration data
# Use cookies because of APEX (otherwise, nothing will be downloaded; redirection will not work)
my $UserAgent = LWP::UserAgent->new;
$UserAgent->cookie_jar({ file => undef });
my $xml = $UserAgent->get($url);
if (defined ($xml->content)) {
if ($xml->content =~/TAPESERVER/oi) {
my $xmlDoc = $xmlParser->parse($xml->content);
# pcitpdp39 ~ > lynx -source "http://oraweb.cern.ch/pls/cdbsqldev/web.show_tpconfig?p_tapeserver=tpsrv027&p_output=xml"
# <?xml version = '1.0'?>
# <TPCONFIGSEARCHLIST> <TAPESERVER NAME="tpsrv027" TAPEDRIVE="994B53A6" DEVICEGROUP="994BR5" UNIXDEVICE="/dev/nst0" DENSITY="200G" COMPRESSION="Y" INITSTATUS="DOWN" CONTROLMETHOD="acs0,3,10,6" MODEL="9940" ROBOTHOST="sunstk62" /> </TPCONFIGSEARCHLIST>
for my $i (0 .. ($xmlDoc->getElementsByTagName("TAPESERVER")->getLength())-1) {
$TPCONFIG{$i}{'tapeserver'} = $xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("NAME");
$TPCONFIG{$i}{'tapedrive'} = $xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("TAPEDRIVE");
$TPCONFIG{$i}{'devicegroup'} = $xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("DEVICEGROUP");
$TPCONFIG{$i}{'unixdevice'} = $xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("UNIXDEVICE");
$TPCONFIG{$i}{'initstatus'} = lc($xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("INITSTATUS"));
$TPCONFIG{$i}{'controlmethod'} = $xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("CONTROLMETHOD");
$TPCONFIG{$i}{'modifdate'} = $xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("MODIFDATE");
$TPCONFIG{$i}{'modifuser'} = $xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("MODIFUSER");
$TPCONFIG{$i}{'tapedrivecomment'} = $xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("TAPEDRIVECOMMENT");
$TPCONFIG{$i}{'tapeservicecomment'} = $xmlDoc->getElementsByTagName("TAPESERVER")->item($i)->getAttribute("TAPESERVICECOMMENT");
warn("$0: database entry nr. ".($i+1)." missing tape server hostname\n") unless ($TPCONFIG{$i}{'tapeserver'});
warn("$0: database entry nr. ".($i+1)." missing tape drive name\n") unless ($TPCONFIG{$i}{'tapedrive'});
warn("$0: database entry nr. ".($i+1)." missing device group name\n") unless ($TPCONFIG{$i}{'devicegroup'});
warn("$0: database entry nr. ".($i+1)." missing unix device\n") unless ($TPCONFIG{$i}{'unixdevice'});
warn("$0: database entry nr. ".($i+1)." missing init status\n") unless ($TPCONFIG{$i}{'initstatus'});
warn("$0: database entry nr. ".($i+1)." missing control method\n") unless ($TPCONFIG{$i}{'controlmethod'});
warn("$0: database entry nr. ".($i+1)." missing the modification date\n") unless ($TPCONFIG{$i}{'modifdate'});
warn("$0: database entry nr. ".($i+1)." missing user name\n") unless ($TPCONFIG{$i}{'modifuser'});
print("$0: database entry nr. ".($i+1)." no tape service comment\n") unless ($TPCONFIG{$i}{'tapeservicecomment'});
print("$0: database entry nr. ".($i+1)." no tape drive comment\n") unless ($TPCONFIG{$i}{'tapedrivecomment'});
}
$xmlDoc->dispose;
} else {
warn("$0: URL $url is not returning any usable data for $hostname. This tape server will not be configured. Please check whether there is a tape drive assigned to this tape server.\n");
}
} else {
warn("$0: URL $url doesn't seem to work. There could be a problem with the Web server or the Oracle APEX database server.\n");
}
return %TPCONFIG;
}
##########################################################################
sub UpdateFile {
##########################################################################
my ($filename, $newcontent) = @_;
my $changes = 0;
if ((-f $filename) and (-r $filename) and (-s $filename)) {
# Check the content of the file and correct it if there are some differences
$changes += LC::Check::file($filename,
source => $filename,
owner => 0,
group => 0,
mode => 0644,
backup => '.old',
code => sub {
my($oldcontent) = @_;
return() unless $oldcontent;
(my $oldfile = $oldcontent) =~ s/^#.*$//gim; # remove lines with comments
$oldfile =~ s/^\s*$//gim; # remove empty lines
(my $newfile = $newcontent) =~ s/^#.*$//gim; # remove lines with comments
$newfile =~ s/^\s*$//gim; # remove empty lines
$oldcontent = $newcontent unless ($oldfile eq $newfile);
return($oldcontent);
}
);
} else {
# The file is missing, create a new one
$changes += LC::File::file_contents($filename, $newcontent);
print "$0: created new $filename\n";
}
die ("$0: error modifying $filename\n") unless (defined($changes));
return $changes;
}
#!/usr/bin/python
"""
This script creates links to tape and medium changer devices.
The association between tape and smc device is made based on
the serial numbers stored in the TOMS DB.
"""
import re
import os
import sys
import socket
import pprint
import urllib2
import optparse
import cookielib
import subprocess
pp = pprint.PrettyPrinter(width=200)
#------------------------------------------------------------
def mklink(dev, sn, drivename, type):
if not options.noaction and drivename is not None:
link = '/dev/' + type + '_' + drivename
subprocess.Popen(['/bin/ln', '-f', '-s', dev, link]).wait()
print 'Created link', link
else:
print 'Cannot create link to ' + dev
print 'Drivename for serial number ' + sn + ' not found'
#------------------------------------------------------------
def fix_mismatch(mm_tape_dev, toms_drives, hostname):
#this fucntions assumes that there is only one mismatch,
#i.e. only one tape device with S/N not found in TOMS
#and only one drives in TOSM with S/N not found in the server.
l = []
cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
for d in toms_drives:
if d['match'] == 0:
#assigning drivename to the mismatched tape device
mm_tape_dev['drivename'] = d['drivename']
#fixing the S/N in TOMS
tomsurl = 'http://castortapeweb.cern.ch/cgi-bin/serial-nr-update.cgi?tapedrive=' + d['drivename'] + '&tapeserver=' + hostname + '&serialnumber=' + mm_tape_dev['sn']
if options.debug: print 'Opening', tomsurl
try:
urlfh = opener.open(tomsurl)
except:
print 'Cannot open ' + tomsurl
#------------------------------------------------------------
#main
#------------------------------------------------------------
# options ---------------------------------------------------
usage = "usage: %prog [options]"
parser = optparse.OptionParser(usage)
parser.add_option("-d", "--debug", action="store_true", dest="debug", help="print debug messages")
parser.add_option("--noaction", action="store_true", dest="noaction", help="do nothing")
(options, args) = parser.parse_args()
tape_devices = []
smc_devices = []
# find tape and smc devices
if options.debug: print 'Searching tape devices'
try:
p = subprocess.Popen(['/usr/bin/lsscsi', '-g'], stdout=subprocess.PIPE)
p.wait()
except:
print 'Cannot run lsscsi. Exit.'
sys.exit(0)
for line in p.stdout:
fields = line.split()
scsi_address = fields[0][1:-1]
type = fields[1]
scsi_generic = fields.pop()
scsi_tape = fields.pop()
if type == 'tape':
tape_devices.append({'scsi_address' : scsi_address, 'scsi_generic' : scsi_generic, 'scsi_tape' : scsi_tape, 'sn' : None, 'drivename' : None})
if type == 'mediumx':
smc_devices.append({'scsi_address' : scsi_address, 'scsi_generic' : scsi_generic, 'scsi_tape' : scsi_tape, 'sn' : None, 'drivename' : None})
ntpdev=len(tape_devices)
if options.debug:
print 'tape_devices:'
pp.pprint(tape_devices)
print 'smc_devices:'
pp.pprint(smc_devices)
# associate tape and smc devices
if options.debug: print 'Coupling tape and smc devices (if any)'
pairs = []
for tapedev in tape_devices:
for smcdev in smc_devices:
if tapedev['scsi_address'][:-2] == smcdev['scsi_address'][:-2]:
pairs.append([tapedev, smcdev])
if options.debug:
print 'pairs:'
pp.pprint(pairs)
if len(tape_devices)>len(smc_devices) and len(smc_devices)>0:
print 'Number of control paths lower that number of drives'
sys.exit(0)
# find the serial number of the tape device
# sg_inq will not work if the /dev/sgX device is not reachable
# sg_inq will not work if the /dev/nstY device is being used
# run sg_inq against the nst dev so that if it is already being used we exit
if options.debug: print 'Reading serial numbers from tape devices'
for tapedev in tape_devices:
tapedn = '/dev/nst'+tapedev['scsi_tape'][-1]
p = subprocess.Popen(['/usr/bin/sg_inq', tapedn], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if p.wait() != 0:
print 'Cannot run sg_inq on ' + tapedn + '. Exit'
sys.exit(0)
else:
for line in p.stdout:
#reg = re.search('(?<=Unit serial number: )(\d+)', line)
if re.search('Unit serial number', line):
l = line.split(':')
tapedev['sn'] = l[1][1:-1]
if tapedev['sn'] is None:
print 'Could not extract the serial number from the output of sg_inq ' + tapedn
sys.exit(0)
if options.debug:
print 'tape_devices:'
pp.pprint(tape_devices)
# search the drive names in toms by serial number
toms_drives = []
if options.debug: print 'Looking into TOMS for drive names'
hostname = socket.gethostname().split('.')[0]
tomsurl = 'https://apex.cern.ch/pls/htmldb_castorns/f?p=toms_prod:250:::NO::P250_TAPESERVER:' + hostname
if options.debug: print 'Opening', tomsurl
cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
try:
urlfh = opener.open(tomsurl)
except:
print 'Cannot open ' + tomsurl
sys.exit(0)
for line in urlfh:
if options.debug: print line
if not re.search('TAPESERVER', line): continue