You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2016/08/11 22:46:03 UTC

[4/4] jclouds git commit: JCLOUDS-482: Add support for arbitrary CPU and RAM

JCLOUDS-482: Add support for arbitrary CPU and RAM

This is a combination of 16 commits:

* First approach to ArbitraryCpuRamTemplateBuilderImpl
* Several fixes: refactoring some names, format, identation problems, some missing license headers and generateId method
* Refactored parse utility
* Added GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl to support GCE custom machine URI
* extracted hardware creation to automaticHardwareForCpuAndRam method
* Fixed ide automatic asterisk imports
* correcting WIP base case PR according to comments
* added machineTypeUriToHardware to set custom hardware in nodes
* fix checkstyle violations and other PR comments
* Set the providerId to custom machineType URI and fix adding node log
* Arbitrary hardware tests added to BaseTemplateBuilderLiveTest and GoogleComputeEngineTemplateBuilderLiveTest
* Added two more tests to BaseTemplateBuilderLiveTest
* Move repeated constants to TestUtils to reuse code
* Fix full path in the Hardware id and URI
* Add custom hardware tests to BaseComputeServiceLiveTest and GCEServiceLiveTest
* Change customHardware test to use buildTemplate and fix identation


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

Branch: refs/heads/gsoc2016-ivan
Commit: 81e348561d404e547fccce4b1217108b2c1419c4
Parents: 449c6d8
Author: Iv�n Lomba <iv...@gmail.com>
Authored: Sat Jun 18 20:27:12 2016 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri Aug 12 00:45:32 2016 +0200

----------------------------------------------------------------------
 .../filesystem/FilesystemBlobStoreTest.java     |   6 +-
 .../FilesystemContainerIntegrationTest.java     |  10 +-
 .../FilesystemStorageStrategyImplTest.java      |   6 +-
 .../org/jclouds/filesystem/utils/TestUtils.java |   3 -
 .../oauth/v2/AuthorizationApiLiveTest.java      |   8 +-
 .../org/jclouds/oauth/v2/OAuthTestUtils.java    |   3 -
 .../ArbitraryCpuRamTemplateBuilderImpl.java     |  84 ++++++
 .../domain/internal/TemplateBuilderImpl.java    |  83 +++---
 ...desWithGroupEncodedIntoNameThenAddToSet.java |   4 +-
 .../compute/util/AutomaticHardwareIdSpec.java   |  72 +++++
 .../ArbitraryCpuRamTemplateBuilderImplTest.java | 275 ++++++++++++++++++
 .../internal/BaseComputeServiceLiveTest.java    |  25 ++
 .../internal/BaseTemplateBuilderLiveTest.java   | 103 +++++++
 .../util/AutomaticHardwareIdSpecTest.java       |  52 ++++
 .../test/java/org/jclouds/utils/TestUtils.java  |   3 +
 ...GoogleComputeEngineServiceContextModule.java |   4 +
 ...ngineArbitraryCpuRamTemplateBuilderImpl.java |  59 ++++
 .../functions/InstanceToNodeMetadata.java       |  46 ++-
 .../GoogleComputeEngineServiceLiveTest.java     |  23 +-
 ...gleComputeEngineTemplateBuilderLiveTest.java |  15 +
 ...eArbitraryCpuRamTemplateBuilderImplTest.java | 278 +++++++++++++++++++
 .../functions/InstanceToNodeMetadataTest.java   |  39 ++-
 22 files changed, 1123 insertions(+), 78 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemBlobStoreTest.java
----------------------------------------------------------------------
diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemBlobStoreTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemBlobStoreTest.java
index 88704e3..d27924f 100644
--- a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemBlobStoreTest.java
+++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemBlobStoreTest.java
@@ -18,6 +18,8 @@ package org.jclouds.filesystem;
 
 import static com.google.common.io.BaseEncoding.base16;
 import static org.jclouds.filesystem.util.Utils.isMacOSX;
