#!/usr/local/bin/perl
#
#Script to update project_types
use strict;
use warnings;

use lib "/cell_root/software/HPCC/lib/site_perl";
#Temporary for debugging
#use lib "/afs/.glue.umd.edu/software/HPCC/1.1/lib/site_perl";

use Glue::HPCC::Cluster;
use Glue::HPCC::Project::ProjectType;
use Glue::HPCC::Project::Log;

use Getopt::Long;

#Globals

my $GHPPType = 'Glue::HPCC::Project::ProjectType';
my $GHPLog = 'Glue::HPCC::Project::Log';
my $where;
my $reason;
my $pkey;
my $ptname;
my $changed=0;
my $hpc;
my $DEBUG;

sub usage()
{	print STDERR <<EOF;
$0: Modify an existing project type

Usage:
$0 [ --cluster CLUSTER ] -type NAME \
	-reason REASON \
	[ -desc DESC ] \
	[ --newname NEWNAME ]  \
	[ --expiry DAYS ] \
	[ --auto-replenishes AUTO ] \
	[ --normal-priority NORMAL ] \
	[ --high-priority HIGH ] \
	[ --special-partition SPECIAL ] \
	[ --scavenger-queue SCAV ] \
	[ --debug-queue DEBUG ] \
	[ --unix-group UNXGRP ] \
	[ --members MEMBERS ] \
	[ --metered METERED ] \
	[ -qos_tag QOS_TAG ]
	

Modifies an existing project type record.  NAME is
the name of the project type.  CLUSTER determines
which Projects Database to connect to, and defaults
based on the host you are running the command on.
REASON is a reason which will be put in logs.

Rest of parameters are optional, and if given indicate
what fields to change.  If the parameter for the field
is NOT given, the field will be unchanged.  For boolean
fields, takes standard Perl boolean values EXCEPT undef
will be interpretteted as no change.

Values are:
DESC: new description for project_type
NEWNAME: new name for project_type (USE WITH CAUTION)
DAYS: new default expiry in days
AUTO: boolean, new auto-replenishes flag (true means replenishes)
NORMAL: boolean, new has_normal_priority account flag
HIGH: boolean, new high_priority account flag
SPECIAL: boolean, new has_special_partition flag
SCAV: boolean, new can_use_scavenger_q flag
DEBUG: boolean, new can_use_debug_q flag
UNXGRP: boolean, new requires_unix_group flag
MEMBERS: boolean, new can_have_members flag
METERED: boolean, new is_metered flag
QOS_TAG: string, new qos_tag.  Set to NULL to nullify existing.

EOF
}


sub update_field($$$)
{	my $field = shift;
	my $newval = shift;
	my $oldval = shift;

	#Changing name
	my $update = { $field => $newval };
	my $error = $GHPPType->pdb_update($hpc, $where, $update);
	die "Error changing field $field for ptype $ptname ($pkey): $error at "
		if $error;
	$GHPLog->add_log_entry($hpc,
		reason => $reason,
		action_code => 'modify-projtype',
		tablename => 'project_type',
		fieldname => $field,
		oldval => $oldval,
		newval => $newval,
		description => "change $field for project_type",
	);
	$changed++;
}

sub debug(@)
{	return unless $DEBUG;
	my $str = join '', @_;
	chomp $str;
	print STDERR "[DEBUG] $str\n";
}

sub do_boolean_change($$$) 
{	my $field = shift;
	my $new = shift;
	my $old = shift;

	return unless defined $new;
	my $tmp = 0;
	$tmp = 1 if ( $new && ! defined $old );
	$tmp = 1 if ( $new && ! $old );
	$tmp = 1 if ( $old && ! $new );
	return unless $tmp;

	update_field($field, $new, $old);
}

sub do_possible_null_change($$$)
#Do a change on a text field which can possibly be NULL
#If new is NULL, nullify existing value
{	my $field = shift;
	my $new = shift;
	my $old = shift;

	return unless $new; #Must be NULL to nullify, so empty is do nothing
	$new = undef if $new eq 'NULL';
	$new = undef unless $new;

	my $tmp = 0;
	$tmp = 1 if $new && ! $old;
	$tmp = 1 if $old && ! $new;
	$tmp = 1 if $old && $new && $old ne $new;
	return unless $tmp;

	update_field($field, $new, $old);
}
	

#-------------------------------------------------------------------
#		Main
#-------------------------------------------------------------------

my $help;
my $cluster;
my ($newdesc, $newauto, $newnormal, $newhigh);
my ( $newspecial, $newscavq, $newdebq);
my ($newdefexp, $newunxgrp, $newmembers, $newname);
my ( $newmetered, $newqos_tag);
my $FORCE;

