You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2016/09/28 13:54:12 UTC

jclouds-labs git commit: JCLOUDS-1181 oneandone-vpn-api

Repository: jclouds-labs
Updated Branches:
  refs/heads/master c458bafdf -> 4e3972023


JCLOUDS-1181 oneandone-vpn-api


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

Branch: refs/heads/master
Commit: 4e3972023e6d5457800379bd9dbb7f61f2d1737f
Parents: c458baf
Author: alibazlamit <al...@ali-G751JT>
Authored: Wed Sep 28 13:28:11 2016 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Wed Sep 28 15:47:10 2016 +0200

----------------------------------------------------------------------
 .../jclouds/oneandone/rest/OneAndOneApi.java    |   4 +
 .../jclouds/oneandone/rest/domain/Types.java    |  15 ++
 .../oneandone/rest/domain/VPNConfig.java        |  32 ++++
 .../jclouds/oneandone/rest/domain/Vpn.java      |  90 +++++++++
 .../jclouds/oneandone/rest/features/VpnApi.java |  88 +++++++++
 .../oneandone/rest/util/VPNConfigParser.java    |  47 +++++
 .../oneandone/rest/features/VpnApiLiveTest.java |  97 ++++++++++
 .../oneandone/rest/features/VpnApiMockTest.java | 183 +++++++++++++++++++
 .../rest/internal/BaseOneAndOneLiveTest.java    |  19 +-
 .../src/test/resources/sharedStorage/get.json   |  24 +++
 .../resources/sharedStorage/list.access.json    |  24 +++
 .../src/test/resources/sharedStorage/list.json  |  62 +++++++
 .../resources/sharedStorage/list.options.json   |  62 +++++++
 .../resources/sharedStorage/server.get.json     |   5 +
 .../resources/sharedStorage/servers.list.json   |  12 ++
 .../src/test/resources/vpn/configuration.json   |   3 +
 oneandone/src/test/resources/vpn/get.json       |  18 ++
 oneandone/src/test/resources/vpn/list.json      |  38 ++++
 .../src/test/resources/vpn/list.options.json    |  38 ++++
 19 files changed, 860 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/OneAndOneApi.java
----------------------------------------------------------------------
diff --git a/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/OneAndOneApi.java b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/OneAndOneApi.java
index 2b0e9d7..c12a75f 100644
--- a/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/OneAndOneApi.java
+++ b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/OneAndOneApi.java
@@ -24,6 +24,7 @@ import org.apache.jclouds.oneandone.rest.features.PrivateNetworkApi;
 import org.apache.jclouds.oneandone.rest.features.PublicIpApi;
 import org.apache.jclouds.oneandone.rest.features.ServerApi;
 import org.apache.jclouds.oneandone.rest.features.SharedStorageApi;
