You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by de...@apache.org on 2016/02/23 13:25:07 UTC

[2/2] jclouds-labs git commit: Implemented Volume API

Implemented Volume 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/9a8b346c
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/9a8b346c
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/9a8b346c

Branch: refs/heads/master
Commit: 9a8b346cafd9e332b0e532f1341e739d0388a347
Parents: 7b3c878
Author: mirza-spc <mi...@stackpointcloud.com>
Authored: Mon Feb 22 14:10:00 2016 +0100
Committer: Reijhanniel Jearl Campos <de...@apache.org>
Committed: Tue Feb 23 20:22:56 2016 +0800

----------------------------------------------------------------------
 profitbricks-rest/pom.xml                       |   7 +
 .../profitbricks/rest/ProfitBricksApi.java      |   8 +
 .../binder/server/AttachCdromRequestBinder.java |   4 +
 .../server/AttachVolumeRequestBinder.java       |   6 +-
 .../server/CreateServerRequestBinder.java       |   3 +
 .../server/UpdateServerRequestBinder.java       |   4 +
 .../volume/CreateSnapshotRequestBinder.java     |  73 +++++++
 .../volume/CreateVolumeRequestBinder.java       |  81 ++++++++
 .../volume/RestoreSnapshotRequestBinder.java    |  72 +++++++
 .../volume/UpdateVolumeRequestBinder.java       |  70 +++++++
 .../profitbricks/rest/domain/Provisionable.java |  25 +++
 .../profitbricks/rest/domain/Server.java        |   4 +-
 .../profitbricks/rest/domain/Snapshot.java      |  91 ++++++++
 .../profitbricks/rest/domain/Volume.java        | 176 +++++++++++++++-
 .../profitbricks/rest/features/ServerApi.java   |   3 +-
 .../profitbricks/rest/features/SnapshotApi.java |  53 +++++
 .../profitbricks/rest/features/VolumeApi.java   | 101 ++++++++-
 .../profitbricks/rest/ids/VolumeRef.java        |  31 +++
 .../profitbricks/rest/util/MacAddresses.java    |  29 +++
 .../profitbricks/rest/util/Passwords.java       |  64 ++++++
 .../server/AttachCdromRequestBinderTest.java    |  63 ++++++
 .../server/AttachVolumeRequestBinderTest.java   |  63 ++++++
 .../server/CreateServerRequestBinderTest.java   |  72 +++++++
 .../server/UpdateServerRequestBinderTest.java   |  66 ++++++
 .../volume/CreateSnapshotRequestBinderTest.java |  63 ++++++
 .../volume/CreateVolumeRequestBinderTest.java   |  73 +++++++
 .../RestoreSnapshotRequestBinderTest.java       |  54 +++++
 .../volume/UpdateVolumeRequestBinderTest.java   |  63 ++++++
 .../rest/features/DataCenterApiMockTest.java    |  32 ---
 .../rest/features/ServerApiLiveTest.java        |  61 +++++-
 .../rest/features/ServerApiMockTest.java        |  39 +---
 .../rest/features/VolumeApiLiveTest.java        | 175 ++++++++++++++++
 .../rest/features/VolumeApiMockTest.java        | 154 ++++++++++++++
 .../internal/BaseProfitBricksApiMockTest.java   |  12 +-
 .../rest/internal/BaseProfitBricksLiveTest.java |  37 +++-
 .../rest/util/MacAddressesTest.java             |  45 ++++
 .../profitbricks/rest/util/ParseIdTest.java     |  49 +++++
 .../profitbricks/rest/util/PasswordsTest.java   |  53 +++++
 .../src/test/resources/volume/get.json          |  33 +++
 .../src/test/resources/volume/list.json         | 205 +++++++++++++++++++
 .../src/test/resources/volume/snapshot.json     |  30 +++
 41 files changed, 2259 insertions(+), 88 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/pom.xml
