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/19 16:33:35 UTC
[25/35] jclouds git commit: JCLOUDS-702: JClouds ProfitBricks
provider - ComputeServiceAdapter
JCLOUDS-702: JClouds ProfitBricks provider - ComputeServiceAdapter
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/ed247e7d
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/ed247e7d
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/ed247e7d
Branch: refs/heads/master
Commit: ed247e7dea753daa94c8f0b228804f6947b694e7
Parents: cd91e00
Author: Reijhanniel Jearl Campos <rj...@toro.io>
Authored: Mon Jun 29 11:13:21 2015 +0800
Committer: Ignasi Barrera <na...@apache.org>
Committed: Tue Jun 30 12:42:51 2015 +0200
----------------------------------------------------------------------
providers/profitbricks/README.md | 63 +++
providers/profitbricks/pom.xml | 1 -
.../profitbricks/ProfitBricksApiMetadata.java | 7 +-
.../ProfitBricksProviderMetadata.java | 25 +-
.../CreateDataCenterRequestBinder.java | 2 +-
.../CreateLoadBalancerRequestBinder.java | 3 +-
.../DeregisterLoadBalancerRequestBinder.java | 3 +-
.../RegisterLoadBalancerRequestBinder.java | 3 +-
.../snapshot/CreateSnapshotRequestBinder.java | 12 +-
.../snapshot/RollbackSnapshotRequestBinder.java | 30 +-
.../snapshot/UpdateSnapshotRequestBinder.java | 32 +-
.../ProfitBricksComputeServiceAdapter.java | 488 +++++++++++++++++++
.../compute/concurrent/ProvisioningJob.java | 62 +++
.../compute/concurrent/ProvisioningManager.java | 88 ++++
...ProfitBricksComputeServiceContextModule.java | 147 ++++++
.../compute/function/DataCenterToLocation.java | 54 ++
.../compute/function/LocationToLocation.java | 47 ++
.../compute/function/ProvisionableToImage.java | 215 ++++++++
.../compute/function/ServerToNodeMetadata.java | 180 +++++++
.../compute/function/StorageToVolume.java | 47 ++
.../ProvisioningStatusPollingPredicate.java | 32 +-
.../config/ProfitBricksComputeProperties.java | 31 ++
.../jclouds/profitbricks/domain/Firewall.java | 6 +-
.../org/jclouds/profitbricks/domain/Image.java | 129 +----
.../profitbricks/domain/LoadBalancer.java | 46 +-
.../jclouds/profitbricks/domain/Location.java | 20 +-
.../org/jclouds/profitbricks/domain/Nic.java | 8 +-
.../org/jclouds/profitbricks/domain/Server.java | 116 ++---
.../jclouds/profitbricks/domain/Snapshot.java | 429 ++++++----------
.../jclouds/profitbricks/domain/Storage.java | 13 +-
.../domain/internal/HotPluggable.java | 102 ++++
.../domain/internal/Provisionable.java | 67 +++
.../domain/internal/ServerCommonProperties.java | 22 +-
.../profitbricks/features/LoadBalancerApi.java | 2 +-
.../firewall/FirewallListResponseHandler.java | 6 +-
.../ipblock/IpBlockListResponseHandler.java | 6 +-
.../BaseLoadBalancerResponseHandler.java | 33 +-
.../LoadBalancerListResponseHandler.java | 25 +-
.../LoadBalancerResponseHandler.java | 13 +-
.../server/BaseServerResponseHandler.java | 40 +-
.../server/ServerInfoResponseHandler.java | 7 +-
.../server/ServerListResponseHandler.java | 24 +-
.../snapshot/BaseSnapshotResponseHandler.java | 48 +-
.../snapshot/SnapshotListResponseHandler.java | 12 +-
.../snapshot/SnapshotResponseHandler.java | 11 +-
.../storage/BaseStorageResponseHandler.java | 12 +-
.../storage/StorageInfoResponseHandler.java | 7 +-
.../storage/StorageListResponseHandler.java | 12 +-
.../jclouds/profitbricks/util/Passwords.java | 64 +++
.../CreateSnapshotRequestBinderTest.java | 14 +-
.../RollbackSnapshotRequestBinderTest.java | 16 +-
...ofitBricksComputeServiceAdapterLiveTest.java | 74 +++
.../ProfitBricksTemplateBuilderLiveTest.java | 37 ++
.../concurrent/ProvisioningManagerTest.java | 118 +++++
.../function/DataCenterToLocationTest.java | 77 +++
.../function/LocationToLocationTest.java | 62 +++
.../function/ProvisionableToImageTest.java | 260 ++++++++++
.../function/ServerToNodeMetadataTest.java | 184 +++++++
.../compute/function/StorageToVolumeTest.java | 61 +++
.../ProvisioningStatusPollingPredicateTest.java | 6 +-
.../features/DrivesApiLiveTest.java | 9 +-
.../features/FirewallApiLiveTest.java | 2 +-
.../features/IpBlockApiLiveTest.java | 2 +-
.../features/IpBlockApiMockTest.java | 4 +-
.../features/LoadbalancerApiLiveTest.java | 2 +-
.../features/LoadbalancerApiMockTest.java | 2 +-
.../features/SnapshotApiLiveTest.java | 32 +-
.../features/SnapshotApiMockTest.java | 174 +++----
.../DataCenterInfoResponseHandlerTest.java | 22 +-
.../image/ImageListResponseHandlerTest.java | 6 +-
.../LoadBalancerListResponseHandlerTest.java | 75 ++-
.../LoadBalancerResponseHandlerTest.java | 28 +-
.../server/ServerInfoResponseHandlerTest.java | 20 +-
.../server/ServerListResponseHandlerTest.java | 32 +-
.../SnapshotListResponseHandlerTest.java | 89 ++--
.../snapshot/SnapshotResponseHandlerTest.java | 49 +-
.../storage/StorageInfoResponseHandlerTest.java | 13 +-
.../storage/StorageListResponseHandlerTest.java | 17 +-
.../profitbricks/util/PasswordsTest.java | 53 ++
.../resources/datacenter/datacenter-deleted.xml | 14 +-
.../datacenter/datacenter-not-found.xml | 28 +-
.../datacenter/datacenter-state-inprocess.xml | 10 +-
.../resources/datacenter/datacenter-state.xml | 10 +-
.../src/test/resources/drives/drives-add.xml | 18 +-
.../src/test/resources/drives/drives-remove.xml | 18 +-
.../resources/firewall/firewall-activate.xml | 18 +-
.../resources/firewall/firewall-addtonic.xml | 40 +-
.../resources/firewall/firewall-deactivate.xml | 18 +-
.../test/resources/firewall/firewall-delete.xml | 18 +-
.../test/resources/firewall/firewall-remove.xml | 18 +-
.../src/test/resources/firewall/firewall.xml | 40 +-
.../test/resources/ipblock/ipblock-addtonic.xml | 18 +-
.../test/resources/ipblock/ipblock-release.xml | 14 +-
.../resources/ipblock/ipblock-removefromnic.xml | 18 +-
.../test/resources/ipblock/ipblock-reserve.xml | 20 +-
.../src/test/resources/ipblock/ipblock.xml | 30 +-
.../loadbalancer/loadbalancer-create.xml | 4 +-
.../loadbalancer/loadbalancer-delete.xml | 18 +-
.../loadbalancer/loadbalancer-deregister.xml | 8 +-
.../loadbalancer/loadbalancer-register.xml | 8 +-
.../loadbalancer/loadbalancer-update.xml | 4 +-
.../resources/loadbalancer/loadbalancer.xml | 2 +-
.../resources/loadbalancer/loadbalancers.xml | 4 +-
.../src/test/resources/server/server.xml | 4 +-
.../src/test/resources/snapshot/snapshots.xml | 38 +-
105 files changed, 3643 insertions(+), 1189 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/README.md
----------------------------------------------------------------------
diff --git a/providers/profitbricks/README.md b/providers/profitbricks/README.md
new file mode 100644
index 0000000..09c367a
--- /dev/null
+++ b/providers/profitbricks/README.md
@@ -0,0 +1,63 @@
+# jclouds ProfitBricks
+
+## Terms
+Like any cloud provider, ProfitBricks has its own set of terms in cloud computing. To abstract this into jclouds' Compute interface, these terms were associated:
+
+- Node - composite instance of `Server` and `Storage`
+- Image - both *user-uploaded* and *provided* `Images`; and `Snapshots`
+- Location - `DataCenters` and `Region` (Las Vegas, Frankfurt, etc.)
+- Hardware - number of cores, RAM size and storage size
+
+## Getting Started
+
+Assuming that there's **atleast one** datacenter existing in your account, the provider needs only an *identity* (your ProfitBricks email), and *credentials* (password) to provision a `Node`, by using a ProfitBricks-provided ubuntu-12.04 image as a template.
+
+```java
+ComputeService compute = ContextBuilder.newBuilder( "profitbricks" )
+ .credentials( "profitbricks email", "password" )
+ .buildView( ComputeServiceContext.class )
+ .getComputeService();
+```
+
+
+This works well; however, we won't be able to use jclouds' ability to execute *scripts* on a remote node. This is because, ProfitBricks' default images require users to change passwords upon first log in.
+
+To enable jclouds to execute script, we need to use a custom image. The easiest way to do this is via ProfitBricks snapshot:
+
+- Go to your [DCD](https://my.profitbricks.com/dashboard/).
+- Provision a server + storage, and connect it to the internet. Upon success, you will receive an email containing the credentials needed to login to your server.
+- Login to your server, and change the password, as requested.
+
+```
+~ ssh root@<remote-ip>
+...
+Changing password for root.
+(current) UNIX password:
+Enter new UNIX password:
+Retype new UNIX password:
+~ root@ubuntu:~# exit
+
+```
+
+- Go back to the DCD, and *make a snapshot* of the storage. Put a descriptive name.
+- Configure jclouds to use this *snapshot*.
+
+```java
+Template template = compute.templateBuilder()
+ .imageNameMatches( "<ideally-unique-snapshot-name>" )
+ .options( compute.templateOptions()
+ .overrideLoginUser( "root" ) // unless you changed the user
+ .overrideLoginPassword( "<changed-password>" ))
+ // more options, as you need
+ .build();
+
+compute.createNodesInGroup( "cluster1", 1, template );
+```
+> If no `locationId` is specified in the template, jclouds will look for a `DataCenter` that is of same scope as the `Image`.
+
+
+## Limitations
+
+- There's no direct way of specifying arbitrary number of cores, RAM size, and storage size via the compute interface, at least until after [JCLOUDS-482](https://issues.apache.org/jira/browse/JCLOUDS-482) is resolved. The adapter uses a predefined list hardware profiles instead.
+
+> Take note that these features are still accessible by *unwraping* the ProfitBricks API, but this'll reduce portability of your code. See [Concepts](https://jclouds.apache.org/start/concepts/).
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/pom.xml
----------------------------------------------------------------------
diff --git a/providers/profitbricks/pom.xml b/providers/profitbricks/pom.xml
index 84f49b4..9311e48 100644
--- a/providers/profitbricks/pom.xml
+++ b/providers/profitbricks/pom.xml
@@ -36,7 +36,6 @@
<test.profitbricks.identity>FIXME</test.profitbricks.identity>
<test.profitbricks.credential>FIXME</test.profitbricks.credential>
<test.profitbricks.api-version>1.3</test.profitbricks.api-version>
- <test.profitbricks.template />
<jclouds.osgi.export>org.jclouds.profitbricks*;version="${project.version}"</jclouds.osgi.export>
<jclouds.osgi.import>
org.jclouds.labs*;version="${project.version}",
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksApiMetadata.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksApiMetadata.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksApiMetadata.java
index 205b246..2973f4a 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksApiMetadata.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksApiMetadata.java
@@ -19,8 +19,10 @@ package org.jclouds.profitbricks;
import java.net.URI;
import java.util.Properties;
+import org.jclouds.profitbricks.compute.config.ProfitBricksComputeServiceContextModule;
import org.jclouds.profitbricks.config.ProfitBricksHttpApiModule;
import org.jclouds.apis.ApiMetadata;
+import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.profitbricks.config.ProfitBricksHttpApiModule.ProfitBricksHttpCommandExecutorServiceModule;
import org.jclouds.rest.internal.BaseHttpApiMetadata;
@@ -60,11 +62,12 @@ public class ProfitBricksApiMetadata extends BaseHttpApiMetadata<ProfitBricksApi
.documentation(URI.create("https://www.profitbricks.com/sites/default/files/profitbricks_api_1_3.pdf"))
.defaultEndpoint("https://api.profitbricks.com/1.3")
.version("1.3")
- // .view(ComputeServiceContext.class)
+ .view(ComputeServiceContext.class)
.defaultProperties(ProfitBricksApiMetadata.defaultProperties())
.defaultModules(ImmutableSet.<Class<? extends Module>>of(
ProfitBricksHttpApiModule.class,
- ProfitBricksHttpCommandExecutorServiceModule.class
+ ProfitBricksHttpCommandExecutorServiceModule.class,
+ ProfitBricksComputeServiceContextModule.class
));
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksProviderMetadata.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksProviderMetadata.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksProviderMetadata.java
index 9ecfbc1..ed6c556 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksProviderMetadata.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/ProfitBricksProviderMetadata.java
@@ -16,8 +16,17 @@
*/
package org.jclouds.profitbricks;
+import static org.jclouds.Constants.PROPERTY_CONNECTION_TIMEOUT;
+import static org.jclouds.Constants.PROPERTY_SO_TIMEOUT;
+import static org.jclouds.profitbricks.config.ProfitBricksComputeProperties.POLL_PERIOD;
+import static org.jclouds.profitbricks.config.ProfitBricksComputeProperties.POLL_MAX_PERIOD;
+import static org.jclouds.profitbricks.config.ProfitBricksComputeProperties.POLL_TIMEOUT;
+
import com.google.auto.service.AutoService;
+
import java.net.URI;
+import java.util.Properties;
+
import org.jclouds.providers.ProviderMetadata;
import org.jclouds.providers.internal.BaseProviderMetadata;
@@ -41,6 +50,19 @@ public class ProfitBricksProviderMetadata extends BaseProviderMetadata {
return new Builder();
}
+ public static Properties defaultProperties() {
+ Properties properties = ProfitBricksApiMetadata.defaultProperties();
+ long defaultTimeout = 60l * 60l; // 1 hour
+ properties.put(POLL_TIMEOUT, defaultTimeout);
+ properties.put(POLL_PERIOD, 2l);
+ properties.put(POLL_MAX_PERIOD, 2l * 10l);
+
+ properties.put(PROPERTY_SO_TIMEOUT, 60000 * 5);
+ properties.put(PROPERTY_CONNECTION_TIMEOUT, 60000 * 5);
+
+ return properties;
+ }
+
public static class Builder extends BaseProviderMetadata.Builder {
protected Builder() {
@@ -49,7 +71,8 @@ public class ProfitBricksProviderMetadata extends BaseProviderMetadata {
.homepage(URI.create("http://www.profitbricks.com"))
.console(URI.create("https://my.profitbricks.com/dashboard/dcdr2/"))
.linkedServices("profitbricks")
- .apiMetadata(new ProfitBricksApiMetadata());
+ .apiMetadata(new ProfitBricksApiMetadata())
+ .defaultProperties(ProfitBricksProviderMetadata.defaultProperties());
}
@Override
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinder.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinder.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinder.java
index 8a07b0a..1873f31 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinder.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinder.java
@@ -35,7 +35,7 @@ public class CreateDataCenterRequestBinder extends BaseProfitBricksRequestBinder
requestBuilder.append("<ws:createDataCenter>")
.append("<request>")
.append(format("<dataCenterName>%s</dataCenterName>", payload.name()))
- .append(format("<location>%s</location>", payload.location().value()))
+ .append(format("<location>%s</location>", payload.location().getId()))
.append("</request>")
.append("</ws:createDataCenter>");
return requestBuilder.toString();
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/CreateLoadBalancerRequestBinder.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/CreateLoadBalancerRequestBinder.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/CreateLoadBalancerRequestBinder.java
index 23e121e..90eb93f 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/CreateLoadBalancerRequestBinder.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/CreateLoadBalancerRequestBinder.java
@@ -39,9 +39,8 @@ public class CreateLoadBalancerRequestBinder extends BaseProfitBricksRequestBind
.append(format("<loadBalancerAlgorithm>%s</loadBalancerAlgorithm>", payload.loadBalancerAlgorithm()))
.append(format("<ip>%s</ip>", payload.ip()))
.append(format("<lanId>%s</lanId>", payload.lanId()));
- for (String serverId : payload.serverIds()) {
+ for (String serverId : payload.serverIds())
requestBuilder.append(format("<serverIds>%s</serverIds>", serverId));
- }
requestBuilder
.append("</request>")
.append("</ws:createLoadBalancer>");
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/DeregisterLoadBalancerRequestBinder.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/DeregisterLoadBalancerRequestBinder.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/DeregisterLoadBalancerRequestBinder.java
index 92f2868..ba237c4 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/DeregisterLoadBalancerRequestBinder.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/DeregisterLoadBalancerRequestBinder.java
@@ -33,9 +33,8 @@ public class DeregisterLoadBalancerRequestBinder extends BaseProfitBricksRequest
protected String createPayload(LoadBalancer.Request.DeregisterPayload payload) {
requestBuilder.append("<ws:deregisterServersOnLoadBalancer>")
.append("<request>");
- for (String s : payload.serverIds()) {
+ for (String s : payload.serverIds())
requestBuilder.append(format("<serverIds>%s</serverIds>", s));
- }
requestBuilder.append(format("<loadBalancerId>%s</loadBalancerId>", payload.id()))
.append("</request>")
.append("</ws:deregisterServersOnLoadBalancer>");
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/RegisterLoadBalancerRequestBinder.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/RegisterLoadBalancerRequestBinder.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/RegisterLoadBalancerRequestBinder.java
index 2e437f0..21f1d84 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/RegisterLoadBalancerRequestBinder.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/loadbalancer/RegisterLoadBalancerRequestBinder.java
@@ -35,9 +35,8 @@ public class RegisterLoadBalancerRequestBinder extends BaseProfitBricksRequestBi
.append("<ws:registerServersOnLoadBalancer>").append("<request>")
.append(format("<loadBalancerId>%s</loadBalancerId>", payload.id()));
- for (String s : payload.serverIds()) {
+ for (String s : payload.serverIds())
requestBuilder.append(format("<serverIds>%s</serverIds>", s));
- }
requestBuilder
.append("</request>")
.append("</ws:registerServersOnLoadBalancer>");
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/CreateSnapshotRequestBinder.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/CreateSnapshotRequestBinder.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/CreateSnapshotRequestBinder.java
index 213a3a8..5ec4644 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/CreateSnapshotRequestBinder.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/CreateSnapshotRequestBinder.java
@@ -33,12 +33,12 @@ public class CreateSnapshotRequestBinder extends BaseProfitBricksRequestBinder<S
@Override
protected String createPayload(Snapshot.Request.CreatePayload payload) {
requestBuilder.append("<ws:createSnapshot>")
- .append("<request>")
- .append(format("<storageId>%s</storageId>", payload.storageId()))
- .append(formatIfNotEmpty("<description>%s</description>", payload.description()))
- .append(formatIfNotEmpty("<snapshotName>%s</snapshotName>", payload.name()))
- .append("</request>")
- .append("</ws:createSnapshot>");
+ .append("<request>")
+ .append(format("<storageId>%s</storageId>", payload.storageId()))
+ .append(formatIfNotEmpty("<description>%s</description>", payload.description()))
+ .append(formatIfNotEmpty("<snapshotName>%s</snapshotName>", payload.name()))
+ .append("</request>")
+ .append("</ws:createSnapshot>");
return requestBuilder.toString();
}
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/RollbackSnapshotRequestBinder.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/RollbackSnapshotRequestBinder.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/RollbackSnapshotRequestBinder.java
index a9997cb..5099324 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/RollbackSnapshotRequestBinder.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/RollbackSnapshotRequestBinder.java
@@ -23,21 +23,21 @@ import static java.lang.String.format;
public class RollbackSnapshotRequestBinder extends BaseProfitBricksRequestBinder<Snapshot.Request.RollbackPayload> {
- protected final StringBuilder requestBuilder;
+ protected final StringBuilder requestBuilder;
- protected RollbackSnapshotRequestBinder() {
- super("snapshot");
- this.requestBuilder = new StringBuilder(128);
- }
+ protected RollbackSnapshotRequestBinder() {
+ super("snapshot");
+ this.requestBuilder = new StringBuilder(128);
+ }
- @Override
- protected String createPayload(Snapshot.Request.RollbackPayload payload) {
- requestBuilder.append("<ws:rollbackSnapshot>")
- .append("<request>")
- .append(format("<snapshotId>%s</snapshotId>", payload.snapshotId()))
- .append(format("<storageId>%s</storageId>", payload.storageId()))
- .append("</request>")
- .append("</ws:rollbackSnapshot>");
- return requestBuilder.toString();
- }
+ @Override
+ protected String createPayload(Snapshot.Request.RollbackPayload payload) {
+ requestBuilder.append("<ws:rollbackSnapshot>")
+ .append("<request>")
+ .append(format("<snapshotId>%s</snapshotId>", payload.snapshotId()))
+ .append(format("<storageId>%s</storageId>", payload.storageId()))
+ .append("</request>")
+ .append("</ws:rollbackSnapshot>");
+ return requestBuilder.toString();
+ }
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/UpdateSnapshotRequestBinder.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/UpdateSnapshotRequestBinder.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/UpdateSnapshotRequestBinder.java
index e396715..df1b7cd 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/UpdateSnapshotRequestBinder.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/binder/snapshot/UpdateSnapshotRequestBinder.java
@@ -32,22 +32,22 @@ public class UpdateSnapshotRequestBinder extends BaseProfitBricksRequestBinder<S
@Override
protected String createPayload(Snapshot.Request.UpdatePayload payload) {
requestBuilder.append("<ws:updateSnapshot>")
- .append("<request>")
- .append(format("<snapshotId>%s</snapshotId>", payload.snapshotId()))
- .append(format("<description>%s</description>", payload.description()))
- .append(format("<snapshotName>%s</snapshotName>", payload.name()))
- .append(formatIfNotEmpty("<bootable>%s</bootable>", payload.bootable()))
- .append(formatIfNotEmpty("<osType>%s</osType>", payload.osType()))
- .append(formatIfNotEmpty("<cpuHotPlug>%s</cpuHotPlug>", payload.cpuHotplug()))
- .append(formatIfNotEmpty("<cpuHotUnPlug>%s</cpuHotUnPlug>", payload.cpuHotunplug()))
- .append(formatIfNotEmpty("<ramHotPlug>%s</ramHotPlug>", payload.ramHotplug()))
- .append(formatIfNotEmpty("<ramHotUnPlug>%s</ramHotUnPlug>", payload.ramHotunplug()))
- .append(formatIfNotEmpty("<nicHotPlug>%s</nicHotPlug>", payload.nicHotplug()))
- .append(formatIfNotEmpty("<nicHotUnPlug>%s</nicHotUnPlug>", payload.nicHotunplug()))
- .append(formatIfNotEmpty("<discVirtioHotPlug>%s</discVirtioHotPlug>", payload.discVirtioHotplug()))
- .append(formatIfNotEmpty("<discVirtioHotUnPlug>%s</discVirtioHotUnPlug>", payload.discVirtioHotunplug()))
- .append("</request>")
- .append("</ws:updateSnapshot>");
+ .append("<request>")
+ .append(format("<snapshotId>%s</snapshotId>", payload.snapshotId()))
+ .append(format("<description>%s</description>", payload.description()))
+ .append(format("<snapshotName>%s</snapshotName>", payload.name()))
+ .append(formatIfNotEmpty("<bootable>%s</bootable>", payload.bootable()))
+ .append(formatIfNotEmpty("<osType>%s</osType>", payload.osType()))
+ .append(formatIfNotEmpty("<cpuHotPlug>%s</cpuHotPlug>", payload.isCpuHotPlug()))
+ .append(formatIfNotEmpty("<cpuHotUnPlug>%s</cpuHotUnPlug>", payload.isCpuHotUnPlug()))
+ .append(formatIfNotEmpty("<ramHotPlug>%s</ramHotPlug>", payload.isRamHotPlug()))
+ .append(formatIfNotEmpty("<ramHotUnPlug>%s</ramHotUnPlug>", payload.isRamHotUnPlug()))
+ .append(formatIfNotEmpty("<nicHotPlug>%s</nicHotPlug>", payload.isNicHotPlug()))
+ .append(formatIfNotEmpty("<nicHotUnPlug>%s</nicHotUnPlug>", payload.isNicHotUnPlug()))
+ .append(formatIfNotEmpty("<discVirtioHotPlug>%s</discVirtioHotPlug>", payload.isDiscVirtioHotPlug()))
+ .append(formatIfNotEmpty("<discVirtioHotUnPlug>%s</discVirtioHotUnPlug>", payload.isDiscVirtioHotUnPlug()))
+ .append("</request>")
+ .append("</ws:updateSnapshot>");
return requestBuilder.toString();
}
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/ProfitBricksComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/ProfitBricksComputeServiceAdapter.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/ProfitBricksComputeServiceAdapter.java
new file mode 100644
index 0000000..add3fb9
--- /dev/null
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/ProfitBricksComputeServiceAdapter.java
@@ -0,0 +1,488 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.profitbricks.compute;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.util.concurrent.Futures.allAsList;
+import static com.google.common.util.concurrent.Futures.getUnchecked;
+import static java.lang.String.format;
+import static org.jclouds.Constants.PROPERTY_USER_THREADS;
+import static org.jclouds.profitbricks.config.ProfitBricksComputeProperties.POLL_PREDICATE_DATACENTER;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.Volume;
+import org.jclouds.compute.domain.internal.VolumeImpl;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.util.ComputeServiceUtils;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.logging.Logger;
+import org.jclouds.profitbricks.ProfitBricksApi;
+import org.jclouds.profitbricks.domain.AvailabilityZone;
+import org.jclouds.profitbricks.domain.DataCenter;
+import org.jclouds.profitbricks.domain.Image;
+import org.jclouds.profitbricks.domain.Server;
+import org.jclouds.profitbricks.domain.Storage;
+import org.jclouds.profitbricks.features.DataCenterApi;
+import org.jclouds.profitbricks.features.ServerApi;
+import org.jclouds.profitbricks.compute.concurrent.ProvisioningJob;
+import org.jclouds.profitbricks.compute.concurrent.ProvisioningManager;
+import org.jclouds.profitbricks.domain.Snapshot;
+import org.jclouds.profitbricks.domain.internal.Provisionable;
+import org.jclouds.profitbricks.util.Passwords;
+import org.jclouds.rest.ResourceNotFoundException;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.inject.Inject;
+
+@Singleton
+public class ProfitBricksComputeServiceAdapter implements ComputeServiceAdapter<Server, Hardware, Provisionable, DataCenter> {
+
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+
+ private final ProfitBricksApi api;
+ private final Predicate<String> waitDcUntilAvailable;
+ private final ListeningExecutorService executorService;
+ private final ProvisioningJob.Factory jobFactory;
+ private final ProvisioningManager provisioningManager;
+
+ private static final Integer DEFAULT_LAN_ID = 1;
+
+ @Inject
+ ProfitBricksComputeServiceAdapter(ProfitBricksApi api,
+ @Named(POLL_PREDICATE_DATACENTER) Predicate<String> waitDcUntilAvailable,
+ @Named(PROPERTY_USER_THREADS) ListeningExecutorService executorService,
+ ProvisioningJob.Factory jobFactory,
+ ProvisioningManager provisioningManager) {
+ this.api = api;
+ this.waitDcUntilAvailable = waitDcUntilAvailable;
+ this.executorService = executorService;
+ this.jobFactory = jobFactory;
+ this.provisioningManager = provisioningManager;
+ }
+
+ @Override
+ public NodeAndInitialCredentials<Server> createNodeWithGroupEncodedIntoName(String group, String name, Template template) {
+ final String dataCenterId = template.getLocation().getId();
+ Hardware hardware = template.getHardware();
+
+ TemplateOptions options = template.getOptions();
+ final String loginUser = isNullOrEmpty(options.getLoginUser()) ? "root" : options.getLoginUser();
+ final String password = options.hasLoginPassword() ? options.getLoginPassword() : Passwords.generate();
+
+ final org.jclouds.compute.domain.Image image = template.getImage();
+
+ // provision all storages based on hardware
+ List<? extends Volume> volumes = hardware.getVolumes();
+ List<String> storageIds = Lists.newArrayListWithExpectedSize(volumes.size());
+
+ int i = 1;
+ for (final Volume volume : volumes)
+ try {
+ logger.trace("<< provisioning storage '%s'", volume);
+ final Storage.Request.CreatePayload request = Storage.Request.creatingBuilder()
+ .dataCenterId(dataCenterId)
+ // put image to first storage
+ .mountImageId(i == 1 ? image.getId() : "")
+ .imagePassword(password)
+ .name(format("%s-disk-%d", name, i++))
+ .size(volume.getSize())
+ .build();
+
+ String storageId = (String) provisioningManager.provision(jobFactory.create(dataCenterId, new Supplier<Object>() {
+
+ @Override
+ public Object get() {
+ return api.storageApi().createStorage(request);
+ }
+ }));
+
+ storageIds.add(storageId);
+ logger.trace(">> provisioning complete for storage. returned id='%s'", storageId);
+ } catch (Exception ex) {
+ if (i - 1 == 1) // if first storage (one with image) provisioning fails; stop method
+ throw Throwables.propagate(ex);
+ logger.warn(ex, ">> failed to provision storage. skipping..");
+ }
+
+ int lanId = DEFAULT_LAN_ID;
+ if (options.getNetworks() != null)
+ try {
+ String networkId = Iterables.get(options.getNetworks(), 0);
+ lanId = Integer.valueOf(networkId);
+ } catch (Exception ex) {
+ logger.warn("no valid network id found from options. using default id='%d'", DEFAULT_LAN_ID);
+ }
+
+ Double cores = ComputeServiceUtils.getCores(hardware);
+
+ // provision server and connect boot storage (first provisioned)
+ String serverId = null;
+ try {
+ String storageBootDeviceId = Iterables.get(storageIds, 0); // must have atleast 1
+ final Server.Request.CreatePayload serverRequest = Server.Request.creatingBuilder()
+ .dataCenterId(dataCenterId)
+ .name(name)
+ .bootFromStorageId(storageBootDeviceId)
+ .cores(cores.intValue())
+ .ram(hardware.getRam())
+ .availabilityZone(AvailabilityZone.AUTO)
+ .hasInternetAccess(true)
+ .lanId(lanId)
+ .build();
+ logger.trace("<< provisioning server '%s'", serverRequest);
+
+ serverId = (String) provisioningManager.provision(jobFactory.create(dataCenterId, new Supplier<Object>() {
+
+ @Override
+ public Object get() {
+ return api.serverApi().createServer(serverRequest);
+ }
+ }));
+ logger.trace(">> provisioning complete for server. returned id='%s'", serverId);
+
+ } catch (Exception ex) {
+ logger.error(ex, ">> failed to provision server. rollbacking..");
+ destroyStorages(storageIds, dataCenterId);
+ throw Throwables.propagate(ex);
+ }
+
+ // connect the rest of storages to server; delete if fails
+ final int storageCount = storageIds.size();
+ for (int j = 1; j < storageCount; j++) { // skip first; already connected
+ String storageId = storageIds.get(j);
+ try {
+ logger.trace("<< connecting storage '%s' to server '%s'", storageId, serverId);
+ final Storage.Request.ConnectPayload request = Storage.Request.connectingBuilder()
+ .storageId(storageId)
+ .serverId(serverId)
+ .build();
+
+ provisioningManager.provision(jobFactory.create(group, new Supplier<Object>() {
+
+ @Override
+ public Object get() {
+ return api.storageApi().connectStorageToServer(request);
+ }
+ }));
+
+ logger.trace(">> storage connected.");
+ } catch (Exception ex) {
+ // delete unconnected storage
+ logger.warn(ex, ">> failed to connect storage '%s'. deleting..", storageId);
+ destroyStorage(storageId, dataCenterId);
+ }
+ }
+
+ // Last paranoid check
+ waitDcUntilAvailable.apply(dataCenterId);
+
+ LoginCredentials serverCredentials = LoginCredentials.builder()
+ .user(loginUser)
+ .password(password)
+ .build();
+
+ Server server = getNode(serverId);
+
+ return new NodeAndInitialCredentials<Server>(server, serverId, serverCredentials);
+ }
+
+ @Override
+ public Iterable<Hardware> listHardwareProfiles() {
+ // Max [cores=48] [disk size per storage=2048GB] [ram=200704 MB]
+ List<Hardware> hardwares = Lists.newArrayList();
+ for (int core = 1; core <= 48; core++)
+ for (int ram : new int[]{1024, 2 * 1024, 4 * 1024, 8 * 1024,
+ 10 * 1024, 16 * 1024, 24 * 1024, 28 * 1024, 32 * 1024})
+ for (float size : new float[]{10, 20, 30, 50, 80, 100, 150, 200, 250, 500}) {
+ String id = String.format("cpu=%d,ram=%s,disk=%f", core, ram, size);
+ hardwares.add(new HardwareBuilder()
+ .ids(id)
+ .ram(ram)
+ .hypervisor("kvm")
+ .name(id)
+ .processor(new Processor(core, 1d))
+ .volume(new VolumeImpl(size, true, true))
+ .build());
+ }
+ return hardwares;
+ }
+
+ @Override
+ public Iterable<Provisionable> listImages() {
+ // fetch images..
+ ListenableFuture<List<Image>> images = executorService.submit(new Callable<List<Image>>() {
+
+ @Override
+ public List<Image> call() throws Exception {
+ logger.trace("<< fetching images..");
+ // Filter HDD types only, since JClouds doesn't have a concept of "CD-ROM" anyway
+ Iterable<Image> filteredImages = Iterables.filter(api.imageApi().getAllImages(), new Predicate<Image>() {
+
+ @Override
+ public boolean apply(Image image) {
+ return image.type() == Image.Type.HDD;
+ }
+ });
+ logger.trace(">> images fetched.");
+
+ return ImmutableList.copyOf(filteredImages);
+ }
+
+ });
+ // and snapshots at the same time
+ ListenableFuture<List<Snapshot>> snapshots = executorService.submit(new Callable<List<Snapshot>>() {
+
+ @Override
+ public List<Snapshot> call() throws Exception {
+ logger.trace("<< fetching snapshots");
+ List<Snapshot> remoteSnapshots = api.snapshotApi().getAllSnapshots();
+ logger.trace(">> snapshots feched.");
+
+ return remoteSnapshots;
+ }
+
+ });
+
+ return Iterables.concat(getUnchecked(images), getUnchecked(snapshots));
+ }
+
+ @Override
+ public Provisionable getImage(String id) {
+ // try search images
+ logger.trace("<< searching for image with id=%s", id);
+ Image image = api.imageApi().getImage(id);
+ if (image != null) {
+ logger.trace(">> found image [%s].", image.name());
+ return image;
+ }
+ // try search snapshots
+ logger.trace("<< not found from images. searching for snapshot with id=%s", id);
+ Snapshot snapshot = api.snapshotApi().getSnapshot(id);
+ if (snapshot != null) {
+ logger.trace(">> found snapshot [%s]", snapshot.name());
+ return snapshot;
+ }
+ throw new ResourceNotFoundException("No image/snapshot with id '" + id + "' was found");
+ }
+
+ @Override
+ public Iterable<DataCenter> listLocations() {
+ logger.trace("<< fetching datacenters..");
+ final DataCenterApi dcApi = api.dataCenterApi();
+
+ // Fetch all datacenters
+ ListenableFuture<List<DataCenter>> futures = allAsList(transform(dcApi.getAllDataCenters(),
+ new Function<DataCenter, ListenableFuture<DataCenter>>() {
+
+ @Override
+ public ListenableFuture<DataCenter> apply(final DataCenter input) {
+ // Fetch more details in parallel
+ return executorService.submit(new Callable<DataCenter>() {
+ @Override
+ public DataCenter call() throws Exception {
+ logger.trace("<< fetching datacenter with id [%s]", input.id());
+ return dcApi.getDataCenter(input.id());
+ }
+
+ });
+ }
+ }));
+
+ return getUnchecked(futures);
+ }
+
+ @Override
+ public Server getNode(String id) {
+ logger.trace("<< searching for server with id=%s", id);
+
+ Server server = api.serverApi().getServer(id);
+ if (server != null)
+ logger.trace(">> found server [%s]", server.name());
+ return server;
+ }
+
+ @Override
+ public void destroyNode(String nodeId) {
+ ServerApi serverApi = api.serverApi();
+ Server server = serverApi.getServer(nodeId);
+ if (server != null) {
+ String dataCenterId = server.dataCenter().id();
+ for (Storage storage : server.storages())
+ destroyStorage(storage.id(), dataCenterId);
+
+ try {
+ destroyServer(nodeId, dataCenterId);
+ } catch (Exception ex) {
+ logger.warn(ex, ">> failed to delete server with id=%s", nodeId);
+ }
+ }
+ }
+
+ @Override
+ public void rebootNode(final String id) {
+ // Fail pre-emptively if not found
+ final Server node = getRequiredNode(id);
+ final DataCenter dataCenter = node.dataCenter();
+ provisioningManager.provision(jobFactory.create(dataCenter.id(), new Supplier<Object>() {
+
+ @Override
+ public Object get() {
+ api.serverApi().resetServer(id);
+
+ return node;
+ }
+ }));
+ }
+
+ @Override
+ public void resumeNode(final String id) {
+ final Server node = getRequiredNode(id);
+ if (node.status() == Server.Status.RUNNING)
+ return;
+
+ final DataCenter dataCenter = node.dataCenter();
+ provisioningManager.provision(jobFactory.create(dataCenter.id(), new Supplier<Object>() {
+
+ @Override
+ public Object get() {
+ api.serverApi().startServer(id);
+
+ return node;
+ }
+ }));
+ }
+
+ @Override
+ public void suspendNode(final String id) {
+ final Server node = getRequiredNode(id);
+ // Intentionally didn't include SHUTDOWN (only achieved via UI; soft-shutdown).
+ // A SHUTOFF server is no longer billed, so we execute method for all other status
+ if (node.status() == Server.Status.SHUTOFF)
+ return;
+
+ final DataCenter dataCenter = node.dataCenter();
+ provisioningManager.provision(jobFactory.create(dataCenter.id(), new Supplier<Object>() {
+
+ @Override
+ public Object get() {
+ api.serverApi().stopServer(id);
+
+ return node;
+ }
+ }));
+ }
+
+ @Override
+ public Iterable<Server> listNodes() {
+ logger.trace(">> fetching all servers..");
+ List<Server> servers = api.serverApi().getAllServers();
+ logger.trace(">> servers fetched.");
+ return servers;
+ }
+
+ @Override
+ public Iterable<Server> listNodesByIds(final Iterable<String> ids) {
+ // Only fetch the requested nodes. Do it in parallel.
+ ListenableFuture<List<Server>> futures = allAsList(transform(ids,
+ new Function<String, ListenableFuture<Server>>() {
+
+ @Override
+ public ListenableFuture<Server> apply(final String input) {
+ return executorService.submit(new Callable<Server>() {
+
+ @Override
+ public Server call() throws Exception {
+ return getNode(input);
+ }
+ });
+ }
+ }));
+
+ return getUnchecked(futures);
+ }
+
+ private void destroyServer(final String serverId, final String dataCenterId) {
+ try {
+ logger.trace("<< deleting server with id=%s", serverId);
+ provisioningManager.provision(jobFactory.create(dataCenterId, new Supplier<Object>() {
+
+ @Override
+ public Object get() {
+ api.serverApi().deleteServer(serverId);
+
+ return serverId;
+ }
+ }));
+ logger.trace(">> server '%s' deleted.", serverId);
+ } catch (Exception ex) {
+ logger.warn(ex, ">> failed to delete server with id=%s", serverId);
+ }
+ }
+
+ private void destroyStorages(List<String> storageIds, String dataCenterId) {
+ for (String storageId : storageIds)
+ destroyStorage(storageId, dataCenterId);
+ }
+
+ private void destroyStorage(final String storageId, final String dataCenterId) {
+ try {
+ logger.trace("<< deleting storage with id=%s", storageId);
+ provisioningManager.provision(jobFactory.create(dataCenterId, new Supplier<Object>() {
+
+ @Override
+ public Object get() {
+ api.storageApi().deleteStorage(storageId);
+
+ return storageId;
+ }
+ }));
+ logger.trace(">> storage '%s' deleted.", storageId);
+ } catch (Exception ex) {
+ logger.warn(ex, ">> failed to delete storage with id=%s", storageId);
+ }
+ }
+
+ private Server getRequiredNode(String nodeId) {
+ Server node = getNode(nodeId);
+ if (node == null)
+ throw new ResourceNotFoundException("Node with id'" + nodeId + "' was not found.");
+ return node;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/concurrent/ProvisioningJob.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/concurrent/ProvisioningJob.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/concurrent/ProvisioningJob.java
new file mode 100644
index 0000000..7da7d3c
--- /dev/null
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/concurrent/ProvisioningJob.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.profitbricks.compute.concurrent;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.profitbricks.config.ProfitBricksComputeProperties.POLL_PREDICATE_DATACENTER;
+
+import java.util.concurrent.Callable;
+
+import javax.inject.Named;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+public class ProvisioningJob implements Callable {
+
+ public interface Factory {
+
+ ProvisioningJob create(String group, Supplier<Object> operation);
+ }
+
+ private final Predicate<String> waitDataCenterUntilReady;
+ private final String group;
+ private final Supplier<Object> operation;
+
+ @Inject
+ ProvisioningJob(@Named(POLL_PREDICATE_DATACENTER) Predicate<String> waitDataCenterUntilReady,
+ @Assisted String group, @Assisted Supplier<Object> operation) {
+ this.waitDataCenterUntilReady = waitDataCenterUntilReady;
+ this.group = checkNotNull(group, "group cannot be null");
+ this.operation = checkNotNull(operation, "operation cannot be null");
+ }
+
+ @Override
+ public Object call() throws Exception {
+ waitDataCenterUntilReady.apply(group);
+ Object obj = operation.get();
+ waitDataCenterUntilReady.apply(group);
+
+ return obj;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/concurrent/ProvisioningManager.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/concurrent/ProvisioningManager.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/concurrent/ProvisioningManager.java
new file mode 100644
index 0000000..820cafe
--- /dev/null
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/concurrent/ProvisioningManager.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.profitbricks.compute.concurrent;
+
+import static com.google.common.util.concurrent.Futures.getUnchecked;
+import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.Resource;
+
+import org.jclouds.concurrent.config.WithSubmissionTrace;
+import org.jclouds.logging.Logger;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+ * Delegates {@link Job} to single-threaded executor services based on it's group.
+ *
+ */
+public final class ProvisioningManager implements Closeable {
+
+ @Resource
+ private Logger logger = Logger.NULL;
+
+ private final Map<String, ListeningExecutorService> workers
+ = new ConcurrentHashMap<String, ListeningExecutorService>(1);
+
+ private final AtomicBoolean terminated = new AtomicBoolean(false);
+
+ public Object provision(ProvisioningJob job) {
+ if (terminated.get()) {
+ logger.warn("Job(%s) submitted but the provisioning manager is already closed", job);
+ return null;
+ }
+
+ logger.debug("Job(%s) submitted to group '%s'", job, job.getGroup());
+ ListeningExecutorService workerGroup = getWorkerGroup(job.getGroup());
+ return getUnchecked(workerGroup.submit(job));
+ }
+
+ protected ListeningExecutorService newExecutorService() {
+ return WithSubmissionTrace.wrap(listeningDecorator(Executors.newSingleThreadExecutor()));
+ }
+
+ private void newWorkerGroupIfAbsent(String name) {
+ if (!workers.containsKey(name))
+ workers.put(name, newExecutorService());
+ }
+
+ private ListeningExecutorService getWorkerGroup(String name) {
+ newWorkerGroupIfAbsent(name);
+ return workers.get(name);
+ }
+
+ @Override
+ public void close() throws IOException {
+ terminated.set(true); // Do not allow to enqueue more jobs
+ Collection<ListeningExecutorService> executors = workers.values();
+ for (ListeningExecutorService executor : executors) {
+ List<Runnable> runnables = executor.shutdownNow();
+ if (!runnables.isEmpty())
+ logger.warn("when shutting down executor %s, runnables outstanding: %s", executor, runnables);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/config/ProfitBricksComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/config/ProfitBricksComputeServiceContextModule.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/config/ProfitBricksComputeServiceContextModule.java
new file mode 100644
index 0000000..d260caf
--- /dev/null
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/config/ProfitBricksComputeServiceContextModule.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.profitbricks.compute.config;
+
+import static org.jclouds.profitbricks.config.ProfitBricksComputeProperties.POLL_PERIOD;
+import static org.jclouds.profitbricks.config.ProfitBricksComputeProperties.POLL_MAX_PERIOD;
+import static org.jclouds.profitbricks.config.ProfitBricksComputeProperties.POLL_PREDICATE_DATACENTER;
+import static org.jclouds.profitbricks.config.ProfitBricksComputeProperties.POLL_TIMEOUT;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Volume;
+import org.jclouds.domain.Location;
+import org.jclouds.functions.IdentityFunction;
+import org.jclouds.lifecycle.Closer;
+import org.jclouds.location.suppliers.ImplicitLocationSupplier;
+import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone;
+import org.jclouds.profitbricks.ProfitBricksApi;
+import org.jclouds.profitbricks.compute.ProfitBricksComputeServiceAdapter;
+import org.jclouds.profitbricks.compute.concurrent.ProvisioningJob;
+import org.jclouds.profitbricks.compute.concurrent.ProvisioningManager;
+import org.jclouds.profitbricks.domain.DataCenter;
+import org.jclouds.profitbricks.domain.Server;
+import org.jclouds.profitbricks.domain.Storage;
+import org.jclouds.profitbricks.compute.function.DataCenterToLocation;
+import org.jclouds.profitbricks.compute.function.LocationToLocation;
+import org.jclouds.profitbricks.compute.function.ProvisionableToImage;
+import org.jclouds.profitbricks.compute.function.ServerToNodeMetadata;
+import org.jclouds.profitbricks.compute.function.StorageToVolume;
+import org.jclouds.profitbricks.compute.internal.ProvisioningStatusAware;
+import org.jclouds.profitbricks.compute.internal.ProvisioningStatusPollingPredicate;
+import org.jclouds.profitbricks.domain.ProvisioningState;
+import org.jclouds.profitbricks.domain.internal.Provisionable;
+import org.jclouds.util.Predicates2;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.inject.Inject;
+import com.google.inject.Provides;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+
+public class ProfitBricksComputeServiceContextModule extends
+ ComputeServiceAdapterContextModule<Server, Hardware, Provisionable, DataCenter> {
+
+ @Override
+ protected void configure() {
+ super.configure();
+
+ install(new LocationsFromComputeServiceAdapterModule<Server, Hardware, Provisionable, DataCenter>() {
+ });
+
+ install(new FactoryModuleBuilder().build(ProvisioningJob.Factory.class));
+
+ bind(ImplicitLocationSupplier.class).to(OnlyLocationOrFirstZone.class).in(Singleton.class);
+
+ bind(new TypeLiteral<ComputeServiceAdapter<Server, Hardware, Provisionable, DataCenter>>() {
+ }).to(ProfitBricksComputeServiceAdapter.class);
+
+ bind(new TypeLiteral<Function<org.jclouds.profitbricks.domain.Location, Location>>() {
+ }).to(LocationToLocation.class);
+
+ bind(new TypeLiteral<Function<DataCenter, Location>>() {
+ }).to(DataCenterToLocation.class);
+
+ bind(new TypeLiteral<Function<Server, NodeMetadata>>() {
+ }).to(ServerToNodeMetadata.class);
+
+ bind(new TypeLiteral<Function<Provisionable, Image>>() {
+ }).to(ProvisionableToImage.class);
+
+ bind(new TypeLiteral<Function<Storage, Volume>>() {
+ }).to(StorageToVolume.class);
+
+ bind(new TypeLiteral<Function<Hardware, Hardware>>() {
+ }).to(Class.class.cast(IdentityFunction.class));
+ }
+
+ @Provides
+ @Singleton
+ @Named(POLL_PREDICATE_DATACENTER)
+ Predicate<String> provideWaitDataCenterUntilAvailablePredicate(
+ final ProfitBricksApi api, ComputeConstants constants) {
+ return Predicates2.retry(new ProvisioningStatusPollingPredicate(
+ api, ProvisioningStatusAware.DATACENTER, ProvisioningState.AVAILABLE),
+ constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS);
+ }
+
+ @Provides
+ @Singleton
+ ProvisioningManager provideProvisioningManager(Closer closer) {
+ ProvisioningManager provisioningManager = new ProvisioningManager();
+ closer.addToClose(provisioningManager);
+
+ return provisioningManager;
+ }
+
+ @Singleton
+ public static class ComputeConstants {
+
+ @Inject
+ @Named(POLL_TIMEOUT)
+ private String pollTimeout;
+
+ @Inject
+ @Named(POLL_PERIOD)
+ private String pollPeriod;
+
+ @Inject
+ @Named(POLL_MAX_PERIOD)
+ private String pollMaxPeriod;
+
+ public long pollTimeout() {
+ return Long.parseLong(pollTimeout);
+ }
+
+ public long pollPeriod() {
+ return Long.parseLong(pollPeriod);
+ }
+
+ public long pollMaxPeriod() {
+ return Long.parseLong(pollMaxPeriod);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/DataCenterToLocation.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/DataCenterToLocation.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/DataCenterToLocation.java
new file mode 100644
index 0000000..93fb3a0
--- /dev/null
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/DataCenterToLocation.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.jclouds.profitbricks.compute.function;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.profitbricks.domain.DataCenter;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Inject;
+
+public class DataCenterToLocation implements Function<DataCenter, Location> {
+
+ private final Function<org.jclouds.profitbricks.domain.Location, Location> fnRegion;
+
+ @Inject
+ DataCenterToLocation(Function<org.jclouds.profitbricks.domain.Location, Location> fnRegion) {
+ this.fnRegion = fnRegion;
+ }
+
+ @Override
+ public Location apply(DataCenter dataCenter) {
+ checkNotNull(dataCenter, "Null dataCenter");
+
+ LocationBuilder builder = new LocationBuilder()
+ .id(dataCenter.id())
+ .description(dataCenter.name())
+ .scope(LocationScope.ZONE)
+ .metadata(ImmutableMap.<String, Object>of(
+ "version", dataCenter.version(),
+ "state", dataCenter.state()));
+ if (dataCenter.location() != null)
+ builder.parent(fnRegion.apply(dataCenter.location()));
+ return builder.build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/LocationToLocation.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/LocationToLocation.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/LocationToLocation.java
new file mode 100644
index 0000000..999069b
--- /dev/null
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/LocationToLocation.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.jclouds.profitbricks.compute.function;
+
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.location.suppliers.all.JustProvider;
+import org.jclouds.profitbricks.domain.Location;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+
+public class LocationToLocation implements Function<Location, org.jclouds.domain.Location> {
+
+ private final JustProvider justProvider;
+
+ @Inject
+ LocationToLocation(JustProvider justProvider) {
+ this.justProvider = justProvider;
+ }
+
+ @Override
+ public org.jclouds.domain.Location apply(Location in) {
+ return new LocationBuilder()
+ .id(in.getId())
+ .description(in.getDescription())
+ .scope(LocationScope.REGION)
+ .parent(Iterables.getOnlyElement(justProvider.get()))
+ .build();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/ProvisionableToImage.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/ProvisionableToImage.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/ProvisionableToImage.java
new file mode 100644
index 0000000..c5fcd78
--- /dev/null
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/ProvisionableToImage.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.profitbricks.compute.function;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.regex.Pattern;
+
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.domain.Location;
+import org.jclouds.profitbricks.domain.OsType;
+import org.jclouds.profitbricks.domain.ProvisioningState;
+import org.jclouds.profitbricks.domain.Snapshot;
+import org.jclouds.profitbricks.domain.internal.Provisionable;
+
+import com.google.common.base.Function;
+import com.google.common.base.Strings;
+import com.google.inject.Inject;
+
+public class ProvisionableToImage implements Function<Provisionable, Image> {
+
+ private final ImageToImage fnImageToImage;
+ private final SnapshotToImage fnSnapshotToImage;
+
+ @Inject
+ ProvisionableToImage(Function<org.jclouds.profitbricks.domain.Location, Location> fnRegion) {
+ this.fnImageToImage = new ImageToImage(fnRegion);
+ this.fnSnapshotToImage = new SnapshotToImage(fnRegion);
+ }
+
+ @Override
+ public Image apply(Provisionable input) {
+ checkNotNull(input, "Cannot convert null input");
+
+ if (input instanceof org.jclouds.profitbricks.domain.Image)
+ return fnImageToImage.apply((org.jclouds.profitbricks.domain.Image) input);
+
+ else if (input instanceof Snapshot)
+ return fnSnapshotToImage.apply((Snapshot) input);
+
+ else
+ throw new UnsupportedOperationException("No implementation found for provisionable of concrete type '"
+ + input.getClass().getCanonicalName() + "'");
+ }
+
+ private static OsFamily mapOsFamily(OsType osType) {
+ if (osType == null)
+ return OsFamily.UNRECOGNIZED;
+ switch (osType) {
+ case WINDOWS:
+ return OsFamily.WINDOWS;
+ case LINUX:
+ return OsFamily.LINUX;
+ case UNRECOGNIZED:
+ case OTHER:
+ default:
+ return OsFamily.UNRECOGNIZED;
+ }
+ }
+
+ private static class ImageToImage implements Function<org.jclouds.profitbricks.domain.Image, Image> {
+
+ private static final Pattern HAS_NUMBERS = Pattern.compile(".*\\d+.*");
+
+ private final Function<org.jclouds.profitbricks.domain.Location, Location> fnRegion;
+
+ ImageToImage(Function<org.jclouds.profitbricks.domain.Location, Location> fnRegion) {
+ this.fnRegion = fnRegion;
+ }
+
+ @Override
+ public Image apply(org.jclouds.profitbricks.domain.Image from) {
+ String desc = from.name();
+ OsFamily osFamily = parseOsFamily(desc, from.osType());
+
+ OperatingSystem os = OperatingSystem.builder()
+ .description(osFamily.value())
+ .family(osFamily)
+ .version(parseVersion(desc))
+ .is64Bit(is64Bit(desc, from.type()))
+ .build();
+
+ return new ImageBuilder()
+ .ids(from.id())
+ .name(desc)
+ .location(fnRegion.apply(from.location()))
+ .status(Image.Status.AVAILABLE)
+ .operatingSystem(os)
+ .build();
+ }
+
+ private OsFamily parseOsFamily(String from, OsType fallbackValue) {
+ if (from != null)
+ try {
+ // ProfitBricks images names are usually in format:
+ // [osType]-[version]-[subversion]-..-[date-created]
+ String desc = from.toUpperCase().split("-")[0];
+ OsFamily osFamily = OsFamily.fromValue(desc);
+ checkArgument(osFamily != OsFamily.UNRECOGNIZED);
+
+ return osFamily;
+ } catch (Exception ex) {
+ // do nothing
+ }
+ return mapOsFamily(fallbackValue);
+ }
+
+ private String parseVersion(String from) {
+ if (from != null) {
+ String[] split = from.toLowerCase().split("-");
+ if (split.length >= 2) {
+ int i = 1; // usually on second token
+ String version = split[i];
+ while (!HAS_NUMBERS.matcher(version).matches())
+ version = split[++i];
+ return version;
+ }
+ }
+ return "";
+ }
+
+ private boolean is64Bit(String from, org.jclouds.profitbricks.domain.Image.Type type) {
+ switch (type) {
+ case CDROM:
+ if (!Strings.isNullOrEmpty(from))
+ return from.matches("x86_64|amd64");
+ case HDD: // HDD provided by ProfitBricks are always 64-bit
+ default:
+ return true;
+ }
+ }
+ }
+
+ private static class SnapshotToImage implements Function<Snapshot, Image> {
+
+ private final Function<org.jclouds.profitbricks.domain.Location, Location> fnRegion;
+
+ SnapshotToImage(Function<org.jclouds.profitbricks.domain.Location, Location> fnRegion) {
+ this.fnRegion = fnRegion;
+ }
+
+ @Override
+ public Image apply(Snapshot from) {
+ String textToParse = from.name() + from.description();
+ OsFamily osFamily = parseOsFamily(textToParse, from.osType());
+
+ OperatingSystem os = OperatingSystem.builder()
+ .description(osFamily.value())
+ .family(osFamily)
+ .is64Bit(true)
+ .version("00.00")
+ .build();
+
+ return new ImageBuilder()
+ .ids(from.id())
+ .name(from.name())
+ .description(from.description())
+ .location(fnRegion.apply(from.location()))
+ .status(mapStatus(from.state()))
+ .operatingSystem(os)
+ .build();
+ }
+
+ private OsFamily parseOsFamily(String text, OsType fallbackValue) {
+ if (text != null)
+ try {
+ // Attempt parsing OsFamily by scanning name and description
+ // @see ProfitBricksComputeServiceAdapter#L190
+ OsFamily[] families = OsFamily.values();
+ for (OsFamily family : families)
+ if (text.contains(family.value()))
+ return family;
+ } catch (Exception ex) {
+ // do nothing
+ }
+ return mapOsFamily(fallbackValue);
+ }
+
+ static Image.Status mapStatus(ProvisioningState state) {
+ if (state == null)
+ return Image.Status.UNRECOGNIZED;
+ switch (state) {
+ case AVAILABLE:
+ return Image.Status.AVAILABLE;
+ case DELETED:
+ return Image.Status.DELETED;
+ case ERROR:
+ return Image.Status.ERROR;
+ case INACTIVE:
+ case INPROCESS:
+ return Image.Status.PENDING;
+ default:
+ return Image.Status.UNRECOGNIZED;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/ServerToNodeMetadata.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/ServerToNodeMetadata.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/ServerToNodeMetadata.java
new file mode 100644
index 0000000..9a8d551
--- /dev/null
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/ServerToNodeMetadata.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.profitbricks.compute.function;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.not;
+import static org.jclouds.profitbricks.domain.OsType.LINUX;
+import static org.jclouds.profitbricks.domain.OsType.WINDOWS;
+import static org.jclouds.profitbricks.domain.Server.Status.BLOCKED;
+import static org.jclouds.profitbricks.domain.Server.Status.CRASHED;
+import static org.jclouds.profitbricks.domain.Server.Status.PAUSED;
+import static org.jclouds.profitbricks.domain.Server.Status.RUNNING;
+import static org.jclouds.profitbricks.domain.Server.Status.SHUTDOWN;
+import static org.jclouds.profitbricks.domain.Server.Status.SHUTOFF;
+
+import java.util.List;
+import java.util.Set;
+
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.Volume;
+import org.jclouds.domain.Location;
+import org.jclouds.profitbricks.domain.Nic;
+import org.jclouds.profitbricks.domain.OsType;
+import org.jclouds.profitbricks.domain.Server;
+import org.jclouds.profitbricks.domain.Storage;
+import org.jclouds.util.InetAddresses2.IsPrivateIPAddress;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.functions.GroupNamingConvention;
+
+public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
+
+ private final Function<Storage, Volume> fnVolume;
+ private final Supplier<Set<? extends Location>> locationSupply;
+ private final Function<List<Nic>, List<String>> fnCollectIps;
+
+ private final GroupNamingConvention groupNamingConvention;
+
+ @Inject
+ ServerToNodeMetadata(Function<Storage, Volume> fnVolume,
+ @Memoized Supplier<Set<? extends Location>> locationsSupply,
+ GroupNamingConvention.Factory groupNamingConvention) {
+ this.fnVolume = fnVolume;
+ this.locationSupply = locationsSupply;
+ this.groupNamingConvention = groupNamingConvention.createWithoutPrefix();
+ this.fnCollectIps = new Function<List<Nic>, List<String>>() {
+
+ @Override
+ public List<String> apply(List<Nic> in) {
+ List<String> ips = Lists.newArrayListWithExpectedSize(in.size());
+ for (Nic nic : in)
+ ips.addAll(nic.ips());
+ return ips;
+ }
+ };
+ }
+
+ @Override
+ public NodeMetadata apply(final Server server) {
+ checkNotNull(server, "Null server");
+
+ // Map fetched dataCenterId with actual populated object
+ Location location = null;
+ if (server.dataCenter() != null)
+ location = Iterables.find(locationSupply.get(), new Predicate<Location>() {
+
+ @Override
+ public boolean apply(Location t) {
+ return t.getId().equals(server.dataCenter().id());
+ }
+ });
+
+ float size = 0f;
+ List<Volume> volumes = Lists.newArrayList();
+ List<Storage> storages = server.storages();
+ if (storages != null)
+ for (Storage storage : storages) {
+ size += storage.size();
+ volumes.add(fnVolume.apply(storage));
+ }
+
+ // Build hardware
+ String id = String.format("cpu=%d,ram=%d,disk=%.0f", server.cores(), server.ram(), size);
+ Hardware hardware = new HardwareBuilder()
+ .ids(id)
+ .name(id)
+ .ram(server.ram())
+ .processor(new Processor(server.cores(), 1d))
+ .hypervisor("kvm")
+ .volumes(volumes)
+ .location(location)
+ .build();
+
+ // Collect ips
+ List<String> addresses = fnCollectIps.apply(server.nics());
+
+ // Build node
+ NodeMetadataBuilder nodeBuilder = new NodeMetadataBuilder();
+ nodeBuilder.ids(server.id())
+ .group(groupNamingConvention.extractGroup(server.name()))
+ .hostname(server.hostname())
+ .name(server.name())
+ .backendStatus(server.state().toString())
+ .status(mapStatus(server.status()))
+ .hardware(hardware)
+ .operatingSystem(mapOsType(server.osType()))
+ .location(location)
+ .privateAddresses(Iterables.filter(addresses, IsPrivateIPAddress.INSTANCE))
+ .publicAddresses(Iterables.filter(addresses, not(IsPrivateIPAddress.INSTANCE)));
+
+ return nodeBuilder.build();
+ }
+
+ static NodeMetadata.Status mapStatus(Server.Status status) {
+ if (status == null)
+ return NodeMetadata.Status.UNRECOGNIZED;
+ switch (status) {
+ case SHUTDOWN:
+ case SHUTOFF:
+ case PAUSED:
+ return NodeMetadata.Status.SUSPENDED;
+ case RUNNING:
+ return NodeMetadata.Status.RUNNING;
+ case BLOCKED:
+ return NodeMetadata.Status.PENDING;
+ case CRASHED:
+ return NodeMetadata.Status.ERROR;
+ default:
+ return NodeMetadata.Status.UNRECOGNIZED;
+ }
+ }
+
+ static OperatingSystem mapOsType(OsType osType) {
+ if (osType != null)
+ switch (osType) {
+ case WINDOWS:
+ return OperatingSystem.builder()
+ .description(OsFamily.WINDOWS.value())
+ .family(OsFamily.WINDOWS)
+ .build();
+ case LINUX:
+ return OperatingSystem.builder()
+ .description(OsFamily.LINUX.value())
+ .family(OsFamily.LINUX)
+ .build();
+ }
+ return OperatingSystem.builder()
+ .description(OsFamily.UNRECOGNIZED.value())
+ .family(OsFamily.UNRECOGNIZED)
+ .build();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/StorageToVolume.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/StorageToVolume.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/StorageToVolume.java
new file mode 100644
index 0000000..5557bca
--- /dev/null
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/function/StorageToVolume.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.jclouds.profitbricks.compute.function;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.compute.domain.Volume;
+import org.jclouds.compute.domain.VolumeBuilder;
+import org.jclouds.profitbricks.domain.Storage;
+
+import com.google.common.base.Function;
+
+public class StorageToVolume implements Function<Storage, Volume> {
+
+ @Override
+ public Volume apply(Storage storage) {
+ checkNotNull(storage, "Null storage");
+
+ String device = "";
+ if (storage.deviceNumber() != null)
+ device = storage.deviceNumber().toString();
+
+ return new VolumeBuilder()
+ .id(storage.id())
+ .size(storage.size())
+ .bootDevice(storage.bootDevice())
+ .device(device)
+ .durable(true)
+ .type(Volume.Type.LOCAL)
+ .build();
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/ed247e7d/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicate.java
----------------------------------------------------------------------
diff --git a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicate.java b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicate.java
index f38abad..41c3e93 100644
--- a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicate.java
+++ b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicate.java
@@ -22,6 +22,7 @@ import org.jclouds.profitbricks.ProfitBricksApi;
import org.jclouds.profitbricks.domain.ProvisioningState;
import com.google.common.base.Predicate;
+import org.jclouds.rest.ResourceNotFoundException;
/**
* A custom predicate for waiting until a virtual resource satisfies the given expected status
@@ -45,19 +46,24 @@ public class ProvisioningStatusPollingPredicate implements Predicate<String> {
@Override
public boolean apply(String input) {
checkNotNull(input, "Virtual item id can't be null.");
- switch (domain) {
- case DATACENTER:
- return expect == api.dataCenterApi().getDataCenterState(input);
- case SERVER:
- return expect == api.serverApi().getServer(input).state();
- case STORAGE:
- return expect == api.storageApi().getStorage(input).state();
- case NIC:
- return expect == api.nicApi().getNic(input).state();
- case SNAPSHOT:
- return expect == api.snapshotApi().getSnapshot(input).state();
- default:
- throw new IllegalArgumentException("Unknown domain '" + domain + "'");
+ try {
+ switch (domain) {
+ case DATACENTER:
+ return expect == api.dataCenterApi().getDataCenterState(input);
+ case SERVER:
+ return expect == api.serverApi().getServer(input).state();
+ case STORAGE:
+ return expect == api.storageApi().getStorage(input).state();
+ case NIC:
+ return expect == api.nicApi().getNic(input).state();
+ case SNAPSHOT:
+ return expect == api.snapshotApi().getSnapshot(input).state();
+ default:
+ throw new IllegalArgumentException("Unknown domain '" + domain + "'");
+ }
+ } catch (ResourceNotFoundException ex) {
+ // After provisioning, a node might still not be "fetchable" via API
+ return false;
}
}