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/03/25 17:12:12 UTC

[1/2] OS Neutron Extension Router

Repository: jclouds-labs-openstack
Updated Branches:
  refs/heads/master c9ff49a1b -> 57a9087fe


http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApiLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApiLiveTest.java b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApiLiveTest.java
new file mode 100644
index 0000000..19fa1cf
--- /dev/null
+++ b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApiLiveTest.java
@@ -0,0 +1,192 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.extensions;
+
+import org.jclouds.openstack.neutron.v2_0.domain.ExternalGatewayInfo;
+import org.jclouds.openstack.neutron.v2_0.domain.Network;
+import org.jclouds.openstack.neutron.v2_0.domain.NetworkType;
+import org.jclouds.openstack.neutron.v2_0.domain.Port;
+import org.jclouds.openstack.neutron.v2_0.domain.ReferenceWithName;
+import org.jclouds.openstack.neutron.v2_0.domain.Router;
+import org.jclouds.openstack.neutron.v2_0.domain.RouterInterface;
+import org.jclouds.openstack.neutron.v2_0.domain.Subnet;
+import org.jclouds.openstack.neutron.v2_0.features.NetworkApi;
+import org.jclouds.openstack.neutron.v2_0.features.PortApi;
+import org.jclouds.openstack.neutron.v2_0.features.SubnetApi;
+import org.jclouds.openstack.neutron.v2_0.internal.BaseNeutronApiLiveTest;
+import org.jclouds.openstack.neutron.v2_0.options.CreateNetworkOptions;
+import org.jclouds.openstack.neutron.v2_0.options.CreateRouterOptions;
+import org.jclouds.openstack.neutron.v2_0.options.UpdateRouterOptions;
+import org.testng.annotations.Test;
+
+import java.util.Set;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Tests parsing and Guice wiring of RouterApi
+ *
+ * @author Nick Livens
+ */
+@Test(groups = "live", testName = "RouterApiLiveTest")
+public class RouterApiLiveTest extends BaseNeutronApiLiveTest {
+
+   public void testGetAndListRouters() {
+      for (String zone : api.getConfiguredZones()) {
+         Set<? extends ReferenceWithName> references = api.getRouterExtensionForZone(zone).get().list().concat().toSet();
+         Set<? extends Router> routers = api.getRouterExtensionForZone(zone).get().listInDetail().concat().toSet();
+
+         assertNotNull(references);
+         assertNotNull(routers);
+         assertEquals(references.size(), routers.size());
+
+         for (Router router : routers) {
+            assertNotNull(router.getName());
+            assertTrue(references.contains(ReferenceWithName.builder().id(router.getId()).tenantId(router.getTenantId()).name(router.getName()).build()));
+
+            Router retrievedRouter = api.getRouterExtensionForZone(zone).get().get(router.getId());
+            assertEquals(router, retrievedRouter);
+         }
+      }
+   }
+
+   public void testCreateUpdateAndDeleteRouter() {
+      for (String zone : api.getConfiguredZones()) {
+         RouterApi routerApi = api.getRouterExtensionForZone(zone).get();
+         NetworkApi networkApi = api.getNetworkApiForZone(zone);
+         SubnetApi subnetApi = api.getSubnetApiForZone(zone);
+
+         Network network = networkApi.create(CreateNetworkOptions.builder().name("jclouds-network-test").external(true).networkType(NetworkType.LOCAL).build());
+         assertNotNull(network);
+
+         Subnet subnet = subnetApi.create(network.getId(), 4, "192.168.0.0/16");
+         assertNotNull(subnet);
+
+         Router ref = routerApi.create(CreateRouterOptions.builder().name("jclouds-router-test")
+            .externalGatewayInfo(ExternalGatewayInfo.builder().networkId(network.getId()).build()).build());
+         assertNotNull(ref);
+
+         Router router = routerApi.get(ref.getId());
+
+         assertEquals(router.getId(), ref.getId());
+         assertEquals(router.getName(), "jclouds-router-test");
+         assertEquals(router.getExternalGatewayInfo().getNetworkId(), network.getId());
+         assertTrue(routerApi.update(router.getId(), UpdateRouterOptions.builder().name("jclouds-router-test-rename").build()));
+
+         router = routerApi.get(ref.getId());
+
+         assertEquals(router.getId(), ref.getId());
+         assertEquals(router.getName(), "jclouds-router-test-rename");
+
+         ReferenceWithName ref2 = routerApi.create(CreateRouterOptions.builder().name("jclouds-router-test2")
+            .externalGatewayInfo(ExternalGatewayInfo.builder().networkId(network.getId()).build()).build());
+         assertNotNull(ref2);
+
+         assertTrue(routerApi.delete(ref.getId()));
+         assertTrue(routerApi.delete(ref2.getId()));
+         assertTrue(subnetApi.delete(subnet.getId()));
+         assertTrue(networkApi.delete(network.getId()));
+      }
+   }
+
+   public void testCreateAndDeleteRouterInterfaceForSubnet() {
+      for (String zone : api.getConfiguredZones()) {
+         RouterApi routerApi = api.getRouterExtensionForZone(zone).get();
+         NetworkApi networkApi = api.getNetworkApiForZone(zone);
+         SubnetApi subnetApi = api.getSubnetApiForZone(zone);
+
+         Network network = networkApi.create(CreateNetworkOptions.builder().name("jclouds-network-test").external(true).networkType(NetworkType.LOCAL).build());
+         assertNotNull(network);
+
+         Subnet subnet = subnetApi.create(network.getId(), 4, "192.168.0.0/16");
+         assertNotNull(subnet);
+
+         Network network2 = networkApi.create(CreateNetworkOptions.builder().name("jclouds-network-test2").external(true).networkType(NetworkType.LOCAL).build());
+         assertNotNull(network2);
+
+         Subnet subnet2 = subnetApi.create(network2.getId(), 4, "192.169.0.0/16");
+         assertNotNull(subnet2);
+
+         Router router = routerApi.create(CreateRouterOptions.builder().name("jclouds-router-test").build());
+         assertNotNull(router);
+
+         RouterInterface routerInterface = routerApi.addInterfaceForSubnet(router.getId(), subnet.getId());
+         assertNotNull(routerInterface);
+
+         RouterInterface routerInterface2 = routerApi.addInterfaceForSubnet(router.getId(), subnet2.getId());
+         assertNotNull(routerInterface2);
+
+         assertTrue(routerApi.removeInterfaceForSubnet(router.getId(), subnet.getId()));
+         assertTrue(routerApi.removeInterfaceForSubnet(router.getId(), subnet2.getId()));
+         assertTrue(routerApi.delete(router.getId()));
+         assertTrue(subnetApi.delete(subnet.getId()));
+         assertTrue(networkApi.delete(network.getId()));
+         assertTrue(subnetApi.delete(subnet2.getId()));
+         assertTrue(networkApi.delete(network2.getId()));
+      }
+   }
+
+   public void testCreateAndDeleteRouterInterfaceForPort() {
+      for (String zone : api.getConfiguredZones()) {
+         RouterApi routerApi = api.getRouterExtensionForZone(zone).get();
+         NetworkApi networkApi = api.getNetworkApiForZone(zone);
+         SubnetApi subnetApi = api.getSubnetApiForZone(zone);
+         PortApi portApi = api.getPortApiForZone(zone);
+
+         Network network = networkApi.create(CreateNetworkOptions.builder().name("jclouds-network-test").external(true).networkType(NetworkType.LOCAL).build());
+         assertNotNull(network);
+
+         Subnet subnet = subnetApi.create(network.getId(), 4, "192.168.0.0/16");
+         assertNotNull(subnet);
+
+         Network network2 = networkApi.create(CreateNetworkOptions.builder().name("jclouds-network-test2").external(true).networkType(NetworkType.LOCAL).build());
+         assertNotNull(network2);
+
+         Subnet subnet2 = subnetApi.create(network2.getId(), 4, "192.169.0.0/16");
+         assertNotNull(subnet2);
+
+         Port port = portApi.create(network.getId());
+         assertNotNull(port);
+
+         Port port2 = portApi.create(network2.getId());
+         assertNotNull(port2);
+
+         Router router = routerApi.create(CreateRouterOptions.builder().name("jclouds-router-test").build());
+         assertNotNull(router);
+
+         RouterInterface routerInterface = routerApi.addInterfaceForPort(router.getId(), port.getId());
+         assertNotNull(routerInterface);
+
+         RouterInterface routerInterface2 = routerApi.addInterfaceForPort(router.getId(), port2.getId());
+         assertNotNull(routerInterface2);
+
+         assertTrue(routerApi.removeInterfaceForPort(router.getId(), port.getId()));
+         assertTrue(routerApi.removeInterfaceForPort(router.getId(), port2.getId()));
+         assertTrue(routerApi.delete(router.getId()));
+         assertTrue(subnetApi.delete(subnet.getId()));
+         assertTrue(networkApi.delete(network.getId()));
+         assertTrue(subnetApi.delete(subnet2.getId()));
+         assertTrue(networkApi.delete(network2.getId()));
+
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApiLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApiLiveTest.java b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApiLiveTest.java
index e8e1f79..6950a89 100644
--- a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApiLiveTest.java
+++ b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApiLiveTest.java
@@ -16,7 +16,9 @@
  */
 package org.jclouds.openstack.neutron.v2_0.features;
 
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
 import org.jclouds.openstack.neutron.v2_0.domain.BulkNetwork;
 import org.jclouds.openstack.neutron.v2_0.domain.Network;
 import org.jclouds.openstack.neutron.v2_0.domain.NetworkType;
@@ -25,6 +27,8 @@ import org.jclouds.openstack.neutron.v2_0.internal.BaseNeutronApiLiveTest;
 import org.jclouds.openstack.neutron.v2_0.options.CreateNetworkBulkOptions;
 import org.jclouds.openstack.neutron.v2_0.options.CreateNetworkOptions;
 import org.jclouds.openstack.neutron.v2_0.options.UpdateNetworkOptions;
+import org.jclouds.openstack.neutron.v2_0.util.PredicateUtil;
+import org.testng.annotations.Test;
 
 import java.util.Set;
 
@@ -37,6 +41,7 @@ import static org.testng.Assert.assertTrue;
  *
  * @author Nick Livens
  */
+@Test(groups = "live", testName = "NetworkApiLiveTest")
 public class NetworkApiLiveTest extends BaseNeutronApiLiveTest {
 
    public void testGetAndListNetworks() {
@@ -107,7 +112,8 @@ public class NetworkApiLiveTest extends BaseNeutronApiLiveTest {
          assertEquals(nets.size(), 3);
 
          for (Network net : nets) {
-            assertTrue(existingNets.contains(net));
+            Predicate<Network> idEqualsPredicate = PredicateUtil.createIdEqualsPredicate(net.getId());
+            assertEquals(1, Sets.filter(existingNets, idEqualsPredicate).size());
             assertTrue(networkApi.delete(net.getId()));
          }
       }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/PortApiLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/PortApiLiveTest.java b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/PortApiLiveTest.java
index 7aed04c..1a368b3 100644
--- a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/PortApiLiveTest.java
+++ b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/PortApiLiveTest.java
@@ -16,31 +16,37 @@
  */
 package org.jclouds.openstack.neutron.v2_0.features;
 
-import com.google.common.collect.ImmutableSet;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+
 import org.jclouds.openstack.neutron.v2_0.domain.BulkPort;
 import org.jclouds.openstack.neutron.v2_0.domain.IP;
 import org.jclouds.openstack.neutron.v2_0.domain.NetworkType;
-import org.jclouds.openstack.neutron.v2_0.domain.ReferenceWithName;
 import org.jclouds.openstack.neutron.v2_0.domain.Port;
+import org.jclouds.openstack.neutron.v2_0.domain.ReferenceWithName;
 import org.jclouds.openstack.neutron.v2_0.internal.BaseNeutronApiLiveTest;
 import org.jclouds.openstack.neutron.v2_0.options.CreateNetworkOptions;
 import org.jclouds.openstack.neutron.v2_0.options.CreatePortBulkOptions;
 import org.jclouds.openstack.neutron.v2_0.options.CreatePortOptions;
 import org.jclouds.openstack.neutron.v2_0.options.CreateSubnetOptions;
 import org.jclouds.openstack.neutron.v2_0.options.UpdatePortOptions;
+import org.jclouds.openstack.neutron.v2_0.util.PredicateUtil;
+import org.testng.annotations.Test;
 
-import java.util.Set;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 
 /**
  * Tests PortApi in combination with the Network & SubnetApi
  *
  * @author Nick Livens
  */
+@Test(groups = "live", testName = "PortApiLiveTest")
 public class PortApiLiveTest extends BaseNeutronApiLiveTest {
 
    public void testGetAndListPorts() {
@@ -134,7 +140,8 @@ public class PortApiLiveTest extends BaseNeutronApiLiveTest {
          assertEquals(ports.size(), 4);
 
          for (Port port : ports) {
-            assertTrue(existingPorts.contains(port));
+            Predicate<Port> idEqualsPredicate = PredicateUtil.createIdEqualsPredicate(port.getId());
+            assertEquals(1, Sets.filter(existingPorts, idEqualsPredicate).size());
             assertTrue(portApi.delete(port.getId()));
          }
          assertTrue(subnetApi.delete(ipv4SubnetId));

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApiLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApiLiveTest.java b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApiLiveTest.java
index 549114c..bd3574f 100644
--- a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApiLiveTest.java
+++ b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApiLiveTest.java
@@ -16,8 +16,12 @@
  */
 package org.jclouds.openstack.neutron.v2_0.features;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+
 import org.jclouds.openstack.neutron.v2_0.domain.AllocationPool;
 import org.jclouds.openstack.neutron.v2_0.domain.BulkSubnet;
 import org.jclouds.openstack.neutron.v2_0.domain.HostRoute;
@@ -29,18 +33,20 @@ import org.jclouds.openstack.neutron.v2_0.options.CreateNetworkOptions;
 import org.jclouds.openstack.neutron.v2_0.options.CreateSubnetBulkOptions;
 import org.jclouds.openstack.neutron.v2_0.options.CreateSubnetOptions;
 import org.jclouds.openstack.neutron.v2_0.options.UpdateSubnetOptions;
+import org.jclouds.openstack.neutron.v2_0.util.PredicateUtil;
+import org.testng.annotations.Test;
 
-import java.util.Set;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 
 /**
  * Tests subnet api in combination with the network api
  *
  * @author Nick Livens
  */
+@Test(groups = "live", testName = "SubnetApiLiveTest")
 public class SubnetApiLiveTest extends BaseNeutronApiLiveTest {
 
    public void testGetAndListSubnets() {
@@ -124,7 +130,8 @@ public class SubnetApiLiveTest extends BaseNeutronApiLiveTest {
          assertEquals(subnets.size(), 3);
 
          for (Subnet net : subnets) {
-            assertTrue(existingSubnets.contains(net));
+            Predicate<Subnet> idEqualsPredicate = PredicateUtil.createIdEqualsPredicate(net.getId());
+            assertEquals(1, Sets.filter(existingSubnets, idEqualsPredicate).size());
             assertTrue(subnetApi.delete(net.getId()));
          }
          assertTrue(networkApi.delete(networkId));

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/parse/ParseRouterTest.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/parse/ParseRouterTest.java b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/parse/ParseRouterTest.java
new file mode 100644
index 0000000..59b829f
--- /dev/null
+++ b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/parse/ParseRouterTest.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.parse;
+
+import org.jclouds.json.BaseItemParserTest;
+import org.jclouds.openstack.neutron.v2_0.domain.ExternalGatewayInfo;
+import org.jclouds.openstack.neutron.v2_0.domain.Router;
+import org.jclouds.openstack.neutron.v2_0.domain.State;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Nick Livens
+ */
+@Test(groups = "unit", testName = "ParseRouterTest")
+public class ParseRouterTest extends BaseItemParserTest<Router> {
+
+   @Override
+   public String resource() {
+      return "/router.json";
+   }
+
+   @Override
+   @SelectJson("router")
+   @Consumes(MediaType.APPLICATION_JSON)
+   public Router expected() {
+      return Router.builder()
+         .externalGatewayInfo(ExternalGatewayInfo.builder().networkId("624312ff-d14b-4ba3-9834-1c78d23d574d").build())
+         .state(State.ACTIVE)
+         .name("jclouds-wibble")
+         .tenantId("1234567890")
+         .id("16dba3bc-f3fa-4775-afdc-237e12c72f6a")
+         .build();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/util/ClassUtil.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/util/ClassUtil.java b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/util/ClassUtil.java
new file mode 100644
index 0000000..a034fb5
--- /dev/null
+++ b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/util/ClassUtil.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.util;
+
+import java.lang.reflect.Field;
+
+/**
+ * @author Nick Livens
+ */
+public class ClassUtil {
+
+    public static Field findField(Class clazz, String fieldName) {
+        Field fieldToFind = null;
+        if (clazz.getSuperclass() != null)
+            fieldToFind = findField(clazz.getSuperclass(), fieldName);
+
+        if (fieldToFind != null)
+            return fieldToFind;
+
+        for (Field field : clazz.getDeclaredFields()) {
+            if (field.getName().equals(fieldName))
+                return field;
+        }
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/util/PredicateUtil.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/util/PredicateUtil.java b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/util/PredicateUtil.java
new file mode 100644
index 0000000..27bef32
--- /dev/null
+++ b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/util/PredicateUtil.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.util;
+
+import com.google.common.base.Predicate;
+import org.jclouds.javax.annotation.Nullable;
+
+import java.lang.reflect.Field;
+
+/**
+ * @author Nick Livens
+ */
+public class PredicateUtil {
+
+    public static <T> Predicate<T> createIdEqualsPredicate(final String id) {
+        return new Predicate<T>() {
+            @Override
+            public boolean apply(@Nullable T input) {
+                if (input == null) return false;
+
+                try {
+                    Class clazz = input.getClass();
+                    Field field = ClassUtil.findField(clazz, "id");
+                    field.setAccessible(true);
+                    String value = (String) field.get(input);
+                    field.setAccessible(false);
+                    return value != null && value.equals(id);
+                } catch (IllegalAccessException iae) {
+                    return false;
+                }
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/resources/list_routers.json
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/resources/list_routers.json b/openstack-neutron/src/test/resources/list_routers.json
new file mode 100644
index 0000000..0e5b95d
--- /dev/null
+++ b/openstack-neutron/src/test/resources/list_routers.json
@@ -0,0 +1,74 @@
+{"routers":[
+    {
+        "status":"ACTIVE",
+        "name":"jclouds-test",
+        "tenant_id":"1234567890",
+        "id":"16dba3bc-f3fa-4775-afdc-237e12c72f6a"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"wibble",
+        "tenant_id":"1234567890",
+        "id":"1a104cf5-cb18-4d35-9407-2fd2646d9d0b"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"jclouds-test",
+        "tenant_id":"1234567890",
+        "id":"31083ae2-420d-48b2-ac98-9f7a4fd8dbdc"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"jclouds-test",
+        "tenant_id":"1234567890",
+        "id":"49c6d6fa-ff2a-459d-b975-75a8d31c9a89"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"wibble",
+        "tenant_id":"1234567890",
+        "id":"5cb3d6f4-62cb-41c9-b964-ba7d9df79e4e"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"jclouds-test",
+        "tenant_id":"1234567890",
+        "id":"5d51d012-3491-4db7-b1b5-6f254015015d"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"wibble",
+        "tenant_id":"1234567890",
+        "id":"5f9cf7dc-22ca-4097-8e49-1cc8b23faf17"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"jclouds-test",
+        "tenant_id":"1234567890",
+        "id":"6319ecad-6bff-48b2-9b53-02ede8cb7588"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"jclouds-test",
+        "tenant_id":"1234567890",
+        "id":"6ba4c788-661f-49ab-9bf8-5f10cbbb2f57"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"jclouds-test",
+        "tenant_id":"1234567890",
+        "id":"74ed170b-5069-4353-ab38-9719766dc57e"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"wibble",
+        "tenant_id":"1234567890",
+        "id":"b71fcac1-e864-4031-8c5b-edbecd9ece36"
+    },
+    {
+        "status":"ACTIVE",
+        "name":"jclouds-test",
+        "tenant_id":"1234567890",
+        "id":"c7681895-d84d-4650-9ca0-82c72036b855"
+    }
+]}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/resources/router.json
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/resources/router.json b/openstack-neutron/src/test/resources/router.json
new file mode 100644
index 0000000..0b45db9
--- /dev/null
+++ b/openstack-neutron/src/test/resources/router.json
@@ -0,0 +1,7 @@
+{"router":{
+    "status":"ACTIVE",
+    "external_gateway_info":{"network_id":"624312ff-d14b-4ba3-9834-1c78d23d574d"},
+    "name":"jclouds-wibble",
+    "tenant_id":"1234567890",
+    "id":"16dba3bc-f3fa-4775-afdc-237e12c72f6a"
+}}
\ No newline at end of file


[2/2] git commit: OS Neutron Extension Router

Posted by za...@apache.org.
OS Neutron Extension Router


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/57a9087f
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/tree/57a9087f
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/diff/57a9087f

Branch: refs/heads/master
Commit: 57a9087fefe5a6a4a34b49fd69db799893dcdd85
Parents: c9ff49a
Author: Kris Sterckx <tw...@gmail.com>
Authored: Thu Nov 7 08:54:36 2013 +0100
Committer: Zack Shoylev <za...@rackspace.com>
Committed: Tue Mar 25 11:09:48 2014 -0500

----------------------------------------------------------------------
 openstack-neutron/pom.xml                       |   1 +
 .../openstack/neutron/v2_0/NeutronApi.java      |   8 +
 .../v2_0/domain/ExternalGatewayInfo.java        | 105 +++++++
 .../openstack/neutron/v2_0/domain/Network.java  |   2 +-
 .../neutron/v2_0/domain/NetworkType.java        |   6 +-
 .../openstack/neutron/v2_0/domain/Port.java     |   4 +-
 .../openstack/neutron/v2_0/domain/Router.java   | 151 ++++++++++
 .../neutron/v2_0/domain/RouterInterface.java    | 127 ++++++++
 .../openstack/neutron/v2_0/domain/Subnet.java   |  10 +-
 .../neutron/v2_0/extensions/RouterApi.java      | 217 ++++++++++++++
 .../neutron/v2_0/features/NetworkApi.java       |  13 +-
 .../neutron/v2_0/features/PortApi.java          |  13 +-
 .../neutron/v2_0/features/SubnetApi.java        |  13 +-
 .../v2_0/functions/ParseNetworkDetails.java     |   6 +-
 .../neutron/v2_0/functions/ParseNetworks.java   |   6 +-
 .../v2_0/functions/ParsePortDetails.java        |   6 +-
 .../neutron/v2_0/functions/ParsePorts.java      |   6 +-
 .../v2_0/functions/ParseRouterDetails.java      |  93 ++++++
 .../neutron/v2_0/functions/ParseRouters.java    |  93 ++++++
 .../v2_0/functions/ParseSubnetDetails.java      |   6 +-
 .../neutron/v2_0/functions/ParseSubnets.java    |   6 +-
 .../v2_0/options/CreateRouterOptions.java       | 168 +++++++++++
 .../neutron/v2_0/options/EmptyOptions.java      |  48 +++
 .../v2_0/options/UpdateRouterOptions.java       | 168 +++++++++++
 .../v2_0/extensions/RouterApiExpectTest.java    | 295 +++++++++++++++++++
 .../v2_0/extensions/RouterApiLiveTest.java      | 192 ++++++++++++
 .../v2_0/features/NetworkApiLiveTest.java       |   8 +-
 .../neutron/v2_0/features/PortApiLiveTest.java  |  25 +-
 .../v2_0/features/SubnetApiLiveTest.java        |  23 +-
 .../neutron/v2_0/parse/ParseRouterTest.java     |  55 ++++
 .../openstack/neutron/v2_0/util/ClassUtil.java  |  43 +++
 .../neutron/v2_0/util/PredicateUtil.java        |  51 ++++
 .../src/test/resources/list_routers.json        |  74 +++++
 .../src/test/resources/router.json              |   7 +
 34 files changed, 1993 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/pom.xml
----------------------------------------------------------------------
diff --git a/openstack-neutron/pom.xml b/openstack-neutron/pom.xml
index 90cfe44..bb62eba 100644
--- a/openstack-neutron/pom.xml
+++ b/openstack-neutron/pom.xml
@@ -126,6 +126,7 @@
                     <test.openstack-neutron.credential>${test.openstack-neutron.credential}</test.openstack-neutron.credential>
                     <test.jclouds.keystone.credential-type>${test.jclouds.keystone.credential-type}</test.jclouds.keystone.credential-type>
                   </systemPropertyVariables>
+                  <parallel>classes</parallel>
                 </configuration>
               </execution>
             </executions>

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/NeutronApi.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/NeutronApi.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/NeutronApi.java
index 1e3ca7c..bf06b0b 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/NeutronApi.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/NeutronApi.java
@@ -18,10 +18,12 @@
  */
 package org.jclouds.openstack.neutron.v2_0;
 
+import com.google.common.base.Optional;
 import com.google.inject.Provides;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Zone;
 import org.jclouds.location.functions.ZoneToEndpoint;
+import org.jclouds.openstack.neutron.v2_0.extensions.RouterApi;
 import org.jclouds.openstack.neutron.v2_0.features.NetworkApi;
 import org.jclouds.openstack.neutron.v2_0.features.PortApi;
 import org.jclouds.openstack.neutron.v2_0.features.SubnetApi;
@@ -71,4 +73,10 @@ public interface NeutronApi extends Closeable {
     */
    @Delegate
    PortApi getPortApiForZone(@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
+
+   /**
+    * Provides synchronous access to Router features.
+    */
+   @Delegate
+   Optional<? extends RouterApi> getRouterExtensionForZone(@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/ExternalGatewayInfo.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/ExternalGatewayInfo.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/ExternalGatewayInfo.java
new file mode 100644
index 0000000..884f270
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/ExternalGatewayInfo.java
@@ -0,0 +1,105 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.domain;
+
+import com.google.common.base.Objects;
+
+import java.beans.ConstructorProperties;
+
+/**
+ * Information on external gateway for the router
+ *
+ * @author Nick Livens
+ */
+public class ExternalGatewayInfo {
+
+   private final String networkId;
+
+   @ConstructorProperties({"network_id"})
+   protected ExternalGatewayInfo(String networkId) {
+      this.networkId = networkId;
+   }
+
+   /**
+    * @return the id of the network which is used as external gateway for the router
+    */
+   public String getNetworkId() {
+      return networkId;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(networkId);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ExternalGatewayInfo that = ExternalGatewayInfo.class.cast(obj);
+      return Objects.equal(this.networkId, that.networkId);
+   }
+
+   protected Objects.ToStringHelper string() {
+      return Objects.toStringHelper(this).add("networkId", networkId);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   public static Builder builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder toBuilder() {
+      return new ConcreteBuilder().fromExternalGatewayInfo(this);
+   }
+
+   public static abstract class Builder {
+      protected abstract Builder self();
+
+      protected String networkId;
+
+      /**
+       * @see ExternalGatewayInfo#getNetworkId()
+       */
+      public Builder networkId(String networkId) {
+         this.networkId = networkId;
+         return self();
+      }
+
+      public ExternalGatewayInfo build() {
+         return new ExternalGatewayInfo(networkId);
+      }
+
+      public Builder fromExternalGatewayInfo(ExternalGatewayInfo in) {
+         return this.networkId(in.getNetworkId());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Network.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Network.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Network.java
index 06485b7..d7c9e8a 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Network.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Network.java
@@ -54,7 +54,7 @@ public class Network extends ReferenceWithName {
       this.adminStateUp = adminStateUp;
       this.shared = shared;
       this.external = external;
-      this.networkType = NetworkType.fromValue(networkType);
+      this.networkType = networkType != null ? NetworkType.fromValue(networkType) : null;
       this.physicalNetworkName = physicalNetworkName;
       this.segmentationId = segmentationId;
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/NetworkType.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/NetworkType.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/NetworkType.java
index ba7e886..53c5a59 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/NetworkType.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/NetworkType.java
@@ -37,10 +37,6 @@ public enum NetworkType {
    }
 
    public static NetworkType fromValue(String value) {
-      for (NetworkType networkType : values()) {
-         if (networkType.getValue().equalsIgnoreCase(value))
-            return networkType;
-      }
-      return null;
+      return NetworkType.valueOf(value.toUpperCase());
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Port.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Port.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Port.java
index 226651b..1b44bc9 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Port.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Port.java
@@ -23,6 +23,8 @@ import com.google.common.base.Objects;
 import java.beans.ConstructorProperties;
 import java.util.Set;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * A Neutron port
  *
@@ -47,7 +49,7 @@ public class Port extends ReferenceWithName {
       super(id, tenantId, name);
       this.adminStateUp = adminStateUp;
       this.state = state;
-      this.networkId = networkId;
+      this.networkId = checkNotNull(networkId, "networkId");
       this.deviceId = deviceId;
       this.deviceOwner = deviceOwner;
       this.fixedIps = fixedIps;

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Router.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Router.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Router.java
new file mode 100644
index 0000000..ca36a6e
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Router.java
@@ -0,0 +1,151 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.domain;
+
+import com.google.common.base.Objects;
+
+import java.beans.ConstructorProperties;
+
+/**
+ * A Neutron Router
+ *
+ * @author Nick Livens
+ * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/router_ext_concepts.html">api doc</a>
+ */
+public class Router extends ReferenceWithName {
+
+   private final Boolean adminStateUp;
+   private final State state;
+   private final ExternalGatewayInfo externalGatewayInfo;
+
+   @ConstructorProperties({
+      "id", "tenant_id", "name", "admin_state_up", "status", "external_gateway_info"
+   })
+   protected Router(String id, String tenantId, String name, Boolean adminStateUp, State state, ExternalGatewayInfo externalGatewayInfo) {
+      super(id, tenantId, name);
+      this.adminStateUp = adminStateUp;
+      this.state = state;
+      this.externalGatewayInfo = externalGatewayInfo;
+   }
+
+   /**
+    * @return the administrative state of the router
+    */
+   public Boolean getAdminStateUp() {
+      return adminStateUp;
+   }
+
+   /**
+    * @return the current state of the router
+    */
+   public State getState() {
+      return state;
+   }
+
+   /**
+    * @return the information on external gateway for the router
+    */
+   public ExternalGatewayInfo getExternalGatewayInfo() {
+      return externalGatewayInfo;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), adminStateUp, state, externalGatewayInfo);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Router that = Router.class.cast(obj);
+      return super.equals(obj)
+         && Objects.equal(this.adminStateUp, that.adminStateUp)
+         && Objects.equal(this.state, that.state)
+         && Objects.equal(this.externalGatewayInfo, that.externalGatewayInfo);
+   }
+
+   protected Objects.ToStringHelper string() {
+      return super.string()
+         .add("adminStateUp", adminStateUp).add("state", state).add("externalGatewayInfo", externalGatewayInfo != null ? externalGatewayInfo.toString() : "");
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromRouter(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> extends ReferenceWithName.Builder<T> {
+
+      protected Boolean adminStateUp;
+      protected State state;
+      protected ExternalGatewayInfo externalGatewayInfo;
+
+      /**
+       * @see Router#getAdminStateUp()
+       */
+      public T adminStateUp(Boolean adminStateUp) {
+         this.adminStateUp = adminStateUp;
+         return self();
+      }
+
+      /**
+       * @see Router#getState()
+       */
+      public T state(State state) {
+         this.state = state;
+         return self();
+      }
+
+      /**
+       * @see Router#getExternalGatewayInfo()
+       */
+      public T externalGatewayInfo(ExternalGatewayInfo externalGatewayInfo) {
+         this.externalGatewayInfo = externalGatewayInfo;
+         return self();
+      }
+
+      public Router build() {
+         return new Router(id, tenantId, name, adminStateUp, state, externalGatewayInfo);
+      }
+
+      public T fromRouter(Router in) {
+         return super.fromReference(in)
+            .adminStateUp(in.getAdminStateUp())
+            .state(in.getState())
+            .externalGatewayInfo(in.getExternalGatewayInfo());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/RouterInterface.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/RouterInterface.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/RouterInterface.java
new file mode 100644
index 0000000..2601445
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/RouterInterface.java
@@ -0,0 +1,127 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.domain;
+
+import com.google.common.base.Objects;
+
+import java.beans.ConstructorProperties;
+
+/**
+ * A Neutron Router Interface
+ *
+ * @author Nick Livens
+ * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/router_add_interface.html">api doc</a>
+ */
+public class RouterInterface {
+
+   private final String subnetId;
+   private final String portId;
+
+   @ConstructorProperties({
+      "subnet_id", "port_id"
+   })
+   protected RouterInterface(String subnetId, String portId) {
+      this.subnetId = subnetId;
+      this.portId = portId;
+   }
+
+   /**
+    * @return the id of the subnet this router interface is associated with
+    */
+   public String getSubnetId() {
+      return subnetId;
+   }
+
+   /**
+    * @return the id of the port this router interface is associated with
+    */
+   public String getPortId() {
+      return portId;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(subnetId, portId);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      RouterInterface that = RouterInterface.class.cast(obj);
+      return Objects.equal(this.subnetId, that.subnetId) && Objects.equal(this.portId, that.portId);
+   }
+
+   protected Objects.ToStringHelper string() {
+      return Objects.toStringHelper(this)
+         .add("subnetId", subnetId).add("portId", portId);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   public static Builder builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder toBuilder() {
+      return new ConcreteBuilder().fromRouterInterface(this);
+   }
+
+   public static abstract class Builder {
+      protected abstract Builder self();
+
+      protected String subnetId;
+      protected String portId;
+
+      /**
+       * @see RouterInterface#getSubnetId()
+       */
+      public Builder subnetId(String subnetId) {
+         this.subnetId = subnetId;
+         return self();
+      }
+
+      /**
+       * @see RouterInterface#getPortId()
+       */
+      public Builder portId(String portId) {
+         this.portId = portId;
+         return self();
+      }
+
+      public RouterInterface build() {
+         return new RouterInterface(subnetId, portId);
+      }
+
+      public Builder fromRouterInterface(RouterInterface in) {
+         return this.subnetId(in.getSubnetId()).portId(in.getPortId());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Subnet.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Subnet.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Subnet.java
index 31f3237..0a08bb3 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Subnet.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/domain/Subnet.java
@@ -26,8 +26,10 @@ import java.beans.ConstructorProperties;
 import java.util.Collection;
 import java.util.Set;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
- * A Neutron network
+ * A Neutron subnet
  *
  * @author Nick Livens
  * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/Subnets.html">api doc</a>
@@ -50,10 +52,10 @@ public class Subnet extends ReferenceWithName {
                     String gatewayIp, Integer ipVersion, String cidr, Set<AllocationPool> allocationPools,
                     Boolean enableDhcp, Set<String> dnsNameServers, Set<HostRoute> hostRoutes) {
       super(id, tenantId, name);
-      this.networkId = networkId;
+      this.networkId = checkNotNull(networkId, "networkId");
       this.gatewayIp = gatewayIp;
-      this.ipVersion = ipVersion;
-      this.cidr = cidr;
+      this.ipVersion = checkNotNull(ipVersion, "ipVersion");
+      this.cidr = checkNotNull(cidr, "cidr");
       this.allocationPools = allocationPools != null ? ImmutableSet.copyOf(allocationPools) : ImmutableSet.<AllocationPool>of();
       this.enableDhcp = enableDhcp;
       this.dnsNameServers = dnsNameServers != null ? ImmutableSet.copyOf(dnsNameServers) : ImmutableSet.<String>of();

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApi.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApi.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApi.java
new file mode 100644
index 0000000..87e1893
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApi.java
@@ -0,0 +1,217 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.extensions;
+
+import org.jclouds.Fallbacks;
+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_0.domain.ReferenceWithName;
+import org.jclouds.openstack.neutron.v2_0.domain.Router;
+import org.jclouds.openstack.neutron.v2_0.domain.RouterInterface;
+import org.jclouds.openstack.neutron.v2_0.functions.ParseRouterDetails;
+import org.jclouds.openstack.neutron.v2_0.functions.ParseRouters;
+import org.jclouds.openstack.neutron.v2_0.options.CreateRouterOptions;
+import org.jclouds.openstack.neutron.v2_0.options.EmptyOptions;
+import org.jclouds.openstack.neutron.v2_0.options.UpdateRouterOptions;
+import org.jclouds.openstack.v2_0.options.PaginationOptions;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.QueryParams;
+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 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.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+
+import static org.jclouds.openstack.keystone.v2_0.KeystoneFallbacks.EmptyPaginatedCollectionOnNotFoundOr404;
+
+/**
+ * Provides synchronous access to Router operations on the OpenStack Neutron API.
+ * <p/>
+ * A logical entity for forwarding packets across internal subnets and NATting them on external
+ * networks through an appropriate external gateway.
+ *
+ * @author Nick Livens
+ * @see <a href=
+ *      "http://docs.openstack.org/api/openstack-network/2.0/content/router_ext.html">api doc</a>
+ */
+@Path("/v2.0/routers")
+@RequestFilters(AuthenticateRequest.class)
+@Consumes(MediaType.APPLICATION_JSON)
+public interface RouterApi {
+
+   /**
+    * Returns the list of all routers currently defined in Neutron for the current tenant. The list provides the unique
+    * identifier of each router configured for the tenant
+    *
+    * @return the list of all router references configured for the tenant.
+    */
+   @Named("router:list")
+   @GET
+   @ResponseParser(ParseRouters.class)
+   @Transform(ParseRouters.ToPagedIterable.class)
+   @Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
+   @QueryParams(keys = {"fields", "fields", "fields"}, values = {"id", "tenant_id", "name"})
+   PagedIterable<? extends ReferenceWithName> list();
+
+   /**
+    * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/pagination.html">api doc</a>
+    */
+   @Named("router:list")
+   @GET
+   @ResponseParser(ParseRouters.class)
+   @Fallback(EmptyPaginatedCollectionOnNotFoundOr404.class)
+   @QueryParams(keys = {"fields", "fields", "fields"}, values = {"id", "tenant_id", "name"})
+   PagedIterable<? extends ReferenceWithName> list(PaginationOptions options);
+
+   /**
+    * Returns all routers currently defined in Neutron for the current tenant.
+    *
+    * @return the list of all routers configured for the tenant
+    */
+   @Named("router:list")
+   @GET
+   @ResponseParser(ParseRouterDetails.class)
+   @Transform(ParseRouterDetails.ToPagedIterable.class)
+   @Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
+   PagedIterable<? extends Router> listInDetail();
+
+   /**
+    * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/pagination.html">api doc</a>
+    */
+   @Named("router:list")
+   @GET
+   @ResponseParser(ParseRouterDetails.class)
+   @Fallback(EmptyPaginatedCollectionOnNotFoundOr404.class)
+   PagedIterable<? extends Router> listInDetail(PaginationOptions options);
+
+   /**
+    * Returns the specific router.
+    *
+    * @param id the id of the router to return
+    * @return Router or null if not found
+    */
+   @Named("router:get")
+   @GET
+   @Path("/{id}")
+   @SelectJson("router")
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   @Nullable
+   Router get(@PathParam("id") String id);
+
+   /**
+    * Create a new router
+    *
+    * @param options optional arguments
+    * @return the newly created router
+    */
+   @Named("router:create")
+   @POST
+   @SelectJson("router")
+   @MapBinder(CreateRouterOptions.class)
+   Router create(CreateRouterOptions... options);
+
+   /**
+    * Update a router
+    *
+    * @param id the id of the router to update
+    * @param options the attributes to update
+    * @return true if update successful, false if not
+    */
+   @Named("router:update")
+   @PUT
+   @Path("/{id}")
+   @MapBinder(UpdateRouterOptions.class)
+   @Fallback(Fallbacks.FalseOnNotFoundOr404.class)
+   boolean update(@PathParam("id") String id, UpdateRouterOptions... options);
+
+   /**
+    * Deletes the specified router
+    *
+    * @param id the id of the router to delete
+    * @return true if delete successful, false if not
+    */
+   @Named("router:delete")
+   @DELETE
+   @Path("/{id}")
+   @Fallback(Fallbacks.FalseOnNotFoundOr404.class)
+   boolean delete(@PathParam("id") String id);
+
+   /**
+    * Add a interface to a router to connect to the specified subnet
+    *
+    * @param routerId the id of the router to create the interface at
+    * @param subnetId the id of the subnet to connect with the interface
+    * @return the newly-created router interface
+    */
+   @Named("router:addInterfaceForSubnet")
+   @PUT
+   @Path("/{id}/add_router_interface")
+   @MapBinder(EmptyOptions.class)
+   RouterInterface addInterfaceForSubnet(@PathParam("id") String routerId, @PayloadParam("subnet_id") String subnetId);
+
+   /**
+    * Add a interface to a router to connect to the specified port
+    *
+    * @param routerId the id of the router to create the interface at
+    * @param portId the id of the port to connect with the interface
+    * @return the newly-created router interface
+    */
+   @Named("router:addInterfaceForPort")
+   @PUT
+   @Path("{id}/add_router_interface")
+   @MapBinder(EmptyOptions.class)
+   RouterInterface addInterfaceForPort(@PathParam("id") String routerId, @PayloadParam("port_id") String portId);
+
+   /**
+    * Remove the interface where the specified subnet is connected to
+    *
+    * @param routerId the id of the router to remove the interface from
+    * @param subnetId the id of the subnet to disconnect from the interface
+    */
+   @Named("router:removeInterfaceForSubnet")
+   @PUT
+   @Path("/{id}/remove_router_interface")
+   @MapBinder(EmptyOptions.class)
+   boolean removeInterfaceForSubnet(@PathParam("id") String routerId, @PayloadParam("subnet_id") String subnetId);
+
+   /**
+    * Remove the interface where the specified port is connected to
+    *
+    * @param routerId the id of the router to remove the interface from
+    * @param portId the id of the port to disconnect from the interface
+    */
+   @Named("router:removeInterfaceForPort")
+   @PUT
+   @Path("{id}/remove_router_interface")
+   @MapBinder(EmptyOptions.class)
+   boolean removeInterfaceForPort(@PathParam("id") String routerId, @PayloadParam("port_id") String portId);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApi.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApi.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApi.java
index 65550a4..0adf848 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApi.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/NetworkApi.java
@@ -21,7 +21,7 @@ package org.jclouds.openstack.neutron.v2_0.features;
 import com.google.common.collect.FluentIterable;
 import org.jclouds.Fallbacks;
 import org.jclouds.collect.PagedIterable;
-import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
 import org.jclouds.openstack.neutron.v2_0.domain.Network;
 import org.jclouds.openstack.neutron.v2_0.domain.ReferenceWithName;
@@ -82,12 +82,15 @@ public interface NetworkApi {
    @QueryParams(keys = {"fields", "fields", "fields"}, values = {"id", "tenant_id", "name"})
    PagedIterable<? extends ReferenceWithName> list();
 
+   /**
+    * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/pagination.html">api doc</a>
+    */
    @Named("network:list")
    @GET
    @ResponseParser(ParseNetworks.class)
    @Fallback(EmptyPaginatedCollectionOnNotFoundOr404.class)
    @QueryParams(keys = {"fields", "fields", "fields"}, values = {"id", "tenant_id", "name"})
-   PaginatedCollection<? extends ReferenceWithName> list(PaginationOptions options);
+   PagedIterable<? extends ReferenceWithName> list(PaginationOptions options);
 
    /**
     * Returns all networks currently defined in Neutron for the current tenant.
@@ -101,11 +104,14 @@ public interface NetworkApi {
    @Fallback(EmptyPagedIterableOnNotFoundOr404.class)
    PagedIterable<? extends Network> listInDetail();
 
+   /**
+    * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/pagination.html">api doc</a>
+    */
    @Named("network:list")
    @GET
    @ResponseParser(ParseNetworkDetails.class)
    @Fallback(EmptyPaginatedCollectionOnNotFoundOr404.class)
-   PaginatedCollection<? extends Network> listInDetail(PaginationOptions options);
+   PagedIterable<? extends Network> listInDetail(PaginationOptions options);
 
    /**
     * Return a specific network
@@ -118,6 +124,7 @@ public interface NetworkApi {
    @Path("/{id}")
    @SelectJson("network")
    @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   @Nullable
    Network get(@PathParam("id") String id);
 
    /**

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/PortApi.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/PortApi.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/PortApi.java
index db412e7..026c61d 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/PortApi.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/PortApi.java
@@ -21,7 +21,7 @@ package org.jclouds.openstack.neutron.v2_0.features;
 import com.google.common.collect.FluentIterable;
 import org.jclouds.Fallbacks;
 import org.jclouds.collect.PagedIterable;
-import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
 import org.jclouds.openstack.neutron.v2_0.domain.Port;
 import org.jclouds.openstack.neutron.v2_0.domain.ReferenceWithName;
@@ -84,12 +84,15 @@ public interface PortApi {
    @QueryParams(keys = {"fields", "fields", "fields"}, values = {"id", "tenant_id", "name"})
    PagedIterable<? extends ReferenceWithName> list();
 
+   /**
+    * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/pagination.html">api doc</a>
+    */
    @Named("port:list")
    @GET
    @ResponseParser(ParsePorts.class)
    @Fallback(EmptyPaginatedCollectionOnNotFoundOr404.class)
    @QueryParams(keys = {"fields", "fields", "fields"}, values = {"id", "tenant_id", "name"})
-   PaginatedCollection<? extends ReferenceWithName> list(PaginationOptions options);
+   PagedIterable<? extends ReferenceWithName> list(PaginationOptions options);
 
    /**
     * Returns the set of ports currently defined in Neutron for the requested network.
@@ -103,11 +106,14 @@ public interface PortApi {
    @Fallback(EmptyPagedIterableOnNotFoundOr404.class)
    PagedIterable<? extends Port> listInDetail();
 
+   /**
+    * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/pagination.html">api doc</a>
+    */
    @Named("port:list")
    @GET
    @ResponseParser(ParsePortDetails.class)
    @Fallback(EmptyPaginatedCollectionOnNotFoundOr404.class)
-   PaginatedCollection<? extends Port> listInDetail(PaginationOptions options);
+   PagedIterable<? extends Port> listInDetail(PaginationOptions options);
 
    /**
     * Returns the specific port
@@ -120,6 +126,7 @@ public interface PortApi {
    @Path("/{id}")
    @SelectJson("port")
    @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   @Nullable
    Port get(@PathParam("id") String id);
 
    /**

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApi.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApi.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApi.java
index e83096e..46f40a0 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApi.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/features/SubnetApi.java
@@ -21,7 +21,7 @@ package org.jclouds.openstack.neutron.v2_0.features;
 import com.google.common.collect.FluentIterable;
 import org.jclouds.Fallbacks;
 import org.jclouds.collect.PagedIterable;
-import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
 import org.jclouds.openstack.neutron.v2_0.domain.ReferenceWithName;
 import org.jclouds.openstack.neutron.v2_0.domain.Subnet;
@@ -80,12 +80,15 @@ public interface SubnetApi {
    @QueryParams(keys = {"fields", "fields", "fields"}, values = {"id", "tenant_id", "name"})
    PagedIterable<? extends ReferenceWithName> list();
 
+   /**
+    * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/pagination.html">api doc</a>
+    */
    @Named("subnet:list")
    @GET
    @ResponseParser(ParseSubnets.class)
    @Fallback(EmptyPaginatedCollectionOnNotFoundOr404.class)
    @QueryParams(keys = {"fields", "fields", "fields"}, values = {"id", "tenant_id", "name"})
-   PaginatedCollection<? extends ReferenceWithName> list(PaginationOptions options);
+   PagedIterable<? extends ReferenceWithName> list(PaginationOptions options);
 
    /**
     * Returns all subnets currently defined in Neutron for the current tenant.
@@ -99,11 +102,14 @@ public interface SubnetApi {
    @Fallback(EmptyPagedIterableOnNotFoundOr404.class)
    PagedIterable<? extends Subnet> listInDetail();
 
+   /**
+    * @see <a href="http://docs.openstack.org/api/openstack-network/2.0/content/pagination.html">api doc</a>
+    */
    @Named("subnet:list")
    @GET
    @ResponseParser(ParseSubnetDetails.class)
    @Fallback(EmptyPaginatedCollectionOnNotFoundOr404.class)
-   PaginatedCollection<? extends Subnet> listInDetail(PaginationOptions options);
+   PagedIterable<? extends Subnet> listInDetail(PaginationOptions options);
 
    /**
     * Returns the specific Subnet.
@@ -116,6 +122,7 @@ public interface SubnetApi {
    @Path("/{id}")
    @SelectJson("subnet")
    @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   @Nullable
    Subnet get(@PathParam("id") String id);
 
    /**

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworkDetails.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworkDetails.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworkDetails.java
index e641c70..ffc3df2 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworkDetails.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworkDetails.java
@@ -47,8 +47,8 @@ public class ParseNetworkDetails extends ParseJson<Networks> {
    static class Networks extends PaginatedCollection<Network> {
 
       @ConstructorProperties({ "networks", "networks_links" })
-      protected Networks(Iterable<Network> networks, Iterable<Link> networks_links) {
-         super(networks, networks_links);
+      protected Networks(Iterable<Network> networks, Iterable<Link> networksLinks) {
+         super(networks, networksLinks);
       }
 
    }
@@ -82,7 +82,7 @@ public class ParseNetworkDetails extends ParseJson<Networks> {
 
             @Override
             public String toString() {
-               return "list()";
+               return "listNetworksInDetail()";
             }
          };
       }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworks.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworks.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworks.java
index a45fee8..35d0f1c 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworks.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseNetworks.java
@@ -47,8 +47,8 @@ public class ParseNetworks extends ParseJson<Networks> {
    static class Networks extends PaginatedCollection<ReferenceWithName> {
 
       @ConstructorProperties({ "networks", "networks_links" })
-      protected Networks(Iterable<ReferenceWithName> networks, Iterable<Link> networks_links) {
-         super(networks, networks_links);
+      protected Networks(Iterable<ReferenceWithName> networks, Iterable<Link> networksLinks) {
+         super(networks, networksLinks);
       }
 
    }
@@ -82,7 +82,7 @@ public class ParseNetworks extends ParseJson<Networks> {
 
             @Override
             public String toString() {
-               return "list()";
+               return "listNetworks()";
             }
          };
       }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePortDetails.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePortDetails.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePortDetails.java
index e545f01..e0c53ed 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePortDetails.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePortDetails.java
@@ -48,8 +48,8 @@ public class ParsePortDetails extends ParseJson<Ports> {
    static class Ports extends PaginatedCollection<Port> {
 
       @ConstructorProperties({ "ports", "ports_links" })
-      protected Ports(Iterable<Port> ports, Iterable<Link> ports_links) {
-         super(ports, ports_links);
+      protected Ports(Iterable<Port> ports, Iterable<Link> portsLinks) {
+         super(ports, portsLinks);
       }
 
    }
@@ -83,7 +83,7 @@ public class ParsePortDetails extends ParseJson<Ports> {
 
             @Override
             public String toString() {
-               return "list()";
+               return "listPortsInDetail()";
             }
          };
       }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePorts.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePorts.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePorts.java
index 028c5d1..9295df9 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePorts.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParsePorts.java
@@ -47,8 +47,8 @@ public class ParsePorts extends ParseJson<Ports> {
    static class Ports extends PaginatedCollection<ReferenceWithName> {
 
       @ConstructorProperties({ "ports", "ports_links" })
-      protected Ports(Iterable<ReferenceWithName> ports, Iterable<Link> ports_links) {
-         super(ports, ports_links);
+      protected Ports(Iterable<ReferenceWithName> ports, Iterable<Link> portsLinks) {
+         super(ports, portsLinks);
       }
 
    }
@@ -82,7 +82,7 @@ public class ParsePorts extends ParseJson<Ports> {
 
             @Override
             public String toString() {
-               return "list()";
+               return "listPorts()";
             }
          };
       }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseRouterDetails.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseRouterDetails.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseRouterDetails.java
new file mode 100644
index 0000000..d1267a9
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseRouterDetails.java
@@ -0,0 +1,93 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.functions;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.inject.TypeLiteral;
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.collect.internal.Arg0ToPagedIterable;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.openstack.neutron.v2_0.NeutronApi;
+import org.jclouds.openstack.neutron.v2_0.domain.Router;
+import org.jclouds.openstack.neutron.v2_0.extensions.RouterApi;
+import org.jclouds.openstack.neutron.v2_0.functions.ParseRouterDetails.Routers;
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.beans.ConstructorProperties;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.openstack.v2_0.options.PaginationOptions.Builder.marker;
+
+/**
+ * @author Nick Livens
+ */
+@Beta
+@Singleton
+public class ParseRouterDetails extends ParseJson<Routers> {
+   static class Routers extends PaginatedCollection<Router> {
+
+      @ConstructorProperties({ "routers", "routers_links" })
+      protected Routers(Iterable<Router> routers, Iterable<Link> routersLinks) {
+         super(routers, routersLinks);
+      }
+
+   }
+
+   @Inject
+   public ParseRouterDetails(Json json) {
+      super(json, TypeLiteral.get(Routers.class));
+   }
+
+   public static class ToPagedIterable extends Arg0ToPagedIterable.FromCaller<Router, ToPagedIterable> {
+
+      private final NeutronApi api;
+
+      @Inject
+      protected ToPagedIterable(NeutronApi api) {
+         this.api = checkNotNull(api, "api");
+      }
+
+      @Override
+      protected Function<Object, IterableWithMarker<Router>> markerToNextForArg0(Optional<Object> arg0) {
+         String zone = arg0.isPresent() ? arg0.get().toString() : null;
+         final RouterApi routerApi = api.getRouterExtensionForZone(zone).get();
+         return new Function<Object, IterableWithMarker<Router>>() {
+
+            @SuppressWarnings("unchecked")
+            @Override
+            public IterableWithMarker<Router> apply(Object input) {
+               return IterableWithMarker.class.cast(routerApi.listInDetail(marker(input.toString())));
+            }
+
+            @Override
+            public String toString() {
+               return "listRoutersInDetail()";
+            }
+         };
+      }
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseRouters.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseRouters.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseRouters.java
new file mode 100644
index 0000000..9a1fe9c
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseRouters.java
@@ -0,0 +1,93 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.functions;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.inject.TypeLiteral;
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.collect.internal.Arg0ToPagedIterable;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.openstack.neutron.v2_0.NeutronApi;
+import org.jclouds.openstack.neutron.v2_0.domain.ReferenceWithName;
+import org.jclouds.openstack.neutron.v2_0.extensions.RouterApi;
+import org.jclouds.openstack.neutron.v2_0.functions.ParseRouters.Routers;
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.beans.ConstructorProperties;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.openstack.v2_0.options.PaginationOptions.Builder.marker;
+
+/**
+ * @author Nick Livens
+ */
+@Beta
+@Singleton
+public class ParseRouters extends ParseJson<Routers> {
+   static class Routers extends PaginatedCollection<ReferenceWithName> {
+
+      @ConstructorProperties({ "routers", "routers_links" })
+      protected Routers(Iterable<ReferenceWithName> routers, Iterable<Link> routersLinks) {
+         super(routers, routersLinks);
+      }
+
+   }
+
+   @Inject
+   public ParseRouters(Json json) {
+      super(json, TypeLiteral.get(Routers.class));
+   }
+
+   public static class ToPagedIterable extends Arg0ToPagedIterable.FromCaller<ReferenceWithName, ToPagedIterable> {
+
+      private final NeutronApi api;
+
+      @Inject
+      protected ToPagedIterable(NeutronApi api) {
+         this.api = checkNotNull(api, "api");
+      }
+
+      @Override
+      protected Function<Object, IterableWithMarker<ReferenceWithName>> markerToNextForArg0(Optional<Object> arg0) {
+         String zone = arg0.isPresent() ? arg0.get().toString() : null;
+         final RouterApi routerApi = api.getRouterExtensionForZone(zone).get();
+         return new Function<Object, IterableWithMarker<ReferenceWithName>>() {
+
+            @SuppressWarnings("unchecked")
+            @Override
+            public IterableWithMarker<ReferenceWithName> apply(Object input) {
+               return IterableWithMarker.class.cast(routerApi.list(marker(input.toString())));
+            }
+
+            @Override
+            public String toString() {
+               return "listRouters()";
+            }
+         };
+      }
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnetDetails.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnetDetails.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnetDetails.java
index dc3af6e..4ef5928 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnetDetails.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnetDetails.java
@@ -47,8 +47,8 @@ public class ParseSubnetDetails extends ParseJson<Subnets> {
    static class Subnets extends PaginatedCollection<Subnet> {
 
       @ConstructorProperties({ "subnets", "subnets_links" })
-      protected Subnets(Iterable<Subnet> subnets, Iterable<Link> subnets_links) {
-         super(subnets, subnets_links);
+      protected Subnets(Iterable<Subnet> subnets, Iterable<Link> subnetsLinks) {
+         super(subnets, subnetsLinks);
       }
 
    }
@@ -82,7 +82,7 @@ public class ParseSubnetDetails extends ParseJson<Subnets> {
 
             @Override
             public String toString() {
-               return "list()";
+               return "listSubnetsInDetail()";
             }
          };
       }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnets.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnets.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnets.java
index 4f2fb43..ba7b731 100644
--- a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnets.java
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/functions/ParseSubnets.java
@@ -47,8 +47,8 @@ public class ParseSubnets extends ParseJson<Subnets> {
    static class Subnets extends PaginatedCollection<ReferenceWithName> {
 
       @ConstructorProperties({ "subnets", "subnets_links" })
-      protected Subnets(Iterable<ReferenceWithName> subnets, Iterable<Link> subnets_links) {
-         super(subnets, subnets_links);
+      protected Subnets(Iterable<ReferenceWithName> subnets, Iterable<Link> subnetsLinks) {
+         super(subnets, subnetsLinks);
       }
 
    }
@@ -82,7 +82,7 @@ public class ParseSubnets extends ParseJson<Subnets> {
 
             @Override
             public String toString() {
-               return "list()";
+               return "listSubnets()";
             }
          };
       }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/CreateRouterOptions.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/CreateRouterOptions.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/CreateRouterOptions.java
new file mode 100644
index 0000000..17f3f82
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/CreateRouterOptions.java
@@ -0,0 +1,168 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.options;
+
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.openstack.neutron.v2_0.domain.ExternalGatewayInfo;
+import org.jclouds.rest.MapBinder;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+/**
+ * @author Nick Livens
+ */
+public class CreateRouterOptions implements MapBinder {
+
+   @Inject
+   private BindToJsonPayload jsonBinder;
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromCreateRouterOptions(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String name;
+      protected Boolean adminStateUp;
+      protected ExternalGatewayInfo externalGatewayInfo;
+
+      /**
+       * @see CreateRouterOptions#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /**
+       * @see CreateRouterOptions#getAdminStateUp()
+       */
+      public T adminStateUp(Boolean adminStateUp) {
+         this.adminStateUp = adminStateUp;
+         return self();
+      }
+
+      /**
+       * @see CreateRouterOptions#getExternalGatewayInfo()
+       */
+      public T externalGatewayInfo(ExternalGatewayInfo externalGatewayInfo) {
+         this.externalGatewayInfo = externalGatewayInfo;
+         return self();
+      }
+
+      public CreateRouterOptions build() {
+         return new CreateRouterOptions(name, adminStateUp, externalGatewayInfo);
+      }
+
+      public T fromCreateRouterOptions(CreateRouterOptions options) {
+         return this.name(options.getName())
+            .adminStateUp(options.getAdminStateUp())
+            .externalGatewayInfo(options.getExternalGatewayInfo());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   protected static class CreateRouterRequest {
+      protected String name;
+      protected Boolean admin_state_up;
+      protected ExternalGatewayInfo external_gateway_info;
+
+      protected CreateRouterRequest() {
+      }
+
+      protected static final class ExternalGatewayInfo {
+         protected String network_id;
+
+         protected ExternalGatewayInfo(String network_id) {
+            this.network_id = network_id;
+         }
+      }
+   }
+
+   protected String name;
+   protected Boolean adminStateUp;
+   protected ExternalGatewayInfo externalGatewayInfo;
+
+   protected CreateRouterOptions() {
+      this.name = null;
+      this.adminStateUp = null;
+      this.externalGatewayInfo = null;
+   }
+
+   public CreateRouterOptions(String name, Boolean adminStateUp, ExternalGatewayInfo externalGatewayInfo) {
+      this.name = name;
+      this.adminStateUp = adminStateUp;
+      this.externalGatewayInfo = externalGatewayInfo;
+   }
+
+   /**
+    * @return the name for the router
+    */
+   public String getName() {
+      return name;
+   }
+
+   /**
+    * @return the administrative state of the router
+    */
+   public Boolean getAdminStateUp() {
+      return adminStateUp;
+   }
+
+   /**
+    * @return the external gateway info for the router
+    */
+   public ExternalGatewayInfo getExternalGatewayInfo() {
+      return externalGatewayInfo;
+   }
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
+      CreateRouterRequest createRouterRequest = new CreateRouterRequest();
+
+      if (this.name != null)
+         createRouterRequest.name = this.name;
+      if (this.adminStateUp != null)
+         createRouterRequest.admin_state_up = this.adminStateUp;
+      if (this.externalGatewayInfo != null)
+         createRouterRequest.external_gateway_info = new CreateRouterRequest.ExternalGatewayInfo(this.externalGatewayInfo.getNetworkId());
+
+      return bindToRequest(request, ImmutableMap.of("router", createRouterRequest));
+   }
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      return jsonBinder.bindToRequest(request, input);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/EmptyOptions.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/EmptyOptions.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/EmptyOptions.java
new file mode 100644
index 0000000..0ab3e2e
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/EmptyOptions.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.options;
+
+import com.google.inject.Inject;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.MapBinder;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import java.util.Map;
+
+/**
+ * This class is used for methods who don't need a wrapper around their JSON body
+ *
+ * @author Nick Livens
+ */
+public class EmptyOptions implements MapBinder {
+
+   @Inject
+   private BindToJsonPayload jsonBinder;
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
+      return bindToRequest(request, (Object) postParams);
+   }
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      return jsonBinder.bindToRequest(request, input);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/UpdateRouterOptions.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/UpdateRouterOptions.java b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/UpdateRouterOptions.java
new file mode 100644
index 0000000..378296c
--- /dev/null
+++ b/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2_0/options/UpdateRouterOptions.java
@@ -0,0 +1,168 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.options;
+
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.openstack.neutron.v2_0.domain.ExternalGatewayInfo;
+import org.jclouds.rest.MapBinder;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+/**
+ * @author Nick Livens
+ */
+public class UpdateRouterOptions implements MapBinder {
+
+   @Inject
+   private BindToJsonPayload jsonBinder;
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromUpdateRouterOptions(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String name;
+      protected Boolean adminStateUp;
+      protected ExternalGatewayInfo externalGatewayInfo;
+
+      /**
+       * @see UpdateRouterOptions#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /**
+       * @see UpdateRouterOptions#getAdminStateUp()
+       */
+      public T adminStateUp(Boolean adminStateUp) {
+         this.adminStateUp = adminStateUp;
+         return self();
+      }
+
+      /**
+       * @see UpdateRouterOptions#getExternalGatewayInfo()
+       */
+      public T externalGatewayInfo(ExternalGatewayInfo externalGatewayInfo) {
+         this.externalGatewayInfo = externalGatewayInfo;
+         return self();
+      }
+
+      public UpdateRouterOptions build() {
+         return new UpdateRouterOptions(name, adminStateUp, externalGatewayInfo);
+      }
+
+      public T fromUpdateRouterOptions(UpdateRouterOptions options) {
+         return this.name(options.getName())
+            .adminStateUp(options.getAdminStateUp())
+            .externalGatewayInfo(options.getExternalGatewayInfo());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   protected static class UpdateRouterRequest {
+      protected String name;
+      protected Boolean admin_state_up;
+      protected ExternalGatewayInfo external_gateway_info;
+
+      protected UpdateRouterRequest() {
+      }
+
+      protected static final class ExternalGatewayInfo {
+         protected String network_id;
+
+         protected ExternalGatewayInfo(String network_id) {
+            this.network_id = network_id;
+         }
+      }
+   }
+
+   protected String name;
+   protected Boolean adminStateUp;
+   protected ExternalGatewayInfo externalGatewayInfo;
+
+   protected UpdateRouterOptions() {
+      this.name = null;
+      this.adminStateUp = null;
+      this.externalGatewayInfo = null;
+   }
+
+   public UpdateRouterOptions(String name, Boolean adminStateUp, ExternalGatewayInfo externalGatewayInfo) {
+      this.name = name;
+      this.adminStateUp = adminStateUp;
+      this.externalGatewayInfo = externalGatewayInfo;
+   }
+
+   /**
+    * @return the new name for the router
+    */
+   public String getName() {
+      return name;
+   }
+
+   /**
+    * @return the new administrative state for the router
+    */
+   public Boolean getAdminStateUp() {
+      return adminStateUp;
+   }
+
+   /**
+    * @return the new information on external gateway for the router
+    */
+   public ExternalGatewayInfo getExternalGatewayInfo() {
+      return externalGatewayInfo;
+   }
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
+      UpdateRouterRequest updateRouterRequest = new UpdateRouterRequest();
+
+      if (this.name != null)
+         updateRouterRequest.name = this.name;
+      if (this.adminStateUp != null)
+         updateRouterRequest.admin_state_up = this.adminStateUp;
+      if (this.externalGatewayInfo != null)
+         updateRouterRequest.external_gateway_info = new UpdateRouterRequest.ExternalGatewayInfo(this.externalGatewayInfo.getNetworkId());
+
+      return bindToRequest(request, ImmutableMap.of("router", updateRouterRequest));
+   }
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      return jsonBinder.bindToRequest(request, input);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-openstack/blob/57a9087f/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApiExpectTest.java
----------------------------------------------------------------------
diff --git a/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApiExpectTest.java b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApiExpectTest.java
new file mode 100644
index 0000000..f685506
--- /dev/null
+++ b/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2_0/extensions/RouterApiExpectTest.java
@@ -0,0 +1,295 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds 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_0.extensions;
+
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.neutron.v2_0.domain.ExternalGatewayInfo;
+import org.jclouds.openstack.neutron.v2_0.domain.ReferenceWithName;
+import org.jclouds.openstack.neutron.v2_0.domain.Router;
+import org.jclouds.openstack.neutron.v2_0.domain.RouterInterface;
+import org.jclouds.openstack.neutron.v2_0.domain.State;
+import org.jclouds.openstack.neutron.v2_0.internal.BaseNeutronApiExpectTest;
+import org.jclouds.openstack.neutron.v2_0.options.CreateRouterOptions;
+import org.jclouds.openstack.neutron.v2_0.options.UpdateRouterOptions;
+import org.jclouds.openstack.neutron.v2_0.parse.ParseRouterTest;
+import org.jclouds.rest.AuthorizationException;
+import org.testng.annotations.Test;
+
+import javax.ws.rs.core.MediaType;
+import java.util.Set;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Tests parsing and Guice wiring of RouterApi
+ *
+ * @author Nick Livens
+ */
+@Test(groups = "unit", testName = "RouterApiExpectTest")
+public class RouterApiExpectTest extends BaseNeutronApiExpectTest {
+
+   private static final String ZONE = "region-a.geo-1";
+
+   public void testListReferencesReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers").addQueryParam("fields", "id", "tenant_id", "name").build(),
+         HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/list_routers.json", APPLICATION_JSON)).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      Set<? extends ReferenceWithName> references = api.list().concat().toSet();
+      assertEquals(references, listOfReferencesWithNames());
+   }
+
+   public void testListReferencesReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers").addQueryParam("fields", "id", "tenant_id", "name").build(),
+         HttpResponse.builder().statusCode(404).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      assertTrue(api.list().concat().isEmpty());
+   }
+
+   public void testListReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers").build(),
+         HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/list_routers.json", APPLICATION_JSON)).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      Set<? extends Router> routers = api.listInDetail().concat().toSet();
+      assertEquals(routers, listOfRouters());
+   }
+
+   public void testListReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers").build(),
+         HttpResponse.builder().statusCode(404).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      assertTrue(api.listInDetail().concat().isEmpty());
+   }
+
+   public void testGetReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/16dba3bc-f3fa-4775-afdc-237e12c72f6a").build(),
+         HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/router.json", APPLICATION_JSON)).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      Router router = api.get("16dba3bc-f3fa-4775-afdc-237e12c72f6a");
+      assertEquals(router, new ParseRouterTest().expected());
+   }
+
+   public void testGetReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/16dba3bc-f3fa-4775-afdc-237e12c72f6a").build(),
+         HttpResponse.builder().statusCode(404).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      assertNull(api.get("16dba3bc-f3fa-4775-afdc-237e12c72f6a"));
+   }
+
+   public void testCreateReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers").method("POST")
+            .payload(payloadFromStringWithContentType("{\"router\":{\"name\":\"test\",\"external_gateway_info\":{\"network_id\":\"1234567890\"}}}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(200).payload(payloadFromStringWithContentType("{\"router\":{\"id\":\"12345\",\"tenant_id\":\"6789\",\"external_gateway_info\":{\"network_id\":\"1234567890\"}}}", APPLICATION_JSON)).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      Router router = api.create(CreateRouterOptions.builder().name("test").externalGatewayInfo(ExternalGatewayInfo.builder().networkId("1234567890").build()).build());
+      assertEquals(router, Router.builder().id("12345").tenantId("6789").externalGatewayInfo(ExternalGatewayInfo.builder().networkId("1234567890").build()).build());
+   }
+
+   @Test(expectedExceptions = AuthorizationException.class)
+   public void testCreateReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers").method("POST")
+            .payload(payloadFromStringWithContentType("{\"router\":{\"name\":\"another-test\",\"external_gateway_info\":{\"network_id\":\"1234567890\"}}}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(401).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      api.create(CreateRouterOptions.builder().name("another-test").externalGatewayInfo(ExternalGatewayInfo.builder().networkId("1234567890").build()).build());
+   }
+
+   public void testUpdateReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"router\":{\"name\":\"another-test\",\"admin_state_up\":true}}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(200).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      assertTrue(api.update("12345", UpdateRouterOptions.builder().name("another-test").adminStateUp(true).build()));
+   }
+
+   public void testUpdateReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"router\":{\"name\":\"another-test\",\"admin_state_up\":true}}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(404).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      assertFalse(api.update("12345", UpdateRouterOptions.builder().name("another-test").adminStateUp(true).build()));
+   }
+
+   public void testDeleteReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345").method("DELETE").build(),
+         HttpResponse.builder().statusCode(200).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      assertTrue(api.delete("12345"));
+   }
+
+   @Test(expectedExceptions = AuthorizationException.class)
+   public void testDeleteReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345").method("DELETE").build(),
+         HttpResponse.builder().statusCode(403).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      api.delete("12345");
+   }
+
+   public void testAddInterfaceForSubnetReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345/add_router_interface").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"subnet_id\":\"1234567890\"}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(200).payload(payloadFromStringWithContentType("{\"subnet_id\":\"1234567890\",\"port_id\":\"987654321\"}", MediaType.APPLICATION_JSON)).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      RouterInterface routerInterface = api.addInterfaceForSubnet("12345", "1234567890");
+      assertEquals(routerInterface, RouterInterface.builder().subnetId("1234567890").portId("987654321").build());
+   }
+
+   @Test(expectedExceptions = AuthorizationException.class)
+   public void testAddInterfaceForSubnetReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345/add_router_interface").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"subnet_id\":\"1234567890\"}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(403).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      api.addInterfaceForSubnet("12345", "1234567890");
+   }
+
+   public void testAddInterfaceForPortReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345/add_router_interface").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"port_id\":\"987654321\"}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(200).payload(payloadFromStringWithContentType("{\"subnet_id\":\"1234567890\",\"port_id\":\"987654321\"}", MediaType.APPLICATION_JSON)).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      RouterInterface routerInterface = api.addInterfaceForPort("12345", "987654321");
+      assertEquals(routerInterface, RouterInterface.builder().subnetId("1234567890").portId("987654321").build());
+   }
+
+   @Test(expectedExceptions = AuthorizationException.class)
+   public void testAddInterfaceForPortReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345/add_router_interface").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"port_id\":\"1234567890\"}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(403).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      api.addInterfaceForPort("12345", "1234567890");
+   }
+
+   public void testRemoveInterfaceForSubnetReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345/remove_router_interface").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"subnet_id\":\"1234567890\"}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(200).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      api.removeInterfaceForSubnet("12345", "1234567890");
+   }
+
+   @Test(expectedExceptions = AuthorizationException.class)
+   public void testRemoveInterfaceForSubnetReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345/remove_router_interface").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"subnet_id\":\"1234567890\"}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(403).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      api.removeInterfaceForSubnet("12345", "1234567890");
+   }
+
+   public void testRemoveInterfaceForPortReturns2xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345/remove_router_interface").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"port_id\":\"1234567890\"}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(200).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      api.removeInterfaceForPort("12345", "1234567890");
+   }
+
+   @Test(expectedExceptions = AuthorizationException.class)
+   public void testRemoveInterfaceForPortReturns4xx() {
+      RouterApi api = requestsSendResponses(
+         keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
+         authenticatedGET().endpoint(endpoint + "/routers/12345/remove_router_interface").method("PUT")
+            .payload(payloadFromStringWithContentType("{\"port_id\":\"1234567890\"}", MediaType.APPLICATION_JSON)).build(),
+         HttpResponse.builder().statusCode(403).build())
+         .getRouterExtensionForZone(ZONE).get();
+
+      api.removeInterfaceForPort("12345", "1234567890");
+   }
+
+   protected Set<Router> listOfRouters() {
+      return ImmutableSet.of(
+         Router.builder().state(State.ACTIVE).name("jclouds-test").tenantId("1234567890").id("16dba3bc-f3fa-4775-afdc-237e12c72f6a").build(),
+         Router.builder().state(State.ACTIVE).name("wibble").tenantId("1234567890").id("1a104cf5-cb18-4d35-9407-2fd2646d9d0b").build(),
+         Router.builder().state(State.ACTIVE).name("jclouds-test").tenantId("1234567890").id("31083ae2-420d-48b2-ac98-9f7a4fd8dbdc").build(),
+         Router.builder().state(State.ACTIVE).name("jclouds-test").tenantId("1234567890").id("49c6d6fa-ff2a-459d-b975-75a8d31c9a89").build(),
+         Router.builder().state(State.ACTIVE).name("wibble").tenantId("1234567890").id("5cb3d6f4-62cb-41c9-b964-ba7d9df79e4e").build(),
+         Router.builder().state(State.ACTIVE).name("jclouds-test").tenantId("1234567890").id("5d51d012-3491-4db7-b1b5-6f254015015d").build(),
+         Router.builder().state(State.ACTIVE).name("wibble").tenantId("1234567890").id("5f9cf7dc-22ca-4097-8e49-1cc8b23faf17").build(),
+         Router.builder().state(State.ACTIVE).name("jclouds-test").tenantId("1234567890").id("6319ecad-6bff-48b2-9b53-02ede8cb7588").build(),
+         Router.builder().state(State.ACTIVE).name("jclouds-test").tenantId("1234567890").id("6ba4c788-661f-49ab-9bf8-5f10cbbb2f57").build(),
+         Router.builder().state(State.ACTIVE).name("jclouds-test").tenantId("1234567890").id("74ed170b-5069-4353-ab38-9719766dc57e").build(),
+         Router.builder().state(State.ACTIVE).name("wibble").tenantId("1234567890").id("b71fcac1-e864-4031-8c5b-edbecd9ece36").build(),
+         Router.builder().state(State.ACTIVE).name("jclouds-test").tenantId("1234567890").id("c7681895-d84d-4650-9ca0-82c72036b855").build()
+      );
+   }
+
+}