+import org.apache.jclouds.oneandone.rest.features.VpnApi;
 import org.jclouds.rest.annotations.Delegate;
 
 public interface OneAndOneApi extends Closeable {
@@ -48,4 +49,7 @@ public interface OneAndOneApi extends Closeable {
 
    @Delegate
    PrivateNetworkApi privateNetworkApi();
+
+   @Delegate
+   VpnApi vpnApi();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/Types.java
----------------------------------------------------------------------
diff --git a/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/Types.java b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/Types.java
index b9dc4c1..6d8c67b 100644
--- a/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/Types.java
+++ b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/Types.java
@@ -185,6 +185,21 @@ public class Types {
       public static IPOwner fromValue(String v) {
          return Enums.getIfPresent(IPOwner.class, v).or(UNRECOGNIZED);
       }
+   }
+
+   public enum VPNState {
+      ACTIVE, UNRECOGNIZED;
 
+      public static VPNState fromValue(String v) {
+         return Enums.getIfPresent(VPNState.class, v).or(UNRECOGNIZED);
+      }
+   }
+
+   public enum VPNType {
+      SSL, UNRECOGNIZED;
+
+      public static VPNType fromValue(String v) {
+         return Enums.getIfPresent(VPNType.class, v).or(UNRECOGNIZED);
+      }
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/VPNConfig.java
----------------------------------------------------------------------
diff --git a/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/VPNConfig.java b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/VPNConfig.java
new file mode 100644
index 0000000..4170413
--- /dev/null
+++ b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/VPNConfig.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jclouds.oneandone.rest.domain;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.json.SerializedNames;
+
+@AutoValue
+public abstract class VPNConfig {
+
+   public abstract String content();
+
+   @SerializedNames({"config_zip_file"})
+   public static VPNConfig create(String content) {
+      return new AutoValue_VPNConfig(content);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/Vpn.java
----------------------------------------------------------------------
diff --git a/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/Vpn.java b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/Vpn.java
new file mode 100644
index 0000000..ea5dc9a
--- /dev/null
+++ b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/domain/Vpn.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jclouds.oneandone.rest.domain;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.apache.jclouds.oneandone.rest.domain.Types.GenericState;
+import org.apache.jclouds.oneandone.rest.domain.Types.VPNType;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+@AutoValue
+public abstract class Vpn {
+
+   public abstract String id();
+
+   public abstract String name();
+
+   @Nullable
+   public abstract String description();
+
+   @Nullable
+   public abstract GenericState state();
+
+   @Nullable
+   public abstract DataCenter datacenter();
+
+   @Nullable
+   public abstract VPNType type();
+
+   @Nullable
+   public abstract String creationDate();
+
+   @Nullable
+   public abstract String cloudpanelId();
+
+   @Nullable
+   public abstract List<String> ips();
+
+   @SerializedNames({"id", "name", "description", "state", "datacenter", "type", "creation_date", "cloudpanel_id", "ips"})
+   public static Vpn create(String id, String name, String description, GenericState state, DataCenter datacenter, VPNType type, String creationDate, String cloudpanelId, List<String> ips) {
+      return new AutoValue_Vpn(id, name, description, state, datacenter, type, creationDate, cloudpanelId, ips == null ? ImmutableList.<String>of() : ImmutableList.copyOf(ips));
+   }
+
+   @AutoValue
+   public abstract static class CreateVpn {
+
+      public abstract String name();
+
+      @Nullable
+      public abstract String description();
+
+      @Nullable
+      public abstract String datacenterId();
+
+      @SerializedNames({"name", "description", "datacenter_id"})
+      public static CreateVpn create(String name, String description, String datacenterId) {
+         return new AutoValue_Vpn_CreateVpn(name, description, datacenterId);
+      }
+   }
+
+   @AutoValue
+   public abstract static class UpdateVpn {
+
+      public abstract String name();
+
+      @Nullable
+      public abstract String description();
+
+      @SerializedNames({"name", "description"})
+      public static UpdateVpn create(String name, String description) {
+         return new AutoValue_Vpn_UpdateVpn(name, description);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/features/VpnApi.java
----------------------------------------------------------------------
diff --git a/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/features/VpnApi.java b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/features/VpnApi.java
new file mode 100644
index 0000000..74374e9
--- /dev/null
+++ b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/features/VpnApi.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jclouds.oneandone.rest.features;
+
+import java.util.List;
+import java.util.zip.ZipInputStream;
+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.Produces;
+import org.apache.jclouds.oneandone.rest.domain.Vpn;
+import org.apache.jclouds.oneandone.rest.domain.options.GenericQueryOptions;
+import org.apache.jclouds.oneandone.rest.filters.AuthenticateRequest;
+import org.apache.jclouds.oneandone.rest.util.VPNConfigParser;
+import org.jclouds.Fallbacks;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.Transform;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+@Path("/vpns")
+@Produces("application/json")
+@Consumes("application/json")
+@RequestFilters(AuthenticateRequest.class)
+public interface VpnApi {
+
+   @Named("vpn:list")
+   @GET
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   List<Vpn> list();
+
+   @Named("vpn:list")
+   @GET
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   List<Vpn> list(GenericQueryOptions options);
+
+   @Named("vpn:get")
+   @GET
+   @Path("/{vpnId}")
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   Vpn get(@PathParam("vpnId") String vpnId);
+
+   @Named("vpn:configurations:get")
+   @GET
+   @Path("/{vpnId}/configuration_file")
+   @ResponseParser(VPNConfigParser.class)
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   @Transform(VPNConfigParser.ToZipStream.class)
+   ZipInputStream getConfiguration(@PathParam("vpnId") String vpnId);
+
+   @Named("vpn:create")
+   @POST
+   Vpn create(@BinderParam(BindToJsonPayload.class) Vpn.CreateVpn vpn);
+
+   @Named("vpn:update")
+   @PUT
+   @Path("/{vpnId}")
+   Vpn update(@PathParam("vpnId") String vpnId, @BinderParam(BindToJsonPayload.class) Vpn.UpdateVpn vpn);
+
+   @Named("vpn:delete")
+   @DELETE
+   @Path("/{vpnId}")
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   @MapBinder(BindToJsonPayload.class)
+   Vpn delete(@PathParam("vpnId") String vpnId);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/util/VPNConfigParser.java
----------------------------------------------------------------------
diff --git a/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/util/VPNConfigParser.java b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/util/VPNConfigParser.java
new file mode 100644
index 0000000..0805c81
--- /dev/null
+++ b/oneandone/src/main/java/org/apache/jclouds/oneandone/rest/util/VPNConfigParser.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jclouds.oneandone.rest.util;
+
+import com.google.common.base.Function;
+import static com.google.common.io.BaseEncoding.base64;
+import com.google.inject.TypeLiteral;
+import java.io.ByteArrayInputStream;
+import java.util.zip.ZipInputStream;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.apache.jclouds.oneandone.rest.domain.VPNConfig;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+
+@Singleton
+public class VPNConfigParser extends ParseJson<VPNConfig> {
+
+   @Inject
+   VPNConfigParser(Json json) {
+      super(json, TypeLiteral.get(VPNConfig.class));
+   }
+
+   public static class ToZipStream implements Function<VPNConfig, ZipInputStream> {
+
+      @Override
+      public ZipInputStream apply(VPNConfig input) {
+         byte[] decoded = base64().decode(input.content());
+         ZipInputStream zipStream = new ZipInputStream(new ByteArrayInputStream(decoded));
+         return zipStream;
+      }
+   };
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/features/VpnApiLiveTest.java
----------------------------------------------------------------------
diff --git a/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/features/VpnApiLiveTest.java b/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/features/VpnApiLiveTest.java
new file mode 100644
index 0000000..38f01f2
--- /dev/null
+++ b/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/features/VpnApiLiveTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jclouds.oneandone.rest.features;
+
+import java.util.List;
+import java.util.zip.ZipInputStream;
+import org.apache.jclouds.oneandone.rest.domain.Vpn;
+import org.apache.jclouds.oneandone.rest.domain.options.GenericQueryOptions;
+import org.apache.jclouds.oneandone.rest.internal.BaseOneAndOneLiveTest;
+import org.testng.Assert;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+@Test(groups = "live", testName = "VpnApiLiveTest")
+public class VpnApiLiveTest extends BaseOneAndOneLiveTest {
+   
+   private Vpn currentVpn;
+   private List<Vpn> vpns;
+   
+   private VpnApi vpnApi() {
+      return api.vpnApi();
+   }
+   
+   @BeforeClass
+   public void setupTest() {
+      currentVpn = vpnApi().create(Vpn.CreateVpn.create("jclouds vpn", "description", null));
+   }
+   
+   @Test
+   public void testList() {
+      vpns = vpnApi().list();
+      
+      assertNotNull(vpns);
+      assertFalse(vpns.isEmpty());
+      Assert.assertTrue(vpns.size() > 0);
+   }
+   
+   @Test
+   public void testListWithOption() {
+      GenericQueryOptions options = new GenericQueryOptions();
+      options.options(0, 0, null, "jclouds", null);
+      List<Vpn> resultWithQuery = vpnApi().list(options);
+      
+      assertNotNull(resultWithQuery);
+      assertFalse(resultWithQuery.isEmpty());
+      Assert.assertTrue(resultWithQuery.size() > 0);
+   }
+   
+   @Test
+   public void testGet() {
+      Vpn result = vpnApi().get(currentVpn.id());
+      
+      assertNotNull(result);
+      assertEquals(result.id(), currentVpn.id());
+   }
+   
+   @Test
+   public void testGetConfiguration() throws InterruptedException {
+      assertVPNAvailable(currentVpn);
+      ZipInputStream result = vpnApi().getConfiguration(currentVpn.id());
+      assertNotNull(result);
+   }
+   
+   @Test(dependsOnMethods = "testGetConfiguration")
+   public void testUpdate() throws InterruptedException {
+      String updatedName = "updatejclouds VPN";
+      
+      Vpn updateResult = vpnApi().update(currentVpn.id(), Vpn.UpdateVpn.create(updatedName, "desc"));
+      
+      assertNotNull(updateResult);
+      assertEquals(updateResult.name(), updatedName);
+      
+   }
+   
+   @AfterClass(alwaysRun = true)
+   public void teardownTest() throws InterruptedException {
+      vpnApi().delete(currentVpn.id());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/features/VpnApiMockTest.java
----------------------------------------------------------------------
diff --git a/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/features/VpnApiMockTest.java b/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/features/VpnApiMockTest.java
new file mode 100644
index 0000000..102378b
--- /dev/null
+++ b/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/features/VpnApiMockTest.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jclouds.oneandone.rest.features;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import java.util.List;
+import java.util.zip.ZipInputStream;
+import org.apache.jclouds.oneandone.rest.domain.Vpn;
+import org.apache.jclouds.oneandone.rest.domain.options.GenericQueryOptions;
+import org.apache.jclouds.oneandone.rest.internal.BaseOneAndOneApiMockTest;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "VpnApiMockTest", singleThreaded = true)
+public class VpnApiMockTest extends BaseOneAndOneApiMockTest {
+
+   private VpnApi vpnApi() {
+      return api.vpnApi();
+   }
+
+   @Test
+   public void testList() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setBody(stringFromResource("/vpn/list.json"))
+      );
+
+      List<Vpn> vpns = vpnApi().list();
+
+      assertNotNull(vpns);
+      assertEquals(vpns.size(), 2);
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/vpns");
+   }
+
+   @Test
+   public void testList404() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setResponseCode(404));
+
+      List<Vpn> vpns = vpnApi().list();
+
+      assertEquals(vpns.size(), 0);
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/vpns");
+   }
+
+   @Test
+   public void testListWithOption() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setBody(stringFromResource("/vpn/list.options.json"))
+      );
+      GenericQueryOptions options = new GenericQueryOptions();
+      options.options(0, 0, null, "New", null);
+      List<Vpn> vpns = vpnApi().list(options);
+
+      assertNotNull(vpns);
+      assertEquals(vpns.size(), 2);
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/vpns?q=New");
+   }
+
+   @Test
+   public void testListWithOption404() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setResponseCode(404));
+      GenericQueryOptions options = new GenericQueryOptions();
+      options.options(0, 0, null, "New", null);
+      List<Vpn> vpns = vpnApi().list(options);
+
+      assertEquals(vpns.size(), 0);
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/vpns?q=New");
+   }
+
+   public void testGet() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setBody(stringFromResource("/vpn/get.json"))
+      );
+      Vpn result = vpnApi().get("vpnId");
+
+      assertNotNull(result);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/vpns/vpnId");
+   }
+
+   public void testGet404() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setResponseCode(404));
+      Vpn result = vpnApi().get("vpnId");
+
+      assertEquals(result, null);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/vpns/vpnId");
+   }
+
+   public void testGetConfiguration() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setBody(stringFromResource("/vpn/configuration.json"))
+      );
+      ZipInputStream result = vpnApi().getConfiguration("vpnId");
+
+      assertNotNull(result);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/vpns/vpnId/configuration_file");
+   }
+
+   public void testGetConfiguration404() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setResponseCode(404));
+      ZipInputStream result = vpnApi().getConfiguration("vpnId");
+
+      assertEquals(result, null);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/vpns/vpnId/configuration_file");
+   }
+
+   @Test
+   public void testCreate() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setBody(stringFromResource("/vpn/get.json"))
+      );
+
+      Vpn response = vpnApi().create(Vpn.CreateVpn.create("name", "desc", null));
+
+      assertNotNull(response);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "POST", "/vpns", "{\"name\":\"name\",\"description\":\"desc\"}");
+   }
+
+   @Test
+   public void testUpdate() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setBody(stringFromResource("/vpn/get.json"))
+      );
+      Vpn response = vpnApi().update("vpnId", Vpn.UpdateVpn.create("name", "desc"));
+
+      assertNotNull(response);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "PUT", "/vpns/vpnId", "{\"name\":\"name\",\"description\":\"desc\"}");
+   }
+
+   @Test
+   public void testDelete() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setBody(stringFromResource("/vpn/get.json"))
+      );
+      Vpn response = vpnApi().delete("vpnId");
+
+      assertNotNull(response);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "DELETE", "/vpns/vpnId");
+   }
+
+   @Test
+   public void testDelete404() throws InterruptedException {
+      server.enqueue(
+              new MockResponse().setResponseCode(404));
+      Vpn response = vpnApi().delete("vpnId");
+
+      assertEquals(response, null);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "DELETE", "/vpns/vpnId");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/internal/BaseOneAndOneLiveTest.java
----------------------------------------------------------------------
diff --git a/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/internal/BaseOneAndOneLiveTest.java b/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/internal/BaseOneAndOneLiveTest.java
index 78b5eb9..7ef372e 100644
--- a/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/internal/BaseOneAndOneLiveTest.java
+++ b/oneandone/src/test/java/org/apache/jclouds/oneandone/rest/internal/BaseOneAndOneLiveTest.java
@@ -32,6 +32,7 @@ import org.apache.jclouds.oneandone.rest.domain.Hdd;
 import org.apache.jclouds.oneandone.rest.domain.PrivateNetwork;
 import org.apache.jclouds.oneandone.rest.domain.Server;
 import org.apache.jclouds.oneandone.rest.domain.Types;