----------------------------------------------------------------------
diff --git a/profitbricks-rest/pom.xml b/profitbricks-rest/pom.xml
index d676d88..7c04c2f 100644
--- a/profitbricks-rest/pom.xml
+++ b/profitbricks-rest/pom.xml
@@ -65,6 +65,13 @@
            <artifactId>jclouds-okhttp</artifactId>
            <version>${jclouds.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.5-SNAPSHOT</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
         <!-- Test dependencies -->
         <dependency>
             <groupId>org.apache.jclouds</groupId>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApi.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApi.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApi.java
index 977fbc7..44d4c0d 100644
--- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApi.java
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApi.java
@@ -21,6 +21,8 @@ import com.google.common.annotations.Beta;
 import java.io.Closeable;
 import org.apache.jclouds.profitbricks.rest.features.DataCenterApi;
 import org.apache.jclouds.profitbricks.rest.features.ServerApi;
+import org.apache.jclouds.profitbricks.rest.features.SnapshotApi;
+import org.apache.jclouds.profitbricks.rest.features.VolumeApi;
 import org.jclouds.rest.annotations.Delegate;
 
 @Beta
@@ -31,5 +33,11 @@ public interface ProfitBricksApi extends Closeable {
    
    @Delegate
    ServerApi serverApi();
+   
+   @Delegate
+   VolumeApi volumeApi();
+   
+   @Delegate
+   SnapshotApi snapshotApi();
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachCdromRequestBinder.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachCdromRequestBinder.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachCdromRequestBinder.java
index 49faee2..efc1195 100644
--- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachCdromRequestBinder.java
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachCdromRequestBinder.java
@@ -44,6 +44,10 @@ public class AttachCdromRequestBinder extends BaseProfitBricksRequestBinder<Serv
    protected String createPayload(Server.Request.AttachCdromPayload payload) {
       
       checkNotNull(payload, "payload");
+      
+      checkNotNull(payload.dataCenterId(), "dataCenterId");
+      checkNotNull(payload.serverId(), "serverId");
+      checkNotNull(payload.imageId(), "imageId");
 
       dataCenterId = payload.dataCenterId();
       serverId = payload.serverId();

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachVolumeRequestBinder.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachVolumeRequestBinder.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachVolumeRequestBinder.java
index f843d34..0ffebaa 100644
--- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachVolumeRequestBinder.java
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachVolumeRequestBinder.java
@@ -44,11 +44,15 @@ public class AttachVolumeRequestBinder extends BaseProfitBricksRequestBinder<Ser
    protected String createPayload(Server.Request.AttachVolumePayload payload) {
       
       checkNotNull(payload, "payload");
+      
+      checkNotNull(payload.dataCenterId(), "dataCenterId");
+      checkNotNull(payload.serverId(), "serverId");
+      checkNotNull(payload.volumeId(), "volumeId");
 
       dataCenterId = payload.dataCenterId();
       serverId = payload.serverId();
       
-      requestBuilder.put("id", payload.imageId());
+      requestBuilder.put("id", payload.volumeId());
       return jsonBinder.toJson(requestBuilder);
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/CreateServerRequestBinder.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/CreateServerRequestBinder.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/CreateServerRequestBinder.java
index aaa07c1..bac058d 100644
--- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/CreateServerRequestBinder.java
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/CreateServerRequestBinder.java
@@ -16,6 +16,7 @@
  */
 package org.apache.jclouds.profitbricks.rest.binder.server;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import com.google.inject.Inject;
 import java.util.HashMap;
 import java.util.Map;
@@ -40,6 +41,8 @@ public class CreateServerRequestBinder extends BaseProfitBricksRequestBinder<Ser
    @Override
    protected String createPayload(Server.Request.CreatePayload payload) {
 
+      checkNotNull(payload.dataCenterId(), "dataCenterId");
+
       dataCenterId = payload.dataCenterId();
       
       Map<String, Object> properties = new HashMap<String, Object>();

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/UpdateServerRequestBinder.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/UpdateServerRequestBinder.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/UpdateServerRequestBinder.java
index 9d7eda9..3c00111 100644
--- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/UpdateServerRequestBinder.java
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/server/UpdateServerRequestBinder.java
@@ -16,6 +16,7 @@
  */
 package org.apache.jclouds.profitbricks.rest.binder.server;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import com.google.inject.Inject;
 import java.util.HashMap;
 import java.util.Map;
@@ -41,6 +42,9 @@ public class UpdateServerRequestBinder extends BaseProfitBricksRequestBinder<Ser
    @Override
    protected String createPayload(Server.Request.UpdatePayload payload) {
 
+      checkNotNull(payload.dataCenterId(), "dataCenterId");
+      checkNotNull(payload.id(), "serverId");
+      
       dataCenterId = payload.dataCenterId();
       serverId = payload.id();
       

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateSnapshotRequestBinder.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateSnapshotRequestBinder.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateSnapshotRequestBinder.java
new file mode 100644
index 0000000..d18d69e
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateSnapshotRequestBinder.java
@@ -0,0 +1,73 @@
+/*
+ * 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.profitbricks.rest.binder.volume;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import javax.ws.rs.core.MediaType;
+import org.apache.jclouds.profitbricks.rest.binder.BaseProfitBricksRequestBinder;
+import org.apache.jclouds.profitbricks.rest.domain.Volume;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpRequest.Builder;
+
+public class CreateSnapshotRequestBinder extends BaseProfitBricksRequestBinder<Volume.Request.CreateSnapshotPayload> {
+
+   protected final Multimap<String, String> requestBuilder;
+   private String dataCenterId;
+   private String volumeId;
+
+   CreateSnapshotRequestBinder() {
+      super("snapshot");
+      this.requestBuilder = HashMultimap.create();
+   }
+
+   @Override
+   protected String createPayload(Volume.Request.CreateSnapshotPayload payload) {
+
+      checkNotNull(payload.dataCenterId(), "dataCenterId");
+      checkNotNull(payload.volumeId(), "volumeId");
+      
+      dataCenterId = payload.dataCenterId();
+      volumeId = payload.volumeId();
+      
+      if (payload.name() != null)
+         requestBuilder.put("name",  payload.name());
+      
+      if (payload.description() != null)
+         requestBuilder.put("description",  payload.description());
+     
+      return "";
+   }
+
+   @Override
+   protected <R extends HttpRequest> R createRequest(R fromRequest, String payload) {
+      
+      fromRequest = super.createRequest(fromRequest, payload);
+      
+      Builder<?> reqBuilder = fromRequest.toBuilder();
+                  
+      reqBuilder.addFormParams(requestBuilder);
+      reqBuilder.replacePath(String.format("/rest/datacenters/%s/volumes/%s/create-snapshot", dataCenterId, volumeId));
+      
+      R req = (R) reqBuilder.build();
+      req.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+      
+      return req;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateVolumeRequestBinder.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateVolumeRequestBinder.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateVolumeRequestBinder.java
new file mode 100644
index 0000000..76e4a3b
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateVolumeRequestBinder.java
@@ -0,0 +1,81 @@
+/*
+ * 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.profitbricks.rest.binder.volume;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.inject.Inject;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.jclouds.profitbricks.rest.binder.BaseProfitBricksRequestBinder;
+import org.apache.jclouds.profitbricks.rest.domain.Volume;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+
+public class CreateVolumeRequestBinder extends BaseProfitBricksRequestBinder<Volume.Request.CreatePayload> {
+
+   protected final Map<String, Object> requestBuilder;
+   final Json jsonBinder;
+   
+   private String dataCenterId;
+
+   @Inject
+   CreateVolumeRequestBinder(Json jsonBinder) {
+      super("volume");
+      this.jsonBinder = jsonBinder;
+      this.requestBuilder = new HashMap<String, Object>();
+   }
+
+   @Override
+   protected String createPayload(Volume.Request.CreatePayload payload) {
+      
+      checkNotNull(payload.dataCenterId(), "dataCenterId");
+
+      dataCenterId = payload.dataCenterId();
+      
+      Map<String, Object> properties = new HashMap<String, Object>();
+      
+      properties.put("size",  payload.size());
+      
+      if (payload.name() != null)
+         properties.put("name", payload.name());
+      
+      if (payload.bus() != null)
+         properties.put("bus", payload.bus());
+      
+      if (payload.type() != null)
+         properties.put("type", payload.type());
+      
+      if (payload.imagePassword() != null)
+         properties.put("imagePassword", payload.imagePassword());
+      
+      if (payload.image() != null)
+         properties.put("image", payload.image());
+      else if (payload.licenceType() != null)
+         properties.put("licenceType", payload.licenceType());
+      
+      requestBuilder.put("properties", properties);
+      
+      return jsonBinder.toJson(requestBuilder);
+   }
+
+   @Override
+   protected <R extends HttpRequest> R createRequest(R fromRequest, String payload) {              
+      R request = (R) fromRequest.toBuilder().replacePath(String.format("/rest/datacenters/%s/volumes", dataCenterId)).build();
+      return super.createRequest(request, payload);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/RestoreSnapshotRequestBinder.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/RestoreSnapshotRequestBinder.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/RestoreSnapshotRequestBinder.java
new file mode 100644
index 0000000..ff08240
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/RestoreSnapshotRequestBinder.java
@@ -0,0 +1,72 @@
+/*
+ * 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.profitbricks.rest.binder.volume;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import javax.ws.rs.core.MediaType;
+import org.apache.jclouds.profitbricks.rest.binder.BaseProfitBricksRequestBinder;
+import org.apache.jclouds.profitbricks.rest.domain.Volume;
+import org.jclouds.http.HttpRequest;
+
+public class RestoreSnapshotRequestBinder extends BaseProfitBricksRequestBinder<Volume.Request.RestoreSnapshotPayload> {
+
+   protected final Multimap<String, String> requestBuilder;
+
+   private String dataCenterId;
+   private String volumeId;
+   private String snapshotId;   
+
+   RestoreSnapshotRequestBinder() {
+      super("snapshot");
+      this.requestBuilder = HashMultimap.create();
+   }
+
+   @Override
+   protected String createPayload(Volume.Request.RestoreSnapshotPayload payload) {
+
+      checkNotNull(payload.dataCenterId(), "dataCenterId");
+      checkNotNull(payload.volumeId(), "volumeId");
+      checkNotNull(payload.snapshotId(), "snapshotId");
+      
+      dataCenterId = payload.dataCenterId();
+      volumeId = payload.volumeId();
+      snapshotId = payload.snapshotId();
+      
+      requestBuilder.put("snapshotId", payload.snapshotId());
+      
+      return "";
+   }
+
+   @Override
+   protected <R extends HttpRequest> R createRequest(R fromRequest, String payload) {
+      
+      fromRequest = super.createRequest(fromRequest, payload);
+      
+      HttpRequest.Builder<?> reqBuilder = fromRequest.toBuilder();
+      
+      reqBuilder.addFormParams(requestBuilder);
+      reqBuilder.replacePath(String.format("/rest/datacenters/%s/volumes/%s/restore-snapshot", dataCenterId, volumeId));
+      
+      R req = (R) reqBuilder.build();
+      req.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+      
+      return req;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/UpdateVolumeRequestBinder.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/UpdateVolumeRequestBinder.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/UpdateVolumeRequestBinder.java
new file mode 100644
index 0000000..51d8027
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/binder/volume/UpdateVolumeRequestBinder.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jclouds.profitbricks.rest.binder.volume;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.inject.Inject;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.jclouds.profitbricks.rest.binder.BaseProfitBricksRequestBinder;
+import org.apache.jclouds.profitbricks.rest.domain.Volume;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+
+public class UpdateVolumeRequestBinder extends BaseProfitBricksRequestBinder<Volume.Request.UpdatePayload> {
+
+   protected final Map<String, Object> requestBuilder;
+   final Json jsonBinder;
+   
+   private String dataCenterId;
+   private String volumeId;
+
+   @Inject
+   UpdateVolumeRequestBinder(Json jsonBinder) {
+      super("volume");
+      this.jsonBinder = jsonBinder;
+      this.requestBuilder = new HashMap<String, Object>();
+   }
+
+   @Override
+   protected String createPayload(Volume.Request.UpdatePayload payload) {
+
+      checkNotNull(payload.dataCenterId(), "dataCenterId");
+      checkNotNull(payload.id(), "volumeId");
+      
+      dataCenterId = payload.dataCenterId();
+      volumeId = payload.id();
+      
+      if (payload.name() != null)
+         requestBuilder.put("name",  payload.name());
+      
+      if (payload.size() != null)
+         requestBuilder.put("size",  payload.size());
+      
+      if (payload.bus() != null)
+         requestBuilder.put("bus",  payload.bus());
+      
+      return jsonBinder.toJson(requestBuilder);
+   }
+
+   @Override
+   protected <R extends HttpRequest> R createRequest(R fromRequest, String payload) {              
+      R request = (R) fromRequest.toBuilder().replacePath(String.format("/rest/datacenters/%s/volumes/%s", dataCenterId, volumeId)).build();
+      return super.createRequest(request, payload);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Provisionable.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Provisionable.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Provisionable.java
new file mode 100644
index 0000000..9e813d2
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Provisionable.java
@@ -0,0 +1,25 @@
+/*
+ * 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.profitbricks.rest.domain;
+
+/**
+ * Marker interface for {@link org.jclouds.profitbricks.domain.Image} and 
+ * {@link org.jclouds.profitbricks.domain.Snapshot}
+ */
+public interface Provisionable {
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Server.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Server.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Server.java
index d832ea0..f574777 100644
--- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Server.java
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Server.java
@@ -259,14 +259,14 @@ public abstract class Server {
       @AutoValue 
       public abstract static class AttachVolumePayload {
 
-         public abstract String imageId();
+         public abstract String volumeId();
          public abstract String dataCenterId();
          public abstract String serverId();
          
          @AutoValue.Builder
          public abstract static class Builder {
             
-            public abstract Builder imageId(String imageId);
+            public abstract Builder volumeId(String volumeId);
             public abstract Builder dataCenterId(String dataCenterId);
             public abstract Builder serverId(String serverId);
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Snapshot.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Snapshot.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Snapshot.java
new file mode 100644
index 0000000..793cede
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Snapshot.java
@@ -0,0 +1,91 @@
+/*
+ * 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.profitbricks.rest.domain;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+@AutoValue
+public abstract class Snapshot {
+
+    public abstract String id();
+    
+    public abstract String type();
+
+    public abstract String href();
+
+    @Nullable
+    public abstract Metadata metadata();
+
+    @Nullable
+    public abstract Properties properties();
+
+    @SerializedNames({"id", "type", "href", "metadata", "properties"})
+    public static Snapshot create(String id, String type, String href, Metadata metadata, Properties properties) {
+        return new AutoValue_Snapshot(id, type, href, metadata, properties);
+    }
+
+    @AutoValue
+    public abstract static class Properties {
+
+        @Nullable
+        public abstract String name();
+
+        @Nullable
+        public abstract String description();
+
+        @Nullable
+        public abstract Integer size();
+
+        @Nullable
+        public abstract LicenceType licenceType();
+        
+        public abstract Location location();
+
+        public abstract boolean cpuHotPlug();
+
+        public abstract boolean cpuHotUnplug();
+
+        public abstract boolean ramHotPlug();
+
+        public abstract boolean ramHotUnplug();
+
+        public abstract boolean nicHotPlug();
+
+        public abstract boolean nicHotUnplug();
+
+        public abstract boolean discVirtioHotPlug();
+
+        public abstract boolean discVirtioHotUnplug();
+
+        public abstract boolean discScsiHotPlug();
+
+        public abstract boolean discScsiHotUnplug();
+
+        @SerializedNames({"name", "description", "size", "licenceType", "location", "cpuHotPlug", "cpuHotUnplug", "ramHotPlug", "ramHotUnplug", "nicHotPlug", "nicHotUnplug", "discVirtioHotPlug", "discVirtioHotUnplug", "discScsiHotPlug", "discScsiHotUnplug"})
+        public static Snapshot.Properties create(String name, String description, Integer size, LicenceType licenceType, Location location,
+                boolean cpuHotPlug, boolean cpuHotUnplug, boolean ramHotPlug, boolean ramHotUnplug, boolean nicHotPlug, boolean nicHotUnplug, boolean discVirtioHotPlug,
+                boolean discVirtioHotUnplug, boolean discScsiHotPlug, boolean discScsiHotUnplug) {
+
+           return new AutoValue_Snapshot_Properties(name, description, size, licenceType, location, cpuHotPlug, cpuHotUnplug, ramHotPlug, ramHotUnplug, nicHotPlug, nicHotUnplug, discVirtioHotPlug, discVirtioHotUnplug, discScsiHotPlug, discScsiHotUnplug);
+           
+
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Volume.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Volume.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Volume.java
index b978294..0608f3d 100644
--- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Volume.java
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/domain/Volume.java
@@ -18,13 +18,18 @@ package org.apache.jclouds.profitbricks.rest.domain;
 
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Enums;
+import org.apache.jclouds.profitbricks.rest.util.Passwords;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.json.SerializedNames;
+import static com.google.common.base.Preconditions.checkArgument;
 
 @AutoValue
 public abstract class Volume {
 
     public abstract String id();
+    
+    @Nullable
+    public abstract String dataCenterId();
 
     public abstract String type();
 
@@ -36,9 +41,9 @@ public abstract class Volume {
     @Nullable
     public abstract Properties properties();
 
-    @SerializedNames({"id", "type", "href", "metadata", "properties"})
-    public static Volume create(String id, String type, String href, Metadata metadata, Properties properties) {
-        return new AutoValue_Volume(id, type, href, metadata, properties);
+    @SerializedNames({"id", "dataCenterId", "type", "href", "metadata", "properties"})
+    public static Volume create(String id, String dataCenterId, String type, String href, Metadata metadata, Properties properties) {
+        return new AutoValue_Volume(id, dataCenterId, type, href, metadata, properties);
     }
 
     @AutoValue
@@ -53,8 +58,10 @@ public abstract class Volume {
             }
         }
 
+        @Nullable
         public abstract String name();
 
+        @Nullable
         public abstract String type();
 
         public abstract float size();
@@ -65,6 +72,7 @@ public abstract class Volume {
         @Nullable
         public abstract String imagePassword();
         
+        @Nullable
         public abstract BusType bus();
 
         public abstract LicenceType licenceType();
@@ -102,4 +110,166 @@ public abstract class Volume {
         }
     }
 
+   
+   public static final class Request {
+
+      public static CreatePayload.Builder creatingBuilder() {
+         return new AutoValue_Volume_Request_CreatePayload.Builder();
+      }
+
+      public static UpdatePayload.Builder updatingBuilder() {
+         return new AutoValue_Volume_Request_UpdatePayload.Builder();
+      }
+      
+      public static CreateSnapshotPayload.Builder createSnapshotBuilder() {
+         return new AutoValue_Volume_Request_CreateSnapshotPayload.Builder();
+      }
+      
+      public static RestoreSnapshotPayload.Builder restoreSnapshotBuilder() {
+         return new AutoValue_Volume_Request_RestoreSnapshotPayload.Builder();
+      }
+      
+      @AutoValue
+      public abstract static class CreatePayload {
+
+         @Nullable
+         public abstract String name();
+
+         @Nullable
+         public abstract String type();
+
+         public abstract int size();
+
+         @Nullable
+         public abstract String image();
+
+         @Nullable
+         public abstract String imagePassword();
+
+         @Nullable
+         public abstract Properties.BusType bus();
+
+         @Nullable
+         public abstract LicenceType licenceType();
+         
+         public abstract String dataCenterId();
+
+         @AutoValue.Builder
+         public abstract static class Builder {
+
+            public abstract Builder name(String name);
+            public abstract Builder type(String type);
+            public abstract Builder size(int size);
+            public abstract Builder image(String image);
+            public abstract Builder imagePassword(String imagePassword);
+            public abstract Builder bus(Properties.BusType bus);
+            public abstract Builder licenceType(LicenceType licenceType);
+            public abstract Builder dataCenterId(String dataCenterId);
+
+            abstract CreatePayload autoBuild();
+
+            public CreatePayload build() {
+               CreatePayload payload = autoBuild();
+               
+               if (payload.imagePassword() != null)
+                  checkArgument(Passwords.isValidPassword(payload.imagePassword()), "Password's format is not valid");
+               
+               checkArgument(
+                  payload.image() != null || payload.licenceType() != null,
+                  "Either image or licenceType need to be present"
+               );
+               
+               return payload;
+            }
+         }
+
+      }
+
+      @AutoValue
+      public abstract static class UpdatePayload {
+
+         @Nullable
+         public abstract String name();
+
+         @Nullable
+         public abstract Integer size();
+
+         @Nullable
+         public abstract Properties.BusType bus();
+         
+         public abstract String dataCenterId();
+         public abstract String id();
+
+         @AutoValue.Builder
+         public abstract static class Builder {
+
+            public abstract Builder name(String name);
+            public abstract Builder size(Integer size);
+            public abstract Builder bus(Properties.BusType bus);
+            public abstract Builder dataCenterId(String dataCenterId);
+            public abstract Builder id(String id);
+
+            abstract UpdatePayload autoBuild();
+
+            public UpdatePayload build() {
+               return autoBuild();
+            }
+         }
+      }
+      
+      @AutoValue
+      public abstract static class CreateSnapshotPayload {
+
+         @Nullable
+         public abstract String name();
+
+         @Nullable
+         public abstract String description();
+         
+         public abstract String dataCenterId();
+         public abstract String volumeId();
+
+         @AutoValue.Builder
+         public abstract static class Builder {
+
+            public abstract Builder name(String name);
+            public abstract Builder description(String description);
+            public abstract Builder dataCenterId(String dataCenterId);
+            public abstract Builder volumeId(String volumeId);
+
+            abstract CreateSnapshotPayload autoBuild();
+
+            public CreateSnapshotPayload build() {
+               return autoBuild();
+            }
+         }
+
+      }
+      
+      @AutoValue
+      public abstract static class RestoreSnapshotPayload {
+
+         public abstract String snapshotId();
+         public abstract String dataCenterId();
+         public abstract String volumeId();
+
+         @AutoValue.Builder
+         public abstract static class Builder {
+
+            public abstract Builder snapshotId(String snapshotId);
+            public abstract Builder dataCenterId(String dataCenterId);
+            public abstract Builder volumeId(String volumeId);
+
+            abstract RestoreSnapshotPayload autoBuild();
+
+            public RestoreSnapshotPayload build() {
+               return autoBuild();
+            }
+         }
+
+      }
+      
+   }
+   
+    
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/ServerApi.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/ServerApi.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/ServerApi.java
index fa1718c..faedf6c 100644
--- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/ServerApi.java
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/ServerApi.java
@@ -182,8 +182,7 @@ public interface ServerApi extends Closeable {
       @Override
       public <V> V apply(InputStream stream, Type type) throws IOException {
          try {
-            return (V) json.fromJson(this.parseService.parseId(Strings2.toStringAndClose(stream), "datacenters", "dataCenterId"), type
-            );
+            return (V) json.fromJson(this.parseService.parseId(Strings2.toStringAndClose(stream), "datacenters", "dataCenterId"), type);
          } finally {
             if (stream != null)
                stream.close();

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/SnapshotApi.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/SnapshotApi.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/SnapshotApi.java
new file mode 100644
index 0000000..f1d4815
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/SnapshotApi.java
@@ -0,0 +1,53 @@
+/*
+ * 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.profitbricks.rest.features;
+
+import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+import java.io.Closeable;
+import javax.inject.Named;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import org.apache.jclouds.profitbricks.rest.domain.Snapshot;
+import org.jclouds.Fallbacks;
+import org.jclouds.http.filters.BasicAuthentication;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+
+@Path("/snapshots")
+@RequestFilters(BasicAuthentication.class)
+public interface SnapshotApi extends Closeable {
+   
+   @Named("snapshot:get")
+   @GET   
+   @Path("/{id}")
+   @ResponseParser(SnapshotApi.SnapshotParser.class)
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   Snapshot getSnapshot(@PathParam("id") String id);
+
+      
+   static final class SnapshotParser extends ParseJson<Snapshot> {
+      @Inject SnapshotParser(Json json) {
+         super(json, TypeLiteral.get(Snapshot.class));
+      }
+   }
+   
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/VolumeApi.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/VolumeApi.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/VolumeApi.java
index 028a648..4f03478 100644
--- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/VolumeApi.java
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/features/VolumeApi.java
@@ -19,20 +19,117 @@ package org.apache.jclouds.profitbricks.rest.features;
 import com.google.inject.Inject;
 import com.google.inject.TypeLiteral;
 import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Type;
+import java.util.List;
+import javax.inject.Named;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import org.apache.jclouds.profitbricks.rest.binder.volume.CreateSnapshotRequestBinder;
+import org.apache.jclouds.profitbricks.rest.binder.volume.CreateVolumeRequestBinder;
+import org.apache.jclouds.profitbricks.rest.binder.volume.RestoreSnapshotRequestBinder;
+import org.apache.jclouds.profitbricks.rest.binder.volume.UpdateVolumeRequestBinder;
+import org.apache.jclouds.profitbricks.rest.domain.Snapshot;
 import org.apache.jclouds.profitbricks.rest.domain.Volume;
+import org.apache.jclouds.profitbricks.rest.domain.options.DepthOptions;
+import org.apache.jclouds.profitbricks.rest.util.ParseId;
+import org.jclouds.Fallbacks;
+import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404;
 import org.jclouds.http.filters.BasicAuthentication;
 import org.jclouds.http.functions.ParseJson;
 import org.jclouds.json.Json;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.PATCH;
+import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.util.Strings2;
 
-@Path("/volumes")
+@Path("/datacenters/{dataCenterId}/volumes")
 @RequestFilters(BasicAuthentication.class)
 public interface VolumeApi extends Closeable {
    
+   @Named("volume:list")
+   @GET
+   @SelectJson("items")
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<Volume> getList(@PathParam("dataCenterId") String dataCenterId);
+
+   @Named("volume:list")
+   @GET
+   @SelectJson("items")
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<Volume> getList(@PathParam("dataCenterId") String dataCenterId, DepthOptions options);
+   
+   @Named("volume:get")
+   @GET   
+   @Path("/{volumeId}")
+   @ResponseParser(VolumeApi.VolumeParser.class)
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   Volume getVolume(@PathParam("dataCenterId") String dataCenterId, @PathParam("volumeId") String volumeId);
+
+   @Named("volume:get")
+   @GET   
+   @Path("/{volumeId}")
+   @ResponseParser(VolumeApi.VolumeParser.class)
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   Volume getVolume(@PathParam("dataCenterId") String dataCenterId, @PathParam("volumeId") String volumeId, DepthOptions options);
+   
+   @Named("volume:create")
+   @POST
+   @MapBinder(CreateVolumeRequestBinder.class)
+   @ResponseParser(VolumeApi.VolumeParser.class)
+   Volume createVolume(@PayloadParam("volume") Volume.Request.CreatePayload payload);
+   
+   @Named("volume:update")
+   @PATCH
+   @Path("/{volumeId}")
+   @MapBinder(UpdateVolumeRequestBinder.class)
+   @ResponseParser(VolumeApi.VolumeParser.class)
+   @Produces("application/vnd.profitbricks.partial-properties+json")
+   Volume updateVolume(@PayloadParam("volume") Volume.Request.UpdatePayload payload);
+   
+   @Named("volume:delete")
+   @DELETE
+   @Path("/{volumeId}")
+   @Fallback(Fallbacks.VoidOnNotFoundOr404.class)
+   void deleteVolume(@PathParam("dataCenterId") String dataCenterId, @PathParam("volumeId") String volumeId);
+   
+   @Named("volume:snapshot:create")
+   @POST
+   @MapBinder(CreateSnapshotRequestBinder.class)
+   @ResponseParser(SnapshotApi.SnapshotParser.class)
+   Snapshot createSnapshot(@PayloadParam("snapshot") Volume.Request.CreateSnapshotPayload payload);
+   
+   @Named("volume:snapshot:restore")
+   @POST
+   @MapBinder(RestoreSnapshotRequestBinder.class)
+   void restoreSnapshot(@PayloadParam("snapshot") Volume.Request.RestoreSnapshotPayload payload);   
+   
    static final class VolumeParser extends ParseJson<Volume> {
-      @Inject VolumeParser(Json json) {
+      
+      final ParseId parseService;
+      
+      @Inject VolumeParser(Json json, ParseId parseId) {
          super(json, TypeLiteral.get(Volume.class));
+         this.parseService = parseId;
+      }
+      
+      @Override
+      public <V> V apply(InputStream stream, Type type) throws IOException {
+         try {
+            return (V) json.fromJson(this.parseService.parseId(Strings2.toStringAndClose(stream), "datacenters", "dataCenterId"), type);
+         } finally {
+            if (stream != null)
+               stream.close();
+         }
       }
    }
    

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ids/VolumeRef.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ids/VolumeRef.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ids/VolumeRef.java
new file mode 100644
index 0000000..a60d57a
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ids/VolumeRef.java
@@ -0,0 +1,31 @@
+/*
+ * 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.profitbricks.rest.ids;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class VolumeRef {
+   
+   public abstract String dataCenterId();
+   public abstract String volumeId();
+      
+   public static VolumeRef create(String dataCenterId, String volumeId) {
+      return new AutoValue_VolumeRef(dataCenterId, volumeId);
+   }
+    
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/util/MacAddresses.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/util/MacAddresses.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/util/MacAddresses.java
new file mode 100644
index 0000000..a27a9ce
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/util/MacAddresses.java
@@ -0,0 +1,29 @@
+/*
+ * 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.profitbricks.rest.util;
+
+import java.util.regex.Pattern;
+
+public class MacAddresses {
+
+   private static final String MAC_ADDR_FORMAT = "^([0-9a-f]{2}[:]){5}([0-9a-f]{2})$";
+   private static final Pattern MAC_ADDR_PATTERN = Pattern.compile(MAC_ADDR_FORMAT);
+
+   public static boolean isMacAddress(String in) {
+      return MAC_ADDR_PATTERN.matcher(in).matches();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/util/Passwords.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/util/Passwords.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/util/Passwords.java
new file mode 100644
index 0000000..b6e5a85
--- /dev/null
+++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/util/Passwords.java
@@ -0,0 +1,64 @@
+/*
+ * 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.profitbricks.rest.util;
+
+import java.util.Random;
+import java.util.regex.Pattern;
+
+import com.google.common.collect.ImmutableSet;
+
+public class Passwords {
+
+   private static final Random random = new Random();
+
+   private static final int MIN_CHAR = 8;
+   private static final int MAX_CHAR = 50;
+   private static final String PASSWORD_FORMAT = String.format(
+           "[a-zA-Z0-9][^iIloOwWyYzZ10]{%d,%d}", MIN_CHAR - 1, MAX_CHAR);
+   private static final Pattern PASSWORD_PATTERN = Pattern.compile(PASSWORD_FORMAT);
+
+   private static final ImmutableSet<Character> INVALID_CHARS = ImmutableSet.<Character>of(
+           'i', 'I', 'l', 'o', 'O', 'w', 'W', 'y', 'Y', 'z', 'Z', '1', '0');
+
+   public static boolean isValidPassword(String password) {
+      return PASSWORD_PATTERN.matcher(password).matches();
+   }
+
+   public static String generate() {
+      int count = random.nextInt(MAX_CHAR - MIN_CHAR) + MIN_CHAR;
+
+      final char[] buffer = new char[count];
+
+      final int start = 'A';
+      final int end = 'z';
+      final int gap = end - start + 1;
+
+      while (count-- != 0) {
+         char ch = (char) (random.nextInt(gap) + start);
+         if ((isBetween(ch, start, 'Z') || isBetween(ch, 'a', end))
+                 && !INVALID_CHARS.contains(ch))
+            buffer[count] = ch;
+         else
+            count++;
+      }
+      return new String(buffer);
+   }
+
+   private static boolean isBetween(char ch, int start, int end) {
+      return ch >= start && ch <= end;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachCdromRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachCdromRequestBinderTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachCdromRequestBinderTest.java
new file mode 100644
index 0000000..b0af74f
--- /dev/null
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachCdromRequestBinderTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.profitbricks.rest.binder.server;
+
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.Map;
+import org.apache.jclouds.profitbricks.rest.domain.Server;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "AttachCdromRequestBinderTest")
+public class AttachCdromRequestBinderTest {
+
+   @Test
+   public void testCreatePayload() {
+      
+      Injector injector = Guice.createInjector(new GsonModule());
+      AttachCdromRequestBinder binder = injector.getInstance(AttachCdromRequestBinder.class);
+            
+      Server.Request.AttachCdromPayload payload = Server.Request.attachCdromBuilder()
+            .dataCenterId("datacenter-id")
+            .serverId("server-id")
+            .imageId("image-id")
+            .build();
+
+      String actual = binder.createPayload(payload);
+
+      HttpRequest request = binder.createRequest(
+              HttpRequest.builder().method("PATCH").endpoint("http://test.com").build(), 
+              actual
+      );
+      
+      assertEquals(request.getEndpoint().getPath(), "/rest/datacenters/datacenter-id/servers/server-id/cdroms");
+      assertNotNull(actual, "Binder returned null payload");
+      
+      Json json = injector.getInstance(Json.class);
+      String expectedJson = json.toJson(json.fromJson(expectedPayload, new TypeToken<Map<String, Object>>(){}.getType()));
+      
+      assertEquals(actual, expectedJson);
+   }
+
+   private final String expectedPayload = "{\"id\":\"image-id\"}";
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachVolumeRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachVolumeRequestBinderTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachVolumeRequestBinderTest.java
new file mode 100644
index 0000000..590da6d
--- /dev/null
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/AttachVolumeRequestBinderTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.profitbricks.rest.binder.server;
+
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.Map;
+import org.apache.jclouds.profitbricks.rest.domain.Server;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "AttachVolumeRequestBinderTest")
+public class AttachVolumeRequestBinderTest {
+
+   @Test
+   public void testCreatePayload() {
+      
+      Injector injector = Guice.createInjector(new GsonModule());
+      AttachVolumeRequestBinder binder = injector.getInstance(AttachVolumeRequestBinder.class);
+            
+      Server.Request.AttachVolumePayload payload = Server.Request.attachVolumeBuilder()
+            .dataCenterId("datacenter-id")
+            .serverId("server-id")
+            .volumeId("volume-id")
+            .build();
+
+      String actual = binder.createPayload(payload);
+
+      HttpRequest request = binder.createRequest(
+              HttpRequest.builder().method("POST").endpoint("http://test.com").build(), 
+              actual
+      );
+      
+      assertEquals(request.getEndpoint().getPath(), "/rest/datacenters/datacenter-id/servers/server-id/volumes");
+      assertNotNull(actual, "Binder returned null payload");
+      
+      Json json = injector.getInstance(Json.class);
+      String expectedJson = json.toJson(json.fromJson(expectedPayload, new TypeToken<Map<String, Object>>(){}.getType()));
+      
+      assertEquals(actual, expectedJson);
+   }
+
+   private final String expectedPayload = "{\"id\":\"volume-id\"}";
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/CreateServerRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/CreateServerRequestBinderTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/CreateServerRequestBinderTest.java
new file mode 100644
index 0000000..da0ee30
--- /dev/null
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/CreateServerRequestBinderTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.profitbricks.rest.binder.server;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.jclouds.profitbricks.rest.domain.Server;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "CreateServerRequestBinderTest")
+public class CreateServerRequestBinderTest {
+
+   @Test
+   public void testCreatePayload() {
+      
+      Injector injector = Guice.createInjector(new GsonModule());
+      CreateServerRequestBinder binder = injector.getInstance(CreateServerRequestBinder.class);
+            
+      Server.Request.CreatePayload payload = Server.Request.creatingBuilder()
+              .dataCenterId("datacenter-id")
+              .name("jclouds-node")
+              .cores(4)
+              .ram(4 * 1024)
+              .build();
+
+      String actual = binder.createPayload(payload);
+
+      HttpRequest request = binder.createRequest(
+              HttpRequest.builder().method("POST").endpoint("http://test.com").build(), 
+              actual
+      );
+      
+      assertEquals(request.getEndpoint().getPath(), "/rest/datacenters/datacenter-id/servers");
+      assertNotNull(actual, "Binder returned null payload");
+      
+      Json json = injector.getInstance(Json.class);
+      
+      Map<String, Object> properties = new HashMap<String, Object>();
+      
+      properties.put("cores", 4);
+      properties.put("name", "jclouds-node");
+      properties.put("ram", 4 * 1024);
+      
+      HashMap<String, Object> expectedPayload = new HashMap<String, Object>();
+      
+      expectedPayload.put("properties", properties);
+      
+      assertEquals(actual, json.toJson(expectedPayload));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/UpdateServerRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/UpdateServerRequestBinderTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/UpdateServerRequestBinderTest.java
new file mode 100644
index 0000000..e1c8102
--- /dev/null
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/server/UpdateServerRequestBinderTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jclouds.profitbricks.rest.binder.server;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.HashMap;
+import org.apache.jclouds.profitbricks.rest.domain.Server;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "UpdateServerRequestBinderTest")
+public class UpdateServerRequestBinderTest {
+
+   @Test
+   public void testCreatePayload() {
+      Injector injector = Guice.createInjector(new GsonModule());
+      UpdateServerRequestBinder binder = injector.getInstance(UpdateServerRequestBinder.class);
+ 
+      Server.Request.UpdatePayload payload = Server.Request.updatingBuilder()
+              .id("server-id")
+              .cores(8)
+              .ram(8 * 1024)
+              .name("apache-node")
+              .dataCenterId("datacenter-id")
+              .build();
+
+      String actual = binder.createPayload(payload);
+
+      HttpRequest request = binder.createRequest(
+              HttpRequest.builder().method("PATCH").endpoint("http://test.com").build(), 
+              actual
+      );
+      
+      assertEquals(request.getEndpoint().getPath(), "/rest/datacenters/datacenter-id/servers/server-id");
+      assertNotNull(actual, "Binder returned null payload");
+
+      Json json = injector.getInstance(Json.class);
+      
+      HashMap<String, Object> expectedPayload = new HashMap<String, Object>();
+      expectedPayload.put("cores", 8);
+      expectedPayload.put("name", "apache-node");
+      expectedPayload.put("ram", 8192);
+
+      assertEquals(actual, json.toJson(expectedPayload));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateSnapshotRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateSnapshotRequestBinderTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateSnapshotRequestBinderTest.java
new file mode 100644
index 0000000..2c1566f
--- /dev/null
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateSnapshotRequestBinderTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.profitbricks.rest.binder.volume;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import javax.ws.rs.core.MediaType;
+import org.apache.jclouds.profitbricks.rest.domain.Volume;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.io.payloads.UrlEncodedFormPayload;
+import org.jclouds.json.config.GsonModule;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "CreateSnapshotRequestBinderTest")
+public class CreateSnapshotRequestBinderTest {
+
+   @Test
+   public void testCreatePayload() {
+      
+      Injector injector = Guice.createInjector(new GsonModule());
+      CreateSnapshotRequestBinder binder = injector.getInstance(CreateSnapshotRequestBinder.class);
+            
+      Volume.Request.CreateSnapshotPayload payload = Volume.Request.createSnapshotBuilder()
+            .dataCenterId("datacenter-id")
+            .volumeId("volume-id")
+            .name("test-snapshot")
+            .description("snapshot desc...")
+            .build();
+
+      HttpRequest request = binder.createRequest(
+              HttpRequest.builder().method("POST").endpoint("http://test.com").build(), 
+              binder.createPayload(payload)
+      );
+      
+      Multimap<String, String> expectedPayload = HashMultimap.create();
+      
+      expectedPayload.put("name", "test-snapshot");
+      expectedPayload.put("description", "snapshot desc...");
+            
+      assertEquals(request.getEndpoint().getPath(), "/rest/datacenters/datacenter-id/volumes/volume-id/create-snapshot");
+      assertEquals(request.getPayload().getContentMetadata().getContentType(), MediaType.APPLICATION_FORM_URLENCODED);
+      assertEquals(request.getPayload().getRawContent(), "&" + (new UrlEncodedFormPayload(expectedPayload)).getRawContent());
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateVolumeRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateVolumeRequestBinderTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateVolumeRequestBinderTest.java
new file mode 100644
index 0000000..f49ab75
--- /dev/null
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/CreateVolumeRequestBinderTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.profitbricks.rest.binder.volume;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.jclouds.profitbricks.rest.domain.LicenceType;
+import org.apache.jclouds.profitbricks.rest.domain.Volume;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "CreateVolumeRequestBinderTest")
+public class CreateVolumeRequestBinderTest {
+
+   @Test
+   public void testCreatePayload() {
+      
+      Injector injector = Guice.createInjector(new GsonModule());
+      CreateVolumeRequestBinder binder = injector.getInstance(CreateVolumeRequestBinder.class);
+            
+      Volume.Request.CreatePayload payload = Volume.Request.creatingBuilder()
+              .dataCenterId("datacenter-id")
+              .name("jclouds-volume")
+              .size(3)
+              .licenceType(LicenceType.LINUX)
+              .build();
+
+      String actual = binder.createPayload(payload);
+      
+      HttpRequest request = binder.createRequest(
+              HttpRequest.builder().method("POST").endpoint("http://test.com").build(), 
+              actual
+      );
+      
+      assertEquals(request.getEndpoint().getPath(), "/rest/datacenters/datacenter-id/volumes");
+      assertNotNull(actual, "Binder returned null payload");
+
+      Json json = injector.getInstance(Json.class);
+      
+      Map<String, Object> properties = new HashMap<String, Object>();
+      
+      properties.put("size", 3);
+      properties.put("licenceType", "LINUX");
+      properties.put("name", "jclouds-volume");
+      
+      HashMap<String, Object> expectedPayload = new HashMap<String, Object>();
+      
+      expectedPayload.put("properties", properties);
+      
+      assertEquals(actual, json.toJson(expectedPayload));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/RestoreSnapshotRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/RestoreSnapshotRequestBinderTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/RestoreSnapshotRequestBinderTest.java
new file mode 100644
index 0000000..f19daa4
--- /dev/null
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/RestoreSnapshotRequestBinderTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.profitbricks.rest.binder.volume;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import javax.ws.rs.core.MediaType;
+import org.apache.jclouds.profitbricks.rest.domain.Volume;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.config.GsonModule;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "RestoreSnapshotRequestBinderTest")
+public class RestoreSnapshotRequestBinderTest {
+
+   @Test
+   public void testRestorePayload() {
+      
+      Injector injector = Guice.createInjector(new GsonModule());
+      RestoreSnapshotRequestBinder binder = injector.getInstance(RestoreSnapshotRequestBinder.class);
+            
+      Volume.Request.RestoreSnapshotPayload payload = Volume.Request.restoreSnapshotBuilder()
+            .dataCenterId("datacenter-id")
+            .volumeId("volume-id")
+            .snapshotId("snapshot-id")
+            .build();
+
+      HttpRequest request = binder.createRequest(
+              HttpRequest.builder().method("POST").endpoint("http://test.com").build(), 
+              binder.createPayload(payload)
+      );
+      
+      assertEquals(request.getEndpoint().getPath(), "/rest/datacenters/datacenter-id/volumes/volume-id/restore-snapshot");
+      assertEquals(request.getPayload().getContentMetadata().getContentType(), MediaType.APPLICATION_FORM_URLENCODED);
+      assertEquals(request.getPayload().getRawContent(), "&snapshotId=snapshot-id");
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/UpdateVolumeRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/UpdateVolumeRequestBinderTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/UpdateVolumeRequestBinderTest.java
new file mode 100644
index 0000000..f0e6694
--- /dev/null
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/binder/volume/UpdateVolumeRequestBinderTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.profitbricks.rest.binder.volume;
+
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.Map;
+import org.apache.jclouds.profitbricks.rest.domain.Volume;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "UpdateVolumeRequestBinderTest")
+public class UpdateVolumeRequestBinderTest {
+
+   @Test
+   public void testCreatePayload() {
+      
+      Injector injector = Guice.createInjector(new GsonModule());
+      UpdateVolumeRequestBinder binder = injector.getInstance(UpdateVolumeRequestBinder.class);
+            
+      Volume.Request.UpdatePayload payload = Volume.Request.updatingBuilder()
+              .dataCenterId("datacenter-id")
+              .id("volume-id")
+              .name("apache-volume")
+              .build();
+
+      String actual = binder.createPayload(payload);
+      
+      HttpRequest request = binder.createRequest(
+              HttpRequest.builder().method("PATCH").endpoint("http://test.com").build(), 
+              actual
+      );
+      
+      assertEquals(request.getEndpoint().getPath(), "/rest/datacenters/datacenter-id/volumes/volume-id");
+      assertNotNull(actual, "Binder returned null payload");
+
+      Json json = injector.getInstance(Json.class);
+      String expectedJson = json.toJson(json.fromJson(expectedPayload, new TypeToken<Map<String, Object>>(){}.getType()));
+      
+      assertEquals(actual, expectedJson);
+   }
+
+   private final String expectedPayload = "{\"name\":\"apache-volume\"}";
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/DataCenterApiMockTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/DataCenterApiMockTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/DataCenterApiMockTest.java
index 022eb92..f4d2e11 100644
--- a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/DataCenterApiMockTest.java
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/DataCenterApiMockTest.java
@@ -24,8 +24,6 @@ import org.apache.jclouds.profitbricks.rest.domain.options.DepthOptions;
 import org.apache.jclouds.profitbricks.rest.internal.BaseProfitBricksApiMockTest;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
 import org.testng.annotations.Test;
 
 @Test(groups = "unit", testName = "DataCenterApiMockTest", singleThreaded = true)
@@ -46,15 +44,6 @@ public class DataCenterApiMockTest extends BaseProfitBricksApiMockTest {
       assertEquals(server.getRequestCount(), 1);
       assertSent(server, "GET", "/datacenters");
    }
-   
-   @Test
-   public void testGetListWith404() throws InterruptedException {
-      server.enqueue(response404());
-      List<DataCenter> list = dataCenterApi().list(new DepthOptions().depth(1));
-      assertTrue(list.isEmpty());
-      assertEquals(server.getRequestCount(), 1);
-      assertSent(server, "GET", "/datacenters?depth=1");
-   }
     
    @Test
    public void testGetDataCenter() throws InterruptedException {
@@ -73,17 +62,6 @@ public class DataCenterApiMockTest extends BaseProfitBricksApiMockTest {
       assertSent(server, "GET", "/datacenters/some-id");
    }
    
-   public void testGetDataCenterWith404() throws InterruptedException {
-      server.enqueue(response404());
-
-      DataCenter dataCenter = dataCenterApi().getDataCenter("some-id");
-      
-      assertNull(dataCenter);
-
-      assertEquals(server.getRequestCount(), 1);
-      assertSent(server, "GET", "/datacenters/some-id");
-   }
-   
    @Test
    public void testCreate() throws InterruptedException {
       server.enqueue(
@@ -123,16 +101,6 @@ public class DataCenterApiMockTest extends BaseProfitBricksApiMockTest {
    }
    
    @Test
-   public void testDeleteWith404() throws InterruptedException {
-      server.enqueue(response404());
-
-      dataCenterApi().delete("some-id");
-      
-      assertEquals(server.getRequestCount(), 1);
-      assertSent(server, "DELETE", "/datacenters/some-id");
-   }
-   
-   @Test
    public void testDepth() throws InterruptedException {
       
       for (int i = 1; i <= 5; ++i) {

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9a8b346c/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/ServerApiLiveTest.java
----------------------------------------------------------------------
diff --git a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/ServerApiLiveTest.java b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/ServerApiLiveTest.java
index cb0a6b4..949588b 100644
--- a/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/ServerApiLiveTest.java
+++ b/profitbricks-rest/src/test/java/org/apache/jclouds/profitbricks/rest/features/ServerApiLiveTest.java
@@ -24,6 +24,7 @@ import org.apache.jclouds.profitbricks.rest.domain.Server;
 import org.apache.jclouds.profitbricks.rest.domain.State;
 import org.apache.jclouds.profitbricks.rest.domain.Volume;
 import org.apache.jclouds.profitbricks.rest.ids.ServerRef;
+import org.apache.jclouds.profitbricks.rest.ids.VolumeRef;
 import org.apache.jclouds.profitbricks.rest.internal.BaseProfitBricksLiveTest;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -142,8 +143,41 @@ public class ServerApiLiveTest extends BaseProfitBricksLiveTest {
       List<Volume> volumes = serverApi().listAttachedVolumes(testServer.dataCenterId(), testServer.id());
       assertTrue(volumes.isEmpty());
    }
-      
+   
    @Test(dependsOnMethods = "testListVolumes")
+   public void testAttachVolume() {
+      
+      Volume volume = createVolume(dataCenter);
+      
+      assertVolumeAvailable(VolumeRef.create(dataCenter.id(), volume.id()));
+      
+      attachedVolume = serverApi().attachVolume(
+         Server.Request.attachVolumeBuilder()
+            .dataCenterId(testServer.dataCenterId())
+            .serverId(testServer.id())
+            .volumeId(volume.id())
+            .build()
+      );
+      
+      assertVolumeAttached(testServer, volume.id());
+      
+      List<Volume> volumes = serverApi().listAttachedVolumes(testServer.dataCenterId(), testServer.id());
+      assertEquals(volumes.size(), 1);
+   }
+   
+   @Test(dependsOnMethods = "testAttachVolume")
+   public void testGetVolume() {
+      Volume volume = serverApi().getVolume(testServer.dataCenterId(), testServer.id(), attachedVolume.id());
+      assertEquals(volume.id(), attachedVolume.id());
+   }
+   
+   @Test(dependsOnMethods = "testGetVolume")
+   public void testDetachVolume() {
+      serverApi().detachVolume(testServer.dataCenterId(), testServer.id(), attachedVolume.id());
+      assertVolumeDetached(testServer, attachedVolume.id());
+   }   
+   
+   @Test(dependsOnMethods = "testDetachVolume")
    public void testListCdroms() {
       List<Image> images = serverApi().listAttachedCdroms(testServer.dataCenterId(), testServer.id());
       assertTrue(images.isEmpty());
@@ -211,5 +245,30 @@ public class ServerApiLiveTest extends BaseProfitBricksLiveTest {
          }
       }, complexId(server.dataCenterId(), server.id(), cdRomId));
    }
+   
+   private void assertVolumeAttached(Server server, String volumeId) {
+      assertRandom(new Predicate<String>() {
+         @Override
+         public boolean apply(String args) {
+            String[] params = args.split(",");
+            Volume volume = serverApi().getVolume(params[0], params[1], params[2]);
+            
+            if (volume == null || volume.metadata() == null)
+               return false;
+            
+            return volume.metadata().state() == State.AVAILABLE;
+         }
+      }, complexId(server.dataCenterId(), server.id(), volumeId));
+   }
+
+   private void assertVolumeDetached(Server server, String volumeId) {
+      assertRandom(new Predicate<String>() {
+         @Override
+         public boolean apply(String args) {
+            String[] params = args.split(",");
+            return serverApi().getVolume(params[0], params[1], params[2]) == null;
+         }
+      }, complexId(server.dataCenterId(), server.id(), volumeId));
+   }
       
 }