You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ab...@apache.org on 2013/06/26 23:59:07 UTC

[1/4] JCLOUDS-101 - Add SecurityGroupExtension for compute

Updated Branches:
  refs/heads/master a906f9f4e -> 5f524ee6c


http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtension.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtension.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtension.java
new file mode 100644
index 0000000..c0e21f0
--- /dev/null
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtension.java
@@ -0,0 +1,177 @@
+/*
+ * 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.aws.ec2.compute.extensions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.base.Predicates.notNull;
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.collect.Iterables.toArray;
+import static com.google.common.collect.Iterables.transform;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+import org.jclouds.Constants;
+import org.jclouds.aws.ec2.AWSEC2Client;
+import org.jclouds.aws.util.AWSUtils;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.functions.GroupNamingConvention.Factory;
+import org.jclouds.domain.Location;
+import org.jclouds.ec2.compute.domain.RegionAndName;
+import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
+import org.jclouds.ec2.compute.extensions.EC2SecurityGroupExtension;
+import org.jclouds.ec2.domain.RunningInstance;
+import org.jclouds.ec2.domain.UserIdGroupPair;
+import org.jclouds.location.Region;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+
+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.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+
+/**
+ * An extension to compute service to allow for the manipulation of {@link SecurityGroup}s. Implementation
+ * is optional by providers.
+ * 
+ * @author Andrew Bayer
+ */
+public class AWSEC2SecurityGroupExtension extends EC2SecurityGroupExtension {
+   protected final AWSEC2Client client;
+
+   @Inject
+   public AWSEC2SecurityGroupExtension(AWSEC2Client client,
+                                       @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
+                                       @Region Supplier<Set<String>> regions,
+                                       Function<org.jclouds.ec2.domain.SecurityGroup, SecurityGroup> groupConverter,
+                                       @Memoized Supplier<Set<? extends Location>> locations,
+                                       @Named("SECURITY") LoadingCache<RegionAndName, String> groupCreator,
+                                       GroupNamingConvention.Factory namingConvention) {
+      super(client, userExecutor, regions, groupConverter, locations, groupCreator, namingConvention);
+      this.client = checkNotNull(client, "client");
+   }
+
+
+   @Override
+   public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) {
+
+      String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
+      String name = group.getName();
+
+      client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name, ipPermission);
+
+      return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
+   }
+
+   @Override
+   public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                        Multimap<String, String> tenantIdGroupNamePairs,
+                                        Iterable<String> ipRanges,
+                                        Iterable<String> groupIds, SecurityGroup group) {
+      String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
+      String name = group.getName();
+
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(protocol);
+      builder.fromPort(startPort);
+      builder.toPort(endPort);
+      
+      if (Iterables.size(ipRanges) > 0) {
+         for (String cidr : ipRanges) {
+            builder.cidrBlock(cidr);
+         }
+      }
+
+      if (tenantIdGroupNamePairs.size() > 0) {
+         for (String userId : tenantIdGroupNamePairs.keySet()) {
+            for (String groupName : tenantIdGroupNamePairs.get(userId)) {
+               builder.tenantIdGroupNamePair(userId, groupName);
+            }
+         }
+      }
+
+      client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name, builder.build());
+
+      return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
+   }
+      
+   @Override
+   public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) {
+      String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
+      String name = group.getName();
+
+      client.getSecurityGroupServices().revokeSecurityGroupIngressInRegion(region, name, ipPermission);
+
+      return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
+   }
+
+   @Override
+   public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                           Multimap<String, String> tenantIdGroupNamePairs,
+                                           Iterable<String> ipRanges,
+                                           Iterable<String> groupIds, SecurityGroup group) {
+      String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
+      String name = group.getName();
+
+
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(protocol);
+      builder.fromPort(startPort);
+      builder.toPort(endPort);
+      
+      if (Iterables.size(ipRanges) > 0) {
+         for (String cidr : ipRanges) {
+            builder.cidrBlock(cidr);
+         }
+      }
+
+      if (tenantIdGroupNamePairs.size() > 0) {
+         for (String userId : tenantIdGroupNamePairs.keySet()) {
+            for (String groupName : tenantIdGroupNamePairs.get(userId)) {
+               builder.tenantIdGroupNamePair(userId, groupName);
+            }
+         }
+      }
+
+      client.getSecurityGroupServices().revokeSecurityGroupIngressInRegion(region, name, builder.build());
+
+      return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeeded.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeeded.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeeded.java
index ad14efe..6676ef6 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeeded.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeeded.java
@@ -32,10 +32,10 @@ import org.jclouds.aws.ec2.services.AWSSecurityGroupClient;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.ec2.compute.domain.RegionAndName;
 import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
-import org.jclouds.ec2.domain.IpPermission;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.UserIdGroupPair;
 import org.jclouds.logging.Logger;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
 
 import com.google.common.base.Predicate;
 import com.google.common.cache.CacheLoader;
@@ -95,7 +95,7 @@ public class AWSEC2CreateSecurityGroupIfNeeded extends CacheLoader<RegionAndName
                                .fromPort(range.getKey())
                                .toPort(range.getValue())
                                .ipProtocol(IpProtocol.TCP)
-                               .ipRange("0.0.0.0/0")
+                               .cidrBlock("0.0.0.0/0")
                                .build());
             }
 
@@ -104,13 +104,13 @@ public class AWSEC2CreateSecurityGroupIfNeeded extends CacheLoader<RegionAndName
                             .fromPort(0)
                             .toPort(65535)
                             .ipProtocol(IpProtocol.TCP)
-                            .userIdGroupPair(myOwnerId, name)
+                            .tenantIdGroupNamePair(myOwnerId, name)
                             .build());
             permissions.add(IpPermission.builder()
                             .fromPort(0)
                             .toPort(65535)
                             .ipProtocol(IpProtocol.UDP)
-                            .userIdGroupPair(myOwnerId, name)
+                            .tenantIdGroupNamePair(myOwnerId, name)
                             .build());
          }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClient.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClient.java
index 75a6f9b..25acb03 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClient.java
@@ -33,12 +33,12 @@ import org.jclouds.aws.filters.FormSigner;
 import org.jclouds.ec2.binders.BindGroupIdsToIndexedFormParams;
 import org.jclouds.ec2.binders.BindIpPermissionToIndexedFormParams;
 import org.jclouds.ec2.binders.BindIpPermissionsToIndexedFormParams;
-import org.jclouds.ec2.domain.IpPermission;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.services.SecurityGroupAsyncClient;
 import org.jclouds.ec2.xml.DescribeSecurityGroupsResponseHandler;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
+import org.jclouds.net.domain.IpPermission;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.EndpointParam;
 import org.jclouds.rest.annotations.Fallback;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClient.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClient.java
index 383b0fd..ac3b301 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClient.java
@@ -18,10 +18,10 @@ package org.jclouds.aws.ec2.services;
 
 import java.util.Set;
 import org.jclouds.aws.ec2.options.CreateSecurityGroupOptions;