+import org.apache.jclouds.oneandone.rest.domain.Vpn;
 import org.apache.jclouds.oneandone.rest.ids.ServerPrivateNetworkRef;
 import org.jclouds.apis.BaseApiLiveTest;
 import org.jclouds.util.Predicates2;
@@ -41,6 +42,7 @@ public class BaseOneAndOneLiveTest extends BaseApiLiveTest<OneAndOneApi> {
 
    Predicate<Server> waitUntilServerReady;
    Predicate<ServerPrivateNetworkRef> waitUntilPrivateNetworkReady;
+   Predicate<Vpn> waitUntilVPNReady;
    private static final OneAndOneProviderMetadata METADATA = new OneAndOneProviderMetadata();
    OneAndOneConstants constants;
 
@@ -81,8 +83,18 @@ public class BaseOneAndOneLiveTest extends BaseApiLiveTest<OneAndOneApi> {
             return server.state() != Types.GenericState.ACTIVE;
          }
       };
+
+      Predicate<Vpn> vpnAvailableCheck = new Predicate<Vpn>() {
+         @Override
+         public boolean apply(Vpn vpn) {
+            Vpn result = api.vpnApi().get(vpn.id());
+            return result.state() == Types.GenericState.ACTIVE;
+         }
+      };
+      waitUntilVPNReady = Predicates2.retry(vpnAvailableCheck, constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS);
       waitUntilPrivateNetworkReady = Predicates2.retry(privateNetworkAvailableCheck, constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS);
       waitUntilServerReady = Predicates2.retry(serverAvailableCheck, constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS);
+
       return injector.getInstance(OneAndOneApi.class);
    }
 
