#
# Copyright 2002-2003 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
#ident	"@(#)Network.pm	1.44	03/09/29 SMI"
#
# IP Multipath (IPMP)

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

# Internationalization
# action=
;# gettext("Add")
;# gettext("Remove")

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

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

##############################################################################
#
# Class Variables
#
##############################################################################

# File names and location
my $NAFOIMAGE = '/images/nafo_16.gif';
my $TELNETIMAGE = '/images/telnet-win-icon.gif';
my $VIEW = '/cgi-bin/network/network_view.pl';
my $CONFIG = '/cgi-bin/network/network_config.pl';
my $SCSTAT = '/usr/cluster/bin/scstat';
my $EXEC = '/cgi-bin/network/network_cmd.pl';

# Instantiate cluster objects
my $rbac = new Cluster::RBAC;
my $runcommand  = new Cluster::RunCommand;

# Global Variable
my @status_icons = (
	["OK", "OK", "ok"], # gettext("OK")
	["DOWN", "Down", "critical"], # gettext("Down")
	["Failed", "Failed", "critical"], # gettext("Failed")
);

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

# Return the nafo related jscript
sub network_jscript() {
;# /* TRANSLATION_NOTE The following 3 strings are Javascript expressions */
	return "
	// verify the given item name is started with the given pattern
	// and ended with digits
	function verify_name(itemvalue, pattern) {
		// verify the item name is started with pattern
                if (itemvalue.indexOf(pattern) != 0) {
                        return false
                }
		// verify the item name is ended with digits
		if (itemvalue.length == pattern.length) {
			return false;
		}
		for (var i = pattern.length; i < itemvalue.length; i++) {
			var digit = itemvalue.charAt(i);
			if (digit < \"0\" || digit > \"9\") {
				return false
			}
                }
		return true
	}
	function trim(itemvalue) {
		// Get rid of leading space(s)
		for (var i = 0; i < itemvalue.length; i++) {
                        var ch = itemvalue.charAt(i);
			if (ch != \" \") {
				break
			}
		}
		// Get rid of trailing space(s)
		for (var j = itemvalue.length - 1; j > -1; j--) {
                        var ch = itemvalue.charAt(j);
			if (ch != \" \") {
				break
			}
		}
		var newvalue = '';
		for (var k = i; k <= j; k++) {
			newvalue += itemvalue.charAt(k)
		}
		return newvalue
	} 
		
	function check_value(itemname, itemvalue) { 
		if (itemvalue.length <= 0) {
			alert('".gettext("Please input %s")."'.replace('%s',itemname));
                        return false
                }
		if (itemvalue.indexOf(\" \") >= 0) {
			alert('".gettext("No space is allowed in %s")."'.replace('%s',itemname));
			return false
		} 
		return true
	}
	function verify_adapter(adapter) {
		var adp = trim(adapter) 
		if (!check_value('".gettext("adapter name")."', adp)) {
			return false
		}
		if (!(verify_name(adp, 'qfe') || verify_name( adp, 'sci') || verify_name(adp, 'hme') || verify_name(adp, 'ge') || verify_name(adp, 'wrsm') || verify_name(adp, 'eri') || verify_name(adp, 'ce') || verify_name(adp, 'bge') || verify_name(adp, 'e1000g') || verify_name(adp, 'iprb'))) {
			alert('".gettext("Invalid adapter name -- %s.  Please make sure it starts with qfe, hme, sci, ge, eri, wrsm, ce, bge, e1000g or iprb and ends with digit(s).")."'.replace('%s',adp))
			return false
		}
		return true
	} 
	function config_adapter_submit(func, node) {
		self.location.href = '$CONFIG\?step=2&func=' + func + '&adp=' + document.inputs.adp.options[document.inputs.adp.selectedIndex].value + '&node=' + node + '&unplumb=' + document.inputs.group.checked
	}";
}

sub network_header {
	my ($self, $q, $headername, $node, $nafogroup) = @_;
  
	# Start the action bar table
        $q->start_action_bar_table($headername, undef,
	    {ALIGN => "right", WIDTH => "99%"});

	# Print the telnet link to the node
	$q->telnet_link($node, $self->get_hostdomain());
	
	# Print the middle of the action table
        $q->mid_action_bar_table();

	# Space the actions menu over
	print "&nbsp;&nbsp";

	# Create the actions menu links        
	my @links = (
		gettext("Create Group..."),
		"$CONFIG\?step=1&func=Add&node=$node&group=new+group",

		gettext("Add Adapter..."),
		"$CONFIG\?step=1&func=Add&node=$node",

		gettext("Remove Adapter from Group..."),
		"$CONFIG\?step=1&func=Remove&node=$node"
		);

	my @action_helpMarkers = (
			          "networkpm-add-group",
			          "networkpm-add-adapter",
			          "networkpm-remove-adapter");
	$q->load_action_helpMarkers(\@action_helpMarkers);

        # Print if the user is root (uid == 0). IPMP actions require
	# the effective user to be root. This follows ifconfig semantics,
	# which are not currently RBAC aware.
        if ($rbac->check_root()) {
                $q->actions_menu(gettext("Action Menu"), \@links);
        }


        # End the action bar table
        $q->end_action_bar_table();
}

# Display list of ipmp groups on the given node
sub nafogroups_table($$$)
{
	my ($self, $q, $nodename) = @_;

	# Set the headers ipmp group table 
        my @table_headers = (gettext("Name"), "25%",
			     "&nbsp;", "14",
			     gettext("Group State"), "25%",
			     gettext("Adapter"), "25%",
			     "&nbsp;", "14",
			     gettext("State"), "25%");

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

	# Start the table
        $q->start_prop_table(sprintf(gettext("IPMP Groups - %s"), $nodename),
			\@table_headers, $NAFOIMAGE);
	
	my $ipmpdata = $self->get_ipmpinfo($nodename);
	if ($#{@{$ipmpdata}} < 0) {
		# Not Found
                # Start a table row and cell with the colspan of the table
                $q->start_prop_tr();
                $q->start_prop_td({ COLSPAN => 6 });

                # Print the message in table-label-text style
                $q->start_table_text('table-label-text');
                print sprintf(gettext(
		    "No IPMP groups have been configured on node %s"),
		    $nodename);
                $q->end_table_text();

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

	my $currgroup = "";
	for (my $i = 0; $i <= $#{@{$ipmpdata}}; $i++) {
		if ($ipmpdata->[$i]{'name'} ne $currgroup) {
		    # Print separator if necessary
		    if ($currgroup ne "") {
			$q->line_row($numcols);
		    }
		    $q->start_prop_tr();
		    $currgroup = $ipmpdata->[$i]{'name'};
		    # Print a table cell for the ipmp group name
		    $q->start_prop_td();
		    print $ipmpdata->[$i]{'name'};
		    $q->end_prop_td();

		    # Print a table cell for the group's status
		    $q->start_prop_td();
		    $q->table_status_text($ipmpdata->[$i]{'state'},
			\@status_icons);
		    $q->end_prop_td();
		} else {
		    $q->start_prop_tr();
		    # Print empty table cells for the ipmp group name
		    $q->start_prop_td();
		    print "&nbsp;";
		    $q->end_prop_td();
		    $q->start_prop_td();
		    print "&nbsp;";
		    $q->end_prop_td();
		    $q->start_prop_td();
		    print "&nbsp;";
		    $q->end_prop_td();
		}


		# Print a table cell for the groups's adapter
                $q->start_prop_td();
		$q->start_table_text('table-normal-text');
                print $ipmpdata->[$i]{'adp'};
		$q->end_table_text();
                $q->end_prop_td();

		# Print a table cell for the adapter's status
                $q->start_prop_td();
		$q->table_status_text($ipmpdata->[$i]{'astate'},
		    \@status_icons);
                $q->end_prop_td();

		$q->end_prop_tr();

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

# check if the given node is in cluster mode
sub is_node_online($$$)
{
	my ($self, $q, $nodename) = @_;

	my @node_list = $self->get_nodelist();

	for (my $i = 0; $i <= $#node_list; $i++) {
		if (($node_list[$i] eq $nodename) && (lc($self->get_nodestate($nodename)) eq "online")) {
		    return 1;
		}
	}
	# not found or not online
	$q->print_title([gettext("Nodes"),
		"/cgi-bin/node/node_status.pl",
	    $nodename, "/cgi-bin/node/node_info.pl?node=$nodename",
	    gettext("IPMP Groups")]);
	print "<form>";
	$q->start_message_table("warning");
	print $q->start_p({ class => "message-header-text" });
	print sprintf(gettext(
	    "Unable to Access Node %s!"),
	    $nodename);
	print $q->end_p();
	$q->end_message_table();
	$q->buttons_table(sprintf(gettext("  View Node %s Information  "),
	    $nodename),
	    "self.location.href=\"/cgi-bin/node/node_info.pl\?node=$nodename\"");
	print "</form>";	
	return 0;
}

# Get the IPMP group information from scstat -i
# If a node is passed in, only data for that node is displayed
# Return an array of data hashes
sub get_ipmpinfo($$)
{
	my ($self, $reqnode) = @_;

	my $data = ();
	my (@SCSTATI_OUT) = $runcommand->scstat("-i");
	for (my $k = 0; $k <= $#SCSTATI_OUT; $k++) {
		my $line = $SCSTATI_OUT[$k];
		# Look for IPMP Group: lines
		if ($line =~ /^ *IPMP( Group)?:/) {
			# Split off part after the colon
			my ($junk, $contents) = split(/:\s*/, $line, 2);
			# Now break up the line
			my ($node, $group, $gstate, $adp, $astate) =
			    split(/\s+/, $contents);
			if (!defined($reqnode) || $reqnode eq $node) {
			    push @$data, {name => $group, node => $node,
				state => $gstate,
				adp => $adp, astate => $astate};
			}
		}
	}
	return ($data);
}

# add new adapters into new or existing ipmp group	
sub add_adapter($$$$$$)
{
	my ($self, $q, $step, $node, $nafogroup, $adp) = @_;

	if (!$self->is_node_online($q, $node)) {
                return;
        }
	my $ipmpdata = $self->get_ipmpinfo($node);
	
	if ($step == 1) {
		my ($title) = gettext("Add Adapter to IPMP Group");
		if ($q->param('group') eq "new group") {
		    $title = gettext("Create Group and Add Adapter");
		}
		$q->print_title([gettext("Nodes"),
			"/cgi-bin/node/node_status.pl",
		    $node, 
			"/cgi-bin/node/node_info.pl?node=$node",
		    gettext("IPMP Groups"), "$VIEW?node=$node",
		    $title]);
		# gather information
                print "<form name=inputs action=$CONFIG>";
                print $q->input({ type => "hidden",
                                        name => "step",
                                        value => 2 });
                print $q->input({ type => "hidden",
                                        name => "node",
                                        value => $node });
                print $q->input({ type => "hidden",
                                        name => "func",
                                        value => "Add" });

		print $q->start_table({ cellpadding => 5 });
                print $q->start_Tr();
                print $q->start_td();
                print $q->start_div({ class => "action-window-label-text" });
		# Create list of groups
		my (%groups, @groups);
		for (my $i = 0; $i <= $#{@{$ipmpdata}}; $i++) {
		    $groups{$ipmpdata->[$i]{'name'}} = 1;
		}
		@groups = sort keys %groups;
		print gettext("IPMP Group:");
		print $q->end_div();
		print $q->end_td();
		print $q->start_td();
		if ($#groups >= 0 &&
			$q->param('group') ne "new group") {
		    print $q->popup_menu({ name => 'group',
				values => \@groups
				});
		} else {
		    # New group selected or no existing groups
		    print $q->input({ type=> "text", name => "group",
				     size => 20 });
		}
		print $q->end_td();
		print $q->end_Tr();

		print $q->start_Tr();
                print $q->start_td();
                print $q->start_div({ class => "action-window-label-text" });
                print gettext("Adapter:");
                print $q->end_div();
                print $q->end_td();
                print $q->start_td();
                print $q->input({ type=> "text", name => "adp",
                                 size => 20 });
                print $q->end_td();
                print $q->end_Tr();

		print $q->start_Tr();
                print $q->start_td();
                print $q->start_div({ class => "action-window-label-text" });
                print gettext("Test Address:");
                print $q->end_div();
                print $q->end_td();
                print $q->start_td();
                print $q->input({ type=> "text", name => "taddr",
                                 size => 20 });
                print $q->end_td();
                print $q->end_Tr();

		print $q->start_Tr();
                print $q->start_td();
                print $q->start_div({ class => "action-window-label-text" });
                print gettext("Base Address:");
		print "<br>",gettext("(optional)");
                print $q->end_div();
                print $q->end_td();
                print $q->start_td();
                print $q->input({ type=> "text", name => "addr",
                                 size => 20 });
                print $q->end_td();
                print $q->end_Tr();

		print $q->start_Tr();
                print $q->start_td();
                print $q->start_div({ class => "action-window-label-text" });
                print gettext("Standby:");
                print $q->end_div();
                print $q->end_td();
                print $q->start_td();
                print $q->checkbox({name => "standby", label => ""});
                print $q->end_td();
                print $q->end_Tr();

                print $q->end_table();

		my ($button);
		if ($q->param('group') eq "new group") {
		    $button = gettext(" Create Group ");
		} else {
		    $button = gettext(" Add Adapter ");
		}
                $q->buttons_table($button,
                        "if (verify_adapter(document.inputs.adp.value)) document.inputs.submit()",
                        gettext("  Cancel  "),
                        "self.location.href = \"$VIEW\?node=$node\"" );
                print "</form>";
	} else {
		# generate the command
		my $group = $q->param('group');
		my $adp = $q->param('adp');
		my $taddr = $q->param('taddr');
		my $addr = $q->param('addr');
		my $standby = $q->param('standby');
		if (!defined($standby) || $addr ne "") {
		    $standby = "off";
		}

		for (my $j = 0; $j <= $#{@{$ipmpdata}}; $j++) {
		    if ($ipmpdata->[$j]{'adp'} eq $adp) {
                        $q->print_title([gettext("Nodes"),
				"/cgi-bin/node/node_status.pl",
			    $node, 
				"/cgi-bin/node/node_info.pl?node=$node",
			    gettext("IPMP Groups"), "$VIEW?node=$node",
			    gettext("Add Adapter to IPMP Group"),
		"$CONFIG\?step=1&func=Add&node=$node",
			    gettext("Warning")]);
                        print "<form>";
                        $q->start_message_table("warning");
                        print $q->start_p({ class => "message-header-text" });
                        print $q->sprintfn(gettext("Adapter %1 on node %2 is already in an IPMP group."), $adp, $node);
                        print $q->end_p();
                        $q->end_message_table($q);
			$q->buttons_table(gettext(" Move Adapter to New Group"),
			    "self.location.href = \"$CONFIG\?step=1&func=Move&node=$node&adp=$adp&group=$group\"",
			    gettext("  Cancel  "),
			    "self.location.href=\"$VIEW\?node=$node\"" );
                        print "</form>";
                        return;
			
		    }
		}

		my $cmd="add $group $adp $taddr $standby $addr";
		
		# Executing the command
                $self->execute_cmd($q, $cmd, $node,
		    [gettext("Nodes"), "/cgi-bin/node/node_status.pl",
			$node, "/cgi-bin/node/node_info.pl?node=$node",
			gettext("IPMP Groups"), "$VIEW?node=$node",
			gettext("Add Adapter"),
	    "$CONFIG\?step=1&func=Add&node=$node"]);
	}
}

# move adapter into new or existing ipmp group	
sub move_adapter($$$$$$)
{
	my ($self, $q, $step, $node, $nafogroup, $adp) = @_;

	if (!$self->is_node_online($q, $node)) {
                return;
        }
	my $ipmpdata = $self->get_ipmpinfo($node);
	
	if ($step == 1 || $step == 2) {
		# generate the command
		my $group = $q->param('group');
		my $adp = $q->param('adp');

		# Check the number of adapters in the group
		my $ipmpdata = $self->get_ipmpinfo($node);
		my %groupcount; # Number of adapters in group
		for (my $i = 0; $i <= $#{@{$ipmpdata}}; $i++) {
		    $groupcount{$ipmpdata->[$i]{'name'}}++;
		}

		my $cmd="move $group $adp";
		if ($step == 2 || $groupcount{$group} == 1) {
		    # Don't do if_mpadm if already done, or
		    # only one member in group
		    $cmd="moveonly $group $adp";
		}
		
		# Executing the command
                $self->execute_cmd($q, $cmd, $node,
		    [gettext("Nodes"), "/cgi-bin/node/node_status.pl",
			$node, "/cgi-bin/node/node_info.pl?node=$node",
			gettext("IPMP Groups"), "$VIEW?node=$node",
			gettext("Move Adapter"),
	    "$CONFIG\?step=2&func=Move&node=$node&adp=$adp&group=$group"]);
	}
}

# Remove an adapter from a ipmp group
sub remove_adapter($$$$$$$)
{
	my ($self, $q, $step, $node, $unplumbgroup, $adp, $unplumb) = @_;


	if (!$self->is_node_online($q, $node)) {
                return;
        }

	if ($step == 1) {
		$q->print_title([gettext("Nodes"),
			"/cgi-bin/node/node_status.pl",
		    $node, "/cgi-bin/node/node_info.pl?node=$node",
		    gettext("IPMP Groups"), "$VIEW?node=$node",
		    gettext("Remove Adapter from IPMP Group")]);
                # gather information
		my $ipmpdata = $self->get_ipmpinfo($node);

		if ($#{@{$ipmpdata}} < 0) {
                        $q->print_title([gettext("Nodes"),
				"/cgi-bin/node/node_status.pl",
			    $node, 
				"/cgi-bin/node/node_info.pl?node=$node",
			    gettext("IPMP Groups"), "$VIEW?node=$node",
			    gettext("Remove Adapter from IPMP Group"),
		"$CONFIG\?step=1&func=Remove&node=$node",
			    gettext("Warning")]);
                        print "<form>";
                        $q->start_message_table("warning");
                        print $q->start_p({ class => "message-header-text" });
                        print sprintf(gettext("No IPMP group has been configured on node %s"), $node);
                        print $q->end_p();
                        $q->end_message_table($q);
                        $q->buttons_table(gettext("  Cancel  "), "self.location.href=\"$VIEW\?node=$node\"" );
                        print "</form>";
                        return;
		}

                print "<form name=inputs action=$CONFIG>";
                print $q->input({ type => "hidden",
                                        name => "step",
                                        value => 2 });
                print $q->input({ type => "hidden",
                                        name => "node",
                                        value => $node });
                print $q->input({ type => "hidden",
                                        name => "func",
                                        value => "Remove" });

		print $q->start_table({ cellpadding => 5 });

		print $q->start_Tr();
                print $q->start_td();
                print $q->start_div({ class => "action-window-label-text" });
                print gettext("Adapter:");
                print $q->end_div();
                print $q->end_td();
                print $q->start_td();
		my (@adapters, %labels);
	        for (my $j = 0; $j <= $#{@{$ipmpdata}}; $j++) {
		     my ($ad) = $ipmpdata->[$j]{'adp'};
		     my ($nm) = $ipmpdata->[$j]{'name'};
		     push @adapters, $ad;
		    $labels{$ad} = "$ad ($nm)";
		}
		print $q->popup_menu({ name => 'adp',
				    values => \@adapters,
				    labels => \%labels,
				    default => $q->param('adp')});
                print $q->end_td();
                print $q->end_Tr();

		print $q->start_Tr();
                print $q->start_td();
                print $q->start_div({ class => "action-window-label-text" });
                print gettext("Unplumb adapter:");
                print $q->end_div();
                print $q->end_td();
                print $q->start_td();
		print $q->checkbox({name => "group", value => "unplumb",
		    label => ""});
                print $q->end_td();
                print $q->end_Tr();

                print $q->end_table();

		$q->start_message_table("warning");
                print $q->start_p({ class => "message-description-text" });
                print gettext("This action will permanently remove the adapter from the IPMP group.");
                print $q->end_p();
                $q->end_message_table($q);

		$q->buttons_table(gettext(" Remove Adapter "),
			"config_adapter_submit('Remove', '$node')",
                        gettext("  Cancel  "),
                        "self.location.href = \"$VIEW\?node=$node\"" );
                print "</form>";
	} else {
		my $cmd;

		# Check the number of adapters in the group
		my $ipmpdata = $self->get_ipmpinfo($node);
		my %groupcount; # Number of adapters in group
		my $group = ""; # Group of the adapter
		for (my $i = 0; $i <= $#{@{$ipmpdata}}; $i++) {
		    $groupcount{$ipmpdata->[$i]{'name'}}++;
		    if ($ipmpdata->[$i]{'adp'} eq $adp) {
			$group = $ipmpdata->[$i]{'name'};
		    }
		}

		if ($step == 3) {
		    # Do the remove of potentially last adapter without if_mpadm
		    $cmd = "removelast $adp $unplumbgroup $unplumb";
		} elsif ($groupcount{$group} != 1) {
		    # Multiple adapters in group
		    $cmd = "remove $adp $unplumbgroup $unplumb";
		} else {
		    # Removing last adapter in the group
		    # Validate the removal
		    $q->print_title([gettext("Nodes"),
			    "/cgi-bin/node/node_status.pl",
			$node, "/cgi-bin/node/node_info.pl?node=$node",
			gettext("IPMP Groups"), "$VIEW?node=$node",
			gettext("Remove Adapter from IPMP Group")]);
		    print "<form>";
		    $q->start_message_table("warning");
		    print $q->start_p({ class => "message-header-text" });
		    print sprintf(gettext(
			"Removing last adapter in group %s"),
			$group);
		    print $q->end_p();
		    print $q->start_p({ class => "message-description-text" });
		    print gettext("Removing the last adapter in an IPMP group may cause a loss of communication with the node.");
		    print $q->end_p();
		    $q->end_message_table();
		  
		    $q->buttons_table(gettext(" Remove Adapter "),
			"self.location.href = \"$CONFIG\?step=3&func=Remove&node=$node&adp=$adp&unplumb=$unplumb&group=$unplumbgroup\"",
			gettext("  Cancel  "),
			"self.location.href=\"$VIEW\?node=$node\"" );
		    print "</form>";	
		    return 0;
		}
		
		# Executing the command
                $self->execute_cmd($q, $cmd, $node,
		    [gettext("Nodes"), "/cgi-bin/node/node_status.pl",
			$node, "/cgi-bin/node/node_info.pl?node=$node",
			gettext("IPMP Groups"), "$VIEW?node=$node",
			gettext("Remove Adapter"),
	    "$CONFIG\?step=3&func=Remove&node=$node&adp=$adp&unplumb=$unplumb"]);
	}
}

sub execute_cmd($$$$$$)
{
	my ($self, $q, $cmd, $node, $title) = @_;
	
	if (!$self->is_node_online($q, $node)) {
		return;
	}

	# replace space with + in order to pass the $cmd through web server 
	my $execcmd = $cmd;
	$execcmd =~ tr/ /+/;
	my $location = "$EXEC\?cmd=$execcmd";
	
	my ($continue_cmd) = pop @$title;
	$q->print_title($title);
	my @output = $self->get_http($node, $self->get_http_port(), $location);
	# Get the executed command back
	$cmd = "";
	if (!defined($output[0])) {
		# Communication error
		return;
	}
	# Remove the cmd= lines from the output
	for (my $i = 0; $i <= $#output; $i++) {
	    if ($output[$i] =~ /^cmd=(.*)/) {
		$cmd = join("<br>", $cmd, $1);
		splice(@output, $i, 1);
		$i--;
	    }
	}
	if ($output[$#output] eq "error") {
		pop @output; # Remove the "error" flag
		# There is error returned, print out error messages
		print "<form>";
                $q->start_message_table("error");
                print $q->start_p({ class => "message-header-text" });
		print gettext("Error");
                print $q->end_p();
		if ($cmd ne "") {
		    print $q->start_p({ class => "message-description-text" });
		    print gettext("The command:");
		    print $q->end_p();
		    print $q->start_p({ class => "message-command-text" });
		    print $cmd;
		    print $q->end_p();
		    print $q->start_p({ class => "message-description-text" });
		    print gettext("Failed with the following error:");
		    print $q->end_p();
		} else {
		    print $q->start_p({ class => "message-description-text" });
		    print gettext("The operation failed with the following error:");
		    print $q->end_p();
		}
                print $q->start_p({ class => "message-command-text" });
                print join("<br>",@output);
                print $q->end_p();

                $q->end_message_table();

		$q->buttons_table(gettext("  < Back  "), "history.back()",
			gettext("  Cancel  "),
			"self.location.href = \"$VIEW\?node=$node\"" );
                print "</form>";
	} elsif ($output[$#output] eq "if_mpadm error") {
		# Special case for if_mpadm problem; want to continue
		pop @output; # Remove the "error" flag
		# There is error returned, print out error messages
		print "<form>";
                $q->start_message_table("warning");
                print $q->start_p({ class => "message-header-text" });
		print gettext("Warning");
                print $q->end_p();
		print $q->start_p({ class => "message-description-text" });
		print gettext("The command:");
		print $q->end_p();
		print $q->start_p({ class => "message-command-text" });
		print $cmd;
		print $q->end_p();
		print $q->start_p({ class => "message-description-text" });
		print gettext("Failed with the following error:");
		print $q->end_p();
                print $q->start_p({ class => "message-command-text" });
                print join("<br>",@output);
                print $q->end_p();

		print $q->start_p({ class => "message-description-text" });
		print gettext("You may continue with the operation, but note that it may cause a loss of communication with the node.");
		print $q->end_p();
		$q->end_message_table();
		my ($link) = pop(@{$title});
		$q->buttons_table($link,
		    "self.location.href = \"$continue_cmd\"",
		    gettext("  Cancel  "),
		    "self.location.href=\"$VIEW\?node=$node\"" );
		print "</form>";	
		return 0;
	} else {
                print "<form>";
                $q->start_message_table("info");
                print $q->start_p({ class => "message-header-text" });
		print gettext("Operation Completed Successfully");
                print $q->end_p();
                print $q->start_p({ class => "message-description-text" });
                print gettext("The following command completed without error:");
                print $q->end_p();
                print $q->start_p({ class => "message-command-text" });
                print $cmd;
                print $q->end_p();
                print $q->start_p({ class => "message-command-text" });
                print join("<br>",@output);
                print $q->end_p();
                $q->end_message_table();
                $q->buttons_table(sprintf(gettext(" View IPMP Group Information on Node %s"), $node),
			"self.location.href= \"$VIEW\?node=$node\"" );
                print "</form>";	
        }	
}
1;
