You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by za...@apache.org on 2014/08/30 03:32:27 UTC

[2/2] git commit: Adds support for the Security Group extension to neutron

Adds support for the Security Group extension to neutron


Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/commit/67b9f472
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/tree/67b9f472
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/diff/67b9f472

Branch: refs/heads/master
Commit: 67b9f47210156ca26139dfadad0eaa2a6b2fd8b8
Parents: de8348d
Author: Zack Shoylev <za...@rackspace.com>
Authored: Fri Aug 15 16:17:39 2014 -0500
Committer: Zack Shoylev <za...@rackspace.com>
Committed: Fri Aug 29 20:29:59 2014 -0500

----------------------------------------------------------------------
 .../openstack/neutron/v2/NeutronApi.java        |  10 +
 .../neutron/v2/domain/AddressPair.java          |  11 +-
 .../openstack/neutron/v2/domain/Rule.java       | 375 +++++++++++
 .../neutron/v2/domain/RuleDirection.java        |  61 ++
 .../neutron/v2/domain/RuleEthertype.java        |  61 ++
 .../neutron/v2/domain/RuleProtocol.java         |  65 ++
 .../openstack/neutron/v2/domain/Rules.java      |  35 +
 .../neutron/v2/domain/SecurityGroup.java        | 219 +++++++
 .../neutron/v2/domain/SecurityGroups.java       |  36 +
 .../neutron/v2/extensions/SecurityGroupApi.java | 186 ++++++
 .../v2/fallbacks/EmptyRulesFallback.java        |  45 ++
 .../fallbacks/EmptySecurityGroupsFallback.java  |  45 ++
 .../neutron/v2/functions/ParseRules.java        |  38 ++
 .../v2/functions/ParseSecurityGroups.java       |  38 ++
 .../v2/functions/RulesToPagedIterable.java      |  66 ++
 .../SecurityGroupsToPagedIterable.java          |  66 ++
 .../v2/extensions/SecurityGroupApiLiveTest.java |  88 +++
 .../v2/extensions/SecurityGroupApiMockTest.java | 655 +++++++++++++++++++
 .../neutron/v2/features/PortApiMockTest.java    |   4 +-
 .../security_group_create_request.json          |   6 +
 .../security_group_create_response.json         |  34 +
 .../resources/security_group_get_response.json  |  58 ++
 .../resources/security_group_list_response.json | 116 ++++
 .../security_group_list_response_paged1.json    | 126 ++++
 .../security_group_list_response_paged2.json    | 122 ++++
 .../security_group_rule_create_request.json     |  11 +
 .../security_group_rule_create_response.json    |  15 +
 .../security_group_rule_get_response.json       |  14 +
 .../security_group_rule_list_response.json      |  52 ++
 ...ecurity_group_rule_list_response_paged1.json |  62 ++
 ...ecurity_group_rule_list_response_paged2.json |  58 ++
 31 files changed, 2766 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java
index 233c41b..358e38f 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java
@@ -25,6 +25,7 @@ import org.jclouds.Constants;
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpoint;
 import org.jclouds.openstack.neutron.v2.extensions.RouterApi;
+import org.jclouds.openstack.neutron.v2.extensions.SecurityGroupApi;
 import org.jclouds.openstack.neutron.v2.features.NetworkApi;
 import org.jclouds.openstack.neutron.v2.features.PortApi;
 import org.jclouds.openstack.neutron.v2.features.SubnetApi;