-import org.jclouds.ec2.domain.IpPermission;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.services.SecurityGroupClient;
 import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.net.domain.IpPermission;
 
 import com.google.common.annotations.Beta;
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java
index 7ee0e9d..ae39523 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java
@@ -48,11 +48,11 @@ import org.jclouds.compute.predicates.NodePredicates;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.ec2.EC2Client;
 import org.jclouds.ec2.compute.EC2ComputeServiceLiveTest;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.KeyPair;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.services.InstanceClient;
 import org.jclouds.ec2.services.KeyPairClient;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.scriptbuilder.domain.Statements;
 import org.testng.annotations.Test;
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionExpectTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionExpectTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionExpectTest.java
new file mode 100644
index 0000000..4bae7c3
--- /dev/null
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionExpectTest.java
@@ -0,0 +1,367 @@
+/*
+ * 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.aws.ec2.compute.extensions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.aws.ec2.compute.internal.BaseAWSEC2ComputeServiceExpectTest;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.ec2.compute.domain.RegionAndName;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Futures;
+
+/**
+ * 
+ * @author Andrew Bayer
+ */
+@Test(groups = "unit", testName = "AWSEC2SecurityGroupExtensionExpectTest")
+public class AWSEC2SecurityGroupExtensionExpectTest extends BaseAWSEC2ComputeServiceExpectTest {
+
+   public void testAddIpPermissionCidrFromIpPermission() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_cidr.xml", MediaType.APPLICATION_XML)).build();
+      
+
+      HttpRequest authorizeSecurityGroupIngressRequestRange = 
+               formSigner.filter(HttpRequest.builder()
+                                 .method("POST")
+                                 .endpoint("https://ec2." + region + ".amazonaws.com/")
+                                 .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                                 .addFormParam("Action", "AuthorizeSecurityGroupIngress")
+                                 .addFormParam("GroupId", "jclouds#some-group")
+                                 .addFormParam("IpPermissions.0.FromPort", "22")
+                                 .addFormParam("IpPermissions.0.IpProtocol", "tcp")
+                                 .addFormParam("IpPermissions.0.IpRanges.0.CidrIp", "0.0.0.0/0")
+                                 .addFormParam("IpPermissions.0.ToPort", "40")
+                                 .build());
+                                 
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse);
+
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(IpProtocol.TCP);
+      builder.fromPort(22);
+      builder.toPort(40);
+      builder.cidrBlock("0.0.0.0/0");
+
+      IpPermission perm = builder.build();
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+
+      SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder();
+      groupBuilder.id("jclouds#some-group");
+      groupBuilder.providerId("sg-3c6ef654");
+      groupBuilder.name("jclouds#some-group");
+      groupBuilder.location(new LocationBuilder()
+                            .scope(LocationScope.REGION)
+                            .id(region)
+                            .description("region")
+                            .build());
+      
+      SecurityGroup origGroup = groupBuilder.build();
+
+      SecurityGroup newGroup = extension.addIpPermission(perm, origGroup);
+
+      assertEquals(1, newGroup.getIpPermissions().size());
+
+      IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions());
+
+      assertNotNull(newPerm);
+      assertEquals(IpProtocol.TCP, newPerm.getIpProtocol());
+      assertEquals(22, newPerm.getFromPort());
+      assertEquals(40, newPerm.getToPort());
+      assertEquals(1, newPerm.getCidrBlocks().size());
+      assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0"));
+   }
+
+   public void testAddIpPermissionCidrFromParams() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_cidr.xml", MediaType.APPLICATION_XML)).build();
+      
+
+      HttpRequest authorizeSecurityGroupIngressRequestRange = 
+               formSigner.filter(HttpRequest.builder()
+                                 .method("POST")
+                                 .endpoint("https://ec2." + region + ".amazonaws.com/")
+                                 .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                                 .addFormParam("Action", "AuthorizeSecurityGroupIngress")
+                                 .addFormParam("GroupId", "jclouds#some-group")
+                                 .addFormParam("IpPermissions.0.FromPort", "22")
+                                 .addFormParam("IpPermissions.0.IpProtocol", "tcp")
+                                 .addFormParam("IpPermissions.0.IpRanges.0.CidrIp", "0.0.0.0/0")
+                                 .addFormParam("IpPermissions.0.ToPort", "40")
+                                 .build());
+      
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse);
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+
+      SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder();
+      groupBuilder.id("jclouds#some-group");
+      groupBuilder.providerId("sg-3c6ef654");
+      groupBuilder.name("jclouds#some-group");
+      groupBuilder.location(new LocationBuilder()
+                            .scope(LocationScope.REGION)
+                            .id(region)
+                            .description("region")
+                            .build());
+      
+      SecurityGroup origGroup = groupBuilder.build();
+
+      SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP,
+                                                         22,
+                                                         40,
+                                                         emptyMultimap(),
+                                                         ImmutableSet.of("0.0.0.0/0"),
+                                                         emptyStringSet(),
+                                                         origGroup);
+
+      assertEquals(1, newGroup.getIpPermissions().size());
+
+      IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions());
+
+      assertNotNull(newPerm);
+      assertEquals(IpProtocol.TCP, newPerm.getIpProtocol());
+      assertEquals(22, newPerm.getFromPort());
+      assertEquals(40, newPerm.getToPort());
+      assertEquals(1, newPerm.getCidrBlocks().size());
+      assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0"));
+   }
+
+   public void testAddIpPermissionGroupFromIpPermission() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build();
+      
+
+      HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = 
+               formSigner.filter(HttpRequest.builder()
+                                 .method("POST")
+                                 .endpoint("https://ec2." + region + ".amazonaws.com/")
+                                 .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                                 .addFormParam("Action", "AuthorizeSecurityGroupIngress")
+                                 .addFormParam("GroupId", "jclouds#some-group")
+                                 .addFormParam("IpPermissions.0.FromPort", "22")
+                                 .addFormParam("IpPermissions.0.Groups.0.GroupName", "jclouds#some-group")
+                                 .addFormParam("IpPermissions.0.Groups.0.UserId", "993194456877")
+                                 .addFormParam("IpPermissions.0.IpProtocol", "tcp")
+                                 .addFormParam("IpPermissions.0.ToPort", "40")
+                                 .build());
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse);
+
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(IpProtocol.TCP);
+      builder.fromPort(22);
+      builder.toPort(40);
+      builder.tenantIdGroupNamePair("993194456877", "jclouds#some-group");
+
+      IpPermission perm = builder.build();
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+
+      SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder();
+      groupBuilder.id("jclouds#some-group");
+      groupBuilder.providerId("sg-3c6ef654");
+      groupBuilder.name("jclouds#some-group");
+      groupBuilder.location(new LocationBuilder()
+                            .scope(LocationScope.REGION)
+                            .id(region)
+                            .description("region")
+                            .build());
+      groupBuilder.ownerId("993194456877");
+      
+      SecurityGroup origGroup = groupBuilder.build();
+
+      SecurityGroup newGroup = extension.addIpPermission(perm, origGroup);
+
+      assertEquals(1, newGroup.getIpPermissions().size());
+
+      IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions());
+
+      assertNotNull(newPerm);
+      assertEquals(IpProtocol.TCP, newPerm.getIpProtocol());
+      assertEquals(22, newPerm.getFromPort());
+      assertEquals(40, newPerm.getToPort());
+      assertEquals(0, newPerm.getCidrBlocks().size());
+      assertEquals(1, newPerm.getTenantIdGroupNamePairs().size());
+      assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId()));
+      assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getName()));
+   }
+
+
+   public void testAddIpPermissionGroupFromParams() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build();
+
+
+      HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = 
+               formSigner.filter(HttpRequest.builder()
+                                 .method("POST")
+                                 .endpoint("https://ec2." + region + ".amazonaws.com/")
+                                 .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                                 .addFormParam("Action", "AuthorizeSecurityGroupIngress")
+                                 .addFormParam("GroupId", "jclouds#some-group")
+                                 .addFormParam("IpPermissions.0.FromPort", "22")
+                                 .addFormParam("IpPermissions.0.Groups.0.GroupName", "jclouds#some-group")
+                                 .addFormParam("IpPermissions.0.Groups.0.UserId", "993194456877")
+                                 .addFormParam("IpPermissions.0.IpProtocol", "tcp")
+                                 .addFormParam("IpPermissions.0.ToPort", "40")
+                                 .build());
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse);
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+
+      SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder();
+      groupBuilder.id("jclouds#some-group");
+      groupBuilder.providerId("sg-3c6ef654");
+      groupBuilder.name("jclouds#some-group");
+      groupBuilder.ownerId("993194456877");
+      groupBuilder.location(new LocationBuilder()
+                            .scope(LocationScope.REGION)
+                            .id(region)
+                            .description("region")
+                            .build());
+      
+      SecurityGroup origGroup = groupBuilder.build();
+
+      ImmutableMultimap.Builder<String, String> permBuilder = ImmutableMultimap.builder();
+      permBuilder.put(origGroup.getOwnerId(), origGroup.getName());
+      
+      SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP,
+                                                         22,
+                                                         40,
+                                                         permBuilder.build(),
+                                                         emptyStringSet(),
+                                                         emptyStringSet(),
+                                                         origGroup);
+
+      assertEquals(1, newGroup.getIpPermissions().size());
+
+      IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions());
+
+      assertNotNull(newPerm);
+      assertEquals(IpProtocol.TCP, newPerm.getIpProtocol());
+      assertEquals(22, newPerm.getFromPort());
+      assertEquals(40, newPerm.getToPort());
+      assertEquals(0, newPerm.getCidrBlocks().size());
+      assertEquals(1, newPerm.getTenantIdGroupNamePairs().size());
+      assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId()));
+      assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getName()));
+   }
+
+   private Multimap<String, String> emptyMultimap() {
+      return LinkedHashMultimap.create();
+   }
+
+   private Set<String> emptyStringSet() {
+      return Sets.newLinkedHashSet();
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionLiveTest.java
new file mode 100644
index 0000000..9de4f24
--- /dev/null
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionLiveTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.aws.ec2.compute.extensions;
+
+import static com.google.common.collect.Iterables.transform;
+
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.extensions.internal.BaseSecurityGroupExtensionLiveTest;
+import org.testng.annotations.Test;
+
+import com.google.inject.Module;
+
+/**
+ * Live test for aws-ec2 {@link SecurityGroupExtension} implementation
+ * 
+ * @author Andrew Bayer
+ * 
+ */
+@Test(groups = "live", singleThreaded = true, testName = "AWSEC2SecurityGroupExtensionLiveTest")
+public class AWSEC2SecurityGroupExtensionLiveTest extends BaseSecurityGroupExtensionLiveTest {
+
+   public AWSEC2SecurityGroupExtensionLiveTest() {
+      provider = "aws-ec2";
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeededTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeededTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeededTest.java
index db3093c..269a8f0 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeededTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeededTest.java
@@ -30,10 +30,10 @@ import java.util.concurrent.ExecutionException;
 import org.jclouds.aws.ec2.services.AWSSecurityGroupClient;
 import org.jclouds.ec2.compute.domain.RegionAndName;
 import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
-import org.jclouds.ec2.domain.IpPermission;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.domain.UserIdGroupPair;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicate;
@@ -63,20 +63,20 @@ public class AWSEC2CreateSecurityGroupIfNeededTest {
                       .fromPort(22)
                       .toPort(22)
                       .ipProtocol(IpProtocol.TCP)
-                      .ipRange("0.0.0.0/0")
+                      .cidrBlock("0.0.0.0/0")
                       .build());
 
       permissions.add(IpPermission.builder()
                       .fromPort(0)
                       .toPort(65535)
                       .ipProtocol(IpProtocol.TCP)
-                      .userIdGroupPair("ownerId", "group")
+                      .tenantIdGroupNamePair("ownerId", "group")
                       .build());
       permissions.add(IpPermission.builder()
                       .fromPort(0)
                       .toPort(65535)
                       .ipProtocol(IpProtocol.UDP)
-                      .userIdGroupPair("ownerId", "group")
+                      .tenantIdGroupNamePair("ownerId", "group")
                       .build());
       
       client.createSecurityGroupInRegion("region", "group", "group");

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/parse/DescribeSecurityGroupsResponseTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/parse/DescribeSecurityGroupsResponseTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/parse/DescribeSecurityGroupsResponseTest.java
index 40e49dd..8f948f5 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/parse/DescribeSecurityGroupsResponseTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/parse/DescribeSecurityGroupsResponseTest.java
@@ -25,12 +25,12 @@ import static org.testng.Assert.assertEquals;
 import java.io.InputStream;
 import java.util.Set;
 
-import org.jclouds.ec2.domain.IpPermission;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.xml.BaseEC2HandlerTest;
 import org.jclouds.ec2.xml.DescribeSecurityGroupsResponseHandler;
 import org.jclouds.http.functions.ParseSax;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.reflect.Invocation;
 import org.jclouds.rest.internal.GeneratedHttpRequest;
 import org.testng.annotations.Test;
@@ -69,7 +69,7 @@ public class DescribeSecurityGroupsResponseTest extends BaseEC2HandlerTest {
 //                                          .vpcId("vpc-99999999")
                                           .ipPermission(IpPermission.builder()
                                                                     .ipProtocol(IpProtocol.ALL)
-                                                                    .userIdGroupPair("123123123123","sg-11111111").build())
+                                                                    .tenantIdGroupNamePair("123123123123","sg-11111111").build())
 //                                          .ipPermissionEgress(IpPermission.builder()
 //                                                                    .ipProtocol(IpProtocol.ALL)
 //                                                                    .ipRange("0.0.0.0/0").build())

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java
index 2ad3467..9ebead5 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java
@@ -22,13 +22,13 @@ import java.io.IOException;
 
 import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404;
 import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
-import org.jclouds.ec2.domain.IpPermission;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.util.IpPermissions;
 import org.jclouds.ec2.xml.DescribeSecurityGroupsResponseHandler;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseSax;
 import org.jclouds.http.functions.ReleasePayloadAndReturn;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.rest.internal.GeneratedHttpRequest;
 import org.testng.annotations.Test;
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClientLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClientLiveTest.java
index 0ec81dd..76c7e91 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClientLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClientLiveTest.java
@@ -21,11 +21,11 @@ import static org.testng.Assert.assertNotNull;
 
 import java.util.Set;
 
-import org.jclouds.ec2.domain.IpPermission;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.services.SecurityGroupClientLiveTest;
 import org.jclouds.ec2.util.IpPermissions;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicate;
@@ -69,7 +69,7 @@ public class AWSSecurityGroupClientLiveTest extends SecurityGroupClientLiveTest
          assertEventually(new GroupHasPermission(client, group2Name, new Predicate<IpPermission>() {
             @Override
             public boolean apply(IpPermission arg0) {
-               return arg0.getUserIdGroupPairs().equals(ImmutableMultimap.of(group.getOwnerId(), group1Name))
+               return arg0.getTenantIdGroupNamePairs().equals(ImmutableMultimap.of(group.getOwnerId(), group1Name))
                      && arg0.getFromPort() == 80 && arg0.getToPort() == 80 && arg0.getIpProtocol() == IpProtocol.TCP;
             }
          }));

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java
----------------------------------------------------------------------
diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java
index 3f5935c..8e34a5e 100644
--- a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java
+++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java
@@ -37,6 +37,7 @@ import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.internal.BaseComputeService;
 import org.jclouds.compute.internal.PersistNodeCredentials;
 import org.jclouds.compute.options.TemplateOptions;
@@ -81,12 +82,12 @@ public class GoGridComputeService extends BaseComputeService {
             InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess,
             RunScriptOnNode.Factory runScriptOnNodeFactory, PersistNodeCredentials persistNodeCredentials,
             Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
-            Optional<ImageExtension> imageExtension) {
+            Optional<ImageExtension> imageExtension, Optional<SecurityGroupExtension> securityGroupExtension) {
       super(context, credentialStore, images, hardwareProfiles, locations, listNodesStrategy, getImageStrategy,
                getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
                resumeNodeStrategy, suspendNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
                nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
-               persistNodeCredentials, timeouts, userExecutor, imageExtension);
+               persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension);
    }
 
    /**


[2/4] JCLOUDS-101 - Add SecurityGroupExtension for compute

Posted by ab...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java
index 5da793f..9b056b9 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java
@@ -23,8 +23,8 @@ import javax.ws.rs.core.MediaType;
 
 import org.jclouds.json.BaseItemParserTest;
 import org.jclouds.json.config.GsonModule;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.config.NovaParserModule;
-import org.jclouds.openstack.nova.v2_0.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
 import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupListTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupListTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupListTest.java
index 9f477ee..4e46ede 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupListTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupListTest.java
@@ -23,8 +23,8 @@ import javax.ws.rs.core.MediaType;
 
 import org.jclouds.json.BaseSetParserTest;
 import org.jclouds.json.config.GsonModule;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.config.NovaParserModule;
-import org.jclouds.openstack.nova.v2_0.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
 import org.jclouds.rest.annotations.SelectJson;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java
index a5c83f4..784a0bd 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java
@@ -23,8 +23,8 @@ import javax.ws.rs.core.MediaType;
 
 import org.jclouds.json.BaseItemParserTest;
 import org.jclouds.json.config.GsonModule;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.config.NovaParserModule;
-import org.jclouds.openstack.nova.v2_0.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
 import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/TerremarkVCloudComputeService.java
----------------------------------------------------------------------
diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/TerremarkVCloudComputeService.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/TerremarkVCloudComputeService.java
index 1431d54..b37e85e 100644
--- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/TerremarkVCloudComputeService.java
+++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/TerremarkVCloudComputeService.java
@@ -37,6 +37,7 @@ import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.internal.BaseComputeService;
 import org.jclouds.compute.internal.PersistNodeCredentials;
 import org.jclouds.compute.options.TemplateOptions;
@@ -84,12 +85,12 @@ public class TerremarkVCloudComputeService extends BaseComputeService {
             RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
             PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
             @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, CleanupOrphanKeys cleanupOrphanKeys,
-            Optional<ImageExtension> imageExtension) {
+            Optional<ImageExtension> imageExtension, Optional<SecurityGroupExtension> securityGroupExtension) {
       super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
                getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
                resumeNodeStrategy, suspendNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
                nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
-               persistNodeCredentials, timeouts, userExecutor, imageExtension);
+               persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension);
       this.cleanupOrphanKeys = cleanupOrphanKeys;
    }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/ComputeService.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/ComputeService.java b/compute/src/main/java/org/jclouds/compute/ComputeService.java
index 610090a..a7c2519 100644
--- a/compute/src/main/java/org/jclouds/compute/ComputeService.java
+++ b/compute/src/main/java/org/jclouds/compute/ComputeService.java
@@ -27,6 +27,7 @@ import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.internal.BaseComputeService;
 import org.jclouds.compute.options.RunScriptOptions;
 import org.jclouds.compute.options.TemplateOptions;
@@ -383,5 +384,13 @@ public interface ComputeService {
    @Beta
    Optional<ImageExtension> getImageExtension();
 
+   /**
+    * Returns the {@link SecurityGroupExtension} for this provider if it implements it.
+    * 
+    * @return an optional of the {@link SecurityGroupExtension} or {@link Optional#absent()} if not
+    *         implemented
+    */
+   @Beta
+   Optional<SecurityGroupExtension> getSecurityGroupExtension();
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
index 7581e86..7307196 100644
--- a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
+++ b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
@@ -44,6 +44,7 @@ import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.functions.CreateSshClientOncePortIsListeningOnNode;
 import org.jclouds.compute.functions.DefaultCredentialsFromImageOrOverridingCredentials;
 import org.jclouds.compute.functions.TemplateOptionsToStatement;
@@ -306,5 +307,11 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
       return Optional.absent();
    }
    
+   @Provides
+   @Singleton
+   protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i){
+      return Optional.absent();
+   }
+   
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/domain/ComputeType.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/domain/ComputeType.java b/compute/src/main/java/org/jclouds/compute/domain/ComputeType.java
index 26df759..8e2c75c 100644
--- a/compute/src/main/java/org/jclouds/compute/domain/ComputeType.java
+++ b/compute/src/main/java/org/jclouds/compute/domain/ComputeType.java
@@ -22,6 +22,6 @@ package org.jclouds.compute.domain;
  */
 public enum ComputeType {
 
-   NODE, IMAGE, HARDWARE;
+   NODE, IMAGE, HARDWARE, SECURITYGROUP;
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/domain/SecurityGroup.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/domain/SecurityGroup.java b/compute/src/main/java/org/jclouds/compute/domain/SecurityGroup.java
new file mode 100644
index 0000000..fecba5a
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/domain/SecurityGroup.java
@@ -0,0 +1,80 @@
+/*
+ * 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.compute.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.compute.domain.ComputeType;
+import org.jclouds.compute.domain.internal.ComputeMetadataImpl;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.ResourceMetadata;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.net.domain.IpPermission;
+
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Describes a security group containing a set of @{link IpPermission}s
+ * 
+ * @author Andrew Bayer
+ */
+public class SecurityGroup extends ComputeMetadataImpl {
+
+   private final Set<IpPermission> ipPermissions;
+   private final String ownerId;
+   
+   public SecurityGroup(String providerId, String name, String id, @Nullable Location location, URI uri,
+                        Map<String, String> userMetadata, Set<String> tags,
+                        Iterable<IpPermission> ipPermissions,
+                        @Nullable String ownerId) { 
+      super(ComputeType.SECURITYGROUP, providerId, name, id, location, uri, userMetadata, tags);
+      this.ipPermissions = ImmutableSet.copyOf(checkNotNull(ipPermissions, "ipPermissions"));
+      this.ownerId = ownerId;
+   }
+
+   /**
+    * 
+    * @return The set of @{link IpPermission}s for this security group
+    */
+   public Set<IpPermission> getIpPermissions() {
+      return ipPermissions;
+   }
+
+   /**
+    *
+    * @return the owner ID. Can be null.
+    */
+   public String getOwnerId() {
+      return ownerId;
+   }
+   
+   @Override
+   protected ToStringHelper string() {
+      ToStringHelper helper = computeToStringPrefix();
+      if (ipPermissions.size() > 0)
+         helper.add("ipPermissions", ipPermissions);
+      return addComputeToStringSuffix(helper);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/domain/SecurityGroupBuilder.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/domain/SecurityGroupBuilder.java b/compute/src/main/java/org/jclouds/compute/domain/SecurityGroupBuilder.java
new file mode 100644
index 0000000..002116f
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/domain/SecurityGroupBuilder.java
@@ -0,0 +1,122 @@
+/*
+ * 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.compute.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.net.domain.IpPermission;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+/**
+ * @author Andrew Bayer
+ */
+public class SecurityGroupBuilder extends ComputeMetadataBuilder {
+   private ImmutableSet.Builder<IpPermission> ipPermissions = ImmutableSet.<IpPermission> builder();
+   private String ownerId;
+   
+   public SecurityGroupBuilder() {
+      super(ComputeType.SECURITYGROUP);
+   }
+
+   public SecurityGroupBuilder ipPermissions() {
+      this.ipPermissions = ImmutableSet.<IpPermission> builder();
+      return this;
+   }
+
+   public SecurityGroupBuilder ipPermissions(Iterable<IpPermission> ipPermissions) {
+      this.ipPermissions.addAll(checkNotNull(ipPermissions, "ipPermissions"));
+      return this;
+   }
+
+   public SecurityGroupBuilder ipPermission(IpPermission ipPermission) {
+      this.ipPermissions.add(checkNotNull(ipPermission, "ipPermission"));
+      return this;
+   }
+
+   public SecurityGroupBuilder ownerId(String ownerId) {
+      this.ownerId = ownerId;
+      return this;
+   }
+
+   @Override
+   public SecurityGroupBuilder id(String id) {
+      return SecurityGroupBuilder.class.cast(super.id(id));
+   }
+
+   @Override
+   public SecurityGroupBuilder tags(Iterable<String> tags) {
+      return SecurityGroupBuilder.class.cast(super.tags(tags));
+   }
+
+   @Override
+   public SecurityGroupBuilder ids(String id) {
+      return SecurityGroupBuilder.class.cast(super.ids(id));
+   }
+
+   @Override
+   public SecurityGroupBuilder providerId(String providerId) {
+      return SecurityGroupBuilder.class.cast(super.providerId(providerId));
+   }
+
+   @Override
+   public SecurityGroupBuilder name(String name) {
+      return SecurityGroupBuilder.class.cast(super.name(name));
+   }
+
+   @Override
+   public SecurityGroupBuilder location(Location location) {
+      return SecurityGroupBuilder.class.cast(super.location(location));
+   }
+
+   @Override
+   public SecurityGroupBuilder uri(URI uri) {
+      return SecurityGroupBuilder.class.cast(super.uri(uri));
+   }
+
+   @Override
+   public SecurityGroupBuilder userMetadata(Map<String, String> userMetadata) {
+      return SecurityGroupBuilder.class.cast(super.userMetadata(userMetadata));
+   }
+
+   @Override
+   public SecurityGroup build() {
+      return new SecurityGroup(providerId, name, id, location, uri, userMetadata, tags,
+                               ipPermissions.build(), ownerId);
+   }
+
+   public static SecurityGroupBuilder fromSecurityGroup(SecurityGroup group) {
+      return new SecurityGroupBuilder().providerId(group.getProviderId())
+         .name(group.getName())
+         .id(group.getId())
+         .location(group.getLocation())
+         .uri(group.getUri())
+         .userMetadata(group.getUserMetadata())
+         .tags(group.getTags())
+         .ipPermissions(group.getIpPermissions())
+         .ownerId(group.getOwnerId());
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/extensions/SecurityGroupExtension.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/extensions/SecurityGroupExtension.java b/compute/src/main/java/org/jclouds/compute/extensions/SecurityGroupExtension.java
new file mode 100644
index 0000000..f07e72d
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/extensions/SecurityGroupExtension.java
@@ -0,0 +1,180 @@
+/*
+ * 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.compute.extensions;
+
+import java.util.Set;
+
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.domain.Location;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * An extension to compute service to allow for the manipulation of {@link SecurityGroup}s. Implementation
+ * is optional by providers.
+ * 
+ * @author Andrew Bayer
+ */
+public interface SecurityGroupExtension {
+
+   /**
+    * List security groups.
+    *
+    * @return The set of @{link SecurityGroup}s we have access to.
+    */
+   Set<SecurityGroup> listSecurityGroups();
+
+   /**
+    * List security groups in a given @{link Location}.
+    *
+    * @return The set of @{link SecurityGroup}s we have access to in the given location.
+    */
+   Set<SecurityGroup> listSecurityGroupsInLocation(Location location);
+   
+   /**
+    * List security groups for a given instance given the instance's ID.
+    *
+    * @return The set of @{link SecurityGroup}s for the given instance..
+    */
+   Set<SecurityGroup> listSecurityGroupsForNode(String id);
+
+   /**
+    * Get a security group by id.
+    *
+    * @return The @{link SecurityGroup}, if it exists.
+    */
+   SecurityGroup getSecurityGroupById(String id);
+
+   /**
+    * Create a new @{link SecurityGroup} from the parameters given.
+    *
+    * @param name
+    *           The name of the security group
+    * @param location
+    *           The @{link Location} of the security group
+    *
+    * @return The SecurityGroup that has been created.
+    */
+   SecurityGroup createSecurityGroup(String name, Location location);
+
+   /**
+    * Remove an existing @{link SecurityGroup}, and its permissions.
+    *
+    * @param id
+    *           The id of the SecurityGroup to delete.
+    *
+    * @return true if we were able to remove the group, false otherwise.
+    */
+   boolean removeSecurityGroup(String id);
+
+   /**
+    * Add a @{link IpPermission} to an existing @{link SecurityGroup}. Applies the permission to the
+    *   security group on the provider.
+    *
+    * @param rule
+    *           The IpPermission to add.
+    * @param group
+    *           The SecurityGroup to add the permission to.
+    *
+    * @return The SecurityGroup with the new permission added, after the permission has been applied on the provider.
+    */
+   SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group);
+
+   /**
+    * Remove a @{link IpPermission} from an existing @{link SecurityGroup}. Removes the permission from the
+    *   security group on the provider.
+    *
+    * @param rule
+    *           The IpPermission to remove.
+    * @param group
+    *           The SecurityGroup to remove the permission from.
+    *
+    * @return The SecurityGroup with the permission removed, after the permission has been removed on the provider.
+    */
+   SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group);
+
+   /**
+    * Add a @{link IpPermission} to an existing @{link SecurityGroup}, based on the parameters given.
+    *   Applies the permission to the security group on the provider.
+    *
+    * @param protocol
+    *           The @{link IpProtocol} for the permission.
+    * @param startPort
+    *           The first port in the range to be opened, or -1 for ICMP.
+    * @param endPort
+    *           The last port in the range to be opened, or -1 for ICMP.
+    * @param tenantIdGroupNamePairs
+    *           source of traffic allowed is on basis of another group in a tenant, as opposed to by cidr
+    * @param ipRanges
+    *           An Iterable of Strings representing the IP range(s) the permission should allow.
+    * @param groupIds
+    *           An Iterable of @{link SecurityGroup} IDs this permission should allow.
+    * @param group
+    *           The SecurityGroup to add the permission to.
+    *
+    * @return The SecurityGroup with the new permission added, after the permission has been applied on the provider.
+    */
+   SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                 Multimap<String, String> tenantIdGroupNamePairs,
+                                 Iterable<String> ipRanges,
+                                 Iterable<String> groupIds, SecurityGroup group);
+
+   /**
+    * Remove a @{link IpPermission} from an existing @{link SecurityGroup}, based on the parameters given.
+    *   Removes the permission from the security group on the provider.
+    *
+    * @param protocol
+    *           The @{link IpProtocol} for the permission.
+    * @param startPort
+    *           The first port in the range to be opened, or -1 for ICMP.
+    * @param endPort
+    *           The last port in the range to be opened, or -1 for ICMP.
+    * @param tenantIdGroupNamePairs
+    *           source of traffic allowed is on basis of another group in a tenant, as opposed to by cidr
+    * @param ipRanges
+    *           An Iterable of Strings representing the IP range(s) the permission should allow.
+    * @param groupIds
+    *           An Iterable of @{link SecurityGroup} IDs this permission should allow.
+    * @param group
+    *           The SecurityGroup to remove the permission from.
+    *
+    * @return The SecurityGroup with the permission removed, after the permission has been removed from the provider.
+    */
+   SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                    Multimap<String, String> tenantIdGroupNamePairs,
+                                    Iterable<String> ipRanges,
+                                    Iterable<String> groupIds, SecurityGroup group);
+
+   /**
+    * Returns true if this SecurityGroupExtension supports tenant ID + group name pairs.
+    */
+   boolean supportsTenantIdGroupNamePairs();
+
+   /**
+    * Returns true if this SecurityGroupExtension supports group IDs.
+    */
+   boolean supportsGroupIds();
+
+   /**
+    * Returns true if this SecurityGroupExtension supports port ranges for group authorization.
+    */
+   boolean supportsPortRangesForGroups();
+   
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
index fd177ac..75ea787 100644
--- a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
+++ b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
@@ -66,6 +66,7 @@ import org.jclouds.compute.domain.NodeMetadataBuilder;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.options.RunScriptOptions;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.reference.ComputeServiceConstants;
@@ -141,6 +142,7 @@ public class BaseComputeService implements ComputeService {
    private final RunScriptOnNode.Factory runScriptOnNodeFactory;
    private final ListeningExecutorService userExecutor;
    private final Optional<ImageExtension> imageExtension;
+   private final Optional<SecurityGroupExtension> securityGroupExtension;
 
    @Inject
    protected BaseComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
@@ -158,7 +160,7 @@ public class BaseComputeService implements ComputeService {
             InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess,
             RunScriptOnNode.Factory runScriptOnNodeFactory, PersistNodeCredentials persistNodeCredentials,
             Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
-            Optional<ImageExtension> imageExtension) {
+            Optional<ImageExtension> imageExtension, Optional<SecurityGroupExtension> securityGroupExtension) {
       this.context = checkNotNull(context, "context");
       this.credentialStore = checkNotNull(credentialStore, "credentialStore");
       this.images = checkNotNull(images, "images");
@@ -184,6 +186,7 @@ public class BaseComputeService implements ComputeService {
       this.persistNodeCredentials = checkNotNull(persistNodeCredentials, "persistNodeCredentials");
       this.userExecutor = checkNotNull(userExecutor, "userExecutor");
       this.imageExtension = checkNotNull(imageExtension, "imageExtension");
+      this.securityGroupExtension = checkNotNull(securityGroupExtension, "securityGroupExtension");
    }
 
    /**
@@ -714,4 +717,11 @@ public class BaseComputeService implements ComputeService {
       return imageExtension;
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Optional<SecurityGroupExtension> getSecurityGroupExtension() {
+      return securityGroupExtension;
+   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java
index 270633a..d17a6cd 100644
--- a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java
+++ b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java
@@ -70,6 +70,8 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
          to.inboundPorts(this.getInboundPorts());
       if (this.getRunScript() != null)
          to.runScript(this.getRunScript());
+      if (this.getGroups().size() > 0)
+         to.securityGroups(this.getGroups());
       if (this.getPrivateKey() != null)
          to.installPrivateKey(this.getPrivateKey());
       if (this.getPublicKey() != null)
@@ -295,6 +297,21 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
       }
 
       @Override
+      public Set<String> getGroups() {
+         return delegate.getGroups();
+      }
+
+      @Override
+      public TemplateOptions securityGroups(Iterable<String> securityGroups) {
+         throw new IllegalArgumentException("tags are immutable");
+      }
+
+      @Override
+      public TemplateOptions securityGroups(String... securityGroups) {
+         throw new IllegalArgumentException("tags are immutable");
+      }
+
+      @Override
       public TemplateOptions userMetadata(Map<String, String> userMetadata) {
          throw new IllegalArgumentException("userMetadata is immutable");
       }
@@ -321,6 +338,8 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
 
    protected Set<String> tags = ImmutableSet.of();
 
+   protected Set<String> securityGroups = ImmutableSet.of();
+
    protected String privateKey;
 
    protected String publicKey;
@@ -339,13 +358,13 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
       return super.equals(that) && equal(this.inboundPorts, that.inboundPorts) && equal(this.script, that.script)
             && equal(this.publicKey, that.publicKey) && equal(this.privateKey, that.privateKey)
             && equal(this.blockUntilRunning, that.blockUntilRunning) && equal(this.tags, that.tags)
-            && equal(this.userMetadata, that.userMetadata);
+            && equal(this.securityGroups, that.securityGroups) && equal(this.userMetadata, that.userMetadata);
    }
 
    @Override
    public int hashCode() {
       return Objects.hashCode(super.hashCode(), inboundPorts, script, publicKey, privateKey, blockUntilRunning, tags,
-            userMetadata);
+                              securityGroups, userMetadata);
    }
 
    @Override
@@ -363,6 +382,8 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
          toString.add("blockUntilRunning", blockUntilRunning);
       if (tags.size() != 0)
          toString.add("tags", tags);
+      if (securityGroups.size() != 0)
+         toString.add("securityGroups", securityGroups);
       if (userMetadata.size() != 0)
          toString.add("userMetadata", userMetadata);
       return toString;
@@ -380,6 +401,10 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
       return tags;
    }
 
+   public Set<String> getGroups() {
+      return securityGroups;
+   }
+
    public String getPrivateKey() {
       return privateKey;
    }
@@ -451,6 +476,21 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
    }
 
    /**
+    * assigns the created nodes to these security groups
+    */
+   public TemplateOptions securityGroups(Iterable<String> securityGroups) {
+      this.securityGroups = ImmutableSet.copyOf(checkNotNull(securityGroups, "securityGroups"));
+      return this;
+   }
+
+   /**
+    * @see TemplateOptions#securityGroups(Iterable<String>)
+    */
+   public TemplateOptions securityGroups(String... securityGroups) {
+      return securityGroups(ImmutableSet.copyOf(securityGroups));
+   }
+
+   /**
     * Opens the set of ports to public access.
     */
    public TemplateOptions inboundPorts(int... ports) {
@@ -522,6 +562,22 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
       }
 
       /**
+       * @see TemplateOptions#securityGroups
+       */
+      public static TemplateOptions securityGroups(Iterable<String> securityGroups) {
+         TemplateOptions options = new TemplateOptions();
+         return options.securityGroups(securityGroups);
+      }
+
+      /**
+       * @see TemplateOptions#securityGroups
+       */
+      public static TemplateOptions securityGroups(String... securityGroups) {
+         TemplateOptions options = new TemplateOptions();
+         return options.securityGroups(securityGroups);
+      }
+
+      /**
        * @see TemplateOptions#blockUntilRunning(boolean)
        */
       public static TemplateOptions blockUntilRunning(boolean blockUntilRunning) {

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceAdapter.java b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceAdapter.java
index 925dc1a..22e7dcf 100644
--- a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceAdapter.java
+++ b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceAdapter.java
@@ -41,13 +41,18 @@ import org.jclouds.compute.domain.NodeMetadata.Status;
 import org.jclouds.compute.domain.NodeMetadataBuilder;
 import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
 import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.predicates.ImagePredicates;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.location.suppliers.all.JustProvider;
 import org.jclouds.rest.ResourceNotFoundException;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
@@ -55,6 +60,7 @@ import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.ListeningExecutorService;
 
@@ -66,20 +72,25 @@ import com.google.common.util.concurrent.ListeningExecutorService;
 public class StubComputeServiceAdapter implements JCloudsNativeComputeServiceAdapter {
    private final Supplier<Location> location;
    private final ConcurrentMap<String, NodeMetadata> nodes;
+   private final Multimap<String, SecurityGroup> groupsForNodes;
    private final ListeningExecutorService ioExecutor;
    private final Provider<Integer> idProvider;
+   private final Provider<Integer> groupIdProvider;
    private final String publicIpPrefix;
    private final String privateIpPrefix;
    private final String passwordPrefix;
    private final Supplier<Set<? extends Location>> locationSupplier;
    private final Map<OsFamily, Map<String, String>> osToVersionMap;
+   private final Optional<SecurityGroupExtension> securityGroupExtension;
 
    @Inject
    public StubComputeServiceAdapter(ConcurrentMap<String, NodeMetadata> nodes,
             @Named(Constants.PROPERTY_IO_WORKER_THREADS) ListeningExecutorService ioExecutor, Supplier<Location> location,
             @Named("NODE_ID") Provider<Integer> idProvider, @Named("PUBLIC_IP_PREFIX") String publicIpPrefix,
             @Named("PRIVATE_IP_PREFIX") String privateIpPrefix, @Named("PASSWORD_PREFIX") String passwordPrefix,
-            JustProvider locationSupplier, Map<OsFamily, Map<String, String>> osToVersionMap) {
+            JustProvider locationSupplier, Map<OsFamily, Map<String, String>> osToVersionMap,
+            Multimap<String, SecurityGroup> groupsForNodes, @Named("GROUP_ID") Provider<Integer> groupIdProvider,
+            Optional<SecurityGroupExtension> securityGroupExtension) {
       this.nodes = nodes;
       this.ioExecutor = ioExecutor;
       this.location = location;
@@ -89,6 +100,9 @@ public class StubComputeServiceAdapter implements JCloudsNativeComputeServiceAda
       this.passwordPrefix = passwordPrefix;
       this.locationSupplier = locationSupplier;
       this.osToVersionMap = osToVersionMap;
+      this.groupsForNodes = groupsForNodes;
+      this.groupIdProvider = groupIdProvider;
+      this.securityGroupExtension = securityGroupExtension;
    }
 
    protected void setStateOnNode(Status status, NodeMetadata node) {
@@ -133,6 +147,22 @@ public class StubComputeServiceAdapter implements JCloudsNativeComputeServiceAda
       builder.credentials(LoginCredentials.builder().user("root").password(passwordPrefix + id).build());
       NodeMetadata node = builder.build();
       nodes.put(node.getId(), node);
+
+      if (template.getOptions().getGroups().size() > 0) {
+         final String groupId = Iterables.getFirst(template.getOptions().getGroups(), "0");
+         Optional<SecurityGroup> secGroup = Iterables.tryFind(securityGroupExtension.get().listSecurityGroups(),
+                                                              new Predicate<SecurityGroup>() {
+                                                                 @Override
+                                                                 public boolean apply(SecurityGroup input) {
+                                                                    return input.getId().equals(groupId);
+                                                                 }
+                                                              });
+
+         if (secGroup.isPresent()) {
+            groupsForNodes.put(node.getId(), secGroup.get());
+         }
+      }
+      
       setStateOnNodeAfterDelay(Status.RUNNING, node, 100);
       return new NodeWithInitialCredentials(node);
    }
@@ -195,6 +225,8 @@ public class StubComputeServiceAdapter implements JCloudsNativeComputeServiceAda
          return;
       setStateOnNodeAfterDelay(Status.PENDING, node, 0);
       setStateOnNodeAfterDelay(Status.TERMINATED, node, 50);
+      groupsForNodes.removeAll(id);
+      
       ioExecutor.execute(new Runnable() {
 
          @Override

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java
index 91e858a..ab381b3 100644
--- a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java
+++ b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java
@@ -17,8 +17,12 @@
 package org.jclouds.compute.stub.config;
 
 import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.concurrent.SingleThreaded;
 
+import com.google.common.base.Optional;
+import com.google.inject.Injector;
+
 /**
  * 
  * @author Adrian Cole
@@ -35,4 +39,10 @@ public class StubComputeServiceContextModule extends JCloudsNativeComputeService
       install(new StubComputeServiceDependenciesModule());
       super.configure();
    }
+
+   @Override
+   protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
+      return Optional.of(i.getInstance(SecurityGroupExtension.class));
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceDependenciesModule.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceDependenciesModule.java b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceDependenciesModule.java
index de28ee1..0d07bbe 100644
--- a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceDependenciesModule.java
+++ b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceDependenciesModule.java
@@ -29,8 +29,11 @@ import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeMetadata.Status;
 import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.SecurityGroup;
 import org.jclouds.compute.domain.Volume;
 import org.jclouds.compute.domain.internal.VolumeImpl;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.stub.extensions.StubSecurityGroupExtension;
 import org.jclouds.domain.Credentials;
 import org.jclouds.location.Provider;
 import org.jclouds.predicates.SocketOpen;
@@ -40,9 +43,12 @@ import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
 import com.google.common.net.HostAndPort;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
+import com.google.inject.TypeLiteral;
 
 /**
  * 
@@ -52,6 +58,8 @@ public class StubComputeServiceDependenciesModule extends AbstractModule {
 
    @Override
    protected void configure() {
+      bind(new TypeLiteral<SecurityGroupExtension>() {
+      }).to(StubSecurityGroupExtension.class);
 
    }
 
@@ -73,6 +81,40 @@ public class StubComputeServiceDependenciesModule extends AbstractModule {
       return backing.get(creds.get().identity);
    }
 
+   protected static final LoadingCache<String, ConcurrentMap<String, SecurityGroup>> groupBacking = CacheBuilder.newBuilder()
+            .build(new CacheLoader<String, ConcurrentMap<String, SecurityGroup>>() {
+
+               @Override
+               public ConcurrentMap<String, SecurityGroup> load(String arg0) throws Exception {
+                  return new ConcurrentHashMap<String, SecurityGroup>();
+               }
+
+            });
+
+   @Provides
+   @Singleton
+   protected ConcurrentMap<String, SecurityGroup> provideGroups(@Provider Supplier<Credentials> creds)
+            throws ExecutionException {
+      return groupBacking.get(creds.get().identity);
+   }
+
+   protected static final LoadingCache<String, Multimap<String, SecurityGroup>> groupsForNodeBacking = CacheBuilder.newBuilder()
+            .build(new CacheLoader<String, Multimap<String, SecurityGroup>>() {
+
+               @Override
+               public Multimap<String, SecurityGroup> load(String arg0) throws Exception {
+                  return LinkedHashMultimap.create();
+               }
+
+            });
+
+   @Provides
+   @Singleton
+   protected Multimap<String, SecurityGroup> provideGroupsForNode(@Provider Supplier<Credentials> creds)
+            throws ExecutionException {
+      return groupsForNodeBacking.get(creds.get().identity);
+   }
+
    protected static final LoadingCache<String, AtomicInteger> nodeIds = CacheBuilder.newBuilder().build(
             new CacheLoader<String, AtomicInteger>() {
 
@@ -89,6 +131,22 @@ public class StubComputeServiceDependenciesModule extends AbstractModule {
       return nodeIds.get(creds.get().identity).incrementAndGet();
    }
 
+   protected static final LoadingCache<String, AtomicInteger> groupIds = CacheBuilder.newBuilder().build(
+            new CacheLoader<String, AtomicInteger>() {
+
+               @Override
+               public AtomicInteger load(String arg0) throws Exception {
+                  return new AtomicInteger(0);
+               }
+
+            });
+
+   @Provides
+   @Named("GROUP_ID")
+   protected Integer provideGroupIdForIdentity(@Provider Supplier<Credentials> creds) throws ExecutionException {
+      return groupIds.get(creds.get().identity).incrementAndGet();
+   }
+
    @Singleton
    @Provides
    @Named("PUBLIC_IP_PREFIX")

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtension.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtension.java b/compute/src/main/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtension.java
new file mode 100644
index 0000000..5e8fdd9
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtension.java
@@ -0,0 +1,256 @@
+/*
+ * 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.compute.stub.extensions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.collect.Iterables.filter;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+import org.jclouds.Constants;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.domain.Location;
+import org.jclouds.location.suppliers.all.JustProvider;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+ * An extension to compute service to allow for the manipulation of {@link SecurityGroup}s. Implementation
+ * is optional by providers.
+ * 
+ * @author Andrew Bayer
+ */
+public class StubSecurityGroupExtension implements SecurityGroupExtension {
+
+   private final Supplier<Location> location;
+   private final Provider<Integer> groupIdProvider;
+   private final Supplier<Set<? extends Location>> locationSupplier;
+   private final ListeningExecutorService ioExecutor;
+   private final ConcurrentMap<String, SecurityGroup> groups;
+   private final Multimap<String, SecurityGroup> groupsForNodes;
+
+   @Inject
+   public StubSecurityGroupExtension(ConcurrentMap<String, SecurityGroup> groups,
+                                     @Named(Constants.PROPERTY_IO_WORKER_THREADS) ListeningExecutorService ioExecutor,
+                                     Supplier<Location> location,
+                                     @Named("GROUP_ID") Provider<Integer> groupIdProvider, 
+                                     JustProvider locationSupplier,
+                                     Multimap<String, SecurityGroup> groupsForNodes) {
+      this.groups = groups;
+      this.ioExecutor = ioExecutor;
+      this.location = location;
+      this.groupIdProvider = groupIdProvider;
+      this.locationSupplier = locationSupplier;
+      this.groupsForNodes = groupsForNodes;
+   }
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroups() {
+      return ImmutableSet.copyOf(groups.values());
+   }
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroupsInLocation(final Location location) {
+      return ImmutableSet.copyOf(filter(groups.values(), new Predicate<SecurityGroup>() {
+               @Override
+               public boolean apply(SecurityGroup group) {
+                  return group.getLocation().equals(location);
+               }
+            }));
+   }
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroupsForNode(String nodeId) {
+      return ImmutableSet.copyOf(groupsForNodes.get(nodeId));
+   }
+
+   @Override
+   public SecurityGroup getSecurityGroupById(String id) {
+      return groups.get(id);
+   }
+   
+   @Override
+   public SecurityGroup createSecurityGroup(String name, Location location) {
+      SecurityGroupBuilder builder = new SecurityGroupBuilder();
+
+      String id = groupIdProvider.get() + "";
+      builder.ids(id);
+      builder.name(name);
+      builder.location(location);
+
+      SecurityGroup group = builder.build();
+
+      groups.put(group.getId(), group);
+
+      return group;
+   }
+
+   @Override
+   public boolean removeSecurityGroup(String id) {
+      if (groups.containsKey(id)) {
+         groups.remove(id);
+         return true;
+      }
+      return false;
+   }
+
+   @Override
+   public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) {
+      SecurityGroupBuilder builder = SecurityGroupBuilder.fromSecurityGroup(checkNotNull(group, "group"));
+
+      builder.ipPermission(checkNotNull(ipPermission, "ipPermission"));
+
+      SecurityGroup newGroup = builder.build();
+
+      if (groups.containsKey(newGroup.getId())) {
+         groups.remove(newGroup.getId());
+      }
+
+      groups.put(newGroup.getId(), newGroup);
+
+      return newGroup;
+   }
+
+   @Override
+   public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                 Multimap<String, String> tenantIdGroupNamePairs,
+                                 Iterable<String> ipRanges,
+                                 Iterable<String> groupIds, SecurityGroup group) {
+      IpPermission.Builder ipBuilder = IpPermission.builder();
+
+      ipBuilder.ipProtocol(protocol);
+      ipBuilder.fromPort(startPort);
+      ipBuilder.toPort(endPort);
+      if (tenantIdGroupNamePairs.size() > 0) {
+         ipBuilder.tenantIdGroupNamePairs(tenantIdGroupNamePairs);
+      }
+      if (Iterables.size(ipRanges) > 0) {
+         ipBuilder.cidrBlocks(ipRanges);
+      }
+      if (Iterables.size(groupIds) > 0) {
+         ipBuilder.groupIds(groupIds);
+      }
+
+      IpPermission perm = ipBuilder.build();
+
+      SecurityGroupBuilder builder = SecurityGroupBuilder.fromSecurityGroup(checkNotNull(group, "group"));
+
+      builder.ipPermission(perm);
+
+      SecurityGroup newGroup = builder.build();
+
+      if (groups.containsKey(newGroup.getId())) {
+         groups.remove(newGroup.getId());
+      }
+
+      groups.put(newGroup.getId(), newGroup);
+
+      return newGroup;
+   }
+      
+   @Override
+   public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) {
+      SecurityGroupBuilder builder = SecurityGroupBuilder.fromSecurityGroup(checkNotNull(group, "group"));
+
+      builder.ipPermissions();
+
+      builder.ipPermissions(filter(group.getIpPermissions(), not(equalTo(ipPermission))));
+                            
+      SecurityGroup newGroup = builder.build();
+
+      if (groups.containsKey(newGroup.getId())) {
+         groups.remove(newGroup.getId());
+      }
+
+      groups.put(newGroup.getId(), newGroup);
+
+      return newGroup;
+   }
+   
+
+   @Override
+   public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                    Multimap<String, String> tenantIdGroupNamePairs,
+                                    Iterable<String> ipRanges,
+                                    Iterable<String> groupIds, SecurityGroup group) {
+      IpPermission.Builder ipBuilder = IpPermission.builder();
+
+      ipBuilder.ipProtocol(protocol);
+      ipBuilder.fromPort(startPort);
+      ipBuilder.toPort(endPort);
+      if (tenantIdGroupNamePairs.size() > 0) {
+         ipBuilder.tenantIdGroupNamePairs(tenantIdGroupNamePairs);
+      }
+      if (Iterables.size(ipRanges) > 0) {
+         ipBuilder.cidrBlocks(ipRanges);
+      }
+      if (Iterables.size(groupIds) > 0) {
+         ipBuilder.groupIds(groupIds);
+      }
+
+      IpPermission perm = ipBuilder.build();
+
+      SecurityGroupBuilder builder = SecurityGroupBuilder.fromSecurityGroup(checkNotNull(group, "group"));
+
+      builder.ipPermissions();
+
+      builder.ipPermissions(filter(group.getIpPermissions(), not(equalTo(perm))));
+      
+      SecurityGroup newGroup = builder.build();
+
+      if (groups.containsKey(newGroup.getId())) {
+         groups.remove(newGroup.getId());
+      }
+
+      groups.put(newGroup.getId(), newGroup);
+
+      return newGroup;
+   }
+
+   @Override
+   public boolean supportsTenantIdGroupNamePairs() {
+      return false;
+   }
+
+   @Override
+   public boolean supportsGroupIds() {
+      return true;
+   }
+
+   @Override
+   public boolean supportsPortRangesForGroups() {
+      return true;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/net/domain/IpPermission.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/net/domain/IpPermission.java b/compute/src/main/java/org/jclouds/net/domain/IpPermission.java
index 895d9d3..d04af0c 100644
--- a/compute/src/main/java/org/jclouds/net/domain/IpPermission.java
+++ b/compute/src/main/java/org/jclouds/net/domain/IpPermission.java
@@ -17,11 +17,15 @@
 package org.jclouds.net.domain;
 
 import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.transform;
+import static org.jclouds.util.Strings2.isCidrFormat;
 
 import java.util.Set;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableMultimap;
@@ -100,6 +104,7 @@ public class IpPermission implements Comparable<IpPermission> {
        * @see IpPermission#getCidrBlocks()
        */
       public Builder cidrBlock(String cidrBlock) {
+         checkArgument(isCidrFormat(cidrBlock), "cidrBlock %s is not a valid CIDR", cidrBlock);
          this.cidrBlocks.add(cidrBlock);
          return this;
       }
@@ -108,7 +113,16 @@ public class IpPermission implements Comparable<IpPermission> {
        * @see IpPermission#getCidrBlocks()
        */
       public Builder cidrBlocks(Iterable<String> cidrBlocks) {
-         Iterables.addAll(this.cidrBlocks, cidrBlocks);
+         Iterables.addAll(this.cidrBlocks, transform(cidrBlocks,
+                                                     new Function<String, String>() {
+                                                        @Override
+                                                        public String apply(String input) {
+                                                           checkArgument(isCidrFormat(input),
+                                                                         "input %s is not a valid CIDR",
+                                                                         input);
+                                                           return input;
+                                                        }
+                                                     }));
          return this;
       }
 
@@ -140,7 +154,7 @@ public class IpPermission implements Comparable<IpPermission> {
    private final IpProtocol ipProtocol;
    private final Set<String> cidrBlocks;
 
-   protected IpPermission(IpProtocol ipProtocol, int fromPort, int toPort,
+   public IpPermission(IpProtocol ipProtocol, int fromPort, int toPort,
             Multimap<String, String> tenantIdGroupNamePairs, Iterable<String> groupIds, Iterable<String> cidrBlocks) {
       this.fromPort = fromPort;
       this.toPort = toPort;
@@ -227,8 +241,8 @@ public class IpPermission implements Comparable<IpPermission> {
 
    protected ToStringHelper string() {
       return Objects.toStringHelper("").add("ipProtocol", ipProtocol).add("fromPort", fromPort).add("toPort", toPort)
-               .add("tenantIdGroupNamePairs", tenantIdGroupNamePairs).add("groupIds", groupIds).add("groupIds",
-                        groupIds);
+               .add("tenantIdGroupNamePairs", tenantIdGroupNamePairs).add("groupIds", groupIds).add("cidrBlocks",
+                        cidrBlocks);
    }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/main/java/org/jclouds/net/domain/IpProtocol.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/net/domain/IpProtocol.java b/compute/src/main/java/org/jclouds/net/domain/IpProtocol.java
index 50d4422..203f0f9 100644
--- a/compute/src/main/java/org/jclouds/net/domain/IpProtocol.java
+++ b/compute/src/main/java/org/jclouds/net/domain/IpProtocol.java
@@ -29,12 +29,22 @@ public enum IpProtocol {
 
    TCP, UDP, ICMP, ALL, UNRECOGNIZED;
 
+   public String value() {
+      return this == ALL ? "-1" : name().toLowerCase();
+   }
+
+   @Override
+   public String toString() {
+      return value();
+   }
+
    public static IpProtocol fromValue(String protocol) {
       try {
+         if (protocol.equalsIgnoreCase("-1"))
+            return ALL;
          return valueOf(checkNotNull(protocol, "protocol").toUpperCase());
       } catch (IllegalArgumentException e) {
          return UNRECOGNIZED;
       }
    }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseSecurityGroupExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseSecurityGroupExtensionLiveTest.java b/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseSecurityGroupExtensionLiveTest.java
new file mode 100644
index 0000000..1a5c646
--- /dev/null
+++ b/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseSecurityGroupExtensionLiveTest.java
@@ -0,0 +1,348 @@
+/*
+ * 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.compute.extensions.internal;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.collect.Iterables.filter;
+import static org.jclouds.compute.predicates.NodePredicates.inGroup;
+import static org.jclouds.util.Predicates2.retry;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
+import org.jclouds.compute.options.TemplateOptions;
+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.net.domain.IpProtocol;
+import org.jclouds.ssh.SshClient;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+
+
+/**
+ * Base test for {@link SecurityGroupExtension} implementations.
+ * 
+ * @author David Alves
+ * 
+ */
+public abstract class BaseSecurityGroupExtensionLiveTest extends BaseComputeServiceContextLiveTest {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   protected String groupId;
+
+   /**
+    * Returns the template for the base node, override to test different templates.
+    * 
+    * @return
+    */
+   public Template getNodeTemplate() {
+      return view.getComputeService().templateBuilder().build();
+   }
+
+      
+
+   @Test(groups = { "integration", "live" }, singleThreaded = true)
+   public void testCreateSecurityGroup() throws RunNodesException, InterruptedException, ExecutionException {
+
+      ComputeService computeService = view.getComputeService();
+
+      Location location = getNodeTemplate().getLocation();
+      
+      Optional<SecurityGroupExtension> securityGroupExtension = computeService.getSecurityGroupExtension();
+
+      assertTrue(securityGroupExtension.isPresent(), "security extension was not present");
+
+      SecurityGroup group = securityGroupExtension.get().createSecurityGroup("test-create-security-group", location);
+
+      logger.info("Group created: %s", group);
+
+      assertTrue(group.getName().contains("test-create-security-group"));
+
+      groupId = group.getId();
+   }
+
+   @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testCreateSecurityGroup")
+   public void testGetSecurityGroupById() throws RunNodesException, InterruptedException, ExecutionException {
+
+      ComputeService computeService = view.getComputeService();
+
+      Location location = getNodeTemplate().getLocation();
+      
+      Optional<SecurityGroupExtension> securityGroupExtension = computeService.getSecurityGroupExtension();
+
+      assertTrue(securityGroupExtension.isPresent(), "security extension was not present");
+
+      SecurityGroup group = securityGroupExtension.get().getSecurityGroupById(groupId);
+
+      logger.info("Group found: %s", group);
+
+      assertTrue(group.getName().contains("test-create-security-group"));
+   }
+
+   @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testGetSecurityGroupById")
+   public void testAddIpPermission() {
+
+      ComputeService computeService = view.getComputeService();
+
+      Optional<SecurityGroupExtension> securityGroupExtension = computeService.getSecurityGroupExtension();
+      assertTrue(securityGroupExtension.isPresent(), "security group extension was not present");
+
+      Optional<SecurityGroup> optGroup = getGroup(securityGroupExtension.get());
+
+      assertTrue(optGroup.isPresent());
+
+      SecurityGroup group = optGroup.get();
+
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(IpProtocol.TCP);
+      builder.fromPort(10);
+      builder.toPort(20);
+      builder.cidrBlock("0.0.0.0/0");
+
+      IpPermission perm = builder.build();
+
+      SecurityGroup newGroup = securityGroupExtension.get().addIpPermission(perm, group);
+
+      assertEquals(perm, Iterables.getOnlyElement(newGroup.getIpPermissions())); 
+   }
+   
+   @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testAddIpPermission")
+   public void testRemoveIpPermission() {
+
+      ComputeService computeService = view.getComputeService();
+
+      Optional<SecurityGroupExtension> securityGroupExtension = computeService.getSecurityGroupExtension();
+      assertTrue(securityGroupExtension.isPresent(), "security group extension was not present");
+
+      Optional<SecurityGroup> optGroup = getGroup(securityGroupExtension.get());
+
+      assertTrue(optGroup.isPresent());
+
+      SecurityGroup group = optGroup.get();
+
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(IpProtocol.TCP);
+      builder.fromPort(10);
+      builder.toPort(20);
+      builder.cidrBlock("0.0.0.0/0");
+
+      IpPermission perm = builder.build();
+
+      SecurityGroup newGroup = securityGroupExtension.get().removeIpPermission(perm, group);
+
+      assertEquals(0, Iterables.size(newGroup.getIpPermissions())); 
+   }
+
+   @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testRemoveIpPermission")
+   public void testAddIpPermissionsFromSpec() {
+
+      ComputeService computeService = view.getComputeService();
+
+      Optional<SecurityGroupExtension> securityGroupExtension = computeService.getSecurityGroupExtension();
+      assertTrue(securityGroupExtension.isPresent(), "security group extension was not present");
+
+      Optional<SecurityGroup> optGroup = getGroup(securityGroupExtension.get());
+
+      assertTrue(optGroup.isPresent());
+
+      SecurityGroup group = optGroup.get();
+
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(IpProtocol.TCP);
+      builder.fromPort(50);
+      builder.toPort(60);
+      builder.cidrBlock("0.0.0.0/0");
+
+      IpPermission perm = builder.build();
+
+      SecurityGroup newGroup = securityGroupExtension.get().addIpPermission(IpProtocol.TCP,
+                                                                            50,
+                                                                            60,
+                                                                            emptyMultimap(),
+                                                                            ImmutableSet.of("0.0.0.0/0"),
+                                                                            emptyStringSet(),
+                                                                            group);
+
+      assertTrue(newGroup.getIpPermissions().contains(perm)); 
+
+      if (securityGroupExtension.get().supportsGroupIds()) {
+         IpPermission.Builder secondBuilder = IpPermission.builder();
+         
+         int fromPort;
+         int toPort;
+
+         if (securityGroupExtension.get().supportsPortRangesForGroups()) {
+            fromPort = 70;
+            toPort = 80;
+         } else {
+            fromPort = 1;
+            toPort = 65535;
+         }
+         secondBuilder.ipProtocol(IpProtocol.TCP);
+         secondBuilder.fromPort(fromPort);
+         secondBuilder.toPort(toPort);
+         secondBuilder.groupId(group.getId());
+         
+         IpPermission secondPerm = secondBuilder.build();
+         
+         SecurityGroup secondNewGroup = securityGroupExtension.get().addIpPermission(IpProtocol.TCP,
+                                                                                     fromPort,
+                                                                                     toPort,
+                                                                                     emptyMultimap(),
+                                                                                     emptyStringSet(),
+                                                                                     ImmutableSet.of(group.getId()),
+                                                                                     newGroup);
+         
+         assertTrue(secondNewGroup.getIpPermissions().contains(secondPerm)); 
+      }
+
+      if (securityGroupExtension.get().supportsTenantIdGroupNamePairs()) {
+         IpPermission.Builder thirdBuilder = IpPermission.builder();
+
+         int fromPort;
+         int toPort;
+
+         if (securityGroupExtension.get().supportsPortRangesForGroups()) {
+            fromPort = 90;
+            toPort = 100;
+         } else {
+            fromPort = 1;
+            toPort = 65535;
+         }
+         thirdBuilder.ipProtocol(IpProtocol.TCP);
+         thirdBuilder.fromPort(fromPort);
+         thirdBuilder.toPort(toPort);
+         thirdBuilder.tenantIdGroupNamePair(group.getOwnerId(), group.getId());
+         
+         IpPermission thirdPerm = thirdBuilder.build();
+
+         SecurityGroup thirdNewGroup = securityGroupExtension.get().addIpPermission(IpProtocol.TCP,
+                                                                                    fromPort,
+                                                                                    toPort,
+                                                                                    thirdPerm.getTenantIdGroupNamePairs(),
+                                                                                    emptyStringSet(),
+                                                                                    emptyStringSet(),
+                                                                                    newGroup);
+
+         assertTrue(thirdNewGroup.getIpPermissions().contains(thirdPerm)); 
+      }
+   }
+   
+   @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testAddIpPermissionsFromSpec")
+   public void testCreateNodeWithSecurityGroup() throws RunNodesException, InterruptedException, ExecutionException {
+
+      ComputeService computeService = view.getComputeService();
+
+      Optional<SecurityGroupExtension> securityGroupExtension = computeService.getSecurityGroupExtension();
+
+      assertTrue(securityGroupExtension.isPresent(), "security group extension was not present");
+
+      Template template = view.getComputeService().templateBuilder()
+         .options(TemplateOptions.Builder.securityGroups(groupId))
+         .build();
+      
+      NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test-create-node-with-group", 1, template));
+
+      Set<SecurityGroup> groups = securityGroupExtension.get().listSecurityGroupsForNode(node.getId());
+
+      assertTrue(groups.size() > 0, "node has no groups");
+      
+      Optional<SecurityGroup> secGroup = Iterables.tryFind(securityGroupExtension.get().listSecurityGroupsForNode(node.getId()),
+                                                           new Predicate<SecurityGroup>() {
+                                                              @Override
+                                                              public boolean apply(SecurityGroup input) {
+                                                                 return input.getId().equals(groupId);
+                                                              }
+                                                           });
+
+      assertTrue(secGroup.isPresent());
+
+      computeService.destroyNodesMatching(inGroup(node.getGroup()));
+
+      
+   }
+
+   // testDeleteSecurityGroup currently disabled until I can find a way to get it to delete the security group while a terminated
+   // instance is still floating around in EC2. - abayer, 6/14/13
+   /*
+   @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testCreateNodeWithSecurityGroup")
+   public void testDeleteSecurityGroup() {
+
+      ComputeService computeService = view.getComputeService();
+
+      Optional<SecurityGroupExtension> securityGroupExtension = computeService.getSecurityGroupExtension();
+      assertTrue(securityGroupExtension.isPresent(), "security group extension was not present");
+
+      Optional<SecurityGroup> optGroup = getGroup(securityGroupExtension.get());
+
+      assertTrue(optGroup.isPresent());
+
+      SecurityGroup group = optGroup.get();
+
+      assertTrue(securityGroupExtension.get().removeSecurityGroup(group.getId()));
+   }
+   */
+   
+   private Multimap<String, String> emptyMultimap() {
+      return LinkedHashMultimap.create();
+   }
+
+   private Set<String> emptyStringSet() {
+      return Sets.newLinkedHashSet();
+   }
+   
+   private Optional<SecurityGroup> getGroup(SecurityGroupExtension ext) {
+      return Iterables.tryFind(ext.listSecurityGroups(), new Predicate<SecurityGroup>() {
+         @Override
+         public boolean apply(SecurityGroup input) {
+            return input.getId().equals(groupId);
+         }
+      });
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/test/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtensionIntegrationTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtensionIntegrationTest.java b/compute/src/test/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtensionIntegrationTest.java
new file mode 100644
index 0000000..8c2b31c
--- /dev/null
+++ b/compute/src/test/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtensionIntegrationTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.compute.extensions.internal;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.util.Predicates2.retry;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
+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.ssh.SshClient;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+
+/**
+ * Base test for {@link SecurityGroupExtension} implementations.
+ * 
+ * @author Andrew Bayer
+ * 
+ */
+@Test(groups = { "integration", "live" }, singleThreaded = true, testName="StubSecurityGroupExtensionIntegrationTest")
+public class StubSecurityGroupExtensionIntegrationTest extends BaseSecurityGroupExtensionLiveTest {
+
+   public StubSecurityGroupExtensionIntegrationTest() {
+      provider = "stub";
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties overrides = super.setupProperties();
+      // This is a hack to make sure we get a different set of node IDs, nodes, groups, etc from StubComputeServiceIntegrationTest.
+      overrides.setProperty(provider + ".identity", "sec-stub");
+
+      return overrides;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/compute/src/test/java/org/jclouds/net/util/IpPermissionsTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/net/util/IpPermissionsTest.java b/compute/src/test/java/org/jclouds/net/util/IpPermissionsTest.java
index 2c278f6..30f5453 100644
--- a/compute/src/test/java/org/jclouds/net/util/IpPermissionsTest.java
+++ b/compute/src/test/java/org/jclouds/net/util/IpPermissionsTest.java
@@ -37,6 +37,20 @@ public class IpPermissionsTest {
                .cidrBlock("0.0.0.0/0").build());
    }
 
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testAllProtocolInvalidCidr() {
+      IpPermissions authorization = IpPermissions.permitAnyProtocol();
+      assertEquals(authorization, IpPermission.builder().ipProtocol(IpProtocol.ALL).fromPort(1).toPort(65535)
+               .cidrBlock("a.0.0.0/0").build());
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testAllProtocolInvalidCidrMultiple() {
+      IpPermissions authorization = IpPermissions.permitAnyProtocol();
+      assertEquals(authorization, IpPermission.builder().ipProtocol(IpProtocol.ALL).fromPort(1).toPort(65535)
+                   .cidrBlocks(ImmutableSet.of("a.0.0.0/0", "0.0.0.0/0")).build());
+   }
+
    public void testAllProtocolCidrBound() {
       IpPermissions authorization = IpPermissions.permit(IpProtocol.ALL).originatingFromCidrBlock("1.1.1.1/32");
       assertEquals(authorization, IpPermission.builder().ipProtocol(IpProtocol.ALL).fromPort(1).toPort(65535)

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/core/src/main/java/org/jclouds/util/Strings2.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/util/Strings2.java b/core/src/main/java/org/jclouds/util/Strings2.java
index 8b85b53..2a47286 100644
--- a/core/src/main/java/org/jclouds/util/Strings2.java
+++ b/core/src/main/java/org/jclouds/util/Strings2.java
@@ -87,7 +87,16 @@ public class Strings2 {
                }
             }
          });
+   
+   private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})";
+   private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,3})";
+   private static final Pattern ADDRESS_PATTERN = Pattern.compile(IP_ADDRESS);
+   private static final Pattern CIDR_PATTERN = Pattern.compile(SLASH_FORMAT);
 
+   public static boolean isCidrFormat(String in) {
+      return CIDR_PATTERN.matcher(in).matches();
+   }
+      
    private static final Pattern URL_ENCODED_PATTERN = Pattern.compile(".*%[a-fA-F0-9][a-fA-F0-9].*");
 
    public static boolean isUrlEncoded(String in) {

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/core/src/test/java/org/jclouds/util/Strings2Test.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/util/Strings2Test.java b/core/src/test/java/org/jclouds/util/Strings2Test.java
index d7e3cd5..5b1c9a0 100644
--- a/core/src/test/java/org/jclouds/util/Strings2Test.java
+++ b/core/src/test/java/org/jclouds/util/Strings2Test.java
@@ -53,4 +53,12 @@ public class Strings2Test {
       assertEquals(actual, urlDecode(urlEncode(actual)));
    }
 
+   public void testIsCidrFormat() {
+      assert Strings2.isCidrFormat("1.2.3.4/5");
+      assert Strings2.isCidrFormat("0.0.0.0/0");
+      assert !Strings2.isCidrFormat("banana");
+      assert !Strings2.isCidrFormat("1.2.3.4");
+      assert !Strings2.isCidrFormat("500.500.500.500/2423");
+   }
+      
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java
index d9a15f3..8bcc4d5 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java
@@ -46,6 +46,7 @@ import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.functions.GroupNamingConvention;
 import org.jclouds.compute.internal.PersistNodeCredentials;
 import org.jclouds.compute.options.TemplateOptions;
@@ -104,13 +105,14 @@ public class AWSEC2ComputeService extends EC2ComputeService {
          @Named("PLACEMENT") LoadingCache<RegionAndName, String> placementGroupMap,
          @Named("DELETED") Predicate<PlacementGroup> placementGroupDeleted, Optional<ImageExtension> imageExtension,
          GroupNamingConvention.Factory namingConvention,
-         @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames) {
+         @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames,
+         Optional<SecurityGroupExtension> securityGroupExtension) {
       super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
             getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
             startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
             nodeTerminated, nodeSuspended, initScriptRunnerFactory, runScriptOnNodeFactory, initAdminAccess,
             persistNodeCredentials, timeouts, userExecutor, client, credentialsMap, securityGroupMap, imageExtension,
-            namingConvention, generateInstanceNames);
+            namingConvention, generateInstanceNames, securityGroupExtension);
       this.client = client;
       this.placementGroupMap = placementGroupMap;
       this.placementGroupDeleted = placementGroupDeleted;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
index 7d18968..5341d57 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
@@ -39,6 +39,7 @@ import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier;
 import org.jclouds.compute.config.BaseComputeServiceContextModule;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass;
 import org.jclouds.ec2.compute.domain.RegionAndName;
@@ -173,4 +174,9 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext
    protected Optional<ImageExtension> provideImageExtension(Injector i) {
       return Optional.of(i.getInstance(ImageExtension.class));
    }
+
+   @Override
+   protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
+      return Optional.of(i.getInstance(SecurityGroupExtension.class));
+   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java
index f82216f..8102cb6 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java
@@ -31,6 +31,7 @@ import javax.inject.Singleton;
 
 import org.jclouds.aws.ec2.compute.AWSEC2ComputeService;
 import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions;
+import org.jclouds.aws.ec2.compute.extensions.AWSEC2SecurityGroupExtension;
 import org.jclouds.aws.ec2.compute.loaders.AWSEC2CreateSecurityGroupIfNeeded;
 import org.jclouds.aws.ec2.compute.suppliers.CallForImages;
 import org.jclouds.aws.ec2.domain.PlacementGroup;
@@ -41,8 +42,10 @@ import org.jclouds.aws.ec2.predicates.PlacementGroupAvailable;
 import org.jclouds.aws.ec2.predicates.PlacementGroupDeleted;
 import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.SecurityGroup;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.config.ValueOfConfigurationKeyOrNull;
 import org.jclouds.domain.LoginCredentials;
@@ -50,9 +53,11 @@ import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule;
 import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey;
 import org.jclouds.ec2.compute.domain.RegionAndName;
 import org.jclouds.ec2.compute.extensions.EC2ImageExtension;
+import org.jclouds.ec2.compute.extensions.EC2SecurityGroupExtension;
 import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair;
 import org.jclouds.ec2.compute.functions.CredentialsForInstance;
 import org.jclouds.ec2.compute.functions.EC2ImageParser;
+import org.jclouds.ec2.compute.functions.EC2SecurityGroupToSecurityGroup;
 import org.jclouds.ec2.compute.functions.PasswordCredentialsFromWindowsInstance;
 import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData;
 import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
@@ -105,8 +110,12 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep
       install(new FactoryModuleBuilder().build(CallForImages.Factory.class));
       bind(new TypeLiteral<Function<org.jclouds.ec2.domain.Image, Image>>() {
       }).to(EC2ImageParser.class);
+      bind(new TypeLiteral<Function<org.jclouds.ec2.domain.SecurityGroup, SecurityGroup>>() {
+      }).to(EC2SecurityGroupToSecurityGroup.class);
       bind(new TypeLiteral<ImageExtension>() {
       }).to(EC2ImageExtension.class);
+      bind(new TypeLiteral<SecurityGroupExtension>() {
+      }).to(AWSEC2SecurityGroupExtension.class);
    }
 
    @Provides


[3/4] JCLOUDS-101 - Add SecurityGroupExtension for compute

Posted by ab...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionExpectTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionExpectTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionExpectTest.java
new file mode 100644
index 0000000..89e54f2
--- /dev/null
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionExpectTest.java
@@ -0,0 +1,585 @@
+/*
+ * 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.ec2.compute.extensions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.ec2.compute.domain.RegionAndName;
+import org.jclouds.ec2.compute.internal.BaseEC2ComputeServiceExpectTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Futures;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "EC2SecurityGroupExtensionExpectTest")
+public class EC2SecurityGroupExtensionExpectTest extends BaseEC2ComputeServiceExpectTest {
+   
+   public void testListSecurityGroups() {
+      HttpRequest describeSecurityGroupsAllRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups").build());
+      
+      HttpResponse describeSecurityGroupsAllResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_new.xml", MediaType.APPLICATION_XML)).build();
+      
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsAllRequest, describeSecurityGroupsAllResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse);
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse);
+      requestResponseMap.put(describeInstanceRequest, describeInstanceResponse);
+      
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+      
+      Set<SecurityGroup> groups = extension.listSecurityGroups();
+      assertEquals(2, groups.size());
+   }
+
+   public void testListSecurityGroupsInLocation() {
+      HttpRequest describeSecurityGroupsAllRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups").build());
+      
+      HttpResponse describeSecurityGroupsAllResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_new.xml", MediaType.APPLICATION_XML)).build();
+      
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsAllRequest, describeSecurityGroupsAllResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse);
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse);
+      requestResponseMap.put(describeInstanceRequest, describeInstanceResponse);
+      
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+      
+      Set<SecurityGroup> groups = extension.listSecurityGroupsInLocation(new LocationBuilder()
+                                                                         .scope(LocationScope.REGION)
+                                                                         .id(region)
+                                                                         .description("region")
+                                                                         .build());
+      assertEquals(2, groups.size());
+   }
+
+   
+   public void testListSecurityGroupsForNode() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "sg-3c6ef654").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_single.xml", MediaType.APPLICATION_XML)).build();
+      
+      HttpResponse describeInstanceWithSGResponse = 
+            HttpResponse.builder().statusCode(200)
+                           .payload(payloadFromResourceWithContentType(
+                                 "/describe_instances_running_securitygroups.xml", MediaType.APPLICATION_XML)).build();
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse);
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse);
+      requestResponseMap.put(describeInstanceRequest, describeInstanceWithSGResponse);
+      
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+      
+      Set<SecurityGroup> groups = extension.listSecurityGroupsForNode(new RegionAndName(region, "i-2baa5550").slashEncode());
+      assertEquals(1, groups.size());
+   }
+
+   public void testGetSecurityGroupById() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_single.xml", MediaType.APPLICATION_XML)).build();
+      
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse);
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse);
+      
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+      
+      SecurityGroup group = extension.getSecurityGroupById(new RegionAndName(region, "jclouds#some-group").slashEncode());
+      assertEquals("sg-3c6ef654", group.getProviderId());
+      assertEquals("jclouds#some-group", group.getId());
+   }
+
+   public void testCreateSecurityGroup() {
+      HttpRequest createSecurityGroupExtRequest = 
+               formSigner.filter(HttpRequest.builder()
+                          .method("POST")
+                          .endpoint("https://ec2." + region + ".amazonaws.com/")
+                          .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                          .addFormParam("Action", "CreateSecurityGroup")
+                          .addFormParam("GroupDescription", "jclouds#some-group")
+                          .addFormParam("GroupName", "jclouds#some-group").build());
+
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_single.xml", MediaType.APPLICATION_XML)).build();
+      
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupExtRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse);
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse);
+      
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+      
+      SecurityGroup group = extension.createSecurityGroup("some-group", new LocationBuilder()
+                                                          .scope(LocationScope.REGION)
+                                                          .id(region)
+                                                          .description("region")
+                                                          .build());
+      
+      assertEquals("sg-3c6ef654", group.getProviderId());
+      assertEquals("jclouds#some-group", group.getId());
+   }
+
+   public void testRemoveSecurityGroup() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_single.xml", MediaType.APPLICATION_XML)).build();
+      
+      HttpRequest deleteSecurityGroupRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DeleteSecurityGroup")
+                           .addFormParam("GroupName", "jclouds#some-group").build());
+      
+      HttpResponse deleteSecurityGroupResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/delete_securitygroup.xml", MediaType.APPLICATION_XML)).build();
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(deleteSecurityGroupRequest, deleteSecurityGroupResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse);
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse);
+      
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+      
+      assertTrue(extension.removeSecurityGroup(new RegionAndName(region, "jclouds#some-group").slashEncode()));
+   }
+
+   public void testAddIpPermissionCidrFromIpPermission() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_cidr.xml", MediaType.APPLICATION_XML)).build();
+      
+
+      HttpRequest authorizeSecurityGroupIngressRequestRange = 
+               formSigner.filter(HttpRequest.builder()
+                          .method("POST")
+                          .endpoint("https://ec2." + region + ".amazonaws.com/")
+                          .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                          .addFormParam("Action", "AuthorizeSecurityGroupIngress")
+                          .addFormParam("CidrIp", "0.0.0.0/0")
+                          .addFormParam("FromPort", "22")
+                          .addFormParam("ToPort", "40")
+                          .addFormParam("GroupName", "jclouds#some-group")
+                          .addFormParam("IpProtocol", "tcp").build());
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse);
+
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(IpProtocol.TCP);
+      builder.fromPort(22);
+      builder.toPort(40);
+      builder.cidrBlock("0.0.0.0/0");
+
+      IpPermission perm = builder.build();
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+
+      SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder();
+      groupBuilder.id("jclouds#some-group");
+      groupBuilder.providerId("sg-3c6ef654");
+      groupBuilder.name("jclouds#some-group");
+      groupBuilder.location(new LocationBuilder()
+                            .scope(LocationScope.REGION)
+                            .id(region)
+                            .description("region")
+                            .build());
+      
+      SecurityGroup origGroup = groupBuilder.build();
+
+      SecurityGroup newGroup = extension.addIpPermission(perm, origGroup);
+
+      assertEquals(1, newGroup.getIpPermissions().size());
+
+      IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions());
+
+      assertNotNull(newPerm);
+      assertEquals(IpProtocol.TCP, newPerm.getIpProtocol());
+      assertEquals(22, newPerm.getFromPort());
+      assertEquals(40, newPerm.getToPort());
+      assertEquals(1, newPerm.getCidrBlocks().size());
+      assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0"));
+   }
+
+   public void testAddIpPermissionCidrFromParams() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_cidr.xml", MediaType.APPLICATION_XML)).build();
+      
+
+      HttpRequest authorizeSecurityGroupIngressRequestRange = 
+               formSigner.filter(HttpRequest.builder()
+                          .method("POST")
+                          .endpoint("https://ec2." + region + ".amazonaws.com/")
+                          .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                          .addFormParam("Action", "AuthorizeSecurityGroupIngress")
+                          .addFormParam("CidrIp", "0.0.0.0/0")
+                          .addFormParam("FromPort", "22")
+                          .addFormParam("ToPort", "40")
+                          .addFormParam("GroupName", "jclouds#some-group")
+                          .addFormParam("IpProtocol", "tcp").build());
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse);
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+
+      SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder();
+      groupBuilder.id("jclouds#some-group");
+      groupBuilder.providerId("sg-3c6ef654");
+      groupBuilder.name("jclouds#some-group");
+      groupBuilder.location(new LocationBuilder()
+                            .scope(LocationScope.REGION)
+                            .id(region)
+                            .description("region")
+                            .build());
+      
+      SecurityGroup origGroup = groupBuilder.build();
+
+      SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP,
+                                                         22,
+                                                         40,
+                                                         emptyMultimap(),
+                                                         ImmutableSet.of("0.0.0.0/0"),
+                                                         emptyStringSet(),
+                                                         origGroup);
+
+      assertEquals(1, newGroup.getIpPermissions().size());
+
+      IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions());
+
+      assertNotNull(newPerm);
+      assertEquals(IpProtocol.TCP, newPerm.getIpProtocol());
+      assertEquals(22, newPerm.getFromPort());
+      assertEquals(40, newPerm.getToPort());
+      assertEquals(1, newPerm.getCidrBlocks().size());
+      assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0"));
+   }
+
+
+   public void testAddIpPermissionGroupFromIpPermission() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build();
+      
+
+      HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = 
+               formSigner.filter(HttpRequest.builder()
+                          .method("POST")
+                          .endpoint("https://ec2." + region + ".amazonaws.com/")
+                          .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                          .addFormParam("Action", "AuthorizeSecurityGroupIngress")
+                          .addFormParam("SourceSecurityGroupName", "jclouds#some-group")
+                          .addFormParam("SourceSecurityGroupOwnerId", "993194456877")
+                          .addFormParam("GroupName", "jclouds#some-group").build());
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse);
+
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(IpProtocol.TCP);
+      builder.fromPort(22);
+      builder.toPort(40);
+      builder.tenantIdGroupNamePair("993194456877", "jclouds#some-group");
+
+      IpPermission perm = builder.build();
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+
+      SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder();
+      groupBuilder.id("jclouds#some-group");
+      groupBuilder.providerId("sg-3c6ef654");
+      groupBuilder.name("jclouds#some-group");
+      groupBuilder.location(new LocationBuilder()
+                            .scope(LocationScope.REGION)
+                            .id(region)
+                            .description("region")
+                            .build());
+      groupBuilder.ownerId("993194456877");
+      
+      SecurityGroup origGroup = groupBuilder.build();
+
+      SecurityGroup newGroup = extension.addIpPermission(perm, origGroup);
+
+      assertEquals(1, newGroup.getIpPermissions().size());
+
+      IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions());
+
+      assertNotNull(newPerm);
+      assertEquals(IpProtocol.TCP, newPerm.getIpProtocol());
+      assertEquals(22, newPerm.getFromPort());
+      assertEquals(40, newPerm.getToPort());
+      assertEquals(0, newPerm.getCidrBlocks().size());
+      assertEquals(1, newPerm.getTenantIdGroupNamePairs().size());
+      assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId()));
+      assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getName()));
+   }
+
+   public void testAddIpPermissionGroupFromParams() {
+      HttpRequest describeSecurityGroupsSingleRequest = 
+         formSigner.filter(HttpRequest.builder()
+                           .method("POST")
+                           .endpoint("https://ec2." + region + ".amazonaws.com/")
+                           .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                           .addFormParam("Action", "DescribeSecurityGroups")
+                           .addFormParam("GroupName.1", "jclouds#some-group").build());
+      
+      HttpResponse describeSecurityGroupsSingleResponse = 
+         HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType(
+                                                     "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build();
+      
+
+      HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = 
+               formSigner.filter(HttpRequest.builder()
+                          .method("POST")
+                          .endpoint("https://ec2." + region + ".amazonaws.com/")
+                          .addHeader("Host", "ec2." + region + ".amazonaws.com")
+                          .addFormParam("Action", "AuthorizeSecurityGroupIngress")
+                          .addFormParam("SourceSecurityGroupName", "jclouds#some-group")
+                          .addFormParam("SourceSecurityGroupOwnerId", "993194456877")
+                          .addFormParam("GroupName", "jclouds#some-group").build());
+
+      Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
+      requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
+      requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse);
+      requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
+      requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
+
+      requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse);
+
+      SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get();
+
+      SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder();
+      groupBuilder.id("jclouds#some-group");
+      groupBuilder.providerId("sg-3c6ef654");
+      groupBuilder.name("jclouds#some-group");
+      groupBuilder.ownerId("993194456877");
+      groupBuilder.location(new LocationBuilder()
+                            .scope(LocationScope.REGION)
+                            .id(region)
+                            .description("region")
+                            .build());
+      
+      SecurityGroup origGroup = groupBuilder.build();
+
+      ImmutableMultimap.Builder<String, String> permBuilder = ImmutableMultimap.builder();
+      permBuilder.put(origGroup.getOwnerId(), origGroup.getName());
+      
+      SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP,
+                                                         22,
+                                                         40,
+                                                         permBuilder.build(),
+                                                         emptyStringSet(),
+                                                         emptyStringSet(),
+                                                         origGroup);
+
+      assertEquals(1, newGroup.getIpPermissions().size());
+
+      IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions());
+
+      assertNotNull(newPerm);
+      assertEquals(IpProtocol.TCP, newPerm.getIpProtocol());
+      assertEquals(22, newPerm.getFromPort());
+      assertEquals(40, newPerm.getToPort());
+      assertEquals(0, newPerm.getCidrBlocks().size());
+      assertEquals(1, newPerm.getTenantIdGroupNamePairs().size());
+      assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId()));
+      assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getName()));
+   }
+
+   private Multimap<String, String> emptyMultimap() {
+      return LinkedHashMultimap.create();
+   }
+
+   private Set<String> emptyStringSet() {
+      return Sets.newLinkedHashSet();
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionLiveTest.java
new file mode 100644
index 0000000..128e1a3
--- /dev/null
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionLiveTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.ec2.compute.extensions;
+
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.extensions.internal.BaseSecurityGroupExtensionLiveTest;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import com.google.inject.Module;
+
+/**
+ * Live test for ec2 {@link SecurityGroupExtension} implementation
+ * 
+ * @author Andrew Bayer
+ * 
+ */
+@Test(groups = "live", singleThreaded = true, testName = "EC2SecurityGroupExtensionLiveTest")
+public class EC2SecurityGroupExtensionLiveTest extends BaseSecurityGroupExtensionLiveTest {
+
+   public EC2SecurityGroupExtensionLiveTest() {
+      provider = "ec2";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroupTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroupTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroupTest.java
new file mode 100644
index 0000000..9c7d3b1
--- /dev/null
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroupTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.ec2.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.ec2.util.IpPermissions;
+import org.jclouds.net.domain.IpPermission;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Andrew Bayer
+ */
+@Test(groups = "unit", testName = "EC2SecurityGroupToSecurityGroupTest")
+public class EC2SecurityGroupToSecurityGroupTest {
+
+   static Location provider = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1")
+         .description("us-east-1").build();
+
+   @Test
+   public void testApply() {
+      IpPermissions authorization = IpPermissions.permitAnyProtocol();
+
+      org.jclouds.ec2.domain.SecurityGroup origGroup = org.jclouds.ec2.domain.SecurityGroup.builder()
+         .region("us-east-1")
+         .id("some-id")
+         .name("some-group")
+         .ownerId("some-owner")
+         .description("some-description")
+         .ipPermission(authorization)
+         .build();
+
+      EC2SecurityGroupToSecurityGroup parser = createGroupParser(ImmutableSet.of(provider));
+
+      SecurityGroup group = parser.apply(origGroup);
+      
+      assertEquals(group.getLocation(), provider);
+      assertEquals(group.getId(), origGroup.getName());
+      assertEquals(group.getProviderId(), origGroup.getId());
+      assertEquals(group.getName(), origGroup.getName());
+      assertEquals(group.getIpPermissions(), (Set<IpPermission>)origGroup);
+      assertEquals(group.getOwnerId(), origGroup.getOwnerId());
+   }
+
+   private EC2SecurityGroupToSecurityGroup createGroupParser(final ImmutableSet<Location> locations) {
+      Supplier<Set<? extends Location>> locationSupplier = new Supplier<Set<? extends Location>>() {
+
+         @Override
+         public Set<? extends Location> get() {
+            return locations;
+         }
+
+      };
+
+      EC2SecurityGroupToSecurityGroup parser = new EC2SecurityGroupToSecurityGroup(locationSupplier);
+
+      return parser;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java
index 85358b3..9597e1a 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java
@@ -29,10 +29,10 @@ import java.util.concurrent.ExecutionException;
 
 import org.jclouds.ec2.compute.domain.RegionAndName;
 import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.domain.UserIdGroupPair;
 import org.jclouds.ec2.services.SecurityGroupClient;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicate;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java
index 24360d7..c22a279 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java
@@ -22,12 +22,12 @@ import java.io.IOException;
 
 import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404;
 import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.UserIdGroupPair;
 import org.jclouds.ec2.xml.DescribeSecurityGroupsResponseHandler;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseSax;
 import org.jclouds.http.functions.ReleasePayloadAndReturn;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.rest.internal.GeneratedHttpRequest;
 import org.testng.annotations.Test;
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java
index 0f25935..c6d02d2 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java
@@ -30,10 +30,10 @@ import java.util.Set;
 import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
 import org.jclouds.ec2.EC2ApiMetadata;
 import org.jclouds.ec2.EC2Client;
-import org.jclouds.ec2.domain.IpPermission;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.domain.UserIdGroupPair;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -174,7 +174,7 @@ public class SecurityGroupClientLiveTest extends BaseComputeServiceContextLiveTe
          assertEventually(new GroupHasPermission(client, group2Name, new Predicate<IpPermission>() {
             @Override
             public boolean apply(IpPermission arg0) {
-               return arg0.getUserIdGroupPairs().equals(ImmutableMultimap.of(group.getOwnerId(), group1Name));
+               return arg0.getTenantIdGroupNamePairs().equals(ImmutableMultimap.of(group.getOwnerId(), group1Name));
             }
          }));
 
@@ -191,7 +191,7 @@ public class SecurityGroupClientLiveTest extends BaseComputeServiceContextLiveTe
       @Override
       public boolean apply(IpPermission arg0) {
          return arg0.getIpProtocol() == IpProtocol.TCP && arg0.getFromPort() == 80 && arg0.getToPort() == 80
-               && arg0.getIpRanges().equals(ImmutableSet.of("0.0.0.0/0"));
+               && arg0.getCidrBlocks().equals(ImmutableSet.of("0.0.0.0/0"));
       }
    }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/util/IpPermissionsTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/util/IpPermissionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/util/IpPermissionsTest.java
index 6d466b7..03a2549 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/util/IpPermissionsTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/util/IpPermissionsTest.java
@@ -18,7 +18,7 @@ package org.jclouds.ec2.util;
 
 import static org.testng.Assert.assertEquals;
 
-import org.jclouds.ec2.domain.IpProtocol;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java
index 8032aaa..caabd1a 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java
@@ -21,10 +21,10 @@ import static org.testng.Assert.assertEquals;
 import java.io.InputStream;
 import java.util.Set;
 
-import org.jclouds.ec2.domain.IpPermission;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.http.functions.ParseSax;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMultimap;
@@ -46,10 +46,10 @@ public class DescribeSecurityGroupsResponseHandlerTest extends BaseEC2HandlerTes
       InputStream is = getClass().getResourceAsStream("/describe_securitygroups.xml");
 
       Set<SecurityGroup> expected = ImmutableSet.of(
-            new SecurityGroup(defaultRegion, null, "WebServers", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Web Servers",
+            new SecurityGroup(defaultRegion, "sg-3c6ef654", "WebServers", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Web Servers",
                   ImmutableSet.of(new IpPermission(IpProtocol.TCP, 80, 80, ImmutableMultimap.<String, String> of(),
                         ImmutableSet.<String> of(), ImmutableSet.of("0.0.0.0/0")))),
-            new SecurityGroup(defaultRegion, null, "RangedPortsBySource", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Group A",
+            new SecurityGroup(defaultRegion, "sg-867309ab", "RangedPortsBySource", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Group A",
                   ImmutableSet.of(new IpPermission(IpProtocol.TCP, 6000, 7000, ImmutableMultimap
                         .<String, String> of(), ImmutableSet.<String> of(), ImmutableSet.<String> of()))));
 
@@ -69,7 +69,7 @@ public class DescribeSecurityGroupsResponseHandlerTest extends BaseEC2HandlerTes
       userIdGroupPairs.put("UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "jclouds#cluster#world");
 
       Set<SecurityGroup> expected = ImmutableSet.of(
-            new SecurityGroup(defaultRegion, null, "jclouds#cluster#world", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Cluster",
+            new SecurityGroup(defaultRegion, "sg-3c6ef654", "jclouds#cluster#world", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Cluster",
                   ImmutableSet.of(
                         new IpPermission(IpProtocol.TCP, 22, 22, ImmutableMultimap.<String, String> of(),
                               ImmutableSet.<String> of(), ImmutableSet.of("0.0.0.0/0")),

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/resources/delete_securitygroup.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/delete_securitygroup.xml b/apis/ec2/src/test/resources/delete_securitygroup.xml
new file mode 100644
index 0000000..9545778
--- /dev/null
+++ b/apis/ec2/src/test/resources/delete_securitygroup.xml
@@ -0,0 +1,4 @@
+<DeleteSecurityGroupResponse xmlns="http://ec2.amazonaws.com/doc/2013-02-01/">
+  <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> 
+  <return>true</return>
+</DeleteSecurityGroupResponse>

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/resources/describe_instances_running_securitygroups.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/describe_instances_running_securitygroups.xml b/apis/ec2/src/test/resources/describe_instances_running_securitygroups.xml
new file mode 100644
index 0000000..4c07ed2
--- /dev/null
+++ b/apis/ec2/src/test/resources/describe_instances_running_securitygroups.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
+    <requestId>f6d3252e-35e5-4ef5-b2c5-62da95dd829b</requestId>
+    <reservationSet>
+        <item>
+            <reservationId>r-205ad944</reservationId>
+            <ownerId>993194456877</ownerId>
+            <groupSet>
+                <item>
+                    <groupId>sg-3c6ef654</groupId>
+                    <groupName>jclouds#some-group</groupName>
+                </item>
+            </groupSet>
+            <instancesSet>
+                <item>
+                    <instanceId>i-2baa5550</instanceId>
+                    <imageId>ami-aecd60c7</imageId>
+                    <instanceState>
+                        <code>16</code>
+                        <name>running</name>
+                    </instanceState>
+                    <privateDnsName>ip-10-28-89-195.ec2.internal</privateDnsName>
+                    <dnsName>ec2-50-16-1-166.compute-1.amazonaws.com</dnsName>
+                    <reason/>
+                    <keyName>jclouds#mygroup2#81</keyName>
+                    <amiLaunchIndex>0</amiLaunchIndex>
+                    <productCodes/>
+                    <instanceType>t1.micro</instanceType>
+                    <launchTime>2012-08-02T04:28:30.000Z</launchTime>
+                    <placement>
+                        <availabilityZone>us-east-1e</availabilityZone>
+                        <groupName/>
+                        <tenancy>default</tenancy>
+                    </placement>
+                    <kernelId>aki-88aa75e1</kernelId>
+                    <monitoring>
+                        <state>disabled</state>
+                    </monitoring>
+                    <privateIpAddress>10.28.89.195</privateIpAddress>
+                    <ipAddress>50.16.1.166</ipAddress>
+                    <groupSet>
+                        <item>
+                          <groupId>sg-3c6ef654</groupId>
+                          <groupName>jclouds#some-group</groupName>
+                        </item>
+                    </groupSet>
+                    <architecture>x86_64</architecture>
+                    <rootDeviceType>ebs</rootDeviceType>
+                    <rootDeviceName>/dev/sda1</rootDeviceName>
+                    <blockDeviceMapping>
+                        <item>
+                            <deviceName>/dev/sda1</deviceName>
+                            <ebs>
+                                <volumeId>vol-f2d7c993</volumeId>
+                                <status>attached</status>
+                                <attachTime>2012-08-02T04:28:56.000Z</attachTime>
+                                <deleteOnTermination>true</deleteOnTermination>
+                            </ebs>
+                        </item>
+                    </blockDeviceMapping>
+                    <virtualizationType>paravirtual</virtualizationType>
+                    <clientToken/>
+                    <tagSet>
+                        <item>
+                            <key>Name</key>
+                            <value>mygroup2-2baa5550</value>
+                        </item>
+                    </tagSet>
+                    <hypervisor>xen</hypervisor>
+                </item>
+            </instancesSet>
+        </item>
+    </reservationSet>
+</DescribeInstancesResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/resources/describe_securitygroups.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/describe_securitygroups.xml b/apis/ec2/src/test/resources/describe_securitygroups.xml
index 3a6c9b8..b703afa 100644
--- a/apis/ec2/src/test/resources/describe_securitygroups.xml
+++ b/apis/ec2/src/test/resources/describe_securitygroups.xml
@@ -3,6 +3,7 @@
 	<securityGroupInfo>
 		<item>
 			<ownerId>UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM</ownerId>
+                        <groupId>sg-3c6ef654</groupId>
 			<groupName>WebServers</groupName>
 			<groupDescription>Web Servers</groupDescription>
 			<ipPermissions>
@@ -21,7 +22,8 @@
 		</item>
 		<item>
 			<ownerId>UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM</ownerId>
-			<groupName>RangedPortsBySource</groupName>
+                        <groupId>sg-867309ab</groupId>
+                        <groupName>RangedPortsBySource</groupName>
 			<groupDescription>Group A</groupDescription>
 			<ipPermissions>
 				<item>

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/resources/describe_securitygroups_empty.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/describe_securitygroups_empty.xml b/apis/ec2/src/test/resources/describe_securitygroups_empty.xml
index 0079ee6..ca89f4e 100644
--- a/apis/ec2/src/test/resources/describe_securitygroups_empty.xml
+++ b/apis/ec2/src/test/resources/describe_securitygroups_empty.xml
@@ -23,6 +23,7 @@
 					<ipRanges />
 					<groups>
 						<item>
+                                                  <groupId>sg-3c6ef654</groupId>
 							<groupName>jclouds#cluster#world</groupName>
 							<userId>UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM</userId>
 						</item>
@@ -31,6 +32,7 @@
 				</item>
 			</ipPermissions>
 			<groupName>jclouds#cluster#world</groupName>
+                        <groupId>sg-3c6ef654</groupId>
 			<groupDescription>Cluster</groupDescription>
 			<ownerId>UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM</ownerId>
 		</item>

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/resources/describe_securitygroups_extension_cidr.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/describe_securitygroups_extension_cidr.xml b/apis/ec2/src/test/resources/describe_securitygroups_extension_cidr.xml
new file mode 100644
index 0000000..f6a6f9e
--- /dev/null
+++ b/apis/ec2/src/test/resources/describe_securitygroups_extension_cidr.xml
@@ -0,0 +1,24 @@
+<DescribeSecurityGroupsResponse
+	xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
+	<securityGroupInfo>
+		<item>
+                  <ownerId>993194456877</ownerId>
+                  <groupId>sg-3c6ef654</groupId>
+                  <groupName>jclouds#some-group</groupName>
+                  <groupDescription>jclouds#some-group</groupDescription>
+                  <ipPermissions>
+                    <item>
+                      <ipProtocol>tcp</ipProtocol>
+                      <fromPort>22</fromPort>
+                      <toPort>40</toPort>
+                      <groups />
+                      <ipRanges>
+                        <item>
+                          <cidrIp>0.0.0.0/0</cidrIp>
+                        </item>
+                      </ipRanges>
+                    </item>
+                  </ipPermissions>
+		</item>
+	</securityGroupInfo>
+</DescribeSecurityGroupsResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/resources/describe_securitygroups_extension_group.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/describe_securitygroups_extension_group.xml b/apis/ec2/src/test/resources/describe_securitygroups_extension_group.xml
new file mode 100644
index 0000000..126d45c
--- /dev/null
+++ b/apis/ec2/src/test/resources/describe_securitygroups_extension_group.xml
@@ -0,0 +1,26 @@
+<DescribeSecurityGroupsResponse
+	xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
+	<securityGroupInfo>
+		<item>
+                  <ownerId>993194456877</ownerId>
+                  <groupName>jclouds#some-group</groupName>
+                  <groupDescription>jclouds#some-group</groupDescription>
+                  <groupId>sg-3c6ef654</groupId>
+                  <ipPermissions>
+                    <item>
+                      <ipProtocol>tcp</ipProtocol>
+                      <fromPort>22</fromPort>
+                      <toPort>40</toPort>
+                      <groups>
+                        <item>
+                          <userId>993194456877</userId>
+                          <groupId>sg-3c6ef654</groupId>
+                          <groupName>jclouds#some-group</groupName>
+                        </item>
+                      </groups>
+                      <ipRanges/>
+                    </item>
+                  </ipPermissions>
+		</item>
+	</securityGroupInfo>
+</DescribeSecurityGroupsResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/resources/describe_securitygroups_extension_new.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/describe_securitygroups_extension_new.xml b/apis/ec2/src/test/resources/describe_securitygroups_extension_new.xml
new file mode 100644
index 0000000..7d2c211
--- /dev/null
+++ b/apis/ec2/src/test/resources/describe_securitygroups_extension_new.xml
@@ -0,0 +1,27 @@
+<DescribeSecurityGroupsResponse
+	xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
+	<securityGroupInfo>
+		<item>
+                  <ownerId>993194456877</ownerId>
+                  <groupName>jclouds#some-group</groupName>
+                  <groupDescription>jclouds#some-group</groupDescription>
+                  <groupId>sg-3c6ef654</groupId>
+                  <ipPermissions/>
+		</item>
+		<item>
+                  <ownerId>993194456877</ownerId>
+                  <groupId>sg-3a2b3c4d</groupId>
+                  <groupName>group-a</groupName>
+                  <groupDescription>Group A</groupDescription>
+                  <ipPermissions>
+                    <item>
+                      <ipProtocol>tcp</ipProtocol>
+                      <fromPort>6000</fromPort>
+                      <toPort>7000</toPort>
+                      <groups />
+                      <ipRanges />
+                    </item>
+                  </ipPermissions>
+		</item>
+	</securityGroupInfo>
+</DescribeSecurityGroupsResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/resources/describe_securitygroups_extension_single.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/describe_securitygroups_extension_single.xml b/apis/ec2/src/test/resources/describe_securitygroups_extension_single.xml
new file mode 100644
index 0000000..f66e4c6
--- /dev/null
+++ b/apis/ec2/src/test/resources/describe_securitygroups_extension_single.xml
@@ -0,0 +1,12 @@
+<DescribeSecurityGroupsResponse
+	xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
+	<securityGroupInfo>
+		<item>
+                  <ownerId>993194456877</ownerId>
+                  <groupName>jclouds#some-group</groupName>
+                  <groupDescription>jclouds#some-group</groupDescription>
+                  <groupId>sg-3c6ef654</groupId>
+                  <ipPermissions/>
+		</item>
+	</securityGroupInfo>
+</DescribeSecurityGroupsResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java
index 28c26e1..7a10942 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java
@@ -40,6 +40,7 @@ import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.functions.GroupNamingConvention;
 import org.jclouds.compute.internal.BaseComputeService;
 import org.jclouds.compute.internal.PersistNodeCredentials;
@@ -107,12 +108,13 @@ public class NovaComputeService extends BaseComputeService {
             LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupMap,
             LoadingCache<ZoneAndName, KeyPair> keyPairCache,
             Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId,
-            GroupNamingConvention.Factory namingConvention, Optional<ImageExtension> imageExtension) {
+            GroupNamingConvention.Factory namingConvention, Optional<ImageExtension> imageExtension,
+            Optional<SecurityGroupExtension> securityGroupExtension) {
       super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
                getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
                startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
                nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
-               persistNodeCredentials, timeouts, userExecutor, imageExtension);
+               persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension);
       this.novaApi = checkNotNull(novaApi, "novaApi");
       this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
       this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/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 92a19b3..57a341d 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
@@ -38,12 +38,14 @@ import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.SecurityGroup;
 import org.jclouds.compute.extensions.ImageExtension;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
 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;
@@ -51,7 +53,9 @@ import org.jclouds.openstack.nova.v2_0.compute.functions.CreateSecurityGroupIfNe
 import org.jclouds.openstack.nova.v2_0.compute.functions.FlavorInZoneToHardware;
 import org.jclouds.openstack.nova.v2_0.compute.functions.ImageInZoneToImage;
 import org.jclouds.openstack.nova.v2_0.compute.functions.ImageToOperatingSystem;
+import org.jclouds.openstack.nova.v2_0.compute.functions.NovaSecurityGroupToSecurityGroup;
 import org.jclouds.openstack.nova.v2_0.compute.functions.OrphanedGroupsByZoneId;
+import org.jclouds.openstack.nova.v2_0.compute.functions.SecurityGroupRuleToIpPermission;
 import org.jclouds.openstack.nova.v2_0.compute.functions.ServerInZoneToNodeMetadata;
 import org.jclouds.openstack.nova.v2_0.compute.loaders.CreateUniqueKeyPair;
 import org.jclouds.openstack.nova.v2_0.compute.loaders.FindSecurityGroupOrCreate;
@@ -60,6 +64,7 @@ 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.zonescoped.FlavorInZone;
 import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ImageInZone;
@@ -108,6 +113,12 @@ public class NovaComputeServiceContextModule extends
       bind(new TypeLiteral<Function<ServerInZone, NodeMetadata>>() {
       }).to(ServerInZoneToNodeMetadata.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<Set<? extends NodeMetadata>,  Multimap<String, String>>>() {
       }).to(OrphanedGroupsByZoneId.class);
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/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 04f992e..6b5d519 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
@@ -28,9 +28,9 @@ import javax.inject.Singleton;
 
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.logging.Logger;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.NovaApi;
 import org.jclouds.openstack.nova.v2_0.domain.Ingress;
-import org.jclouds.openstack.nova.v2_0.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.openstack.nova.v2_0.domain.zonescoped.SecurityGroupInZone;
 import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ZoneSecurityGroupNameAndPorts;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/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
new file mode 100644
index 0000000..3e8b8a6
--- /dev/null
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroup.java
@@ -0,0 +1,74 @@
+/*
+ * 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.transform;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+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.common.base.Predicate;
+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.
+ * 
+ * @author Andrew Bayer
+ */
+@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());
+      builder.ipPermissions(transform(group.getRules(), ruleToPermission));
+      
+      return builder.build();
+   }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/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
new file mode 100644
index 0000000..c2e5e64
--- /dev/null
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermission.java
@@ -0,0 +1,57 @@
+/*
+ * 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 javax.annotation.Resource;
+import javax.inject.Named;
+
+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;
+
+
+/**
+ * A function for transforming a nova-specific SecurityGroupRule into a generic
+ * IpPermission object.
+ * 
+ * @author Andrew Bayer
+ */
+public class SecurityGroupRuleToIpPermission implements Function<SecurityGroupRule, IpPermission> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+   
+   public SecurityGroupRuleToIpPermission() {
+   }
+
+   @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) 
+         builder.tenantIdGroupNamePair(rule.getGroup().getTenantId(), rule.getGroup().getName());
+      if (rule.getIpRange() != null)
+         builder.cidrBlock(rule.getIpRange());
+      
+      return builder.build();
+   }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java
index d687e83..3a9322d 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java
@@ -21,6 +21,7 @@ import java.beans.ConstructorProperties;
 import javax.inject.Named;
 
 import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.net.domain.IpProtocol;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/IpProtocol.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/IpProtocol.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/IpProtocol.java
deleted file mode 100644
index ac13977..0000000
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/IpProtocol.java
+++ /dev/null
@@ -1,39 +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.domain;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-public enum IpProtocol {
-   TCP, UDP, ICMP, UNRECOGNIZED;
-   public String value() {
-      return name().toLowerCase();
-   }
-
-   @Override
-   public String toString() {
-      return value();
-   }
-
-   public static IpProtocol fromValue(String protocol) {
-      try {
-         return valueOf(checkNotNull(protocol, "protocol").toUpperCase());
-      } catch (IllegalArgumentException e) {
-         return UNRECOGNIZED;
-      }
-   }
-}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java
index 3da080d..92d58f7 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java
@@ -23,6 +23,7 @@ import java.beans.ConstructorProperties;
 import javax.inject.Named;
 
 import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.net.domain.IpProtocol;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/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
new file mode 100644
index 0000000..8db1e15
--- /dev/null
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroupTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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 static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+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.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Andrew Bayer
+ */
+@Test(groups = "unit", testName = "NovaSecurityGroupToSecurityGroupTest")
+public class NovaSecurityGroupToSecurityGroupTest {
+
+   private static final SecurityGroupRuleToIpPermission ruleConverter = new SecurityGroupRuleToIpPermission();
+   
+   @Test
+   public void testApplyWithGroup() {
+      TenantIdAndName group = TenantIdAndName.builder().tenantId("tenant").name("name").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();
+
+      NovaSecurityGroupToSecurityGroup parser = createGroupParser();
+
+      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() {
+      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-group")
+         .description("some-description")
+         .rules(ruleToConvert)
+         .build();
+
+      NovaSecurityGroupToSecurityGroup parser = createGroupParser();
+
+      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/incubator-jclouds/blob/5f524ee6/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
new file mode 100644
index 0000000..1702a13
--- /dev/null
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermissionTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ * 
+ * @author Andrew Bayer
+ */
+public class SecurityGroupRuleToIpPermissionTest {
+
+   @Test
+   public void testApplyWithGroup() {
+
+      TenantIdAndName group = TenantIdAndName.builder().tenantId("tenant").name("name").build();
+      
+      SecurityGroupRule ruleToConvert = SecurityGroupRule.builder()
+         .id("some-id")
+         .ipProtocol(IpProtocol.TCP)
+         .fromPort(10)
+         .toPort(20)
+         .group(group)
+         .parentGroupId("some-other-id")
+         .build();
+
+      SecurityGroupRuleToIpPermission converter = new SecurityGroupRuleToIpPermission();
+
+      IpPermission convertedPerm = converter.apply(ruleToConvert);
+
+      assertEquals(convertedPerm.getIpProtocol(), ruleToConvert.getIpProtocol());
+      assertEquals(convertedPerm.getFromPort(), ruleToConvert.getFromPort());
+      assertEquals(convertedPerm.getToPort(), ruleToConvert.getToPort());
+      assertTrue(convertedPerm.getTenantIdGroupNamePairs().containsKey(group.getTenantId()));
+      assertTrue(convertedPerm.getTenantIdGroupNamePairs().containsValue(group.getName()));
+      assertTrue(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();
+
+      SecurityGroupRuleToIpPermission converter = new SecurityGroupRuleToIpPermission();
+
+      IpPermission convertedPerm = converter.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"));
+      assertTrue(convertedPerm.getTenantIdGroupNamePairs().size() == 0);
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiExpectTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiExpectTest.java
index 1a9f4fd..1fa8416 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiExpectTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiExpectTest.java
@@ -24,9 +24,9 @@ import java.net.URI;
 
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.NovaApi;
 import org.jclouds.openstack.nova.v2_0.domain.Ingress;
-import org.jclouds.openstack.nova.v2_0.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
 import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiExpectTest;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiLiveTest.java
index 665bb4e..c0fb2df 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiLiveTest.java
@@ -20,8 +20,8 @@ import static org.testng.Assert.assertNotNull;
 
 import java.util.Set;
 
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.domain.Ingress;
-import org.jclouds.openstack.nova.v2_0.domain.IpProtocol;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
 import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiLiveTest;


[4/4] git commit: JCLOUDS-101 - Add SecurityGroupExtension for compute

Posted by ab...@apache.org.
JCLOUDS-101 - Add SecurityGroupExtension for compute

- Adds the SecurityGroupExtension to compute, with tests and stub
support.
- Gets everything else to actually build against this.
- Unifies on compute's IpPermission/IpProtocol, eliminating EC2's.
- Converters from EC2/Nova/CloudStack SecurityGroup (and rules, for
the latter two) to the compute SecurityGroup (and rules, etc).
- EC2SecurityGroupExtension and tests.
- AWSEC2SecurityGroupExtension and tests - depends on JCLOUDS-99.


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

Branch: refs/heads/master
Commit: 5f524ee6c91916bfbda15d484a47315c1f0ece6e
Parents: a906f9f
Author: Andrew Bayer <an...@gmail.com>
Authored: Wed Jun 26 13:48:17 2013 -0700
Committer: Andrew Bayer <an...@gmail.com>
Committed: Wed Jun 26 14:59:01 2013 -0700

----------------------------------------------------------------------
 .../compute/CloudStackComputeService.java       |   6 +-
 .../CloudStackComputeServiceContextModule.java  |   8 +
 .../CloudStackSecurityGroupToSecurityGroup.java |  74 +++
 .../functions/IngressRuleToIpPermission.java    |  55 ++
 ...udStackSecurityGroupToSecurityGroupTest.java |  80 +++
 .../IngressRuleToIpPermissionTest.java          |  61 ++
 .../clojure/org/jclouds/ec2/security_group2.clj |   6 +-
 .../BindIpPermissionToIndexedFormParams.java    |   2 +-
 .../BindIpPermissionsToIndexedFormParams.java   |   2 +-
 .../jclouds/ec2/compute/EC2ComputeService.java  |   6 +-
 .../config/EC2ComputeServiceContextModule.java  |   5 +
 .../EC2ComputeServiceDependenciesModule.java    |   8 +
 .../extensions/EC2SecurityGroupExtension.java   | 384 ++++++++++++
 .../EC2SecurityGroupToSecurityGroup.java        |  95 +++
 .../loaders/CreateSecurityGroupIfNeeded.java    |   2 +-
 .../org/jclouds/ec2/domain/IpPermission.java    | 189 ------
 .../java/org/jclouds/ec2/domain/IpProtocol.java |  48 --
 .../org/jclouds/ec2/domain/SecurityGroup.java   |   1 +
 .../ec2/services/SecurityGroupAsyncClient.java  |   2 +-
 .../ec2/services/SecurityGroupClient.java       |   2 +-
 .../org/jclouds/ec2/util/IpPermissions.java     |  22 +-
 .../jclouds/ec2/xml/IpPermissionHandler.java    |   8 +-
 ...plicationArchitecturesEC2ClientLiveTest.java |   2 +-
 .../jclouds/ec2/EBSBootEC2ClientLiveTest.java   |   2 +-
 .../ec2/compute/EC2ComputeServiceLiveTest.java  |   2 +-
 .../EC2SecurityGroupExtensionExpectTest.java    | 585 +++++++++++++++++++
 .../EC2SecurityGroupExtensionLiveTest.java      |  39 ++
 .../EC2SecurityGroupToSecurityGroupTest.java    |  85 +++
 .../CreateSecurityGroupIfNeededTest.java        |   2 +-
 .../services/SecurityGroupAsyncClientTest.java  |   2 +-
 .../services/SecurityGroupClientLiveTest.java   |   8 +-
 .../org/jclouds/ec2/util/IpPermissionsTest.java |   2 +-
 ...scribeSecurityGroupsResponseHandlerTest.java |  10 +-
 .../src/test/resources/delete_securitygroup.xml |   4 +
 ...escribe_instances_running_securitygroups.xml |  74 +++
 .../test/resources/describe_securitygroups.xml  |   4 +-
 .../resources/describe_securitygroups_empty.xml |   2 +
 .../describe_securitygroups_extension_cidr.xml  |  24 +
 .../describe_securitygroups_extension_group.xml |  26 +
 .../describe_securitygroups_extension_new.xml   |  27 +
 ...describe_securitygroups_extension_single.xml |  12 +
 .../nova/v2_0/compute/NovaComputeService.java   |   6 +-
 .../config/NovaComputeServiceContextModule.java |  11 +
 .../functions/CreateSecurityGroupIfNeeded.java  |   2 +-
 .../NovaSecurityGroupToSecurityGroup.java       |  74 +++
 .../SecurityGroupRuleToIpPermission.java        |  57 ++
 .../openstack/nova/v2_0/domain/Ingress.java     |   1 +
 .../openstack/nova/v2_0/domain/IpProtocol.java  |  39 --
 .../nova/v2_0/domain/SecurityGroupRule.java     |   1 +
 .../NovaSecurityGroupToSecurityGroupTest.java   | 112 ++++
 .../SecurityGroupRuleToIpPermissionTest.java    |  86 +++
 .../extensions/SecurityGroupApiExpectTest.java  |   2 +-
 .../extensions/SecurityGroupApiLiveTest.java    |   2 +-
 ...eComputeServiceTypicalSecurityGroupTest.java |   2 +-
 .../v2_0/parse/ParseSecurityGroupListTest.java  |   2 +-
 .../nova/v2_0/parse/ParseSecurityGroupTest.java |   2 +-
 .../compute/TerremarkVCloudComputeService.java  |   5 +-
 .../org/jclouds/compute/ComputeService.java     |   9 +
 .../config/BaseComputeServiceContextModule.java |   7 +
 .../org/jclouds/compute/domain/ComputeType.java |   2 +-
 .../jclouds/compute/domain/SecurityGroup.java   |  80 +++
 .../compute/domain/SecurityGroupBuilder.java    | 122 ++++
 .../extensions/SecurityGroupExtension.java      | 180 ++++++
 .../compute/internal/BaseComputeService.java    |  12 +-
 .../compute/options/TemplateOptions.java        |  60 +-
 .../stub/config/StubComputeServiceAdapter.java  |  34 +-
 .../config/StubComputeServiceContextModule.java |  10 +
 .../StubComputeServiceDependenciesModule.java   |  58 ++
 .../extensions/StubSecurityGroupExtension.java  | 256 ++++++++
 .../org/jclouds/net/domain/IpPermission.java    |  22 +-
 .../java/org/jclouds/net/domain/IpProtocol.java |  12 +-
 .../BaseSecurityGroupExtensionLiveTest.java     | 348 +++++++++++
 ...ubSecurityGroupExtensionIntegrationTest.java |  70 +++
 .../org/jclouds/net/util/IpPermissionsTest.java |  14 +
 .../main/java/org/jclouds/util/Strings2.java    |   9 +
 .../java/org/jclouds/util/Strings2Test.java     |   8 +
 .../aws/ec2/compute/AWSEC2ComputeService.java   |   6 +-
 .../AWSEC2ComputeServiceContextModule.java      |   6 +
 .../AWSEC2ComputeServiceDependenciesModule.java |   9 +
 .../AWSEC2SecurityGroupExtension.java           | 177 ++++++
 .../AWSEC2CreateSecurityGroupIfNeeded.java      |  10 +-
 .../services/AWSSecurityGroupAsyncClient.java   |   2 +-
 .../ec2/services/AWSSecurityGroupClient.java    |   2 +-
 .../compute/AWSEC2ComputeServiceLiveTest.java   |   2 +-
 .../AWSEC2SecurityGroupExtensionExpectTest.java | 367 ++++++++++++
 .../AWSEC2SecurityGroupExtensionLiveTest.java   |  39 ++
 .../AWSEC2CreateSecurityGroupIfNeededTest.java  |  10 +-
 .../DescribeSecurityGroupsResponseTest.java     |   6 +-
 .../AWSSecurityGroupAsyncClientTest.java        |   4 +-
 .../AWSSecurityGroupClientLiveTest.java         |   6 +-
 .../gogrid/compute/GoGridComputeService.java    |   5 +-
 91 files changed, 3982 insertions(+), 364 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java
index 32d4d59..fe25a68 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java
@@ -43,6 +43,7 @@ import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.functions.GroupNamingConvention;
 import org.jclouds.compute.internal.BaseComputeService;
 import org.jclouds.compute.internal.PersistNodeCredentials;
@@ -113,12 +114,13 @@ public class CloudStackComputeService extends BaseComputeService {
             Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId,
             GroupNamingConvention.Factory namingConvention,
             Supplier<LoadingCache<String, Zone>> zoneIdToZone,
-            Optional<ImageExtension> imageExtension) {
+            Optional<ImageExtension> imageExtension,
+            Optional<SecurityGroupExtension> securityGroupExtension) {
       super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
                getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
                startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
                nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
-               persistNodeCredentials, timeouts, userExecutor, imageExtension);
+               persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension);
       this.zoneIdToZone = checkNotNull(zoneIdToZone, "zoneIdToZone");
       this.client = checkNotNull(client, "client");
       this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
----------------------------------------------------------------------
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 92e0b6c..fa4c214 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
@@ -32,6 +32,8 @@ import javax.inject.Singleton;
 import org.jclouds.cloudstack.CloudStackClient;
 import org.jclouds.cloudstack.compute.CloudStackComputeService;
 import org.jclouds.cloudstack.compute.extensions.CloudStackImageExtension;
+import org.jclouds.cloudstack.compute.functions.CloudStackSecurityGroupToSecurityGroup;
+import org.jclouds.cloudstack.compute.functions.IngressRuleToIpPermission;
 import org.jclouds.cloudstack.compute.functions.OrphanedGroupsByZoneId;
 import org.jclouds.cloudstack.compute.functions.ServiceOfferingToHardware;
 import org.jclouds.cloudstack.compute.functions.TemplateToImage;
@@ -46,6 +48,7 @@ import org.jclouds.cloudstack.compute.strategy.BasicNetworkOptionsConverter;
 import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter;
 import org.jclouds.cloudstack.compute.strategy.OptionsConverter;
 import org.jclouds.cloudstack.domain.FirewallRule;
+import org.jclouds.cloudstack.domain.IngressRule;
 import org.jclouds.cloudstack.domain.IPForwardingRule;
 import org.jclouds.cloudstack.domain.Network;
 import org.jclouds.cloudstack.domain.NetworkType;
@@ -77,6 +80,7 @@ import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.extensions.ImageExtension;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.domain.Location;
+import org.jclouds.net.domain.IpPermission;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
 
@@ -112,6 +116,10 @@ public class CloudStackComputeServiceContextModule extends
       }).to(CloudStackComputeServiceAdapter.class);
       bind(new TypeLiteral<Function<VirtualMachine, NodeMetadata>>() {
       }).to(VirtualMachineToNodeMetadata.class);
+      bind(new TypeLiteral<Function<SecurityGroup, org.jclouds.compute.domain.SecurityGroup>>() {
+      }).to(CloudStackSecurityGroupToSecurityGroup.class);
+      bind(new TypeLiteral<Function<IngressRule, IpPermission>>() {
+      }).to(IngressRuleToIpPermission.class);
       bind(new TypeLiteral<Function<Template, org.jclouds.compute.domain.Image>>() {
       }).to(TemplateToImage.class);
       bind(new TypeLiteral<Function<ServiceOffering, org.jclouds.compute.domain.Hardware>>() {

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroup.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroup.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroup.java
new file mode 100644
index 0000000..10a3e31
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroup.java
@@ -0,0 +1,74 @@
+/*
+ * 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.cloudstack.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.transform;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+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.cloudstack.domain.IngressRule;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+
+
+/**
+ * A function for transforming a CloudStack-specific SecurityGroup into a generic
+ * SecurityGroup object.
+ * 
+ * @author Andrew Bayer
+ */
+@Singleton
+public class CloudStackSecurityGroupToSecurityGroup implements Function<org.jclouds.cloudstack.domain.SecurityGroup, SecurityGroup> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   protected final Function<IngressRule,IpPermission> ruleToPermission;
+   
+   @Inject
+   public CloudStackSecurityGroupToSecurityGroup(Function<IngressRule,IpPermission> ruleToPermission) {
+      this.ruleToPermission = ruleToPermission;
+   }
+
+   @Override
+   public SecurityGroup apply(org.jclouds.cloudstack.domain.SecurityGroup group) {
+      SecurityGroupBuilder builder = new SecurityGroupBuilder();
+      
+      builder.id(group.getId());
+      builder.providerId(group.getId());
+      builder.name(group.getName());
+      builder.ownerId(group.getAccount());
+      builder.ipPermissions(transform(group.getIngressRules(), ruleToPermission));
+      
+      return builder.build();
+   }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermission.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermission.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermission.java
new file mode 100644
index 0000000..ed04568
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermission.java
@@ -0,0 +1,55 @@
+/*
+ * 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.cloudstack.compute.functions;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.cloudstack.domain.IngressRule;
+import org.jclouds.logging.Logger;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+
+import com.google.common.base.Function;
+
+
+/**
+ * A function for transforming a CloudStack-specific IngressRule into a generic
+ * IpPermission object.
+ * 
+ * @author Andrew Bayer
+ */
+public class IngressRuleToIpPermission implements Function<IngressRule, IpPermission> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+   
+   public IngressRuleToIpPermission() {
+   }
+
+   @Override
+   public IpPermission apply(IngressRule rule) {
+      IpPermission.Builder builder = IpPermission.builder();
+      builder.ipProtocol(IpProtocol.fromValue(rule.getProtocol()));
+      builder.fromPort(rule.getStartPort());
+      builder.toPort(rule.getEndPort());
+      builder.cidrBlock(rule.getCIDR());
+      
+      return builder.build();
+   }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroupTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroupTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroupTest.java
new file mode 100644
index 0000000..22420de
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroupTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.cloudstack.compute.functions;
+
+import static com.google.common.collect.Iterables.transform;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+import org.jclouds.cloudstack.domain.IngressRule;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Andrew Bayer
+ */
+@Test(groups = "unit", testName = "CloudStackSecurityGroupToSecurityGroupTest")
+public class CloudStackSecurityGroupToSecurityGroupTest {
+
+   private static final IngressRuleToIpPermission ruleConverter = new IngressRuleToIpPermission();
+   
+   @Test
+   public void testApply() {
+      IngressRule ruleToConvert = IngressRule.builder()
+         .id("some-id")
+         .account("some-account")
+         .securityGroupName("some-group-name")
+         .protocol(IpProtocol.TCP.toString())
+         .startPort(10)
+         .endPort(20)
+         .CIDR("0.0.0.0/0")
+         .build();
+
+      org.jclouds.cloudstack.domain.SecurityGroup origGroup = org.jclouds.cloudstack.domain.SecurityGroup.builder()
+         .id("some-id")
+         .name("some-group")
+         .description("some-description")
+         .account("some-account")
+         .ingressRules(ImmutableSet.of(ruleToConvert))
+         .build();
+
+      CloudStackSecurityGroupToSecurityGroup parser = createGroupParser();
+
+      SecurityGroup group = parser.apply(origGroup);
+      
+      assertEquals(group.getId(), origGroup.getId());
+      assertEquals(group.getProviderId(), origGroup.getId());
+      assertEquals(group.getName(), origGroup.getName());
+      assertEquals(group.getOwnerId(), origGroup.getAccount());
+      assertEquals(group.getIpPermissions(), ImmutableSet.copyOf(transform(origGroup.getIngressRules(), ruleConverter)));
+   }
+
+   private CloudStackSecurityGroupToSecurityGroup createGroupParser() {
+      CloudStackSecurityGroupToSecurityGroup parser = new CloudStackSecurityGroupToSecurityGroup(ruleConverter);
+
+      return parser;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermissionTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermissionTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermissionTest.java
new file mode 100644
index 0000000..0f5babb
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermissionTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.cloudstack.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.cloudstack.domain.IngressRule;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+
+/**
+ * Tests for the function for transforming a cloudstack specific IngressRule into a generic
+ * IpPermission object.
+ * 
+ * @author Andrew Bayer
+ */
+public class IngressRuleToIpPermissionTest {
+
+   @Test
+   public void testApplyWithTCP() {
+      IngressRule ruleToConvert = IngressRule.builder()
+         .id("some-id")
+         .account("some-account")
+         .securityGroupName("some-group-name")
+         .protocol(IpProtocol.TCP.toString())
+         .startPort(10)
+         .endPort(20)
+         .CIDR("0.0.0.0/0")
+         .build();
+
+      IngressRuleToIpPermission converter = new IngressRuleToIpPermission();
+
+      IpPermission convertedPerm = converter.apply(ruleToConvert);
+
+      assertEquals(convertedPerm.getIpProtocol(), IpProtocol.fromValue(ruleToConvert.getProtocol()));
+      assertEquals(convertedPerm.getFromPort(), ruleToConvert.getStartPort());
+      assertEquals(convertedPerm.getToPort(), ruleToConvert.getEndPort());
+      assertEquals(convertedPerm.getCidrBlocks(), ImmutableSet.of("0.0.0.0/0"));
+      assertTrue(convertedPerm.getTenantIdGroupNamePairs().size() == 0);
+      assertTrue(convertedPerm.getGroupIds().size() == 0);
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/clojure/org/jclouds/ec2/security_group2.clj
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/clojure/org/jclouds/ec2/security_group2.clj b/apis/ec2/src/main/clojure/org/jclouds/ec2/security_group2.clj
index bc000a2..a6bc49c 100644
--- a/apis/ec2/src/main/clojure/org/jclouds/ec2/security_group2.clj
+++ b/apis/ec2/src/main/clojure/org/jclouds/ec2/security_group2.clj
@@ -21,9 +21,9 @@
   org.jclouds.ec2.security-group2
   (:require (org.jclouds [compute2 :as compute])
     [org.jclouds.ec2.ebs2 :as ebs])
-  (:import org.jclouds.ec2.domain.IpProtocol
-           org.jclouds.ec2.domain.SecurityGroup
-           org.jclouds.ec2.services.SecurityGroupClient))
+  (:import org.jclouds.ec2.domain.SecurityGroup
+           org.jclouds.ec2.services.SecurityGroupClient
+           org.jclouds.net.domain.IpProtocol))
 
 (defn #^SecurityGroupClient
   sg-service

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionToIndexedFormParams.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionToIndexedFormParams.java b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionToIndexedFormParams.java
index f90e938..403f592 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionToIndexedFormParams.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionToIndexedFormParams.java
@@ -19,9 +19,9 @@ package org.jclouds.ec2.binders;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import org.jclouds.ec2.domain.IpPermission;
 import org.jclouds.ec2.util.IpPermissions;
 import org.jclouds.http.HttpRequest;
+import org.jclouds.net.domain.IpPermission;
 import org.jclouds.rest.Binder;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionsToIndexedFormParams.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionsToIndexedFormParams.java b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionsToIndexedFormParams.java
index d9260f9..4bead83 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionsToIndexedFormParams.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionsToIndexedFormParams.java
@@ -19,9 +19,9 @@ package org.jclouds.ec2.binders;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import org.jclouds.ec2.domain.IpPermission;
 import org.jclouds.ec2.util.IpPermissions;
 import org.jclouds.http.HttpRequest;
+import org.jclouds.net.domain.IpPermission;
 import org.jclouds.rest.Binder;
 
 import com.google.common.collect.ImmutableMultimap;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java
index 9e6949c..c62a259 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java
@@ -55,6 +55,7 @@ import org.jclouds.compute.domain.NodeMetadataBuilder;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.functions.GroupNamingConvention;
 import org.jclouds.compute.functions.GroupNamingConvention.Factory;
 import org.jclouds.compute.internal.BaseComputeService;
@@ -128,12 +129,13 @@ public class EC2ComputeService extends BaseComputeService {
             ConcurrentMap<RegionAndName, KeyPair> credentialsMap,
             @Named("SECURITY") LoadingCache<RegionAndName, String> securityGroupMap,
             Optional<ImageExtension> imageExtension, GroupNamingConvention.Factory namingConvention,
-            @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames) {
+            @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames,
+            Optional<SecurityGroupExtension> securityGroupExtension) {
       super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
                getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
                startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
                nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
-               persistNodeCredentials, timeouts, userExecutor, imageExtension);
+               persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension);
       this.client = client;
       this.credentialsMap = credentialsMap;
       this.securityGroupMap = securityGroupMap;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java
index 5e209c9..2a1b495 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java
@@ -31,6 +31,7 @@ import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.config.BaseComputeServiceContextModule;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.ec2.compute.EC2ComputeService;
 import org.jclouds.ec2.compute.domain.RegionAndName;
 import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
@@ -137,5 +138,9 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod
       return Optional.of(i.getInstance(ImageExtension.class));
    }
 
+   @Override
+   protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
+      return Optional.of(i.getInstance(SecurityGroupExtension.class));
+   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
index 06b524f..c93baee 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
@@ -29,18 +29,22 @@ import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeMetadata.Status;
+import org.jclouds.compute.domain.SecurityGroup;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.ec2.compute.EC2ComputeService;
 import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey;
 import org.jclouds.ec2.compute.domain.RegionAndName;
 import org.jclouds.ec2.compute.extensions.EC2ImageExtension;
+import org.jclouds.ec2.compute.extensions.EC2SecurityGroupExtension;
 import org.jclouds.ec2.compute.functions.AddElasticIpsToNodemetadata;
 import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair;
 import org.jclouds.ec2.compute.functions.CredentialsForInstance;
 import org.jclouds.ec2.compute.functions.EC2ImageParser;
+import org.jclouds.ec2.compute.functions.EC2SecurityGroupToSecurityGroup;
 import org.jclouds.ec2.compute.functions.PasswordCredentialsFromWindowsInstance;
 import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata;
 import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData;
@@ -129,8 +133,12 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule {
       }).to(PasswordCredentialsFromWindowsInstance.class);
       bind(new TypeLiteral<Function<org.jclouds.ec2.domain.Image, Image>>() {
       }).to(EC2ImageParser.class);
+      bind(new TypeLiteral<Function<org.jclouds.ec2.domain.SecurityGroup, SecurityGroup>>() {
+      }).to(EC2SecurityGroupToSecurityGroup.class);
       bind(new TypeLiteral<ImageExtension>() {
       }).to(EC2ImageExtension.class);
+      bind(new TypeLiteral<SecurityGroupExtension>() {
+      }).to(EC2SecurityGroupExtension.class);
    }
 
    /**

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtension.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtension.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtension.java
new file mode 100644
index 0000000..4b09d56
--- /dev/null
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtension.java
@@ -0,0 +1,384 @@
+/*
+ * 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.ec2.compute.extensions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.base.Predicates.notNull;
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.collect.Iterables.toArray;
+import static com.google.common.collect.Iterables.transform;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+import org.jclouds.Constants;
+import org.jclouds.aws.util.AWSUtils;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.functions.GroupNamingConvention.Factory;
+import org.jclouds.domain.Location;
+import org.jclouds.ec2.EC2Client;
+import org.jclouds.ec2.compute.domain.RegionAndName;
+import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
+import org.jclouds.ec2.domain.RunningInstance;
+import org.jclouds.ec2.domain.UserIdGroupPair;
+import org.jclouds.location.Region;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+
+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.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+
+/**
+ * An extension to compute service to allow for the manipulation of {@link SecurityGroup}s. Implementation
+ * is optional by providers.
+ * 
+ * @author Andrew Bayer
+ */
+public class EC2SecurityGroupExtension implements SecurityGroupExtension {
+
+   protected final EC2Client client;
+   protected final ListeningExecutorService userExecutor;
+   protected final Supplier<Set<String>> regions;
+   protected final Function<org.jclouds.ec2.domain.SecurityGroup, SecurityGroup> groupConverter;
+   protected final Supplier<Set<? extends Location>> locations;
+   protected final LoadingCache<RegionAndName, String> groupCreator;
+   protected final Factory namingConvention;
+
+   @Inject
+   public EC2SecurityGroupExtension(EC2Client client,
+                                    @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
+                                    @Region Supplier<Set<String>> regions,
+                                    Function<org.jclouds.ec2.domain.SecurityGroup, SecurityGroup> groupConverter,
+                                    @Memoized Supplier<Set<? extends Location>> locations,
+                                    @Named("SECURITY") LoadingCache<RegionAndName, String> groupCreator,
+                                    GroupNamingConvention.Factory namingConvention) {
+                                    
+      this.client = checkNotNull(client, "client");
+      this.userExecutor = checkNotNull(userExecutor, "userExecutor");
+      this.regions = checkNotNull(regions, "regions");
+      this.groupConverter = checkNotNull(groupConverter, "groupConverter");
+      this.locations = checkNotNull(locations, "locations");
+      this.groupCreator = checkNotNull(groupCreator, "groupCreator");
+      this.namingConvention = checkNotNull(namingConvention, "namingConvention");
+   }
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroups() {
+      Iterable<? extends org.jclouds.ec2.domain.SecurityGroup> rawGroups = pollSecurityGroups();
+      Iterable<SecurityGroup> groups = transform(filter(rawGroups, notNull()),
+                                                 groupConverter);
+      return ImmutableSet.copyOf(groups);
+   }
+
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroupsInLocation(final Location location) {
+      String region = AWSUtils.getRegionFromLocationOrNull(location);
+      if (region == null) {
+         return ImmutableSet.of();
+      }
+      return listSecurityGroupsInLocation(region);
+   }
+
+   public Set<SecurityGroup> listSecurityGroupsInLocation(String region) {
+      Iterable<? extends org.jclouds.ec2.domain.SecurityGroup> rawGroups = pollSecurityGroupsByRegion(region);
+      Iterable<SecurityGroup> groups = transform(filter(rawGroups, notNull()),
+                                                 groupConverter);
+      return ImmutableSet.copyOf(groups);
+   }
+   
+   @Override
+   public Set<SecurityGroup> listSecurityGroupsForNode(String id) {
+      checkNotNull(id, "id");
+      String[] parts = AWSUtils.parseHandle(id);
+      String region = parts[0];
+      String instanceId = parts[1];
+      
+      RunningInstance instance = getOnlyElement(Iterables.concat(client.getInstanceServices().describeInstancesInRegion(region, instanceId)));
+
+      if (instance == null) {
+         return ImmutableSet.of();
+      }
+      
+      Set<String> groupNames = instance.getGroupNames();
+      Set<? extends org.jclouds.ec2.domain.SecurityGroup> rawGroups =
+         client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, Iterables.toArray(groupNames, String.class));
+      
+      return ImmutableSet.copyOf(transform(filter(rawGroups, notNull()), groupConverter));
+   }
+
+   @Override
+   public SecurityGroup getSecurityGroupById(String id) {
+      checkNotNull(id, "id");
+      String[] parts = AWSUtils.parseHandle(id);
+      String region = parts[0];
+      String groupId = parts[1];
+
+      Set<? extends org.jclouds.ec2.domain.SecurityGroup> rawGroups =
+         client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, groupId);
+      
+      return getOnlyElement(transform(filter(rawGroups, notNull()), groupConverter));
+   }
+
+   @Override
+   public SecurityGroup createSecurityGroup(String name, Location location) {
+      String region = AWSUtils.getRegionFromLocationOrNull(location);
+      if (region != null) {
+         return createSecurityGroup(name, region);
+      } else {
+         return null;
+      }
+   }
+   
+   public SecurityGroup createSecurityGroup(String name, String region) {
+      String markerGroup = namingConvention.create().sharedNameForGroup(name);
+      RegionNameAndIngressRules regionAndName = new RegionNameAndIngressRules(region, markerGroup, new int[] {},
+                                                                              false);
+
+      groupCreator.getUnchecked(regionAndName);
+
+      return getSecurityGroupById(regionAndName.slashEncode());
+   }
+
+   @Override
+   public boolean removeSecurityGroup(String id) {
+      checkNotNull(id, "id");
+      String[] parts = AWSUtils.parseHandle(id);
+      String region = parts[0];
+      String groupName = parts[1];
+      
+      if (client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, groupName).size() > 0) {
+         client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, groupName);
+         // TODO: test this clear happens
+         groupCreator.invalidate(new RegionNameAndIngressRules(region, groupName, null, false));
+         return true;
+      }
+
+      return false;
+   }
+
+
+   @Override
+   public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) {
+      String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
+      String name = group.getName();
+
+      if (ipPermission.getCidrBlocks().size() > 0) {
+         for (String cidr : ipPermission.getCidrBlocks()) {
+            client.getSecurityGroupServices().
+               authorizeSecurityGroupIngressInRegion(region,
+                                                     name,
+                                                     ipPermission.getIpProtocol(),
+                                                     ipPermission.getFromPort(),
+                                                     ipPermission.getToPort(),
+                                                     cidr);
+         }
+      }
+
+      if (ipPermission.getTenantIdGroupNamePairs().size() > 0) {
+         for (String userId : ipPermission.getTenantIdGroupNamePairs().keySet()) {
+            for (String groupName : ipPermission.getTenantIdGroupNamePairs().get(userId)) {
+               client.getSecurityGroupServices().
+                  authorizeSecurityGroupIngressInRegion(region,
+                                                        name,
+                                                        new UserIdGroupPair(userId, groupName));
+            }
+         }
+      }
+      System.out.println("group: " + group);
+      return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
+   }
+
+   @Override
+   public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                        Multimap<String, String> tenantIdGroupNamePairs,
+                                        Iterable<String> ipRanges,
+                                        Iterable<String> groupIds, SecurityGroup group) {
+      String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
+      String name = group.getName();
+
+      if (Iterables.size(ipRanges) > 0) {
+         for (String cidr : ipRanges) {
+            client.getSecurityGroupServices().
+               authorizeSecurityGroupIngressInRegion(region,
+                                                     name,
+                                                     protocol,
+                                                     startPort,
+                                                     endPort,
+                                                     cidr);
+         }
+      }
+
+      if (tenantIdGroupNamePairs.size() > 0) {
+         for (String userId : tenantIdGroupNamePairs.keySet()) {
+            for (String groupName : tenantIdGroupNamePairs.get(userId)) {
+               client.getSecurityGroupServices().
+                  authorizeSecurityGroupIngressInRegion(region,
+                                                        name,
+                                                        new UserIdGroupPair(userId, groupName));
+            }
+         }
+      }
+      
+      return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
+   }
+      
+   @Override
+   public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) {
+      String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
+      String name = group.getName();
+
+      if (ipPermission.getCidrBlocks().size() > 0) {
+         for (String cidr : ipPermission.getCidrBlocks()) {
+            client.getSecurityGroupServices().
+               revokeSecurityGroupIngressInRegion(region,
+                                                  name,
+                                                  ipPermission.getIpProtocol(),
+                                                  ipPermission.getFromPort(),
+                                                  ipPermission.getToPort(),
+                                                  cidr);
+         }
+      }
+
+      if (ipPermission.getTenantIdGroupNamePairs().size() > 0) {
+         for (String userId : ipPermission.getTenantIdGroupNamePairs().keySet()) {
+            for (String groupName : ipPermission.getTenantIdGroupNamePairs().get(userId)) {
+               client.getSecurityGroupServices().
+                  revokeSecurityGroupIngressInRegion(region,
+                                                     name,
+                                                     new UserIdGroupPair(userId, groupName));
+            }
+         }
+      }
+
+      return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
+   }
+
+   @Override
+   public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                           Multimap<String, String> tenantIdGroupNamePairs,
+                                           Iterable<String> ipRanges,
+                                           Iterable<String> groupIds, SecurityGroup group) {
+      String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
+      String name = group.getName();
+
+      if (Iterables.size(ipRanges) > 0) {
+         for (String cidr : ipRanges) {
+            client.getSecurityGroupServices().
+               revokeSecurityGroupIngressInRegion(region,
+                                                  name,
+                                                  protocol,
+                                                  startPort,
+                                                  endPort,
+                                                  cidr);
+         }
+      }
+
+      if (tenantIdGroupNamePairs.size() > 0) {
+         for (String userId : tenantIdGroupNamePairs.keySet()) {
+            for (String groupName : tenantIdGroupNamePairs.get(userId)) {
+               client.getSecurityGroupServices().
+                  revokeSecurityGroupIngressInRegion(region,
+                                                     name,
+                                                     new UserIdGroupPair(userId, groupName));
+            }
+         }
+      }
+      
+      return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
+   }
+
+   @Override
+   public boolean supportsTenantIdGroupNamePairs() {
+      return true;
+   }
+
+   @Override
+   public boolean supportsGroupIds() {
+      return false;
+   }
+
+   @Override
+   public boolean supportsPortRangesForGroups() {
+      return false;
+   }
+
+   protected Iterable<? extends org.jclouds.ec2.domain.SecurityGroup> pollSecurityGroups() {
+      Iterable<? extends Set<? extends org.jclouds.ec2.domain.SecurityGroup>> groups
+         = transform(regions.get(), allSecurityGroupsInRegion());
+      
+      return concat(groups);
+   }
+
+   
+   protected Iterable<? extends org.jclouds.ec2.domain.SecurityGroup> pollSecurityGroupsByRegion(String region) {
+      return allSecurityGroupsInRegion().apply(region);
+   }
+
+   protected Function<String, Set<? extends org.jclouds.ec2.domain.SecurityGroup>> allSecurityGroupsInRegion() {
+      return new Function<String, Set<? extends org.jclouds.ec2.domain.SecurityGroup>>() {
+         
+         @Override
+         public Set<? extends org.jclouds.ec2.domain.SecurityGroup> apply(String from) {
+            return client.getSecurityGroupServices().describeSecurityGroupsInRegion(from);
+         }
+         
+      };
+   }
+
+   protected Location findLocationWithId(final String locationId) {
+      if (locationId == null)
+         return null;
+      try {
+         Location location = Iterables.find(locations.get(), new Predicate<Location>() {
+
+            @Override
+            public boolean apply(Location input) {
+               return input.getId().equals(locationId);
+            }
+
+         });
+         return location;
+
+      } catch (NoSuchElementException e) {
+         return null;
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroup.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroup.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroup.java
new file mode 100644
index 0000000..7e0ff66
--- /dev/null
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroup.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ec2.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.SecurityGroup;
+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 com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+
+
+/**
+ * A function for transforming an EC2-specific SecurityGroup into a generic
+ * SecurityGroup object.
+ * 
+ * @author Andrew Bayer
+ */
+@Singleton
+public class EC2SecurityGroupToSecurityGroup implements Function<org.jclouds.ec2.domain.SecurityGroup, SecurityGroup> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+   
+   protected final Supplier<Set<? extends Location>> locations;
+
+   @Inject
+   public EC2SecurityGroupToSecurityGroup(@Memoized Supplier<Set<? extends Location>> locations) {
+      this.locations = checkNotNull(locations, "locations");
+   }
+
+   @Override
+   public SecurityGroup apply(org.jclouds.ec2.domain.SecurityGroup group) {
+      SecurityGroupBuilder builder = new SecurityGroupBuilder();
+      Location location = findLocationWithId(group.getRegion());
+      builder.location(location);
+      builder.id(group.getName());
+      builder.providerId(group.getId());
+      builder.name(group.getName());
+      builder.ipPermissions(group);
+      builder.ownerId(group.getOwnerId());
+      
+      return builder.build();
+   }
+
+   private Location findLocationWithId(final String locationId) {
+      if (locationId == null)
+         return null;
+      try {
+         Location location = Iterables.find(locations.get(), new Predicate<Location>() {
+
+            @Override
+            public boolean apply(Location input) {
+               return input.getId().equals(locationId);
+            }
+
+         });
+         return location;
+
+      } catch (NoSuchElementException e) {
+         logger.debug("couldn't match instance location %s in: %s", locationId, locations.get());
+         return null;
+      }
+   }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java
index 0d54164..de3b906 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java
@@ -27,10 +27,10 @@ import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.ec2.EC2Client;
 import org.jclouds.ec2.compute.domain.RegionAndName;
 import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.UserIdGroupPair;
 import org.jclouds.ec2.services.SecurityGroupClient;
 import org.jclouds.logging.Logger;
+import org.jclouds.net.domain.IpProtocol;
 
 import com.google.common.base.Predicate;
 import com.google.common.cache.CacheLoader;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java
deleted file mode 100644
index 91c75e7..0000000
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java
+++ /dev/null
@@ -1,189 +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.ec2.domain;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Set;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-
-/**
- *
- * @see <a href=
- *      "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-ItemType-IpPermissionType.html"
- *      />
- * @author Adrian Cole
- */
-public class IpPermission {
-   public static Builder builder() {
-      return new Builder();
-   }
-
-   public static class Builder {
-      private int fromPort;
-      private int toPort;
-      private IpProtocol ipProtocol;
-      private Multimap<String, String> userIdGroupPairs = LinkedHashMultimap.create();
-      private Set<String> groupIds = Sets.newLinkedHashSet();
-      private Set<String> ipRanges = Sets.newLinkedHashSet();
-
-      public Builder fromPort(int fromPort) {
-         this.fromPort = fromPort;
-         return this;
-      }
-
-      public Builder toPort(int toPort) {
-         this.toPort = toPort;
-         return this;
-      }
-
-      public Builder ipProtocol(IpProtocol ipProtocol) {
-         this.ipProtocol = checkNotNull(ipProtocol, "ipProtocol");
-         return this;
-      }
-
-      public Builder userIdGroupPair(String userId, String groupNameOrId) {
-         this.userIdGroupPairs.put(checkNotNull(userId, "userId"), checkNotNull(groupNameOrId, "groupNameOrId of %s", userId));
-         return this;
-      }
-
-      public Builder userIdGroupPairs(Multimap<String, String> userIdGroupPairs) {
-         this.userIdGroupPairs.putAll(checkNotNull(userIdGroupPairs, "userIdGroupPairs"));
-         return this;
-      }
-
-      public Builder ipRange(String ipRange) {
-         this.ipRanges.add(ipRange);
-         return this;
-      }
-
-      public Builder ipRanges(Iterable<String> ipRanges) {
-         Iterables.addAll(this.ipRanges, checkNotNull(ipRanges, "ipRanges"));
-         return this;
-      }
-
-      public Builder groupId(String groupId) {
-         this.groupIds.add(checkNotNull(groupId, "groupId"));
-         return this;
-      }
-
-      public Builder groupIds(Iterable<String> groupIds) {
-         Iterables.addAll(this.groupIds, checkNotNull(groupIds, "groupIds"));
-         return this;
-      }
-
-      public IpPermission build() {
-         return new IpPermission(ipProtocol, fromPort, toPort, userIdGroupPairs, groupIds, ipRanges);
-      }
-   }
-
-   private final int fromPort;
-   private final int toPort;
-   private final Multimap<String, String> userIdGroupPairs;
-   private final Set<String> groupIds;
-   private final IpProtocol ipProtocol;
-   private final Set<String> ipRanges;
-
-   public IpPermission(IpProtocol ipProtocol, int fromPort, int toPort, Multimap<String, String> userIdGroupPairs,
-         Iterable<String> groupIds, Iterable<String> ipRanges) {
-      this.fromPort = fromPort;
-      this.toPort = toPort;
-      this.userIdGroupPairs = ImmutableMultimap.copyOf(checkNotNull(userIdGroupPairs, "userIdGroupPairs"));
-      this.ipProtocol = checkNotNull(ipProtocol, "ipProtocol");
-      this.groupIds = ImmutableSet.copyOf(checkNotNull(groupIds, "groupIds"));
-      this.ipRanges = ImmutableSet.copyOf(checkNotNull(ipRanges, "ipRanges"));
-   }
-
-   /**
-    * Start of port range for the TCP and UDP protocols, or an ICMP type number.
-    * An ICMP type number of -1 indicates a wildcard (i.e., any ICMP type
-    * number).
-    */
-   public int getFromPort() {
-      return fromPort;
-   }
-
-   /**
-    * End of port range for the TCP and UDP protocols, or an ICMP code. An ICMP
-    * code of -1 indicates a wildcard (i.e., any ICMP code).
-    */
-   public int getToPort() {
-      return toPort;
-   }
-
-   /**
-    * List of security group and user ID pairs.
-    */
-   public Multimap<String, String> getUserIdGroupPairs() {
-      return userIdGroupPairs;
-   }
-
-   /**
-    * List of security group Ids
-    */
-   public Set<String> getGroupIds() {
-      return groupIds;
-   }
-
-   /**
-    * IP protocol
-    */
-   public IpProtocol getIpProtocol() {
-      return ipProtocol;
-   }
-
-   /**
-    * IP ranges.
-    */
-   public Set<String> getIpRanges() {
-      return ipRanges;
-   }
-
-   @Override
-   public int hashCode() {
-      return Objects.hashCode(fromPort, toPort, groupIds, ipProtocol, ipRanges, userIdGroupPairs);
-   }
-
-   @Override
-   public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null || getClass() != obj.getClass())
-         return false;
-      IpPermission that = IpPermission.class.cast(obj);
-      return Objects.equal(this.fromPort, that.fromPort) && Objects.equal(this.toPort, that.toPort)
-            && Objects.equal(this.groupIds, that.groupIds) && Objects.equal(this.ipProtocol, that.ipProtocol)
-            && Objects.equal(this.ipRanges, that.ipRanges)
-            && Objects.equal(this.userIdGroupPairs, that.userIdGroupPairs);
-   }
-
-   @Override
-   public String toString() {
-      return Objects.toStringHelper(this).omitNullValues().add("fromPort", fromPort == -1 ? null : fromPort)
-            .add("toPort", toPort == -1 ? null : toPort).add("groupIds", groupIds.size() == 0 ? null : groupIds)
-            .add("ipProtocol", ipProtocol).add("ipRanges", ipRanges.size() == 0 ? null : ipRanges)
-            .add("userIdGroupPairs", userIdGroupPairs.size() == 0 ? null : userIdGroupPairs).toString();
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpProtocol.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpProtocol.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpProtocol.java
deleted file mode 100644
index 41e771c..0000000
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpProtocol.java
+++ /dev/null
@@ -1,48 +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.ec2.domain;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * @author Adrian Cole
- * 
- */
-public enum IpProtocol {
-
-   TCP, UDP, ICMP, ALL, UNRECOGNIZED;
-
-   public String value() {
-      return this == ALL ? "-1" : name().toLowerCase();
-   }
-
-   @Override
-   public String toString() {
-      return value();
-   }
-
-   public static IpProtocol fromValue(String protocol) {
-      try {
-         if (protocol.equalsIgnoreCase("-1"))
-            return ALL;
-         return valueOf(checkNotNull(protocol, "protocol").toUpperCase());
-      } catch (IllegalArgumentException e) {
-         return UNRECOGNIZED;
-      }
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/domain/SecurityGroup.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/SecurityGroup.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/SecurityGroup.java
index ca5cdbb..caccebc 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/SecurityGroup.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/SecurityGroup.java
@@ -21,6 +21,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import java.util.Set;
 
 import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.net.domain.IpPermission;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java
index 4d074ee..0de10f9 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java
@@ -30,12 +30,12 @@ import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
 import org.jclouds.aws.filters.FormSigner;
 import org.jclouds.ec2.binders.BindGroupNamesToIndexedFormParams;
 import org.jclouds.ec2.binders.BindUserIdGroupPairToSourceSecurityGroupFormParams;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.domain.UserIdGroupPair;
 import org.jclouds.ec2.xml.DescribeSecurityGroupsResponseHandler;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.EndpointParam;
 import org.jclouds.rest.annotations.Fallback;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupClient.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupClient.java
index 3874bfb..23c8272 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupClient.java
@@ -17,10 +17,10 @@
 package org.jclouds.ec2.services;
 
 import java.util.Set;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.SecurityGroup;
 import org.jclouds.ec2.domain.UserIdGroupPair;
 import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.net.domain.IpProtocol;
 
 /**
  * Provides access to EC2 via their REST API.

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/util/IpPermissions.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/util/IpPermissions.java b/apis/ec2/src/main/java/org/jclouds/ec2/util/IpPermissions.java
index ac32cda..9c938f0 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/util/IpPermissions.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/util/IpPermissions.java
@@ -21,8 +21,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import org.jclouds.ec2.domain.IpPermission;
-import org.jclouds.ec2.domain.IpProtocol;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.util.Maps2;
 
 import com.google.common.annotations.Beta;
@@ -58,7 +58,7 @@ public class IpPermissions extends IpPermission {
       headers.put("IpPermissions.%d.ToPort", permission.getToPort() + "");
       String prefix = "IpPermissions.%d.IpRanges.";
       int i = 0;
-      for (String cidrIp : checkNotNull(permission.getIpRanges(), "cidrIps")) {
+      for (String cidrIp : checkNotNull(permission.getCidrBlocks(), "cidrIps")) {
          headers.put(prefix + i++ + ".CidrIp", cidrIp);
       }
       prefix = "IpPermissions.%d.Groups.";
@@ -68,15 +68,15 @@ public class IpPermissions extends IpPermission {
       }
       prefix = "IpPermissions.%d.Groups.";
       i = 0;
-      for (Entry<String, String> userIdGroupNamePair : checkNotNull(permission.getUserIdGroupPairs(),
-            "userIdGroupNamePairs").entries()) {
-         headers.put(prefix + i + ".UserId", userIdGroupNamePair.getKey());
-         headers.put(prefix + i + ".GroupName", userIdGroupNamePair.getValue());
+      for (Entry<String, String> tenantIdGroupNamePair : checkNotNull(permission.getTenantIdGroupNamePairs(),
+            "tenantIdGroupNamePairs").entries()) {
+         headers.put(prefix + i + ".UserId", tenantIdGroupNamePair.getKey());
+         headers.put(prefix + i + ".GroupName", tenantIdGroupNamePair.getValue());
          i++;
       }
       prefix = "IpPermissions.%d.IpRanges.";
       i = 0;
-      for (String cidrIp : checkNotNull(permission.getIpRanges(), "cidrIps")) {
+      for (String cidrIp : checkNotNull(permission.getCidrBlocks(), "cidrIps")) {
          headers.put(prefix + i++ + ".CidrIp", cidrIp);
       }
       return Multimaps.forMap(Maps2.transformKeys(headers, new Function<String, String>() {
@@ -165,7 +165,7 @@ public class IpPermissions extends IpPermission {
       }
 
       public IpPermissions toVPCSecurityGroups(Iterable<String> groupIds) {
-         return new IpPermissions(getIpProtocol(), getFromPort(), getToPort(), getUserIdGroupPairs(), groupIds,
+         return new IpPermissions(getIpProtocol(), getFromPort(), getToPort(), getTenantIdGroupNamePairs(), groupIds,
                ImmutableSet.<String> of());
       }
    }
@@ -189,8 +189,8 @@ public class IpPermissions extends IpPermission {
                checkNotNull(groupName, "groupName")));
       }
 
-      public IpPermissions toEC2SecurityGroups(Multimap<String, String> userIdGroupNamePairs) {
-         return new IpPermissions(getIpProtocol(), getFromPort(), getToPort(), userIdGroupNamePairs, getGroupIds(),
+      public IpPermissions toEC2SecurityGroups(Multimap<String, String> tenantIdGroupNamePairs) {
+         return new IpPermissions(getIpProtocol(), getFromPort(), getToPort(), tenantIdGroupNamePairs, getGroupIds(),
                ImmutableSet.<String> of());
       }
    }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/main/java/org/jclouds/ec2/xml/IpPermissionHandler.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/IpPermissionHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/IpPermissionHandler.java
index 5b4dc6d..980ef35 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/IpPermissionHandler.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/IpPermissionHandler.java
@@ -19,9 +19,9 @@ package org.jclouds.ec2.xml;
 import static org.jclouds.util.SaxUtils.currentOrNull;
 import static org.jclouds.util.SaxUtils.equalsOrSuffix;
 
-import org.jclouds.ec2.domain.IpPermission;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.http.functions.ParseSax;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
 import org.xml.sax.SAXException;
 
 /**
@@ -66,14 +66,14 @@ public class IpPermissionHandler extends ParseSax.HandlerForGeneratedRequestWith
          // EC2)
          builder.toPort(Integer.parseInt(currentOrNegative(currentText)));
       } else if (equalsOrSuffix(qName, "cidrIp")) {
-         builder.ipRange(currentOrNull(currentText));
+         builder.cidrBlock(currentOrNull(currentText));
       } else if (equalsOrSuffix(qName, "userId")) {
          this.userId = currentOrNull(currentText);
       } else if (equalsOrSuffix(qName, "groupName") || equalsOrSuffix(qName, "groupId")) {
          this.groupId = currentOrNull(currentText);
       } else if (equalsOrSuffix(qName, "item")) {
          if (userId != null && groupId != null)
-            builder.userIdGroupPair(userId, groupId);
+            builder.tenantIdGroupNamePair(userId, groupId);
          userId = groupId = null;
       }
       currentText = new StringBuilder();

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java
index 584e6d9..1fc8de9 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java
@@ -40,7 +40,6 @@ import org.jclouds.ec2.domain.BlockDevice;
 import org.jclouds.ec2.domain.Image.EbsBlockDevice;
 import org.jclouds.ec2.domain.InstanceState;
 import org.jclouds.ec2.domain.InstanceType;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.KeyPair;
 import org.jclouds.ec2.domain.PublicIpInstanceIdPair;
 import org.jclouds.ec2.domain.Reservation;
@@ -49,6 +48,7 @@ import org.jclouds.ec2.domain.Volume.InstanceInitiatedShutdownBehavior;
 import org.jclouds.ec2.predicates.InstanceHasIpAddress;
 import org.jclouds.ec2.predicates.InstanceStateRunning;
 import org.jclouds.http.HttpResponseException;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.predicates.SocketOpen;
 import org.jclouds.scriptbuilder.ScriptBuilder;
 import org.jclouds.scriptbuilder.domain.OsFamily;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java
index 67af652..3b8bd75 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java
@@ -42,7 +42,6 @@ import org.jclouds.ec2.domain.Image.Architecture;
 import org.jclouds.ec2.domain.Image.ImageType;
 import org.jclouds.ec2.domain.InstanceState;
 import org.jclouds.ec2.domain.InstanceType;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.KeyPair;
 import org.jclouds.ec2.domain.Reservation;
 import org.jclouds.ec2.domain.RootDeviceType;
@@ -58,6 +57,7 @@ import org.jclouds.ec2.predicates.VolumeAttached;
 import org.jclouds.ec2.predicates.VolumeAvailable;
 import org.jclouds.http.HttpResponseException;
 import org.jclouds.io.Payloads;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.predicates.SocketOpen;
 import org.jclouds.scriptbuilder.InitScript;
 import org.jclouds.scriptbuilder.domain.OsFamily;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/5f524ee6/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
index 67db38b..6334c9a 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
@@ -39,7 +39,6 @@ import org.jclouds.ec2.EC2ApiMetadata;
 import org.jclouds.ec2.EC2Client;
 import org.jclouds.ec2.compute.options.EC2TemplateOptions;
 import org.jclouds.ec2.domain.BlockDevice;
-import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.KeyPair;
 import org.jclouds.ec2.domain.PublicIpInstanceIdPair;
 import org.jclouds.ec2.domain.RunningInstance;
@@ -51,6 +50,7 @@ import org.jclouds.ec2.services.ElasticBlockStoreClient;
 import org.jclouds.ec2.services.InstanceClient;
 import org.jclouds.ec2.services.KeyPairClient;
 import org.jclouds.ec2.services.SecurityGroupClient;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.scriptbuilder.domain.Statements;
 import org.jclouds.sshj.config.SshjSshClientModule;
 import org.jclouds.util.InetAddresses2;