From f0fc752865d5e32dd79c763e88498288bf422d71 Mon Sep 17 00:00:00 2001 From: andreisavu Date: Tue, 22 Nov 2011 16:37:10 +0200 Subject: [PATCH] A VM and an IP address can have multiple forwarding rules attached --- ...CloudStackComputeServiceContextModule.java | 24 ++++---- .../VirtualMachineToNodeMetadata.java | 59 ++++++++++++------- .../CloudStackComputeServiceAdapter.java | 9 ++- .../cloudstack/features/NATAsyncClient.java | 10 ++-- .../cloudstack/features/NATClient.java | 12 ++-- .../StaticNATVirtualMachineInNetwork.java | 13 ++-- ...oudStackComputeServiceAdapterLiveTest.java | 11 ++-- .../VirtualMachineToNodeMetadataTest.java | 14 ++--- .../features/NetworkClientLiveTest.java | 4 +- ...ticNATVirtualMachineInNetworkLiveTest.java | 18 +++++- 10 files changed, 105 insertions(+), 69 deletions(-) diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java index 77682a28f8..daf3ef54ae 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java @@ -22,12 +22,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import com.google.common.collect.ImmutableSet; import org.jclouds.cloudstack.CloudStackAsyncClient; import org.jclouds.cloudstack.CloudStackClient; import org.jclouds.cloudstack.compute.functions.ServiceOfferingToHardware; @@ -104,8 +106,8 @@ public class CloudStackComputeServiceContextModule bind(new TypeLiteral>() { }).to(TemplateToOperatingSystem.class); install(new FactoryModuleBuilder().build(StaticNATVirtualMachineInNetwork.Factory.class)); - bind(new TypeLiteral>() { - }).to(GetIPForwardingRuleByVirtualMachine.class); + bind(new TypeLiteral>>() { + }).to(GetIPForwardingRulesByVirtualMachine.class); } @Provides @@ -171,17 +173,17 @@ public class CloudStackComputeServiceContextModule @Provides @Singleton - protected Cache getIPForwardingRuleByVirtualMachine( - CacheLoader getIPForwardingRule) { - return CacheBuilder.newBuilder().build(getIPForwardingRule); + protected Cache> getIPForwardingRulesByVirtualMachine( + CacheLoader> getIPForwardingRules) { + return CacheBuilder.newBuilder().build(getIPForwardingRules); } @Singleton - public static class GetIPForwardingRuleByVirtualMachine extends CacheLoader { + public static class GetIPForwardingRulesByVirtualMachine extends CacheLoader> { private final CloudStackClient client; @Inject - public GetIPForwardingRuleByVirtualMachine(CloudStackClient client) { + public GetIPForwardingRulesByVirtualMachine(CloudStackClient client) { this.client = checkNotNull(client, "client"); } @@ -190,11 +192,9 @@ public class CloudStackComputeServiceContextModule * when there is no ip forwarding rule available for the VM */ @Override - public IPForwardingRule load(Long input) { - IPForwardingRule rule = client.getNATClient().getIPForwardingRuleForVirtualMachine(input); - if (rule == null) - throw new ResourceNotFoundException("no ip forwarding rule for: " + input); - return rule; + public Set load(Long input) { + Set rules = client.getNATClient().getIPForwardingRulesForVirtualMachine(input); + return rules != null ? rules : ImmutableSet.of(); } } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java index e1732f15a4..e7cd005e15 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java @@ -19,14 +19,19 @@ package org.jclouds.cloudstack.compute.functions; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.transform; import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName; import java.util.Map; import java.util.Set; +import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import org.jclouds.cloudstack.domain.IPForwardingRule; import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.collect.FindResourceInSet; @@ -56,32 +61,32 @@ import com.google.common.util.concurrent.UncheckedExecutionException; public class VirtualMachineToNodeMetadata implements Function { public static final Map vmStateToNodeState = ImmutableMap - . builder().put(VirtualMachine.State.STARTING, NodeState.PENDING) - .put(VirtualMachine.State.RUNNING, NodeState.RUNNING).put(VirtualMachine.State.STOPPING, NodeState.SUSPENDED) - .put(VirtualMachine.State.STOPPED, NodeState.PENDING) - .put(VirtualMachine.State.DESTROYED, NodeState.TERMINATED) - .put(VirtualMachine.State.EXPUNGING, NodeState.TERMINATED) - .put(VirtualMachine.State.MIGRATING, NodeState.PENDING).put(VirtualMachine.State.ERROR, NodeState.ERROR) - .put(VirtualMachine.State.UNKNOWN, NodeState.UNRECOGNIZED) + .builder().put(VirtualMachine.State.STARTING, NodeState.PENDING) + .put(VirtualMachine.State.RUNNING, NodeState.RUNNING).put(VirtualMachine.State.STOPPING, NodeState.SUSPENDED) + .put(VirtualMachine.State.STOPPED, NodeState.PENDING) + .put(VirtualMachine.State.DESTROYED, NodeState.TERMINATED) + .put(VirtualMachine.State.EXPUNGING, NodeState.TERMINATED) + .put(VirtualMachine.State.MIGRATING, NodeState.PENDING).put(VirtualMachine.State.ERROR, NodeState.ERROR) + .put(VirtualMachine.State.UNKNOWN, NodeState.UNRECOGNIZED) // TODO: is this really a state? - .put(VirtualMachine.State.SHUTDOWNED, NodeState.PENDING) - .put(VirtualMachine.State.UNRECOGNIZED, NodeState.UNRECOGNIZED).build(); + .put(VirtualMachine.State.SHUTDOWNED, NodeState.PENDING) + .put(VirtualMachine.State.UNRECOGNIZED, NodeState.UNRECOGNIZED).build(); private final FindLocationForVirtualMachine findLocationForVirtualMachine; private final FindHardwareForVirtualMachine findHardwareForVirtualMachine; private final FindImageForVirtualMachine findImageForVirtualMachine; - private final Cache getIPForwardingRuleByVirtualMachine; + private final Cache> getIPForwardingRulesByVirtualMachine; @Inject VirtualMachineToNodeMetadata(FindLocationForVirtualMachine findLocationForVirtualMachine, - FindHardwareForVirtualMachine findHardwareForVirtualMachine, - FindImageForVirtualMachine findImageForVirtualMachine, - Cache getIPForwardingRuleByVirtualMachine) { + FindHardwareForVirtualMachine findHardwareForVirtualMachine, + FindImageForVirtualMachine findImageForVirtualMachine, + Cache> getIPForwardingRulesByVirtualMachine) { this.findLocationForVirtualMachine = checkNotNull(findLocationForVirtualMachine, "findLocationForVirtualMachine"); this.findHardwareForVirtualMachine = checkNotNull(findHardwareForVirtualMachine, "findHardwareForVirtualMachine"); this.findImageForVirtualMachine = checkNotNull(findImageForVirtualMachine, "findImageForVirtualMachine"); - this.getIPForwardingRuleByVirtualMachine = checkNotNull(getIPForwardingRuleByVirtualMachine, - "getIPForwardingRuleByVirtualMachine"); + this.getIPForwardingRulesByVirtualMachine = checkNotNull(getIPForwardingRulesByVirtualMachine, + "getIPForwardingRulesByVirtualMachine"); } @Override @@ -108,15 +113,27 @@ public class VirtualMachineToNodeMetadata implements Function addresses = ImmutableSet. of(from.getIPAddress()); + Set addresses = ImmutableSet.of(from.getIPAddress()); if (isPrivate) builder.privateAddresses(addresses); else builder.publicAddresses(addresses); } try { - IPForwardingRule rule = getIPForwardingRuleByVirtualMachine.getUnchecked(from.getId()); - builder.publicAddresses(ImmutableSet. of(rule.getIPAddress())); + + builder.publicAddresses(transform(filter(getIPForwardingRulesByVirtualMachine.getUnchecked(from.getId()), + new Predicate() { + @Override + public boolean apply(@Nullable IPForwardingRule rule) { + return !"Deleting".equals(rule.getState()); + } + }), + new Function() { + @Override + public String apply(@Nullable IPForwardingRule rule) { + return rule.getIPAddress(); + } + })); } catch (UncheckedExecutionException e) { if (Throwables2.getFirstThrowableOfType(e, ResourceNotFoundException.class) == null) { Throwables.propagateIfPossible(e.getCause()); @@ -165,9 +182,9 @@ public class VirtualMachineToNodeMetadata implements Function forwardingRules = client.getNATClient().getIPForwardingRulesForVirtualMachine(guestId); + for(IPForwardingRule rule : forwardingRules) { + job = client.getNATClient().deleteIPForwardingRule(rule.getId()); + jobComplete.apply(job); + } } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/NATAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/NATAsyncClient.java index 23a96a5d30..352d387b9b 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/NATAsyncClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/NATAsyncClient.java @@ -78,26 +78,24 @@ public interface NATAsyncClient { ListenableFuture getIPForwardingRule(@QueryParam("id") long id); /** - * @see NATClient#getIPForwardingRuleForIPAddress + * @see NATClient#getIPForwardingRulesForIPAddress */ @GET @QueryParams(keys = "command", values = "listIpForwardingRules") @SelectJson("ipforwardingrule") - @OnlyElement @Consumes(MediaType.APPLICATION_JSON) @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture getIPForwardingRuleForIPAddress(@QueryParam("ipaddressid") long id); + ListenableFuture> getIPForwardingRulesForIPAddress(@QueryParam("ipaddressid") long id); /** - * @see NATClient#getIPForwardingRuleForVirtualMachine + * @see NATClient#getIPForwardingRulesForVirtualMachine */ @GET @QueryParams(keys = "command", values = "listIpForwardingRules") @SelectJson("ipforwardingrule") - @OnlyElement @Consumes(MediaType.APPLICATION_JSON) @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture getIPForwardingRuleForVirtualMachine(@QueryParam("virtualmachineid") long id); + ListenableFuture> getIPForwardingRulesForVirtualMachine(@QueryParam("virtualmachineid") long id); /** * @see NATClient#createIPForwardingRule diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/NATClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/NATClient.java index 42e7c1c5e6..9fafec7909 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/NATClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/NATClient.java @@ -59,22 +59,22 @@ public interface NATClient { IPForwardingRule getIPForwardingRule(long id); /** - * get a specific IPForwardingRule by ipaddress id + * get a set of IPForwardingRules by ipaddress id * * @param id * IPAddress of rule to get - * @return IPForwardingRule or null if not found + * @return IPForwardingRule matching query or empty if not found */ - IPForwardingRule getIPForwardingRuleForIPAddress(long id); + Set getIPForwardingRulesForIPAddress(long id); /** - * get a specific IPForwardingRule by virtual machine id + * get a set of IPForwardingRules by virtual machine id * * @param id * virtual machine of rule to get - * @return IPForwardingRule or null if not found + * @return IPForwardingRule matching query or empty set if not found */ - IPForwardingRule getIPForwardingRuleForVirtualMachine(long id); + Set getIPForwardingRulesForVirtualMachine(long id); /** * Creates an ip forwarding rule diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetwork.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetwork.java index 5d2d076a0c..c15bc0c952 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetwork.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetwork.java @@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkState; import javax.inject.Inject; import javax.inject.Singleton; +import com.google.common.collect.ImmutableSet; import org.jclouds.cloudstack.CloudStackClient; import org.jclouds.cloudstack.domain.AsyncCreateResponse; import org.jclouds.cloudstack.domain.AsyncJob; @@ -37,6 +38,8 @@ import com.google.common.base.Predicate; import com.google.common.cache.Cache; import com.google.inject.assistedinject.Assisted; +import java.util.Set; + /** * * @author Adrian Cole @@ -51,17 +54,17 @@ public class StaticNATVirtualMachineInNetwork implements Function jobComplete; - private final Cache getIPForwardingRuleByVirtualMachine; + private final Cache> getIPForwardingRulesByVirtualMachine; @Inject public StaticNATVirtualMachineInNetwork(CloudStackClient client, ReuseOrAssociateNewPublicIPAddress reuseOrAssociate, Predicate jobComplete, - Cache getIPForwardingRuleByVirtualMachine, @Assisted Network network) { + Cache> getIPForwardingRulesByVirtualMachine, @Assisted Network network) { this.client = checkNotNull(client, "client"); this.reuseOrAssociate = checkNotNull(reuseOrAssociate, "reuseOrAssociate"); this.jobComplete = checkNotNull(jobComplete, "jobComplete"); - this.getIPForwardingRuleByVirtualMachine = checkNotNull(getIPForwardingRuleByVirtualMachine, - "getIPForwardingRuleByVirtualMachine"); + this.getIPForwardingRulesByVirtualMachine = checkNotNull(getIPForwardingRulesByVirtualMachine, + "getIPForwardingRulesByVirtualMachine"); this.network = checkNotNull(network, "network"); } @@ -86,7 +89,7 @@ public class StaticNATVirtualMachineInNetwork implements Function response = client.getAsyncJobClient().getAsyncJob(job.getJobId()); checkState(response.getResult() != null, "No result after creating IP forwarding rule: ", response); - getIPForwardingRuleByVirtualMachine.asMap().put(vm.getId(), response.getResult()); + getIPForwardingRulesByVirtualMachine.asMap().put(vm.getId(), ImmutableSet.of(response.getResult())); return ip; } } \ No newline at end of file diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java index 8248b6d049..22b0fe803d 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack.compute; +import static com.google.common.collect.Iterables.getFirst; import static com.google.inject.name.Names.bindProperties; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -26,12 +27,13 @@ import static org.testng.Assert.fail; import java.io.IOException; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.concurrent.TimeUnit; import javax.inject.Singleton; import org.jclouds.cloudstack.CloudStackClient; -import org.jclouds.cloudstack.compute.config.CloudStackComputeServiceContextModule.GetIPForwardingRuleByVirtualMachine; +import org.jclouds.cloudstack.compute.config.CloudStackComputeServiceContextModule.GetIPForwardingRulesByVirtualMachine; import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter; import org.jclouds.cloudstack.domain.IPForwardingRule; @@ -117,8 +119,8 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien @SuppressWarnings("unused") @Provides @Singleton - protected Cache getIPForwardingRuleByVirtualMachine( - GetIPForwardingRuleByVirtualMachine getIPForwardingRule) { + protected Cache> getIPForwardingRuleByVirtualMachine( + GetIPForwardingRulesByVirtualMachine getIPForwardingRule) { return CacheBuilder.newBuilder().build(getIPForwardingRule); } }; @@ -160,7 +162,8 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien assertEquals(vm.getNode().getDisplayName(), name); // check to see if we setup a NAT rule (conceding we could check this from // cache) - IPForwardingRule rule = client.getNATClient().getIPForwardingRuleForVirtualMachine(vm.getNode().getId()); + IPForwardingRule rule = getFirst( + client.getNATClient().getIPForwardingRulesForVirtualMachine(vm.getNode().getId()), null); String address = rule != null ? rule.getIPAddress() : vm.getNode().getIPAddress(); diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadataTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadataTest.java index 2958cafcdd..0fa4331d21 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadataTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadataTest.java @@ -64,12 +64,12 @@ public class VirtualMachineToNodeMetadataTest { . of(TemplateToImageTest.one, TemplateToImageTest.two)); VirtualMachineToNodeMetadata parser = new VirtualMachineToNodeMetadata(new FindLocationForVirtualMachine( locationSupplier), new FindHardwareForVirtualMachine(hardwareSupplier), new FindImageForVirtualMachine( - imageSupplier), CacheBuilder.newBuilder(). build( - new CacheLoader() { + imageSupplier), CacheBuilder.newBuilder().> build( + new CacheLoader>() { @Override - public IPForwardingRule load(Long arg0) throws Exception { - return IPForwardingRule.builder().id(1234l).IPAddress("1.1.1.1").build(); + public Set load(Long arg0) throws Exception { + return ImmutableSet.of(IPForwardingRule.builder().id(1234l).IPAddress("1.1.1.1").build()); } })); @@ -102,11 +102,11 @@ public class VirtualMachineToNodeMetadataTest { . of(TemplateToImageTest.one, TemplateToImageTest.two)); VirtualMachineToNodeMetadata parser = new VirtualMachineToNodeMetadata(new FindLocationForVirtualMachine( locationSupplier), new FindHardwareForVirtualMachine(hardwareSupplier), new FindImageForVirtualMachine( - imageSupplier), CacheBuilder.newBuilder(). build( - new CacheLoader() { + imageSupplier), CacheBuilder.newBuilder().> build( + new CacheLoader>() { @Override - public IPForwardingRule load(Long arg0) throws Exception { + public Set load(Long arg0) throws Exception { throw new ResourceNotFoundException("no ip forwarding rule for: " + arg0); } diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkClientLiveTest.java index 310f949c4f..fb8800756f 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkClientLiveTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkClientLiveTest.java @@ -115,7 +115,7 @@ public class NetworkClientLiveTest extends BaseCloudStackClientLiveTest { .getNetworkClient() // startIP/endIP/netmask/gateway must be specified together .createNetworkInZone(zone.getId(), offering.getId(), name, name, - vlan("2").startIP("192.168.1.2").netmask("255.255.255.0").gateway("192.168.1.1")); + vlan("65").startIP("192.168.1.2").netmask("255.255.255.0").gateway("192.168.1.1")); checkNetwork(network); } catch (IllegalStateException e) { Logger.getAnonymousLogger().log(Level.SEVERE, "couldn't create a network, skipping test", e); @@ -173,7 +173,7 @@ public class NetworkClientLiveTest extends BaseCloudStackClientLiveTest { break; case DIRECT: // TODO: I've found a network that doesn't have a netmask associated - // assert network.getNetmask() != null : network; + assert network.getNetmask() != null : network; assert network.getGateway() != null : network; assert network.getVLAN() != null : network; assertEquals(network.getBroadcastURI(), URI.create("vlan://" + network.getVLAN())); diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java index e3d2097cb0..5cedee70c1 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java @@ -18,12 +18,16 @@ */ package org.jclouds.cloudstack.functions; +import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.find; +import static com.google.common.collect.Iterables.getOnlyElement; import static org.testng.Assert.assertEquals; import java.util.NoSuchElementException; +import java.util.Set; -import org.jclouds.cloudstack.compute.config.CloudStackComputeServiceContextModule.GetIPForwardingRuleByVirtualMachine; +import com.google.common.base.Predicate; +import org.jclouds.cloudstack.compute.config.CloudStackComputeServiceContextModule.GetIPForwardingRulesByVirtualMachine; import org.jclouds.cloudstack.domain.IPForwardingRule; import org.jclouds.cloudstack.domain.Network; import org.jclouds.cloudstack.domain.PublicIPAddress; @@ -41,6 +45,8 @@ import org.testng.annotations.Test; import com.google.common.cache.CacheBuilder; +import javax.annotation.Nullable; + /** * Tests behavior of {@code StaticNATVirtualMachineInNetwork} * @@ -75,9 +81,15 @@ public class StaticNATVirtualMachineInNetworkLiveTest extends NATClientLiveTest if (networksDisabled) return; ip = new StaticNATVirtualMachineInNetwork(client, reuseOrAssociate, jobComplete, CacheBuilder.newBuilder() - . build(new GetIPForwardingRuleByVirtualMachine(client)), network).apply(vm); + .>build(new GetIPForwardingRulesByVirtualMachine(client)), network).apply(vm); - rule = client.getNATClient().getIPForwardingRuleForIPAddress(ip.getId()); + rule = getOnlyElement(filter(client.getNATClient().getIPForwardingRulesForIPAddress(ip.getId()), + new Predicate() { + @Override + public boolean apply(@Nullable IPForwardingRule rule) { + return rule != null && rule.getStartPort() == 22; + } + })); assertEquals(rule.getIPAddressId(), ip.getId()); assertEquals(rule.getVirtualMachineId(), vm.getId()); assertEquals(rule.getStartPort(), 22);