@@ -89,4 +90,13 @@ public interface NeutronApi extends Closeable {
    @Delegate
    Optional<? extends RouterApi> getRouterExtensionApi(@EndpointParam(parser = RegionToEndpoint.class) String region);
 
+   /**
+    * Provides access to SecurityGroup features.
+    *
+    * <h3>NOTE</h3>
+    * This API is an extension that may or may not be present in your OpenStack cloud. Use the Optional return type
+    * to determine if it is present.
+    */
+   @Delegate
+   Optional<SecurityGroupApi> getSecurityGroupApi(@EndpointParam(parser = RegionToEndpoint.class) String region);
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/AddressPair.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/AddressPair.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/AddressPair.java
index 4e317ce..a222908 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/AddressPair.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/AddressPair.java
@@ -101,20 +101,11 @@ public class AddressPair  {
     * In this case, both parameters are required.
     * @return the Builder for AddressPair
     */
-   public static Builder createOptions(String macAddress, String ipAddress) {
+   public static Builder builder(String macAddress, String ipAddress) {
       return new Builder(macAddress, ipAddress);
    }
 
    /**
-    * Returns a builder, but requires the user to specify any parameters required when updating a resource.
-    * In this case, there are none.
-    * @return the Builder for AddressPair
-    */
-   public static Builder updateOptions() {
-      return new Builder();
-   }
-
-   /**
     * Gets a Builder configured as this object.
     */
    public Builder toBuilder() {

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Rule.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Rule.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Rule.java
new file mode 100644
index 0000000..e3b7aca
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Rule.java
@@ -0,0 +1,375 @@
+/*
+ * 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.neutron.v2.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.beans.ConstructorProperties;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+/**
+ * Contains a mapping between a MAC address and an IP address.
+ */
+public class Rule {
+
+   private String id;
+   @Named("tenant_id")
+   private String tenantId;
+   private RuleDirection direction;
+   @Named("security_group_id")
+   private String securityGroupId;
+   private RuleEthertype ethertype;
+   @Named("port_range_min")
+   private Integer portRangeMin;
+   @Named("port_range_max")
+   private Integer portRangeMax;
+   private RuleProtocol protocol;
+   @Named("remote_group_id")
+   private String remoteGroupId;
+   @Named("remote_ip_prefix")
+   private String remoteIpPrefix;
+
+   @ConstructorProperties({"id", "tenant_id", "direction", "security_group_id", "ethertype", "port_range_min",
+         "port_range_max", "protocol", "remote_group_id", "remote_ip_prefix"})
+   protected Rule(String id, String tenantId, RuleDirection direction, String securityGroupId,
+         RuleEthertype ethertype, Integer portRangeMin, Integer portRangeMax,
+         RuleProtocol protocol, String remoteGroupId, String remoteIpPrefix) {
+      this.id = id;
+      this.tenantId = tenantId;
+      this.direction = direction;
+      this.securityGroupId = securityGroupId;
+      this.ethertype = ethertype;
+      this.portRangeMin = portRangeMin;
+      this.portRangeMax = portRangeMax;
+      this.protocol = protocol;
+      this.remoteGroupId = remoteGroupId;
+      this.remoteIpPrefix = remoteIpPrefix;
+   }
+
+   private Rule(Rule rule) {
+      this(rule.id,
+            rule.tenantId,
+            rule.direction,
+            rule.securityGroupId,
+            rule.ethertype,
+            rule.portRangeMin,
+            rule.portRangeMax,
+            rule.protocol,
+            rule.remoteGroupId,
+            rule.remoteIpPrefix
+      );
+   }
+
+   private Rule() {}
+
+   /**
+    * @return The identifier for this rule.
+    */
+   @Nullable
+   public String getId() {
+      return id;
+   }
+
+   /**
+    * @return The identifier of the tenant for this rule.
+    */
+   @Nullable
+   public String getTenantId() {
+      return tenantId;
+   }
+
+   /**
+    * @return The direction in which the security group rule is applied.
+    */
+   @Nullable
+   public RuleDirection getDirection() {
+      return direction;
+   }
+
+   /**
+    * @return The security group ID to associate with this security group rule.
+    */
+   @Nullable
+   public String getSecurityGroupId() {
+      return securityGroupId;
+   }
+
+   /**
+    * @return The internet protocol version type of this rule.
+    */
+   @Nullable
+   public RuleEthertype getEthertype() {
+      return ethertype;
+   }
+
+   /**
+    * @return The minimum port number in the range that is matched by the security group rule. If the protocol is TCP
+    * or UDP, this value must be less than or equal to the value of the port_range_max attribute. If the protocol is
+    * ICMP, this value must be an ICMP type.
+    */
+   @Nullable
+   public Integer getPortRangeMin() {
+      return portRangeMin;
+   }
+
+   /**
+    * @return The maximum port number in the range that is matched by the security group rule. The port_range_min
+    * attribute constrains the port_range_max attribute. If the protocol is ICMP, this value must be an ICMP type.
+    */
+   @Nullable
+   public Integer getPortRangeMax() {
+      return portRangeMax;
+   }
+
+   /**
+    * @return The protocol that is matched by the security group rule. Valid values are null, tcp, udp, and icmp.
+    */
+   @Nullable
+   public RuleProtocol getProtocol() {
+      return protocol;
+   }
+
+   /**
+    * @return The remote group ID to be associated with this security group rule.
+    */
+   @Nullable
+   public String getRemoteGroupId() {
+      return remoteGroupId;
+   }
+
+   /**
+    * @return The remote IP prefix to be associated with this security group rule. This attribute matches the specified
+    * IP prefix as the source IP address of the IP packet.
+    */
+   @Nullable
+   public String getRemoteIpPrefix() {
+      return remoteIpPrefix;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o)
+         return true;
+      if (o == null || getClass() != o.getClass())
+         return false;
+
+      Rule that = (Rule) o;
+
+      return Objects.equal(this.id, that.id) &&
+            Objects.equal(this.tenantId, that.tenantId) &&
+            Objects.equal(this.direction, that.direction) &&
+            Objects.equal(this.securityGroupId, that.securityGroupId) &&
+            Objects.equal(this.ethertype, that.ethertype) &&
+            Objects.equal(this.portRangeMin, that.portRangeMin) &&
+            Objects.equal(this.portRangeMax, that.portRangeMax) &&
+            Objects.equal(this.protocol, that.protocol) &&
+            Objects.equal(this.remoteGroupId, that.remoteGroupId) &&
+            Objects.equal(this.remoteIpPrefix, that.remoteIpPrefix);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(id, tenantId, direction, securityGroupId, ethertype, portRangeMin,
+            portRangeMax, protocol, remoteGroupId, remoteIpPrefix);
+   }
+
+   @Override
+   public String toString() {
+      return MoreObjects.toStringHelper(this)
+            .add("id", id)
+            .add("tenantId", tenantId)
+            .add("direction", direction)
+            .add("securityGroupId", securityGroupId)
+            .add("ethertype", ethertype)
+            .add("portRangeMin", portRangeMin)
+            .add("portRangeMax", portRangeMax)
+            .add("protocol", protocol)
+            .add("remoteGroupId", remoteGroupId)
+            .add("remoteIpPrefix", remoteIpPrefix)
+            .toString();
+   }
+
+
+   /*
+    * Methods to get the Create and Update builders follow
+    */
+
+   /**
+    * @return the Builder for creating a new SecurityGroupRule
+    */
+   public static CreateBuilder createOptions(RuleDirection direction, String securityGroupId) {
+      return new CreateBuilder(direction, securityGroupId);
+   }
+
+   public abstract static class Builder<ParameterizedBuilderType> {
+      // Keep track of the builder's state.
+      protected Rule rule;
+
+      private Builder() {
+         rule = new Rule();
+      }
+
+      protected abstract ParameterizedBuilderType self();
+
+      /**
+       * The tenant id for this rule. Usually can only be specified by administrators.
+       *
+       * @return the Builder.
+       * @see Rule#getTenantId()
+       */
+      public ParameterizedBuilderType tenantId(String tenantId) {
+         rule.tenantId = tenantId;
+         return self();
+      }
+
+      /**
+       * The direction in which the security group rule is applied.
+       *
+       * @return the Builder.
+       * @see Rule#getDirection()
+       */
+      public ParameterizedBuilderType direction(RuleDirection direction) {
+         rule.direction = direction;
+         return self();
+      }
+
+      /**
+       * The security group ID to associate with this security group rule.
+       *
+       * @return the Builder.
+       * @see Rule#getSecurityGroupId()
+       */
+      public ParameterizedBuilderType securityGroupId(String securityGroupId) {
+         rule.securityGroupId = securityGroupId;
+         return self();
+      }
+
+      /**
+       * The internet protocol version for this rule.
+       *
+       * @return the Builder.
+       * @see Rule#getEthertype()
+       */
+      public ParameterizedBuilderType ethertype(RuleEthertype ethertype) {
+         rule.ethertype = ethertype;
+         return self();
+      }
+
+      /**
+       * The minimum port number in the range that is matched by the security group rule.
+       *
+       * @return the Builder.
+       * @see Rule#getPortRangeMin()
+       */
+      public ParameterizedBuilderType portRangeMin(Integer portRangeMin) {
+         rule.portRangeMin = portRangeMin;
+         return self();
+      }
+
+      /**
+       * The maximum port number in the range that is matched by the security group rule.
+       *
+       * @return the Builder.
+       * @see Rule#getPortRangeMax()
+       */
+      public ParameterizedBuilderType portRangeMax(Integer portRangeMax) {
+         rule.portRangeMax = portRangeMax;
+         return self();
+      }
+
+      /**
+       * The protocol that is matched by the security group rule. Valid values are null, tcp, udp, and icmp.
+       *
+       * @return the Builder.
+       * @see Rule#getProtocol()
+       */
+      public ParameterizedBuilderType protocol(RuleProtocol protocol) {
+         rule.protocol = protocol;
+         return self();
+      }
+
+      /**
+       * The remote group ID to be associated with this security group rule. You can specify either remote_group_id or
+       * remote_ip_prefix in the request body.
+       *
+       * @return the Builder.
+       * @see Rule#getRemoteGroupId()
+       */
+      public ParameterizedBuilderType remoteGroupId(String remoteGroupId) {
+         rule.remoteGroupId = remoteGroupId;
+         return self();
+      }
+
+      /**
+       * The remote IP prefix to be associated with this security group rule. You can specify either remote_group_id
+       * or remote_ip_prefix in the request body. This attribute matches the specified IP prefix as the source IP
+       * address of the IP packet.
+       *
+       * @return the Builder.
+       * @see Rule#getRemoteIpPrefix()
+       */
+      public ParameterizedBuilderType remoteIpPrefix(String remoteIpPrefix) {
+         rule.remoteIpPrefix = remoteIpPrefix;
+         return self();
+      }
+   }
+
+   /**
+    * This is used to build a CreateOptions object.
+    */
+   public static class CreateBuilder extends Builder<CreateBuilder> {
+      /**
+       * Supply required properties for creating a Builder
+       */
+      private CreateBuilder(RuleDirection direction, String securityGroupId) {
+         rule.direction = direction;
+         rule.securityGroupId = securityGroupId;
+      }
+
+      /**
+       * @return a CreateOptions constructed with this Builder.
+       */
+      public CreateOptions build() {
+         return new CreateOptions(rule);
+      }
+
+      protected CreateBuilder self() {
+         return this;
+      }
+   }
+
+   /**
+    * Create and Update options - extend the domain class, passed to API update and create calls.
+    * Essentially the same as the domain class. Ensure validation and safe typing.
+    */
+   public static class CreateOptions extends Rule {
+      private CreateOptions(Rule rule) {
+         super(rule);
+         checkNotNull(rule.getDirection(), "direction should not be null");
+         checkNotNull(rule.getSecurityGroupId(), "security group id should not be null");
+         checkState(rule.getPortRangeMax()>= rule.getPortRangeMin(),
+               "port range max should be greater than or equal to port range min");
+         checkState(rule.getRemoteGroupId()==null || rule.getRemoteIpPrefix()==null,
+               "You can specify either remote_group_id or remote_ip_prefix in the request body.");
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleDirection.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleDirection.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleDirection.java
new file mode 100644
index 0000000..759bb8c
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleDirection.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.openstack.neutron.v2.domain;
+
+/**
+ * The direction in which the security group rule is applied.
+ */
+public enum RuleDirection {
+   /**
+    * For a compute instance, an ‘ingress’ security group rule matches traffic that is incoming (ingress) for that instance.
+    */
+   INGRESS("ingress"),
+   /**
+    * An ‘egress’ rule is applied to traffic leaving the instance.
+    */
+   EGRESS("egress"),
+   /**
+    * Used by jclouds when the service returns an unknown value other than null.
+    */
+   UNRECOGNIZED("unrecognized");
+
+   private String name;
+
+   private RuleDirection(String name) {
+      this.name = name;
+   }
+
+   public String toString() {
+      return name;
+   }
+
+   /**
+    * This provides GSON enum support in jclouds.
+    * */
+   public static RuleDirection fromValue(String name){
+      if (name != null) {
+         for (RuleDirection value : RuleDirection.values()) {
+            if (name.equalsIgnoreCase(value.name)) {
+               return value;
+            }
+         }
+         return UNRECOGNIZED;
+      }
+      return null;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleEthertype.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleEthertype.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleEthertype.java
new file mode 100644
index 0000000..1508070
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleEthertype.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.openstack.neutron.v2.domain;
+
+/**
+ * The direction in which the security group rule is applied.
+ */
+public enum RuleEthertype {
+   /**
+    * Internet Protocol version 4
+    */
+   IPV4("IPv4"),
+   /**
+    * Internet Protocol version 6
+    */
+   IPV6("IPv6"),
+   /**
+    * Used by jclouds when the service returns an unknown value other than null.
+    */
+   UNRECOGNIZED("unrecognized");
+
+   private String name;
+
+   private RuleEthertype(String name) {
+      this.name = name;
+   }
+
+   public String toString() {
+      return name;
+   }
+
+   /**
+    * This provides GSON enum support in jclouds.
+    * */
+   public static RuleEthertype fromValue(String name){
+      if (name != null) {
+         for (RuleEthertype value : RuleEthertype.values()) {
+            if (name.equalsIgnoreCase(value.name)) {
+               return value;
+            }
+         }
+         return UNRECOGNIZED;
+      }
+      return null;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleProtocol.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleProtocol.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleProtocol.java
new file mode 100644
index 0000000..9ad105f
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/RuleProtocol.java
@@ -0,0 +1,65 @@
+/*
+ * 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.neutron.v2.domain;
+
+/**
+ * The protocol that is matched by the security group rule. Valid values are null, tcp, udp, and icmp.
+ */
+public enum RuleProtocol {
+   /**
+    * Transmission Control Protocol
+    */
+   TCP("tcp"),
+   /**
+    * User Datagram Protocol
+    */
+   UDP("udp"),
+   /**
+    * Internet Control Message Protocol
+    */
+   ICMP("icmp"),
+   /**
+    * Used by jclouds when the service returns an unknown value other than null.
+    */
+   UNRECOGNIZED("unrecognized");
+
+   private String name;
+
+   private RuleProtocol(String name) {
+      this.name = name;
+   }
+
+   public String toString() {
+      return name;
+   }
+
+   /**
+    * This provides GSON enum support in jclouds.
+    * */
+   public static RuleProtocol fromValue(String name){
+      if (name != null) {
+         for (RuleProtocol value : RuleProtocol.values()) {
+            if (name.equalsIgnoreCase(value.name)) {
+               return value;
+            }
+         }
+         return UNRECOGNIZED;
+      }
+      return null;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Rules.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Rules.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Rules.java
new file mode 100644
index 0000000..4794a18
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Rules.java
@@ -0,0 +1,35 @@
+/*
+ * 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.neutron.v2.domain;
+
+import java.beans.ConstructorProperties;
+
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * A collection of Networks
+ */
+public class Rules extends PaginatedCollection<Rule> {
+   public static final Rules EMPTY = new Rules(ImmutableSet.<Rule> of(), ImmutableSet.<Link> of());
+
+   @ConstructorProperties({"security_group_rules", "security_group_rules_links"})
+   protected Rules(Iterable<Rule> securityGroups, Iterable<Link> securityGroupRulesLinks) {
+      super(securityGroups, securityGroupRulesLinks);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/SecurityGroup.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/SecurityGroup.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/SecurityGroup.java
new file mode 100644
index 0000000..604776e
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/SecurityGroup.java
@@ -0,0 +1,219 @@
+/*
+ * 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.neutron.v2.domain;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Contains a mapping between a MAC address and an IP address.
+ */
+public class SecurityGroup {
+
+   private String id;
+   @Named("tenant_id")
+   private String tenantId;
+   private String name;
+   private String description;
+   @Named("security_group_rules")
+   private ImmutableList<Rule> rules;
+
+   @ConstructorProperties({"id", "tenant_id", "name", "description", "security_group_rules"})
+   protected SecurityGroup(String id, String tenantId, String name, String description,
+         ImmutableList<Rule> rules) {
+      this.id = id;
+      this.tenantId = tenantId;
+      this.name = name;
+      this.description = description;
+      this.rules = rules;
+   }
+
+   private SecurityGroup(SecurityGroup securityGroup) {
+      this(securityGroup.id,
+            securityGroup.tenantId,
+            securityGroup.name,
+            securityGroup.description,
+            securityGroup.rules
+      );
+   }
+
+   private SecurityGroup() {}
+
+   /**
+    * @return The identifier for this Security Group.
+    */
+   @Nullable
+   public String getId() {
+      return id;
+   }
+
+   /**
+    * @return The identifier of the tenant for this Security Group.
+    */
+   @Nullable
+   public String getTenantId() {
+      return tenantId;
+   }
+
+   /**
+    * @return The name of the Security Group.
+    */
+   @Nullable
+   public String getName() {
+      return name;
+   }
+
+   /**
+    * @return The description of the Security Group.
+    */
+   @Nullable
+   public String getDescription() {
+      return description;
+   }
+
+   /**
+    * @return The collection of rules for this Security Group.
+    */
+   public ImmutableList<Rule> getRules() {
+      return rules!=null ? rules : ImmutableList.<Rule>of();
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o)
+         return true;
+      if (o == null || getClass() != o.getClass())
+         return false;
+
+      SecurityGroup that = (SecurityGroup) o;
+
+      return Objects.equal(this.id, that.id) &&
+            Objects.equal(this.tenantId, that.tenantId) &&
+            Objects.equal(this.name, that.name) &&
+            Objects.equal(this.description, that.description) &&
+            Objects.equal(this.rules, that.rules);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(id, tenantId, name, description, rules);
+   }
+
+   @Override
+   public String toString() {
+      return MoreObjects.toStringHelper(this)
+            .add("id", id)
+            .add("tenantId", tenantId)
+            .add("name", name)
+            .add("description", description)
+            .add("securityGroupRules", rules)
+            .toString();
+   }
+
+   /*
+    * Methods to get the Create and Update builders follow
+    */
+
+   /**
+    * @return the Builder for creating a new SecurityGroup
+    */
+   public static CreateBuilder createOptions() {
+      return new CreateBuilder();
+   }
+
+   private abstract static class Builder<ParameterizedBuilderType> {
+      // Keep track of the builder's state.
+      protected SecurityGroup securityGroup;
+
+      private Builder() {
+         securityGroup = new SecurityGroup();
+      }
+
+      protected abstract ParameterizedBuilderType self();
+
+      /**
+       * The tenant id for this Security Group. Usually can only be specified by administrators.
+       *
+       * @return the Builder.
+       * @see SecurityGroup#getTenantId()
+       */
+      public ParameterizedBuilderType tenantId(String tenantId) {
+         securityGroup.tenantId = tenantId;
+         return self();
+      }
+
+      /**
+       * The name for this Security Group.
+       *
+       * @return the Builder.
+       * @see SecurityGroup#getName()
+       */
+      public ParameterizedBuilderType name(String name) {
+         securityGroup.name = name;
+         return self();
+      }
+
+      /**
+       * The description for this Security Group.
+       *
+       * @return the Builder.
+       * @see SecurityGroup#getDescription()
+       */
+      public ParameterizedBuilderType description(String description) {
+         securityGroup.description = description;
+         return self();
+      }
+   }
+
+   /**
+    * Create and Update builders (inheriting from Builder)
+    */
+   public static class CreateBuilder extends Builder<CreateBuilder> {
+      /**
+       * Supply required properties for creating a Builder
+       */
+      private CreateBuilder() {
+      }
+
+      /**
+       * @return a CreateOptions constructed with this Builder.
+       */
+      public CreateOptions build() {
+         return new CreateOptions(securityGroup);
+      }
+
+      protected CreateBuilder self() {
+         return this;
+      }
+   }
+
+   /**
+    * Create and Update options - extend the domain class, passed to API update and create calls.
+    * Essentially the same as the domain class. Ensure validation and safe typing.
+    */
+   public static class CreateOptions extends SecurityGroup {
+      private CreateOptions(SecurityGroup securityGroup) {
+         super(securityGroup);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/SecurityGroups.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/SecurityGroups.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/SecurityGroups.java
new file mode 100644
index 0000000..044ce1c
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/SecurityGroups.java
@@ -0,0 +1,36 @@
+/*
+ * 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.neutron.v2.domain;
+
+import java.beans.ConstructorProperties;
+
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * A collection of Networks
+ */
+public class SecurityGroups extends PaginatedCollection<SecurityGroup> {
+   public static final SecurityGroups EMPTY = new SecurityGroups(ImmutableSet.<SecurityGroup> of(), ImmutableSet.<Link> of());
+
+   @ConstructorProperties({"security_groups", "security_groups_links"})
+   protected SecurityGroups(Iterable<SecurityGroup> securityGroups, Iterable<Link> securityGroupsLinks) {
+      super(securityGroups, securityGroupsLinks);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/extensions/SecurityGroupApi.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/extensions/SecurityGroupApi.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/extensions/SecurityGroupApi.java
new file mode 100644
index 0000000..4c0b4da
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/extensions/SecurityGroupApi.java
@@ -0,0 +1,186 @@
+/*
+ * 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.neutron.v2.extensions;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks;
+import org.jclouds.Fallbacks.EmptyPagedIterableOnNotFoundOr404;
+import org.jclouds.collect.PagedIterable;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
+import org.jclouds.openstack.neutron.v2.domain.Rule;
+import org.jclouds.openstack.neutron.v2.domain.Rules;
+import org.jclouds.openstack.neutron.v2.domain.SecurityGroup;
+import org.jclouds.openstack.neutron.v2.domain.SecurityGroups;
+import org.jclouds.openstack.neutron.v2.fallbacks.EmptyRulesFallback;
+import org.jclouds.openstack.neutron.v2.fallbacks.EmptySecurityGroupsFallback;
+import org.jclouds.openstack.neutron.v2.functions.ParseRules;
+import org.jclouds.openstack.neutron.v2.functions.ParseSecurityGroups;
+import org.jclouds.openstack.neutron.v2.functions.RulesToPagedIterable;
+import org.jclouds.openstack.neutron.v2.functions.SecurityGroupsToPagedIterable;
+import org.jclouds.openstack.v2_0.options.PaginationOptions;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.annotations.Transform;
+import org.jclouds.rest.annotations.WrapWith;
+import com.google.common.annotations.Beta;
+
+/**
+ * Provides access to Security Group extension operations for the OpenStack Networking (Neutron) v2 API.
+ * <p/>
+ * Security groups and security group rules allows administrators and tenants the ability to specify the type of
+ * traffic and direction (ingress/egress) that is allowed to pass through a port. A security group is a container for
+ * security group rules.
+ */
+@Beta
+@RequestFilters(AuthenticateRequest.class)
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface SecurityGroupApi {
+   /**
+    * Groups
+    */
+
+   /**
+    * @return all security groups currently defined in Neutron for the current tenant.
+    */
+   @Path("/security-groups")
+   @Named("security-group:list")
+   @GET
+   @ResponseParser(ParseSecurityGroups.class)
+   @Transform(SecurityGroupsToPagedIterable.class)
+   @Fallback(EmptyPagedIterableOnNotFoundOr404.class)
+   PagedIterable<SecurityGroup> listSecurityGroups();
+
+   /**
+    * @return all security groups currently defined in Neutron for the current tenant.
+    */
+   @Path("/security-groups")
+   @Named("security-group:list")
+   @GET
+   @ResponseParser(ParseSecurityGroups.class)
+   @Fallback(EmptySecurityGroupsFallback.class)
+   SecurityGroups listSecurityGroups(PaginationOptions options);
+
+   /**
+    * @param id the id of the security group to return
+    * @return SecurityGroup or null if not found.
+    */
+   @Path("/security-groups/{id}")
+   @Named("security-group:get")
+   @GET
+   @SelectJson("security_group")
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   @Nullable
+   SecurityGroup getSecurityGroup(@PathParam("id") String id);
+
+   /**
+    * Create a new SecurityGroup.
+    *
+    * @param securityGroup Describes the security group to be created.
+    * @return a reference of the newly-created security group
+    */
+   @Path("/security-groups")
+   @Named("secuity-group:create")
+   @POST
+   @SelectJson("security_group")
+   SecurityGroup create(@WrapWith("security_group") SecurityGroup.CreateOptions securityGroup);
+
+   /**
+    * Deletes the specified Security Group.
+    *
+    * @param id the id of the security group to delete
+    * @return true if delete was successful, false if not
+    */
+   @Path("/security-groups/{id}")
+   @Named("security-group:delete")
+   @DELETE
+   @Fallback(Fallbacks.FalseOnNotFoundOr404.class)
+   boolean deleteSecurityGroup(@PathParam("id") String id);
+
+   /**
+    * Rules
+    */
+
+   /**
+    * @return all security groups rules currently defined in Neutron for the current tenant.
+    */
+   @Path("/security-group-rules")
+   @Named("security-group-rule:list")
+   @GET
+   @ResponseParser(ParseRules.class)
+   @Transform(RulesToPagedIterable.class)
+   @Fallback(EmptyPagedIterableOnNotFoundOr404.class)
+   PagedIterable<Rule> listRules();
+
+   /**
+    * @return all security groups rules currently defined in Neutron for the current tenant.
+    */
+   @Path("/security-group-rules")
+   @Named("security-group-rule:list")
+   @GET
+   @ResponseParser(ParseRules.class)
+   @Fallback(EmptyRulesFallback.class)
+   Rules listRules(PaginationOptions options);
+
+   /**
+    * @param id the id of the security group rule to return.
+    * @return SecurityGroupRule or null if not found.
+    */
+   @Path("/security-group-rules/{id}")
+   @Named("security-group-rule:get")
+   @GET
+   @SelectJson("security_group_rule")
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   @Nullable
+   Rule get(@PathParam("id") String id);
+
+   /**
+    * Create a new Security Group Rule.
+    *
+    * @param securityGroupRule Describes the security group rule to be created.
+    * @return a reference of the newly-created security group rule.
+    */
+   @Path("/security-group-rules")
+   @Named("security-group-rule:create")
+   @POST
+   @SelectJson("security_group_rule")
+   Rule create(@WrapWith("security_group_rule") Rule.CreateOptions securityGroupRule);
+
+   /**
+    * Deletes the specified Security Group Rule.
+    *
+    * @param id the id of the security group rule to delete.
+    * @return true if delete was successful, false if not.
+    */
+   @Path("/security-group-rules/{id}")
+   @Named("security-group-rule:delete")
+   @DELETE
+   @Fallback(Fallbacks.FalseOnNotFoundOr404.class)
+   boolean deleteRule(@PathParam("id") String id);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/fallbacks/EmptyRulesFallback.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/fallbacks/EmptyRulesFallback.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/fallbacks/EmptyRulesFallback.java
new file mode 100644
index 0000000..a559f4f
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/fallbacks/EmptyRulesFallback.java
@@ -0,0 +1,45 @@
+/*
+ * 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.neutron.v2.fallbacks;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagate;
+import static com.google.common.util.concurrent.Futures.immediateFuture;
+import static org.jclouds.http.HttpUtils.contains404;
+import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
+
+import org.jclouds.Fallback;
+import org.jclouds.openstack.neutron.v2.domain.Rules;
+import org.jclouds.rest.ResourceNotFoundException;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class EmptyRulesFallback implements Fallback<Rules> {
+
+   public ListenableFuture<Rules> create(Throwable t) throws Exception {
+      return immediateFuture(createOrPropagate(t));
+   }
+
+   @Override
+   public Rules createOrPropagate(Throwable t) throws Exception {
+      if ((getFirstThrowableOfType(checkNotNull(t, "throwable"), ResourceNotFoundException.class) != null)
+            || contains404(t)) {
+         return Rules.EMPTY;
+      }
+      throw propagate(t);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/fallbacks/EmptySecurityGroupsFallback.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/fallbacks/EmptySecurityGroupsFallback.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/fallbacks/EmptySecurityGroupsFallback.java
new file mode 100644
index 0000000..5343c3f
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/fallbacks/EmptySecurityGroupsFallback.java
@@ -0,0 +1,45 @@
+/*
+ * 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.neutron.v2.fallbacks;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.jclouds.Fallback;
+import org.jclouds.openstack.neutron.v2.domain.SecurityGroups;
+import org.jclouds.rest.ResourceNotFoundException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagate;
+import static com.google.common.util.concurrent.Futures.immediateFuture;
+import static org.jclouds.http.HttpUtils.contains404;
+import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
+
+public class EmptySecurityGroupsFallback implements Fallback<SecurityGroups> {
+
+   public ListenableFuture<SecurityGroups> create(Throwable t) throws Exception {
+      return immediateFuture(createOrPropagate(t));
+   }
+
+   @Override
+   public SecurityGroups createOrPropagate(Throwable t) throws Exception {
+      if ((getFirstThrowableOfType(checkNotNull(t, "throwable"), ResourceNotFoundException.class) != null)
+            || contains404(t)) {
+         return SecurityGroups.EMPTY;
+      }
+      throw propagate(t);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/ParseRules.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/ParseRules.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/ParseRules.java
new file mode 100644
index 0000000..954180b
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/ParseRules.java
@@ -0,0 +1,38 @@
+/*
+ * 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.neutron.v2.functions;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.openstack.neutron.v2.domain.Rules;
+
+import com.google.inject.TypeLiteral;
+
+/**
+ * Used by jclouds to provide more specific collections and fallbacks.
+ */
+@Singleton
+public class ParseRules extends ParseJson<Rules> {
+
+   @Inject
+   public ParseRules(Json json) {
+      super(json, TypeLiteral.get(Rules.class));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/ParseSecurityGroups.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/ParseSecurityGroups.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/ParseSecurityGroups.java
new file mode 100644
index 0000000..fbc3fd4
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/ParseSecurityGroups.java
@@ -0,0 +1,38 @@
+/*
+ * 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.neutron.v2.functions;
+
+import com.google.inject.TypeLiteral;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.openstack.neutron.v2.domain.SecurityGroup;
+import org.jclouds.openstack.neutron.v2.domain.SecurityGroups;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Used by jclouds to provide more specific collections and fallbacks.
+ */
+@Singleton
+public class ParseSecurityGroups extends ParseJson<SecurityGroups> {
+
+   @Inject
+   public ParseSecurityGroups(Json json) {
+      super(json, TypeLiteral.get(SecurityGroups.class));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/RulesToPagedIterable.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/RulesToPagedIterable.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/RulesToPagedIterable.java
new file mode 100644
index 0000000..d664cd6
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/RulesToPagedIterable.java
@@ -0,0 +1,66 @@
+/*
+ * 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.neutron.v2.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.inject.Inject;
+
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.collect.internal.Arg0ToPagedIterable;
+import org.jclouds.openstack.neutron.v2.NeutronApi;
+import org.jclouds.openstack.neutron.v2.domain.Rule;
+import org.jclouds.openstack.neutron.v2.extensions.SecurityGroupApi;
+import org.jclouds.openstack.v2_0.options.PaginationOptions;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+
+/**
+ * Ensures Routers works as PagedIterable.
+ */
+public class RulesToPagedIterable extends
+      Arg0ToPagedIterable.FromCaller<Rule, RulesToPagedIterable> {
+
+   private final NeutronApi api;
+
+   @Inject
+   protected RulesToPagedIterable(NeutronApi api) {
+      this.api = checkNotNull(api, "api");
+   }
+
+   @Override
+   protected Function<Object, IterableWithMarker<Rule>> markerToNextForArg0(Optional<Object> arg0) {
+      String region = arg0.isPresent() ? arg0.get().toString() : null;
+      final SecurityGroupApi securityGroupApi = api.getSecurityGroupApi(region).get();
+      return new Function<Object, IterableWithMarker<Rule>>() {
+
+         @SuppressWarnings("unchecked")
+         @Override
+         public IterableWithMarker<Rule> apply(Object input) {
+            PaginationOptions paginationOptions = PaginationOptions.class.cast(input);
+            return IterableWithMarker.class.cast(securityGroupApi.listRules(paginationOptions));
+         }
+
+         @Override
+         public String toString() {
+            return "listRules()";
+         }
+      };
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/SecurityGroupsToPagedIterable.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/SecurityGroupsToPagedIterable.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/SecurityGroupsToPagedIterable.java
new file mode 100644
index 0000000..15c7c14
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/SecurityGroupsToPagedIterable.java
@@ -0,0 +1,66 @@
+/*
+ * 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.neutron.v2.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.inject.Inject;
+
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.collect.internal.Arg0ToPagedIterable;
+import org.jclouds.openstack.neutron.v2.NeutronApi;
+import org.jclouds.openstack.neutron.v2.domain.SecurityGroup;
+import org.jclouds.openstack.neutron.v2.extensions.SecurityGroupApi;
+import org.jclouds.openstack.v2_0.options.PaginationOptions;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+
+/**
+ * Ensures Routers works as PagedIterable.
+ */
+public class SecurityGroupsToPagedIterable extends
+      Arg0ToPagedIterable.FromCaller<SecurityGroup, SecurityGroupsToPagedIterable> {
+
+   private final NeutronApi api;
+
+   @Inject
+   protected SecurityGroupsToPagedIterable(NeutronApi api) {
+      this.api = checkNotNull(api, "api");
+   }
+
+   @Override
+   protected Function<Object, IterableWithMarker<SecurityGroup>> markerToNextForArg0(Optional<Object> arg0) {
+      String region = arg0.isPresent() ? arg0.get().toString() : null;
+      final SecurityGroupApi securityGroupApi = api.getSecurityGroupApi(region).get();
+      return new Function<Object, IterableWithMarker<SecurityGroup>>() {
+
+         @SuppressWarnings("unchecked")
+         @Override
+         public IterableWithMarker<SecurityGroup> apply(Object input) {
+            PaginationOptions paginationOptions = PaginationOptions.class.cast(input);
+            return IterableWithMarker.class.cast(securityGroupApi.listSecurityGroups(paginationOptions));
+         }
+
+         @Override
+         public String toString() {
+            return "listSecurityGroups()";
+         }
+      };
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/67b9f472/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/SecurityGroupApiLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/SecurityGroupApiLiveTest.java b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/SecurityGroupApiLiveTest.java
new file mode 100644
index 0000000..8771a3e
--- /dev/null
+++ b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/SecurityGroupApiLiveTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.neutron.v2.extensions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.openstack.neutron.v2.domain.Rule;
+import org.jclouds.openstack.neutron.v2.domain.RuleDirection;
+import org.jclouds.openstack.neutron.v2.domain.RuleEthertype;
+import org.jclouds.openstack.neutron.v2.domain.RuleProtocol;
+import org.jclouds.openstack.neutron.v2.domain.SecurityGroup;
+import org.jclouds.openstack.neutron.v2.internal.BaseNeutronApiLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * Tests parsing and Guice wiring of RouterApi
+ */
+@Test(groups = "live", testName = "SecurityGroupApiLiveTest")
+public class SecurityGroupApiLiveTest extends BaseNeutronApiLiveTest {
+
+   /**
+    * Smoke test for the Security Group extension for Neutron
+    */
+   public void testCreateUpdateAndDeleteSecurityGroup() {
+      for (String region : api.getConfiguredRegions()) {
+         SecurityGroupApi sgApi = api.getSecurityGroupApi(region).get();
+
+         SecurityGroup securityGroup = sgApi.create(
+               SecurityGroup.createOptions().name("jclouds-test").description("jclouds test security group").build());
+         assertNotNull(securityGroup);
+
+         Rule rule = sgApi.create(
+               Rule.createOptions(RuleDirection.EGRESS, securityGroup.getId())
+                     .ethertype(RuleEthertype.IPV6)
+                     .portRangeMax(90)
+                     .portRangeMin(80)
+                     .protocol(RuleProtocol.TCP)
+                     .build());
+
+         assertNotNull(rule);
+
+         // Refresh
+         securityGroup = sgApi.getSecurityGroup(securityGroup.getId());
+
+         assertEquals(securityGroup.getName(), "jclouds-test");
+         assertEquals(securityGroup.getDescription(), "jclouds test security group");
+
+         assertEquals(securityGroup.getRules().size(), 3, "Expected 2 default rules");
+
+         Rule newSecGroupRule = null;
+         for(Rule sgr : securityGroup.getRules()) {
+            if(sgr.getId().equals(rule.getId())) {
+               newSecGroupRule = sgr;
+               break;
+            }
+         }
+         assertNotNull(newSecGroupRule, "Did not find the new rule in the group.");
+
+         assertEquals(rule, newSecGroupRule);
+
+         assertEquals(rule.getEthertype(), RuleEthertype.IPV6);
+         assertEquals(rule.getProtocol(), RuleProtocol.TCP);
+         assertEquals(rule.getPortRangeMax().intValue(), 90);
+         assertEquals(rule.getPortRangeMin().intValue(), 80);
+         assertEquals(rule.getDirection(), RuleDirection.EGRESS);
+
+         assertTrue(sgApi.deleteRule(rule.getId()));
+         assertTrue(sgApi.deleteSecurityGroup(securityGroup.getId()));
+      }
+   }
+}