#
# Copyright 2001-2003 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
#ident	"@(#)Rg.pm	1.45	03/02/26 SMI"
#
# Resource Group class

package Cluster::Rg;
use strict;
use Cluster::RunCommand;
use Cluster::RBAC;
use Cluster::Common;
use Sun::Solaris::Utils qw(gettext);
use vars qw(@ISA $VERSION);
$VERSION = '1.00';
@ISA = qw(Cluster::Common);

##############################################################################
#
# Class constructor
#
##############################################################################

sub new {
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $self  = {};
	$self->{NAME} = shift;
	bless ($self, $class);
	return $self;
}

##############################################################################
#
# Class variables
#
##############################################################################

# Create a rbac object
my $rbac = new Cluster::RBAC;

my $PFEXEC = "/usr/bin/pfexec";
my $cmd = new Cluster::RunCommand;
my $rg_view = '/cgi-bin/rg/rg_view.pl';
my $rg_config = '/cgi-bin/rg/rg_config.pl';
my $rg_add = '/cgi-bin/rg/rg_add.pl';
my $rs_add = '/cgi-bin/rg/rs_add.pl';

my @status_icons = (
	["OFFLINE", "Offline", "critical"], # gettext("Offline")
	["STARTING", "Starting", "minor"], # gettext("Starting")
	["STOPPING", "Stopping", "critical"], # gettext("Stopping")
	["ONLINE_SOMEWHERE", "Offline", "ok"], # gettext("Offline")
	["ONLINE", "Online", "ok"], # gettext("Online")
	["ONLINE_FAULTED", "Online faulted", "critical"], # gettext("Online faulted")
	["START_FAILED", "Start failed", "critical"], # gettext("Start failed")
	["STOP_FAILED", "Stop failed", "critical"], # gettext("Stop failed")
	["MONITOR_FAILED", "Monitor failed", "critical"], # gettext("Monitor failed")
	["ONLINE_NOT_MONITORED", "Online (not monitored)", "major"], # gettext("Online (not monotored)")
	["DETACHED", "Detached", "minor"], # gettext("Detached")
	["UNMANAGED", "Unmanaged", "minor"], # gettext("Unmanaged")
	["PENDING_ONLINE", "Pending online", "minor"], # gettext("Pending online")
	["PENDING_OFFLINE", "Pending offline", "minor"], # gettext("Pending offline")
	["ERROR_STOP_FAILED", "Stop failed", "critical"] # gettext("Stop failed")
);

my @yesno_status_icons = (
	["yes", "Yes", "ok"], # gettext("Yes")
	["no", "No", "critical"] # gettext("No")
);

my @managed_status_icons = (
	["managed", "Managed", "ok"], # gettext("Managed")
	["unmanaged", "Unmanaged", "critical"] # gettext("Unmanaged")
);

##############################################################################
#
# Class methods
#
##############################################################################

sub get_rglist {
	my ($self) = @_;

	my @rglist = $cmd->scha_cluster_get("-O all_resourcegroups");
	my @rgs;
    
	foreach my $rg (@rglist) {
		if (defined $rg) {
			chomp $rg;
		    
			if ($rg ne "") {
				push @rgs, $rg;
			}
		}
	}		
    
	return sort @rgs;
}