@@ -115,6 +127,10 @@ public class BaseOneAndOneLiveTest extends BaseApiLiveTest<OneAndOneApi> {
    protected void assertPrivateNetworkAvailable(ServerPrivateNetworkRef ref) {
       assertTrue(waitUntilPrivateNetworkReady.apply(ref), String.format("ServerPrivateNetworkRef %s is not Ready", ref));
    }
+   
+   protected void assertVPNAvailable(Vpn vpn) {
+      assertTrue(waitUntilVPNReady.apply(vpn), String.format("VPN %s is not Ready", vpn));
+   }
 
    protected Server deleteServer(String serverId) {
       return api.serverApi().delete(serverId);
@@ -123,7 +139,8 @@ public class BaseOneAndOneLiveTest extends BaseApiLiveTest<OneAndOneApi> {
    protected Server turnOnServer(String serverId) {
       return api.serverApi().updateStatus(serverId, Server.UpdateStatus.create(Types.ServerAction.POWER_ON, Types.ServerActionMethod.SOFTWARE));
    }
+
    protected Server turnOFFServer(String serverId) {
       return api.serverApi().updateStatus(serverId, Server.UpdateStatus.create(Types.ServerAction.POWER_OFF, Types.ServerActionMethod.SOFTWARE));
-}
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/sharedStorage/get.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/sharedStorage/get.json b/oneandone/src/test/resources/sharedStorage/get.json
new file mode 100644
index 0000000..7f3d370
--- /dev/null
+++ b/oneandone/src/test/resources/sharedStorage/get.json
@@ -0,0 +1,24 @@
+{
+    "id": "6AD2F180B7B666539EF75A02FE227084",
+    "size": 200,
+    "state": "ACTIVE",
+    "description": "My shared storage test description",
+    "datacenter": {
+        "id": "D0F6D8C8ED29D3036F94C27BBB7BAD36",
+        "location": "USA",
+        "country_code": "US"
+    },
+    "cloudpanel_id": "vid35780",
+    "size_used": "0.00",
+    "cifs_path": "\\vid50995.nas1.lan\\vid50995",
+    "nfs_path": "vid50995.nas1.lan/:vid50995",
+    "name": "My shared storage test",
+    "creation_date": "2015-05-06T08:33:25+00:00",
+    "servers": [
+        {
+            "id": "638ED28205B1AFD7ADEF569C725DD85F",
+            "name": "My server 1",
+            "rights": "RW"
+        }
+    ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/sharedStorage/list.access.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/sharedStorage/list.access.json b/oneandone/src/test/resources/sharedStorage/list.access.json
new file mode 100644
index 0000000..6b72404
--- /dev/null
+++ b/oneandone/src/test/resources/sharedStorage/list.access.json
@@ -0,0 +1,24 @@
+[
+    {
+        "datacenter": {
+            "id": "D0F6D8C8ED29D3036F94C27BBB7BAD36",
+            "location": "USA",
+            "country_code": "US"
+        },
+        "state": "CONFIGURING",
+        "kerberos_content_file": "BQIAAABSAAIACERFVjEuTEFOAANuZnMAEnVpZDYyNDQ1OS5kZXYxLmxhbgAAAAEAAAAAQAASACAobHpZknT8WqX14kQhOrFI9hwO37NUg/p3Ne/8w2MPJA==",
+        "needs_password_reset": 0,
+        "user_domain": "nas2\\uid183564"
+    },
+    {
+        "datacenter": {
+            "id": "D0F6D8C8ED29D3036F94C27BBB789536",
+            "location": "Spain",
+            "country_code": "ES"
+        },
+        "state": "CONFIGURING",
+        "kerberos_content_file": "BQIAAABSAAIACERFVjEuTEFOAANuZnMAEnVpZDYyNDQ1OS5kZXYxLmxhbgAAAAEAAAAAQQASACAobHpZknT8WqX14kQhOrFI9hwO37NUg/p3Ne/8w2MPJA==",
+        "needs_password_reset": 0,
+        "user_domain": "nas2\\uid183564"
+    }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/sharedStorage/list.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/sharedStorage/list.json b/oneandone/src/test/resources/sharedStorage/list.json
new file mode 100644
index 0000000..d9c8c93
--- /dev/null
+++ b/oneandone/src/test/resources/sharedStorage/list.json
@@ -0,0 +1,62 @@
+[
+    {
+        "id": "6AD2F180B7B666539EF75A02FE227084",
+        "size": 200,
+        "state": "ACTIVE",
+        "description": "My shared storage test description",
+        "datacenter": {
+            "id": "D0F6D8C8ED29D3036F94C27BBB7BAD36",
+            "location": "USA",
+            "country_code": "US"
+        },
+        "cloudpanel_id": "vid35780",
+        "size_used": "0.00",
+        "cifs_path": "\\vid50995.nas1.lan\\vid50995",
+        "nfs_path": "vid50995.nas1.lan/:vid50995",
+        "name": "My shared storage test",
+        "creation_date": "2015-05-06T08:33:25+00:00",
+        "servers": [
+            {
+                "id": "638ED28205B1AFD7ADEF569C725DD85F",
+                "name": "My server 1",
+                "rights": "RW"
+            }
+        ]
+    },
+    {
+        "id": "4406CE4723BB441C7956E25C51CE8C1B",
+        "size": 50,
+        "state": "ACTIVE",
+        "description": "My shared storage description",
+        "datacenter": {
+            "id": "D0F6D8C8ED29D3036F94C27BBB7BAD36",
+            "location": "USA",
+            "country_code": "US"
+        },
+        "cloudpanel_id": "vid30534",
+        "size_used": "0.00",
+        "cifs_path": "\\vid50995.nas1.lan\\vid50995",
+        "nfs_path": "vid50995.nas1.lan/:vid50995",
+        "name": "My shared storage",
+        "creation_date": "2015-03-17T11:57:48+00:00",
+        "servers": []
+    },
+    {
+        "id": "1A5418172DD3BD39F8010A6633F1018A",
+        "size": 250,
+        "state": "ACTIVE",
+        "description": null,
+        "cloudpanel_id": "vid19857",
+        "datacenter": {
+            "id": "D0F6D8C8ED29D3036F94C27BBB7BAD36",
+            "location": "USA",
+            "country_code": "US"
+        },
+        "size_used": "0.00",
+        "cifs_path": "\\vid50995.nas1.lan\\vid50995",
+        "nfs_path": "vid50995.nas1.lan/:vid50995",
+        "name": "My shared storage 2",
+        "creation_date": "2015-05-05T09:36:31+00:00",
+        "servers": []
+    }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/sharedStorage/list.options.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/sharedStorage/list.options.json b/oneandone/src/test/resources/sharedStorage/list.options.json
new file mode 100644
index 0000000..d9c8c93
--- /dev/null
+++ b/oneandone/src/test/resources/sharedStorage/list.options.json
@@ -0,0 +1,62 @@
+[
+    {
+        "id": "6AD2F180B7B666539EF75A02FE227084",
+        "size": 200,
+        "state": "ACTIVE",
+        "description": "My shared storage test description",
+        "datacenter": {
+            "id": "D0F6D8C8ED29D3036F94C27BBB7BAD36",
+            "location": "USA",
+            "country_code": "US"
+        },
+        "cloudpanel_id": "vid35780",
+        "size_used": "0.00",
+        "cifs_path": "\\vid50995.nas1.lan\\vid50995",
+        "nfs_path": "vid50995.nas1.lan/:vid50995",
+        "name": "My shared storage test",
+        "creation_date": "2015-05-06T08:33:25+00:00",
+        "servers": [
+            {
+                "id": "638ED28205B1AFD7ADEF569C725DD85F",
+                "name": "My server 1",
+                "rights": "RW"
+            }
+        ]
+    },
+    {
+        "id": "4406CE4723BB441C7956E25C51CE8C1B",
+        "size": 50,
+        "state": "ACTIVE",
+        "description": "My shared storage description",
+        "datacenter": {
+            "id": "D0F6D8C8ED29D3036F94C27BBB7BAD36",
+            "location": "USA",
+            "country_code": "US"
+        },
+        "cloudpanel_id": "vid30534",
+        "size_used": "0.00",
+        "cifs_path": "\\vid50995.nas1.lan\\vid50995",
+        "nfs_path": "vid50995.nas1.lan/:vid50995",
+        "name": "My shared storage",
+        "creation_date": "2015-03-17T11:57:48+00:00",
+        "servers": []
+    },
+    {
+        "id": "1A5418172DD3BD39F8010A6633F1018A",
+        "size": 250,
+        "state": "ACTIVE",
+        "description": null,
+        "cloudpanel_id": "vid19857",
+        "datacenter": {
+            "id": "D0F6D8C8ED29D3036F94C27BBB7BAD36",
+            "location": "USA",
+            "country_code": "US"
+        },
+        "size_used": "0.00",
+        "cifs_path": "\\vid50995.nas1.lan\\vid50995",
+        "nfs_path": "vid50995.nas1.lan/:vid50995",
+        "name": "My shared storage 2",
+        "creation_date": "2015-05-05T09:36:31+00:00",
+        "servers": []
+    }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/sharedStorage/server.get.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/sharedStorage/server.get.json b/oneandone/src/test/resources/sharedStorage/server.get.json
new file mode 100644
index 0000000..d68f370
--- /dev/null
+++ b/oneandone/src/test/resources/sharedStorage/server.get.json
@@ -0,0 +1,5 @@
+{
+    "id": "638ED28205B1AFD7ADEF569C725DD85F",
+    "name": "Mi servidor 1",
+    "rights": "RW"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/sharedStorage/servers.list.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/sharedStorage/servers.list.json b/oneandone/src/test/resources/sharedStorage/servers.list.json
new file mode 100644
index 0000000..e46295d
--- /dev/null
+++ b/oneandone/src/test/resources/sharedStorage/servers.list.json
@@ -0,0 +1,12 @@
+[
+    {
+        "id": "C72CF0A681B0CCE7EC624DD194D585C6",
+        "name": "My Server",
+        "rights": "RW"
+    },
+    {
+        "id": "4ECD9D188EB457317B2CF8F07885E7B4",
+        "name": "My Server 2",
+        "rights": "R"
+    }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/vpn/configuration.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/vpn/configuration.json b/oneandone/src/test/resources/vpn/configuration.json
new file mode 100644
index 0000000..c7f55f8
--- /dev/null
+++ b/oneandone/src/test/resources/vpn/configuration.json
@@ -0,0 +1,3 @@
+{
+    "config_zip_file": "UEsDBBQAAAAIADxTm0gHg1yVTwQAAL0TAAAKAAAAcmVhZG1lLnR4dO1XXW/bNhR9N+D/cBHsKYgltM7S1igKdE46BGvjYk47DOgLI11ZRGRSIKkoBvKz9gv2y3YvKdly7Lpp4XQDViIvsngP78fhOQpAWP3eYH31e6eT8Yd3ZxeXry/PJxdb3vd7b7QBkSRorVQzcBoWujIwLnSVgkVzg8aCVCDoIakMglApoErMonSYQi0W4HKjq1lOx398fwGvPRZMp2+PGApycYMMm2iVyRkjuByBdyaFROUYnH9J9LysHBrIjJ4TVp3LJPcItaBdIcV7+U1DfhHXMfjc6vfOlXWiKISTWgH9/SFVqms72hnEfZEhkBvDZbcl+EY1RYyXRfi82irWa6dySjSZNnMfl+mi0DWjWIcl59HvATyJBnCqa1VokcKkRNXpke8Jh+osk4kUBZRihiPgOF65c4QTx5rCbkoVKXQxFYm3UZmX/teBpfQSjNPmBBvlbl5w/FM69+yWhuvCaJqikcuHFDNRFY6Oc/kIxqP4vdEzI+bwRhZo4ybNyCcyoWhTS4uh+FoWxbIDmEp32Mw5NDFMIyMYoMnQaVpFjDKkdH7VHMP1E+n8voYkBWYO5qgqv/WYtk6xoC2h+SlaaYiUHOjnVcjkmgfuQ6W6ZlQqzjho28BD4O0Mt5mZjeAyR4vhAURD3tX8tEIeH4/gOHrS8BESNE7SnKis9t3Tlihb3g3hN1y0D8er0W+kw3t+pprHuuRbt0qMylrPa/e44gDNcCed4bcHL5vUcJzpSXsH+1zU8Ds4vNCOSHyeeb4kuVCz0OCWdbJ7cbmmtVvFnPLbed0x4GbLIl+V33UYaboagXGeHavzVo0jltBEPaBPcZ/rLgAmAg5eMl8H3fIGXN6rT6GCT4mIEuMOHghIlPoy5Esq/vz01Q7cBu4aFw9Ho80Hu/LbL2t26fymzL+Vqrod7
 Yr4zhp/ildSKH/Mh6tKuWp7clvsgEOaRJfXNGQVwZ98dwi2svfJHERtPqfolVV01k+idIMZLu8ZNPYREmBfOFfBaUuhsDiC2cN0efhf0uXhDl0e7tDlYVeXh1/U5eOOLm/zuPbCeA1qv2I29Y36yWj+refvkm0R/LJYCmOMLlma/dIWDFL29KWmsOZepFQyN9YTlLNalWiPwuSkJepY3M4OmF+n0qydFdN9t4EeJ92CNVv/mhnRcLggn9wzNhkW6+b6bDSHSWExiDlHWfrO2Ui5+Uy6n6USSq/n2G313p0Lgi1EUbRXVyBT2OhzHFzg2/DYEzYROybwNWhsCZ8H47dfkR3s2xEAnrPgeP1oL2kt6TKta0KrhNtp1FS2m0ljUorJtKPaK6luD5ZW/f0X/btyI2QhrgpsFbK9uQECDJbaSqfNwkuZdxaGat2kVeRMGmfhTJEAlIY+rRtR6IRvvbmLar6EwBKLgSE5pou+UvbHtpa1FNZ8ZfjNvvJ//97/F3zl5Lv7yrOH+8rzH77yw1ce01deEMF+r9Sjuso/UEsDBBQAAAAIADxTm0gnffqGWAkAAJgRAAAOAAAAdnBuREREQkVfMS5jcnSdVtty4kgSfecr9LgTnhlKdykjOmJ1A8tGAoEAw5suJSEQYHOHr99M3HbTbWZ3oxVhSZSq8nIy8xw7fLOriipLdhwaAl5uskve3+ga8c22Wq9AkIV/sZP0x+eHAd9USS2E+2XKNyCIEqMNuvG+Y1CVq2S333DBqsv1ptrNliBsZ4mkamP80R9Y3irbnF93aPvTpL/d7smW8204+FMYxN86L5b4p9D5FrYdXOh+PIfXFyHku+N6s6hW5Z+CE34bDDqjXthcJUv+8c6XSVVbeb7h2+23VZlt/023v+vkh89RUld5tTt/LtAVrneCzYv1hoMQJBtBkgVRBFUEUREkJmpCO4i/HLCKHd8I3w+Itweknw8M9umcZzvK0xte8+x1+9c835
 /vef59TSoxZU1Nc5NpXBRlSWGamslqyhVdS3nKsn9KkW9/dSf09mldZcIzPwv+qljDTwncfLwp2Gab3CnTz2f+wjMg/EtkkiKk1e6PL9uCdb6v91v48oEuxiDNgDPgJigKmAXkKSQZ4gZyAWYOqgyZDIoEqnHfgswhkcDQIRMhUUDJQEVTBegFyDLIChgGiDroOb3ftVAwMEUwNTKiq5DIoBmgSpBpoBWQGaDptCHLoTD/IYYEMg4mBskg0yFRQdIpfrznIr0YGaQyqAmZvWtBF687E8IBdzKVMEkLwNikFHJOmKQKpcD5fQscUUpA10HVaBtaMFIKW0khRRg5iCoFZqjk5a4FSYMU/SJcGvAM5BxShMUARSPwiwJSAwyTCpTL9y2oOkgFucszME3IEjplYGA6vSgmyFjcnILk/1CLrABNhjQBJhFuhQhFTnfV/LLdO72uV3yFc6SpqqwT+4iMMfFHB76ozDzIAj/t+IpI7Jce/P7ZTrbY+Q5+3m2SarW706mOBS2rM/B+nnm+22bJK8ejyyXF8TXCZHv+C6lOaPMV3yDB5oLzg23vxfIxq9chzdEo7kVG/GKZYZEMcBiIJmDrWNcGlR3wDPBagF9dizao2P0SuFh1G0QXRAeb4J5ba7+b0cyf/6fjBT9XOEke6BJ4EpgymC7YKqg2aDguNvULxqFhq2qg2KBb4Irg6RQodofifLHoVpsQKRuaxPrN76TffOf8Zvf74wvjN3+b8D+u7VW/wLSoOzE02SW4cNRdFwytcQ8mjxopxyoSSsNtUt4ZxbgzEMY8FZy6QhSvyBKaWPBfKfS70f9iy63Kaoca+ymmvy+tgqwSJeCIaylkDPCnkQNDpkHqwveEvuJ00qCnoODMIY3dsl2hQYbdhIrGiKAUFYqM6BE5DRsNyRNpk+VEvFkGegJaTvx541+5knhBbpGGkOtlEzQVcrzzq1JqkDNiTHTAEyIa6ZYrJaRhXFJAxN0YuQi6AYl
 GiSAxI+9iXrgBA0HeyiVIzZ+ZEpkN00YmwUBkA3ROriROMoHeMBEUDiQcpB1MDVka92S3ioM+NYkYVsyJIhE5hVE4BSc+zVIiO8wZOQ75DjkOSTC75VlMD3HG6cA4UZEQbVzRGekb0wgLonnjqngYGjIl3tnNeQyGI3gFoSjJoGeElpaRaiC1YhS5BoVMOZqopxpIWM7b80S7CiTpVec4HUb80BWex7ywhMj1CBuqDvF+AqlEP3+cR5ywMKRLMnEN0bNJTgqJdA8hoMoVxPrGVT8RKfkWP37NGTsE5V43KRFDIxJBaTZTSlsxqMFylTxrjLRYzm77J6XZxD7B1QSlXSJFR7XGaLGpseGYTkZJZjTKEZPKbuMXJXJOhk3SVRRkRaSao97ie56T7OCdFeQm0SlG8/Y8NSzSbQKJSGFjF0ukSgRhcW14HBAskMmpKKRfnL7e4J9RPDgkatH4iy7ba/uh4Hj92G/5jhV719VG4PveS+w4FhuX1tG3rdK3uWuFdrl4my2qtnlkthVtW5br2LXrdQJr0bbEoWfPAmc0Ck4NN7Y6dhmO8GRse6dJFHunH2sZrqmPkRjcrK0/19quNW68L25jl10Xy3g5YrlknhOp3k8vXi+w2NWjdQraQzEMRrXdDSLj6EYTdxRFrw3vOI2H7DQeesrp0bWS29CdyBu7Y3U1kUMrXebz7KxuJ2Pl+DjLwiCeHIOLf2kEsSfiHxvT4vy6ePpcmzt2efk57/5oeLpNu2G37N5w/kveH2tzK/jMe3hNcR8MFsen4zV81zvbs0Y3DqRw/LToxpY0RQAxAha4E3FyCcTJcsjCy7Sajv3j5OKlgb1AbXprD/xUdiPvyepPgsY1N2bvp1J46SxHl8BvFwTczUYs49CylHZIpayiZ7uMHEPp1t7K3G8bLfchHxqhM3a1ama0F3Gr7hWXgFV2boY9p1+vc3EtzQanZdOfqo/hQ7zpLmr38XWQNZ9Mr9mYd8JkuHrs
 d/fLi82dpuEc+KYTMX0fPufihNmTfX+gi7yVjFvdKhE1O9320vakX8+be3+VNpiYHQ75eXo+PLj9pvnU1URZDcz5vvSbRiGfxpHvWpFlrxXfHlywY4fZ0aEe6LPYtvxjw3KtnV3W5WxR2rPDzLEi5vn2hPfHrQsfiIMh88q+NNpPX55meXu08L2wzlb91+mynk9e+nUjsK+dlrtlNLbtyG8H1jRLeL/XnR+ccD0Z+Z1RdWJZK3bs87vnue0/2gj2Q2lHD41s/xyv0lE6Xe4nvXYkDeRNXz3VL/Fz216+Dez6cn+IPvqm8btD9DFDjd8doo8Zavz/Q6Q5TuQkfTEzR+0in82t+JrC42Doua6F7VVu7NJr2VHmWvP3sB8jA9swcGzFIs9Pz+upPztkoRV5HTuy3LL0bCzjsPW0PFjhhLnOdvxsx7vJA5PltyLF+jmXfnE6jGfVeH0Ue5fH191p3ndeFKnuu3UtjdV+o7WzVl737cmvZq9PtppbL9UyzMQokZbjXv6s6psXy4gjBf8t3hcPr4fYNXCkfeXcEqvBTF43LGYtmvNN57wUn0oNUZylrWV779v96XF4nKjPZpW/VIsHP5gf5syfF6G9ZUXzQTz0rFV3go30LLI3NemyxxlnY3+yzTtlL307s5UR2bzbt+aL2nj1tafTw7SUZs/dbtly05M6V6bnlrrPG4MgHm2XzmPP3a2G8fkQrESxN5hqttJsYsNWx5kfTvxlc+61Am1yyScm6021XF0somP7rXVuzAet6cvhpJn7B5521t1xcnHWovHtXQu80P2iBP8BUEsDBBQAAAAIADxTm0iq3ECuLwQAAI4GAAAGAAAAY2EuY3J0nZXJkqM4FEX3fEXviQqDsZ14UQsJxCzmeYdNmsFgAzYI8/VNZlVUZUdHL7pYEScknq7eEfr2bX0gklXzLwG5viqpAvDRB/xGYVVFfS0IYK8XgKgQFKoGtE4JFzZIj1sZA0YWvF721BMnO
 ggKJABYlQPsPIjgJCIVOo6MiBYGCzIxeMiADZAAMfKlEmL/N4Pzyq65+I9xJUbUT5hgmPyAM7Y/2QtaaexyJ04bsmh/ww5PxLXiWlBEJPUDZo4CtJspcQEmLMwQClcfNqbvS6nj1+iM4f3r0jXgJuapzeszA8d0ay5GO5enGrxTEmFm00cvLOLZrIMdXsxsZa+VzXgBv9jX3F9jU3+S+2ts6k9yf41N/f/ca8s99ePDmn5PVaqcziZY+wsdIBYFsoG4DnDuwvoOAU7eLhFx927O4/JowyxVepkUTJMnWV/F+/dHQykn48EfnazaBIfofRIfUH+ymPSe3dnk7cUPBXcW2JftcW4qC/bhLhoCM/vW0PJRS28V6jhEqQCEgBv0miB7iEb6ZFRSF+54QcptRs4vZWUP8eZx2Iz5XlAMuzHudxYOtmgL9pm6pPD97VTfHr3tbHXC0UrX+daLpuP5ORZHlksWs2G6UU7ckg6rU6alkx6HZuTf2fB2m6jkXGWqpmwSaA68FJdeOTIeh1ojbdqTSwfj26U0hVmtqsyteBsfpWAUR/MgpbCe3NqhwGgJSIuzlu7bo9fRBXx4Jf3UmRc9wScXjJZkp6J5UWpNnxXraXeBAAgCILPgRhQgRfsg/+ip4uyQVDiBfTOq637LRtt0aMX5al6fB+b9nIYMIcX5+iFTrhIPEl+A0x0E1H9PeJVF2l+L9EyK1JmFBWif8oDEB03oY4chAvlxnFVEXByh1f2fcGXKyiyXMf0vTP/F3IJIxSczKESOn1BF+yZX8uncPrtTe/6tK8BfT6n98YP50F93sRywJqbCBlrYJUQufqj5w8wrlAr2U19H3t8SDo8nWRqL4graax7boXvjkoSI4COCy/gQBGTdWbj5rHrty2slHwkDgfOQALBWtRHQQ6MF4VInHf1Oe5N4v6CDE0s3amdfkqed8QeHj/uxDMKsUXfLEtFGOk2bzcVB9VNrSVI/dnMkva7Q9GHeqA
 fF1cKhSy2K3J14m3c3e/JxyG+Tjd7zLlszh8Sj9+/bDqBKicy09c77iOndvWJu1Dd6W+0wnumDylJDIxWTrzdxXGB8kMO55S1huDh9r3GdvpPYLSEdnsVLZwQlrNwtuht8b23w6bIdmzeW8o4iD2nL1d6Ynp1VfvO4etaiZfuMFryl3B2LeBDzs01HY37QMnbPnYLlWOuZPXAOPc1UsrUHgNPX2e4LtiJcKGPh2cmhZDSGwz6n20lZ1LDZBR7XoWNMDtFFb27Qf0zJS3S+f6c+rxdkiv+6cv4GUEsDBBQAAAAIADxTm0jkLKGyzAIAAI8DAAAOAAAAdnBuREREQkVfMS5rZXllk7eyo0oARHO+YnPqFggzQDh4o8EIO2SS8AiEESDp6999m26HHXSd6qrz8/MbWTMs949/sRIYaX8cDf9f/hDIspQisGSoQleu+7npW0M6aBkGmg5hqMCHeNS1fYO1BuHTkOH5W4M4rBhwxD5/ioiDtrE/O+ZxvaA0lij0DS2dpWnRNtKGTburfjZuYjP2d5b3VhDGqHzYX3GkIxKtpykk7oUGwA3p3Jk6HAmsLzXaOHpiky7QW14/7wmXTDgG9pI99e2Q1rwxUp/k2mKPkg8pEaw9OhTnmhTdC0tW9V/GA7Cj3lSl57BGMoSaUmOY2FGEw11Xefx+TXxF9abnLPazJhCgWIn53Ewqsf2213oO3DdycyE6ODAUI3bD5HEfMwyj91eeKpIqtVhRfD4wvNkfr8QwSBlrzGKZuEp1Cj88Z/FnlGsU80qky2Iint6fKMSt8oZQaJDKfLOXQ80rlzKk+UBE9zSGNZwo6Rk/EJt9+sCWoXeVuk2YOu12aXLdmU/+lUxIW8qLgrLoDwuEe4Y32r2/VEICbV7E7jil8lya6+RSjqp2U3JSG0TWwa0+QuCHVf+8xUoQqJeuFaOcd58nu6TJ1GwJfVjJz+vW1HcTXxy+/33bLgeOV3fm4fmsaD66rVyafhP
 ifde9kn0Dj7S/Kn7Lx03QFUIwygT2kEzuDCrBAHg5nQthXiKx4oDlNmNnyJFk1VafV5l9pmiJ8j2O/eaoMtdkTwirVlA8MRDvg4/U0BDxbrxaJs+G8XdYXpHX6khtK+fwMJa7U5RleYobf0nmqKwtihi2r+X6w4IllgMilZw+sWAcfTAEfbqpnabl2MbLc5XGjxMbsjVcTu9f4uBUBe36KXjCSQ9FtC1uHytSWC/g4nexJI6+PGEbyT7KsnmY6NHIFLK2JsuJ49K/3un56mZjqU0ZUfBO6uFMNjxzIf76pbnqP879B1BLAwQUAAAACAA8U5tImIZGCXgGAACXDgAADwAAAHZwbkREREJFXzEub3ZwbpVW728bNwz9XqD/A5F86Ab0nDppsbX9MKRJhwXrDwNxuw1zMSh3PFuITrpJOrvuX79HSefabYctQZvYdxRJPT4+8vj4Lj/37x3Ttep6w1QbzTZWQTdMb3u272dv6HTyiGpnW72kVsMm2bfOy0PLddR2SdFRN5ioq+yAAvs1+wnJT7K/w0+yn690KFEHr6J2lmpl6YZpCNzQzTbH68d8ctzwkFZuwwhNrOoVjdms3GAaWqk1j/51DOQ2cMo+krIN3fI2XS9MvpnPXfN/a+k3bRu3QUpbN1Cnl6tIG4VsgJVnqzqmKJcseAqywSEvpBlI0cSte0v8MbINcvtD/3esb6pwz7VutwiqkAiT8vg/IiQIlBdiu9HGCNb9YIyUV1BS2h7QoNFeir/mkPL3roMHHit//172nGO/C5xfyq0Dx8QZXFOQkTyGIA+cFdsDJwnIzgXUcBsid0BT3gstU47WRWoHWwtBxHqwqGD22ysftTJmS6BqO8iHRgd1Y3iM0uIGG1gkMsuD+bs3J/PzGWkb2beqZmTwvOE1RdXfv5c+DDbfqBSXLHMT8uHzWYWnZ6ekGtXDAUmND7B5w3Hj/C1dlMZxNiBNyyYxsk1pJ5J2zgtgILyzDEIChN9n
 dD07fSiWiU9qm2ILm8q1Du409mjJbMypXKiyDg3+ejuXe4npOeKBE4cdrWh+MQN6qYKXs1KVn+jfyil2YOpBAXvv4CnWiPM8fx6aEnMOuxVKKzCdXM0SB3uHdnTtlyT4AzeW9s/gjI3vuXORCSzzOrMQ7o1TkAdllK0ZFI4b5v2UAvyVcz88mpw9mUyfnk6mU5pOnz7+6s3Z5zfi/WLlXJCm8UgVJZXkPxc3nxU7o+U5wJdcqpwLEJI6wtBvdMgU9NvkIoBvyYPzDVgTUp9qbnaZVjleTuJX5l6OCuTgILfa6sggd5KV4Mw61UZMU35JaAqio6Lv1Pk9IwcIKtqjFK+DbGrLgTYrDQGV5pQe69l3IKqNCFRIwk1BXDynjrGMNh3kVCADurk+oy1JVZ7lvtrmfPNdXkuCRbmpcfZB3HH6RosiObFSIyQ1AK2VySSxQ3eT2GGd2GaHl5D0pVfgdu/1Giq1xE1UK80oYSEH+lMeJt9ZZ6uxiZ012+9BUCCBrnU3rtni29K7od99TYz1CeYeVxIEodfC/6hAGVV7B+HBmwjdkXsDsgAiVBgsn7/s9OOq3YnfftOtEHMJAJOY/TKfzxDMfSzFlYkmYKs6DoChVFMsc0EfQq9jssiH8tPSWmK2BxyBiKIs15xbGdWFFC35sxL5Ay8I//cAbUltpgYcsVHXCUvRlFWMfZXsS6GPKf91dnc/fGyVNkNysneC/twP9GH8mpI9/jDKreek7DZLKGqGqlpJsRlq6UnjYqJwS83QG0kNMKj6lmNI14x52rZGLQtvA/ghIvGlPaa0t6iGFPF5N0j/cW/Uthqfl3l6/epk/upa5kwXkkqNWBbE9oelqIGIupg1HGqv+wQd0VV8EKBTIe0FQ1YGMJ7hVlKa1D6eTMblBMG0H6V9b8OBn3OSGSornNrtE/sbk5yQWVfaTQa0oqOLZ4vFzDv0TEc/y/azWBRaLRY5f/xVksQRDsiu9L+OYHW5vLx88
 fKvaTkqF7jrSZw5ylBDpmRzGXFFGqIGAg/2wHrF9S2unqe6yg2QbJKFbFPyxIYLPJtv+zySNWMhDJxQXxxlz4sj4JgWT/zLLag7oaFsbWj6GrwXFicJgC7X2JuWWItCzEXr8cyKyJCKEVSSwVwPQdCXbnuW9kjh/bOTE4eL464T8PkEK2t0k1XszHGnI3Q+b8CJDoW1rCL6Jq+SefEpOrlky0KUshf4b4AELddxdUcQAO2gTSPqVeUXSWcSccd5xSqg3YMCt4xMrpRY41LK0js2VJJGFRGPRidF/BRFEyoRkrR5446JpQfrQ9nTsAWvZU6VVbUb0CvKYFdO24CYw4PE23mMKnXMtDQqlENKRbXfYiiBfj1mG9W6X5X14iqPx/yEXGrN/0gp7WC7TELZrWV7F11Fr2Rf5y+vq+npj9XFi4uczEubVrXadTJF0mZfAshgxrJ9O8mDTIYh27LXSS55s0UEISeCilk2aMaCfC08AovEqswnN6IRoZVFlmB944KOAp98prNiVKQRwgfmyWTqEBzjIRRJpNNHybIsKLnMJuxX+fX8HX7LjaouDjR98vjxP1BLAQIAABQAAAAIADxTm0gHg1yVTwQAAL0TAAAKAAAAAAAAAAAAAAAAAAAAAAByZWFkbWUudHh0UEsBAgAAFAAAAAgAPFObSCd9+oZYCQAAmBEAAA4AAAAAAAAAAAAAAAAAdwQAAHZwbkREREJFXzEuY3J0UEsBAgAAFAAAAAgAPFObSKrcQK4vBAAAjgYAAAYAAAAAAAAAAAAAAAAA+w0AAGNhLmNydFBLAQIAABQAAAAIADxTm0jkLKGyzAIAAI8DAAAOAAAAAAAAAAAAAAAAAE4SAAB2cG5ERERCRV8xLmtleVBLAQIAABQAAAAIADxTm0iYhkYJeAYAAJcOAAAPAAAAAAAAAAAAAAAAAEYVAAB2cG5ERERCRV8xLm92cG5QSwUGAAAAAAUABQAhAQAA6xsAAAAA"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/vpn/get.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/vpn/get.json b/oneandone/src/test/resources/vpn/get.json
new file mode 100644
index 0000000..6e7d3c9
--- /dev/null
+++ b/oneandone/src/test/resources/vpn/get.json
@@ -0,0 +1,18 @@
+{
+    "id": "39AA65F5D5B02FA02D58173094EBAF95",
+    "name": "My VPN 1",
+    "description": "My VPN 1 description",
+    "state": "ACTIVE",
+    "datacenter": {
+        "id": "D0F6D8C8ED29D3036F94C27BBB789536",
+        "location": "Spain",
+        "country_code": "ES"
+    },
+    "type": "SSL",
+    "ips": [
+        "10.131.1.233",
+        "10.131.0.233"
+    ],
+    "cloudpanel_id": "FW99AA4_7",
+    "creation_date": "30-06-2015T 14:52:35+00.00"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/vpn/list.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/vpn/list.json b/oneandone/src/test/resources/vpn/list.json
new file mode 100644
index 0000000..76715d1
--- /dev/null
+++ b/oneandone/src/test/resources/vpn/list.json
@@ -0,0 +1,38 @@
+[
+{
+  "id": "39AA65F5D5B02FA02D58173094EBAF95",
+  "name": "My VPN 1",
+  "description": "My VPN 1 description",
+  "state": "ACTIVE",
+  "datacenter": {
+    "id": "D0F6D8C8ED29D3036F94C27BBB789536",
+    "location": "Spain",
+    "country_code": "ES"
+  },
+  "type": "SSL",
+  "ips": [
+  "10.131.1.233",
+  "10.131.0.233"
+  ],
+  "cloudpanel_id": "FW99AA4_7",
+  "creation_date": "30-06-2015T 14:52:35+00.00"
+},
+{
+  "id": "987565F5D5B02FA02D58173094EBAF96",
+  "name": "My VPN",
+  "description": null,
+  "state": "ACTIVE",
+  "datacenter": {
+    "id": "D0F6D8C8ED29D3036F94C27BBB789536",
+    "location": "Spain",
+    "country_code": "ES"
+  },
+  "type": "SSL",
+  "ips": [
+  "10.131.1.45",
+  "10.131.0.45"
+  ],
+  "cloudpanel_id": "FW99AA4_8",
+  "creation_date": "30-06-2015T 14:52:35+00.00"
+}
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/4e397202/oneandone/src/test/resources/vpn/list.options.json
----------------------------------------------------------------------
diff --git a/oneandone/src/test/resources/vpn/list.options.json b/oneandone/src/test/resources/vpn/list.options.json
new file mode 100644
index 0000000..76715d1
--- /dev/null
+++ b/oneandone/src/test/resources/vpn/list.options.json
@@ -0,0 +1,38 @@
+[
+{
+  "id": "39AA65F5D5B02FA02D58173094EBAF95",
+  "name": "My VPN 1",
+  "description": "My VPN 1 description",
+  "state": "ACTIVE",
+  "datacenter": {
+    "id": "D0F6D8C8ED29D3036F94C27BBB789536",
+    "location": "Spain",
+    "country_code": "ES"
+  },
+  "type": "SSL",
+  "ips": [
+  "10.131.1.233",
+  "10.131.0.233"
+  ],
+  "cloudpanel_id": "FW99AA4_7",
+  "creation_date": "30-06-2015T 14:52:35+00.00"
+},
+{
+  "id": "987565F5D5B02FA02D58173094EBAF96",
+  "name": "My VPN",
+  "description": null,
+  "state": "ACTIVE",
+  "datacenter": {
+    "id": "D0F6D8C8ED29D3036F94C27BBB789536",
+    "location": "Spain",
+    "country_code": "ES"
+  },
+  "type": "SSL",
+  "ips": [
+  "10.131.1.45",
+  "10.131.0.45"
+  ],
+  "cloudpanel_id": "FW99AA4_8",
+  "creation_date": "30-06-2015T 14:52:35+00.00"
+}
+]
\ No newline at end of file