diff --git a/apt/APT_Instance.pm.in b/apt/APT_Instance.pm.in
index 26a6cd8a3f706a8b2cd8519c5ced5d5d722eed93..9cf69636ec11fc284af27774ebac9b1db7273b58 100644
--- a/apt/APT_Instance.pm.in
+++ b/apt/APT_Instance.pm.in
@@ -2993,15 +2993,25 @@ sub CheckResources($$)
     foreach my $request (@{$spectrum}) {
 	my $frequency_low  = $request->{"frequency_low"};
 	my $frequency_high = $request->{"frequency_high"};
+	my $frequency_uuid = $request->{"frequency_uuid"};
 	my $power          = $request->{"power"};
 	my $bandwidth      = $request->{"bandwidth"};
 	my $type           = $request->{"type"};
 	my $target         = $request->{"target"};
 	my $element        = $request->{"element"};
-	
-	if (!APT_Instance::RFRanges->Lookup(
-		 $self, $frequency_low, $frequency_high)) {
-	    my $rfrange = APT_Instance::RFRanges->Create(
+	my $rfrange;
+
+	# New way of doing this. But need to fall back.
+	if (defined($frequency_uuid)) {
+	    $rfrange = APT_Instance::RFRanges->Lookup(
+		$self, $frequency_uuid);
+	}
+	else {
+	    $rfrange = APT_Instance::RFRanges->Lookup(
+		$self, $frequency_low, $frequency_high);
+	}
+	if (!defined($rfrange)) {
+	    $rfrange = APT_Instance::RFRanges->Create(
 		$self, $frequency_low, $frequency_high,
 		$power, $bandwidth, $type, $target);
 
@@ -3009,6 +3019,8 @@ sub CheckResources($$)
 		$$perrmsg = "Could not create rfrange entry";
 		return -1;
 	    }
+	}
+	if (!GeniXML::GetText("frequency_uuid", $element)) {
 	    GeniXML::SetText("frequency_uuid", $element, $rfrange->freq_uuid());
 	    $modified = 1;
 	}
@@ -3694,7 +3706,7 @@ sub RDZFLAGS($)
     }
     return $self->{'RDZFLAGS'};
 }
-# For experiments using an inner RDZ
+# For experiments using an RDZ
 sub RDZINFO($)
 {
     my ($self) = @_;
diff --git a/apt/APT_Profile.pm.in b/apt/APT_Profile.pm.in
index 6a3323d9bfced028e21e9e08b49d4d40bbf83ce5..00eaec3df79c79b290197d9cf4c4ce7839b3874f 100755
--- a/apt/APT_Profile.pm.in
+++ b/apt/APT_Profile.pm.in
@@ -2531,12 +2531,6 @@ sub CheckSpectrum($$;$$)
 		    return 1;
 		}
 	    }
-	    else {
-		# This will get written back to the RSPEC later
-		$frequency_uuid = NewUUID();
-		$request->{"frequency_uuid"} = $frequency_uuid;
-		GeniXML::SetText("frequency_uuid", $element, $frequency_uuid);		
-	    }
 	}
 	return 0;
     };
@@ -2830,7 +2824,7 @@ sub CheckSpectrumReservations($$$$$$$$;$$)
 	# The user does not provide this
 	$request->{"frequency_zones"} = $resources->{"zones"};
 
