#!/usr/bin/perl -w # This script is a GIP provider (and not a plugin) generating information # related to static space tokens configured on the local DPM server. # It must be invoked by a wrapper in /opt/lcg/var/gip/providers with # the database connection file as a parameter # # Written by Michel Jouvin - LAL / CNRS - 19/9/07 use strict; use DBI; use Net::Domain qw(hostfqdn hostdomain); # Debug flag my $debug = 0; # DPM server certificate my $dpmsrv_cert_file = "/etc/grid-security/dpmmgr/dpmcert.pem"; my $dpmsrv_cert_subj; # Mds-Vo-Name used for published DN # Use local on gLite 3.0, resource on gLite 3.1 my $mds_vo_name = "resource"; #my $mds_vo_name = "local"; # DPM domain name (under /dpm) my $dpm_domain = `hostname -d`; chomp $dpm_domain; # Hashes for space tokens attributes my (%st_descr, %st_gid, %st_uid, %st_client_dn, %st_total_space, %st_free_space); my ($db_user, $db_pw, $db_host, $dpns_db_name, $dpm_db_name, $db_port); my $dpns_db_h; my $gb_factor = 1000000; my $this_host = hostfqdn(); my $this_domain = hostdomain(); sub usage() { die "Usage: $0 [--debug|--verbose] DB_CONFIG_FILE\n\n", " DB_CONFIG_FILE is usually /opt/lcg/etc/DPMINFO\n", " Use --debug flag only for testing.\n"; } sub parse_db_config { my $conf_file = $_[0]; open(DB_CONF, $conf_file) || die "Database configuration file $conf_file cannot be read: " . $! . "\n"; my $conf = ; chomp $conf; # Optional match for db name if ($conf =~ s/\/(\w+)$//) { $dpns_db_name = $1; $dpm_db_name = $1; } else { $dpns_db_name = "cns_db"; $dpm_db_name = "dpm_db"; } # Optional port match if ($conf =~ s/:(\d+)//) { $db_port = $1; } else { $db_port = getservbyname("mysql", "tcp"); } if ($conf =~ /^(\w+)\/([^@]+)@([A-Za-z0-9\.-]+)$/) { $db_user = $1; $db_pw = $2; $db_host = $3; } else { die "Failed to interpret database configuration file. Format should\n", "be a single line:\n", "USER/PASSWORD\@HOST[:PORT][/DPNS_DB_NAME]\n", "Port is optional (defaults to mysql port in services).\n", "Database name is optional (defaults to cns_db).\n"; } print STDERR "DB User: $db_user\nDB Password: $db_pw\nDB Host: $db_host\n", "DPNS DB: $dpns_db_name\n\n" if $debug; close DB_CONF; } # Function to get VO name from virtual GID sub dpns_get_group_name($) { my $gid = shift; unless ( $gid ) { die "Missing required parameter : GID\n"; return; } my $group_name; # Connect to DB if not yet opened unless ( $dpns_db_h ) { my $dsn = "DBI:mysql:database=$dpns_db_name;host=$db_host;port=$db_port"; $dpns_db_h = DBI->connect($dsn, $db_user, $db_pw); if (!$dpns_db_h) { die "Failed to connect to MySQL server. Check username/password.\n"; } } # Execute SQL query against DPNS to retrieve the VO name from the GID. my $sth = $dpns_db_h->prepare("Select groupname from Cns_groupinfo where gid='$gid'"); if (!$sth) { die "Failed to prepare MySQL query ($dpns_db_name): " . $sth->errstr . "\n"; } if (!$sth->execute) { die "Failed to execute MySQL query ($dpns_db_name): " . $sth->errstr . "\n"; }; print STDERR "SQL Debug ($dpns_db_name): query returned ", $sth->{'NUM_OF_FIELDS'}, " fields.\n" if $debug; my $names = $sth->{'NAME'}; my $numFields = $sth->{'NUM_OF_FIELDS'}; for (my $i = 0; $i < $numFields; $i++) { printf STDERR ("%s%s", $i ? "," : "", $$names[$i]) if $debug; } print STDERR "\n" if $debug; while (my $ref = $sth->fetchrow_arrayref) { $group_name = $$ref[0]; for (my $i = 0; $i < $numFields; $i++) { printf STDERR ("%s%s", $i ? "," : "", $$ref[$i]) if $debug; } print STDERR "\n" if $debug; } $sth->finish(); print STDERR "\n" if $debug; return $group_name; } # Function to query DPM database about defined space tokens. # Information is returned in a set of hashed indexed by space token guid. sub dpm_space_tokens_query() { # Execute SQL query against DPM to retrieve defined space tokens my $dsn = "DBI:mysql:database=$dpm_db_name;host=$db_host;port=$db_port"; my $dbh = DBI->connect($dsn, $db_user, $db_pw); if (!$dbh) { die "Failed to connect to MySQL server. Check username/password.\n"; } # Select only entries with client DN = local server (static reservations) my $sth = $dbh->prepare("Select s_token,u_token,s_gid,s_uid,client_dn,g_space,u_space from dpm_space_reserv where client_dn='$dpmsrv_cert_subj'"); if (!$sth) { die "Failed to execute MySQL query ($dpm_db_name): " . $sth->errstr . "\n"; } if (!$sth->execute) { die "Failed to execute MySQL query ($dpm_db_name): " . $sth->errstr . "\n"; }; print STDERR "SQL Debug ($dpm_db_name): query returned ", $sth->{'NUM_OF_FIELDS'}, " fields.\n" if $debug; my $names = $sth->{'NAME'}; my $numFields = $sth->{'NUM_OF_FIELDS'}; for (my $i = 0; $i < $numFields; $i++) { printf STDERR ("%s%s", $i ? "," : "", $$names[$i]) if $debug; } print STDERR "\n" if $debug; while (my $ref = $sth->fetchrow_arrayref) { $st_descr{$$ref[0]} = $$ref[1]; $st_gid{$$ref[0]} = $$ref[2]; $st_uid{$$ref[0]} = $$ref[3]; $st_client_dn{$$ref[0]} = $$ref[4]; $st_total_space{$$ref[0]} = $$ref[5]; $st_free_space{$$ref[0]} = $$ref[6]; for (my $i = 0; $i < $numFields; $i++) { printf STDERR ("%s%s", $i ? "," : "", $$ref[$i]) if $debug; } print STDERR "\n" if $debug; } $sth->finish(); print STDERR "\n" if $debug; } sub init_x509_proxy() { # Try and get a host proxy based on my EUID. my $user = getpwuid($>) || $ENV{USER}; print STDERR "Plugin user is $user.\n" if $debug; $ENV{LCG_LOCATION} = "/opt/lcg" unless defined $ENV{LCG_LOCATION}; if ( -r "$ENV{LCG_LOCATION}/hostproxy.$user" ) { $ENV{X509_USER_PROXY} = "$ENV{LCG_LOCATION}/hostproxy.$user"; print STDERR "Found X509 proxy $ENV{X509_USER_PROXY}.\n" if $debug; } elsif ( -r "$ENV{LCG_LOCATION}/var/hostproxy.$user" ) { $ENV{X509_USER_PROXY} = "$ENV{LCG_LOCATION}/var/hostproxy.$user"; print STDERR "Found X509 proxy $ENV{X509_USER_PROXY}.\n" if $debug; } else { print STDERR "No X509 proxy found ($ENV{LCG_LOCATION}/hostproxy.$user).\n" if $debug; } } # Method retrieving certificate subject from X509 certificate and returning it # in a format suitable for VOMS. sub getCertSubject ($) { my $cert = shift; unless ( $cert ) { die "Missing required parameter : certificate\n"; return; } # Extract the subject name using OpenSSL to help avoid # typos. my $subject = `cat "$cert" | openssl x509 -noout -subject`; if ($?) { die "Cannot extract subject name for certificate $cert\n"; return(undef); } # Remove the subject label at the beginning of the line. if ( $subject =~ m/\w+=\s*(.*)$/ ) { $subject = $1; } else { die "Invalid subject name in certificate\n"; return(undef); } # Fix up the email address part (emailAddress -> Email). $subject =~ s/emailAddress/Email/g; return($subject); } # Convert size into kilobytes. sub convert{ $_= shift; # Strip off suffix and store # Note we have asked DPM for SI units in dpm-qryconf my $suffix = "k"; $suffix = $1 if s/([PTGMkK])//; if ($suffix eq "P") { $_ *= 1000000000000.0; } elsif ($suffix eq "T") { $_ *= 1000000000.0; } elsif ($suffix eq "G") { $_ *= 1000000.0; } elsif ($suffix eq "M") { $_ *= 1000.0; } elsif ($suffix eq "k" || $suffix eq "K") { ;# Do nothing } else { warn "Failed to parse suffix from dpm-qryconf output.\n"; } return $_; } # Main starts here if ($ARGV[0] =~ /^(--verbose|--debug)$/) { shift; $debug = 1; } usage() if $#ARGV != 0; # Prepare chomp(my $host=`hostname -f`); my $GlueSEUniqueID = $host; init_x509_proxy(); parse_db_config($ARGV[0]); $dpmsrv_cert_subj = getCertSubject($dpmsrv_cert_file); unless ( $dpmsrv_cert_subj ) { die "Failed to get certificate subject of DPM server"; } print "DPM server certificate subject = $dpmsrv_cert_subj\n" if $debug; ## Get VO information from each source dpm_space_tokens_query(); # Now go through each VO and calculate available space if ( %st_descr ) { foreach my $space_token (keys(%st_descr)) { print "Processing space token $st_descr{$space_token} ($space_token)...\n" if $debug; my $group_name = dpns_get_group_name($st_gid{$space_token}); if ( ! $group_name ) { print STDERR "Error getting group name for virtual GID $st_gid{$space_token}\n"; next; } my @toks = split /\//, $group_name; my $vo_name = $toks[0]; my $voinfo_id = "$vo_name:$st_descr{$space_token}"; my $sa_id = "$voinfo_id:online"; my $acbr; if ( @toks == 1 ) { $acbr = "VO:$vo_name"; } else { $acbr = "VOMS:/$group_name"; } my $st_total_space = $st_total_space{$space_token}; my $st_free_space = $st_free_space{$space_token}; my $st_used_space = $st_total_space - $st_free_space; print "dn: GlueSALocalID=$sa_id,GlueSEUniqueID=$this_host,mds-vo-name=$mds_vo_name,o=grid\n" . "objectClass: GlueSATop\n" . "objectClass: GlueSA\n" . "objectClass: GlueSAPolicy\n" . "objectClass: GlueSAState\n" . "objectClass: GlueSAAccessControlBase\n" . "objectClass: GlueKey\n" . "objectClass: GlueSchemaVersion\n" . "GlueSARoot: $vo_name:/dpm/$dpm_domain/home/$vo_name\n" . "GlueSAPath: /dpm/$dpm_domain/home/$vo_name\n" . "GlueSAType: permanent\n" . "GlueSALocalID: $sa_id\n" . "GlueSAName: Replica online storage for VO $vo_name\n" . "GlueSATotalOnlineSize: " . int($st_total_space/$gb_factor) . "\n" . "GlueSAUsedOnlineSize: " . int($st_used_space/$gb_factor) . "\n" . "GlueSAFreeOnlineSize: " . int($st_free_space/$gb_factor) . "\n" . "GlueSAReservedOnlineSize: 0\n" . "GlueSATotalNearlineSize: 0\n" . "GlueSAUsedNearlineSize: 0\n" . "GlueSAFreeNearlineSize: 0\n" . "GlueSAReservedNearlineSize: 0\n" . "GlueSARetentionPolicy: replica\n" . "GlueSAAccessLatency: online\n" . "GlueSAExpirationMode: neverExpire\n" . "GlueSACapability: unset\n" . "GlueSAPolicyMaxFileSize: 10000\n" . "GlueSAPolicyMinFileSize: 1\n" . "GlueSAPolicyMaxData: 100\n" . "GlueSAPolicyMaxNumFiles: 10\n" . "GlueSAPolicyMaxPinDuration: 10\n" . "GlueSAPolicyQuota: 0\n" . "GlueSAPolicyFileLifeTime: permanent\n" . "GlueSAStateAvailableSpace: $st_free_space\n" . "GlueSAStateUsedSpace: $st_used_space\n" . "GlueSAAccessControlBaseRule: $vo_name\n" . "GlueChunkKey: GlueSEUniqueID=$this_host\n" . "GlueSchemaVersionMajor: 1\n" . "GlueSchemaVersionMinor: 3\n" . "\n"; print "dn: GlueVOInfoLocalID=$voinfo_id,GlueSALocalID=$vo_name,GlueSEUniqueID=$this_host,mds-vo-name=$mds_vo_name,o=grid\n" . "objectClass: GlueSATop\n" . "objectClass: GlueVOInfo\n" . "objectClass: GlueKey\n" . "objectClass: GlueSchemaVersion\n" . "GlueVOInfoLocalID: $voinfo_id\n" . "GlueVOInfoName: $voinfo_id\n" . "GlueVOInfoPath: /dpm/$this_domain/home/$vo_name\n" . "GlueVOInfoTag: $st_descr{$space_token}\n" . "GlueVOInfoAccessControlBaseRule: $acbr\n" . "GlueChunkKey: GlueSALocalID=$sa_id\n" . "GlueChunkKey: GlueSEUniqueID=$this_host\n" . "GlueSchemaVersionMajor: 1\n" . "GlueSchemaVersionMinor: 3\n" . "\n"; } } exit 0;