+import static org.jclouds.utils.TestUtils.NO_INVOCATIONS;
+import static org.jclouds.utils.TestUtils.SINGLE_NO_ARG_INVOCATION;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
@@ -912,7 +914,7 @@ public class FilesystemBlobStoreTest {
 
     @DataProvider
     public Object[][] ignoreOnMacOSX() {
-        return isMacOSX() ? TestUtils.NO_INVOCATIONS
-                : TestUtils.SINGLE_NO_ARG_INVOCATION;
+        return isMacOSX() ? NO_INVOCATIONS
+                : SINGLE_NO_ARG_INVOCATION;
     }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemContainerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemContainerIntegrationTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemContainerIntegrationTest.java
index 7a701a0..7dfdd3a 100644
--- a/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemContainerIntegrationTest.java
+++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemContainerIntegrationTest.java
@@ -19,6 +19,8 @@ package org.jclouds.filesystem.integration;
 import static org.jclouds.blobstore.options.ListContainerOptions.Builder.maxResults;
 import static org.jclouds.filesystem.util.Utils.isMacOSX;
 import static org.testng.Assert.assertEquals;
+import static org.jclouds.utils.TestUtils.NO_INVOCATIONS;
+import static org.jclouds.utils.TestUtils.SINGLE_NO_ARG_INVOCATION;
 
 import java.io.IOException;
 import java.util.Properties;
@@ -165,14 +167,14 @@ public class FilesystemContainerIntegrationTest extends BaseContainerIntegration
 
    @DataProvider
    public Object[][] ignoreOnMacOSX() {
-      return isMacOSX() ? TestUtils.NO_INVOCATIONS
-            : TestUtils.SINGLE_NO_ARG_INVOCATION;
+      return isMacOSX() ? NO_INVOCATIONS
+            : SINGLE_NO_ARG_INVOCATION;
    }
 
    @DataProvider
    public Object[][] ignoreOnWindows() {
-      return TestUtils.isWindowsOs() ? TestUtils.NO_INVOCATIONS
-            : TestUtils.SINGLE_NO_ARG_INVOCATION;
+      return TestUtils.isWindowsOs() ? NO_INVOCATIONS
+            : SINGLE_NO_ARG_INVOCATION;
    }
 
    @Override

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/apis/filesystem/src/test/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImplTest.java
----------------------------------------------------------------------
diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImplTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImplTest.java
index 3fdd855..9079fa0 100644
--- a/apis/filesystem/src/test/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImplTest.java
+++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImplTest.java
@@ -17,6 +17,8 @@
 package org.jclouds.filesystem.strategy.internal;
 
 import static org.jclouds.filesystem.util.Utils.isMacOSX;
+import static org.jclouds.utils.TestUtils.NO_INVOCATIONS;
+import static org.jclouds.utils.TestUtils.SINGLE_NO_ARG_INVOCATION;
 import static org.jclouds.utils.TestUtils.randomByteSource;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
@@ -699,7 +701,7 @@ public class FilesystemStorageStrategyImplTest {
 
    @DataProvider
    public Object[][] ignoreOnMacOSX() {
-        return isMacOSX() ? TestUtils.NO_INVOCATIONS
-                : TestUtils.SINGLE_NO_ARG_INVOCATION;
+        return isMacOSX() ? NO_INVOCATIONS
+                : SINGLE_NO_ARG_INVOCATION;
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/apis/filesystem/src/test/java/org/jclouds/filesystem/utils/TestUtils.java
----------------------------------------------------------------------
diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/utils/TestUtils.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/utils/TestUtils.java
index cd0276c..224c21e 100644
--- a/apis/filesystem/src/test/java/org/jclouds/filesystem/utils/TestUtils.java
+++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/utils/TestUtils.java
@@ -54,9 +54,6 @@ public class TestUtils {
     private static final Iterator<File> IMAGE_RESOURCES_ITERATOR =
             Iterators.cycle(IMAGE_RESOURCES);
 
-    public static final Object[][] NO_INVOCATIONS = new Object[0][0];
-    public static final Object[][] SINGLE_NO_ARG_INVOCATION = { new Object[0] };
-
     /**
      * Generate a random blob key simple name (with no path in the key)
      * @return 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/apis/oauth/src/test/java/org/jclouds/oauth/v2/AuthorizationApiLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/test/java/org/jclouds/oauth/v2/AuthorizationApiLiveTest.java b/apis/oauth/src/test/java/org/jclouds/oauth/v2/AuthorizationApiLiveTest.java
index 5d0d7cf..0aba641 100644
--- a/apis/oauth/src/test/java/org/jclouds/oauth/v2/AuthorizationApiLiveTest.java
+++ b/apis/oauth/src/test/java/org/jclouds/oauth/v2/AuthorizationApiLiveTest.java
@@ -24,6 +24,8 @@ import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
 import static org.jclouds.oauth.v2.config.OAuthProperties.CERTIFICATE;
 import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
 import static org.jclouds.providers.AnonymousProviderMetadata.forApiOnEndpoint;
+import static org.jclouds.utils.TestUtils.NO_INVOCATIONS;
+import static org.jclouds.utils.TestUtils.SINGLE_NO_ARG_INVOCATION;
 import static org.testng.Assert.assertNotNull;
 
 import java.util.Properties;
@@ -63,19 +65,19 @@ public class AuthorizationApiLiveTest extends BaseApiLiveTest<AuthorizationApi>
    @DataProvider
    public Object[][] onlyRunForP12PrivateKeyCredentials() {
       return (CredentialType.fromValue(credentialType) == CredentialType.P12_PRIVATE_KEY_CREDENTIALS) ?
-            OAuthTestUtils.SINGLE_NO_ARG_INVOCATION : OAuthTestUtils.NO_INVOCATIONS;
+            SINGLE_NO_ARG_INVOCATION : NO_INVOCATIONS;
    }
 
    @DataProvider
    public Object[][] onlyRunForClientCredentialsSecret() {
       return (CredentialType.fromValue(credentialType) == CredentialType.CLIENT_CREDENTIALS_SECRET) ?
-              OAuthTestUtils.SINGLE_NO_ARG_INVOCATION : OAuthTestUtils.NO_INVOCATIONS;
+              SINGLE_NO_ARG_INVOCATION : NO_INVOCATIONS;
    }
 
    @DataProvider
    public Object[][] onlyRunForClientCredentialsP12() {
       return (CredentialType.fromValue(credentialType) == CredentialType.CLIENT_CREDENTIALS_P12_AND_CERTIFICATE) ?
-              OAuthTestUtils.SINGLE_NO_ARG_INVOCATION : OAuthTestUtils.NO_INVOCATIONS;
+              SINGLE_NO_ARG_INVOCATION : NO_INVOCATIONS;
    }
 
    @Test(dataProvider = "onlyRunForP12PrivateKeyCredentials")

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/apis/oauth/src/test/java/org/jclouds/oauth/v2/OAuthTestUtils.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/test/java/org/jclouds/oauth/v2/OAuthTestUtils.java b/apis/oauth/src/test/java/org/jclouds/oauth/v2/OAuthTestUtils.java
index d15f8b0..920ff18 100644
--- a/apis/oauth/src/test/java/org/jclouds/oauth/v2/OAuthTestUtils.java
+++ b/apis/oauth/src/test/java/org/jclouds/oauth/v2/OAuthTestUtils.java
@@ -32,9 +32,6 @@ import com.google.common.io.Files;
 
 public class OAuthTestUtils {
 
-   public static final Object[][] NO_INVOCATIONS = new Object[0][0];
-   public static final Object[][] SINGLE_NO_ARG_INVOCATION = { new Object[0] };
-
    public static Properties defaultProperties(Properties properties) {
       try {
          properties = properties == null ? new Properties() : properties;

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/compute/src/main/java/org/jclouds/compute/domain/internal/ArbitraryCpuRamTemplateBuilderImpl.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/domain/internal/ArbitraryCpuRamTemplateBuilderImpl.java b/compute/src/main/java/org/jclouds/compute/domain/internal/ArbitraryCpuRamTemplateBuilderImpl.java
new file mode 100644
index 0000000..f99b991
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/domain/internal/ArbitraryCpuRamTemplateBuilderImpl.java
@@ -0,0 +1,84 @@
+/*
+ * 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.compute.domain.internal;
+
+import com.google.common.base.Supplier;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.util.AutomaticHardwareIdSpec;
+import org.jclouds.domain.Location;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import static org.jclouds.compute.util.AutomaticHardwareIdSpec.automaticHardwareIdSpecBuilder;
+import static org.jclouds.compute.util.AutomaticHardwareIdSpec.isAutomaticId;
+import static org.jclouds.compute.util.AutomaticHardwareIdSpec.parseId;
+
+public class ArbitraryCpuRamTemplateBuilderImpl extends TemplateBuilderImpl {
+   @Inject
+   protected ArbitraryCpuRamTemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
+         @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
+         Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
+         @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider) {
+      super(locations, images, hardwares, defaultLocation, optionsProvider, defaultTemplateProvider);
+   }
+
+   protected Hardware automaticHardwareForCpuAndRam(double cores, int ram) {
+      return new HardwareBuilder()
+            .id(automaticHardwareIdSpecBuilder(cores, ram).toString())
+            .ram(ram)
+            .processor(new Processor(cores, 1.0))
+            .build();
+   }
+
+   protected Hardware findHardwareWithId(Set<? extends Hardware> hardwaresToSearch) {
+      try {
+         return super.findHardwareWithId(hardwaresToSearch);
+      } catch (NoSuchElementException ex) {
+         if (isAutomaticId(hardwareId)) {
+            AutomaticHardwareIdSpec spec = parseId(hardwareId);
+            return automaticHardwareForCpuAndRam(spec.getCores(), spec.getRam());
+         }
+         else {
+            throw ex;
+         }
+        }
+    }
+
+   protected Hardware resolveHardware(Set<? extends Hardware> hardwarel, final Iterable<? extends Image> images) {
+      try {
+         return super.resolveHardware(hardwarel, images);
+      }
+      catch (NoSuchElementException ex) {
+         if (super.minCores != 0 && super.minRam != 0) {
+            return automaticHardwareForCpuAndRam(minCores, minRam);
+         }
+         else throw new IllegalArgumentException("No hardware profile matching the given criteria was found. If " +
+               "you want to use exact values, please set the minCores and minRam values", ex);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
index 2865409..f860674 100644
--- a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
+++ b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
@@ -16,33 +16,20 @@
  */
 package org.jclouds.compute.domain.internal;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Predicates.and;
-import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.find;
-import static com.google.common.collect.Iterables.size;
-import static com.google.common.collect.Iterables.transform;
-import static com.google.common.collect.Iterables.tryFind;
-import static com.google.common.collect.Lists.newArrayList;
-import static java.lang.String.format;
-import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
-import static org.jclouds.compute.util.ComputeServiceUtils.getCoresAndSpeed;
-import static org.jclouds.compute.util.ComputeServiceUtils.getSpace;
-
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-import javax.annotation.Resource;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Provider;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Ordering;
+import com.google.common.primitives.Doubles;
 import org.jclouds.collect.Memoized;
 import org.jclouds.compute.domain.ComputeMetadata;
 import org.jclouds.compute.domain.Hardware;
@@ -59,20 +46,31 @@ import org.jclouds.compute.suppliers.ImageCacheSupplier;
 import org.jclouds.domain.Location;
 import org.jclouds.logging.Logger;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.base.Supplier;
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Ordering;
-import com.google.common.primitives.Doubles;
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Predicates.and;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Iterables.size;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Iterables.tryFind;
+import static com.google.common.collect.Lists.newArrayList;
+import static java.lang.String.format;
+import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
+import static org.jclouds.compute.util.ComputeServiceUtils.getCoresAndSpeed;
+import static org.jclouds.compute.util.ComputeServiceUtils.getSpace;
 
 public class TemplateBuilderImpl implements TemplateBuilder {
    @Resource
@@ -348,7 +346,6 @@ public class TemplateBuilderImpl implements TemplateBuilder {
          return "imageDescription(" + imageDescription + ")";
       }
    };
-
    private final Predicate<Hardware> hardwareIdPredicate = new Predicate<Hardware>() {
       @Override
       public boolean apply(Hardware input) {
@@ -727,7 +724,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
       return image.get();
    }
 
-   private Hardware findHardwareWithId(Set<? extends Hardware> hardwaresToSearch) {
+   protected Hardware findHardwareWithId(Set<? extends Hardware> hardwaresToSearch) {
       Hardware hardware;
       // TODO: switch to GetHardwareStrategy in version 1.5
       hardware = tryFind(hardwaresToSearch, hardwareIdPredicate).orNull();

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/compute/src/main/java/org/jclouds/compute/strategy/impl/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/strategy/impl/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java b/compute/src/main/java/org/jclouds/compute/strategy/impl/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java
index e7f3384..6ac36c7 100644
--- a/compute/src/main/java/org/jclouds/compute/strategy/impl/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java
+++ b/compute/src/main/java/org/jclouds/compute/strategy/impl/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java
@@ -33,6 +33,7 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import com.google.common.base.Objects;
 import org.jclouds.Constants;
 import org.jclouds.compute.config.CustomizationResponse;
 import org.jclouds.compute.domain.ComputeMetadata;
@@ -75,7 +76,8 @@ public class CreateNodesWithGroupEncodedIntoNameThenAddToSet implements CreateNo
       public AtomicReference<NodeMetadata> call() throws Exception {
          NodeMetadata node = null;
          logger.debug(">> adding node location(%s) name(%s) image(%s) hardware(%s)", template.getLocation().getId(),
-                  name, template.getImage().getProviderId(), template.getHardware().getProviderId());
+               name, Objects.firstNonNull(template.getImage().getProviderId(), template.getImage().getId()),
+               Objects.firstNonNull(template.getHardware().getProviderId(), template.getHardware().getId()));
          node = addNodeWithGroupStrategy.createNodeWithGroupEncodedIntoName(group, name, template);
          logger.debug("<< %s node(%s)", formatStatus(node), node.getId());
          return new AtomicReference<NodeMetadata>(node);

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/compute/src/main/java/org/jclouds/compute/util/AutomaticHardwareIdSpec.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/util/AutomaticHardwareIdSpec.java b/compute/src/main/java/org/jclouds/compute/util/AutomaticHardwareIdSpec.java
new file mode 100644
index 0000000..6aaa44a
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/util/AutomaticHardwareIdSpec.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.compute.util;
+
+import com.google.common.base.Splitter;
+
+import java.util.Map;
+
+public class AutomaticHardwareIdSpec {
+
+   private double cores;
+   private int ram;
+
+   public static boolean isAutomaticId(String id) {
+      return id.startsWith("automatic:");
+   }
+
+   public static AutomaticHardwareIdSpec parseId(String hardwareId) {
+      AutomaticHardwareIdSpec spec = new AutomaticHardwareIdSpec();
+      String hardwareSpec = hardwareId.substring(10);
+      Map<String, String> specValues = Splitter.on(';')
+            .trimResults()
+            .omitEmptyStrings()
+            .withKeyValueSeparator('=')
+            .split(hardwareSpec);
+      if (!specValues.containsKey("ram") || !specValues.containsKey("cores")) {
+         throw new IllegalArgumentException(String.format("Omitted keys on hardwareId: %s. Please set number " +
+               "of cores and ram amount.", hardwareId));
+      }
+      spec.ram = Integer.parseInt(specValues.get("ram"));
+      spec.cores = Double.parseDouble(specValues.get("cores"));
+      return spec;
+   }
+
+   public static AutomaticHardwareIdSpec automaticHardwareIdSpecBuilder(double cores, int ram) {
+      AutomaticHardwareIdSpec spec = new AutomaticHardwareIdSpec();
+      if (cores == 0 || ram == 0) {
+         throw new IllegalArgumentException(String.format("Omitted or wrong minCores and minRam. If you" +
+               " want to use exact values, please set the minCores and minRam values."));
+      }
+      spec.cores = cores;
+      spec.ram = ram;
+      return spec;
+   }
+
+   @Override
+   public String toString() {
+      return String.format("automatic:cores=%s;ram=%s", cores, ram);
+   }
+
+   public double getCores() {
+      return cores;
+   }
+
+   public int getRam() {
+      return ram;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/compute/src/test/java/org/jclouds/compute/domain/internal/ArbitraryCpuRamTemplateBuilderImplTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/domain/internal/ArbitraryCpuRamTemplateBuilderImplTest.java b/compute/src/test/java/org/jclouds/compute/domain/internal/ArbitraryCpuRamTemplateBuilderImplTest.java
new file mode 100644
index 0000000..568a60b
--- /dev/null
+++ b/compute/src/test/java/org/jclouds/compute/domain/internal/ArbitraryCpuRamTemplateBuilderImplTest.java
@@ -0,0 +1,275 @@
+/*
+ * 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.compute.domain.internal;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Atomics;
+import com.google.inject.Provider;
+import com.google.inject.util.Providers;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.strategy.GetImageStrategy;
+import org.jclouds.compute.suppliers.ImageCacheSupplier;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.rest.AuthorizationException;
+import org.testng.annotations.Test;
+
+import java.net.URI;
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+@Test(groups = "unit", singleThreaded = true, testName = "ArbitraryCpuRamTemplateBuilderImplTest")
+public class ArbitraryCpuRamTemplateBuilderImplTest {
+   private Location provider = new LocationBuilder()
+         .scope(LocationScope.PROVIDER)
+         .id("generic-provider")
+         .description("generic-provider")
+         .build();
+
+   private Location region = new LocationBuilder()
+         .scope(LocationScope.REGION)
+         .id("us-east-1")
+         .description("us-east-1")
+         .parent(provider)
+         .build();
+
+   private OperatingSystem os = OperatingSystem.builder()
+         .name("osName")
+         .version("osVersion")
+         .description("osDescription")
+         .arch("X86_32")
+         .build();
+
+   private Image image = new ImageBuilder()
+         .id("imageId")
+         .providerId("imageId")
+         .name("imageName")
+         .description("imageDescription")
+         .version("imageVersion")
+         .operatingSystem(os)
+         .status(Image.Status.AVAILABLE)
+         .location(null)
+         .build();
+
+   private Hardware hardware = new HardwareBuilder()
+         .ram(2048)
+         .processor(new Processor(2, 1))
+         .id("hardwareId")
+         .name("hardwareName")
+         .location(region)
+         .uri(URI.create("uri"))
+         .build();
+
+   private final String errorMessage = "No hardware profile matching the given criteria was found. " +
+         "If you want to use exact values, please set the minCores and minRam values";
+
+   @Test
+   public void testAutoGeneratedHardwareFromId(){
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+
+      TemplateBuilderImpl templateBuilder = new ArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region),
+            optionsProvider, templateBuilderProvider);
+
+      Hardware hardware = templateBuilder.hardwareId("automatic:cores=2;ram=256").build().getHardware();
+      assertThat(hardware.getRam()).isEqualTo(256);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
+      assertThat(hardware.getId()).isEqualTo("automatic:cores=2.0;ram=256");
+   }
+
+   @Test
+   public void testAutoGeneratedHardwareWithMinCoresAndMinRam(){
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet.of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new ArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minRam(1024);
+      templateBuilder.minCores(4);
+      Template template = templateBuilder.build();
+      Hardware hardware = template.getHardware();
+      assertThat(hardware.getRam()).isEqualTo(1024);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(4.0);
+      assertThat(hardware.getId()).isEqualTo("automatic:cores=4.0;ram=1024");
+   }
+
+   @Test
+   public void testExistingHardwareProfileMatchHardwareProfileWithMinCoresMinRam() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new ArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minCores(2);
+      templateBuilder.minRam(1024);
+      Template template = templateBuilder.build();
+      Hardware hardware = template.getHardware();
+      assertThat(hardware.getRam()).isEqualTo(2048);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
+      assertThat(hardware.getId()).isEqualTo("hardwareId");
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = errorMessage)
+   public void testOnlyRamTest() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet.of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new ArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minRam(4096);
+      templateBuilder.build();
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = errorMessage)
+   public void testOnlyCoresTest() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new ArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minCores(4);
+      templateBuilder.build();
+   }
+
+   @Test
+   public void testOnlyRamMatchHardwareProfileTest() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet.of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new ArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minRam(1024);
+      templateBuilder.build();
+      assertThat(hardware.getRam()).isEqualTo(2048);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
+      assertThat(hardware.getId()).isEqualTo("hardwareId");
+   }
+
+   @Test
+   public void testOnlyCoresMatchHardwareProfileTest() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new ArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minCores(1);
+      templateBuilder.build();
+      assertThat(hardware.getRam()).isEqualTo(2048);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
+      assertThat(hardware.getId()).isEqualTo("hardwareId");
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/compute/src/test/java/org/jclouds/compute/internal/BaseComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/internal/BaseComputeServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/internal/BaseComputeServiceLiveTest.java
index d720538..d598e79 100644
--- a/compute/src/test/java/org/jclouds/compute/internal/BaseComputeServiceLiveTest.java
+++ b/compute/src/test/java/org/jclouds/compute/internal/BaseComputeServiceLiveTest.java
@@ -33,6 +33,7 @@ import static java.lang.String.format;
 import static java.lang.System.currentTimeMillis;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static java.util.logging.Logger.getAnonymousLogger;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.jclouds.Constants.PROPERTY_USER_THREADS;
 import static org.jclouds.compute.options.RunScriptOptions.Builder.nameTask;
 import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript;
@@ -44,6 +45,8 @@ import static org.jclouds.compute.predicates.NodePredicates.inGroup;
 import static org.jclouds.compute.predicates.NodePredicates.runningInGroup;
 import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
 import static org.jclouds.util.Predicates2.retry;
+import static org.jclouds.utils.TestUtils.NO_INVOCATIONS;
+import static org.jclouds.utils.TestUtils.SINGLE_NO_ARG_INVOCATION;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
@@ -81,6 +84,7 @@ import org.jclouds.compute.domain.NodeMetadata.Status;
 import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.domain.internal.ArbitraryCpuRamTemplateBuilderImpl;
 import org.jclouds.compute.util.OpenSocketFinder;
 import org.jclouds.domain.Credentials;
 import org.jclouds.domain.Location;
@@ -96,6 +100,7 @@ import org.jclouds.ssh.SshClient;
 import org.jclouds.ssh.SshException;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Function;
@@ -937,6 +942,26 @@ public abstract class BaseComputeServiceLiveTest extends BaseComputeServiceConte
       }
    }
 
+   @DataProvider
+   public Object[][] onlyIfAutomaticHardwareSupported() {
+      return  client.templateBuilder() instanceof ArbitraryCpuRamTemplateBuilderImpl ?
+            SINGLE_NO_ARG_INVOCATION : NO_INVOCATIONS;
+   }
+
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"})
+   public void testCreateNodeWithCustomHardware() throws Exception {
+      Template template = buildTemplate(templateBuilder()
+            .hardwareId("automatic:cores=2;ram=4096"));
+      try {
+         NodeMetadata node = getOnlyElement(client.createNodesInGroup("custom", 1, template));
+         assertThat(node.getHardware().getRam()).isEqualTo(4096);
+         assertThat(node.getHardware().getProcessors().get(0).getCores()).isEqualTo(2);
+      }
+      finally {
+         client.destroyNodesMatching(inGroup("custom"));
+      }
+   }
+
    @AfterClass(groups = { "integration", "live" })
    @Override
    protected void tearDownContext() {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java b/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
index 29b543f..5e27d5a 100644
--- a/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
+++ b/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
@@ -16,7 +16,10 @@
  */
 package org.jclouds.compute.internal;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
+import static org.jclouds.utils.TestUtils.NO_INVOCATIONS;
+import static org.jclouds.utils.TestUtils.SINGLE_NO_ARG_INVOCATION;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
@@ -29,10 +32,14 @@ import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.internal.ArbitraryCpuRamTemplateBuilderImpl;
+import org.jclouds.compute.util.AutomaticHardwareIdSpec;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LocationScope;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.rest.config.CredentialStoreModule;
+import org.testng.SkipException;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Splitter;
@@ -225,4 +232,100 @@ public abstract class BaseTemplateBuilderLiveTest extends BaseComputeServiceCont
       assertTrue(actual.getLocation().getScope().compareTo(expected.getLocation().getScope()) <= 0);
    }
 
+   @DataProvider
+   public Object[][] onlyIfAutomaticHardwareSupported() {
+      return  view.getComputeService().templateBuilder() instanceof ArbitraryCpuRamTemplateBuilderImpl ?
+            SINGLE_NO_ARG_INVOCATION : NO_INVOCATIONS;
+   }
+
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"})
+   public void testAutoGeneratedHardwareFromId() {
+      Template template = view.getComputeService().templateBuilder()
+            .hardwareId("automatic:cores=2;ram=1024").build();
+      assertThat(template.getHardware().getId()).isEqualTo("automatic:cores=2;ram=1024");
+      assertThat(template.getHardware().getRam()).isEqualTo(1024);
+      assertThat(template.getHardware().getProcessors().get(0).getCores()).isEqualTo(2);
+   }
+
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"})
+   public void testAutoGeneratedHardwareMatchHardwareProfile() {
+      if (!view.getComputeService().listHardwareProfiles().isEmpty()) {
+         Template template = view.getComputeService().templateBuilder()
+               .minRam(2048).minCores(2).build();
+         assertThat(AutomaticHardwareIdSpec.isAutomaticId(template.getHardware().getId())).isFalse();
+         assertThat(template.getHardware().getRam()).isGreaterThanOrEqualTo(2048);
+         assertThat(template.getHardware().getProcessors().get(0).getCores()).isGreaterThanOrEqualTo(2);
+      }
+      else {
+         throw new SkipException("Hardware profile list is empty, this provider can not match any hardware profile" +
+               "to the specified minRam and minCores.");
+      }
+   }
+
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"})
+   public void testAutoGeneratedHardwareWithMinCoresAndMinRam() {
+      if (view.getComputeService().listHardwareProfiles().isEmpty()) {
+         Template template = view.getComputeService().templateBuilder()
+               .minRam(2048).minCores(2).build();
+         assertThat(AutomaticHardwareIdSpec.isAutomaticId(template.getHardware().getId())).isTrue();
+         assertThat(template.getHardware().getRam()).isEqualTo(2048);
+         assertThat(template.getHardware().getProcessors().get(0).getCores()).isEqualTo(2);
+      }
+      else {
+         throw new SkipException("Hardware profile list not empty.");
+      }
+   }
+
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"})
+   public void testAutoGeneratedHardwareWithOnlyMinCoresMatchHardwareProfile() {
+      if (!view.getComputeService().listHardwareProfiles().isEmpty()) {
+         Template template = view.getComputeService().templateBuilder().minCores(4).build();
+         assertThat(AutomaticHardwareIdSpec.isAutomaticId(template.getHardware().getId())).isFalse();
+         assertThat(template.getHardware().getProcessors().get(0).getCores()).isGreaterThanOrEqualTo(4);
+      }
+      else {
+         throw new SkipException("Hardware profile list is empty, this provider can not match any hardware profile" +
+               "to the specified minRam and minCores.");
+      }
+   }
+
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"})
+   public void testAutoGeneratedHardwareWithOnlyMinRamMatchHardwareProfile() {
+      if (!view.getComputeService().listHardwareProfiles().isEmpty()) {
+         Template template = view.getComputeService().templateBuilder().minRam(4096).build();
+         assertThat(AutomaticHardwareIdSpec.isAutomaticId(template.getHardware().getId())).isFalse();
+         assertThat(template.getHardware().getRam()).isGreaterThanOrEqualTo(4096);
+      }
+      else {
+         throw new SkipException("Hardware profile list is empty, this provider can not match any hardware profile" +
+               "to the specified minRam and minCores.");
+      }
+   }
+
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"},
+         expectedExceptions = IllegalArgumentException.class,
+         expectedExceptionsMessageRegExp = "No hardware profile matching the given criteria was found. " +
+         "If you want to use exact values, please set the minCores and minRam values")
+   public void testAutoGeneratedHardwareWithOnlyMinRamNotMatchHardwareProfile() {
+      if (view.getComputeService().listHardwareProfiles().isEmpty()) {
+         view.getComputeService().templateBuilder().minRam(4096).build();
+      }
+      else {
+         throw new SkipException("Hardware profile list not empty.");
+      }
+   }
+
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"},
+         expectedExceptions = IllegalArgumentException.class,
+         expectedExceptionsMessageRegExp = "No hardware profile matching the given criteria was found. " +
+               "If you want to use exact values, please set the minCores and minRam values")
+   public void testAutoGeneratedHardwareWithOnlyMinCoresNotMatchHardwareProfile() {
+      if (view.getComputeService().listHardwareProfiles().isEmpty()) {
+         view.getComputeService().templateBuilder().minCores(4).build();
+      }
+      else {
+         throw new SkipException("Hardware profile list not empty.");
+      }
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/compute/src/test/java/org/jclouds/compute/util/AutomaticHardwareIdSpecTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/util/AutomaticHardwareIdSpecTest.java b/compute/src/test/java/org/jclouds/compute/util/AutomaticHardwareIdSpecTest.java
new file mode 100644
index 0000000..9dc72a2
--- /dev/null
+++ b/compute/src/test/java/org/jclouds/compute/util/AutomaticHardwareIdSpecTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.compute.util;
+
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Test(groups = "unit", testName = "AutomaticHardwareIdSpecTest")
+public class AutomaticHardwareIdSpecTest {
+   @Test
+   public void isAutomaticIdTest() {
+      assertThat(AutomaticHardwareIdSpec.isAutomaticId("automatic:cores=2;ram=256")).isTrue();
+   }
+
+   @Test
+   public void isNotAutomaticId() {
+      assertThat(AutomaticHardwareIdSpec.isAutomaticId("Hi, I'm a non automatic id.")).isFalse();
+   }
+
+   @Test
+   public void parseAutomaticIdTest() {
+      AutomaticHardwareIdSpec parser = AutomaticHardwareIdSpec.parseId("automatic:cores=2;ram=256");
+      assertThat(parser.getRam()).isEqualTo(256);
+      assertThat(parser.getCores()).isEqualTo(2);
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void parseAutomaticIdMissingValuesTest() {
+         AutomaticHardwareIdSpec.parseId("automatic:cores=2");
+   }
+
+   @Test
+   public void generateAutomaticIdTest() {
+      AutomaticHardwareIdSpec spec = AutomaticHardwareIdSpec.parseId("automatic:cores=2;ram=1024");
+      assertThat(spec.toString()).isEqualTo("automatic:cores=2.0;ram=1024");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/core/src/test/java/org/jclouds/utils/TestUtils.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/utils/TestUtils.java b/core/src/test/java/org/jclouds/utils/TestUtils.java
index 7082346..cce923b 100644
--- a/core/src/test/java/org/jclouds/utils/TestUtils.java
+++ b/core/src/test/java/org/jclouds/utils/TestUtils.java
@@ -27,6 +27,9 @@ import com.google.common.io.ByteSource;
  */
 public class TestUtils {
 
+   public static final Object[][] NO_INVOCATIONS = new Object[0][0];
+   public static final Object[][] SINGLE_NO_ARG_INVOCATION = { new Object[0] };
+
    public static boolean isJava6() {
       return System.getProperty("java.version", "").contains("1.6.");
    }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
----------------------------------------------------------------------
diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
index 484df91..8c35f32 100644
--- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
+++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
@@ -79,6 +79,8 @@ import com.google.inject.Injector;
 import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.TypeLiteral;
+import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
+import org.jclouds.googlecomputeengine.compute.domain.internal.GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl;
 
 public final class GoogleComputeEngineServiceContextModule
       extends ComputeServiceAdapterContextModule<Instance, MachineType, Image, Location> {
@@ -92,6 +94,8 @@ public final class GoogleComputeEngineServiceContextModule
       bind(new TypeLiteral<ComputeServiceAdapter<Instance, MachineType, Image, Location>>() {
       }).to(GoogleComputeEngineServiceAdapter.class);
 
+      bind(TemplateBuilderImpl.class).to(GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl.class);
+
       // Use compute service to supply locations, which are always zones.
       install(new LocationsFromComputeServiceAdapterModule<Instance, MachineType, Image, Location>() {
       });

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/domain/internal/GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl.java
----------------------------------------------------------------------
diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/domain/internal/GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/domain/internal/GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl.java
new file mode 100644
index 0000000..774dce9
--- /dev/null
+++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/domain/internal/GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl.java
@@ -0,0 +1,59 @@
+/*
+ * 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.googlecomputeengine.compute.domain.internal;
+
+import com.google.common.base.Supplier;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.domain.internal.ArbitraryCpuRamTemplateBuilderImpl;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.domain.Location;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import java.net.URI;
+import java.util.Set;
+
+public class GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl extends ArbitraryCpuRamTemplateBuilderImpl {
+   @Inject
+   protected GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
+         @Memoized Supplier<Set<? extends org.jclouds.compute.domain.Image>> images,
+         @Memoized Supplier<Set<? extends Hardware>> hardwares, Supplier<Location> defaultLocation,
+         @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
+         @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider) {
+      super(locations, images, hardwares, defaultLocation, optionsProvider, defaultTemplateProvider);
+   }
+
+   protected Hardware automaticHardwareForCpuAndRam(double cores, int ram) {
+      if (location == null) {
+         location = defaultLocation.get();
+      }
+      String uri = location.getDescription() + "/machineTypes/custom-" + (int)cores + "-" + ram;
+      return new HardwareBuilder()
+            .id(uri)
+            .ram(ram)
+            .processor(new Processor((int)cores, 1.0))
+            .providerId(uri)
+            .uri(URI.create(uri))
+            .build();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceToNodeMetadata.java
----------------------------------------------------------------------
diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceToNodeMetadata.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceToNodeMetadata.java
index 6f72bee..7e7407e 100644
--- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceToNodeMetadata.java
+++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceToNodeMetadata.java
@@ -16,27 +16,30 @@
  */
 package org.jclouds.googlecomputeengine.compute.functions;
 
-import static org.jclouds.compute.util.ComputeServiceUtils.groupFromMapOrName;
-
-import javax.inject.Inject;
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-
 import com.google.common.base.Function;
+import com.google.common.base.Splitter;
 import com.google.common.base.Supplier;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableList;
 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.NodeMetadata.Status;
 import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.Processor;
 import org.jclouds.compute.functions.GroupNamingConvention;
 import org.jclouds.domain.Location;
 import org.jclouds.googlecomputeengine.domain.Image;
 import org.jclouds.googlecomputeengine.domain.Instance;
 
+import javax.inject.Inject;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import static org.jclouds.compute.util.ComputeServiceUtils.groupFromMapOrName;
+
 public final class InstanceToNodeMetadata implements Function<Instance, NodeMetadata> {
 
    private final Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus;
@@ -73,13 +76,21 @@ public final class InstanceToNodeMetadata implements Function<Instance, NodeMeta
       // a loading cache. That would be more expensive, but could ensure this isn't null.
       Image image = diskURIToImage.getUnchecked(input.disks().get(0).source());
 
+      Hardware hardware;
+      if (isCustomMachineTypeURI(input.machineType())) {
+         hardware = machineTypeURIToCustomHardware(input.machineType());
+      }
+      else {
+         hardware = hardwares.get().get(input.machineType());
+      }
+
       builder.id(input.selfLink().toString())
              .name(input.name())
              .providerId(input.id())
              .hostname(input.name())
              .location(zone)
              .imageId(image != null ? image.selfLink().toString() : null)
-             .hardware(hardwares.get().get(input.machineType()))
+             .hardware(hardware)
              .status(input.status() != null ? toPortableNodeStatus.get(input.status()) : Status.UNRECOGNIZED)
              .tags(input.tags().items())
              .uri(input.selfLink())
@@ -111,4 +122,23 @@ public final class InstanceToNodeMetadata implements Function<Instance, NodeMeta
       }
       return publicAddressesBuilder.build();
    }
+
+   public static boolean isCustomMachineTypeURI(URI machineType) {
+      return machineType.toString().contains("machineTypes/custom");
+   }
+
+   public static Hardware machineTypeURIToCustomHardware(URI machineType) {
+      String uri = machineType.toString();
+      String values = uri.substring(uri.lastIndexOf('/') + 8);
+      List<String> hardwareValues = Splitter.on('-')
+            .trimResults()
+            .splitToList(values);
+      return new HardwareBuilder()
+            .id(uri)
+            .providerId(uri)
+            .processor(new Processor(Double.parseDouble(hardwareValues.get(0)), 1.0))
+            .ram(Integer.parseInt(hardwareValues.get(1)))
+            .uri(machineType)
+            .build();
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceLiveTest.java b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceLiveTest.java
index ca381db..d8bd28e 100644
--- a/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceLiveTest.java
+++ b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceLiveTest.java
@@ -17,6 +17,9 @@
 package org.jclouds.googlecomputeengine.compute;
 
 import static com.google.common.collect.Iterables.contains;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jclouds.compute.predicates.NodePredicates.inGroup;
 import static org.jclouds.util.Strings2.toStringAndClose;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
@@ -33,9 +36,9 @@ import com.google.inject.Module;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
 import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.compute.predicates.NodePredicates;
 import org.jclouds.googlecloud.internal.TestProperties;
 import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
@@ -95,7 +98,7 @@ public class GoogleComputeEngineServiceLiveTest extends BaseComputeServiceLiveTe
          assertTrue(instance.scheduling().preemptible());
 
       } finally {
-         client.destroyNodesMatching(NodePredicates.inGroup(group));
+         client.destroyNodesMatching(inGroup(group));
       }
    }
    /**
@@ -152,4 +155,20 @@ public class GoogleComputeEngineServiceLiveTest extends BaseComputeServiceLiveTe
       // Hardware profiles might not have volumes.
    }
 
+   @Override
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"})
+   public void testCreateNodeWithCustomHardware() throws Exception {
+      Template template = buildTemplate(templateBuilder()
+            .hardwareId("automatic:cores=2;ram=4096"));
+      try {
+         NodeMetadata node = getOnlyElement(client.createNodesInGroup("custom", 1, template));
+         assertThat(node.getHardware().getRam()).isEqualTo(4096);
+         assertThat(node.getHardware().getProcessors().get(0).getCores()).isEqualTo(2);
+         assertThat(node.getHardware().getId()).isEqualTo(node.getLocation().getDescription() + "/machineTypes/custom-2-4096");
+      }
+      finally {
+         client.destroyNodesMatching(inGroup("custom"));
+      }
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineTemplateBuilderLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineTemplateBuilderLiveTest.java b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineTemplateBuilderLiveTest.java
index dbbedb3..a954e21 100644
--- a/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineTemplateBuilderLiveTest.java
+++ b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineTemplateBuilderLiveTest.java
@@ -17,6 +17,7 @@
 package org.jclouds.googlecomputeengine.compute;
 
 import static com.google.common.base.Objects.firstNonNull;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.jclouds.compute.domain.OsFamily.COREOS;
 import static org.jclouds.compute.domain.OsFamily.DEBIAN;
 import static org.jclouds.compute.domain.OsFamily.WINDOWS;
@@ -25,6 +26,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
@@ -76,4 +78,17 @@ public class GoogleComputeEngineTemplateBuilderLiveTest extends BaseTemplateBuil
       return ImmutableSet.<String> of();
    }
 
+   @Override
+   @Test(dataProvider = "onlyIfAutomaticHardwareSupported", groups = {"integration", "live"})
+   public void testAutoGeneratedHardwareFromId() {
+      Template template = view.getComputeService().templateBuilder()
+            .hardwareId("automatic:cores=2;ram=1024").build();
+      assertThat(template.getHardware().getId()).isEqualTo(template.getLocation()
+            .getDescription() + "/machineTypes/custom-2-1024");
+      assertThat(template.getHardware().getRam()).isEqualTo(1024);
+      assertThat(template.getHardware().getProcessors().get(0).getCores()).isEqualTo(2);
+      assertThat(template.getHardware().getUri()).isEqualTo(URI.create(template.getLocation()
+            .getDescription() + "/machineTypes/custom-2-1024"));
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/81e34856/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/domain/internal/GoogleComputeEngineArbitraryCpuRamTemplateBuilderImplTest.java
----------------------------------------------------------------------
diff --git a/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/domain/internal/GoogleComputeEngineArbitraryCpuRamTemplateBuilderImplTest.java b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/domain/internal/GoogleComputeEngineArbitraryCpuRamTemplateBuilderImplTest.java
new file mode 100644
index 0000000..98b3144
--- /dev/null
+++ b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/domain/internal/GoogleComputeEngineArbitraryCpuRamTemplateBuilderImplTest.java
@@ -0,0 +1,278 @@
+/*
+ * 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.googlecomputeengine.compute.domain.internal;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Atomics;
+import com.google.inject.Provider;
+import com.google.inject.util.Providers;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.strategy.GetImageStrategy;
+import org.jclouds.compute.suppliers.ImageCacheSupplier;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.rest.AuthorizationException;
+import org.testng.annotations.Test;
+
+import java.net.URI;
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+@Test(groups = "unit", singleThreaded = true, testName = "GoogleComputeEngineTemplateBuilderImplTest")
+public class GoogleComputeEngineArbitraryCpuRamTemplateBuilderImplTest {
+   private Location provider = new LocationBuilder()
+         .scope(LocationScope.PROVIDER)
+         .id("google-compute-engine")
+         .description("google-compute-engine")
+         .build();
+
+   private Location region = new LocationBuilder()
+         .scope(LocationScope.REGION)
+         .id("us-east-1")
+         .description("http://localhost/projects/party/zones/us-east-1")
+         .parent(provider)
+         .build();
+
+   private OperatingSystem os = OperatingSystem.builder()
+         .name("osName")
+         .version("osVersion")
+         .description("osDescription")
+         .arch("X86_32")
+         .build();
+
+   private Image image = new ImageBuilder()
+         .id("imageId")
+         .providerId("imageId")
+         .name("imageName")
+         .description("imageDescription")
+         .version("imageVersion")
+         .operatingSystem(os)
+         .status(Image.Status.AVAILABLE)
+         .location(null)
+         .build();
+
+   private Hardware hardware = new HardwareBuilder()
+         .ram(2048)
+         .processor(new Processor(2, 1))
+         .id("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2")
+         .name("n2-standard-2")
+         .location(region)
+         .uri(URI.create("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2"))
+         .build();
+
+   private final String errorMessage = "No hardware profile matching the given criteria was found. " +
+         "If you want to use exact values, please set the minCores and minRam values";
+
+   @Test
+   public void testAutoGeneratedHardwareFromIdTest(){
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet.of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+
+      TemplateBuilderImpl templateBuilder =
+            new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
+                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+                  Suppliers.ofInstance(region),
+                  optionsProvider, templateBuilderProvider);
+
+      Hardware hardware = templateBuilder.hardwareId("automatic:cores=2;ram=1024").build().getHardware();
+      assertThat(hardware.getRam()).isEqualTo(1024);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
+      assertThat(hardware.getUri()).isEqualTo(URI.create("http://localhost/projects/party/zones/us-east-1/machineTypes/custom-2-1024"));
+      assertThat(hardware.getId()).isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/custom-2-1024");
+   }
+
+   @Test
+   public void testAutoGeneratedHardwareWithMinCoresAndMinRamDontMatchTest() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minRam(4096);
+      templateBuilder.minCores(2);
+      Hardware hardware = templateBuilder.build().getHardware();
+      assertThat(hardware.getRam()).isEqualTo(4096);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
+      assertThat(hardware.getId()).isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/custom-2-4096");
+   }
+
+   @Test
+   public void testAutoGeneratedHardwareMatchHardwareProfile() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minRam(1024);
+      templateBuilder.minCores(2);
+      Hardware hardware = templateBuilder.build().getHardware();
+      assertThat(hardware.getRam()).isEqualTo(2048);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
+      assertThat(hardware.getId()).isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2");
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = errorMessage)
+   public void testAutoGeneratedHardwareWithOnlyMinCoresTest() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minCores(4);
+      templateBuilder.build().getHardware();
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = errorMessage)
+   public void testAutoGeneratedHardwareWithOnlyMinRamTest() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minRam(4096);
+      templateBuilder.build().getHardware();
+   }
+
+   @Test
+   public void testAutoGeneratedHardwareWithOnlyMinCoresMatchedHardware() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minCores(2);
+      Hardware hardware = templateBuilder.build().getHardware();
+      assertThat(hardware.getRam()).isEqualTo(2048);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
+      assertThat(hardware.getId())
+            .isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2");
+   }
+
+   @Test
+   public void testAutoGeneratedHardwareOnlyRamMatchHardwareProfile() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(hardware));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
+      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
+
+      expect(optionsProvider.get()).andReturn(new TemplateOptions());
+      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
+      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
+            new ImageCacheSupplier(images, 60,
+            Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
+            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
+      templateBuilder.minRam(1024);
+      Hardware hardware = templateBuilder.build().getHardware();
+      assertThat(hardware.getRam()).isEqualTo(2048);
+      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
+      assertThat(hardware.getId())
+            .isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2");
+   }
+
+}