You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by an...@apache.org on 2017/02/13 11:33:47 UTC

jclouds git commit: Fix O(n^2) response time for "list-security-groups" on openstack-nova.

Repository: jclouds
Updated Branches:
  refs/heads/master 4ae2d208e -> 717b75a34


Fix O(n^2) response time for "list-security-groups" on openstack-nova.

For https://issues.apache.org/jira/browse/JCLOUDS-1235.

This change takes the approach of storing the information about the
overall list of groups within the `SecurityGroupInRegion` when it is
created, so that any subsequent conversion operation has access to all
the groups in the same region as the one to be converted.

It also collapses the functionality of `NovaSecurityGroupToSecurityGroup`,
`SecurityGroupRuleToIpPermission` and `FindSecurityGroupWithNameAndReturnTrue`
all into `NovaSecurityGroupInRegionToSecurityGroup`, and deletes the
now unused-classes SecurityGroupRuleToIpPermission,
NovaSecurityGroupToSecurityGroup and associated tests.


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/717b75a3
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/717b75a3
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/717b75a3

Branch: refs/heads/master
Commit: 717b75a34e261689f900c7f623088a866932a33e
Parents: 4ae2d20
Author: Geoff Macartney <ge...@cloudsoftcorp.com>
Authored: Wed Feb 1 10:04:32 2017 +0000
Committer: Andrea Turli <an...@gmail.com>
Committed: Mon Feb 13 12:30:18 2017 +0100

----------------------------------------------------------------------
 .../config/NovaComputeServiceContextModule.java |  10 --
 .../extensions/NovaSecurityGroupExtension.java  |  18 ++-
 .../functions/CreateSecurityGroupIfNeeded.java  |  16 +-
 ...ovaSecurityGroupInRegionToSecurityGroup.java | 103 ++++++++++---
 .../NovaSecurityGroupToSecurityGroup.java       |  66 --------
 .../SecurityGroupRuleToIpPermission.java        |  96 ------------
 .../regionscoped/SecurityGroupInRegion.java     |  34 ++++-
 .../FindSecurityGroupWithNameAndReturnTrue.java |   6 +-
 .../NovaSecurityGroupExtensionExpectTest.java   |  87 ++++++++---
 .../NovaSecurityGroupExtensionLiveTest.java     |  86 +++++++++++
 ...ecurityGroupInRegionToSecurityGroupTest.java |  88 +++++++++--
 .../NovaSecurityGroupToSecurityGroupTest.java   | 152 -------------------
 .../SecurityGroupRuleToIpPermissionTest.java    |  79 ----------
 .../CreateSecurityGroupIfNeededTest.java        |  30 ++--
 ...ityGroupWithNameAndReturnTrueExpectTest.java |   9 +-
 .../rest/internal/BaseRestApiExpectTest.java    |   3 +-
 16 files changed, 399 insertions(+), 484 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
index 477ff0a..cead92b 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
@@ -46,7 +46,6 @@ import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThen
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.functions.IdentityFunction;
-import org.jclouds.net.domain.IpPermission;
 import org.jclouds.openstack.nova.v2_0.compute.NovaComputeService;
 import org.jclouds.openstack.nova.v2_0.compute.NovaComputeServiceAdapter;
 import org.jclouds.openstack.nova.v2_0.compute.extensions.NovaImageExtension;
@@ -57,9 +56,7 @@ import org.jclouds.openstack.nova.v2_0.compute.functions.FlavorInRegionToHardwar
 import org.jclouds.openstack.nova.v2_0.compute.functions.ImageInRegionToImage;
 import org.jclouds.openstack.nova.v2_0.compute.functions.ImageToOperatingSystem;
 import org.jclouds.openstack.nova.v2_0.compute.functions.NovaSecurityGroupInRegionToSecurityGroup;
-import org.jclouds.openstack.nova.v2_0.compute.functions.NovaSecurityGroupToSecurityGroup;
 import org.jclouds.openstack.nova.v2_0.compute.functions.OrphanedGroupsByRegionId;
-import org.jclouds.openstack.nova.v2_0.compute.functions.SecurityGroupRuleToIpPermission;
 import org.jclouds.openstack.nova.v2_0.compute.functions.ServerInRegionToNodeMetadata;
 import org.jclouds.openstack.nova.v2_0.compute.loaders.CreateUniqueKeyPair;
 import org.jclouds.openstack.nova.v2_0.compute.loaders.FindSecurityGroupOrCreate;
@@ -68,7 +65,6 @@ import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
 import org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
 import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
 import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
-import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
 import org.jclouds.openstack.nova.v2_0.domain.Server;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.FlavorInRegion;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.ImageInRegion;
@@ -114,12 +110,6 @@ public class NovaComputeServiceContextModule extends
       bind(new TypeLiteral<Function<ServerInRegion, NodeMetadata>>() {
       }).to(ServerInRegionToNodeMetadata.class);
 
-      bind(new TypeLiteral<Function<SecurityGroupRule, IpPermission>>() {
-      }).to(SecurityGroupRuleToIpPermission.class);
-
-      bind(new TypeLiteral<Function<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup, SecurityGroup>>() {
-      }).to(NovaSecurityGroupToSecurityGroup.class);
-
       bind(new TypeLiteral<Function<SecurityGroupInRegion, SecurityGroup>>() {
       }).to(NovaSecurityGroupInRegionToSecurityGroup.class);
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtension.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtension.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtension.java
index 1a5c4fe..88ab63c 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtension.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtension.java
@@ -58,6 +58,7 @@ import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Supplier;
 import com.google.common.cache.LoadingCache;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
 import com.google.common.util.concurrent.ListeningExecutorService;
@@ -135,8 +136,9 @@ public class NovaSecurityGroupExtension implements SecurityGroupExtension {
       }
 
       Set<String> groupNames = instance.getSecurityGroupNames();
-      Set<? extends SecurityGroupInRegion> rawGroups =
-              sgApi.get().list().filter(nameIn(groupNames)).transform(groupToGroupInRegion(region)).toSet();
+       final FluentIterable<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups = sgApi.get().list();
+       Set<? extends SecurityGroupInRegion> rawGroups =
+              allGroups.filter(nameIn(groupNames)).transform(groupToGroupInRegion(allGroups, region)).toSet();
 
       return ImmutableSet.copyOf(transform(filter(rawGroups, notNull()), groupConverter));
    }
@@ -153,7 +155,8 @@ public class NovaSecurityGroupExtension implements SecurityGroupExtension {
          return null;
       }
 
-      SecurityGroupInRegion rawGroup = new SecurityGroupInRegion(sgApi.get().get(groupId), region);
+      final FluentIterable<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups = sgApi.get().list();
+      SecurityGroupInRegion rawGroup = new SecurityGroupInRegion(sgApi.get().get(groupId), region, allGroups);
 
       return groupConverter.apply(rawGroup);
    }
@@ -359,17 +362,20 @@ public class NovaSecurityGroupExtension implements SecurityGroupExtension {
             }
 
 
-            return sgApi.get().list().transform(groupToGroupInRegion(from)).toSet();
+             final FluentIterable<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups = sgApi.get().list();
+             return allGroups.transform(groupToGroupInRegion(allGroups, from)).toSet();
          }
 
       };
    }
 
