# -*-perl-*-

# $Id: OsMap.plm,v 1.2 2005/09/14 20:08:56 thomas Exp $

# $Log: OsMap.plm,v $
# Revision 1.2  2005/09/14 20:08:56  thomas
# add get_attrs for arbitrary attribute support
#
# Revision 1.1  2005/05/26 18:33:07  thomas
# general-purpose module to map csdb to/from afs OS names
#
# Revision 1.1  2005/05/26 18:32:23  thomas
# general-purpose module to map csdb to/from afs OS names
#

=head1 NAME

B<OsMap.pm> - Map OS names between csdb and afs

=head1 SYNOPSIS

  use CSL::What;
  use What::OsMap;

  my $map = new What::OsMap (%opts);

  my ($afs, $csdb, @csdb, @afs, $attr_type);

  @csdb = $map->get_csdb ();
  $csdb = $map->get_csdb ($afs);

  @afs = $map->get_afs ();
  $afs = $map->get_afs ($csdb);

  $attrs = $map->get_attrs (csdb=>$csdb);
  $attrs = $map->get_attrs (afs=>$afs);


=head1 DESCRIPTION

This module converts operating system names between the conventions
used by the inventory database and the afs file system.

The only currently recognized attribute of B<%opts> is B<mapfile>,
which points to the map file to use for the conversion.  If no mapfile
is specified, the default mapfile (/s/what-0.1.31/xml/osmap.xml) is used.

=head1 MAP FILE FORMAT

The map file is in XML format, a sample of which is listed below:

 <what>

  <map csdb="AIX 5" sys="rs_aix51" />
  <map csdb="tao 1.0" sys="i386_tao10" />
  <map csdb="a64tao 1.0" sys="amd64_tao10" />
  <map csdb="Cent 4.0" sys="i386_cent40" />
  <map csdb="XEROX" sys="xrx_phaser" />
  <map csdb="xeroxcolor" sys="xrx_color" />
 . . .
  <default sys="unknown" />

 </what>

Each B<map> section contains and csdb tag and the corresponding afs
sysname.  An optional B<default> tag gives the value to return for a
search on a csdb or afs name for which there is no B<map> tag present.

=head1 FILES

/s/what/xml/osmap.xml

=head1 METHODS

The methods available in this module follow.

=over 4

=cut

package What::OsMap;

use strict;
use XML::Simple;

###############################################################################

=item $map = new What::OsMap (%opts);

Create a new mapping object.  This loads data from either the
default map file, or the file specified by the B<mapfile> attribute of
the B<%opts> hash.

=cut

sub new {
  my $ref = shift;

  my $self = bless {} => $ref;
  my $map;

  $self->{mapfile} = $ref->{mapfile} if (ref ($ref) && defined ($ref->{mapfile}));

  $self->{mapfile} = '/s/what-0.1.31/xml/osmap.xml' unless (defined $self->{mapfile});

# Make perl shut up about undefined values.
  # Load the data on the first query.
  $self->{mtime} = 0;

  $self;
}

sub check_reload {
  my $self = shift;

  my $mtime = (stat $self->{mapfile})[9];
  return if $mtime == $self->{mtime};
  $self->{mtime} = $mtime;

  # Read the map
  $self->{xml} = XMLin ($self->{mapfile});
  warn "Failed to read $self->{mapfile}: $@\n" unless defined $self->{xml};

  # Work through entries in the XML
  if (defined ($self->{xml}->{default}->{sys})) {
    $self->{default} = $self->{xml}->{default}->{sys};
  }
  if (defined ($self->{xml}->{map}) && ref ($self->{xml}->{map}) eq 'ARRAY') {
    foreach (@{$self->{xml}->{map}}) {
      $self->{afs}->{$_->{sys}} = $_->{csdb};
      $self->{csdb}->{$_->{csdb}} = $_->{sys};
      $self->{afsplus}->{$_->{sys}} = $_;
      $self->{csdbplus}->{$_->{csdb}} = $_;
    }
  }
  delete $self->{xml};
  $self;
}


###############################################################################

=item @csdb = map->get_csdb ();

=item $csdb = map->get_csdb ($afs);

Without an argument, B<get_csdb> returns all the csdb names present in
the map.  With an argument, it return the csdb name for the passed afs
name.

=cut

sub get_csdb {
  my $self = shift;

  $self->check_reload;

  if (@_) {
    my $afs = shift;
    if (defined $self->{afs}->{$afs}) {
      return $self->{afs}->{$afs};
    } elsif (defined $self->{default}) {
      return $self->{default};
    } else {
      return undef;
    }
  }
  return sort keys %{$self->{csdb}};
}


###############################################################################

=item @afs = map->get_afs ();

=item $afs = map->get_afs ($csdb);

Without an argument, B<get_afs> returns all the afs names present in
the map.  With an argument, it return the afs name for the passed csdb
name.

=cut

sub get_afs {
  my $self = shift;

  $self->check_reload;

  if (@_) {
    my $csdb = shift;
    if (defined $self->{csdb}->{$csdb}) {
      return $self->{csdb}->{$csdb};
    } elsif (defined $self->{default}) {
      return $self->{default};
    } else {
      return undef;
    }
  }
  return sort keys %{$self->{afs}};
}


###############################################################################

=item $attrs = $map->get_attrs (csdb=>$csdb);

=item $attrs = $map->get_attrs (afs=>$afs);

This method returns all attributes of the requested os, referenced
by either its csdb or afs name.  If the os is found, the returned hashref
will contain at least two elements (the csdb and afs names for the OS).

The B<undef> value will be returned if the OS cannot be found.

=cut

sub get_attrs {
  my $self = shift;
  my $params = {@_};

  $self->check_reload;

  if (defined $params->{csdb}) {
    # Lookup by csdb
    if (defined $self->{csdbplus}) {
      return $self->{csdbplus}->{$params->{csdb}};
    }
    return undef;
  }
  if (defined $params->{afs}) {
    if (defined $self->{afsplus}) {
      return $self->{afsplus}->{$params->{afs}};
    }
    return undef;
  }

  return undef;
}

###############################################################################
# Finish pod

=back

=cut

###############################################################################

1;