my $res = GetOptions(
	'h|help!' => \$help,
	'force!' => \$FORCE,
	'debug!' => \$DEBUG,
	'cluster=s' => \$cluster,
	'type|name=s' => \$ptname,
	'reason=s' => \$reason,

	'description=s' => \$newdesc,
	'expiry-days|expiry_days=i' => \$newdefexp,
	'newname|new_name|new-name=s' => \$newname,
	'auto-replenishes|auto_replenishes=s' => \$newauto,
	'normal-priority|normal_priority=s' => \$newnormal,
	'high-priority|high_priority|hi-priority|hi_priority=s' => \$newhigh,
	'special-partition|special_partition=s' => \$newspecial,
	'scavenger-queue|scavenger_queue=s' => \$newscavq,
	'debug-queue|debug_queue=s' => \$newdebq,
	'unix-group|unix_group=s' => \$newunxgrp,
	'members=s' => \$newmembers,
	'metered=s' => \$newmetered,
	'qos_tag|qos-tag=s' => \$newqos_tag,
);

unless ( $res ) 
{	usage;
	die "\nError processing options.\n";
}
if ( $help )
{	usage;
	exit 0;
}

unless ($ptname)
{	usage;
	die "\nYou must supply a project type name\n";
}
unless ($reason)
{	usage;
	die "\nYou must supply a reason\n";
}

if ( scalar(@ARGV) )
{	usage;
	my $tmp = join ", ", @ARGV;
	die "\nExtraneous arguments [ $tmp ], aborting.\n";
}

#Default cluster if needed
$hpc = Glue::HPCC::Cluster->new_from_clustername($cluster);
die "\nInvalid cluster $cluster at " if $cluster && ! $hpc && ! ref($hpc);
die "\nError instantiating cluster $hpc " unless $hpc && ref($hpc);
$cluster = $hpc->db_clustername;

my $ptype = Glue::HPCC::Project::ProjectType->projecttype_by_name(
	$hpc, $ptname);
unless ( $ptype )
{	die "Unrecognized project type name '$ptname'.\n";
}
unless ( ref($ptype) )
{	die "Error looking up project type '$ptname': $ptype.\n";
}

$newqos_tag = lc $newqos_tag if $newqos_tag;

#Canonicalize the boolean inputs
$newauto = $newauto?1:0 if defined $newauto;
$newnormal = $newnormal?1:0 if defined $newnormal;
$newhigh = $newhigh?1:0 if defined $newhigh;
$newspecial = $newspecial?1:0 if defined $newspecial;
$newscavq = $newscavq?1:0 if defined $newscavq;
$newdebq = $newdebq?1:0 if defined $newdebq;
$newunxgrp = $newunxgrp?1:0 if defined $newunxgrp;
$newmembers = $newmembers?1:0 if defined $newmembers;
$newmetered = $newmetered?1:0 if defined $newmetered;

#Get current values
$pkey = $ptype->pkey;
my $desc = $ptype->description;
my $auto = $ptype->auto_replenishes?1:0;
my $normal  = $ptype->has_normal_priority?1:0;
my $high  = $ptype->has_high_priority?1:0;
my $special  = $ptype->has_special_partition?1:0;
my $scavq  = $ptype->can_use_scavenger_q?1:0;
my $debq  = $ptype->can_use_debug_q?1:0;
my $defexp = $ptype->default_expiry_days;
my $unxgrp  = $ptype->requires_unix_group?1:0;
my $members  = $ptype->can_have_members?1:0;
my $metered  = $ptype->is_metered?1:0;
my $qos_tag  = $ptype->qos_tag;

$where = { pkey => $pkey };
my $update;

if ( $newname && $ptname ne $newname )
{	unless ( $FORCE )
	{	print STDERR <<EOF;
*****	WARNING ******
You have requested to change the name of the
project type $ptname to $newname.

NOTE: some project type names are hardcoded in
assorted codes, template files, etc.  If you are not
sure what you are doing, changing a project type
name can break things.

If you are unsure, control-c out now.
If you are sure you want to do this, type 'yes' to 
continue:

EOF
		my $ans = <STDIN>;
		chomp $ans;
		unless ( $ans=~/^\s*y(es)?\s*$/i )
		{	die "Aborting at user request.\n";
		}
	}

	#Changing name
	update_field('name', $newname, $ptname);
}

if ( $newdesc && $desc ne $newdesc )
{	#Changing desc
	update_field('description', $newdesc, $desc);
}

if ( $newdefexp && $defexp != $newdefexp )
{	#Changing defexp
	update_field('default_expiry_days', 
		$newdefexp, $defexp);
}

do_boolean_change('auto_replenishes', $newauto, $auto);
do_boolean_change('has_normal_priority', $newnormal, $normal);
do_boolean_change('has_high_priority', $newhigh, $high);
do_boolean_change('has_special_partition', $newspecial, $special);
do_boolean_change('can_use_scavenger_q', $newscavq, $scavq);
do_boolean_change('can_use_debug_q', $newdebq, $debq);
do_boolean_change('requires_unix_group', $newunxgrp, $unxgrp);
do_boolean_change('can_have_members', $newmembers, $members);
$DEBUG=1;
do_boolean_change('is_metered', $newmetered, $metered);
$DEBUG=0;

do_possible_null_change('qos_tag', $newqos_tag, $qos_tag);

if ( $changed )
{	print "Committing changes to db\n";
	$hpc->db_commit;
} else
{	print "No changes made.\n";
}