-   protected Function<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup, SecurityGroupInRegion> groupToGroupInRegion(final String region) {
+   protected Function<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup, SecurityGroupInRegion> groupToGroupInRegion(
+       final Iterable<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups, final String region) {
+
       return new Function<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup, SecurityGroupInRegion>() {
          @Override
          public SecurityGroupInRegion apply(org.jclouds.openstack.nova.v2_0.domain.SecurityGroup group) {
-            return new SecurityGroupInRegion(group, region);
+            return new SecurityGroupInRegion(group, region, allGroups);
          }
       };
    }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java
index 4e9b11c..deea2e9 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java
@@ -38,6 +38,7 @@ import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi;
 
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
+import com.google.common.collect.FluentIterable;
 
 @Singleton
 public class CreateSecurityGroupIfNeeded implements Function<RegionSecurityGroupNameAndPorts, SecurityGroupInRegion> {
@@ -58,31 +59,30 @@ public class CreateSecurityGroupIfNeeded implements Function<RegionSecurityGroup
       String regionId = regionSecurityGroupNameAndPorts.getRegion();
       Optional<? extends SecurityGroupApi> api = novaApi.getSecurityGroupApi(regionId);
       checkArgument(api.isPresent(), "Security groups are required, but the extension is not available in region %s!", regionId);
+      final FluentIterable<SecurityGroup> allGroups = api.get().list();
       logger.debug(">> creating securityGroup %s", regionSecurityGroupNameAndPorts);
       try {
-
          SecurityGroup securityGroup = api.get().createWithDescription(
-                  regionSecurityGroupNameAndPorts.getName(), regionSecurityGroupNameAndPorts.getName());
+            regionSecurityGroupNameAndPorts.getName(), regionSecurityGroupNameAndPorts.getName());
 
          logger.debug("<< created securityGroup(%s)", securityGroup);
          for (int port : regionSecurityGroupNameAndPorts.getPorts()) {
             authorizeGroupToItselfAndAllIPsToTCPPort(api.get(), securityGroup, port);
          }
-         return new SecurityGroupInRegion(api.get().get(securityGroup.getId()), regionId);
+         return new SecurityGroupInRegion(api.get().get(securityGroup.getId()), regionId, allGroups);
       } catch (IllegalStateException e) {
          logger.trace("<< trying to find securityGroup(%s): %s", regionSecurityGroupNameAndPorts, e.getMessage());
-         SecurityGroup group = find(api.get().list(), nameEquals(regionSecurityGroupNameAndPorts
-                  .getName()));
+         SecurityGroup group = find(allGroups, nameEquals(regionSecurityGroupNameAndPorts.getName()));
          logger.debug("<< reused securityGroup(%s)", group.getId());
-         return new SecurityGroupInRegion(group, regionId);
+         return new SecurityGroupInRegion(group, regionId, allGroups);
       }
    }
 
    private void authorizeGroupToItselfAndAllIPsToTCPPort(SecurityGroupApi securityGroupApi,
-            SecurityGroup securityGroup, int port) {
+                                                         SecurityGroup securityGroup, int port) {
       logger.debug(">> authorizing securityGroup(%s) permission to 0.0.0.0/0 on port %d", securityGroup, port);
       securityGroupApi.createRuleAllowingCidrBlock(securityGroup.getId(), Ingress.builder().ipProtocol(
-               IpProtocol.TCP).fromPort(port).toPort(port).build(), "0.0.0.0/0");
+         IpProtocol.TCP).fromPort(port).toPort(port).build(), "0.0.0.0/0");
       logger.debug("<< authorized securityGroup(%s) permission to 0.0.0.0/0 on port %d", securityGroup, port);
 
    }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroup.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroup.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroup.java
index 33b641e..184e9ab 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroup.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroup.java
@@ -18,7 +18,10 @@ package org.jclouds.openstack.nova.v2_0.compute.functions;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.transform;
 
+import java.util.Collection;
 import java.util.Map;
 
 import javax.annotation.Resource;
@@ -30,43 +33,103 @@ import org.jclouds.compute.domain.SecurityGroupBuilder;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.domain.Location;
 import org.jclouds.logging.Logger;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
+import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
 
 import com.google.common.base.Function;
