ComputeService implementation for ProfitBricks Rest
Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs/commit/5742745e Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/5742745e Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/5742745e Branch: refs/heads/master Commit: 5742745e029cf765137b03f246415739a5feb4e3 Parents: 4e39720 Author: Ali Bazlamit <[email protected]> Authored: Fri Sep 16 12:29:00 2016 +0200 Committer: Ignasi Barrera <[email protected]> Committed: Thu Oct 6 13:07:36 2016 +0200 ---------------------------------------------------------------------- profitbricks-rest/pom.xml | 25 +- .../profitbricks/rest/ProfitBricksApi.java | 25 +- .../rest/ProfitBricksApiMetadata.java | 26 +- .../rest/ProfitBricksProviderMetadata.java | 45 +- .../ProfitBricksComputeServiceAdapter.java | 576 +++++++++++++++++++ .../compute/concurrent/ProvisioningJob.java | 62 ++ .../compute/concurrent/ProvisioningManager.java | 87 +++ ...ProfitBricksComputeServiceContextModule.java | 330 +++++++++++ .../compute/function/ProvisionableToImage.java | 245 ++++++++ .../ServerInDataCenterToNodeMetadata.java | 174 ++++++ .../rest/compute/function/VolumeToVolume.java | 44 ++ .../strategy/AssignDataCenterToTemplate.java | 100 ++++ .../strategy/TemplateWithDataCenter.java | 108 ++++ .../config/ProfitBricksComputeProperties.java | 5 +- .../profitbricks/rest/domain/CpuFamily.java | 28 + .../profitbricks/rest/domain/DataCenter.java | 2 + .../profitbricks/rest/domain/Firewall.java | 16 +- .../jclouds/profitbricks/rest/domain/Image.java | 133 +++-- .../profitbricks/rest/domain/IpBlock.java | 104 ++++ .../profitbricks/rest/domain/Location.java | 20 +- .../profitbricks/rest/domain/Provisionable.java | 13 +- .../profitbricks/rest/domain/Server.java | 109 ++-- .../profitbricks/rest/domain/Snapshot.java | 99 ++-- .../profitbricks/rest/domain/VolumeType.java | 2 +- .../rest/domain/zonescoped/DataCenterAndId.java | 90 +++ .../domain/zonescoped/ServerInDataCenter.java | 46 ++ .../profitbricks/rest/features/IpBlockApi.java | 89 +++ .../profitbricks/rest/features/ServerApi.java | 50 +- .../jclouds/profitbricks/rest/ids/NicRef.java | 34 ++ .../rest/util/ApiPredicatesModule.java | 34 +- .../ProfitBricksComputeServiceLiveTest.java | 106 ++++ .../ProfitBricksTemplateBuilderLiveTest.java | 36 ++ .../concurrent/ProvisioningManagerTest.java | 118 ++++ .../compute/config/StatusPredicateTest.java | 120 ++++ .../function/ProvisionableToImageTest.java | 198 +++++++ .../ServerInDataCenterToNodeMetadataTest.java | 157 +++++ .../compute/function/VolumeToVolumeTest.java | 58 ++ .../rest/features/DataCenterApiMockTest.java | 67 +-- .../rest/features/IpBlockApiMockTest.java | 150 +++++ .../rest/features/IpblockApiLiveTest.java | 96 ++++ .../rest/features/ServerApiLiveTest.java | 73 +-- .../rest/features/SnapshotApiLiveTest.java | 76 +-- .../rest/features/SnapshotApiMockTest.java | 77 ++- .../rest/features/VolumeApiLiveTest.java | 66 +-- .../rest/internal/BaseProfitBricksLiveTest.java | 93 +-- .../src/test/resources/compute/datacenter.json | 123 ++++ .../src/test/resources/compute/image.json | 32 ++ .../src/test/resources/compute/image1.json | 32 ++ .../src/test/resources/compute/image2.json | 32 ++ .../src/test/resources/compute/image3.json | 32 ++ .../compute/predicate/datacenter-inprocess.json | 41 ++ .../resources/compute/predicate/datacenter.json | 41 ++ .../compute/predicate/server-inprocess.json | 173 ++++++ .../resources/compute/predicate/server.json | 173 ++++++ .../compute/predicate/snapshot-inprocess.json | 30 + .../resources/compute/predicate/snapshot.json | 30 + .../src/test/resources/compute/server.json | 140 +++++ .../src/test/resources/compute/snapshot1.json | 30 + .../src/test/resources/compute/snapshot2.json | 30 + .../src/test/resources/compute/volume.json | 33 ++ .../src/test/resources/ipblock/get.json | 19 + .../test/resources/ipblock/list.depth-5.json | 42 ++ .../src/test/resources/ipblock/list.json | 14 + 63 files changed, 4797 insertions(+), 462 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/5742745e/profitbricks-rest/pom.xml ---------------------------------------------------------------------- diff --git a/profitbricks-rest/pom.xml b/profitbricks-rest/pom.xml index d676d88..0f1b41d 100644 --- a/profitbricks-rest/pom.xml +++ b/profitbricks-rest/pom.xml @@ -51,6 +51,11 @@ <version>${jclouds.version}</version> </dependency> <dependency> + <groupId>org.apache.jclouds</groupId> + <artifactId>jclouds-compute</artifactId> + <version>${jclouds.version}</version> + </dependency> + <dependency> <groupId>com.google.auto.service</groupId> <artifactId>auto-service</artifactId> <scope>provided</scope> @@ -61,9 +66,9 @@ <scope>provided</scope> </dependency> <dependency> - <groupId>org.apache.jclouds.driver</groupId> - <artifactId>jclouds-okhttp</artifactId> - <version>${jclouds.version}</version> + <groupId>org.apache.jclouds.driver</groupId> + <artifactId>jclouds-okhttp</artifactId> + <version>${jclouds.version}</version> </dependency> <!-- Test dependencies --> <dependency> @@ -74,6 +79,13 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.apache.jclouds</groupId> + <artifactId>jclouds-compute</artifactId> + <version>${jclouds.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.apache.jclouds.driver</groupId> <artifactId>jclouds-sshj</artifactId> <version>${jclouds.version}</version> @@ -108,6 +120,13 @@ <artifactId>logback-classic</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.jclouds.driver</groupId> + <artifactId>jclouds-log4j</artifactId> + <version>${jclouds.version}</version> + <scope>test</scope> + <type>jar</type> + </dependency> </dependencies> <profiles> http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/5742745e/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 a97dd02..7c4b2b9 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 @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.jclouds.profitbricks.rest; import com.google.common.annotations.Beta; @@ -22,6 +21,7 @@ import java.io.Closeable; import org.apache.jclouds.profitbricks.rest.features.DataCenterApi; import org.apache.jclouds.profitbricks.rest.features.FirewallApi; import org.apache.jclouds.profitbricks.rest.features.ImageApi; +import org.apache.jclouds.profitbricks.rest.features.IpBlockApi; import org.apache.jclouds.profitbricks.rest.features.LanApi; import org.apache.jclouds.profitbricks.rest.features.NicApi; import org.apache.jclouds.profitbricks.rest.features.ServerApi; @@ -31,29 +31,32 @@ import org.jclouds.rest.annotations.Delegate; @Beta public interface ProfitBricksApi extends Closeable { - + @Delegate DataCenterApi dataCenterApi(); - + + @Delegate + LanApi lanApi(); + + @Delegate + FirewallApi firewallApi(); + @Delegate ServerApi serverApi(); - + @Delegate VolumeApi volumeApi(); @Delegate ImageApi imageApi(); - + @Delegate SnapshotApi snapshotApi(); - + @Delegate NicApi nicApi(); - - @Delegate - LanApi lanApi(); - + @Delegate - FirewallApi firewallApi(); + IpBlockApi ipBlockApi(); } http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/5742745e/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApiMetadata.java ---------------------------------------------------------------------- diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApiMetadata.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApiMetadata.java index f5ac5e1..6bb680c 100644 --- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApiMetadata.java +++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksApiMetadata.java @@ -20,8 +20,9 @@ import com.google.common.collect.ImmutableSet; import com.google.inject.Module; import java.net.URI; import java.util.Properties; -import org.apache.jclouds.profitbricks.rest.util.ApiPredicatesModule; +import org.apache.jclouds.profitbricks.rest.compute.config.ProfitBricksComputeServiceContextModule; import org.apache.jclouds.profitbricks.rest.config.ProfitBricksHttpApiModule; +import org.jclouds.compute.ComputeServiceContext; import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule; import org.jclouds.rest.internal.BaseHttpApiMetadata; @@ -49,17 +50,18 @@ public class ProfitBricksApiMetadata extends BaseHttpApiMetadata<ProfitBricksApi protected Builder() { id("profitbricks-rest") - .name("ProfitBricks REST API") - .identityName("API Username") - .credentialName("API Password") - .documentation(URI.create("https://devops.profitbricks.com/api/rest/")) - .defaultEndpoint("https://api.profitbricks.com/rest/v2/") - .defaultProperties(ProfitBricksApiMetadata.defaultProperties()) - .defaultModules(ImmutableSet.<Class<? extends Module>>builder() - .add(OkHttpCommandExecutorServiceModule.class) - .add(ProfitBricksHttpApiModule.class) - .add(ApiPredicatesModule.class) - .build()); + .name("ProfitBricks REST API") + .identityName("API Username") + .credentialName("API Password") + .documentation(URI.create("https://devops.profitbricks.com/api/rest/")) + .defaultEndpoint("https://api.profitbricks.com/rest/v2/") + .view(ComputeServiceContext.class) + .defaultProperties(ProfitBricksApiMetadata.defaultProperties()) + .defaultModules(ImmutableSet.<Class<? extends Module>>builder() + .add(OkHttpCommandExecutorServiceModule.class) + .add(ProfitBricksHttpApiModule.class) + .add(ProfitBricksComputeServiceContextModule.class) + .build()); } @Override http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/5742745e/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksProviderMetadata.java ---------------------------------------------------------------------- diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksProviderMetadata.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksProviderMetadata.java index 133e7f6..477fc24 100644 --- a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksProviderMetadata.java +++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/ProfitBricksProviderMetadata.java @@ -55,19 +55,22 @@ public class ProfitBricksProviderMetadata extends BaseProviderMetadata { public static Properties defaultProperties() { Properties properties = ProfitBricksApiMetadata.defaultProperties(); - - properties.setProperty(PROPERTY_ISO3166_CODES, "US-NV,DE-HE,DE-BW"); - - properties.setProperty(PROPERTY_REGIONS, "DE,US"); - properties.setProperty(PROPERTY_REGION + ".DE.zones", "FKB,FRA"); - properties.setProperty(PROPERTY_REGION + ".US.zones", "LAS,LASDEV"); - - properties.setProperty(PROPERTY_ZONES, "FKB,FRA,LAS,LASDEV"); - properties.setProperty(PROPERTY_ZONE + ".FKB." + ISO3166_CODES, "DE-BW"); - properties.setProperty(PROPERTY_ZONE + ".FRA." + ISO3166_CODES, "DE-HE"); - properties.setProperty(PROPERTY_ZONE + ".LAS." + ISO3166_CODES, "US-NV"); - properties.setProperty(PROPERTY_ZONE + ".LASDEV." + ISO3166_CODES, "US-NV"); - + + properties.setProperty(PROPERTY_REGIONS, "de,us"); + properties.setProperty(PROPERTY_REGION + ".de.zones", "de/fkb,de/fra"); + properties.setProperty(PROPERTY_REGION + ".us.zones", "us/las,us/lasdev"); + properties.setProperty(PROPERTY_ZONES, "de/fkb,de/fra,us/las,us/lasdev"); + properties.setProperty(PROPERTY_ISO3166_CODES, "DE-BW,DE-HE,US_NV"); + properties.setProperty(PROPERTY_REGION + ".de." + ISO3166_CODES, "DE-BW,DE-HE"); + properties.setProperty(PROPERTY_REGION + ".us." + ISO3166_CODES, "US-NV"); + properties.setProperty(PROPERTY_ZONE + ".de/fkb." + ISO3166_CODES, "DE-BW"); + properties.setProperty(PROPERTY_ZONE + ".de/fra." + ISO3166_CODES, "DE-HE"); + properties.setProperty(PROPERTY_ZONE + ".us/las." + ISO3166_CODES, "US-NV"); + properties.setProperty(PROPERTY_ZONE + ".us/lasdev." + ISO3166_CODES, "US-NV"); + + properties.put("jclouds.ssh.max-retries", "7"); + properties.put("jclouds.ssh.retry-auth", "true"); + long defaultTimeout = 60l * 60l; // 1 hour properties.put(POLL_TIMEOUT, defaultTimeout); properties.put(POLL_PERIOD, 2l); @@ -75,7 +78,7 @@ public class ProfitBricksProviderMetadata extends BaseProviderMetadata { properties.put(PROPERTY_SO_TIMEOUT, 60000 * 5); properties.put(PROPERTY_CONNECTION_TIMEOUT, 60000 * 5); - + return properties; } @@ -83,13 +86,13 @@ public class ProfitBricksProviderMetadata extends BaseProviderMetadata { protected Builder() { id("profitbricks-rest") - .name("ProfitBricks REST Compute") - .apiMetadata(new ProfitBricksApiMetadata()) - .homepage(URI.create("https://www.profitbricks.com/")) - .console(URI.create("https://my.profitbricks.com/dashboard/dcdr2")) - .iso3166Codes("DE-FKB", "DE-FRA", "US-LAS", "US-LASDEV") - .endpoint("https://api.profitbricks.com/rest/v2/") - .defaultProperties(ProfitBricksProviderMetadata.defaultProperties()); + .name("ProfitBricks REST Compute") + .apiMetadata(new ProfitBricksApiMetadata()) + .homepage(URI.create("https://www.profitbricks.com/")) + .console(URI.create("https://my.profitbricks.com/dashboard/dcdr2")) + .iso3166Codes("DE-BW", "DE-HE", "US-NV") + .endpoint("https://api.profitbricks.com/rest/v2/") + .defaultProperties(ProfitBricksProviderMetadata.defaultProperties()); } @Override http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/5742745e/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/ProfitBricksComputeServiceAdapter.java ---------------------------------------------------------------------- diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/ProfitBricksComputeServiceAdapter.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/ProfitBricksComputeServiceAdapter.java new file mode 100644 index 0000000..f7f4145 --- /dev/null +++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/ProfitBricksComputeServiceAdapter.java @@ -0,0 +1,576 @@ +/* + * 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.compute; + +import static com.google.common.base.Preconditions.checkArgument; +import com.google.common.base.Predicate; +import static com.google.common.base.Strings.isNullOrEmpty; +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import static com.google.common.collect.Iterables.contains; +import static com.google.common.collect.Iterables.filter; +import com.google.common.collect.Lists; +import static com.google.common.util.concurrent.Futures.getUnchecked; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.inject.Inject; +import static java.lang.String.format; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; +import org.apache.jclouds.profitbricks.rest.ProfitBricksApi; +import org.apache.jclouds.profitbricks.rest.compute.concurrent.ProvisioningJob; +import org.apache.jclouds.profitbricks.rest.compute.concurrent.ProvisioningManager; +import org.apache.jclouds.profitbricks.rest.compute.function.ProvisionableToImage; +import org.apache.jclouds.profitbricks.rest.compute.strategy.TemplateWithDataCenter; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_DATACENTER; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_NIC; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_SERVER; +import org.apache.jclouds.profitbricks.rest.domain.DataCenter; +import org.apache.jclouds.profitbricks.rest.domain.Image; +import org.apache.jclouds.profitbricks.rest.domain.Lan; +import org.apache.jclouds.profitbricks.rest.domain.Nic; +import org.apache.jclouds.profitbricks.rest.domain.Provisionable; +import org.apache.jclouds.profitbricks.rest.domain.Server; +import org.apache.jclouds.profitbricks.rest.domain.Snapshot; +import org.apache.jclouds.profitbricks.rest.domain.VolumeType; +import org.apache.jclouds.profitbricks.rest.domain.options.DepthOptions; +import org.apache.jclouds.profitbricks.rest.domain.zonescoped.DataCenterAndId; +import org.apache.jclouds.profitbricks.rest.domain.zonescoped.ServerInDataCenter; +import org.apache.jclouds.profitbricks.rest.features.ServerApi; +import org.apache.jclouds.profitbricks.rest.ids.NicRef; +import org.apache.jclouds.profitbricks.rest.ids.ServerRef; +import org.apache.jclouds.profitbricks.rest.ids.VolumeRef; +import org.apache.jclouds.profitbricks.rest.util.Passwords; +import static org.jclouds.Constants.PROPERTY_USER_THREADS; +import org.jclouds.compute.ComputeServiceAdapter; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED; +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.Location; +import org.jclouds.domain.LocationScope; +import org.jclouds.domain.LoginCredentials; +import org.jclouds.logging.Logger; +import org.jclouds.rest.ResourceNotFoundException; + +@Singleton +public class ProfitBricksComputeServiceAdapter implements ComputeServiceAdapter<ServerInDataCenter, Hardware, Provisionable, Location> { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final ProfitBricksApi api; + private final Predicate<String> waitDcUntilAvailable; + private final Predicate<VolumeRef> waitVolumeUntilAvailable; + private final Predicate<ServerRef> waitServerUntilAvailable; + private final Predicate<ServerRef> waitServerUntilRunning; + private final Predicate<ServerRef> waitServerUntilSuspended; + private final Predicate<NicRef> waitNICUntilAvailable; + private final ListeningExecutorService executorService; + private final ProvisioningJob.Factory jobFactory; + private final ProvisioningManager provisioningManager; + private List<DataCenter> datacetners; + + private static final Integer DEFAULT_LAN_ID = 1; + + @Inject + ProfitBricksComputeServiceAdapter(ProfitBricksApi api, + @Named(POLL_PREDICATE_DATACENTER) Predicate<String> waitDcUntilAvailable, + @Named(TIMEOUT_NODE_RUNNING) Predicate<VolumeRef> waitVolumeUntilAvailable, + @Named(PROPERTY_USER_THREADS) ListeningExecutorService executorService, + @Named(POLL_PREDICATE_SERVER) Predicate<ServerRef> waitServerUntilAvailable, + @Named(TIMEOUT_NODE_RUNNING) Predicate<ServerRef> waitServerUntilRunning, + @Named(TIMEOUT_NODE_SUSPENDED) Predicate<ServerRef> waitServerUntilSuspended, + @Named(POLL_PREDICATE_NIC) Predicate<NicRef> waitNICUntilAvailable, + ProvisioningJob.Factory jobFactory, + ProvisioningManager provisioningManager) { + this.api = api; + this.waitDcUntilAvailable = waitDcUntilAvailable; + this.waitVolumeUntilAvailable = waitVolumeUntilAvailable; + this.waitServerUntilAvailable = waitServerUntilAvailable; + this.waitNICUntilAvailable = waitNICUntilAvailable; + this.waitServerUntilSuspended = waitServerUntilSuspended; + this.waitServerUntilRunning = waitServerUntilRunning; + this.executorService = executorService; + this.jobFactory = jobFactory; + this.provisioningManager = provisioningManager; + this.datacetners = ImmutableList.of(); + } + + @Override + public NodeAndInitialCredentials<ServerInDataCenter> createNodeWithGroupEncodedIntoName(String group, String name, Template template) { + checkArgument(template instanceof TemplateWithDataCenter, "This implementation requires a TemplateWithDataCenter"); + return createNodeWithGroupEncodedIntoName(group, name, TemplateWithDataCenter.class.cast(template)); + } + + protected NodeAndInitialCredentials<ServerInDataCenter> createNodeWithGroupEncodedIntoName(String group, String name, TemplateWithDataCenter template) { + checkArgument(template.getLocation().getScope() == LocationScope.ZONE, "Template must use a ZONE-scoped location"); + final String dataCenterId = template.getDataCenter().id(); + 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 volumes based on hardware + List<? extends Volume> volumes = hardware.getVolumes(); + List<String> volumeIds = Lists.newArrayListWithExpectedSize(volumes.size()); + + int i = 1; + for (final Volume volume : volumes) { + try { + logger.trace("<< provisioning volume '%s'", volume); + final org.apache.jclouds.profitbricks.rest.domain.Volume.Request.CreatePayload.Builder request = org.apache.jclouds.profitbricks.rest.domain.Volume.Request.creatingBuilder(); + if (i == 1) { + request.image(image.getId()); + // we don't need to pass password to the API if we're using a snapshot + Provisionable.Type provisionableType = Provisionable.Type.fromValue( + image.getUserMetadata().get(ProvisionableToImage.KEY_PROVISIONABLE_TYPE)); + if (provisionableType == Provisionable.Type.IMAGE) { + request.imagePassword(password); + } + + } + request.dataCenterId(dataCenterId). + name(format("%s-disk-%d", name, i++)). + size(volume.getSize().intValue()). + type(VolumeType.HDD); + + String volumeId = (String) provisioningManager.provision(jobFactory.create(dataCenterId, new Supplier<Object>() { + + @Override + public Object get() { + return api.volumeApi().createVolume(request.build()).id(); + } + })); + + volumeIds.add(volumeId); + logger.trace(">> provisioning complete for volume. returned id='%s'", volumeId); + } catch (Exception ex) { + if (i - 1 == 1) // if first volume (one with image) provisioning fails; stop method + { + throw Throwables.propagate(ex); + } + logger.warn(ex, ">> failed to provision volume. skipping.."); + } + } + + String volumeBootDeviceId = Iterables.get(volumeIds, 0); // must have atleast 1 + waitVolumeUntilAvailable.apply(VolumeRef.create(dataCenterId, volumeBootDeviceId)); + + // provision server + final String serverId; + Double cores = ComputeServiceUtils.getCores(hardware); + + Server.BootVolume bootVolume = Server.BootVolume.create(volumeBootDeviceId); + + try { + final Server.Request.CreatePayload serverRequest = Server.Request.creatingBuilder() + .dataCenterId(dataCenterId) + .name(name) + .bootVolume(bootVolume) + .cores(cores.intValue()) + .ram(hardware.getRam()) + .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).id(); + } + })); + logger.trace(">> provisioning complete for server. returned id='%s'", serverId); + + } catch (Exception ex) { + logger.error(ex, ">> failed to provision server. rollbacking.."); + destroyVolumes(volumeIds, dataCenterId); + throw Throwables.propagate(ex); + } + + waitServerUntilAvailable.apply(ServerRef.create(dataCenterId, serverId)); + waitDcUntilAvailable.apply(dataCenterId); + + //attach bootVolume to Server + api.serverApi().attachVolume(Server.Request.attachVolumeBuilder() + .dataCenterId(dataCenterId) + .serverId(serverId) + .volumeId(bootVolume.id()) + .build()); + + waitServerUntilAvailable.apply(ServerRef.create(dataCenterId, serverId)); + waitDcUntilAvailable.apply(dataCenterId); + + //fetch an existing lan and creat if non was found + Lan lan = null; + + List<Lan> lans = api.lanApi().list(dataCenterId); + if (lans != null && !lans.isEmpty()) { + lan = FluentIterable.from(lans).firstMatch(new Predicate<Lan>() { + @Override + public boolean apply(Lan input) { + input = api.lanApi().get(dataCenterId, input.id(), new DepthOptions().depth(3)); + return input.properties().isPublic(); + } + }).orNull(); + } + if (lan == null) { + logger.warn("Could not find an existing lan Creating one...."); + lan = api.lanApi().create(Lan.Request.creatingBuilder() + .dataCenterId(dataCenterId) + .isPublic(Boolean.TRUE) + .name("lan " + name) + .build()); + } + + //add a NIC to the server + 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); + } + } + + Nic nic = api.nicApi().create(Nic.Request.creatingBuilder() + .dataCenterId(dataCenterId) + .name("jclouds" + name) + .dhcp(Boolean.TRUE) + .lan(lanId) + .firewallActive(Boolean.FALSE) + .serverId(serverId). + build()); + + waitNICUntilAvailable.apply(NicRef.create(dataCenterId, serverId, nic.id())); + waitDcUntilAvailable.apply(dataCenterId); + waitServerUntilAvailable.apply(ServerRef.create(dataCenterId, serverId)); + + //connect the rest of volumes to server;delete if fails + final int volumeCount = volumeIds.size(); + for (int j = 1; j < volumeCount; j++) { // skip first; already connected + final String volumeId = volumeIds.get(j); + try { + logger.trace("<< connecting volume '%s' to server '%s'", volumeId, serverId); + provisioningManager.provision(jobFactory.create(group, new Supplier<Object>() { + + @Override + public Object get() { + return api.serverApi().attachVolume( + Server.Request.attachVolumeBuilder() + .dataCenterId(dataCenterId) + .serverId(serverId) + .volumeId(volumeId) + .build() + ); + } + })); + + logger.trace(">> volume connected."); + } catch (Exception ex) { + try { + // delete unconnected volume + logger.warn(ex, ">> failed to connect volume '%s'. deleting..", volumeId); + destroyVolume(volumeId, dataCenterId); + logger.warn(ex, ">> rolling back server..", serverId); + destroyServer(serverId, dataCenterId); + throw ex; + } catch (Exception ex1) { + logger.error(ex, ">> failed to rollback"); + } + } + } + waitDcUntilAvailable.apply(dataCenterId); + waitServerUntilAvailable.apply(ServerRef.create(dataCenterId, serverId)); + + LoginCredentials serverCredentials = LoginCredentials.builder() + .user(loginUser) + .password(password) + .build(); + + String serverInDataCenterId = DataCenterAndId.fromDataCenterAndId(dataCenterId, serverId).slashEncode(); + ServerInDataCenter server = getNode(serverInDataCenterId); + + return new NodeAndInitialCredentials<ServerInDataCenter>(server, serverInDataCenterId, serverCredentials); + } + + @Override + public Iterable<Hardware> listHardwareProfiles() { + // Max [cores=48] [disk size per volume=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().getList(new DepthOptions().depth(1)), new Predicate<Image>() { + + @Override + public boolean apply(Image image) { + return image.properties().imageType() == 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().list(new DepthOptions().depth(1)); + 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.properties().name()); + return image; + } + // try search snapshots + logger.trace("<< not found from images. searching for snapshot with id=%s", id); + Snapshot snapshot = api.snapshotApi().get(id); + if (snapshot != null) { + logger.trace(">> found snapshot [%s]", snapshot.properties().name()); + return snapshot; + } + throw new ResourceNotFoundException("No image/snapshot with id '" + id + "' was found"); + } + + @Override + public Iterable<Location> listLocations() { + // Will never be called + throw new UnsupportedOperationException("Locations are configured in jclouds properties"); + } + + @Override + public ServerInDataCenter getNode(String id) { + DataCenterAndId datacenterAndId = DataCenterAndId.fromSlashEncoded(id); + logger.trace("<< searching for server with id=%s", id); + + Server server = api.serverApi().getServer(datacenterAndId.getDataCenter(), datacenterAndId.getId(), new DepthOptions().depth(3)); + if (server != null) { + logger.trace(">> found server [%s]", server.properties().name()); + } + return server == null ? null : new ServerInDataCenter(server, datacenterAndId.getDataCenter()); + } + + @Override + public void destroyNode(String nodeId) { + DataCenterAndId datacenterAndId = DataCenterAndId.fromSlashEncoded(nodeId); + ServerApi serverApi = api.serverApi(); + Server server = serverApi.getServer(datacenterAndId.getDataCenter(), datacenterAndId.getId(), new DepthOptions().depth(5)); + if (server != null) { + for (org.apache.jclouds.profitbricks.rest.domain.Volume volume : server.entities().volumes().items()) { + destroyVolume(volume.id(), datacenterAndId.getDataCenter()); + } + + try { + destroyServer(datacenterAndId.getId(), datacenterAndId.getDataCenter()); + } catch (Exception ex) { + logger.warn(ex, ">> failed to delete server with id=%s", datacenterAndId.getId()); + } + } + } + + @Override + public void rebootNode(final String id) { + final DataCenterAndId datacenterAndId = DataCenterAndId.fromSlashEncoded(id); + // Fail pre-emptively if not found + final ServerInDataCenter node = getRequiredNode(id); + provisioningManager.provision(jobFactory.create(datacenterAndId.getDataCenter(), new Supplier<Object>() { + + @Override + public Object get() { + api.serverApi().rebootServer(datacenterAndId.getDataCenter(), datacenterAndId.getId()); + waitServerUntilRunning.apply(ServerRef.create(datacenterAndId.getDataCenter(), datacenterAndId.getId())); + return node; + } + })); + } + + @Override + public void resumeNode(final String id) { + final DataCenterAndId datacenterAndId = DataCenterAndId.fromSlashEncoded(id); + final ServerInDataCenter node = getRequiredNode(id); + if (node.getServer().properties().vmState() == Server.Status.RUNNING) { + return; + } + + provisioningManager.provision(jobFactory.create(datacenterAndId.getDataCenter(), new Supplier<Object>() { + + @Override + public Object get() { + api.serverApi().startServer(datacenterAndId.getDataCenter(), datacenterAndId.getId()); + waitServerUntilRunning.apply(ServerRef.create(datacenterAndId.getDataCenter(), datacenterAndId.getId())); + return node; + } + })); + } + + @Override + public void suspendNode(final String id) { + final DataCenterAndId datacenterAndId = DataCenterAndId.fromSlashEncoded(id); + final ServerInDataCenter 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.getServer().properties().vmState() == Server.Status.SHUTOFF) { + return; + } + provisioningManager.provision(jobFactory.create(datacenterAndId.getDataCenter(), new Supplier<Object>() { + @Override + public Object get() { + api.serverApi().stopServer(datacenterAndId.getDataCenter(), datacenterAndId.getId()); + waitServerUntilSuspended.apply(ServerRef.create(datacenterAndId.getDataCenter(), datacenterAndId.getId())); + return node; + } + })); + } + + @Override + public Iterable<ServerInDataCenter> listNodes() { + logger.trace("<< fetching servers.."); + datacetners = api.dataCenterApi().list(); + List<ServerInDataCenter> servers = new ArrayList<ServerInDataCenter>(); + for (DataCenter dataCenter : datacetners) { + + List<Server> serversInDataCenter = api.serverApi().getList(dataCenter.id(), new DepthOptions().depth(4)); + for (Server server : serversInDataCenter) { + servers.add(new ServerInDataCenter(server, dataCenter.id())); + } + } + logger.trace("<< fetching servers.."); + return servers; + } + + @Override + public Iterable<ServerInDataCenter> listNodesByIds(final Iterable<String> ids) { + return filter(listNodes(), new Predicate<ServerInDataCenter>() { + @Override + public boolean apply(ServerInDataCenter server) { + return contains(ids, server.slashEncode()); + } + }); + } + + 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(dataCenterId, 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 destroyVolumes(List<String> volumeIds, String dataCenterId) { + for (String volumeId : volumeIds) { + destroyVolume(volumeId, dataCenterId); + } + } + + private void destroyVolume(final String volumeId, final String dataCenterId) { + try { + logger.trace("<< deleting volume with id=%s", volumeId); + provisioningManager.provision(jobFactory.create(dataCenterId, new Supplier<Object>() { + + @Override + public Object get() { + api.volumeApi().deleteVolume(dataCenterId, volumeId); + + return volumeId; + } + })); + logger.trace(">> volume '%s' deleted.", volumeId); + } catch (Exception ex) { + logger.warn(ex, ">> failed to delete volume with id=%s", volumeId); + } + } + + private ServerInDataCenter getRequiredNode(String nodeId) { + ServerInDataCenter 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-labs/blob/5742745e/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/concurrent/ProvisioningJob.java ---------------------------------------------------------------------- diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/concurrent/ProvisioningJob.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/concurrent/ProvisioningJob.java new file mode 100644 index 0000000..af089fa --- /dev/null +++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/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.apache.jclouds.profitbricks.rest.compute.concurrent; + +import static com.google.common.base.Preconditions.checkNotNull; + +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; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_DATACENTER; + +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-labs/blob/5742745e/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/concurrent/ProvisioningManager.java ---------------------------------------------------------------------- diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/concurrent/ProvisioningManager.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/concurrent/ProvisioningManager.java new file mode 100644 index 0000000..061628c --- /dev/null +++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/concurrent/ProvisioningManager.java @@ -0,0 +1,87 @@ +/* + * 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.compute.concurrent; + +import static com.google.common.util.concurrent.Futures.getUnchecked; +import com.google.common.util.concurrent.ListeningExecutorService; +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; + +/** + * 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-labs/blob/5742745e/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/config/ProfitBricksComputeServiceContextModule.java ---------------------------------------------------------------------- diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/config/ProfitBricksComputeServiceContextModule.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/config/ProfitBricksComputeServiceContextModule.java new file mode 100644 index 0000000..15f2112 --- /dev/null +++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/config/ProfitBricksComputeServiceContextModule.java @@ -0,0 +1,330 @@ +/* + * 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.compute.config; + +import com.google.common.base.Function; +import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Predicate; +import com.google.common.util.concurrent.Uninterruptibles; +import com.google.inject.Inject; +import com.google.inject.Provides; +import com.google.inject.Scopes; +import com.google.inject.TypeLiteral; +import com.google.inject.assistedinject.FactoryModuleBuilder; +import java.util.concurrent.TimeUnit; +import javax.inject.Named; +import javax.inject.Singleton; +import org.apache.jclouds.profitbricks.rest.ProfitBricksApi; +import org.apache.jclouds.profitbricks.rest.compute.ProfitBricksComputeServiceAdapter; +import org.apache.jclouds.profitbricks.rest.compute.concurrent.ProvisioningJob; +import org.apache.jclouds.profitbricks.rest.compute.concurrent.ProvisioningManager; +import org.apache.jclouds.profitbricks.rest.compute.function.ProvisionableToImage; +import org.apache.jclouds.profitbricks.rest.compute.function.ServerInDataCenterToNodeMetadata; +import org.apache.jclouds.profitbricks.rest.compute.function.VolumeToVolume; +import org.apache.jclouds.profitbricks.rest.compute.strategy.AssignDataCenterToTemplate; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_MAX_PERIOD; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PERIOD; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_DATACENTER; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_NIC; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_SERVER; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_SNAPSHOT; +import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_TIMEOUT; +import org.apache.jclouds.profitbricks.rest.domain.DataCenter; +import org.apache.jclouds.profitbricks.rest.domain.Nic; +import org.apache.jclouds.profitbricks.rest.domain.Provisionable; +import org.apache.jclouds.profitbricks.rest.domain.Server; +import org.apache.jclouds.profitbricks.rest.domain.State; +import org.apache.jclouds.profitbricks.rest.domain.zonescoped.ServerInDataCenter; +import org.apache.jclouds.profitbricks.rest.ids.NicRef; +import org.apache.jclouds.profitbricks.rest.ids.ServerRef; +import org.apache.jclouds.profitbricks.rest.ids.VolumeRef; +import org.jclouds.compute.ComputeServiceAdapter; +import org.jclouds.compute.config.ComputeServiceAdapterContextModule; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED; +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.compute.domain.internal.ArbitraryCpuRamTemplateBuilderImpl; +import org.jclouds.compute.domain.internal.TemplateBuilderImpl; +import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; +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 static org.jclouds.util.Predicates2.retry; + +public class ProfitBricksComputeServiceContextModule extends + ComputeServiceAdapterContextModule<ServerInDataCenter, Hardware, Provisionable, Location> { + + @Override + protected void configure() { + super.configure(); + + install(new FactoryModuleBuilder().build(ProvisioningJob.Factory.class)); + + bind(ImplicitLocationSupplier.class).to(OnlyLocationOrFirstZone.class).in(Scopes.SINGLETON); + + bind(CreateNodesInGroupThenAddToSet.class).to(AssignDataCenterToTemplate.class).in(Scopes.SINGLETON); + + bind(new TypeLiteral<ComputeServiceAdapter<ServerInDataCenter, Hardware, Provisionable, Location>>() { + }).to(ProfitBricksComputeServiceAdapter.class); + + bind(TemplateBuilderImpl.class).to(ArbitraryCpuRamTemplateBuilderImpl.class); + + bind(new TypeLiteral<Function<ServerInDataCenter, NodeMetadata>>() { + }).to(ServerInDataCenterToNodeMetadata.class); + + bind(new TypeLiteral<Function<Provisionable, Image>>() { + }).to(ProvisionableToImage.class); + + bind(new TypeLiteral<Function<org.apache.jclouds.profitbricks.rest.domain.Volume, Volume>>() { + }).to(VolumeToVolume.class); + + bind(new TypeLiteral<Function<Hardware, Hardware>>() { + }).to(Class.class.cast(IdentityFunction.class)); + } + + @Provides + @Singleton + @Named(POLL_PREDICATE_DATACENTER) + Predicate<String> provideDataCenterAvailablePredicate(final ProfitBricksApi api, ComputeConstants constants) { + return retry(new DataCenterProvisioningStatePredicate( + api, State.AVAILABLE), + constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS); + } + + @Provides + @Named(TIMEOUT_NODE_RUNNING) + Predicate<ServerRef> provideServerRunningPredicate(final ProfitBricksApi api, ComputeConstants constants) { + return retry(new ServerStatusPredicate( + api, Server.Status.RUNNING), + constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS); + } + + @Provides + @Named(TIMEOUT_NODE_SUSPENDED) + Predicate<ServerRef> provideServerSuspendedPredicate(final ProfitBricksApi api, ComputeConstants constants) { + return retry(new ServerStatusPredicate( + api, Server.Status.SHUTOFF), + constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS); + } + + @Provides + @Named(POLL_PREDICATE_SERVER) + Predicate<ServerRef> provideServerAvailablePredicate(final ProfitBricksApi api, ComputeConstants constants) { + return retry(new ServerAvaiblablePredicate( + api, State.AVAILABLE), + constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS); + } + + @Provides + @Singleton + ProvisioningManager provideProvisioningManager(Closer closer) { + ProvisioningManager provisioningManager = new ProvisioningManager(); + closer.addToClose(provisioningManager); + + return provisioningManager; + } + + @Provides + @Singleton + @Named(POLL_PREDICATE_SNAPSHOT) + Predicate<String> provideSnapshotAvailablePredicate(final ProfitBricksApi api, ComputeConstants constants) { + return retry(new SnapshotProvisioningStatePredicate( + api, State.AVAILABLE), + constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS); + } + + @Provides + @Singleton + @Named(TIMEOUT_NODE_RUNNING) + Predicate<VolumeRef> provideVolumeAvailablePredicate(final ProfitBricksApi api, ComputeConstants constants) { + return retry(new VolumeProvisoningStatusPredicate( + api, State.AVAILABLE), + constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS); + } + + @Provides + @Singleton + @Named(POLL_PREDICATE_NIC) + Predicate<NicRef> provideNicAvailablePredicate(final ProfitBricksApi api, ProfitBricksComputeServiceContextModule.ComputeConstants constants) { + return retry(new NicAvailable( + api, State.AVAILABLE), + constants.pollTimeout(), constants.pollPeriod(), constants.pollMaxPeriod(), TimeUnit.SECONDS); + } + + static class DataCenterProvisioningStatePredicate implements Predicate<String> { + + private final ProfitBricksApi api; + private final State expectedState; + + public DataCenterProvisioningStatePredicate(ProfitBricksApi api, State expectedState) { + this.api = checkNotNull(api, "api must not be null"); + this.expectedState = checkNotNull(expectedState, "expectedState must not be null"); + } + + @Override + public boolean apply(String input) { + checkNotNull(input, "datacenter id"); + DataCenter dataCenter = api.dataCenterApi().getDataCenter(input); + return dataCenter.metadata().state() == expectedState; + } + + } + + static class ServerAvaiblablePredicate implements Predicate<ServerRef> { + + private final ProfitBricksApi api; + private final State expectedState; + + public ServerAvaiblablePredicate(ProfitBricksApi api, State expectedState) { + this.api = checkNotNull(api, "api must not be null"); + this.expectedState = checkNotNull(expectedState, "expectedState must not be null"); + } + + @Override + public boolean apply(ServerRef serverRef) { + + checkNotNull(serverRef, "serverRef"); + //give time for the operation to actually start + Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS); + Server server = api.serverApi().getServer(serverRef.dataCenterId(), serverRef.serverId()); + + if (server == null || server.metadata() == null) { + return false; + } + return server.metadata().state().toString().equals(expectedState.toString()); + } + + } + + static class ServerStatusPredicate implements Predicate<ServerRef> { + + private final ProfitBricksApi api; + private final Server.Status expectedStatus; + + public ServerStatusPredicate(ProfitBricksApi api, Server.Status expectedStatus) { + this.api = checkNotNull(api, "api must not be null"); + this.expectedStatus = checkNotNull(expectedStatus, "expectedStatus must not be null"); + } + + @Override + public boolean apply(ServerRef serverRef) { + checkNotNull(serverRef, "serverRef"); + //give time for the operation to actually start + Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS); + Server server = api.serverApi().getServer(serverRef.dataCenterId(), serverRef.serverId()); + + if (server == null || server.properties().vmState() == null) { + return false; + } + + return server.properties().vmState() == expectedStatus; + } + + } + + static class SnapshotProvisioningStatePredicate implements Predicate<String> { + + private final ProfitBricksApi api; + private final State expectedState; + + public SnapshotProvisioningStatePredicate(ProfitBricksApi api, State expectedState) { + this.api = checkNotNull(api, "api must not be null"); + this.expectedState = checkNotNull(expectedState, "expectedState must not be null"); + } + + @Override + public boolean apply(String input) { + checkNotNull(input, "snapshot id"); + return api.snapshotApi().get(input).metadata().state().toString().equals(expectedState.toString()); + } + } + + static class VolumeProvisoningStatusPredicate implements Predicate<VolumeRef> { + + private final ProfitBricksApi api; + private final State expectedState; + + public VolumeProvisoningStatusPredicate(ProfitBricksApi api, State expectedState) { + this.api = checkNotNull(api, "api must not be null"); + this.expectedState = checkNotNull(expectedState, "expectedState must not be null"); + } + + @Override + public boolean apply(VolumeRef input) { + checkNotNull(input, "Volume REF"); + org.apache.jclouds.profitbricks.rest.domain.Volume volume = api.volumeApi().getVolume(input.dataCenterId(), input.volumeId()); + if (volume == null || volume.metadata() == null || volume.metadata().state() == null) { + return false; + } + return volume.metadata().state().toString().equals(expectedState.toString()); + } + } + + static class NicAvailable implements Predicate<NicRef> { + + private final ProfitBricksApi api; + private final State expectedState; + + public NicAvailable(ProfitBricksApi api, State expectedState) { + this.api = checkNotNull(api, "api must not be null"); + this.expectedState = checkNotNull(expectedState, "expectedState must not be null"); + } + + @Override + public boolean apply(NicRef input) { + checkNotNull(input, "NicRef "); + Nic nice = api.nicApi().get(input.dataCenterId(), input.serverId(), input.nicId()); + if (nice == null || nice.metadata() == null || nice.metadata().state() == null) { + return false; + } + return nice.metadata().state().toString().equals(expectedState.toString()); + } + } + + @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-labs/blob/5742745e/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/ProvisionableToImage.java ---------------------------------------------------------------------- diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/ProvisionableToImage.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/ProvisionableToImage.java new file mode 100644 index 0000000..3071b92 --- /dev/null +++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/ProvisionableToImage.java @@ -0,0 +1,245 @@ +/* + * 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.compute.function; + +import com.google.common.base.Function; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Strings; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; +import static com.google.common.collect.Iterables.find; +import com.google.inject.Inject; +import java.util.Set; +import java.util.regex.Pattern; +import static org.apache.jclouds.profitbricks.rest.domain.Image.Type.CDROM; +import org.apache.jclouds.profitbricks.rest.domain.LicenceType; +import org.apache.jclouds.profitbricks.rest.domain.Provisionable; +import org.apache.jclouds.profitbricks.rest.domain.Snapshot; +import org.jclouds.collect.Memoized; +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 static org.jclouds.location.predicates.LocationPredicates.idEquals; + +public class ProvisionableToImage implements Function<Provisionable, Image> { + + public static final String KEY_PROVISIONABLE_TYPE = "provisionableType"; + + private final ImageToImage fnImageToImage; + private final SnapshotToImage fnSnapshotToImage; + + @Inject + ProvisionableToImage(@Memoized Supplier<Set<? extends Location>> locations) { + this.fnImageToImage = new ImageToImage(locations); + this.fnSnapshotToImage = new SnapshotToImage(locations); + } + + @Override + public Image apply(Provisionable input) { + checkNotNull(input, "Cannot convert null input"); + + if (input instanceof org.apache.jclouds.profitbricks.rest.domain.Image) { + return fnImageToImage.apply((org.apache.jclouds.profitbricks.rest.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(LicenceType 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 ImageFunction<org.apache.jclouds.profitbricks.rest.domain.Image> { + + private static final Pattern HAS_NUMBERS = Pattern.compile(".*\\d+.*"); + + private final Supplier<Set<? extends Location>> locations; + + ImageToImage(Supplier<Set<? extends Location>> locations) { + this.locations = locations; + } + + @Override + public Image apply(org.apache.jclouds.profitbricks.rest.domain.Image from) { + String desc = from.properties().name(); + OsFamily osFamily = parseOsFamily(desc, from.properties().licenceType()); + Location location = find(locations.get(), idEquals(from.properties().location().getId())); + + OperatingSystem os = OperatingSystem.builder() + .description(osFamily.value()) + .family(osFamily) + .version(parseVersion(desc)) + .is64Bit(is64Bit(desc, from.properties().imageType())) + .build(); + + return addTypeMetadata(new ImageBuilder() + .ids(from.id()) + .name(desc) + .location(location) + .status(Image.Status.AVAILABLE) + .operatingSystem(os)) + .build(); + } + + private OsFamily parseOsFamily(String from, LicenceType 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.apache.jclouds.profitbricks.rest.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; + } + } + + @Override + public ImageBuilder addTypeMetadata(ImageBuilder builder) { + return builder.userMetadata(ImmutableMap.of(KEY_PROVISIONABLE_TYPE, Provisionable.Type.IMAGE.toString())); + } + } + + private static class SnapshotToImage implements ImageFunction<Snapshot> { + + private final Supplier<Set<? extends Location>> locations; + + SnapshotToImage(Supplier<Set<? extends Location>> locations) { + this.locations = locations; + } + + @Override + public Image apply(Snapshot from) { + String textToParse = from.properties().name() + from.properties().description(); + OsFamily osFamily = parseOsFamily(textToParse, from.properties().licenceType()); + Location location = find(locations.get(), idEquals(from.properties().location().getId())); + + OperatingSystem os = OperatingSystem.builder() + .description(osFamily.value()) + .family(osFamily) + .is64Bit(true) + .version("00.00") + .build(); + + return addTypeMetadata(new ImageBuilder() + .ids(from.id()) + .name(from.properties().name()) + .description(from.properties().description()) + .location(location) + .status(mapStatus(from.metadata().state())) + .operatingSystem(os)) + .build(); + } + + private OsFamily parseOsFamily(String text, LicenceType 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(org.apache.jclouds.profitbricks.rest.domain.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; + } + } + + @Override + public ImageBuilder addTypeMetadata(ImageBuilder builder) { + return builder.userMetadata(ImmutableMap.of(KEY_PROVISIONABLE_TYPE, org.apache.jclouds.profitbricks.rest.domain.Provisionable.Type.SNAPSHOT.toString())); + } + } + + private interface ImageFunction<T extends Provisionable> extends Function<T, Image> { + + ImageBuilder addTypeMetadata(ImageBuilder builder); + + } +} http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/5742745e/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/ServerInDataCenterToNodeMetadata.java ---------------------------------------------------------------------- diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/ServerInDataCenterToNodeMetadata.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/ServerInDataCenterToNodeMetadata.java new file mode 100644 index 0000000..e7f0cf0 --- /dev/null +++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/ServerInDataCenterToNodeMetadata.java @@ -0,0 +1,174 @@ +/* + * 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.compute.function; + +import com.google.common.base.Function; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.not; +import com.google.common.base.Supplier; +import com.google.common.collect.Iterables; +import static com.google.common.collect.Iterables.find; +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import java.util.List; +import java.util.Set; +import org.apache.jclouds.profitbricks.rest.ProfitBricksApi; +import org.apache.jclouds.profitbricks.rest.domain.DataCenter; +import org.apache.jclouds.profitbricks.rest.domain.LicenceType; +import org.apache.jclouds.profitbricks.rest.domain.Nic; +import org.apache.jclouds.profitbricks.rest.domain.Server; +import org.apache.jclouds.profitbricks.rest.domain.zonescoped.ServerInDataCenter; +import org.jclouds.collect.Memoized; +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.compute.functions.GroupNamingConvention; +import org.jclouds.domain.Location; +import static org.jclouds.location.predicates.LocationPredicates.idEquals; +import org.jclouds.util.InetAddresses2; + +public class ServerInDataCenterToNodeMetadata implements Function<ServerInDataCenter, NodeMetadata> { + + private final Function<org.apache.jclouds.profitbricks.rest.domain.Volume, Volume> fnVolume; + private final Supplier<Set<? extends Location>> locations; + private final Function<List<Nic>, List<String>> fnCollectIps; + private final ProfitBricksApi api; + + private final GroupNamingConvention groupNamingConvention; + + @Inject + public ServerInDataCenterToNodeMetadata(Function<org.apache.jclouds.profitbricks.rest.domain.Volume, Volume> fnVolume, + @Memoized Supplier<Set<? extends Location>> locations, + ProfitBricksApi api, + GroupNamingConvention.Factory groupNamingConvention) { + this.fnVolume = fnVolume; + this.locations = locations; + this.api = api; + 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.properties().ips()); + } + return ips; + } + }; + } + + @Override + public NodeMetadata apply(final ServerInDataCenter serverInDataCenter) { + Server server = serverInDataCenter.getServer(); + checkNotNull(server, "Null server"); + + // Location is not populated in the datacenter on a server response + DataCenter dataCenter = api.dataCenterApi().getDataCenter(serverInDataCenter.getDataCenter()); + Location location = find(locations.get(), idEquals(dataCenter.properties().location().getId())); + + float size = 0f; + List<Volume> volumes = Lists.newArrayList(); + List<org.apache.jclouds.profitbricks.rest.domain.Volume> storages = server.entities().volumes().items(); + if (storages != null) { + for (org.apache.jclouds.profitbricks.rest.domain.Volume storage : storages) { + size += storage.properties().size(); + volumes.add(fnVolume.apply(storage)); + } + } + + // Build hardware + String id = String.format("cpu=%d,ram=%d,disk=%.0f", server.properties().cores(), server.properties().ram(), size); + Hardware hardware = new HardwareBuilder() + .ids(id) + .name(id) + .ram(server.properties().ram()) + .processor(new Processor(server.properties().cores(), 1d)) + .hypervisor("kvm") + .volumes(volumes) + .location(location) + .build(); + + // Collect ips + List<String> addresses = fnCollectIps.apply(server.entities().nics().items()); + OperatingSystem os = null; + if (server.properties() != null && server.properties().bootVolume() != null && server.properties().bootVolume().properties() != null) { + os = mapOsType(server.properties().bootVolume().properties().licenceType()); + + } + // Build node + NodeMetadataBuilder nodeBuilder = new NodeMetadataBuilder(); + nodeBuilder.ids(serverInDataCenter.slashEncode()) + .group(groupNamingConvention.extractGroup(server.properties().name())) + .name(server.properties().name()) + .backendStatus(server.metadata().state().toString()) + .status(mapStatus(server.properties().vmState())) + .hardware(hardware) + .operatingSystem(os) + .location(location) + .privateAddresses(Iterables.filter(addresses, InetAddresses2.IsPrivateIPAddress.INSTANCE)) + .publicAddresses(Iterables.filter(addresses, not(InetAddresses2.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(LicenceType 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-labs/blob/5742745e/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/VolumeToVolume.java ---------------------------------------------------------------------- diff --git a/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/VolumeToVolume.java b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/VolumeToVolume.java new file mode 100644 index 0000000..5e2e8ee --- /dev/null +++ b/profitbricks-rest/src/main/java/org/apache/jclouds/profitbricks/rest/compute/function/VolumeToVolume.java @@ -0,0 +1,44 @@ +/* + * 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.compute.function; + +import com.google.common.base.Function; +import static com.google.common.base.Preconditions.checkNotNull; +import org.jclouds.compute.domain.Volume; +import org.jclouds.compute.domain.VolumeBuilder; + +public class VolumeToVolume implements Function<org.apache.jclouds.profitbricks.rest.domain.Volume, Volume> { + + @Override + public Volume apply(org.apache.jclouds.profitbricks.rest.domain.Volume volume) { + checkNotNull(volume, "Null storage"); + + String device = ""; + if (volume.properties().deviceNumber() != null) { + device = volume.properties().deviceNumber().toString(); + } + + return new VolumeBuilder() + .id(volume.id()) + .size((float) volume.properties().size()) + .device(device) + .durable(true) + .type(Volume.Type.LOCAL) + .build(); + + } +}