-	if (!$noresokay &&
+	if (!$noresokay && !$rdzenabled && 
 	    !APT_Reservation::Group::RFReservation->RangeReserved(
 		 $group, $user, $start, $end,
 		 $frequency_low, $frequency_high, $resources->{"zones"})) {
diff --git a/apt/APT_RDZ.pm.in b/apt/APT_RDZ.pm.in
index 3ac6beae1797d59b50c048728c9117b5f5ee00d2..37b7040d41d66c6ecbb6e789b4588a0cb7a91a62 100644
--- a/apt/APT_RDZ.pm.in
+++ b/apt/APT_RDZ.pm.in
@@ -30,6 +30,7 @@ use JSON;
 use Scalar::Util qw(blessed);
 use Socket;
 use Errno;
+use POSIX qw(isatty);
 use POSIX qw(:sys_wait_h);
 use Date::Parse;
 use Carp;
@@ -121,13 +122,14 @@ sub GetRDZFlags($)
 sub GetZMC($)
 {
     my ($instance) = @_;
+    my $rdzinfo = $instance->RDZINFO();
 
-    # Not using an inner RDZ
-    return APT_RDZRest::ZMC->New(APT_RDZRest::POWDER())
-	if (! $instance->rdz_rdzinfo());
+    # Not using an inner RDZ, talk to outer RDZ as the temp user.
+    return APT_RDZRest::ZMC->New($rdzinfo->{'usertoken'})
+	if (! exists($rdzinfo->{'url'}));
     
-    my $url = $instance->RDZINFO()->{'url'};
-    my $token = $instance->RDZINFO()->{'element_token'};
+    my $url = $rdzinfo->{'url'};
+    my $token = $rdzinfo->{'element_token'};
     return APT_RDZRest::ZMC->New($token, $url);
 }
 
@@ -162,6 +164,8 @@ sub initRDZstate($$)
     my ($instance, $perrmsg) = @_;
     my $rspec   = $instance->RSPEC();
     my @dbflags = ();
+    # All experiments using an RDZ get this. 
+    my $rdzinfo = {};
 
     return 0
 	if (!UseRDZ($rspec));
@@ -174,7 +178,6 @@ sub initRDZstate($$)
 	push(@dbflags, "rdzinrdz");
     }
     elsif (UseRDZinRDZ($rspec)) {
-	my $rdzinfo;
 	my $element = UseRDZ($rspec);
 
 	if (my $parent = GeniXML::GetText("instance", $element)) {
@@ -253,15 +256,16 @@ sub initRDZstate($$)
     if (@dbflags) {
 	$instance->SetRDZFLAGS(\@dbflags);
     }
+    $instance->SetRDZINFO($rdzinfo);
     $instance->Update({'rdz_status' => "new"});
 }
 
 #
-# Special case for RDZ-in-RDZ experiments. 
+# Setup for experiments using an RDZ.
 #
-sub setupRDZinRDZ($$$)
+sub setupUsingRDZ($$$)
 {
-    my ($instance, $grant_ids, $perrmsg) = @_;
+    my ($instance, $ZMC, $perrmsg) = @_;
     my $pid     = $instance->pid();
     my $eid     = $instance->name();
     my $email   = $instance->GetGeniUser()->email();
@@ -275,13 +279,65 @@ sub setupRDZinRDZ($$$)
     return 0
 	if (GeniXML::GetText("user-id", $element));
 
+    my $rdzinfo = $instance->RDZINFO();
+
+    #
+    # When using the outer RDZ, we put in the temporary user info.
+    # When using an inner RDZ, we put in just inner element token.
+    #
+    if (UseRDZinRDZ($rspec)) {
+	if (! exists($rdzinfo->{'element_token'})) {
+	    $$perrmsg = "setupUsingRDZ: Inconsistent rdzinfo";
+	    return -1;
+	}
+	
+	# Use an encrypted password to store the outer token
+	my $pswdref = AddElement("password", $rspec, $GeniXML::EMULAB_NS);
+	SetText("name", $pswdref, "element-token");
+	$pswdref->appendText($rdzinfo->{'element_token'});
+    }
+    else {
+	if (! exists($rdzinfo->{'usertoken'})) {
+	    $$perrmsg = "setupUsingRDZ: Inconsistent rdzinfo";
+	    return -1;
+	}
+	GeniXML::SetText("user-id", $element, $rdzinfo->{"user-id"});
+	GeniXML::SetText("username", $element, $rdzinfo->{"username"});
+
+	# Use an encrypted password to store the outer token
+	my $pswdref = AddElement("password", $rspec, $GeniXML::EMULAB_NS);
+	SetText("name", $pswdref, "element-token");
+	$pswdref->appendText($rdzinfo->{'usertoken'});
+    }
+    GeniXML::SetText("zmc-http", $element, $ZMC->url());
+    GeniXML::SetText("element-id", $element, $ZMC->elementid());
+    
+    return 0;
+}
+
+#
+# Special case for RDZ-in-RDZ experiments. 
+#
+sub setupRDZinRDZ($$$)
+{
+    my ($instance, $grant_ids, $perrmsg) = @_;
+    my $pid     = $instance->pid();
+    my $eid     = $instance->name();
+    my $email   = $instance->GetGeniUser()->email();
+    my $rspec   = $instance->RSPEC();
+    my $element = UseRDZ($rspec);
+
+    return 0
+	if (!defined($element));
+
+    my $rdzinfo = $instance->RDZINFO();
     my $rdzinrdzinfo = $instance->RDZINRDZINFO();
-    if (! (defined($rdzinrdzinfo) && exists($rdzinrdzinfo->{'usertoken'}))) {
-	$$perrmsg = "setupRDZinRDZ: Inconsistent rdzinrdzinfo";
+    
+    if (! (defined($rdzinrdzinfo) && defined($rdzinfo) &&
+	   exists($rdzinfo->{'usertoken'}))) {
+	$$perrmsg = "setupRDZinRDZ: Inconsistent rdzinfo";
 	return -1;
     }
-    GeniXML::SetText("user-id", $element, $rdzinrdzinfo->{"user-id"});
-    GeniXML::SetText("username", $element, $rdzinrdzinfo->{"username"});
 
     # Add temporary user ID as an ansible fact.
     my $override = AddElement("override", $rspec, $GeniXML::ANSIBLE_NS);
@@ -289,12 +345,12 @@ sub setupRDZinRDZ($$$)
     SetText("source", $override, "");
     SetText("source_name", $override, "");
     SetText("on_empty", $override, "");
-    $override->appendText($rdzinrdzinfo->{"user-id"});
+    $override->appendText($rdzinfo->{"user-id"});
     
     # Use an encrypted password to store the outer token
     my $pswdref = AddElement("password", $rspec, $GeniXML::EMULAB_NS);
     SetText("name", $pswdref, "openzms_parent_element_token");
-    $pswdref->appendText($rdzinrdzinfo->{'usertoken'});
+    $pswdref->appendText($rdzinfo->{'usertoken'});
 
     # And add it as an ansible fact.
     $override = AddElement("override", $rspec, $GeniXML::ANSIBLE_NS);
@@ -309,16 +365,8 @@ sub setupRDZinRDZ($$$)
     SetText("source", $override, "");
     SetText("source_name", $override, "");
     SetText("on_empty", $override, "");
-    $override->appendText($rdzinrdzinfo->{"element-id"});
+    $override->appendText($rdzinfo->{"element-id"});
 
-    #
-    # Insert the grant list that provides spectrum to the inner RDZ
-    #
-    my $spectrum = AddElement("spectrum", $element, $GeniXML::EMULAB_NS);
-    foreach my $id (@$grant_ids) {
-	my $gref = AddElement("grant", $spectrum, $GeniXML::EMULAB_NS);
-	SetText("grant-id", $gref, $id);
-    }
     # And add it as an ansible fact.
     $override = AddElement("override", $rspec, $GeniXML::ANSIBLE_NS);
     SetText("name", $override, "openzms_parent_grant_list");
@@ -369,11 +417,6 @@ sub setupRDZinRDZ($$$)
     $rdzinrdzinfo->{'admin_token'} = $admin_token;
     $instance->SetRDZINRDZINFO($rdzinrdzinfo);
 
-    # Must "Finalize" the rspec again since we added encrypted blocks.
-    my $retval = $instance->FinalizeRSpec($perrmsg);
-    return $retval
-	if ($retval);
-
     return 0;
 }
 
@@ -425,19 +468,18 @@ sub TerminateRDZinRDZExperiments($$)
     return 0;
 }
 
-sub cleanupRDZinRDZ($)
+sub cleanupRDZ($)
 {
     my ($instance) = @_;
 
-    # Not an rdzinrdz.
     return 0
-	if (!$instance->rdz_rdzinrdzinfo());
+	if (!$instance->rdz_rdzinfo());
 
     # Might not have got this far.
     return 0
-	if (!exists($instance->RDZINRDZINFO()->{'user-id'}));
+	if (!exists($instance->RDZINFO()->{'user-id'}));
     
-    return DeleteTemporaryUser($instance->RDZINRDZINFO()->{'user-id'});
+    return DeleteTemporaryUser($instance->RDZINFO()->{'user-id'});
 }
 
 #
@@ -486,15 +528,19 @@ sub MapOuterToInnerRadios($$$)
 # Block, waiting for RDZ to approve spectrum requests
 # Failure to get the spectrum throws the instance into a failed state.
 #
-sub RequestSpectrum($$)
+sub RequestSpectrum($$;$)
 {
-    my ($instance, $perrmsg) = @_;
+    my ($instance, $perrmsg, $overwrite) = @_;
     my $failed = 0;
+    my $denied = 0;
     my $failure_message = "Internal error";
     my @grants = ();
     my @radio_ports = ();
     my %dynamic = ();
     my $rdzinrdzinfo;
+    # Debuging!!!
+    my $debug_denial = 0;
+    my $denial_count = 1;
 
     print "RequestSpectrum: $instance\n";
 
@@ -503,20 +549,47 @@ sub RequestSpectrum($$)
 	print "RequestSpectrum: No rfranges for $instance\n";
 	return 0;
     }
+    # Lets see if there is anything to do.
+    my %tmp = ();
+    foreach my $rfrange (values(%{$rfranges})) {
+	my $frequency_low  = $rfrange->freq_low();
+	my $frequency_high = $rfrange->freq_high();
+    	my $grant;
 
-    my $ZMC = GetZMC($instance);
-    if (!defined($ZMC)) {
-	$$perrmsg = "Cannot open the ZMC rest interface";
-	print "RequestSpectrum: $$perrmsg\n";
-	return -1;
+	if ($rfrange->rdz_grantid()) {
+	    if ($rfrange->rdz_status() eq "active" && !$overwrite) {
+		print "RequestSpectrum: Already have a grant for ".
+		    "$frequency_low, $frequency_high\n";
+		next;
+	    }
+	    else {
+		print "RequestSpectrum: Clearing existing grant for ".
+		    "$frequency_low, $frequency_high\n";
+		$rfrange->Update({"rdz_grantid" => undef,
+				  "rdz_status"  => undef});
+	    }
+	}
+	print "RequestSpectrum: Adding $rfrange to the list\n";
+	
+	$tmp{$rfrange->idx()} = $rfrange;
     }
-    print $ZMC . "\n";
+    return -1
+	if ($failed);
+    
+    if (!keys(%tmp)) {
+	print "RequestSpectrum: No rfranges left to process for $instance\n";
+	return 0;
+    }
+    $rfranges = \%tmp;
+
+    # Update the web UI
+    $instance->SetStatus("rdzwait");
     
     my $heartbeats = exists($instance->RDZFLAGS()->{'heartbeats'});
     # Is acting as an RDZinRDZ
-    my $rdzinrdz = exists($instance->RDZFLAGS()->{'rdzinrdz'});
-    # Is using an RDZinRDZ; null if using the root RDZ
-    my $userdzinrdz = $instance->rdz_rdzinfo();
+    my $is_rdzinrdz = exists($instance->RDZFLAGS()->{'rdzinrdz'});
+    # Is using an RDZinRDZ
+    my $use_rdzinrdz = exists($instance->RDZINFO()->{'element_token'});
     
     my $resources = $instance->_resources();
     if (!defined($resources)) {
@@ -544,12 +617,37 @@ sub RequestSpectrum($$)
 	    }
 	    else {
 		$$perrmsg = "$radio is not registered with the RDZ";
+
 		print "RequestSpectrum: $$perrmsg\n";
 		return -1;
 	    }
 	}
     }
-    if ($userdzinrdz) {
+    my $rdzinfo = $instance->RDZINFO();
+    # No temporary users when using an inner RDZ, not needed.
+    if (!$use_rdzinrdz && !exists($rdzinfo->{'user-id'})) {
+	my $pid   = $instance->pid();
+	my $eid   = $instance->name();
+	my $email = $instance->GetGeniUser()->email();
+	my $user;
+	    
+	if (CreateTemporaryUser($pid, $eid, $email, \$user, 1)) {
+	    $$perrmsg = $user;
+	    return -1;
+	}
+	# Update DB
+	$rdzinfo->{'user-id'}    = $user->{"user_id"};
+	$rdzinfo->{'username'}   = $user->{"username"};
+	$rdzinfo->{'usertoken'}  = $user->{"token"};
+	$rdzinfo->{'element-id'} = $user->{"element_id"};
+
+	# Must do this now in case of errors below.
+	$instance->SetRDZINFO($rdzinfo);
+    }
+    my $ZMC = GetZMC($instance);
+    print $ZMC . "\n";
+    
+    if ($use_rdzinrdz) {
 	if (MapOuterToInnerRadios($instance, \%radios, $perrmsg)){
 	    return -1;
 	}
@@ -558,54 +656,21 @@ sub RequestSpectrum($$)
 	    push(@radio_ports, {"radio_port_id" => $id});
 	}
     }
-    elsif ($rdzinrdz) {
-	$rdzinrdzinfo = $instance->RDZINRDZINFO();
-	if (!exists($rdzinrdzinfo->{'user_id'})) {
-	    my $pid   = $instance->pid();
-	    my $eid   = $instance->name();
-	    my $email = $instance->GetGeniUser()->email();
-	    my $user;
-	    
-	    if (CreateTemporaryUser($pid, $eid, $email, \$user, 1)) {
-		$$perrmsg = $user;
-		return -1;
-	    }
-	    # Update DB, marking it as an rdzinrdz
-	    $rdzinrdzinfo = {
-		'user-id'    => $user->{"user_id"},
-		'username'   => $user->{"username"},
-		'usertoken'  => $user->{"token"},
-		'element-id' => $user->{"element_id"},
-	    };
-	    # Must do this now in case of errors below.
-	    $instance->SetRDZINRDZINFO($rdzinrdzinfo);
 
-	    $ZMC = APT_RDZRest::ZMC->New($user->{'token'});
-	    if (!defined($ZMC)) {
-		$$perrmsg = "Cannot create ZMC rest interface for ".
-		    "temporary RDZinRDZ user";
-		return -1;
-	    }
-	}
-    }
-    
     foreach my $rfrange (values(%{$rfranges})) {
 	my $frequency_low  = $rfrange->freq_low();
 	my $frequency_high = $rfrange->freq_high();
 	my $power          = $rfrange->power();
 	my $width          = $rfrange->width();
+    	my $grant;
     
-	# As for Modify, although unlikely. 
-	next
-	    if ($rfrange->rdz_grantid());
-
 	print "RequestSpectrum: Requesting $frequency_low, $frequency_high\n";
 
 	my $request = {
 	    "name"        => $instance->Printable(),
 	    "description" => "Powder Experiment",
 	    "html_url"    => $instance->webURL(),
-	    "ext_id"	  => $instance->uuid(),
+	    "ext_id"	  => $instance->uuid() . ":" . $rfrange->freq_uuid(),
 	    "element_id"  => $ZMC->elementid(),
 	    "starts_at"   => TBDateStringGMT($instance->started()),
 	    "expires_at"  => TBDateStringGMT($instance->Expires()),
@@ -626,7 +691,6 @@ sub RequestSpectrum($$)
 	    $request->{"constraints"}[0]->{"constraint"}->{'bandwidth'} = $width * 1000000;
 	}
 	print Dumper($request);
-	my $grant;
 	if ($ZMC->POST("/grants", $request, \$grant)) {
 	    $failure_message = "Internal error";
 	    $failed++;
@@ -649,17 +713,14 @@ sub RequestSpectrum($$)
 	# Update DB so we know we have a grant for this range.
 	$rfrange->Update({"rdz_grantid" => $grant_id,
 			  "rdz_status"  => "new"});
-	
+
+	$grant->{'approved_at'} = undef;
 	push(@grants, $grant);
-	# We have to update these below. 
+	# We have to update these below.
 	if (defined($width) && $width != 0) {
-	    $dynamic{"$grant_id"} = $rfrange;
+	    $dynamic{"$grant_id"} = $grant_id;
 	}
-	#
-	# XXX We want to do a poll below even though it was
-	# probably approved immediately.
-	#
-	$grant->{'approved_at'} = undef;
+	$grant->{'rfrange'} = $rfrange;
     }
     
     #
@@ -683,6 +744,7 @@ sub RequestSpectrum($$)
 	
 	foreach my $grant (@grants) {
 	    my $grant_id = $grant->{'id'};
+	    my $rfrange  = $grant->{'rfrange'};
 
 	    # Got our approval already, no need to ask.
 	    if ($grant->{'approved_at'}) {
@@ -705,6 +767,40 @@ sub RequestSpectrum($$)
 		$failure_message = "No grant ID in response";
 		$failed++;
 	    }
+	    elsif ($response->{'denied_at'} || ($debug_denial && $denial_count)) {
+		my $type  = $rfrange->type();
+		my $low   = $rfrange->freq_low();
+		my $high  = $rfrange->freq_high();
+		my $width = $rfrange->width() ? "," . $rfrange->width() : "";
+		
+		$failure_message = "Grant for $type range $low,$high${width} denied";
+		$waiting--;
+		$denied++;
+		# The grant is auto deleted, so clear this.
+		$rfrange->Update({"rdz_status"  => undef,
+				  "rdz_grantid" => undef});
+
+		if (!$debug_denial) {
+		    #
+		    # Lets find the log entry, for more info
+		    #
+		    foreach my $log (@{$response->{'logs'}}) {
+			if ($log->{'status'} eq "denied") {
+			    $failure_message .= ": " . $log->{'message'};
+			    last;
+			}
+		    }
+		}
+		else {
+		    $failure_message .= ": forced denial for testing";
+		    # A denied grant is auto deleted, so do that if the
+		    # grant was actuall approved
+		    $ZMC->DELETE("/grants/$grant_id", \$response)
+			if ($response->{'approved_at'});
+		    $denial_count--;
+		}
+		print "RequestSpectrum: $failure_message\n";
+	    }
 	    elsif ($response->{'approved_at'}) {
 		$grant->{'approved_at'} = $response->{'approved_at'};
 		$waiting--;
@@ -714,7 +810,6 @@ sub RequestSpectrum($$)
 		# For dynamic requests, need to update the rfrange.
 		#
 		if (exists($dynamic{"$grant_id"})) {
-		    my $rfrange = $dynamic{"$grant_id"};
 		    my $details = $response->{"constraints"}[0]->{"constraint"};
 		    my $low     = $details->{"min_freq"} / 1000000.0;
 		    my $high    = $details->{"max_freq"} / 1000000.0;
@@ -722,25 +817,22 @@ sub RequestSpectrum($$)
 		    print "RequestSpectrum: rfrange for $grant_id updated ".
 			"to $low,$high\n";
 
-		    if (UpdateSpectrum($instance, $rfrange, $low, $high)) {
+		    if (UpdateSpectrum(
+			    $instance, $rfrange, $low, $high, $grant_id)) {
 			$failure_message = "Failed to update range";
 			$failed++;
 		    }
 		}
-	    }
-	    elsif ($response->{'denied_at'}) {
-		$failure_message = "Grant $grant_id denied";
-		$failed++;
-		print "RequestSpectrum: Grant ID $grant_id denied\n";
+		$rfrange->Update({"rdz_status" => "active"});
 	    }
 	    elsif ($response->{'revoked_at'}) {
 		$failure_message = "Grant $grant_id revoked";
 		$failed++;
 		print "RequestSpectrum: Grant ID $grant_id revoked\n";
+		$rfrange->Update({"rdz_status" => "revoked"});
 	    }
 	    last
 		if ($failed);
-	    
 	}
 	last
 	    if ($failed || $waiting == 0);
@@ -757,19 +849,36 @@ sub RequestSpectrum($$)
     goto failed
 	if ($failed);
 
-    # Write the rspec back with narrowed frequency ranges.
-    if (keys(%dynamic)) {
-	$instance->SetRSPEC($instance->RSPEC());
+    if (!$denied) {
+	print "RequestSpectrum: Got all grant requests\n";
+    }
+    else {
+	print "RequestSpectrum: Not able to get all grants\n";
     }
-    print "RequestSpectrum: Got all grant requests\n";
 
-    if ($rdzinrdz) {
+    if (!$denied) {
 	my @grant_ids = map { $_->{'id'} } @grants;
-	if (setupRDZinRDZ($instance, \@grant_ids, \$failure_message)) {
+    
+	# Add some extra info
+	if (setupUsingRDZ($instance, $ZMC, \$failure_message)) {
 	    goto failed;
 	}
+	
+	# We can defer this until we have all the grants.
+	if ($is_rdzinrdz) {
+	    if (setupRDZinRDZ($instance, \@grant_ids, \$failure_message)) {
+		goto failed;
+	    }
+	}
+	# We made various rspec changes at this point, including encrypted blocks
+	my $retval = $instance->FinalizeRSpec(\$failure_message);
+	goto failed
+	    if ($retval)
     }
-    return 0;
+    
+    $$perrmsg = $failure_message if ($denied);
+    # Signal the caller we can defer/retry
+    return $denied;
 
   failed:
     # Lets not keep anything that we did get.
@@ -780,12 +889,94 @@ sub RequestSpectrum($$)
     return -1;
 }
 
+#
+# Update the spectrum allocations, by issuing new requests to the RDZ for
+# grants that have been revoked. Might want to generalize this to requesting
+# all new spectrum. 
+#
+sub UpdateSpectrumRequest($$)
+{
+    my ($instance, $perrmsg) = @_;
+    
+    print "UpdateSpectrumRequest: $instance\n";
+
+    my $rfranges = APT_Instance::RFRanges->LookupForInstance($instance);
+    if (!defined($rfranges) || !keys(%{$rfranges})) {
+	print "UpdateSpectrumRequests: No rfranges for $instance\n";
+	return 0;
+    }
+    
+    foreach my $rfrange (values(%{$rfranges})) {
+	my $grant_id = $rfrange->rdz_grantid();
+
+	if (!$grant_id ||
+	    $rfrange->rdz_status() =~ /(deleted|denied|revoked)/) {
+
+	    if (DeleteGrant($rfrange, $perrmsg)) {
+		return -1;
+	    }
+	    if (ResetSpectrum($instance, $rfrange)) {
+		$$perrmsg = "Could not reset $rfrange";
+		return -1;
+	    }
+	}
+    }
+    $instance->SetStatus("rdzwait");
+    if (my $rval = APT_RDZ::RequestSpectrum($instance, $perrmsg)) {
+	$instance->SetStatus("ready");
+	return $rval;
+    }
+    $instance->SetStatus("ready");
+    return 0;
+}
+
+#
+# Delete grant associated with a single rfrange
+#
+sub DeleteGrant($$;$)
+{
+    my ($rfrange, $perrmsg, $ZMC) = @_;
+    my $instance = $rfrange->instance();
+    my $grant_id = $rfrange->rdz_grantid();
+
+    if (!$grant_id || $rfrange->rdz_status() =~ /(deleted|denied|revoked)/) {
+	$rfrange->Update({"rdz_status"  => undef,
+			  "rdz_grantid" => undef});
+	return 0;
+    }
+    print "DeleteGrant: Deleting grant ID $grant_id for rfrange: $rfrange\n";
+
+    if (!defined($ZMC)) {
+	$ZMC = GetZMC($instance);
+	if (!defined($ZMC)) {
+	    $$perrmsg = "DeleteGrants: Could not get the REST object";
+	    print $$perrmsg . "\n";
+	    return -1;
+	}
+    }
+    my $response;
+    if ($ZMC->DELETE("/grants/$grant_id", \$response)) {
+	# Ignore if already deleted.
+	if ($response->{'error'} =~ /already deleted/i) {
+	    print "DeleteGrant: Grant has already been deleted\n";
+	}
+	else {
+	    $$perrmsg = "DeleteGrant: Could not delete grant ID $grant_id";
+	    print $$perrmsg . "\n";
+	    return -1;
+	}
+    }
+    $rfrange->Update({"rdz_status"  => undef,
+		      "rdz_grantid" => undef});
+    return 0;
+}
+
 #
 # Delete grants, as when terminating an experiment.
 #
-sub DeleteGrants($)
+sub DeleteGrants($$)
 {
-    my ($instance) = @_;
+    my ($instance, $perrmsg) = @_;
     my $errors = 0;
     my $ZMC;
 
@@ -798,12 +989,10 @@ sub DeleteGrants($)
     }
     foreach my $rfrange (values(%{$rfranges})) {
 	my $grant_id = $rfrange->rdz_grantid();
-	if (!$grant_id) {
-	    next;
-	}
-	if ($rfrange->rdz_status() ne "deleted") {
-	    print "DeleteGrants: Deleting grant ID $grant_id\n";
+	next
+	    if (!$grant_id);
 
+	if ($rfrange->rdz_status() !~ /(deleted|denied|revoked)/) {
 	    if (!defined($ZMC)) {
 		$ZMC = GetZMC($instance);
 		if (!defined($ZMC)) {
@@ -811,15 +1000,11 @@ sub DeleteGrants($)
 		    return -1;
 		}
 	    }
-	    my $response;
-	    if ($ZMC->DELETE("/grants/$grant_id", \$response)) {
-		print "DeleteGrants: Could not delete grant ID $grant_id\n";
+	    if (DeleteGrant($rfrange, $perrmsg, $ZMC)) {
 		$errors++;
 		next;
 	    }
-	    $rfrange->Update({"rdz_status" => "deleted"});
 	}
-	$rfrange->Update({"rdz_grantid" => undef});
     }
     return $errors;
 }
@@ -875,6 +1060,59 @@ sub ExtendGrantsFromInstance($$)
 		$errors++;
 		next;
 	    }
+	    #
+	    # The return value is now the new grant. The approval/denial
+	    # might not have happened yet, and we will poll for a little
+	    # while so we can return a useful error to the user/webui.
+	    #
+	    # Also note that we handle the actual replacement via the event
+	    # handling in WatchExperiment() below. 
+	    #
+	    my $new_grant_id = $response->{'id'};
+	    my $new_status = $response->{'status'};
+	    print "Grant replacement ID: $new_grant_id\n";
+	    print "Grant replacement status: $new_status\n";
+
+	    next
+		if ($new_status eq "active");
+
+	    #
+	    # Wait for the new grant to go active or denied. Lets not wait
+	    # too long though. 
+	    #
+	    sleep(1);
+	    for (my $i = 0; $i < 15; $i++) {
+		if ($ZMC->GET("/grants/$new_grant_id", \$grant)) {
+		    print "ExtendGrants: Could not GET new grant $new_grant_id\n";
+		    $errors++;
+		    next;
+		}
+		$new_status = $grant->{'status'};
+		print "Grant replacement status: $new_status\n";
+
+		last
+		    if ($new_status eq "active");
+
+		# A denied grant will automatically be deleted ...
+		if ($new_status eq "denied" || $new_status eq "deleted") {
+		    #print Dumper($grant);
+		    
+		    #
+		    # No need to delete (auto deleted), but we do want the log.
+		    #
+		    foreach my $log (@{$grant->{'logs'}}) {
+			if ($log->{'status'} eq "denied") {
+			    $$perrmsg = "RDZ extension denied: " .
+				$log->{'message'};
+			    print "ExtendGrants: $$perrmsg\n";
+			    last;
+			}
+		    }
+		    $errors++;
+		    last;
+		}
+		sleep(2);
+	    }
 	}
     }
     return $errors;
@@ -882,21 +1120,32 @@ sub ExtendGrantsFromInstance($$)
 
 #
 # Update the rspec with the narrowed RDZ frequency range.
+# XXX Only looking at global spectrum. Fix later.
 #
-sub UpdateSpectrum($$$$)
+sub UpdateSpectrum($$$$$)
 {
-    my ($instance, $rfrange, $new_low, $new_high) = @_;
+    my ($instance, $rfrange, $new_low, $new_high, $grant_id) = @_;
     my $rspec = $instance->RSPEC();
 
     my $updateGlobal = sub () {
 	my $spectrum = GeniXML::GetSpectrum($rspec);
 
 	foreach my $request (@{$spectrum}) {
-	    if ($request->{"frequency_low"} == $rfrange->freq_low() &&
-		$request->{"frequency_high"} == $rfrange->freq_high()) {
+	    if ($request->{"frequency_uuid"} eq $rfrange->freq_uuid()) {
 		my $xmlref = $request->{'element'};
+
+		# This is handy information for debugging.
+		if (!GeniXML::GetText("original_frequency_low", $xmlref)) {
+		    GeniXML::SetText("original_frequency_low", $xmlref,
+				     $request->{"frequency_low"});
+		}
+		if (!GeniXML::GetText("original_frequency_high", $xmlref)) {
+		    GeniXML::SetText("original_frequency_high", $xmlref,
+				     $request->{"frequency_high"});
+		}
 		GeniXML::SetText("frequency_low", $xmlref, $new_low);
 		GeniXML::SetText("frequency_high", $xmlref, $new_high);
+		GeniXML::SetText("rdz_grant_id", $xmlref, $grant_id);
 	    }
 	}
     };
@@ -917,6 +1166,38 @@ sub UpdateSpectrum($$$$)
     return 0;
 }
 
+#
+# For dynamic ranges, we want to reset the low,high back to their
+# original values so that we can issue new requests.
+# 
+sub ResetSpectrum($$)
+{
+    my ($instance, $rfrange) = @_;
+    my $rspec = $instance->RSPEC();
+
+    if (1) {
+	# Global spectrum only at the moment.
+	my $spectrum = GeniXML::GetSpectrum($rspec);
+
+	foreach my $request (@{$spectrum}) {
+	    if ($request->{"frequency_uuid"} eq $rfrange->freq_uuid()) {
+		my $xmlref = $request->{'element'};
+		
+		GeniXML::SetText("frequency_low", $xmlref,
+				 $rfrange->rdz_freq_low());
+		GeniXML::SetText("frequency_high", $xmlref,
+				 $rfrange->rdz_freq_high());
+		last
+	    }
+	}
+    }
+    if ($rfrange->Update({"freq_low"  => $rfrange->rdz_freq_low(),
+			  "freq_high" => $rfrange->rdz_freq_high()}, 1)) {
+	return 1;
+    }
+    return 0;
+}
+
 #
 # Convert range reservations to RDZ claims.
 #
@@ -1027,8 +1308,15 @@ sub DeleteClaimFromReservation($$)
     }
     my $response;
     if ($ZMC->DELETE("/claims/$claim_id", \$response)) {
-	$$perrmsg = "\n" . "Could not delete claim for $range";
-	return -1;
+	# Ignore if already deleted.
+	if ($response->{'error'} =~ /already deleted/i) {
+	    print "DeleteClaimFromReservation: Claim $claim_id ".
+		"has already been deleted\n";
+	}
+	else {
+	    $$perrmsg = "\n" . "Could not delete claim $claim_id for $range";
+	    return -1;
+	}
     }
     $range->RDZMarkUnclaimed();
     return 0;
@@ -1154,8 +1442,15 @@ sub DeleteClaimsFromInstance($$)
 
 	my $response;
 	if ($ZMC->DELETE("/claims/$claim_id", \$response)) {
-	    $$perrmsg = "\n" . "Could not delete claim for $range";
-	    return -1;
+	    # Ignore if already deleted.
+	    if ($response->{'error'} =~ /already deleted/i) {
+		print "DeleteClaimsFromInstance: Claim $claim_id ".
+		    "has already been deleted\n";
+	    }
+	    else {
+		$$perrmsg = "\n" . "Could not delete claim $claim_id for $range";
+		return -1;
+	    }
 	}
 	$range->Update({"rdz_claimid" => undef});
     }
@@ -1341,12 +1636,19 @@ sub DeleteTemporaryUser($)
 #
 # ZMS Constants. There are many more.
 #
-our $EVENTTYPE_REPLACED	= 1;
-our $EVENTTYPE_UPDATED	= 3;
-our $EVENTTYPE_REVOKED	= 5;
-our $EVENTSOURCE_ZMC     = 2;
-our $EVENTSOURCE_DST     = 3;
-our $EVENTCODE_GRANT	= 2006;
+our $EVENTTYPE_REPLACED 	= 1;
+our $EVENTTYPE_CREATED		= 2;
+our $EVENTTYPE_UPDATED  	= 3;
+our $EVENTTYPE_DELETED		= 4;
+our $EVENTTYPE_REVOKED		= 5;
+our $EVENTTYPE_APPROVED		= 8;     # approved
+our $EVENTTYPE_DENIED		= 9;     # 
+our $EVENTTYPE_STARTED		= 18;    # active
+our $EVENTTYPE_STOPPED		= 19;    # paused
+our $EVENTTYPE_PENDING		= 23;    # pending
+our $EVENTSOURCE_ZMC		= 2;
+our $EVENTSOURCE_DST		= 3;
+our $EVENTCODE_GRANT		= 2006;
 
 #
 # Watch an experiment. 
@@ -1365,7 +1667,17 @@ sub WatchExperiment($$;$)
 	logit("WatchExperiment: No rfranges for $instance");
 	return 0;
     }
-    my $ZMC = GetZMC($instance);
+    #
+    # For simplicity, we want to use the element token for both
+    # inner and outer RDZ. Not the temporary user token,
+    #
+    my $ZMC;
+    if (exists($instance->RDZINFO()->{'element_token'})) {
+	$ZMC = GetZMC($instance);
+    }
+    else {
+	$ZMC = APT_RDZRest::ZMC->New(APT_RDZRest::POWDER());
+    }
     if (!defined($ZMC)) {
 	logit("WatchExperiment: Cannot open the ZMC rest interface");
 	return -1;
@@ -1423,11 +1735,11 @@ sub WatchExperiment($$;$)
 	logit("heartBeat $pideid: $grant_id, $status") if ($debug);
 
 	my $reply;
-	if ($status eq "active" || $status eq "paused") {
+	if ($status eq "active" || $status eq "pending") {
 	    $reply = $status;
 	}
 	elsif ($status eq "revoked" || $status eq "replacing") {
-	    $reply = "paused";
+	    $reply = "pending";
 	}
 	elsif ($status eq "deleted" || $status eq "denied") {
 	    $grant->{'status_ack_by'} = undef;
@@ -1460,39 +1772,15 @@ sub WatchExperiment($$;$)
     };
 
     #
-    # Call back for events and polls. 
+    # Process a single grant (poll or event)
     #
-    my $callback = sub ()
+    my $processGrant = sub ()
     {
 	my ($type, $ref) = @_;
 	my ($grant_id, $status, $nextack);
 	my $errmsg;
 	my $errors = 0;
 
-	logit("WatchExperiment Callback $PID ($pideid): $type") if ($debug);
-
-	#
-	# Check to see if any grants need a heartbeat.
-	#
-	if ($type eq "heartbeat") {
-	    foreach my $ref (values(%grants)) {
-		my $grant = $ref->{'grant'};
-		
-		next
-		    if ($grant->{"allow_skip_acks"} ||
-			!$grant->{'status_ack_by'});
-
-		my $nextack = str2time($grant->{'status_ack_by'});
-		next
-		    if ($nextack - time() > 10);
-
-		if (&$heartBeat($grant)) {
-		    $errors++;
-		}
-	    }
-	    return $errors;
-	}
-
 	if ($type eq "poll") {
 	    #
 	    # A poll of the grant.
@@ -1509,27 +1797,44 @@ sub WatchExperiment($$;$)
 		exists($ref->{'replacement'}->{'new_grant_id'})) {
 		my $new_grant_id = $ref->{'replacement'}->{'new_grant_id'};
 		my $old_grant_id = $grant_id;
-
-		logit("WatchExperiment($PID) $pideid: polled grant $old_grant_id ".
-		      "has been replaced by $new_grant_id");
-		
 		my $grant = $grants{$old_grant_id};
-		$grant->{'grant'} = $ref;
-		$grants{$new_grant_id} = $grant;
-		delete($grants{$old_grant_id});
 
-		#
-		# Update the rfrange DB record.
-		#
-		$grant->{'rfrange'}->Update({"rdz_grantid" => $new_grant_id});
+		if (! exists($grants{$new_grant_id})) {
+		    logit("WatchExperiment($PID) $pideid: polled grant $old_grant_id ".
+			  "has been replaced by $new_grant_id");
 
-		my $response;
-		if ($ZMC->DELETE("/grants/$old_grant_id", \$response)) {
-		    logit("WatchExperiment($PID) $pideid: could not remove ".
-			  "revoked grant $old_grant_id from the ZMC");
-		}
-		# Flag non-fatal error, to force a restart of the stream.
-		return 1;
+		    my $new_grant;
+		    if ($ZMC->GET("/grants/$new_grant_id", \$new_grant)) {
+			logit("WatchExperiment: Could not get replacement grant ".
+			      "$new_grant_id");
+			return -1;
+		    }
+		    # Clear so we process the current status below.
+		    $new_grant->{'status'} = "";
+		
+		    $grants{$new_grant_id} = {
+			"grant"     => $new_grant,
+			"old_grant" => $grant,
+			"rfrange"   => $grant->{'rfrange'},
+			# To avoid email blizzard. 
+			"lastop"    => "",
+			"lastemail" => 0,
+		    };
+		    #delete($grants{$old_grant_id});
+
+		    #
+		    # Update the rfrange DB record.
+		    #
+		    $grant->{'rfrange'}->Update({"rdz_grantid" => $new_grant_id});
+		}
+	    }
+	    else {
+		if (exists($grants{$grant_id})) {
+		    my $grant = $grants{$grant_id};
+		
+		    # Clear so we process the current status below.
+		    $grant->{'grant'}->{'status'} = "";
+		}
 	    }
 	    # Fall through to below to handle change in grant status.
 	}
@@ -1537,81 +1842,105 @@ sub WatchExperiment($$;$)
 	    my $code   = $ref->{'header'}->{'code'};
 	    my $type   = $ref->{'header'}->{'type'};
 	    my $source = $ref->{'header'}->{'source_type'};
-	    
-	    if ($code == $EVENTCODE_GRANT && $source == $EVENTSOURCE_ZMC) {
-		my $object = $ref->{'object'};
-		
-		$grant_id = $object->{'id'};
-		if ($type == $EVENTTYPE_REPLACED) {
-		    #
-		    # Grant has been replaced. Next event is going to be a
-		    # revoke on the old grant.
-		    #
-		    if (!exists($grants{$grant_id})) {
-			logit("WatchExperiment($PID) $pideid: ".
-			      "No grant for grant replacement of $grant_id!");
-			return 0;
-		    }
-		    my $new_grant_id = $object->{'replacement'}->{'new_grant_id'};
-		    my $grant = $grants{$grant_id};
-		    $grant->{"old_grant_id"} = $grant_id;
-		    $grant->{"new_grant_id"} = $new_grant_id;
-		    # I do not think we need to reload the grant here.
-		    $grant->{'grant'} = $object;
-		    $grants{$new_grant_id} = $grant;
 
-		    #
-		    # Update the rfrange DB record.
-		    # XXX if we lose the revoked event below, the old grant
-		    # will be left behind in the RDZ DB. But it will be in a
-		    # revoked state, no not harmful. 
-		    #
-		    $grant->{'rfrange'}->Update({"rdz_grantid" => $new_grant_id});
-		    
-		    logit("WatchExperiment($PID) $pideid: Grant $grant_id has been ".
-			  "replaced with $new_grant_id");
+	    if ($code != $EVENTCODE_GRANT || $source != $EVENTSOURCE_ZMC) {
+		logit("WatchExperiment($PID) $pideid: Unknown event: ".
+		    "source:$source, code:$code, type:$type");
+		logit(Dumper($ref)) if ($debug > 1);
+		return 0;
+
+	    }
+	    my $object = $ref->{'object'};
+	    my $ext_id = $object->{'ext_id'};
+	    $status   = $object->{'status'};
+	    $grant_id = $object->{'id'};
+	    $nextack  = $object->{'status_ack_by'};
+
+	    # Need to ignore grants that are created outside of
+	    # a Powder experiment.
+	    my ($instance_uuid,$rfrange_uuid);
+	    if (defined($ext_id)) {
+		($instance_uuid,$rfrange_uuid) = split(":", $ext_id);
+	    }
+
+	    if (!defined($instance_uuid) || $instance_uuid ne $instance->uuid()) {
+		logit("WatchExperiment($PID) $pideid: ignoring grant event ".
+		      (defined($instance_uuid) ? 
+		       "for a different experiment: " : ": ") . $grant_id);
+		logit(Dumper($ref)) if ($debug > 1);
+		return 0;
+	    }
+	    logit("WatchExperiment($PID) $pideid: grant event $type ($status) ".
+		  "for grant $grant_id");
+
+	    #
+	    # We can get a new grant creation for a replacement, or for a grant
+	    # that could not be satisfied during create or update,
+	    #
+	    if ($type == $EVENTTYPE_CREATED) {
+		if (exists($grants{$grant_id})) {
+		    logit("WatchExperiment($PID) $pideid: ".
+			  "Grant already exists. Ignoring");
 		    return 0;
 		}
-		elsif ($type == $EVENTTYPE_REVOKED) {
-		    #
-		    # Check to see if this is the revoke of a replaced grant,
-		    #
-		    if (!exists($grants{$grant_id})) {
-			logit("WatchExperiment($PID) $pideid: ".
-			      "No grant for revoke of $grant_id!");
-			return 0;
-		    }
-		    my $grant = $grants{$grant_id};
-		    if (exists($grant->{"old_grant_id"})) {
-			my $old_grant_id = $grant->{"old_grant_id"};
-			my $new_grant_id = $grant->{"new_grant_id"};
-			    
-			delete($grant->{"old_grant_id"});
-			delete($grant->{"new_grant_id"});
-			delete($grants{$grant_id});
-
-			# David says replaced grants are auto deleted now.
-			if (0) {
-			logit("WatchExperiment($PID) $pideid: Removing revoked grant ".
-			      "$old_grant_id which was replaced by $new_grant_id");
-			
-			my $response;
-			if ($ZMC->DELETE("/grants/$old_grant_id", \$response)) {
-			    logit("WatchExperiment($PID) $pideid: could not remove ".
-				  "revoked grant $old_grant_id from the ZMC");
-			}
-			}
-			# Flag non-fatal error, to force a restart of the stream.
-			return 1;
-		    }
+		if (!defined($rfrange_uuid)) {
+		    logit("WatchExperiment($PID) $pideid: ".
+			  "No rfrange ID in the grant external ID. Ignoring");
+		    return 0;
 		}
-		$status   = $object->{'status'};
-		$nextack  = $object->{'status_ack_by'};
-		# Fall through to below to handle change in grant status.
+		my $rfrange = APT_Instance::RFRanges->Lookup(
+		    $instance, $rfrange_uuid);
+		if (!defined($rfrange)) {
+		    logit("WatchExperiment($PID) $pideid: ".
+			  "No rfrange $rfrange_uuid in the DB. Ignoring");
+		    return 0;
+		}
+		$grants{$grant_id} = {
+		    "grant"     => $object,
+		    "rfrange"   => $rfrange,
+		    # To avoid email blizzard. 
+		    "lastop"    => "",
+		    "lastemail" => 0,
+		};
+		logit("WatchExperiment($PID) $pideid: ".
+		      "Created new grant for $grant_id");
+		return 0;
 	    }
-	    else {
-		logit("WatchExperiment($PID) $pideid: Unknown event:");
-		logit(Dumper($ref));
+	    elsif ($type == $EVENTTYPE_REPLACED) {
+		if (!exists($grants{$grant_id})) {
+		    logit("WatchExperiment($PID) $pideid: ".
+			  "No grant for grant replacement of $grant_id!");
+		    return 0;
+		}
+		my $old_grant = $grants{$grant_id};
+
+		#
+		# We should already have the new grant.
+		#
+		my $new_grant_id = $object->{'replacement'}->{'new_grant_id'};
+		if (!exists($grants{$new_grant_id})) {
+		    logit("WatchExperiment($PID) $pideid: ".
+			  "The new grant ($new_grant_id) for replaced grant ".
+			  "($grant_id) does not exist!");
+		    return 0;
+		}
+		my $new_grant = $grants{$new_grant_id};
+
+		# Flag for below
+		$old_grant->{'new_grant'} = $new_grant;
+		$new_grant->{'old_grant'} = $old_grant;
+
+		logit("WatchExperiment($PID) $pideid: Grant $grant_id has been ".
+		      "replaced with $new_grant_id (status: $status)");
+		#
+		# Mark rfrange as replacing.
+		#
+		#$old_grant->{'rfrange'}->Update({"rdz_status" => "replacing"});
+		return 0;
+	    }
+	    elsif (!exists($grants{$grant_id})) {
+		logit("WatchExperiment($PID) ".
+		      "$pideid: ignoring event for unknown grant: $grant_id");
 		return 0;
 	    }
 	}
@@ -1623,12 +1952,15 @@ sub WatchExperiment($$;$)
 	
 	my $rfrange = $grant->{'rfrange'};
 	$rfrange->Refresh();
-	my $rdz_status = $rfrange->rdz_status() || "";
-	if ($status eq $rdz_status) {
+	
+	my $grant_status = $grant->{'grant'}->{'status'} || "";
+	if ($status eq $grant_status) {
 	    return 0;
 	}
+	$grant->{'grant'}->{'status'} = $status;
+
 	logit("WatchExperiment($PID) $pideid: Grant $grant_id status change: ".
-	      "$rdz_status -> $status");
+	      "$grant_status -> $status");
 
 	#
 	# Might need to send a heartbeat to ack a change in status.
@@ -1638,35 +1970,106 @@ sub WatchExperiment($$;$)
 	    logit("WatchExperiment($PID) $pideid: Grant $grant_id next heartbeat: ".
 		  TBDateStringLocal($nextack));
 	}
-	$rfrange->Update({"rdz_status" => $status}) if (!$impotent);
-	$grant->{'grant'}->{'status'} = $status;
 	if ($beats && $nextack) {
 	    &$heartBeat($grant->{'grant'});
 	}
 	
 	if ($status eq "revoked") {
-	    $instance->Refresh();
 	    #
-	    # Sledge hammer for now. 
+	    # We can ignore the revoke of a replaced grant. It will be auto
+	    # deleted, so just remove it.
 	    #
-	    if (!$impotent && $instance->rdz_status() ne "revoked") {
-		$instance->Update({"rdz_status" => "revoked"});
-		system("$SUDO -u $PROTOUSER $MANAGEINSTANCE terminate $uuid");
-		#
-		# Send email on failure
-		#
+	    if (defined($grant->{'new_grant'})) {
+		logit("WatchExperiment($PID) $pideid: replaced grant $grant_id ".
+		      "has been revoked");
+		delete($grants{$grant_id});
+		return 0;
+	    }
+
+	    #
+	    # Power off the radios. 
+	    #
+	    if ($rfrange->rdz_status() ne "revoked") {
+		$rfrange->Update({"rdz_status" => "revoked"});
+
+		if (!$impotent && PowerControl($instance, "off", \$errmsg)) {
+		    &$notify($grant, "off", $errmsg);
+		    return 0;
+		}
+		$grant->{'lastop'}    = "";
+		$grant->{'lastemail'} = 0;
 	    }
 	}
-	elsif ($status eq "paused" && $rdz_status eq "active") {
-	    # Do not change current status if this fails
-	    if (!$impotent && PowerControl($instance, "off", \$errmsg)) {
+	elsif ($status eq "denied") {
+	    #
+	    # This will happen if a replacement (say, as for extension)
+	    # is denied.
+	    #
+	    if (!defined($grant->{'old_grant'})) {
+		logit("WatchExperiment($PID) $pideid: Unexpected denied event");
+		return 0;
+	    }
+	    my $old_grant = $grant->{'old_grant'};
+	    
+	    #
+	    # We can dump the new grant at this point.
+	    # The RDZ auto deletes a denied, so no need to do that.
+	    #
+	    delete($grants{$grant_id});
+
+	    #
+	    # And set the original back to a good state.
+	    #
+	    $old_grant->{'new_grant'} = undef;
+	    $rfrange->Update({"rdz_status" => $old_grant->{'status'}});
+	}
+	elsif ($status eq "active" && defined($grant->{'old_grant'})) {
+	    #
+	    # This will happen when a replacement goes active. Maybe we want
+	    # to do this when the "approved" event come in?
+	    #
+	    logit("WatchExperiment($PID) $pideid: replacement grant $grant_id ".
+		  "has been activated");
+	    
+	    $grant->{'old_grant'} = undef;
+
+	    # Might be a replacement for a paused grant. 
+	    if ($rfrange->rdz_status() eq "pending" &&
+		!$impotent && PowerControl($instance, "on", \$errmsg)) {
+		&$notify($grant, "on", $errmsg);
+		return 0;
+	    }
+	    
+	    #
+	    # Update the rfrange DB record.
+	    #
+	    $rfrange->Update({"rdz_status"  => "active",
+			      "rdz_grantid" => $grant_id});
+	}
+	elsif (($status eq "pending" || $status eq "paused") &&
+	       $grant_status eq "active") {
+	    $rfrange->Update({"rdz_status" => "pending"});
+
+	    if (!$impotent && $status eq "paused" &&
+		PowerControl($instance, "off", \$errmsg)) {
 		&$notify($grant, "off", $errmsg);
 		return 0;
 	    }
 	    $grant->{'lastop'}    = "";
 	    $grant->{'lastemail'} = 0;
+
+	    #
+	    # Fire off a realloc to see if we can move the range to
+	    # different spot. If this fails, we turn the power off.
+	    #
+	    if ($status eq "pending") {
+		GetNewSpectrum($instance);
+	    }
 	}
-	elsif ($status eq "active" && $rdz_status eq "paused") {
+	elsif ($status eq "active" &&
+	       ($grant_status eq "pending" || $grant_status eq "paused")) {
+	    $rfrange->Update({"rdz_status" => "active"});
+	    
 	    # Do not change current status if this fails
 	    if (!$impotent && PowerControl($instance, "on", \$errmsg)) {
 		&$notify($grant, "on", $errmsg);
@@ -1675,31 +2078,122 @@ sub WatchExperiment($$;$)
 	    $grant->{'lastop'}    = "";
 	    $grant->{'lastemail'} = 0;
 	}
+	elsif ($status eq "deleted") {
+	    $rfrange->Update({"rdz_status" => "deleted"});
+	    #
+	    # We can dump the grant at this point.
+	    #
+	    logit("WatchExperiment($PID) $pideid: Removing grant from our list");
+	    delete($grants{$grant_id});
+	}
 	return 0;
     };
     
+    # Poll the grants if we go too long without an event (via the heartbeat).
+    my $pollTimeout = ($debug ? 10 : 60); # seconds
+    my $lastPoll    = 0;
+
+    my $pollGrants  = sub () {
+	return
+	    if (time() - $lastPoll < $pollTimeout);
+	
+	logit("WatchExperiment($PID) $pideid: Polling grants") if ($debug);
+	$lastPoll = time();
+
+	my $error = 0;
+	foreach my $grant_id (keys(%grants)) {
+	    my $grant;
+	    if ($ZMC->GET("/grants/$grant_id", \$grant)) {
+		logit("PollGrants: /grants/$grant_id failed");
+		next;
+	    }
+	    $error = &$processGrant("poll", $grant);
+	    last
+		if ($error);
+	}
+	return $error;
+    };
+
+    #
+    # Call back for events and polls. 
+    #
+    my $callback = sub ()
+    {
+	my ($type, $ref) = @_;
+
+	logit("WatchExperiment Callback $PID ($pideid): $type") if ($debug);
+
+	#
+	# Check to see if any grants need a heartbeat.
+	#
+	if ($type eq "heartbeat") {
+	    my $errors = 0;
+	    
+	    foreach my $ref (values(%grants)) {
+		my $grant = $ref->{'grant'};
+		
+		next
+		    if ($grant->{"allow_skip_acks"} ||
+			!$grant->{'status_ack_by'});
+
+		my $nextack = str2time($grant->{'status_ack_by'});
+		next
+		    if ($nextack - time() > 10);
+
+		if (&$heartBeat($grant)) {
+		    $errors++;
+		}
+	    }
+	    if (!$errors) {
+		#$errors = &$pollGrants();
+	    }
+	    return $errors;
+	}
+
+	return &$processGrant($type, $ref);
+    };
+
     #
     # Fork a child to handle the event stream. Parent watches the experiment.
     #
     my $childpid = fork();
     if (!$childpid) {
+	my $stop = 0;
+	local $SIG{TERM} = sub {
+	    $stop = 1;
+	};
 	#
 	# Keep going, parent will kill us.
 	#
-	while (1) {
+	while (!$stop) {
 	    logit("WatchExperiment($PID) $pideid: (re)starting event stream");
-	    my $rval = WatchGrants($callback, [keys(%grants)], $ZMC);
+	    # Force initial poll
+	    $lastPoll = 0;
+	    &$pollGrants();
+
+	    my $rval = WatchGrants($callback, $ZMC);
 	    logit("WatchExperiment($PID) $pideid: event stream exited($rval)");
 	    if ($rval < 0) {
-		# XXX Parent is still running below, need to fix this.
 		exit(-1);
 	    }
-	    sleep(2)
+	    last
+		if ($stop);
+
+	    sleep(2);
 	}
 	exit(0);
     }
     logit("WatchExperiment($PID) $pideid: starting looper");
-    while (1) {
+    #
+    # XXX Need a TERM handler here so we kill the child.
+    #
+    my $stop = 0;
+    local $SIG{TERM} = sub {
+	logit("WatchExperiment($PID) $pideid: stopping the looper");
+	$stop = 1;
+    };
+    
+    while (!$stop) {
 	#
 	# Watch for the instance terminating. Grants deleted on that path.
 	#
@@ -1728,7 +2222,10 @@ sub WatchExperiment($$;$)
 		    if (!$impotent);
 	    }
 	}
-	sleep(5);
+	last
+	    if ($stop);
+	
+	sleep(2);
 	my $foo = waitpid($childpid, &WNOHANG);
 	if ($foo) {
 	    my $status = $? >> 8;
@@ -1736,6 +2233,7 @@ sub WatchExperiment($$;$)
 	    exit($status);
 	}
     }
+    logit("WatchExperiment($PID) $pideid: looper: stopping the grant watcher");
     kill('TERM', $childpid);
     waitpid($childpid, 0);
     return 0;
@@ -1744,9 +2242,9 @@ sub WatchExperiment($$;$)
 #
 # Watch grants in an experiment.
 #
-sub WatchGrants($$;$$)
+sub WatchGrants($;$$)
 {
-    my ($callback, $grant_ids, $ZMC, $debug) = @_;
+    my ($callback, $ZMC, $debug) = @_;
     $debug    = 0 if (!defined($debug));
     my $error = 0;
     my $response;
@@ -1780,7 +2278,7 @@ sub WatchGrants($$;$$)
     my $subscription_request = {
 	"id"      => $id,
 	"filters" => [
-	    { "object_ids"  => $grant_ids,
+	    { "codes"       => [$EVENTCODE_GRANT],
 	      "element_ids" => [$ZMC->elementid()],
 	    },
 	 ],
@@ -1875,23 +2373,6 @@ sub WatchGrants($$;$$)
     # Connect causes the Protocol object to create a handshake and write it
     $client->connect;
 
-    # Poll the grants if we go too long without an event.
-    my $pollTimeout = ($debug ? 10 : 60); # seconds
-    my $lastPoll    = time();
-    my $pollGrants  = sub () {
-	foreach my $grant_id (@$grant_ids) {
-	    my $grant;
-	    if ($ZMC->GET("/grants/$grant_id", \$grant)) {
-		logit("WatchGrants: /grants/$grant_id failed");
-		next;
-	    }
-	    $error = &$callback("poll", $grant);
-	    last
-		if ($error);
-	}
-    };
-    &$pollGrants();
-
     #
     # Loop until we are killed by the parent or error.
     #
@@ -1909,11 +2390,6 @@ sub WatchGrants($$;$$)
 		$error = &$callback("heartbeat", undef);
 		last
 		    if ($error);
-
-		if (time() - $lastPoll > $pollTimeout) {
-		    &$pollGrants();
-		    $lastPoll = time();
-		}
 		next;
 	    }
 	    logit("WatchGrants: sysread on socket failed: $!");
@@ -1937,9 +2413,9 @@ sub WatchGrants($$;$$)
 # At the moment, all grants in an experiment use the same set of radio
 # ports, so cycle all of the radios in the experiment.
 #
-sub PowerControl($$$)
+sub PowerControl($$$;$)
 {
-    my ($instance, $which, $perrmsg) = @_;
+    my ($instance, $which, $perrmsg, $foreground) = @_;
     my $uuid     = $instance->uuid();
     my @node_ids = ();
     my $action   = ($which eq "off" ? "stop" : "start");
@@ -1970,8 +2446,10 @@ sub PowerControl($$$)
     #   -P (portal rpc fast path which skips all the geni checks)
     #   -M (do not start a monitor, we depend on the events).
     #
+    my $opts = "-M -P -N " . ($foreground ? "" : "-b");
+    
     my $command = "$SUDO -u $PROTOUSER $WAP $MANAGEINSTANCE ".
-	"$action $uuid -M -P -N -b @node_ids";
+	"$action $uuid $opts @node_ids";
     logit("PowerControl($PID): Powering $which @node_ids");
     logit("$command");
     # Verbose flag to duplicate output to stdout as it happens
@@ -1983,11 +2461,212 @@ sub PowerControl($$$)
     return 0;
 }
 
+#
+# Try to get new spectrum after Pause events. This is messy,
+#
+sub GetNewSpectrum($;$)
+{
+    my ($instance, $debug) = @_;
+    my $instance_uuid = $instance->uuid();
+    my $error;
+    my $logfile;
+    my $updated = 0;
+    $debug = 0 if (!defined($debug));
+
+    exists($instance->RDZINFO()->{'element_token'});    
+    my $ZMC = GetZMC($instance);
+    if (!defined($ZMC)) {
+	logit("GetNewSpectrum: Could not get the ZMC REST object");
+	return -1;
+    }
+
+    logit("GetNewSpectrum ($PID): $instance");
+
+    #
+    # We are going to fork off from the caller (which is the event loop).
+    #
+    if (!$debug) {
+	$logfile = $instance->NewLogFile("getrdzspectrum");
+	if (!defined($logfile)) {
+	    return -1;
+	}
+	logit("GetNewSpectrum: " . $logfile->filename());
+	
+	if (TBBackGround($logfile->filename())) {
+	    return 0;
+	}
+	logit("GetNewSpectrum ($PID): child");
+	# Give the event loop a chance to move on.
+	sleep(5);
+    }
+
+    #
+    # Try to get the lock for a while.
+    #
+    my $locked = 0;
+    my $count  = 30;
+    while ($count) {
+	last
+	    if ($instance->Lock() == 0);
+	$count--;
+	logit("Trying to get the experiment lock. $count tries to go");
+	sleep(2);
+    }
+    if ($count == 0) {
+	logit("Could not get the lock after a long time.");
+	goto bad;
+    }
+    $locked = 1;
+
+    my $rfranges = APT_Instance::RFRanges->LookupForInstance($instance);
+    
+    $instance->SetStatus("rdzwait");
+    my $denied = 0;
+    foreach my $rfrange (values(%{$rfranges})) {
+	my $grant_id = $rfrange->rdz_grantid();
+
+	next
+	    if (!$grant_id || $rfrange->rdz_status() ne "pending");
+	next
+	    if (!$rfrange->width());
+
+	ResetSpectrum($instance, $rfrange);
+	my $grant;
+
+	#
+	# Get the current grant, change it, post it back.
+	#
+	if ($ZMC->GET("/grants/$grant_id", \$grant)) {
+	    logit("GetNewSpectrum: Could not GET grant $grant_id");
+	    next;
+	}
+	my $frequency_low  = $rfrange->freq_low();
+	my $frequency_high = $rfrange->freq_high();
+	my $constraint     = $grant->{'constraints'}->[0]->{'constraint'};
+
+	$constraint->{'min_freq'} = ($frequency_low * 1000000) + 0;
+	$constraint->{'max_freq'} = ($frequency_high * 1000000) + 0;
+
+	logit("Reallocating grant ID $grant_id to ". $rfrange->Printable());
+	
+	my $response;
+	if ($ZMC->POST("/grants/$grant_id", $grant, \$response)) {
+	    logit("GetNewSpectrum: Could not POST grant $grant_id");
+	    next;
+	}
+	#
+	# The return value is now the new grant. The approval/denial
+	# might not have happened yet, and we will poll for a little
+	# while so we can return a useful error to the user/webui.
+	#
+	# Also note that we handle the actual replacement via the event
+	# handling in WatchExperiment() below. 
+	#
+	my $new_grant_id = $response->{'id'};
+	my $new_status = $response->{'status'};
+	logit("Grant replacement ID: $new_grant_id");
+
+	#
+	# Wait for the new grant to go active or denied. Lets not wait
+	# too long though. 
+	#
+	sleep(1);
+	for (my $i = 0; $i < 15; $i++) {
+	    if ($ZMC->GET("/grants/$new_grant_id", \$grant)) {
+		logit("GetNewSpectrum: Could not GET new grant $new_grant_id");
+		next;
+	    }
+	    $new_status = $grant->{'status'};
+	    logit("Grant replacement status: $new_status");
+
+	    if ($new_status eq "active") {
+		my $details = $grant->{"constraints"}[0]->{"constraint"};
+		my $low     = $details->{"min_freq"} / 1000000.0;
+		my $high    = $details->{"max_freq"} / 1000000.0;
+
+		logit("GetNewSpectrum: rfrange for $grant_id updated ".
+		      "to $low,$high");
+
+		UpdateSpectrum($instance, $rfrange, $low, $high, $grant_id);
+		$updated++;
+		last;
+	    }
+
+	    # A denied grant will automatically be deleted ...
+	    if ($new_status eq "denied" || $new_status eq "deleted") {
+		#
+		# No need to delete (auto deleted), but we do want the log.
+		#
+		foreach my $log (@{$grant->{'logs'}}) {
+		    if ($log->{'status'} eq "denied") {
+			logit("GetNewSpectrum: " . $log->{'message'});
+		    }
+		}
+		# Flag to do this in the foreground.
+		PowerControl($instance, "off", \$error, 1);
+		$denied++;
+		last;
+	    }
+	    sleep(2);
+	}
+    }
+    if ($denied) {
+	$instance->SetStatus("rdzerror");
+	goto bad;
+    }
+    $instance->SetStatus("ready");
+    
+    my $rspec = $instance->RSPEC();
+    my $filename = emutil::WriteToFile(GeniXML::Serialize($rspec));
+    my $command = "$MANAGEINSTANCE -d modify $instance_uuid -N $filename";
+    logit("GetNewSpectrum: $command");
+    
+    my $creator = $instance->GetCreator();
+    # Flag to duplicate output to stdout.
+    my ($status, $output) = $creator->ExecuteAs(
+	$command, $instance->GetGroup(), 0);
+
+    if (!$debug) {
+	$logfile->Close();
+	SENDMAIL($TBOPS,
+	     "New RDZ spectrum for " . $instance->Printable(),
+	     "See attached log\n\n".
+	     $instance->webURL() . "\n",
+	     $TBOPS, undef, $logfile->filename());
+	$logfile->Store();
+    }
+    $instance->Unlock();
+    if ($debug) {
+	return 0;
+    }
+    exit(0);
+	
+  bad:
+    if (!$debug) {
+	$logfile->Close();
+	SENDMAIL($TBOPS,
+	     "Not able to get new RDZ spectrum for " . $instance->Printable(),
+	     "See attached log\n\n".
+	     $instance->webURL() . "\n",
+	     $TBOPS, undef, $logfile->filename());
+	$logfile->Store();
+    }
+    $instance->Unlock() if ($locked);
+    if ($debug) {
+	return 1;
+    }
+    exit(1);
+}
+
 sub logit($)
 {
     my ($msg) = @_;
-    my $now = POSIX::strftime("20%y-%m-%d %H:%M:%S", localtime());
-    print "$now: $msg\n";
+    my $now = "";
+
+    if (1) {
+	$now = POSIX::strftime("20%y-%m-%d %H:%M:%S", localtime()) . ": ";
+    }
+    print $now . "$msg\n";
 }
 
 # _Always_ make sure that this 1 is at the end of the file...
diff --git a/apt/create_slivers.in b/apt/create_slivers.in
index 81cc9ad4844cf8275a49f40edc615a37a36d1026..0ae4c0d43573855863e4c6329c042830b98dfeae 100644
--- a/apt/create_slivers.in
+++ b/apt/create_slivers.in
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 #
-# Copyright (c) 2008-2024 University of Utah and the Flux Group.
+# Copyright (c) 2008-2025 University of Utah and the Flux Group.
 # 
 # {{{GENIPUBLIC-LICENSE
 # 
@@ -234,7 +234,6 @@ my $curStatus   = $instance->status();
 my $uuid        = $instance->uuid();
 my $webtask     = $instance->webtask();
 my $webtask_id  = $webtask->task_id();
-my $sequence    = $instance->GetPortalSequence();
 my $update      = defined($instance->update_rspec()) ? 1 : 0;
 my $genislice   = $instance->GetGeniSlice();
 if (!defined($genislice)) {
@@ -543,24 +542,36 @@ if (defined($instance->rdz_status())) {
     my $spectrum = $instance->_spectrum();
     if ($spectrum && @{$spectrum}) {
 	my $error;
-	
-	$instance->SetStatus("rdzwait");
-	if (APT_RDZ::RequestSpectrum($instance, \$error)) {
-	    $instance->ResolveDefer();
-	    $instance->SetStatus("failed");
-	    $instance->RecordError(GENIRESPONSE_ERROR, "RDZ failure: $error");
-	    $webtask->output("RDZ failure: $error");
-	    $webtask->Exited(GENIRESPONSE_ERROR);
+
+	# This will change the instance status if there is something to do.
+	if (my $rval = APT_RDZ::RequestSpectrum($instance, \$error)) {
+	    #
+	    # Positive return value means defer till later. 
+	    #
+	    if ($rval < 0) {
+		$instance->ResolveDefer();
+		$instance->SetStatus("failed");
+		$instance->RecordError(GENIRESPONSE_ERROR, "RDZ failure: $error");
+		$webtask->output("RDZ failure: $error");
+		$webtask->Exited(GENIRESPONSE_ERROR);
+	    }
+	    else {
+		$instance->SetStatus("pending");
+		$instance->Defer("RDZ: $error");
+		$webtask->output("RDZ: $error");
+		$webtask->Exited(0);
+	    }
 	    if (defined($logfile)) {
 		$logfile->Close();
 		$logfile->Store();
 		$logfile = undef;
 	    }
 	    $genislice->UnLock();
-	    exit(1);
+	    exit($rval);
 	}
 	$instance->Update({'rdz_status' => "ready"});
-	$rspecstr = ($update ? $instance->update_rspec() : $instance->rspec());
+	# This changed in RequestSpectrum.
+	$rspecstr = $instance->rspec();
     }
 }
 
@@ -893,6 +904,7 @@ sub CreateSliver($)
     my $aptagg    = $aggobj->GetAptAggregate();
     my $cmurl     = $authority->url();
     my $urn       = $authority->urn();
+    my $sequence  = $instance->GetPortalSequence();
     my $manifest;
     $webtask->Refresh();
 
diff --git a/apt/manage_instance.in b/apt/manage_instance.in
index 06504b80651e05934f8ef85ea1bc9ef1498a42c5..cce3ccd3b9956e13894ed77157f3a18ab40569bd 100644
--- a/apt/manage_instance.in
+++ b/apt/manage_instance.in
@@ -1630,7 +1630,7 @@ sub DoTerminate()
     #
     my $rfranges = APT_Instance::RFRanges->LookupForInstance($instance);
     if (keys(%{$rfranges})) {
-	if ($DORDZ && APT_RDZ::DeleteGrants($instance)) {
+	if ($DORDZ && APT_RDZ::DeleteGrants($instance, \$errmsg)) {
 	    $exitcode = -1;
 	    $errcode  = GENIRESPONSE_ERROR;
 	    $errmsg   = "Could not delete RDZ grants";
@@ -1645,13 +1645,13 @@ sub DoTerminate()
     }
 
     #
-    # Special case for RDZ-in-RDZ
+    # Special case for RDZ
     #
-    if ($DORDZ && $instance->rdz_rdzinrdzinfo()) {
-	if (APT_RDZ::cleanupRDZinRDZ($instance)) {
+    if ($DORDZ && $instance->rdz_rdzinfo()) {
+	if (APT_RDZ::cleanupRDZ($instance)) {
 	    $exitcode = -1;
 	    $errcode  = GENIRESPONSE_ERROR;
-	    $errmsg   = "Could not cleanup RDZ-in-RDZ";
+	    $errmsg   = "Could not cleanup RDZ";
 	    goto bad;
 	}
     }
@@ -1856,6 +1856,7 @@ sub DoExtend()
     my $force    = 0;
     my $doperms  = 0;
     my $errcode  = 1;
+    my $errmsg   = "Not able to extend experiment";
     my $noemail  = 0;
     my $autoextend_maximum = GetSiteVar("aptui/autoextend_maximum");
     my $autoextend_maxage  = GetSiteVar("aptui/autoextend_maxage");
@@ -1879,7 +1880,6 @@ sub DoExtend()
     my $maxextension;
     my $message;
     my $reason;
-    my $errmsg;
     my $wantstring;
     my $grantstring;
     my $resources;
@@ -1952,7 +1952,7 @@ sub DoExtend()
     #
     if ($slice->Lock()) {
 	$errcode = GENIRESPONSE_BUSY;
-	$errmsg  ="Experiment is busy, cannot lock it. Try again later.";
+	$errmsg  = "Experiment is busy, cannot lock it. Try again later.";
 	if (defined($webtask)) {
 	    $webtask->output($errmsg);
 	    $webtask->Exited($errcode);
@@ -2572,8 +2572,12 @@ sub ExtendInternal($$$$$$)
     # If this fails, easy to unroll before the slices are done.
     #
     if ($DORDZ) {
-	if (APT_RDZ::CreateClaimsFromInstance($instance, \$errmsg) ||
-	    APT_RDZ::ExtendGrantsFromInstance($instance, \$errmsg)) {
+	if (APT_RDZ::CreateClaimsFromInstance($instance, \$errmsg)) {
+	    goto bad;
+	}
+	if (APT_RDZ::ExtendGrantsFromInstance($instance, \$errmsg)) {
+	    # We want the user to see these errors
+	    $errcode = 1;
 	    goto bad;
 	}
     }
@@ -8140,11 +8144,12 @@ sub DoModify()
     my $resources;
     my $gotlock = 0;
 
-    my $optlist    = "fNS";
+    my $optlist    = "fNSR";
     my $foreground = 0;
     my $nolock     = 0;	# Caller holds the lock.
     my $nostatus   = 0; # Caller handles instance status changes.
     my $docreate   = 1; # Fire off create_slivers.
+    my $redo       = 0; # Run with current rspec.
     my $nopending  = 0;
     my %options    = ();
     if (! getopts($optlist, \%options)) {
@@ -8159,8 +8164,11 @@ sub DoModify()
     if (defined($options{"S"})) {
 	$nostatus = 1;
     }
+    if (defined($options{"R"})) {
+	$redo = 1;
+    }
     usage()
-	if (!@ARGV);
+	if (!@ARGV && !$redo);
 
     my $creator = $instance->GetCreator();
     if (!defined($creator)) {
@@ -8180,10 +8188,16 @@ sub DoModify()
     my $group     = $instance->GetGroup();
     my $expires   = str2time($instance->Expires());
     my $noresokay = 1;
-    
-    my $rspecstr = emutil::ReadFile($ARGV[0]);
-    if (!$rspecstr) {
-	fatal("Could not read " . $ARGV[0] . ": $!");
+    my $rspecstr;
+
+    if ($redo) {
+	$rspecstr = $instance->rspec();
+    }
+    else {
+	$rspecstr = emutil::ReadFile($ARGV[0]);
+	if (!$rspecstr) {
+	    fatal("Could not read " . $ARGV[0] . ": $!");
+	}
     }
     # Trim()
     $rspecstr =~ s/^\s+|\s+$//g;
diff --git a/apt/rdz_expdaemon.in b/apt/rdz_expdaemon.in
index b13b357a3682122ecf96960f000d2190cd4698d4..1bcba97b56edfb4b6d974841811d6afaa94ce38f 100644
--- a/apt/rdz_expdaemon.in
+++ b/apt/rdz_expdaemon.in
@@ -246,7 +246,9 @@ while (1) {
 		    "join apt_instance_rfranges as rf on rf.uuid=i.uuid ".
 		    "where i.canceled=0 and ".
 		    "      i.status!='failed' and ".
-		    "      rf.rdz_grantid is not null".
+		    "      rf.rdz_grantid is not null and ".
+		    #      Do not start wtaching until the request is complete
+		    "      rf.rdz_status!='new' and rf.rdz_status!='denied'".
 		    ($leebee ? " and i.creator='leebee' " :
 		     ($noleebee ? " and i.creator!='leebee' " : "")));
 
@@ -288,6 +290,9 @@ sub Watcher($)
     if ($mypid) {
 	return $mypid;
     }
+    $SIG{TERM} = 'DEFAULT';
+    $SIG{INT}  = 'IGNORE';
+    
     select(undef, undef, undef, 0.2);
     logit("Starting Watcher($PID) for $instance");
     APT_RDZ::WatchExperiment($instance, $debug, $impotent);