+import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
+
 /**
  * A function for transforming a Nova-specific SecurityGroup into a generic
  * SecurityGroup object.
  */
 @Singleton
 public class NovaSecurityGroupInRegionToSecurityGroup implements Function<SecurityGroupInRegion, SecurityGroup> {
-   @Resource
-   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
-   protected Logger logger = Logger.NULL;
+    @Resource
+    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+    protected Logger logger = Logger.NULL;
+
+    protected final Supplier<Map<String, Location>> locationIndex;
+
+    @Inject
+    public NovaSecurityGroupInRegionToSecurityGroup(Supplier<Map<String, Location>> locationIndex) {
+        this.locationIndex = checkNotNull(locationIndex, "locationIndex");
+    }
+
+    @Override
+    public SecurityGroup apply(final SecurityGroupInRegion groupInRegion) {
+        SecurityGroupBuilder builder = new SecurityGroupBuilder();
 
-   protected final Function<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup, SecurityGroup> baseConverter;
-   protected final Supplier<Map<String, Location>> locationIndex;
+        final org.jclouds.openstack.nova.v2_0.domain.SecurityGroup group = groupInRegion.getSecurityGroup();
+        builder.id(group.getId());
+        builder.providerId(group.getId());
+        builder.ownerId(group.getTenantId());
+        builder.name(group.getName());
+        if (group.getRules() != null) {
+           builder.ipPermissions(filter(transform(group.getRules(), new Function<SecurityGroupRule, IpPermission>() {
+              @Override
+              public IpPermission apply(SecurityGroupRule input) {
+                 return securityGroupRuleToIpPermission(groupInRegion, input);
+              }
+           }), Predicates.notNull()));
+        }
 
-   @Inject
-   public NovaSecurityGroupInRegionToSecurityGroup(Function<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup, SecurityGroup> baseConverter,
-                                                 Supplier<Map<String, Location>> locationIndex) {
-      this.baseConverter = checkNotNull(baseConverter, "baseConverter");
-      this.locationIndex = checkNotNull(locationIndex, "locationIndex");
-   }
+        final String regionId = groupInRegion.getRegion();
+        Location region = locationIndex.get().get(regionId);
+        checkState(region != null, "location %s not in locationIndex: %s", regionId, locationIndex.get());
 
-   @Override
-   public SecurityGroup apply(SecurityGroupInRegion group) {
-      SecurityGroupBuilder builder = SecurityGroupBuilder.fromSecurityGroup(baseConverter.apply(group.getSecurityGroup()));
+        builder.location(region);
 
-      Location region = locationIndex.get().get(group.getRegion());
-      checkState(region != null, "location %s not in locationIndex: %s", group.getRegion(), locationIndex.get());
+        builder.id(regionId + "/" + group.getId());
 
-      builder.location(region);
+        return builder.build();
+    }
 
-      builder.id(group.getRegion() + "/" + group.getSecurityGroup().getId());
+    private IpPermission securityGroupRuleToIpPermission(SecurityGroupInRegion groupInRegion, SecurityGroupRule rule) {
+        IpPermission.Builder builder = IpPermission.builder();
+        builder.ipProtocol(rule.getIpProtocol());
+        builder.fromPort(rule.getFromPort());
+        builder.toPort(rule.getToPort());
+        final TenantIdAndName ruleGroup = rule.getGroup();
+        if (ruleGroup != null) {
+           final org.jclouds.openstack.nova.v2_0.domain.SecurityGroup owningGroup =
+              groupInRegion.getSecurityGroup();
+           final Collection<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> referredGroup =
+              groupInRegion.getGroupsByName().get(ruleGroup);
+           if (null == referredGroup) {
+              logger.warn("Unknown group {} used in security rule, refusing to add it to {} ({})",
+                 ruleGroup, owningGroup.getName(), owningGroup.getId());
+              return null;
+           }
+           /*  Checking referredGroup.size(), see comments on SecurityGroupInRegion.getGroupsByName(). If there are
+               duplicate groups with the same tenant-id-and-name as that of ruleGroup then it is not possible
+               with the Nova /v2/12345/os-security-groups API to know which group is intended, as the only
+               information it returns about referred groups is the tenant id and name:
+                        "group": {
+                           "tenant_id": "a0ade3ca76784719845363979dc1014e",
+                           "name": "jclouds-qa-scheduler-docker-entity"
+                        },
+              Rather than pick one group at random and risk using the wrong group, here we fall back to the
+              least-worst option(?) and refuse to add any rule.
 
-      return builder.build();
-   }
+              See https://issues.apache.org/jira/browse/JCLOUDS-1234.
+           */
+           if (referredGroup.size() != 1) {
+              logger.warn("Ambiguous group %s used in security rule, refusing to add it to %s (%s)",
+                 ruleGroup, owningGroup.getName(), owningGroup.getId());
+              return null;
+           }
+           builder.groupId(groupInRegion.getRegion() + "/" + Iterables.getOnlyElement(referredGroup).getId());
+        }
+        if (rule.getIpRange() != null) {
+            builder.cidrBlock(rule.getIpRange());
+        }
+        return builder.build();
+    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroup.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroup.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroup.java
deleted file mode 100644
index bdb7ba6..0000000
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroup.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.openstack.nova.v2_0.compute.functions;
-
-import static com.google.common.collect.Iterables.transform;
-
-import javax.annotation.Resource;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
-import org.jclouds.compute.domain.SecurityGroup;
-import org.jclouds.compute.domain.SecurityGroupBuilder;
-import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.logging.Logger;
-import org.jclouds.net.domain.IpPermission;
-import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
-
-import com.google.common.base.Function;
-import com.google.inject.Inject;
-
-/**
- * A function for transforming a Nova-specific SecurityGroup into a generic
- * SecurityGroup object.
- */
-@Singleton
-public class NovaSecurityGroupToSecurityGroup implements Function<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup, SecurityGroup> {
-   @Resource
-   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
-   protected Logger logger = Logger.NULL;
-
-   protected Function<SecurityGroupRule, IpPermission> ruleToPermission;
-
-   @Inject
-   public NovaSecurityGroupToSecurityGroup(Function<SecurityGroupRule, IpPermission> ruleToPermission) {
-      this.ruleToPermission = ruleToPermission;
-   }
-
-   @Override
-   public SecurityGroup apply(org.jclouds.openstack.nova.v2_0.domain.SecurityGroup group) {
-      SecurityGroupBuilder builder = new SecurityGroupBuilder();
-
-      builder.id(group.getId());
-      builder.providerId(group.getId());
-      builder.ownerId(group.getTenantId());
-      builder.name(group.getName());
-      if (group.getRules() != null) {
-         builder.ipPermissions(transform(group.getRules(), ruleToPermission));
-      }
-
-      return builder.build();
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermission.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermission.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermission.java
deleted file mode 100644
index 6dddcc8..0000000
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermission.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.openstack.nova.v2_0.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.getFirst;
-
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.annotation.Resource;
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.domain.Location;
-import org.jclouds.logging.Logger;
-import org.jclouds.net.domain.IpPermission;
-import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
-import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
-import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.base.Supplier;
-import com.google.common.cache.LoadingCache;
-import com.google.common.util.concurrent.Atomics;
-
-/**
- * A function for transforming a nova-specific SecurityGroupRule into a generic
- * IpPermission object.
- */
-public class SecurityGroupRuleToIpPermission implements Function<SecurityGroupRule, IpPermission> {
-   @Resource
-   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
-   protected Logger logger = Logger.NULL;
-   protected final Predicate<AtomicReference<RegionAndName>> returnSecurityGroupExistsInRegion;
-   protected final Supplier<Map<String, Location>> locationIndex;
-   LoadingCache<RegionAndName, SecurityGroupInRegion> groupMap;
-
-   @Inject
-   public SecurityGroupRuleToIpPermission(@Named("SECURITYGROUP_PRESENT") Predicate<AtomicReference<RegionAndName>> returnSecurityGroupExistsInRegion,
-                                          Supplier<Map<String, Location>> locationIndex,
-                                          LoadingCache<RegionAndName, SecurityGroupInRegion> groupMap) {
-      this.returnSecurityGroupExistsInRegion = checkNotNull(returnSecurityGroupExistsInRegion,
-              "returnSecurityGroupExistsInRegion");
-      this.locationIndex = checkNotNull(locationIndex, "locationIndex");
-      this.groupMap = checkNotNull(groupMap, "groupMap");
-   }
-
-   @Override
-   public IpPermission apply(SecurityGroupRule rule) {
-      IpPermission.Builder builder = IpPermission.builder();
-      builder.ipProtocol(rule.getIpProtocol());
-      builder.fromPort(rule.getFromPort());
-      builder.toPort(rule.getToPort());
-      if (rule.getGroup() != null) {
-         String region = getFirst(filter(locationIndex.get().keySet(), isSecurityGroupInRegion(rule.getGroup().getName())),
-                 null);
-         if (region != null) {
-            SecurityGroupInRegion group = groupMap.getUnchecked(RegionAndName.fromRegionAndName(region, rule.getGroup().getName()));
-            builder.groupId(region + "/" + group.getSecurityGroup().getId());
-         }
-      }
-      if (rule.getIpRange() != null)
-         builder.cidrBlock(rule.getIpRange());
-
-      return builder.build();
-   }
-
-   protected Predicate<String> isSecurityGroupInRegion(final String groupName) {
-      return new Predicate<String>() {
-
-         @Override
-         public boolean apply(String region) {
-            AtomicReference<RegionAndName> securityGroupInRegionRef = Atomics.newReference(RegionAndName.fromRegionAndName(region, groupName));
-            return returnSecurityGroupExistsInRegion.apply(securityGroupInRegionRef);
-         }
-      };
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/regionscoped/SecurityGroupInRegion.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/regionscoped/SecurityGroupInRegion.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/regionscoped/SecurityGroupInRegion.java
index 88168d4..41466c4 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/regionscoped/SecurityGroupInRegion.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/regionscoped/SecurityGroupInRegion.java
@@ -18,28 +18,58 @@ package org.jclouds.openstack.nova.v2_0.domain.regionscoped;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Collection;
+import java.util.Map;
+
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
+import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName;
 
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
 
 public class SecurityGroupInRegion extends RegionAndName {
    protected final SecurityGroup securityGroup;
 
-   public SecurityGroupInRegion(SecurityGroup securityGroup, String regionId) {
+   protected final Multimap<TenantIdAndName, SecurityGroup> groupsByName;
+
+   public SecurityGroupInRegion(SecurityGroup securityGroup, String regionId, Iterable<SecurityGroup> allGroupsInRegion) {
       super(regionId, checkNotNull(securityGroup, "securityGroup").getName());
       this.securityGroup = securityGroup;
+      this.groupsByName = HashMultimap.create();
+      for (SecurityGroup groupInRegion : allGroupsInRegion) {
+         final TenantIdAndName tenantIdAndName = TenantIdAndName.builder()
+            .tenantId(groupInRegion.getTenantId())
+            .name(groupInRegion.getName())
+            .build();
+         this.groupsByName.put(tenantIdAndName, groupInRegion);
+      }
    }
 
    public SecurityGroup getSecurityGroup() {
       return securityGroup;
    }
 
+   /**
+    * Returns a map of group {@link TenantIdAndName}s to groups.
+    *
+    * The returned value is a collection, to take into account the possibility that certain clouds
+    * may permit duplicate group names.
+    *
+    * @return The map of names to (collections of) groups.
+    */
+   public Map<TenantIdAndName, Collection<SecurityGroup>> getGroupsByName() {
+       return groupsByName.asMap();
+   }
+
    // superclass hashCode/equals are good enough, and help us use RegionAndName and SecurityGroupInRegion
    // interchangeably as Map keys
 
    @Override
    protected ToStringHelper string() {
-      return super.string().add("securityGroup", securityGroup);
+      return super.string()
+          .add("securityGroup", securityGroup)
+          .add("groupsByName", groupsByName);
    }
 
    @Override

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/FindSecurityGroupWithNameAndReturnTrue.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/FindSecurityGroupWithNameAndReturnTrue.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/FindSecurityGroupWithNameAndReturnTrue.java
index b7b7f0d..cc1ddd3 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/FindSecurityGroupWithNameAndReturnTrue.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/FindSecurityGroupWithNameAndReturnTrue.java
@@ -35,6 +35,7 @@ import org.jclouds.rest.ResourceNotFoundException;
 
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
@@ -63,7 +64,8 @@ public class FindSecurityGroupWithNameAndReturnTrue implements Predicate<AtomicR
 
       logger.trace("looking for security group %s", securityGroupInRegion.slashEncode());
       try {
-         SecurityGroup returnVal = Iterables.find(api.get().list(), new Predicate<SecurityGroup>() {
+         final FluentIterable<SecurityGroup> allGroups = api.get().list();
+         SecurityGroup returnVal = Iterables.find(allGroups, new Predicate<SecurityGroup>() {
 
             @Override
             public boolean apply(SecurityGroup input) {
@@ -71,7 +73,7 @@ public class FindSecurityGroupWithNameAndReturnTrue implements Predicate<AtomicR
             }
 
          });
-         securityGroupInRegionRef.set(new SecurityGroupInRegion(returnVal, securityGroupInRegion.getRegion()));
+         securityGroupInRegionRef.set(new SecurityGroupInRegion(returnVal, securityGroupInRegion.getRegion(), allGroups));
          return true;
       } catch (ResourceNotFoundException e) {
          return false;

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionExpectTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionExpectTest.java
index caa80fd..7753dad 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionExpectTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionExpectTest.java
@@ -255,10 +255,23 @@ public class NovaSecurityGroupExtensionExpectTest extends BaseNovaComputeService
               payloadFromResource("/securitygroup_details_extension.json")).build();
 
 
-      SecurityGroupExtension extension = orderedRequestsSendResponses(ImmutableList.of(keystoneAuthWithUsernameAndPasswordAndTenantName,
-              extensionsOfNovaRequest, getSecurityGroup, createRule, getSecurityGroup, list, list),
-              ImmutableList.of(responseWithKeystoneAccess, extensionsOfNovaResponse, getSecurityGroupNoRulesResponse,
-                      createRuleResponse, getSecurityGroupResponse, listResponse, listResponse)).getSecurityGroupExtension().get();
+      SecurityGroupExtension extension = orderedRequestsSendResponses(
+         ImmutableList.of(
+            keystoneAuthWithUsernameAndPasswordAndTenantName,
+            extensionsOfNovaRequest,
+            list,
+            getSecurityGroup,
+            createRule,
+            list,
+            getSecurityGroup),
+         ImmutableList.of(
+            responseWithKeystoneAccess,
+            extensionsOfNovaResponse,
+            listResponse,
+            getSecurityGroupNoRulesResponse,
+            createRuleResponse,
+            listResponse,
+            getSecurityGroupResponse)).getSecurityGroupExtension().get();
 
       IpPermission.Builder builder = IpPermission.builder();
 
@@ -304,10 +317,24 @@ public class NovaSecurityGroupExtensionExpectTest extends BaseNovaComputeService
               payloadFromResource("/securitygroup_details_extension.json")).build();
 
 
-      SecurityGroupExtension extension = orderedRequestsSendResponses(ImmutableList.of(keystoneAuthWithUsernameAndPasswordAndTenantName,
-              extensionsOfNovaRequest, getSecurityGroup, createRule, getSecurityGroup, list, list),
-              ImmutableList.of(responseWithKeystoneAccess, extensionsOfNovaResponse, getSecurityGroupNoRulesResponse,
-                      createRuleResponse, getSecurityGroupResponse, listResponse, listResponse)).getSecurityGroupExtension().get();
+      SecurityGroupExtension extension = orderedRequestsSendResponses(
+         ImmutableList.of(
+            keystoneAuthWithUsernameAndPasswordAndTenantName,
+            extensionsOfNovaRequest,
+            list,
+            getSecurityGroup,
+            createRule,
+            list,
+            getSecurityGroup),
+         ImmutableList.of(
+            responseWithKeystoneAccess,
+            extensionsOfNovaResponse,
+            listResponse,
+            getSecurityGroupNoRulesResponse,
+            createRuleResponse,
+            listResponse,
+            getSecurityGroupResponse))
+         .getSecurityGroupExtension().get();
 
       SecurityGroup origGroup = extension.getSecurityGroupById(region + "/160");
 
@@ -349,11 +376,23 @@ public class NovaSecurityGroupExtensionExpectTest extends BaseNovaComputeService
       HttpResponse getSecurityGroupResponse = HttpResponse.builder().statusCode(200).payload(
               payloadFromResource("/securitygroup_details_extension.json")).build();
 
-
-      SecurityGroupExtension extension = orderedRequestsSendResponses(ImmutableList.of(keystoneAuthWithUsernameAndPasswordAndTenantName,
-              extensionsOfNovaRequest, getSecurityGroup, createRule, getSecurityGroup, list, list),
-              ImmutableList.of(responseWithKeystoneAccess, extensionsOfNovaResponse, getSecurityGroupNoRulesResponse,
-                      createRuleResponse, getSecurityGroupResponse, listResponse, listResponse)).getSecurityGroupExtension().get();
+      SecurityGroupExtension extension = orderedRequestsSendResponses(
+         ImmutableList.of(
+            keystoneAuthWithUsernameAndPasswordAndTenantName,
+            extensionsOfNovaRequest,
+            list,
+            getSecurityGroup,
+            createRule,
+            list,
+            getSecurityGroup),
+         ImmutableList.of(
+            responseWithKeystoneAccess,
+            extensionsOfNovaResponse,
+            listResponse,
+            getSecurityGroupNoRulesResponse,
+            createRuleResponse,
+            listResponse,
+            getSecurityGroupResponse)).getSecurityGroupExtension().get();
 
       IpPermission.Builder builder = IpPermission.builder();
 
@@ -399,10 +438,24 @@ public class NovaSecurityGroupExtensionExpectTest extends BaseNovaComputeService
               payloadFromResource("/securitygroup_details_extension.json")).build();
 
 
-      SecurityGroupExtension extension = orderedRequestsSendResponses(ImmutableList.of(keystoneAuthWithUsernameAndPasswordAndTenantName,
-              extensionsOfNovaRequest, getSecurityGroup, createRule, getSecurityGroup, list, list),
-              ImmutableList.of(responseWithKeystoneAccess, extensionsOfNovaResponse, getSecurityGroupNoRulesResponse,
-                      createRuleResponse, getSecurityGroupResponse, listResponse, listResponse)).getSecurityGroupExtension().get();
+      SecurityGroupExtension extension = orderedRequestsSendResponses(
+         ImmutableList.of(
+            keystoneAuthWithUsernameAndPasswordAndTenantName,
+            extensionsOfNovaRequest,
+            list,
+            getSecurityGroup,
+            createRule,
+            list,
+            getSecurityGroup),
+         ImmutableList.of(
+            responseWithKeystoneAccess,
+            extensionsOfNovaResponse,
+            listResponse,
+            getSecurityGroupNoRulesResponse,
+            createRuleResponse,
+            listResponse,
+            getSecurityGroupResponse))
+         .getSecurityGroupExtension().get();
 
       SecurityGroup origGroup = extension.getSecurityGroupById(region + "/160");
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionLiveTest.java
index 804d546..02fbfb7 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/extensions/NovaSecurityGroupExtensionLiveTest.java
@@ -16,17 +16,103 @@
  */
 package org.jclouds.openstack.nova.v2_0.compute.extensions;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Date;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.extensions.internal.BaseSecurityGroupExtensionLiveTest;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
 /**
  * Live test for openstack-nova {@link org.jclouds.compute.extensions.SecurityGroupExtension} implementation.
  */
 @Test(groups = "live", singleThreaded = true, testName = "NovaSecurityGroupExtensionLiveTest")
 public class NovaSecurityGroupExtensionLiveTest extends BaseSecurityGroupExtensionLiveTest {
 
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.CONSOLE;
+
    public NovaSecurityGroupExtensionLiveTest() {
       provider = "openstack-nova";
    }
 
+   @Test(groups = {"integration", "live"}, singleThreaded = true)
+   public void testListSecurityGroups() throws Exception {
+      skipIfSecurityGroupsNotSupported();
+
+      final long begin = new Date().getTime();
+      ComputeService computeService = view.getComputeService();
+      Optional<SecurityGroupExtension> securityGroupExtension = computeService.getSecurityGroupExtension();
+      assertTrue(securityGroupExtension.isPresent(), "security extension was not present");
+
+      logger.info("Loading security groups");
+      final SecurityGroupExtension security = securityGroupExtension.get();
+      Set<SecurityGroup> beforeAdd = security.listSecurityGroups();
+      int countBeforeAdd = beforeAdd.size();
+      logger.info("Found %d security groups", countBeforeAdd);
+
+      String someUnlikelyName = String.valueOf(new Random().nextInt(1000000) + 1000000);
+      logger.info("Adding security group %s", someUnlikelyName);
+      final SecurityGroup testGroup = security.createSecurityGroup(someUnlikelyName, getNodeTemplate().getLocation());
+
+      try {
+         verifyAndDeleteSecurityGroup(security, countBeforeAdd, testGroup);
+      } catch (Exception e) {
+         logger.error(e, "Exception caught, live test leaking security group %s", testGroup.getName());
+         throw e;
+      }
+
+      final long end = new Date().getTime();
+
+      assertTrue(end - begin < TimeUnit.MINUTES.toMillis(5)); // see https://issues.apache.org/jira/browse/JCLOUDS-1235
+
+   }
+
+   private void verifyAndDeleteSecurityGroup(SecurityGroupExtension security, int countBeforeAdd,
+                                             final SecurityGroup testGroup) {
+      logger.info("Loading security groups");
+      Set<SecurityGroup> afterAdd = security.listSecurityGroups();
+      final int countAfterAdd = afterAdd.size();
+      logger.info("Found %d security groups", countAfterAdd);
+
+      assertEquals(countAfterAdd, countBeforeAdd + 1);
+      final Predicate<SecurityGroup> findTestGroup = new Predicate<SecurityGroup>() {
+         @Override
+         public boolean apply(SecurityGroup input) {
+            return input.getName().equals(testGroup.getName());
+         }
+      };
+      final SecurityGroup created = Iterables.find(afterAdd, findTestGroup);
+      assertNotNull(created, "Did not find security group created as expected");
+
+      logger.info("Removing %s", testGroup.getName());
+      security.removeSecurityGroup(testGroup.getId());
+
+      logger.info("Loading security groups");
+      Set<SecurityGroup> afterRemove = security.listSecurityGroups();
+      final int sizeAfterRemove = afterRemove.size();
+      logger.info("Found %d security groups", sizeAfterRemove);
+      assertEquals(sizeAfterRemove, countBeforeAdd);
+      final Optional<SecurityGroup> removed = Iterables.tryFind(afterRemove, findTestGroup);
+      assertTrue(!removed.isPresent(), "Did not remove test security group as expected");
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroupTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroupTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroupTest.java
index 969b3f4..ea46cd9 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroupTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupInRegionToSecurityGroupTest.java
@@ -16,10 +16,8 @@
  */
 package org.jclouds.openstack.nova.v2_0.compute.functions;
 
-import static com.google.common.collect.Iterables.transform;
-import static org.jclouds.openstack.nova.v2_0.compute.functions.NovaSecurityGroupToSecurityGroupTest.securityGroupWithCidr;
-import static org.jclouds.openstack.nova.v2_0.compute.functions.NovaSecurityGroupToSecurityGroupTest.securityGroupWithGroup;
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
 
 import java.util.Map;
 
@@ -27,13 +25,19 @@ import org.jclouds.compute.domain.SecurityGroup;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LocationBuilder;
 import org.jclouds.domain.LocationScope;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
+import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
 
 @Test(groups = "unit", testName = "NovaSecurityGroupInRegionToSecurityGroupTest")
 public class NovaSecurityGroupInRegionToSecurityGroupTest {
@@ -45,12 +49,67 @@ public class NovaSecurityGroupInRegionToSecurityGroupTest {
    Supplier<Map<String, Location>> locationIndex = Suppliers.<Map<String, Location>> ofInstance(ImmutableMap
            .<String, Location>of("az-1.region-a.geo-1", region));
 
+   public static final String SOME_GROUP_ID = "some-group-id";
+   public static final String SOME_OTHER_GROUP_ID = "some-other-group-id";
+   public static final String IP_RANGE = "0.0.0.0/0";
+   public static final String SOME_OTHER_GROUP = "some-other-group";
+   public static final String SOME_GROUP = "some-group";
+
+   public static final ImmutableList<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups =
+      ImmutableList.of(securityGroupWithGroup(), securityGroupWithCidr());
+
+   public static org.jclouds.openstack.nova.v2_0.domain.SecurityGroup securityGroupWithGroup() {
+      TenantIdAndName group = TenantIdAndName.builder().tenantId("tenant").name(SOME_OTHER_GROUP).build();
+
+      SecurityGroupRule ruleToConvert = SecurityGroupRule.builder()
+         .id("some-rule-id")
+         .ipProtocol(IpProtocol.TCP)
+         .fromPort(10)
+         .toPort(20)
+         .group(group)
+         .parentGroupId(SOME_GROUP_ID)
+         .build();
+
+      org.jclouds.openstack.nova.v2_0.domain.SecurityGroup origGroup =
+         org.jclouds.openstack.nova.v2_0.domain.SecurityGroup.builder()
+            .tenantId("tenant")
+            .id(SOME_GROUP_ID)
+            .name(SOME_GROUP)
+            .description("some-description")
+            .rules(ruleToConvert)
+            .build();
+
+      return origGroup;
+   }
+
+   public static org.jclouds.openstack.nova.v2_0.domain.SecurityGroup securityGroupWithCidr() {
+      SecurityGroupRule ruleToConvert = SecurityGroupRule.builder()
+         .id("some-other-rule-id")
+         .ipProtocol(IpProtocol.TCP)
+         .fromPort(10)
+         .toPort(20)
+         .ipRange(IP_RANGE)
+         .parentGroupId(SOME_OTHER_GROUP_ID)
+         .build();
+
+      org.jclouds.openstack.nova.v2_0.domain.SecurityGroup origGroup =
+         org.jclouds.openstack.nova.v2_0.domain.SecurityGroup.builder()
+            .tenantId("tenant")
+            .id(SOME_OTHER_GROUP_ID)
+            .name(SOME_OTHER_GROUP)
+            .description("some-description")
+            .rules(ruleToConvert)
+            .build();
+
+      return origGroup;
+   }
 
    @Test
    public void testApplyWithGroup() {
       NovaSecurityGroupInRegionToSecurityGroup parser = createGroupParser();
 
-      SecurityGroupInRegion origGroup = new SecurityGroupInRegion(securityGroupWithGroup(), region.getId());
+      final org.jclouds.openstack.nova.v2_0.domain.SecurityGroup otherGroup = securityGroupWithCidr();
+      SecurityGroupInRegion origGroup = new SecurityGroupInRegion(securityGroupWithGroup(), region.getId(), allGroups);
 
       SecurityGroup newGroup = parser.apply(origGroup);
 
@@ -58,8 +117,11 @@ public class NovaSecurityGroupInRegionToSecurityGroupTest {
       assertEquals(newGroup.getProviderId(), origGroup.getSecurityGroup().getId());
       assertEquals(newGroup.getName(), origGroup.getSecurityGroup().getName());
       assertEquals(newGroup.getOwnerId(), origGroup.getSecurityGroup().getTenantId());
-      assertEquals(newGroup.getIpPermissions(), ImmutableSet.copyOf(transform(origGroup.getSecurityGroup().getRules(),
-              NovaSecurityGroupToSecurityGroupTest.ruleConverter)));
+      final IpPermission permission = Iterables.getOnlyElement(newGroup.getIpPermissions());
+      assertEquals(Iterables.getOnlyElement(permission.getGroupIds()), region.getId() + "/" + otherGroup.getId());
+      assertEquals(permission.getFromPort(), 10);
+      assertEquals(permission.getToPort(), 20);
+      assertTrue(permission.getCidrBlocks().isEmpty());
       assertEquals(newGroup.getLocation().getId(), origGroup.getRegion());
    }
 
@@ -68,7 +130,7 @@ public class NovaSecurityGroupInRegionToSecurityGroupTest {
 
       NovaSecurityGroupInRegionToSecurityGroup parser = createGroupParser();
 
-      SecurityGroupInRegion origGroup = new SecurityGroupInRegion(securityGroupWithCidr(), region.getId());
+      SecurityGroupInRegion origGroup = new SecurityGroupInRegion(securityGroupWithCidr(), region.getId(), allGroups);
 
       SecurityGroup newGroup = parser.apply(origGroup);
 
@@ -76,15 +138,17 @@ public class NovaSecurityGroupInRegionToSecurityGroupTest {
       assertEquals(newGroup.getProviderId(), origGroup.getSecurityGroup().getId());
       assertEquals(newGroup.getName(), origGroup.getSecurityGroup().getName());
       assertEquals(newGroup.getOwnerId(), origGroup.getSecurityGroup().getTenantId());
-      assertEquals(newGroup.getIpPermissions(), ImmutableSet.copyOf(transform(origGroup.getSecurityGroup().getRules(),
-              NovaSecurityGroupToSecurityGroupTest.ruleConverter)));
+      final IpPermission permission = Iterables.getOnlyElement(newGroup.getIpPermissions());
+      assertEquals(permission.getFromPort(), 10);
+      assertEquals(permission.getToPort(), 20);
+      assertEquals(Iterables.getOnlyElement(permission.getCidrBlocks()), IP_RANGE);
+      assertTrue(permission.getGroupIds().isEmpty());
       assertEquals(newGroup.getLocation().getId(), origGroup.getRegion());
    }
 
    private NovaSecurityGroupInRegionToSecurityGroup createGroupParser() {
-      NovaSecurityGroupToSecurityGroup baseParser = new NovaSecurityGroupToSecurityGroup(NovaSecurityGroupToSecurityGroupTest.ruleConverter);
 
-      NovaSecurityGroupInRegionToSecurityGroup parser = new NovaSecurityGroupInRegionToSecurityGroup(baseParser, locationIndex);
+      NovaSecurityGroupInRegionToSecurityGroup parser = new NovaSecurityGroupInRegionToSecurityGroup(locationIndex);
 
       return parser;
    }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroupTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroupTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroupTest.java
deleted file mode 100644
index 5edc44d..0000000
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroupTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.openstack.nova.v2_0.compute.functions;
-
-import static com.google.common.collect.Iterables.transform;
-import static org.testng.Assert.assertEquals;
-
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.jclouds.compute.domain.SecurityGroup;
-import org.jclouds.domain.Location;
-import org.jclouds.domain.LocationBuilder;
-import org.jclouds.domain.LocationScope;
-import org.jclouds.net.domain.IpProtocol;
-import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
-import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName;
-import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
-import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
-import org.testng.annotations.Test;
-
-import com.google.common.base.Functions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-@Test(groups = "unit", testName = "NovaSecurityGroupToSecurityGroupTest")
-public class NovaSecurityGroupToSecurityGroupTest {
-
-   private static final Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("openstack-nova")
-           .description("openstack-nova").build();
-   private static final Location region = new LocationBuilder().id("az-1.region-a.geo-1").description("az-1.region-a.geo-1")
-           .scope(LocationScope.REGION).parent(provider).build();
-   private static final Supplier<Map<String, Location>> locationIndex = Suppliers.<Map<String, Location>> ofInstance(ImmutableMap
-           .<String, Location>of("az-1.region-a.geo-1", region));
-
-
-   private static final Predicate<AtomicReference<RegionAndName>> returnSecurityGroupExistsInRegion = Predicates.alwaysTrue();
-
-   private static final Map<RegionAndName, SecurityGroupInRegion> groupMap = ImmutableMap.of(
-           RegionAndName.fromRegionAndName("az-1.region-a.geo-1", "some-group"), new SecurityGroupInRegion(securityGroupWithGroup(), "az-1.region-a.geo-1"),
-           RegionAndName.fromRegionAndName("az-1.region-a.geo-1", "some-other-group"), new SecurityGroupInRegion(securityGroupWithCidr(), "az-1.region-a.geo-1"));
-
-   // weird compilation error means have to declare extra generics for call to build() - see https://bugs.eclipse.org/bugs/show_bug.cgi?id=365818
-   private static final Supplier <LoadingCache<RegionAndName, SecurityGroupInRegion>> groupCache = Suppliers.<LoadingCache<RegionAndName, SecurityGroupInRegion>> ofInstance(
-           CacheBuilder.newBuilder().<RegionAndName, SecurityGroupInRegion>build(CacheLoader.from(Functions.forMap(groupMap))));
-
-   public static final SecurityGroupRuleToIpPermission ruleConverter = new SecurityGroupRuleToIpPermission(returnSecurityGroupExistsInRegion, locationIndex,
-           groupCache.get());
-
-   public static org.jclouds.openstack.nova.v2_0.domain.SecurityGroup securityGroupWithGroup() {
-      TenantIdAndName group = TenantIdAndName.builder().tenantId("tenant").name("some-other-group").build();
-
-      SecurityGroupRule ruleToConvert = SecurityGroupRule.builder()
-              .id("some-id")
-              .ipProtocol(IpProtocol.TCP)
-              .fromPort(10)
-              .toPort(20)
-              .group(group)
-              .parentGroupId("some-other-id")
-              .build();
-
-      org.jclouds.openstack.nova.v2_0.domain.SecurityGroup origGroup = org.jclouds.openstack.nova.v2_0.domain.SecurityGroup.builder()
-              .tenantId("tenant")
-              .id("some-id")
-              .name("some-group")
-              .description("some-description")
-              .rules(ruleToConvert)
-              .build();
-
-      return origGroup;
-   }
-
-   public static org.jclouds.openstack.nova.v2_0.domain.SecurityGroup securityGroupWithCidr() {
-      SecurityGroupRule ruleToConvert = SecurityGroupRule.builder()
-              .id("some-id")
-              .ipProtocol(IpProtocol.TCP)
-              .fromPort(10)
-              .toPort(20)
-              .ipRange("0.0.0.0/0")
-              .parentGroupId("some-other-id")
-              .build();
-
-      org.jclouds.openstack.nova.v2_0.domain.SecurityGroup origGroup = org.jclouds.openstack.nova.v2_0.domain.SecurityGroup.builder()
-              .tenantId("tenant")
-              .id("some-id")
-              .name("some-other-group")
-              .description("some-description")
-              .rules(ruleToConvert)
-              .build();
-
-      return origGroup;
-   }
-
-   @Test
-   public void testApplyWithGroup() {
-      NovaSecurityGroupToSecurityGroup parser = createGroupParser();
-
-      org.jclouds.openstack.nova.v2_0.domain.SecurityGroup origGroup = securityGroupWithGroup();
-
-      SecurityGroup newGroup = parser.apply(origGroup);
-
-      assertEquals(newGroup.getId(), origGroup.getId());
-      assertEquals(newGroup.getProviderId(), origGroup.getId());
-      assertEquals(newGroup.getName(), origGroup.getName());
-      assertEquals(newGroup.getOwnerId(), origGroup.getTenantId());
-      assertEquals(newGroup.getIpPermissions(), ImmutableSet.copyOf(transform(origGroup.getRules(), ruleConverter)));
-   }
-
-   @Test
-   public void testApplyWithCidr() {
-
-      NovaSecurityGroupToSecurityGroup parser = createGroupParser();
-
-      org.jclouds.openstack.nova.v2_0.domain.SecurityGroup origGroup = securityGroupWithCidr();
-
-      SecurityGroup group = parser.apply(origGroup);
-
-      assertEquals(group.getId(), origGroup.getId());
-      assertEquals(group.getProviderId(), origGroup.getId());
-      assertEquals(group.getName(), origGroup.getName());
-      assertEquals(group.getOwnerId(), origGroup.getTenantId());
-      assertEquals(group.getIpPermissions(), ImmutableSet.copyOf(transform(origGroup.getRules(), ruleConverter)));
-   }
-
-   private NovaSecurityGroupToSecurityGroup createGroupParser() {
-      NovaSecurityGroupToSecurityGroup parser = new NovaSecurityGroupToSecurityGroup(ruleConverter);
-
-      return parser;
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermissionTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermissionTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermissionTest.java
deleted file mode 100644
index dedf100..0000000
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermissionTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.openstack.nova.v2_0.compute.functions;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-import org.jclouds.net.domain.IpPermission;
-import org.jclouds.net.domain.IpProtocol;
-import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
-import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableSet;
-
-
-/**
- * Tests for the function for transforming a nova specific SecurityGroupRule into a generic
- * IpPermission object.
- */
-public class SecurityGroupRuleToIpPermissionTest {
-
-   @Test
-   public void testApplyWithGroup() {
-
-      TenantIdAndName group = TenantIdAndName.builder().tenantId("tenant").name("some-group").build();
-      
-      SecurityGroupRule ruleToConvert = SecurityGroupRule.builder()
-         .id("some-id")
-         .ipProtocol(IpProtocol.TCP)
-         .fromPort(10)
-         .toPort(20)
-         .group(group)
-         .parentGroupId("some-other-id")
-         .build();
-
-      IpPermission convertedPerm = NovaSecurityGroupToSecurityGroupTest.ruleConverter.apply(ruleToConvert);
-
-      assertEquals(convertedPerm.getIpProtocol(), ruleToConvert.getIpProtocol());
-      assertEquals(convertedPerm.getFromPort(), ruleToConvert.getFromPort());
-      assertEquals(convertedPerm.getToPort(), ruleToConvert.getToPort());
-      assertTrue(convertedPerm.getGroupIds().contains("az-1.region-a.geo-1/some-id"));
-      assertEquals(convertedPerm.getCidrBlocks().size(), 0);
-   }
-
-   @Test
-   public void testApplyWithCidr() {
-      SecurityGroupRule ruleToConvert = SecurityGroupRule.builder()
-         .id("some-id")
-         .ipProtocol(IpProtocol.TCP)
-         .fromPort(10)
-         .toPort(20)
-         .ipRange("0.0.0.0/0")
-         .parentGroupId("some-other-id")
-         .build();
-
-      IpPermission convertedPerm = NovaSecurityGroupToSecurityGroupTest.ruleConverter.apply(ruleToConvert);
-
-      assertEquals(convertedPerm.getIpProtocol(), ruleToConvert.getIpProtocol());
-      assertEquals(convertedPerm.getFromPort(), ruleToConvert.getFromPort());
-      assertEquals(convertedPerm.getToPort(), ruleToConvert.getToPort());
-      assertEquals(convertedPerm.getCidrBlocks(), ImmutableSet.of("0.0.0.0/0"));
-      assertEquals(convertedPerm.getTenantIdGroupNamePairs().size(), 0);
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/CreateSecurityGroupIfNeededTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/CreateSecurityGroupIfNeededTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/CreateSecurityGroupIfNeededTest.java
index 3ee404f..248fad8 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/CreateSecurityGroupIfNeededTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/CreateSecurityGroupIfNeededTest.java
@@ -24,6 +24,7 @@ import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.openstack.nova.v2_0.NovaApi;
 import org.jclouds.openstack.nova.v2_0.compute.functions.CreateSecurityGroupIfNeeded;
+import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionSecurityGroupNameAndPorts;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
 import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiExpectTest;
@@ -47,13 +48,14 @@ public class CreateSecurityGroupIfNeededTest extends BaseNovaApiExpectTest {
                               "{\"security_group\":{\"name\":\"jclouds_mygroup\",\"description\":\"jclouds_mygroup\"}}",
                               "application/json")).build();
 
+   private final int groupId = 2769;
+
    public void testCreateNewGroup() throws Exception {
 
       Builder<HttpRequest, HttpResponse> builder = ImmutableMap.builder();
 
       builder.put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess);
       builder.put(extensionsOfNovaRequest, extensionsOfNovaResponse);
-      int groupId = 2769;
 
       HttpResponse createResponse = HttpResponse.builder().statusCode(200)
                .payload(
@@ -113,15 +115,24 @@ public class CreateSecurityGroupIfNeededTest extends BaseNovaApiExpectTest {
 
       builder.put(getSecurityGroup, getSecurityGroupResponse);
 
+
+      HttpRequest listSecurityGroups = HttpRequest.builder().method("GET").endpoint(
+         URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456/os-security-groups")).headers(
+         ImmutableMultimap.<String, String> builder().put("Accept", "application/json").put("X-Auth-Token",
+            authToken).build()).build();
+      HttpResponse listSecurityGroupsResponse = HttpResponse.builder().statusCode(200).payload(
+         payloadFromResource("/securitygroup_list_details_computeservice_typical.json")).build();
+      builder.put(listSecurityGroups, listSecurityGroupsResponse);
+
       NovaApi apiCanCreateSecurityGroup = requestsSendResponses(builder.build());
 
       CreateSecurityGroupIfNeeded fn = new CreateSecurityGroupIfNeeded(apiCanCreateSecurityGroup);
 
       // we can find it
-      assertEquals(fn.apply(
-               new RegionSecurityGroupNameAndPorts("az-1.region-a.geo-1", "jclouds_mygroup", ImmutableSet.of(22, 8080)))
-               .toString(), new SecurityGroupInRegion(new ParseComputeServiceTypicalSecurityGroupTest().expected(),
-               "az-1.region-a.geo-1").toString());
+      final SecurityGroup expected = new ParseComputeServiceTypicalSecurityGroupTest().expected();
+      assertEquals(
+         fn.apply(new RegionSecurityGroupNameAndPorts("az-1.region-a.geo-1", "jclouds_mygroup", ImmutableSet.of(22, 8080))).toString(),
+         new SecurityGroupInRegion(expected, "az-1.region-a.geo-1", ImmutableList.of(expected)).toString());
 
    }
 
@@ -155,10 +166,9 @@ public class CreateSecurityGroupIfNeededTest extends BaseNovaApiExpectTest {
       CreateSecurityGroupIfNeeded fn = new CreateSecurityGroupIfNeeded(apiWhenSecurityGroupsExist);
 
       // we can find it
-      assertEquals(fn.apply(
-               new RegionSecurityGroupNameAndPorts("az-1.region-a.geo-1", "jclouds_mygroup", ImmutableSet.of(22, 8080)))
-               .toString(), new SecurityGroupInRegion(new ParseComputeServiceTypicalSecurityGroupTest().expected(),
-               "az-1.region-a.geo-1").toString());
-
+      final SecurityGroup expected = new ParseComputeServiceTypicalSecurityGroupTest().expected();
+      assertEquals(
+         fn.apply(new RegionSecurityGroupNameAndPorts("az-1.region-a.geo-1", "jclouds_mygroup", ImmutableSet.of(22, 8080))).toString(),
+         new SecurityGroupInRegion(expected, "az-1.region-a.geo-1", ImmutableList.of(expected)).toString());
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/FindSecurityGroupWithNameAndReturnTrueExpectTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/FindSecurityGroupWithNameAndReturnTrueExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/FindSecurityGroupWithNameAndReturnTrueExpectTest.java
index d49fb8c..e9e9b4d 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/FindSecurityGroupWithNameAndReturnTrueExpectTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/FindSecurityGroupWithNameAndReturnTrueExpectTest.java
@@ -21,11 +21,13 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
 import java.net.URI;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.openstack.nova.v2_0.NovaApi;
+import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
 import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiExpectTest;
@@ -63,9 +65,10 @@ public class FindSecurityGroupWithNameAndReturnTrueExpectTest extends BaseNovaAp
       assertTrue(predicate.apply(securityGroupInRegionRef));
 
       // the reference is now up to date, and includes the actual group found.
-      assertEquals(securityGroupInRegionRef.get().toString(), new SecurityGroupInRegion(Iterables
-               .getOnlyElement(new ParseSecurityGroupListTest().expected()), "az-1.region-a.geo-1").toString());
-
+      final Set<SecurityGroup> expected = new ParseSecurityGroupListTest().expected();
+      assertEquals(
+         securityGroupInRegionRef.get().toString(),
+         new SecurityGroupInRegion(Iterables.getOnlyElement(expected), "az-1.region-a.geo-1", expected).toString());
    }
 
    public void testDoesNotUpdateReferenceWhenSecurityGroupListMissingGroupName() throws Exception {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/717b75a3/core/src/test/java/org/jclouds/rest/internal/BaseRestApiExpectTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/rest/internal/BaseRestApiExpectTest.java b/core/src/test/java/org/jclouds/rest/internal/BaseRestApiExpectTest.java
index 6b4e2ec..d0f1aa4 100644
--- a/core/src/test/java/org/jclouds/rest/internal/BaseRestApiExpectTest.java
+++ b/core/src/test/java/org/jclouds/rest/internal/BaseRestApiExpectTest.java
@@ -337,7 +337,8 @@ public abstract class BaseRestApiExpectTest<S> {
                         String.format("request %s is out of range (%s)", index, requests.size())).payload(
                         Payloads.newStringPayload(renderRequest(input))).build();
             if (!httpRequestsAreEqual(input, requests.get(index))) {
-               assertEquals(renderRequest(input), renderRequest(requests.get(index)));
+               assertEquals(renderRequest(input), renderRequest(requests.get(index)),
+                  "Actual request did not match expected request " + index);
             }
             return responses.get(index);
          }