sub get_num_rgs ($) {
	my ($self) = @_;
	my @rglist = $self->get_rglist();
	return ($#rglist + 1);
}


sub get_rslist ($$) {
    my ($self, $rg) = @_;
    my @output = $cmd->scha_resourcegroup_get("-O resource_list -G $rg");
    my @rslist;

    foreach my $rs (@output) {
	if (defined $rs) {
	    chomp $rs;
	    
	    if ($rs ne "") {
		push @rslist, $rs;
	    }
	}
    }

    return sort @rslist;
}

#
# Get the list of nodes where this rg is registered
#

sub get_rg_nodelist ($$) {
    my ($self, $rg) = @_;
    my @output = $cmd->scha_resourcegroup_get("-O nodelist -G $rg");
    my @nodelist;

    foreach my $node (@output) {
	if (defined $node) {
	    chomp $node;
	    
	    if ($node ne "") {
		push @nodelist, $node;
	    }
	}
    }

    return sort @nodelist;
}

#
# Get the aggregated state with the list of nodes on which
# the object is online
#

sub get_aggregated_state ($$) {

	my ($self, %node_state) = @_;
	my ($state, @state_list);

	# check if the object is ONLINE at least on one node

	while((my $n, my $s) = each %node_state) {
		$s = uc($s);
		if ($s eq "ONLINE") {
			push @state_list, $n;		
		}
	}
    	if ($#state_list > -1) {
		$state = "ONLINE";
    	} else {
		$state = "OFFLINE";
		while((my $n, my $s) = each %node_state) {
			$s = uc($s);
			if ( $s ne "OFFLINE" ) {
				if ( $state eq "OFFLINE") {
					# change the state ONLY if it was OFFLINE
					$state = $s;
				}
			}
		}	
    	}

    	return ($state, @state_list);	
}


#
# Get the rg state on each node
#
sub get_rg_state_nodelist ($$) {
    my ($self, $rg) = @_;
    my (@rg_nodelist, %node_state);

    @rg_nodelist = $self->get_rg_nodelist($rg);
    %node_state = ();

    foreach my $node (@rg_nodelist) {
	my $rg_state = $self->get_rg_state_node($rg, $node);
	chomp $rg_state;
	$node_state{$node} = $rg_state;
    }

    return (%node_state);
}

#
# Get the aggregated rg state, along with the list of nodes on which
# the specified RG is in this state
#

sub get_aggregated_rg_state_nodelist ($$) {
    my ($self, $rg) = @_;
    my (@rg_nodelist, %node_state);

    %node_state = $self->get_rg_state_nodelist($rg);

    my ($state, @state_list) = 
	$self->get_aggregated_state(%node_state);

    return ($state, @state_list);
}

#
# Get the aggregated resource state, along with the list of nodes on
# which the specified resource is in this state
#

sub get_rs_state_online_nodelist ($$$) {
    my ($self, $rg, $rs) = @_;
    my (@rs_nodelist, %node_state);

    # Find out where its containing rg is registered
    @rs_nodelist = $self->get_rg_nodelist($rg);
    foreach my $node (@rs_nodelist) {
	my $rs_state = $self->get_rs_state_node($rg, $rs, $node);
	chomp $rs_state;
	$node_state{$node} = $rs_state;
    }

    my ($state, @state_list) = 
	$self->get_aggregated_state (%node_state);

    return ($state, @state_list);
}

#
# Return a hash of the nodes and states of the resource on them
#

sub get_rs_state_nodelist ($$$) {
    my ($self, $rg, $rs) = @_;
    my (@online_list, @rs_nodelist, $state, %results);

    # Find out where its containing rg is registered
    @rs_nodelist = $self->get_rg_nodelist($rg);
    foreach my $node (@rs_nodelist) {
	my $rs_state = $self->get_rs_state_node($rg, $rs, $node);
	$results{$node} = $rs_state;
    }

    return (%results);
}

#
# Get the list of nodes on which the specified RG is online
#

sub get_rg_online_nodelist ($$) {
    my ($self, $rg) = @_;
    my (@online_list, @rg_nodelist);

    @rg_nodelist = $self->get_rg_nodelist($rg);
    foreach my $node (@rg_nodelist) {
	if ($self->get_rg_state_node($rg, $node) eq "ONLINE") {
	    push @online_list, $node;
	}
    }

    return @online_list;
}

#
# Get the state of the specified RG on the specified node
#

sub get_rg_state_node ($$$) {
    my ($self, $rg, $node) = @_;
    my ($status) = $cmd->scha_resourcegroup_get("-O rg_state_node " .
						"-G $rg $node");
    return $status;
}

#
# Get the state of the specified RS on the specified node
#

sub get_rs_state_node ($$$$) {
    my ($self, $rg, $rs, $node) = @_;
    my ($status) = $cmd->scha_resource_get("-O resource_state_node " .
					   "-R $rs -G $rg $node");
    return $status;
}

sub member {
    my ($item, @array) = @_;

    # Return position of element if we find it
    for (my $i = 0; $i <= $#array; $i++) {
        if ($item eq $array[$i]) {
            return $i;
        }
    }

    # return -1 if we didn't find element
    return -1;
}

##############################################################################
#
# Updated Cluster Manager Look and Feel functions.
#
# See http://spgweb.eng/hci/behret/sc3x-cm/ for more information.
#
##############################################################################

#
# Get the action bar links for resource groups
#

sub rg_action_links($$$) {
	my ($self, $type, $name) = @_;

	# Create the actions menu links
        my @links = ();

	# Add if the user has solaris.cluster.resource.admin
	if ($rbac->check_auth($CL_AUTH_RESOURCE_ADMIN)) {
		push (@links, gettext("Bring Online/Switch Primaries..."),
		     "$rg_config?func=online\&$type=$name",

		      gettext("Take Group Offline..."),
		      "$rg_config?func=offline\&$type=$name",

		      gettext("Restart Group..."),
		      "$rg_config?func=restart\&$type=$name",

		      gettext("---------------------"),
		      $rg_view,

		      gettext("Manage Group..."),
		      "$rg_config?func=manage\&$type=$name",

		      gettext("Unmanage Group..."),
		      "$rg_config?func=unmanage\&$type=$name",

		      gettext("---------------------"),
		      $rg_view);
	}

	# Add if the user has solaris.cluster.resource.modify
	if ($rbac->check_auth($CL_AUTH_RESOURCE_MODIFY)) {
		push (@links, gettext("Create New Group..."),
		     $rg_add,

		      gettext("Delete Group..."),
		      "$rg_config?func=delete\&$type=$name",

		      gettext("Modify Group Properties..."),
		      "$rg_config?func=editrg\&$type=$name",

		      gettext("---------------------"),
		      $rg_view);
	}

	# Add if the user has solaris.cluster.resource.admin
	if ($rbac->check_auth($CL_AUTH_RESOURCE_ADMIN)) {
		push (@links, gettext("Enable Resource..."),
		      "$rg_config?func=enable\&$type=$name",

		      gettext("Disable Resource..."),
		      "$rg_config?func=disable\&$type=$name",

		      gettext("---------------------"),
		      $rg_view,

		      gettext("Enable Resource Monitor..."),
		      "$rg_config?func=enable_mon\&$type=$name",

		      gettext("Disable Resource Monitor..."),
		      "$rg_config?func=disable_mon\&$type=$name",

		      gettext("---------------------"),
		      $rg_view);
	}

	# Add if the user has solaris.cluster.resource.modify
	if ($rbac->check_auth($CL_AUTH_RESOURCE_MODIFY)) {
		push (@links, gettext("Modify Resource Properties..."),
		     "$rg_config?func=edit\&$type=$name",

		      gettext("Modify Load Balancing..."),
		      "$rg_config?func=weights\&$type=$name");
	}

	# Add if the user has solaris.cluster.resource.admin
	if ($rbac->check_auth($CL_AUTH_RESOURCE_ADMIN)) {
		push (@links, gettext("Clear Stop-Failed Flag..."),
		      "$rg_config?func=clear\&$type=$name");
	}

	# Add if the user has resource.admin or resource.modify
	if ($rbac->check_auth($CL_AUTH_RESOURCE_MODIFY) ||
	    $rbac->check_auth($CL_AUTH_RESOURCE_ADMIN)) {
		push (@links, gettext("---------------------"),
		      $rg_view);
	}

	# Add if the user has solaris.cluster.resource.modify
	if ($rbac->check_auth($CL_AUTH_RESOURCE_MODIFY)) {
		push (@links, gettext("Create New Resource..."),
		      "$rs_add?$type=$name",

		      gettext("Delete Resource..."),
		      "$rg_config?func=deleters\&$type=$name");
	}

	return @links;
}

sub rg_action_helpMarkers ($$) {
	my ($self, $q) = @_;
	my @action_helpMarkers = ();

	# Add if the user has solaris.cluster.resource.admin
	if ($rbac->check_auth($CL_AUTH_RESOURCE_ADMIN)) {
		push (@action_helpMarkers, "rgpm-online",
		      "rgpm-offline",
		      "rgpm-restart",
		      "separator",
		      "rgpm-manage",
		      "rgpm-unmanage",
		      "separator");
	}

	# Add if the user has solaris.cluster.resource.modify
	if ($rbac->check_auth($CL_AUTH_RESOURCE_MODIFY)) {
		push (@action_helpMarkers, "rgpm-create-group",
		      "rgpm-delete-group",
		      "rgpm-modify-group",
		      "separator");
	}

	# Add if the user has solaris.cluster.resource.admin
	if ($rbac->check_auth($CL_AUTH_RESOURCE_ADMIN)) {
		push (@action_helpMarkers, "rgpm-enable-resource",
		      "rgpm-disable-resource",
		      "separator",
		      "rgpm-enable-resmonitor",
		      "rgpm-disable-resmonitor",
		      "separator");
	}

	# Add if the user has solaris.cluster.resource.modify
	if ($rbac->check_auth($CL_AUTH_RESOURCE_MODIFY)) {
		push (@action_helpMarkers, "rgpm-resource-properties",
		      "rgpm-resource-weights");
	}

	# Add if the user has solaris.cluster.resource.admin
	if ($rbac->check_auth($CL_AUTH_RESOURCE_ADMIN)) {
		push (@action_helpMarkers,
		      "rgpm-resource-clear-stop-failed");
	}
	
	# Add if the user has resource.admin or resource.modify
	if ($rbac->check_auth($CL_AUTH_RESOURCE_MODIFY) ||
	    $rbac->check_auth($CL_AUTH_RESOURCE_ADMIN)) {
		push (@action_helpMarkers, "separator");
	}

	# Add if the user has solaris.cluster.resource.modify
	if ($rbac->check_auth($CL_AUTH_RESOURCE_MODIFY)) {
		push (@action_helpMarkers, "rgpm-create-resource",
		      "rgpm-delete-resource");
	}

	return @action_helpMarkers;
}


#	
# Print the resource groups table
#

sub resource_groups_table($$) {
	my ($self, $q) = @_;

	# Set the headers for the resource group status table. 
	my @rgheaders = (gettext("Name"), "33%",
			 "&nbsp;", "14",
			 gettext("Status"), "20%",
			 gettext("Current Primaries"), "47%");

	# Determine the number of columns from the headers
	my $numcols = ($#rgheaders + 1) / 2;

	# Start the property table
	$q->start_prop_table(gettext("Resource Groups"), \@rgheaders,
		"/images/rsrcgrps_16.gif");
    
	# Get the rg state and online nodelist
	my @rglist = $self->get_rglist();
    
	# If we don't have any resource groups, print a message
	if ($#rglist == -1) {
	
		# Start a table row and cell with the colspan of the table
		$q->start_prop_tr();
		$q->start_prop_td({ COLSPAN => 4 });
	
		# Print the message in table-label-text style
		$q->start_table_text('table-label-text');
		print gettext("No resource groups have been configured " .
			      "on this cluster");
		$q->end_table_text();

		# End the cell and row
		$q->end_prop_td();
		$q->end_prop_tr();
	}

	# Cycle through the list of groups and print the status
	for (my $i=0; $i<=$#rglist; $i++) {
	
		# Get the node status
		my ($rgstate, @online_nodelist) = 
			$self->get_aggregated_rg_state_nodelist($rglist[$i]);

		# Start a table row for the node
		$q->start_prop_tr();

		# Print a table cell for the nodename
		$q->start_prop_td();

		# This function will print an icon and a link. It
		# takes the label text, the alt tag text, the link to
		# point to, and the icon to display before the text.
		$q->table_link_text($rglist[$i], gettext("Resource Group"),
				    "/cgi-bin/rg/rg_view.pl?rg=$rglist[$i]");
	
		$q->end_prop_td();

		# Print a table cell for the status
		$q->start_prop_td();

		# This function will print the correctly-colored &
		# styled text for the given status.
		$q->table_status_text($rgstate, \@status_icons);

		# End the table cell and table row
		$q->end_prop_td();

		# Start the current primaries table cell
		$q->start_prop_td();

		# Start table-normal-text printing of the online nodelist
		$q->start_table_text('table-normal-text');

		# Create the string of online nodes
		my $nodelist_string;
		if (defined $online_nodelist[0]) {
			$nodelist_string = join ', ', @online_nodelist;
		} else {
			$nodelist_string = "&nbsp";
		}
		print $nodelist_string;

		# End the online nodelist style
		$q->end_table_text();

		# End the cell and row
		$q->end_prop_td();
		$q->end_prop_tr();

		# If we're not on our last row, print the line row divider
		if ($i < $#rglist) {
			$q->line_row($numcols);
		}
	}

	# End the node status table
	$q->end_prop_table();
}

#
# Print the properties of the selected resource group
#

sub resource_group_properties_table($$$$) {
    my ($self, $q, $rg) = @_;

    # Set the headers for the resource group status table. 
    my @rgheaders = (gettext("Property"), "50%",
		     gettext("Value"), "50%");

    # Determine the number of columns from the headers
    my $numcols = ($#rgheaders + 1) / 2;

    # Create the title for the table

    my $headertext = sprintf(gettext("Resource Group " .
					   "Properties - %s"), $rg);

    # Start the property table
    $q->start_prop_table($headertext, \@rgheaders,
		"/images/rsrcgrps_16.gif");

    # Get the node status
    my (@rg_property_list) = $self->get_rg_property_list($rg);

    # Cycle through the list of resources and print the status
    for (my $i=0; $i<=$#rg_property_list; $i++) {

	# Start a table row for the node
	$q->start_prop_tr();
	
	# Print a table cell for the nodename
	$q->start_prop_td();
	$q->start_table_text("table-normal-text");
	print $rg_property_list[$i]{'name'};
	$q->end_table_text();
	$q->end_prop_td();
	
	# Print a table cell for the status
	$q->start_prop_td();

	print $rg_property_list[$i]{'value'};
	
	# End the table cell and table row
	$q->end_prop_td();
	$q->end_prop_tr();
	
	# If we're not on our last row, print the line row divider
	if ($i < $#rg_property_list) {
	    $q->line_row($numcols);
	}
    }
    
    # End the node status table
    $q->end_prop_table();
}

#
# Print the status of selected resource group
#

sub resource_group_status_table($$$) {
    my ($self, $q, $rg) = @_;

    # Set the headers for the resource group status table. 
    my @rgheaders = (gettext("Node"), "50%",
		     "&nbsp;", "14",
		     gettext("Resource Group Status"), "50%");

    # Determine the number of columns from the headers
    my $numcols = ($#rgheaders + 1) / 2;

    # Start the property table
    $q->start_prop_table(sprintf(gettext("Resource Group Status - %s"), $rg),
			 \@rgheaders, "/images/rsrcgrps_16.gif");

    # Get the node status
    my (%rg_state_hash) = $self->get_rg_state_nodelist($rg);

    # Get the nodelist
    my @nodelist = sort(keys(%rg_state_hash));

    # See if we're online anywhere
    my $offline_status = "OFFLINE";
    for (my $i=0; $i<=$#nodelist; $i++) {
	if (lc($rg_state_hash{$nodelist[$i]}) eq "online") {
	    $offline_status = "ONLINE_SOMEWHERE";
	}
    }

    # Cycle through the list of resource groups and print the status
    for (my $i=0; $i<=$#nodelist; $i++) {

	# Start a table row for the node
	$q->start_prop_tr();
	
	# Print a table cell for the nodename
	$q->start_prop_td();
	$q->start_table_text("table-normal-text");
	print $nodelist[$i];
	$q->end_table_text();
	$q->end_prop_td();
	
	# Print a table cell for the status
	$q->start_prop_td();

	# This function will print the correctly-colored & styled text for
	# the given status.
	my $status = $rg_state_hash{$nodelist[$i]};
	if (lc($status) eq "offline") {
	    # Adjust status; only critical if no nodes are online
	    $status = $offline_status;
	}
	$q->table_status_text($status, \@status_icons);
	
	# End the table cell and table row
	$q->end_prop_td();
	$q->end_prop_tr();
	
	# Print the line row divider
	$q->line_row($numcols);
    }

    # Start a table row for the overall status
    $q->start_prop_tr();
    $q->start_prop_td();
    $q->start_table_text("table-summary-text");
    print gettext("Overall Group Status:");
    $q->end_table_text();
    $q->end_prop_td();

    $q->start_prop_td();
    my ($status) = $self->get_aggregated_state(%rg_state_hash);
    $q->table_status_text($status, \@status_icons, "strong");

    # End the table cell and table row
    $q->end_prop_td();
    $q->end_prop_tr();

    # End the node status table
    $q->end_prop_table();
}

#
# Print the table of resources in this group
#

sub rg_resources_table($$$) {
    my ($self, $q, $rg) = @_;

    # Set the headers for the resource group status table. 
    my @rsheaders = (gettext("Name"), "33%",
		     "&nbsp;", "14",
		     gettext("Resource Status"), "33%",
		     gettext("Resource Type"), "33%",
		     );

    # Determine the number of columns from the headers
    my $numcols = ($#rsheaders + 1) / 2;

    # Start the property table
    $q->start_prop_table(sprintf(gettext("Resources - %s"), $rg),
			 \@rsheaders, "/images/rsrc_16.gif");
    
    # Get the rs state and online nodelist
    my @rslist = $self->get_rslist($rg);

    # If we don't have any resources, print a message
    if ($#rslist == -1) {
	
	# Start a table row and cell with the colspan of the table
	$q->start_prop_tr();
	$q->start_prop_td({ COLSPAN => 4 });
	
	# Print the message in table-label-text style
	$q->start_table_text('table-label-text');
	print gettext("No resources have been configured in " .
		      "this resource group");
	$q->end_table_text();
	
	# End the cell and row
	$q->end_prop_td();
	$q->end_prop_tr();
    }

    # Cycle through the list of resources and print the status
    for (my $i=0; $i<=$#rslist; $i++) {
	
	# Get the node status
	my ($rs_state, @online_nodelist) =
	    $self->get_rs_state_online_nodelist($rg, $rslist[$i]);
	
	# Start a table row for the node
	$q->start_prop_tr();
	
	# Print a table cell for the nodename
	$q->start_prop_td();

	# This function will print an icon and a link. It takes the label
	# text, the alt tag text, the link to point to, and the icon to
	# display before the text.
	$q->table_link_text($rslist[$i], gettext("Resource"),
			    "/cgi-bin/rg/rs_view.pl?rs=$rslist[$i]&rg=$rg");
	
	$q->end_prop_td();
	
	# Print a table cell for the status
	$q->start_prop_td();

	# This function will print the correctly-colored & styled text for
	# the given status.
	$q->table_status_text($rs_state, \@status_icons);
	
	$q->end_prop_td();
	
	# Print a table cell for the resource type
	$q->start_prop_td();

	# Lookup and print the resource type for this resource
	my $type = $self->get_rs_type($rslist[$i], $rg);
	$q->start_table_text("table-normal-text");
	print $type; # XXX I18N?
	$q->end_table_text("table-normal-text");

	# End the table cell and table row
	$q->end_prop_td();
	$q->end_prop_tr();
	
	# If we're not on our last row, print the line row divider
	if ($i < $#rslist) {
	    $q->line_row($numcols);
	}
    }
    
    # End the node status table
    $q->end_prop_table();
}

#
# Get the resource property list as a hash table
#
sub get_rg_properties($$) {
	my ($self, $rg) = @_;
	my (%props);
	my (@fields) = ("RG_DESCRIPTION", "RG_MODE", "FAILBACK",
	    "NODELIST", "MAXIMUM_PRIMARIES", "DESIRED_PRIMARIES",
	    "RG_DEPENDENCIES", "IMPLICIT_NETWORK_DEPENDENCIES",
	    "GLOBAL_RESOURCES_USED", "PINGPONG_INTERVAL", "PATHPREFIX",
	    "AUTO_START_ON_NEW_CLUSTER", "RG_PROJECT_NAME");

	if (`uname -r` =~ /5.8/) {
	    # No project name in Solaris 8
	    @fields = grep !/RG_PROJECT_NAME/, @fields;
	}
	foreach my $field (@fields) {
	    $props{$field} = join(",", grep(!/^$/,$cmd->scha_resourcegroup_get("-G $rg -O $field")));
	}
	return \%props;
}

#
# Get the resource property list
#

sub get_rg_property_list($$) {
	my ($self, $rg) = @_;
	my (@props);
	
	# Resource group name
	push @props, { name  => gettext("Name:"),
		       value => $rg
		       };

	# Resource description
	my ($description) = $cmd->scha_resourcegroup_get("-G $rg " .
							 "-O RG_DESCRIPTION");
	chomp $description;

	if ($description eq "") {
		$description = gettext("&lt;none&gt;");
	}

	push @props, { name  => gettext("Description:"),
		       value => $description
		       };

	# Management state -- optimize to not have redundant scha calls
	my (%rg_state_hash) = $self->get_rg_state_nodelist($rg);
	my ($managed_state) = $self->get_aggregated_state(%rg_state_hash);

	if ($managed_state eq "UNMANAGED") {
		$managed_state = gettext("Unmanaged");
	} else {
		# If the state is anything else, it's managed
		$managed_state = gettext("Managed");
	}

	push @props, { name  => gettext("Management State:"),
		       value => $managed_state
		       };

	# Nodelist
	my (@nodelist) = $cmd->scha_resourcegroup_get("-G $rg " .
						  "-O NODELIST");
	my $nodestring = "";
	foreach my $node (@nodelist) {
		chomp $node;
		
		if ($nodestring eq "") {
			$nodestring = $node;
		} else {
			$nodestring .= ", $node";
		}
	}
	push @props, { name  => gettext("Nodelist:"),
		       value => $nodestring
		       };

	# RG Dependencies
	my @rg_deps = $cmd->scha_resourcegroup_get("-G $rg " .
					     "-O RG_DEPENDENCIES");
	my $depstring = "";
	if ($rg_deps[0] eq "") {
		$depstring = gettext("&lt;none&gt;");
	} else {
		foreach my $dep (@rg_deps) {
			chomp $dep;
			if ($depstring eq "") {
				$depstring = $dep;
			} else {
				$depstring .= ", $dep";
			}
		}
	}
	push @props, { name  => gettext("Dependencies:"),
		       value => $depstring
		       };

	# Standard properties
	; # gettext("Maximum Primaries:")
	; # gettext("Desired Primaries:")
	; # gettext("Mode:")
	; # gettext("Failback:")
	; # gettext("Global Resources Used:")
	; # gettext("Implicit Network Dependencies:")
	; # gettext("Pingpong Interval:")
	; # gettext("Pathprefix:")
	; # gettext("Automatic start on new cluster:")
	; # gettext("Project Name:")
	; # gettext("Resource Group Affinities:")
	; # gettext("Scalable")
	; # gettext("Failover")
	; # gettext("True")
	; # gettext("False")
	my @details = ( [ "Maximum Primaries:", "MAXIMUM_PRIMARIES"],
			[ "Desired Primaries:", "DESIRED_PRIMARIES"],
			[ "Mode:", "RG_MODE"],
			[ "Failback:", "FAILBACK"],
			[ "Global Resources Used:", "GLOBAL_RESOURCES_USED"],
			[ "Implicit Network Dependencies:", 
                          "IMPLICIT_NETWORK_DEPENDENCIES"],
			[ "Pingpong Interval:", "PINGPONG_INTERVAL"],
			[ "Pathprefix:", "PATHPREFIX"],
			[ "Automatic start on new cluster:",
			    "AUTO_START_ON_NEW_CLUSTER"],
			[ "Project Name:", "RG_PROJECT_NAME"]
			);

	if (`uname -r` =~ /5.8/) {
		# Remove RG_PROJECT_NAME for Solaris 8
		@details = grep $_->[1] ne "RG_PROJECT_NAME", @details;
	}
	
	for (my $i=0; $i<=$#details; $i++) {
		my $title    = $details[$i][0];
		my $property = $details[$i][1];

		my (@result) = $cmd->scha_resourcegroup_get(
				   "-G $rg -O $property");

		if ($result[0] eq "") {
			$result[0] = gettext("&lt;none&gt;");
		} elsif ($property eq "RG_PROJECT_NAME") {
		    # No processing
		} else {
		    $result[0] = ucfirst(lc($result[0]));
		    if ($property eq "RG_MODE" || $property eq "FAILBACK"
			    || $property eq "IMPLICIT_NETWORK_DEPENDENCIES" ||
			    $property eq "AUTO_START_ON_NEW_CLUSTER") {
			$result[0] = gettext($result[0]);
		    }
		}

		push @props, { name  => gettext($title),
			       value => @result
			       };
	}

	return @props;
}

#
# Print the status of selected resources
#

sub resource_status_table($$$$$) {
    my ($self, $q, $rs, $rg) = @_;

    # Set the headers for the resource group status table. 
    my @rsheaders = (gettext("Node"), "50%",
		     "&nbsp;", "14",
		     gettext("Resource Status"), "50%");

    # Determine the number of columns from the headers
    my $numcols = ($#rsheaders + 1) / 2;

    # Start the property table
    $q->start_prop_table(sprintf(gettext("Resource Status - %s"), $rs),
			 \@rsheaders, "/images/rsrc_16.gif");

    # Get the node status
    my (%rs_state_hash) = $self->get_rs_state_nodelist($rg, $rs);

    # Get the nodelist
    my @nodelist = sort(keys(%rs_state_hash));

    # See if we're online anywhere
    my $offline_status = "OFFLINE";
    for (my $i=0; $i<=$#nodelist; $i++) {
	if (lc($rs_state_hash{$nodelist[$i]}) eq "online") {
	    $offline_status = "ONLINE_SOMEWHERE";
	}
    }

    # Cycle through the list of nodes and print the status
    for (my $i=0; $i<=$#nodelist; $i++) {

	# Start a table row for the node
	$q->start_prop_tr();
	
	# Print a table cell for the nodename
	$q->start_prop_td();
	$q->start_table_text("table-normal-text");
	print $nodelist[$i];
	$q->end_table_text();
	$q->end_prop_td();
	
	# Print a table cell for the status
	$q->start_prop_td();

	# This function will print the correctly-colored & styled text for
	# the given status.
	my $status = $rs_state_hash{$nodelist[$i]};
	if (lc($status) eq "offline") {
	    # Adjust status; only critical if no nodes are online
	    $status = $offline_status;
	}
	$q->table_status_text($status, \@status_icons);
	
	# End the table cell and table row
	$q->end_prop_td();
	$q->end_prop_tr();
	
	# If we're not on our last row, print the line row divider
	$q->line_row($numcols);
    }

    # Start a table row for the overall status
    $q->start_prop_tr();
    $q->start_prop_td();
    $q->start_table_text("table-summary-text");
    print gettext("Overall Resource Status:");
    $q->end_table_text();
    $q->end_prop_td();

    $q->start_prop_td();
    my ($status) = $self->get_aggregated_state(%rs_state_hash);
    $q->table_status_text($status, \@status_icons, "strong");

    # End the table cell and table row
    $q->end_prop_td();
    $q->end_prop_tr();
    
    # End the node status table
    $q->end_prop_table();
}


#
# Print the properties of the selected resource
#

sub resource_properties_table($$$$$$) {
    my ($self, $q, $rs, $rg, $view) = @_;

    # Set the headers for the resource group status table. 
    my @rsheaders = (gettext("Property"), "50%",
		     gettext("Value"), "50%");

    # Determine the number of columns from the headers
    my $numcols = ($#rsheaders + 1) / 2;

    # Create the title for the table
    my $headertext;

    if (defined $view && $view eq "detailed") {
	    $headertext = sprintf(gettext("Resource Detailed " .
					   "Properties - %s"), $rs);
    } else {
	    $headertext = sprintf(gettext("Resource General " .
					   "Properties - %s"), $rs);
    }	

    # Start the property table
    $q->start_prop_table($headertext, \@rsheaders, "/images/rsrc_16.gif");

    # Get the node status
    my (@rs_property_list) = $self->get_rs_property_list($rs, $rg, $view);

    # Cycle through the list of resources and print the status
    for (my $i=0; $i<=$#rs_property_list; $i++) {

	# Start a table row for the node
	$q->start_prop_tr();
	
	# Print a table cell for the nodename
	$q->start_prop_td();
	$q->start_table_text("table-normal-text");
	print $rs_property_list[$i]{'name'};
	$q->end_table_text();
	$q->end_prop_td();
	
	# Print a table cell for the status
	$q->start_prop_td();

	print $rs_property_list[$i]{'value'};
	
	# End the table cell and table row
	$q->end_prop_td();
	$q->end_prop_tr();
	
	# If we're not on our last row, print the line row divider
	if ($i < $#rs_property_list) {
	    $q->line_row($numcols);
	}
    }
    
    # End the node status table
    $q->end_prop_table();
}

#
# Get the resource property list
# The general list is internationalized
#

sub get_rs_property_list($$$$) {
	my ($self, $rs, $rg, $view) = @_;
	my (@props);

	# Standard resource info
	if (!(defined $view && $view eq "detailed")) {
		
		# Resource name
		push @props, { name  => gettext("Name:"),
			       value => $rs
			       };

		# Resource description
		my ($description) = $cmd->scha_resource_get("-R $rs -G $rg " .
							    "-O R_DESCRIPTION");
		chomp $description;

		if ($description eq "") {
			$description = gettext("&lt;none&gt;");
		}

		push @props, { name  => gettext("Description:"),
			       value => $description
			       };

		# Resource type
		my ($rt) = $cmd->scha_resource_get("-R $rs -G $rg -O TYPE");
		push @props, { name  => gettext("Resource Type:"),
			       value => $rt
			       };

		# Scalable
		my ($scalable) = $cmd->scha_resource_get("-R $rs -G $rg " .
							"-O SCALABLE");
		if ($scalable eq "TRUE") {
			$scalable = gettext("Yes");
		} else {
			$scalable = gettext("No");
		}

		push @props, { name  => gettext("Scalable:"),
			       value => $scalable
			       };

		# Enabled
		my ($enabled) = $cmd->scha_resource_get("-R $rs -G $rg " .
							"-O ON_OFF_SWITCH");
		if ($enabled eq "ENABLED") {
			$enabled = gettext("Yes");
		} elsif ($enabled eq "DISABLED") {
			$enabled = gettext("No");
		}

		push @props, { name  => gettext("Enabled:"),
			       value => $enabled
			       };

		# Monitored
		my ($monitored) = $cmd->scha_resource_get("-R $rs -G $rg " .
							  "-O MONITORED_SWITCH");
		if ($monitored eq "ENABLED") {
			$monitored = gettext("Yes");
		} elsif ($monitored eq "DISABLED") {
			$monitored = gettext("No");
		}

		push @props, { name  => gettext("Monitored:"),
			       value => $monitored
			       };

		# Strong Dependencies
		my @s_deps = $cmd->scha_resource_get("-R $rs -G $rg " .
						     "-O RESOURCE_DEPENDENCIES");
		if ($s_deps[0] eq "") {
			$s_deps[0] = gettext("&lt;none&gt;");
		}
		
		push @props, { name  => gettext("Strong Dependencies:"),
			       value => @s_deps
			       };

		# Weak dependencies
		my @w_deps = $cmd->scha_resource_get("-R $rs -G $rg -O " .
						     "RESOURCE_DEPENDENCIES_WEAK");
		if ($w_deps[0] eq "") {
			$w_deps[0] = gettext("&lt;none&gt;");
		}
		
		push @props, { name  => gettext("Weak Dependencies:"),
			       value => @w_deps
			       };

	} else {
		# Detailed resource info
		# Add timeouts and other details
		my @details = ( "R_description",
				"Type",
				"Resource_project_name",
				"Type_version",
				"On_off_switch",
				"Monitored_switch",
				"Resource_dependencies",
				"Resource_dependencies_weak",
				"Failover_mode",
				"Scalable",
				"Network_resources_used",
				"Port_list",
				"Load_balancing_policy",
				"Load_balancing_weights",
				"Cheap_Probe_Interval",
				"Thorough_Probe_Interval",
				"Retry_Count",
				"Retry_Interval",
				"START_TIMEOUT",
				"STOP_TIMEOUT",
				"VALIDATE_TIMEOUT",
				"UPDATE_TIMEOUT",
				"INIT_TIMEOUT",
				"FINI_TIMEOUT",
				"BOOT_TIMEOUT",
				"UPDATE_FAILED",
				"INIT_FAILED",
				"FINI_FAILED",
				"BOOT_FAILED",
				"MONITOR_START_TIMEOUT",
				"MONITOR_STOP_TIMEOUT",
				"MONITOR_CHECK_TIMEOUT",
				"PRENET_START_TIMEOUT",
				"POSTNET_STOP_TIMEOUT",
				);

		if (`uname -r` =~ /5.8/) {
		    # Solaris 8 doesn't support Resource_project_name
		    @details = grep !/Resource_project_name/, @details;
		}

		my @is_scalable = $cmd->scha_resource_get(
					 "-R $rs -G $rg -O Scalable");
		
		$is_scalable[0] = uc($is_scalable[0]);

		foreach my $detail (@details) {
			if ( $is_scalable[0] ne "TRUE"  and 
			     $detail eq "Load_balancing_policy") {
				next;
			}
			if ( $is_scalable[0] ne "TRUE"  and 
			     $detail eq "Load_balancing_weights") {
				next;
			}
			my @result = $cmd->scha_resource_get(
					 "-R $rs -G $rg -O $detail");

			my $value_str = join ', ', @result;

			if ($value_str eq "") {
				$value_str = "&lt;none&gt;"; # I18N in Cgi.pm
			}

			push @props, { name  => $detail,
				       value => $value_str
				       };
		}

		# Add extension properties
		my @ext_props = $cmd->scha_resource_get("-R $rs -G $rg -O " .
					     "ALL_EXTENSIONS");

		foreach my $ext (@ext_props) {
			my @result = $cmd->scha_resource_get(
				     "-R $rs -G $rg -O EXTENSION $ext");

			# Get rid of the type
			shift @result;
			my $value_str = join ', ', @result;

			# Check if it's empty now
			if ($value_str eq "") {
				$value_str = "&lt;none&gt;"; # I18N in Cgi.pm
			}
			
			push @props, { name  => $ext,
				       value => $value_str
				       };
		}

	}

	return @props;
}

#
# Return menu data
#

sub get_menu_data() {
    return ("Bar", "/cgi-bin/bar.html");
}

#
# Start table
#

sub start_rg_action_table {
    my ($self, $q) = @_;
    $q->start_action_table
}

#
# Print a line using an action table
#

sub action_table_line {
    my ($self, $q) = @_;
    print $q->start_table({BORDER      => 0,
			   CELLSPACING => 0,
			   CELLPADDING => 0,
			   WIDTH       => "97%",
			   ALIGN       => "CENTER",
			   CLASS       => "action-table"
			   });
    print $q->start_Tr({HEIGHT	=> 6});
    print $q->start_td(),"\n";
    print $q->img({	SRC => "/images/dot.gif",
			ALT => "",
			HEIGHT => 6,
			});
    print $q->end_td(), $q->end_Tr(), "\n";

    print $q->start_Tr({HEIGHT	=> 1});
    print $q->start_td({CLASS => "action-line-row"}),"\n";
    print $q->img({	SRC => "/images/dot.gif",
			ALT => "",
			HEIGHT => 1,
			});
    print $q->end_td(), $q->end_Tr(), "\n";

    print $q->start_Tr({HEIGHT	=> 6});
    print $q->start_td(),"\n";
    print $q->img({	SRC => "/images/dot.gif",
			ALT => "",
			HEIGHT => 6,
			});
    print $q->end_td(), $q->end_Tr(), $q->end_table(), "\n";
}

#
# Print a warning
#

sub warning {
    my ($self, $q, $text) = @_;

    print $q->start_table({BORDER      => 0,
			   CELLSPACING => 0,
			   CELLPADDING => 2,
			   WIDTH       => "97%",
			   ALIGN       => "CENTER",
			   CLASS       => "action-table"
			   });
    print $q->start_Tr({HEIGHT	=> 6});
    print $q->start_td({NOWRAP	=> undef,
			ALIGN	=> "CENTER",
			WIDTH	=> 32,
			HEIGHT	=> 32,
			VALIGN	=> "top",
			});
    print $q->img({	SRC	=> "/images/warning_32.gif",
			ALT	=> gettext("Warning"),
			WIDTH	=> 32,
			HEIGHT	=> 32,
			});
    print $q->end_td(), "\n";

    print $q->start_td({WIDTH	=> "99%",
			VALIGN	=> "bottom",
			});
    print $q->p({CLASS => "message-description-text"}, $text), "\n";
			
    print $q->end_td(), $q->end_Tr(), $q->end_table(), "\n";
}

#
# Print the submit and cancel buttons
#
sub submit_button {
    my ($self, $q, $label, $cancel) = @_;
    $self->action_table_line($q);

    print $q->start_table({BORDER      => 0,
			   CELLSPACING => 0,
			   CELLPADDING => 2,
			   WIDTH       => "97%",
			   ALIGN       => "CENTER",
			   CLASS       => "action-table"
			   });
    print $q->start_Tr();
    print $q->start_td({ALIGN	=> "right",
			WIDTH	=> "80%",
			}), "\n";
    if ($label ne "") {
	print $q->input({	TYPE	=> "submit",
			    VALUE	=> $label,
			    NAME	=> "doit",
			    });
    }
    print $q->start_td({ALIGN	=> "right",
			WIDTH	=> "20%",
			}), "\n";
    if (defined($cancel)) {
	print $q->input({	TYPE	=> "button",
			    VALUE	=> gettext("  Cancel  "),
			    ONCLICK	=> "history.back()",
			    });
    } else {
	print "&nbsp;";
    }
    print $q->end_td(), $q->end_Tr(), $q->end_table(), "\n";
}

#
# Print a resource group selector
# If $jump is defined, use JumpViewMenu and also don't close the table
#
sub select_rg {
    my ($self, $q, $rgvalues, $jump) = @_;

    print $q->start_table({BORDER      => 0,
			   CELLSPACING => 0,
			   CELLPADDING => 5,
			   CLASS       => "action-table"
			   });
    print $q->start_Tr(), $q->start_td();
    print $q->CGI::div({CLASS => "action-window-label-text"},
	gettext("Resource Group:"));
    print $q->end_td(), "\n";

    print $q->start_td();

    if (! defined($jump)) {
	print $q->popup_menu({	NAME	=> "rg",
				VALUES	=> $rgvalues,
				});
    } else {
	my @nodelinks;
	my $selectindex = 0;
	my $rg = ($q->param('rg') || "");
	for (my $i = 0; $i <= $#{@{$rgvalues}}; $i++) {
	    push @nodelinks, $rgvalues->[$i];
	    push @nodelinks, $jump.$rgvalues->[$i];
	    if ($rgvalues->[$i] eq $rg) {
		$selectindex = $i;
	    }
	}
	$q->select_menu(\@nodelinks, "rg_name", $nodelinks[2*$selectindex+1]);
	print $q->start_script();
	print "document.inputs.rg_name.selectedIndex=$selectindex";
	print $q->end_script();
    }

    print $q->end_td(), $q->end_Tr();
    if (! defined($jump)) {
	print $q->end_table(), "\n";
    }
}

#
# Get resource names and labels
#
sub get_rslists {
    my ($self, $q) = @_;
    my (@rsname, %rslabel);
    my @rgs = $self->get_rglist(); 
    foreach my $rg (@rgs) {
	my @rs = $self->get_rslist($rg);
	foreach my $rs (@rs) {
	    push @rsname, $rs;
	    $rslabel{$rs} = $q->sprintfn(gettext("%1 (%2)"), $rs, $rg);
	}
    }
    return (\@rsname, \%rslabel);
}

#
# Print missing rg message
#
sub need_rg {
    my ($self, $q) = @_;
    $self->warning($q, gettext("No resource groups exist."));
    $self->submit_button($q, "", "cancel");
}

#
# Print missing rs message
#
sub need_rs {
    my ($self, $q) = @_;
    $self->warning($q, gettext("No resources exist."));
    $self->submit_button($q, "", "cancel");
}

#
# Print a resource selector
#
sub select_rs {
    my ($self, $q, $rsvalues, $rslabels, $jump) = @_;

    print $q->start_table({BORDER      => 0,
			   CELLSPACING => 0,
			   CELLPADDING => 5,
			   CLASS       => "action-table"
			   });
    print $q->start_Tr(), $q->start_td();
    print $q->CGI::div({CLASS => "action-window-label-text"},
	gettext("Resource:"));
    print $q->end_td(), "\n";

    print $q->start_td();


    print $q->popup_menu({	NAME	=> "rs",
				VALUES	=> $rsvalues,
				LABELS	=> $rslabels,
				});
				
    print $q->end_td();
    if (! defined($jump)) {
	print $q->end_Tr(), $q->end_table(), "\n";
    }
}

#
# Print View Resource Group Information button
#
sub view_button {
    my ($self, $q) = @_;
    print $q->start_form({ ACTION => "/cgi-bin/rg/rg_view.pl",
			   METHOD => "POST",
			   });
    $self->submit_button($q,
	gettext("  View Resource Group Information  "));
    print $q->end_form();
}

#
# Get the resource type for a resource
#
sub get_rs_type ($$) {
	my ($self, $rs, $rg) = @_;
	my ($type) = $cmd->scha_resource_get("-O TYPE -R $rs -G $rg");
	chomp $type;
	return $type;
}

# Return true
1;
