JCLOUDS-664 Azurecompute-arm compute service

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

Branch: refs/heads/master
Commit: 03f72d5e2f225f212d05151c1c15a99af14cd29e
Parents: e4cbe22
Author: Rita Zhang <[email protected]>
Authored: Mon May 16 18:55:01 2016 -0700
Committer: Ignasi Barrera <[email protected]>
Committed: Fri Jun 17 22:24:32 2016 +0200

----------------------------------------------------------------------
 azurecompute-arm/README.md                      |  20 +-
 .../azurecompute/arm/AzureComputeApi.java       |  11 +-
 .../arm/AzureComputeProviderMetadata.java       |  21 +-
 .../arm/AzureManagementApiMetadata.java         |   2 +
 .../arm/compute/AzureComputeService.java        | 104 +++++
 .../arm/compute/AzureComputeServiceAdapter.java | 393 +++++++++++++++++++
 .../AzureComputeServiceContextModule.java       | 191 +++++++++
 .../functions/DeploymentToNodeMetadata.java     | 207 ++++++++++
 .../compute/functions/LocationToLocation.java   |   7 +-
 .../compute/functions/VMHardwareToHardware.java |  79 ++++
 .../arm/compute/functions/VMImageToImage.java   | 124 ++++++
 .../CreateResourceGroupThenCreateNodes.java     |  96 +++++
 .../arm/config/AzureComputeProperties.java      |   8 +
 .../arm/domain/ResourceProviderMetaData.java    |  68 ++++
 .../azurecompute/arm/domain/VMDeployment.java   |   2 +
 .../azurecompute/arm/domain/VMHardware.java     |  68 ++++
 .../azurecompute/arm/domain/VMImage.java        |  53 +++
 .../azurecompute/arm/domain/VirtualMachine.java |   2 +-
 .../arm/features/ResourceProviderApi.java       |  57 +++
 .../arm/functions/CleanupResources.java         | 107 +++++
 .../arm/util/DeploymentTemplateBuilder.java     |  31 +-
 .../AzureComputeServiceContextLiveTest.java     | 284 ++++++++++++++
 .../compute/AzureComputeServiceLiveTest.java    |  73 ++++
 .../compute/AzureTemplateBuilderLiveTest.java   |  77 ++++
 .../features/ResourceProviderAPIMockTest.java   |  67 ++++
 .../features/ResourceProviderApiLiveTest.java   |  55 +++
 .../AbstractAzureComputeApiLiveTest.java        |  24 +-
 .../internal/BaseAzureComputeApiLiveTest.java   |  19 -
 .../resources/getresourceprovidermetadata.json  | 366 +++++++++++++++++
 29 files changed, 2564 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/README.md
----------------------------------------------------------------------
diff --git a/azurecompute-arm/README.md b/azurecompute-arm/README.md
index fc8bb54..c944a8a 100644
--- a/azurecompute-arm/README.md
+++ b/azurecompute-arm/README.md
@@ -53,13 +53,25 @@ azure login -u <Application-id> -p <password> 
--service-principal --tenant <Tena
 
 ## Run Live Tests
 
-Use the following to run the live tests
+Use the following to run one live test:
+
+```bash
+mvn -Dtest=<name of the live test> \
+    -Dtest.azurecompute-arm.identity="<Application-id>" \
+    -Dtest.azurecompute-arm.credential="<password>" \
+    
-Dtest.azurecompute-arm.endpoint="https://management.azure.com/subscriptions/<Subscription-id>"
 \
+    
-Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token"
 test
+
+```
+
+Use the following to run all the live tests:
 
 ```bash
 
 mvn clean verify -Plive \
-    -Dtest.azurecompute-arm.identity=<Application-id> \
-    -Dtest.azurecompute-arm.credential=<password> \
-    
-Dtest.azurecompute-arm.endpoint=https://management.azure.com/subscriptions/<Subscription-id>
 \
+    -Dtest.azurecompute-arm.identity="<Application-id>"" \
+    -Dtest.azurecompute-arm.credential="<password>"" \
+    
-Dtest.azurecompute-arm.endpoint="https://management.azure.com/subscriptions/<Subscription-id>""
 \
     
-Dtest.oauth.endpoint=https://login.microsoftonline.com/<Tenant-id>/oauth2/token
+
 ```

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
index c39022e..42749cf 100644
--- 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
@@ -17,18 +17,19 @@
 package org.jclouds.azurecompute.arm;
 
 import org.jclouds.azurecompute.arm.features.DeploymentApi;
+import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
 import org.jclouds.azurecompute.arm.features.JobApi;
 import org.jclouds.azurecompute.arm.features.LocationApi;
 import org.jclouds.azurecompute.arm.features.NetworkInterfaceCardApi;
 import org.jclouds.azurecompute.arm.features.OSImageApi;
 import org.jclouds.azurecompute.arm.features.PublicIPAddressApi;
 import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
+import org.jclouds.azurecompute.arm.features.ResourceProviderApi;
 import org.jclouds.azurecompute.arm.features.StorageAccountApi;
 import org.jclouds.azurecompute.arm.features.SubnetApi;
 import org.jclouds.azurecompute.arm.features.VirtualMachineApi;
 import org.jclouds.azurecompute.arm.features.VirtualNetworkApi;
 import org.jclouds.azurecompute.arm.features.VMSizeApi;
-import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
 import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
 import org.jclouds.azurecompute.arm.features.NetworkSecurityRuleApi;
 import org.jclouds.rest.annotations.Delegate;
@@ -156,6 +157,14 @@ public interface AzureComputeApi extends Closeable {
    NetworkSecurityRuleApi 
getNetworkSecurityRuleApi(@PathParam("resourcegroup") String resourcegroup,
                                                     
@PathParam("networksecuritygroup") String networksecuritygroup);
 
+   /**
+    * The Azure Resource Provider API provides information about a resource 
provider and its supported resource types.
+    *
+    * @see <a 
href="https://msdn.microsoft.com/en-us/library/azure/dn790534.aspx";>docs</a>
+    */
+   @Delegate
+   ResourceProviderApi getResourceProviderApi();
+
    @Provides
    DeploymentTemplateBuilder.Factory deploymentTemplateFactory();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
index 39defdc..4bbc508 100644
--- 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
@@ -16,11 +16,17 @@
  */
 package org.jclouds.azurecompute.arm;
 
+
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.IMAGE_PUBLISHERS;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
 import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
 import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
-import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
 import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
 import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
+
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_IMAGE_LOGIN;
+
 import static 
org.jclouds.oauth.v2.config.CredentialType.CLIENT_CREDENTIALS_SECRET;
 import static org.jclouds.oauth.v2.config.OAuthProperties.RESOURCE;
 import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
@@ -30,6 +36,9 @@ import java.util.Properties;
 import org.jclouds.azurecompute.arm.domain.Region;
 import org.jclouds.providers.ProviderMetadata;
 import org.jclouds.providers.internal.BaseProviderMetadata;
+import org.jclouds.compute.config.ComputeServiceProperties;
+
+import static 
org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
 
 import com.google.auto.service.AutoService;
 
@@ -51,13 +60,19 @@ public class AzureComputeProviderMetadata extends 
BaseProviderMetadata {
 
    public static Properties defaultProperties() {
       final Properties properties = 
AzureManagementApiMetadata.defaultProperties();
-      properties.setProperty(OPERATION_TIMEOUT, "60000");
+      properties.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
+      properties.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
+      properties.setProperty(OPERATION_TIMEOUT, "46000000");
       properties.setProperty(OPERATION_POLL_INITIAL_PERIOD, "5");
       properties.setProperty(OPERATION_POLL_MAX_PERIOD, "15");
       properties.setProperty(TCP_RULE_FORMAT, "tcp_%s-%s");
       properties.setProperty(TCP_RULE_REGEXP, "tcp_\\d{1,5}-\\d{1,5}");
-      properties.put(RESOURCE, "https://management.azure.com";);
+      properties.put(RESOURCE, "https://management.azure.com/";);
       properties.put(CREDENTIAL_TYPE, CLIENT_CREDENTIALS_SECRET.toString());
+      properties.put(RESOURCE_GROUP_NAME, "jcloudsgroup");
+      properties.put(IMAGE_PUBLISHERS, "Microsoft.WindowsAzure.Compute, 
MicrosoftWindowsServer, Canonical");
+      properties.put(DEFAULT_IMAGE_LOGIN, "jclouds:Password1!");
+      properties.put(TIMEOUT_NODE_TERMINATED, 60 * 10 * 1000);
       return properties;
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java
index 9a3292c..989fd84 100644
--- 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java
@@ -26,6 +26,7 @@ import 
org.jclouds.azurecompute.arm.config.AzureComputeHttpApiModule;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.rest.internal.BaseHttpApiMetadata;
 import org.jclouds.oauth.v2.config.OAuthModule;
+import 
org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Module;
@@ -68,6 +69,7 @@ public class AzureManagementApiMetadata extends 
BaseHttpApiMetadata<AzureCompute
                  
.defaultProperties(AzureManagementApiMetadata.defaultProperties())
                  .view(typeToken(ComputeServiceContext.class))
                  .defaultModules(ImmutableSet.<Class<? extends 
Module>>builder()
+                         .add(AzureComputeServiceContextModule.class)
                          .add(OAuthModule.class)
                          .add(OkHttpCommandExecutorServiceModule.class)
                          .add(AzureComputeHttpApiModule.class).build());

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
new file mode 100644
index 0000000..c215e37
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
@@ -0,0 +1,104 @@
+/*
+ * 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.azurecompute.arm.compute;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static 
org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static 
org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+import static 
org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.callables.RunScriptOnNode;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.internal.BaseComputeService;
+import org.jclouds.compute.internal.PersistNodeCredentials;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
+import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
+import org.jclouds.compute.strategy.DestroyNodeStrategy;
+import org.jclouds.compute.strategy.GetImageStrategy;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
+import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.compute.strategy.RebootNodeStrategy;
+import org.jclouds.compute.strategy.ResumeNodeStrategy;
+import org.jclouds.compute.strategy.SuspendNodeStrategy;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
+import org.jclouds.azurecompute.arm.functions.CleanupResources;
+import org.jclouds.scriptbuilder.functions.InitAdminAccess;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+@Singleton
+public class AzureComputeService extends BaseComputeService {
+   protected final CleanupResources cleanupResources;
+
+   @Inject
+   protected AzureComputeService(ComputeServiceContext context, Map<String, 
Credentials> credentialStore,
+                                @Memoized Supplier<Set<? extends Image>> 
images, @Memoized Supplier<Set<? extends Hardware>> sizes,
+                                @Memoized Supplier<Set<? extends Location>> 
locations, ListNodesStrategy listNodesStrategy,
+                                GetImageStrategy getImageStrategy, 
GetNodeMetadataStrategy getNodeMetadataStrategy,
+                                CreateNodesInGroupThenAddToSet 
runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy,
+                                DestroyNodeStrategy destroyNodeStrategy, 
ResumeNodeStrategy startNodeStrategy,
+                                SuspendNodeStrategy stopNodeStrategy, 
Provider<TemplateBuilder> templateBuilderProvider,
+                                @Named("DEFAULT") Provider<TemplateOptions> 
templateOptionsProvider,
+                                @Named(TIMEOUT_NODE_RUNNING) 
Predicate<AtomicReference<NodeMetadata>> nodeRunning,
+                                @Named(TIMEOUT_NODE_TERMINATED) 
Predicate<AtomicReference<NodeMetadata>> nodeTerminated,
+                                @Named(TIMEOUT_NODE_SUSPENDED) 
Predicate<AtomicReference<NodeMetadata>> nodeSuspended,
+                                
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory,
+                                RunScriptOnNode.Factory 
runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
+                                PersistNodeCredentials persistNodeCredentials, 
Timeouts timeouts,
+                                @Named(Constants.PROPERTY_USER_THREADS) 
ListeningExecutorService userExecutor,
+                                 CleanupResources cleanupResources,
+                                Optional<ImageExtension> imageExtension,
+                                Optional<SecurityGroupExtension> 
securityGroupExtension) {
+      super(context, credentialStore, images, sizes, locations, 
listNodesStrategy, getImageStrategy,
+              getNodeMetadataStrategy, runNodesAndAddToSetStrategy, 
rebootNodeStrategy, destroyNodeStrategy,
+              startNodeStrategy, stopNodeStrategy, templateBuilderProvider, 
templateOptionsProvider, nodeRunning,
+              nodeTerminated, nodeSuspended, initScriptRunnerFactory, 
initAdminAccess, runScriptOnNodeFactory,
+              persistNodeCredentials, timeouts, userExecutor, imageExtension, 
securityGroupExtension);
+      this.cleanupResources = checkNotNull(cleanupResources, 
"cleanupResources");
+
+   }
+
+   @Override
+   protected void cleanUpIncidentalResourcesOfDeadNodes(Set<? extends 
NodeMetadata> deadNodes) {
+      for (NodeMetadata deadNode : deadNodes) {
+         cleanupResources.apply(deadNode.getId());
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
new file mode 100644
index 0000000..9a1d221
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
@@ -0,0 +1,393 @@
+/*
+ * 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.azurecompute.arm.compute;
+
+import static java.lang.String.format;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.util.Predicates2.retry;
+import java.util.ArrayList;
+
+import java.util.Collection;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.net.UrlEscapers;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import 
org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.AzureComputeConstants;
+import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
+import org.jclouds.azurecompute.arm.domain.Deployment;
+import org.jclouds.azurecompute.arm.domain.DeploymentBody;
+import org.jclouds.azurecompute.arm.domain.DeploymentProperties;
+import org.jclouds.azurecompute.arm.domain.VMImage;
+import org.jclouds.azurecompute.arm.domain.VMHardware;
+import org.jclouds.azurecompute.arm.domain.Location;
+import org.jclouds.azurecompute.arm.domain.Offer;
+import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
+import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
+import org.jclouds.azurecompute.arm.domain.SKU;
+import org.jclouds.azurecompute.arm.domain.VMDeployment;
+import org.jclouds.azurecompute.arm.domain.VMSize;
+import org.jclouds.azurecompute.arm.domain.VirtualMachine;
+import org.jclouds.azurecompute.arm.features.DeploymentApi;
+import org.jclouds.azurecompute.arm.features.OSImageApi;
+import org.jclouds.azurecompute.arm.features.VirtualMachineApi;
+import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.logging.Logger;
+import org.jclouds.azurecompute.arm.functions.CleanupResources;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.base.Splitter;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Defines the connection between the {@link AzureComputeApi} implementation 
and the jclouds
+ * {@link org.jclouds.compute.ComputeService}.
+ */
+@Singleton
+public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<VMDeployment, VMHardware, VMImage, Location> {
+
+   private String azureGroup;
+   protected final CleanupResources cleanupResources;
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   private Logger logger = Logger.NULL;
+
+   private final AzureComputeApi api;
+
+   private final AzureComputeConstants azureComputeConstants;
+
+   @Inject
+   AzureComputeServiceAdapter(final AzureComputeApi api, final 
AzureComputeConstants azureComputeConstants,
+                              CleanupResources cleanupResources) {
+
+      this.api = api;
+      this.azureComputeConstants = azureComputeConstants;
+
+      this.azureGroup = azureComputeConstants.azureResourceGroup();
+
+      logger.debug("AzureComputeServiceAdapter set azuregroup to: " + 
azureGroup);
+
+      this.cleanupResources = cleanupResources;
+   }
+
+   @Override
+   public NodeAndInitialCredentials<VMDeployment> 
createNodeWithGroupEncodedIntoName(
+           final String group, final String name, final Template template) {
+
+      DeploymentTemplateBuilder deploymentTemplateBuilder = 
api.deploymentTemplateFactory().create(group, name, template);
+
+      final String loginUser = 
DeploymentTemplateBuilder.getLoginUserUsername();
+      final String loginPassword = 
DeploymentTemplateBuilder.getLoginPassword();
+
+      DeploymentBody deploymentTemplateBody =  
deploymentTemplateBuilder.getDeploymentTemplate();
+
+      DeploymentProperties properties = 
DeploymentProperties.create(deploymentTemplateBody);
+
+      final String deploymentTemplate = 
UrlEscapers.urlFormParameterEscaper().escape(deploymentTemplateBuilder.getDeploymentTemplateJson(properties));
+
+      logger.debug("Deployment created with name: %s group: %s", name, group);
+
+
+
+      final Set<VMDeployment> deployments = Sets.newHashSet();
+
+      final DeploymentApi deploymentApi = api.getDeploymentApi(group);
+
+      if (!retry(new Predicate<String>() {
+         @Override
+         public boolean apply(final String name) {
+
+            Deployment deployment = deploymentApi.create(name, 
deploymentTemplate);
+
+            if (deployment != null) {
+               VMDeployment vmDeployment = new VMDeployment();
+               vmDeployment.deployment = deployment;
+               deployments.add(vmDeployment);
+            } else {
+               logger.debug("Failed to create deployment!");
+            }
+            return !deployments.isEmpty();
+         }
+      }, azureComputeConstants.operationTimeout(), 1, SECONDS).apply(name)) {
+         final String illegalStateExceptionMessage = format("Deployment %s was 
not created within %sms so it will be destroyed.",
+                 name, azureComputeConstants.operationTimeout());
+         logger.warn(illegalStateExceptionMessage);
+         destroyNode(name);
+         throw new IllegalStateException(illegalStateExceptionMessage);
+      }
+
+      final VMDeployment deployment = deployments.iterator().next();
+
+
+      return new NodeAndInitialCredentials<VMDeployment>(deployment, name,
+              
LoginCredentials.builder().user(loginUser).identity(loginUser).password(loginPassword).authenticateSudo(true).build());
+   }
+
+   @Override
+   public Iterable<VMHardware> listHardwareProfiles() {
+
+      final List<VMHardware> hwProfiles = Lists.newArrayList();
+      final List<String> locationIds = Lists.newArrayList();
+
+      Iterable<Location> locations = listLocations();
+      for (Location location : locations){
+         locationIds.add(location.name());
+
+         Iterable<VMSize> vmSizes = api.getVMSizeApi(location.name()).list();
+
+         for (VMSize vmSize : vmSizes){
+            VMHardware hwProfile = new VMHardware();
+            hwProfile.name = vmSize.name();
+            hwProfile.numberOfCores = vmSize.numberOfCores();
+            hwProfile.osDiskSizeInMB = vmSize.osDiskSizeInMB();
+            hwProfile.resourceDiskSizeInMB = vmSize.resourceDiskSizeInMB();
+            hwProfile.memoryInMB = vmSize.memoryInMB();
+            hwProfile.maxDataDiskCount = vmSize.maxDataDiskCount();
+            hwProfile.location = location.name();
+            hwProfiles.add(hwProfile);
+         }
+
+      }
+
+      checkAndSetHwAvailability(hwProfiles, Sets.newHashSet(locationIds));
+
+      return hwProfiles;
+   }
+   private void checkAndSetHwAvailability(List<VMHardware> hwProfiles, 
Collection<String> locations) {
+      Multimap<String, String> hwMap = ArrayListMultimap.create();
+      for (VMHardware hw : hwProfiles) {
+         hwMap.put(hw.name, hw.location);
+      }
+
+      for (VMHardware hw : hwProfiles) {
+         hw.globallyAvailable = hwMap.get(hw.name).containsAll(locations);
+      }
+   }
+
+   private void getImagesFromPublisher(String publisherName, List<VMImage> 
osImagesRef, String location) {
+
+      OSImageApi osImageApi = api.getOSImageApi(location);
+
+      Iterable<Offer> offerList = osImageApi.listOffers(publisherName);
+
+      for (Offer offer : offerList) {
+         Iterable<SKU> skuList = osImageApi.listSKUs(publisherName, 
offer.name());
+
+         for (SKU sku : skuList) {
+            VMImage vmImage = new VMImage();
+            vmImage.publisher = publisherName;
+            vmImage.offer = offer.name();
+            vmImage.sku = sku.name();
+            vmImage.location = location;
+            osImagesRef.add(vmImage);
+         }
+      }
+   }
+
+   private List<VMImage> listImagesByLocation(String location) {
+      final List<VMImage> osImages = Lists.newArrayList();
+      Iterable<String> publishers = 
Splitter.on(',').trimResults().omitEmptyStrings().split(this.azureComputeConstants.azureImagePublishers());
+      for (String publisher : publishers) {
+         getImagesFromPublisher(publisher, osImages, location);
+      }
+      return osImages;
+   }
+
+   @Override
+   public Iterable<VMImage> listImages() {
+
+      final List<VMImage> osImages = Lists.newArrayList();
+      final List<String> locationIds = Lists.newArrayList();
+
+      for (Location location : listLocations()){
+         locationIds.add(location.name());
+         osImages.addAll(listImagesByLocation(location.name()));
+      }
+      checkAndSetImageAvailability(osImages, Sets.newHashSet(locationIds));
+      return osImages;
+   }
+
+   private void checkAndSetImageAvailability(List<VMImage> images, 
Collection<String> locations) {
+      Multimap<String, String> map = ArrayListMultimap.create();
+
+      for (VMImage image : images) {
+         map.put( image.offer + "/" + image.sku, image.location);
+      }
+
+      for (VMImage image : images) {
+         image.globallyAvailable = map.get(image.offer + "/" + 
image.sku).containsAll(locations);
+      }
+   }
+
+   @Override
+   public VMImage getImage(final String id) {
+      String[] fields = VMImageToImage.decodeFieldsFromUniqueId(id);
+
+      Iterable<VMImage> images = listImages();
+
+      for (VMImage image : images) {
+         String imageId = VMImageToImage.encodeFieldsToUniqueId(image);
+         if (id.equals(imageId)){
+            return image;
+         }
+      }
+      return null;
+   }
+
+   @Override
+   public Iterable<Location> listLocations() {
+      List<Location> locations = api.getLocationApi().list();
+
+      List<ResourceProviderMetaData> resources = 
api.getResourceProviderApi().get("Microsoft.Compute");
+
+      final List<String> vmLocations = new ArrayList<String>();
+
+      for (ResourceProviderMetaData m : resources){
+         if (m.resourceType().equals("virtualMachines")){
+            vmLocations.addAll(m.locations());
+            break;
+         }
+      }
+
+      Iterable<Location> result = Iterables.filter(locations, new 
Predicate<Location>() {
+         @Override
+         public boolean apply(Location input) {
+            return vmLocations.contains(input.displayName());
+         }
+      });
+
+      return  result;
+   }
+
+   private String getResourceGroupFromId(String id) {
+      String searchStr = "/resourceGroups/";
+      int indexStart = id.lastIndexOf(searchStr) + searchStr.length();
+      searchStr = "/providers/";
+      int indexEnd = id.lastIndexOf(searchStr);
+
+      String resourceGroup = id.substring(indexStart, indexEnd);
+      return resourceGroup;
+   }
+
+   @Override
+   public VMDeployment getNode(final String id) {
+      Deployment deployment = api.getDeploymentApi(azureGroup).get(id);
+      if (deployment == null)
+         return null;
+      String resourceGroup = getResourceGroupFromId(deployment.id());
+      VMDeployment vmDeployment = new VMDeployment();
+      vmDeployment.deployment = deployment;
+      List<PublicIPAddress> list = getIPAddresses(deployment);
+      vmDeployment.ipAddressList = list;
+
+      VirtualMachine vm = api.getVirtualMachineApi(azureGroup).get(id);
+      vmDeployment.virtualMachine = vm;
+      return vmDeployment;
+   }
+
+   @Override
+   public void destroyNode(final String id) {
+      checkState(cleanupResources.apply(id), "server(%s) and its resources 
still there after deleting!?", id);
+   }
+
+   @Override
+   public void rebootNode(final String id) {
+      api.getVirtualMachineApi(azureGroup).restart(id);
+   }
+
+   @Override
+   public void resumeNode(final String id) {
+      api.getVirtualMachineApi(azureGroup).start(id);
+   }
+
+   @Override
+   public void suspendNode(final String id) {
+      api.getVirtualMachineApi(azureGroup).stop(id);
+   }
+
+   private List<PublicIPAddress> getIPAddresses(Deployment deployment) {
+      List<PublicIPAddress> list = new ArrayList<PublicIPAddress>();
+      String resourceGroup = getResourceGroupFromId(deployment.id());
+
+      if (deployment.properties() != null && 
deployment.properties().dependencies() != null) {
+         List<Deployment.Dependency> dependencies = 
deployment.properties().dependencies();
+         for (int d = 0; d < dependencies.size(); d++) {
+            if 
(dependencies.get(d).resourceType().equals("Microsoft.Network/networkInterfaces"))
 {
+               List<Deployment.Dependency> dependsOn = 
dependencies.get(d).dependsOn();
+               for (int e = 0; e < dependsOn.size(); e++) {
+                  if 
(dependsOn.get(e).resourceType().equals("Microsoft.Network/publicIPAddresses")) 
{
+                     String resourceName = dependsOn.get(e).resourceName();
+                     PublicIPAddress ip = 
api.getPublicIPAddressApi(resourceGroup).get(resourceName);
+                     list.add(ip);
+                     break;
+                  }
+               }
+            }
+         }
+      }
+      return list;
+   }
+
+   @Override
+   public Iterable<VMDeployment> listNodes() {
+      List<Deployment> deployments = api.getDeploymentApi(azureGroup).list();
+
+      List<VMDeployment> vmDeployments = new ArrayList<VMDeployment>();
+
+      for (Deployment d : deployments){
+         VMDeployment vmDeployment = new VMDeployment();
+         vmDeployment.deployment = d;
+         VirtualMachineApi vmApi = api.getVirtualMachineApi(azureGroup);
+         vmDeployment.vm = vmApi.getInstanceDetails(d.name());
+         List<PublicIPAddress> list = getIPAddresses(d);
+         vmDeployment.ipAddressList = list;
+
+         VirtualMachine virtualMachine = vmApi.get(d.name());
+         vmDeployment.virtualMachine = virtualMachine;
+
+         vmDeployments.add(vmDeployment);
+      }
+      return vmDeployments;
+   }
+
+   @Override
+   public Iterable<VMDeployment> listNodesByIds(final Iterable<String> ids) {
+      return Iterables.filter(listNodes(), new Predicate<VMDeployment>() {
+         @Override
+         public boolean apply(final VMDeployment input) {
+            return Iterables.contains(ids, input.deployment.name());
+         }
+      });
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
new file mode 100644
index 0000000..9844be4
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
@@ -0,0 +1,191 @@
+/*
+ * 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.azurecompute.arm.compute.config;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+import com.google.inject.Provides;
+
+import org.jclouds.azurecompute.arm.compute.AzureComputeServiceAdapter;
+import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
+import org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata;
+import org.jclouds.azurecompute.arm.compute.functions.VMHardwareToHardware;
+import org.jclouds.azurecompute.arm.compute.functions.LocationToLocation;
+import org.jclouds.azurecompute.arm.domain.VMDeployment;
+import org.jclouds.azurecompute.arm.domain.VMHardware;
+import org.jclouds.azurecompute.arm.domain.VMImage;
+import org.jclouds.azurecompute.arm.domain.Location;
+import 
org.jclouds.azurecompute.arm.compute.strategy.CreateResourceGroupThenCreateNodes;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.functions.ParseJobStatus;
+import org.jclouds.azurecompute.arm.compute.AzureComputeService;
+
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
+import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
+import org.jclouds.compute.reference.ComputeServiceConstants.PollPeriod;
+import org.jclouds.compute.ComputeService;
+import static org.jclouds.util.Predicates2.retry;
+import static 
org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+
+import com.google.common.base.Function;
+import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+import com.google.common.base.Predicate;
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.annotations.VisibleForTesting;
+import java.net.URI;
+
+
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.IMAGE_PUBLISHERS;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_IMAGE_LOGIN;
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+
+public class AzureComputeServiceContextModule
+        extends ComputeServiceAdapterContextModule<VMDeployment, VMHardware, 
VMImage, Location> {
+
+   @Override
+   protected void configure() {
+      super.configure();
+      bind(new TypeLiteral<ComputeServiceAdapter<VMDeployment, VMHardware, 
VMImage, Location>>() {
+      }).to(AzureComputeServiceAdapter.class);
+      bind(new TypeLiteral<Function<VMImage, 
org.jclouds.compute.domain.Image>>() {
+      }).to(VMImageToImage.class);
+      bind(new TypeLiteral<Function<VMHardware, Hardware>>() {
+      }).to(VMHardwareToHardware.class);
+      bind(new TypeLiteral<Function<VMDeployment, NodeMetadata>>() {
+      }).to(DeploymentToNodeMetadata.class);
+      bind(new TypeLiteral<Function<Location, org.jclouds.domain.Location>>() {
+      }).to(LocationToLocation.class);
+      bind(ComputeService.class).to(AzureComputeService.class);
+      install(new LocationsFromComputeServiceAdapterModule<VMDeployment, 
VMHardware, VMImage, Location>() {
+      });
+
+      
bind(CreateNodesInGroupThenAddToSet.class).to(CreateResourceGroupThenCreateNodes.class);
+   }
+
+   @Singleton
+   public static class AzureComputeConstants {
+
+      @Named(OPERATION_TIMEOUT)
+      @Inject
+      private String operationTimeoutProperty;
+
+      @Named(OPERATION_POLL_INITIAL_PERIOD)
+      @Inject
+      private String operationPollInitialPeriodProperty;
+
+      @Named(OPERATION_POLL_MAX_PERIOD)
+      @Inject
+      private String operationPollMaxPeriodProperty;
+
+      @Named(TCP_RULE_FORMAT)
+      @Inject
+      private String tcpRuleFormatProperty;
+
+      @Named(TCP_RULE_REGEXP)
+      @Inject
+      private String tcpRuleRegexpProperty;
+
+      @Named(RESOURCE_GROUP_NAME)
+      @Inject
+      private String azureResourceGroupProperty;
+
+      @Named(IMAGE_PUBLISHERS)
+      @Inject
+      private String azureImagePublishersProperty;
+
+      @Named(DEFAULT_IMAGE_LOGIN)
+      @Inject
+      private String azureDefaultImageLoginProperty;
+
+      public Long operationTimeout() {
+         return Long.parseLong(operationTimeoutProperty);
+      }
+
+      public String azureResourceGroup() {
+         return azureResourceGroupProperty;
+      }
+
+      public String azureImagePublishers() {
+         return azureImagePublishersProperty;
+      }
+
+      public String azureDefaultImageLogin() {
+         return azureDefaultImageLoginProperty;
+      }
+
+      public Integer operationPollInitialPeriod() {
+         return Integer.parseInt(operationPollInitialPeriodProperty);
+      }
+
+      public Integer operationPollMaxPeriod() {
+         return Integer.parseInt(operationPollMaxPeriodProperty);
+      }
+
+      public String tcpRuleFormat() {
+         return tcpRuleFormatProperty;
+      }
+
+      public String tcpRuleRegexp() {
+         return tcpRuleRegexpProperty;
+      }
+   }
+
+   @Provides
+   @Named(TIMEOUT_NODE_TERMINATED)
+   protected Predicate<URI> provideNodeTerminatedPredicate(final 
AzureComputeApi api, Timeouts timeouts,
+                                                                  PollPeriod 
pollPeriod) {
+      return retry(new ActionDonePredicate(api), timeouts.nodeTerminated, 
pollPeriod.pollInitialPeriod,
+              pollPeriod.pollMaxPeriod);
+   }
+
+   @Provides
+   @Named(TIMEOUT_RESOURCE_DELETED)
+   protected Predicate<URI> provideResourceDeletedPredicate(final 
AzureComputeApi api, Timeouts timeouts,
+                                                           PollPeriod 
pollPeriod) {
+      return retry(new ActionDonePredicate(api), timeouts.nodeTerminated, 
pollPeriod.pollInitialPeriod,
+              pollPeriod.pollMaxPeriod);
+   }
+
+   @VisibleForTesting
+   static class ActionDonePredicate implements Predicate<URI> {
+
+      private final AzureComputeApi api;
+
+      public ActionDonePredicate(AzureComputeApi api) {
+         this.api = checkNotNull(api, "api must not be null");
+      }
+
+      @Override
+      public boolean apply(URI uri) {
+         checkNotNull(uri, "uri cannot be null");
+         return ParseJobStatus.JobStatus.DONE == 
api.getJobApi().jobStatus(uri);
+      }
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
new file mode 100644
index 0000000..bccc63c
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
@@ -0,0 +1,207 @@
+/*
+ * 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.azurecompute.arm.compute.functions;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import com.google.common.collect.Sets;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.domain.ComputeNode;
+import org.jclouds.azurecompute.arm.domain.Deployment;
+import org.jclouds.azurecompute.arm.domain.ImageReference;
+import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
+import org.jclouds.azurecompute.arm.domain.VMDeployment;
+import org.jclouds.azurecompute.arm.domain.VMHardware;
+import org.jclouds.azurecompute.arm.domain.VMImage;
+import org.jclouds.azurecompute.arm.domain.VMSize;
+import org.jclouds.azurecompute.arm.domain.VirtualMachineInstance;
+import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
+import org.jclouds.azurecompute.arm.util.GetEnumValue;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.Hardware;
+
+public class DeploymentToNodeMetadata implements Function<VMDeployment, 
NodeMetadata> {
+
+   public static final String AZURE_LOGIN_USERNAME = 
DeploymentTemplateBuilder.getLoginUserUsername();
+   public static final String AZURE_LOGIN_PASSWORD = 
DeploymentTemplateBuilder.getLoginPassword();
+
+   private static final Map<ComputeNode.Status, NodeMetadata.Status> 
INSTANCESTATUS_TO_NODESTATUS =
+           ImmutableMap.<ComputeNode.Status, NodeMetadata.Status>builder().
+                   put(ComputeNode.Status.GOOD, NodeMetadata.Status.RUNNING).
+                   put(ComputeNode.Status.BAD, NodeMetadata.Status.ERROR).
+                   put(ComputeNode.Status.UNRECOGNIZED, 
NodeMetadata.Status.UNRECOGNIZED).
+                   build();
+
+   // When using the Deployment API to deploy an ARM template, the deployment 
goes through
+   // stages.  Accepted -> Running -> Succeeded.  Only when the deployment has 
SUCCEEDED is
+   // the resource deployed using the template actually ready.
+   //
+   // To get details about the resource(s) deployed via template, one needs to 
query the
+   // various resources after the deployment has "SUCCEEDED".
+   private static final Map<Deployment.ProvisioningState, NodeMetadata.Status> 
STATUS_TO_NODESTATUS =
+           ImmutableMap.<Deployment.ProvisioningState, 
NodeMetadata.Status>builder().
+                   put(Deployment.ProvisioningState.ACCEPTED, 
NodeMetadata.Status.PENDING).
+                   put(Deployment.ProvisioningState.READY, 
NodeMetadata.Status.PENDING).
+                   put(Deployment.ProvisioningState.RUNNING, 
NodeMetadata.Status.PENDING).
+                   put(Deployment.ProvisioningState.CANCELED, 
NodeMetadata.Status.TERMINATED).
+                   put(Deployment.ProvisioningState.FAILED, 
NodeMetadata.Status.ERROR).
+                   put(Deployment.ProvisioningState.DELETED, 
NodeMetadata.Status.TERMINATED).
+                   put(Deployment.ProvisioningState.SUCCEEDED, 
NodeMetadata.Status.RUNNING).
+                   put(Deployment.ProvisioningState.UNRECOGNIZED, 
NodeMetadata.Status.UNRECOGNIZED).
+                   build();
+
+   public static Deployment.ProvisioningState 
provisioningStateFromString(final String text) {
+      return (Deployment.ProvisioningState) 
GetEnumValue.fromValueOrDefault(text, 
Deployment.ProvisioningState.UNRECOGNIZED);
+   }
+
+   private final AzureComputeApi api;
+
+   private final LocationToLocation locationToLocation;
+
+   private final GroupNamingConvention nodeNamingConvention;
+
+   private final VMImageToImage vmImageToImage;
+
+   private final VMHardwareToHardware vmHardwareToHardware;
+
+   private final Map<String, Credentials> credentialStore;
+
+   @Inject
+   DeploymentToNodeMetadata(
+           AzureComputeApi api,
+           LocationToLocation locationToLocation,
+           GroupNamingConvention.Factory namingConvention, VMImageToImage 
vmImageToImage,
+           VMHardwareToHardware vmHardwareToHardware, Map<String, Credentials> 
credentialStore) {
+
+      this.nodeNamingConvention = namingConvention.createWithoutPrefix();
+      this.locationToLocation = locationToLocation;
+      this.vmImageToImage = vmImageToImage;
+      this.vmHardwareToHardware = vmHardwareToHardware;
+      this.credentialStore = credentialStore;
+      this.api = api;
+   }
+
+   @Override
+   public NodeMetadata apply(final VMDeployment from) {
+      final NodeMetadataBuilder builder = new NodeMetadataBuilder();
+      final Deployment deployment = from.deployment;
+      builder.id(deployment.name());
+      builder.providerId(deployment.name());
+      builder.name(deployment.name());
+      String group =  
this.nodeNamingConvention.extractGroup(deployment.name());
+      builder.group(group);
+
+      NodeMetadata.Status status = 
STATUS_TO_NODESTATUS.get(provisioningStateFromString(deployment.properties().provisioningState()));
+      if (status == NodeMetadata.Status.RUNNING && from.vm != null && 
from.vm.statuses() != null) {
+         List<VirtualMachineInstance.VirtualMachineStatus> statuses = 
from.vm.statuses();
+         for (int c = 0; c < statuses.size(); c++) {
+            if (statuses.get(c).code().substring(0, 10).equals("PowerState")) {
+               if (statuses.get(c).displayStatus().equals("VM running")) {
+                  status = NodeMetadata.Status.RUNNING;
+               } else if (statuses.get(c).displayStatus().equals("VM 
stopped")) {
+                  status = NodeMetadata.Status.SUSPENDED;
+               }
+               break;
+            }
+         }
+      }
+
+      builder.status(status);
+
+      Credentials credentials = credentialStore.get("node#" + 
from.deployment.name());
+      if (credentials == null) {
+         credentials = new Credentials(AZURE_LOGIN_USERNAME, 
AZURE_LOGIN_PASSWORD);
+      }
+      builder.credentials(LoginCredentials.fromCredentials(credentials));
+
+      final Set<String> publicIpAddresses = Sets.newLinkedHashSet();
+      if (from.ipAddressList != null) {
+         for (int c = 0; c < from.ipAddressList.size(); c++) {
+            PublicIPAddress ip = from.ipAddressList.get(c);
+            if (ip != null && ip.properties() != null && 
ip.properties().ipAddress() != null)
+            {
+               publicIpAddresses.add(ip.properties().ipAddress());
+               break;
+            }
+
+         }
+         if (publicIpAddresses.size() > 0)
+            builder.publicAddresses(publicIpAddresses);
+      }
+
+      org.jclouds.azurecompute.arm.domain.Location myLocation = null;
+      if (from.virtualMachine != null) {
+         String locationName = from.virtualMachine.location();
+         List<org.jclouds.azurecompute.arm.domain.Location> locations = 
api.getLocationApi().list();
+
+         for (org.jclouds.azurecompute.arm.domain.Location location : 
locations) {
+            if (location.name().equals(locationName)) {
+               myLocation = location;
+               break;
+            }
+         }
+         Location jLocation = this.locationToLocation.apply(myLocation);
+         builder.location(jLocation);
+
+         ImageReference imageReference = 
from.virtualMachine.properties().storageProfile().imageReference();
+
+         VMImage vmImage = new VMImage();
+         vmImage.publisher = imageReference.publisher();
+         vmImage.offer = imageReference.offer();
+         vmImage.sku = imageReference.sku();
+         vmImage.location = locationName;
+         Image image = vmImageToImage.apply(vmImage);
+         builder.imageId(image.getId());
+
+         VMSize myVMSize = null;
+         String vmSizeName = 
from.virtualMachine.properties().hardwareProfile().vmSize();
+         List<VMSize> vmSizes = api.getVMSizeApi(locationName).list();
+         for (VMSize vmSize : vmSizes) {
+            if (vmSize.name().equals(vmSizeName)) {
+               myVMSize = vmSize;
+               break;
+            }
+         }
+
+         VMHardware hwProfile = new VMHardware();
+         hwProfile.name = myVMSize.name();
+         hwProfile.numberOfCores = myVMSize.numberOfCores();
+         hwProfile.osDiskSizeInMB = myVMSize.osDiskSizeInMB();
+         hwProfile.resourceDiskSizeInMB = myVMSize.resourceDiskSizeInMB();
+         hwProfile.memoryInMB = myVMSize.memoryInMB();
+         hwProfile.maxDataDiskCount = myVMSize.maxDataDiskCount();
+         hwProfile.location = locationName;
+
+         Hardware hardware = vmHardwareToHardware.apply(hwProfile);
+         builder.hardware(hardware);
+      }
+
+      return builder.build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java
index a4d4b1e..0ca1458 100644
--- 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/LocationToLocation.java
@@ -38,6 +38,7 @@ public class LocationToLocation implements Function<Location, 
org.jclouds.domain
 
    private final JustProvider justProvider;
 
+   // allow us to lazy discover the provider of a resource
    @Inject
    LocationToLocation(JustProvider justProvider) {
       this.justProvider = justProvider;
@@ -46,7 +47,11 @@ public class LocationToLocation implements 
Function<Location, org.jclouds.domain
    @Override
    public org.jclouds.domain.Location apply(final Location location) {
       final LocationBuilder builder = new LocationBuilder();
-      builder.id(location.id());
+      String id = location.id();
+      int index = id.lastIndexOf('/');
+      if (index > 0 && (index + 1) < id.length())
+         id = id.substring(index + 1);
+      builder.id(id);
       builder.description(location.displayName());
       builder.parent(getOnlyElement(justProvider.get()));
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
new file mode 100644
index 0000000..51a6e5e
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
@@ -0,0 +1,79 @@
+/*
+ * 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.azurecompute.arm.compute.functions;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.FluentIterable;
+import com.google.inject.Inject;
+import org.jclouds.azurecompute.arm.domain.VMHardware;
+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.Volume;
+import org.jclouds.compute.domain.VolumeBuilder;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.domain.Location;
+import org.jclouds.location.predicates.LocationPredicates;
+
+import java.util.Set;
+
+public class VMHardwareToHardware implements Function<VMHardware, Hardware> {
+
+   private final Supplier<Set<? extends Location>> locations;
+
+   @Inject
+   VMHardwareToHardware(@Memoized final Supplier<Set<? extends Location>> 
locations) {
+      this.locations = locations;
+   }
+
+   @Override
+   public Hardware apply(VMHardware from) {
+      final HardwareBuilder builder = new HardwareBuilder()
+              .name(from.name)
+              .id(from.name)
+              .processors(ImmutableList.of(new Processor(from.numberOfCores, 
2)))
+              .ram(from.memoryInMB)
+              .location(from.globallyAvailable ? null : 
FluentIterable.from(locations.get())
+                      .firstMatch(LocationPredicates.idEquals(from.location))
+                      .get());
+
+      // No id or providerId from Azure
+      if (from.resourceDiskSizeInMB != null) {
+         builder.volume(new VolumeBuilder()
+                 .size(Float.valueOf(from.resourceDiskSizeInMB))
+                 .type(Volume.Type.LOCAL)
+                 .build());
+      }
+      if (from.osDiskSizeInMB != null) {
+         builder.volume(new VolumeBuilder()
+                 .size(Float.valueOf(from.osDiskSizeInMB))
+                 .type(Volume.Type.LOCAL)
+                 .build());
+      }
+
+      ImmutableMap.Builder<String, String> metadata = ImmutableMap.builder();
+      metadata.put("maxDataDiskCount", String.valueOf(from.maxDataDiskCount));
+      builder.userMetadata(metadata.build());
+
+      return builder.build();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
new file mode 100644
index 0000000..65a3d4b
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
@@ -0,0 +1,124 @@
+/*
+ * 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.azurecompute.arm.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.FluentIterable;
+import org.jclouds.azurecompute.arm.domain.VMImage;
+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 com.google.common.base.Function;
+import com.google.inject.Inject;
+import org.jclouds.domain.Location;
+import org.jclouds.location.predicates.LocationPredicates;
+
+import java.util.Set;
+
+public class VMImageToImage implements Function<VMImage, Image> {
+
+   private static final String UNRECOGNIZED = "UNRECOGNIZED";
+
+   private static final String UBUNTU = "Ubuntu";
+
+   private static final String WINDOWS = "Windows";
+
+   private static final String OPENLOGIC = "openLogic";
+
+   private static final String CENTOS = "CentOS";
+
+   private static final String COREOS = "CoreOS";
+
+   private static final String OPENSUSE = "openSUSE";
+
+   private static final String SUSE = "SUSE";
+
+   private static final String SQL_SERVER = "SQL Server";
+
+   private static final String ORACLE_lINUX = "Oracle Linux";
+
+   private final Supplier<Set<? extends org.jclouds.domain.Location>> 
locations;
+
+   public static String encodeFieldsToUniqueId(VMImage imageReference){
+      return (imageReference.globallyAvailable ? "global" : 
imageReference.location) + "/" + imageReference.publisher + "/" + 
imageReference.offer + "/" + imageReference.sku;
+   }
+
+   public static String[] decodeFieldsFromUniqueId(final String id) {
+      return checkNotNull(id, "id").split("/");
+   }
+
+   @Inject
+   VMImageToImage(@Memoized final Supplier<Set<? extends Location>> locations) 
{
+      this.locations = locations;
+   }
+
+   @Override
+   public Image apply(final VMImage image) {
+
+      final ImageBuilder builder = new ImageBuilder()
+              .name(image.offer)
+              .description(image.sku)
+              .status(Image.Status.AVAILABLE)
+              .version(image.sku)
+              .id(encodeFieldsToUniqueId(image))
+              .providerId(image.publisher)
+              .location(image.globallyAvailable ? null : 
FluentIterable.from(locations.get())
+                      .firstMatch(LocationPredicates.idEquals(image.location))
+                      .get());
+
+
+      final OperatingSystem.Builder osBuilder = osFamily().apply(image);
+      return builder.operatingSystem(osBuilder.build()).build();
+   }
+
+   public static Function<VMImage, OperatingSystem.Builder> osFamily() {
+      return new Function<VMImage, OperatingSystem.Builder>() {
+         @Override
+         public OperatingSystem.Builder apply(final VMImage image) {
+            checkNotNull(image.offer, "offer");
+            final String label = image.offer;
+
+            OsFamily family = OsFamily.UNRECOGNIZED;
+            if (label.contains(CENTOS)) {
+               family = OsFamily.CENTOS;
+            } else if (label.contains(OPENLOGIC)) {
+               family = OsFamily.CENTOS;
+            } else if (label.contains(SUSE)) {
+               family = OsFamily.SUSE;
+            } else if (label.contains(UBUNTU)) {
+               family = OsFamily.UBUNTU;
+            } else if (label.contains(WINDOWS)) {
+               family = OsFamily.WINDOWS;
+            } else if (label.contains(ORACLE_lINUX)) {
+               family = OsFamily.OEL;
+            }
+
+            // only 64bit OS images are supported by Azure ARM
+            return OperatingSystem.builder().
+                    family(family).
+                    is64Bit(true).
+                    description(image.sku).
+                    version(image.sku);
+         }
+      };
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
new file mode 100644
index 0000000..6900f17
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
@@ -0,0 +1,96 @@
+/*
+ * 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.azurecompute.arm.compute.strategy;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.Constants;
+import org.jclouds.azurecompute.arm.domain.ResourceGroup;
+import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
+import org.jclouds.compute.config.CustomizationResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
+import 
org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import 
org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+
+import org.jclouds.logging.Logger;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+@Singleton
+public class CreateResourceGroupThenCreateNodes extends 
CreateNodesWithGroupEncodedIntoNameThenAddToSet {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final AzureComputeApi api;
+
+   @Inject
+   protected CreateResourceGroupThenCreateNodes(
+           CreateNodeWithGroupEncodedIntoName addNodeWithGroupStrategy,
+           ListNodesStrategy listNodesStrategy,
+           GroupNamingConvention.Factory namingConvention,
+           @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService 
userExecutor,
+           CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory 
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
+           AzureComputeApi api) {
+      super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, 
userExecutor,
+              customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
+      this.api = checkNotNull(api, "api cannot be null");
+      checkNotNull(userExecutor, "userExecutor cannot be null");
+   }
+
+   @Override
+   public Map<?, ListenableFuture<Void>> execute(String group, int count, 
Template template,
+                                                 Set<NodeMetadata> goodNodes, 
Map<NodeMetadata, Exception> badNodes,
+                                                 Multimap<NodeMetadata, 
CustomizationResponse> customizationResponses) {
+
+      ResourceGroupApi resourceGroupApi = api.getResourceGroupApi();
+      ResourceGroup resourceGroup = resourceGroupApi.get(group);
+      final String location = template.getLocation().getId();
+      final String resourceGroupName;
+
+      if (resourceGroup == null){
+
+         final Map<String, String> tags = ImmutableMap.of("description", 
"jClouds managed VMs");
+         resourceGroupName = resourceGroupApi.create(group, location, 
tags).name();
+      } else {
+         resourceGroupName = resourceGroup.name();
+      }
+
+      Map<?, ListenableFuture<Void>> responses = 
super.execute(resourceGroupName, count, template, goodNodes, badNodes,
+              customizationResponses);
+
+      return responses;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
index 48188c4..48d6287 100644
--- 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
@@ -33,4 +33,12 @@ public class AzureComputeProperties {
 
    public static final String STORAGE_API_VERSION = "2015-06-15";
 
+   public static final String RESOURCE_GROUP_NAME = 
"jclouds.azurecompute.arm.operation.resourcegroup";
+
+   public static final String IMAGE_PUBLISHERS = 
"jclouds.azurecompute.arm.publishers";
+
+   public static final String DEFAULT_IMAGE_LOGIN = 
"jclouds.azurecompute.arm.defaultimagelogin";
+
+   public static final String TIMEOUT_RESOURCE_DELETED = 
"jclouds.azurecompute.arm.timeout.resourcedeleted";
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/ResourceProviderMetaData.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/ResourceProviderMetaData.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/ResourceProviderMetaData.java
new file mode 100644
index 0000000..84526b9
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/ResourceProviderMetaData.java
@@ -0,0 +1,68 @@
+/*
+ * 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.azurecompute.arm.domain;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import org.jclouds.json.SerializedNames;
+
+import java.util.List;
+
+@AutoValue
+public abstract class ResourceProviderMetaData {
+
+   public abstract String resourceType();
+
+   public abstract List<String> locations();
+
+   public abstract List<String> apiVersions();
+
+   @SerializedNames({"resourceType", "locations", "apiVersions"})
+   public static ResourceProviderMetaData create(final String resourceType, 
final List<String> locations, final List<String> apiVersions) {
+      ResourceProviderMetaData.Builder builder = 
ResourceProviderMetaData.builder()
+              .resourceType(resourceType)
+              .locations(locations == null ? ImmutableList.<String>of() : 
ImmutableList.copyOf(locations))
+              .apiVersions(apiVersions == null ? ImmutableList.<String>of() : 
ImmutableList.copyOf(apiVersions));
+
+      return builder.build();
+   }
+
+   public static Builder builder() {
+      return new AutoValue_ResourceProviderMetaData.Builder();
+   }
+
+   @AutoValue.Builder
+   public abstract static class Builder {
+      public abstract Builder resourceType(String resourceType);
+
+      public abstract Builder locations(List<String> locations);
+
+      public abstract Builder apiVersions(List<String> apiVersions);
+
+      abstract List<String> locations();
+
+      abstract List<String> apiVersions();
+
+      abstract ResourceProviderMetaData autoBuild();
+
+      public ResourceProviderMetaData build() {
+         locations(locations() != null ? ImmutableList.copyOf(locations()) : 
ImmutableList.<String>of());
+         apiVersions(apiVersions() != null ? 
ImmutableList.copyOf(apiVersions()) : ImmutableList.<String>of());
+         return autoBuild();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
index 2504409..6909a7b 100644
--- 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
@@ -26,4 +26,6 @@ public class VMDeployment {
    public List<PublicIPAddress> ipAddressList;
 
    public VirtualMachineInstance vm;
+
+   public VirtualMachine virtualMachine;
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
new file mode 100644
index 0000000..d338327
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
@@ -0,0 +1,68 @@
+/*
+ * 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.azurecompute.arm.domain;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * A VM Size that is available in a region for a given subscription.
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/azure/mt269440.aspx"; 
>api</a>
+ */
+@AutoValue
+public class VMHardware {
+
+   /**
+    * The name of the VM size.
+    */
+   public String name;
+
+   /**
+    * The number of cores that are available in the VM size.
+    */
+   public Integer numberOfCores;
+
+   /**
+    * Specifies the size in MB of the OS Disk.
+    */
+   public Integer osDiskSizeInMB;
+
+   /**
+    * The size of the resource disk.
+    */
+   public Integer resourceDiskSizeInMB;
+
+   /**
+    * Specifies the available RAM in MB.
+    */
+   public Integer memoryInMB;
+
+   /**
+    * Specifies the maximum number of data disks that can be attached to the 
VM size.
+    */
+   public Integer maxDataDiskCount;
+
+   /**
+    * Specifies the location of the HW resource
+    */
+   public String location;
+
+   /**
+    * Specifies if this HW is globally available
+    */
+   public boolean globallyAvailable;
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
new file mode 100644
index 0000000..ccfb05a
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.domain;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public class VMImage {
+
+   /**
+    * The publisher of the image reference.
+    */
+   public String publisher;
+
+   /**
+    * The offer of the image reference.
+    */
+   public String offer;
+
+   /**
+    * The sku of the image reference.
+    */
+   public String sku;
+
+   /**
+    * The version of the image reference.
+    */
+   public String version;
+
+   /**
+    * The location from where Image was fetched
+    */
+   public String location;
+
+   /**
+    * Specifies if this image is globally available
+    */
+   public boolean globallyAvailable;
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java
index 71387e6..3013543 100644
--- 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VirtualMachine.java
@@ -64,6 +64,6 @@ public abstract class VirtualMachine {
    public static VirtualMachine create(final String id, final String name, 
final String type, final String location,
                                        @Nullable final Map<String, String> 
tags, VirtualMachineProperties properties) {
 
-      return new AutoValue_VirtualMachine(id, name, location, type, tags == 
null ? null : ImmutableMap.copyOf(tags), properties);
+      return new AutoValue_VirtualMachine(id, name, type, location, tags == 
null ? null : ImmutableMap.copyOf(tags), properties);
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/ResourceProviderApi.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/ResourceProviderApi.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/ResourceProviderApi.java
new file mode 100644
index 0000000..e3d38b8
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/ResourceProviderApi.java
@@ -0,0 +1,57 @@
+/*
+ * 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.azurecompute.arm.features;
+
+
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.oauth.v2.filters.OAuthFilter;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SelectJson;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import java.io.Closeable;
+import java.util.List;
+
+/**
+ * The Azure Resource Provider API provides information about a resource 
provider and its supported resource types.
+ *
+ * @see <a 
href="https://msdn.microsoft.com/en-us/library/azure/dn790534.aspx";>docs</a>
+ */
+@Path("/providers")
+
+@QueryParams(keys = "api-version", values = "2015-01-01")
+@RequestFilters(OAuthFilter.class)
+@Consumes(MediaType.APPLICATION_JSON)
+public interface ResourceProviderApi extends Closeable {
+
+   @Named("providers:get")
+   @GET
+   @Path("/{namespace}")
+   @SelectJson("resourceTypes")
+   @Fallback(NullOnNotFoundOr404.class)
+   @Nullable
+   List<ResourceProviderMetaData> get(@PathParam("namespace") String 
namespace);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/03f72d5e/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
----------------------------------------------------------------------
diff --git 
a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
new file mode 100644
index 0000000..2b6a18e
--- /dev/null
+++ 
b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
@@ -0,0 +1,107 @@
+/*
+ * 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.azurecompute.arm.functions;
+
+import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+import static 
org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import com.google.common.base.Predicate;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+
+import java.net.URI;
+
+@Singleton
+public class CleanupResources implements Function<String, Boolean> {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   protected final AzureComputeApi api;
+   private Predicate<URI> nodeTerminated;
+   private Predicate<URI> resourceDeleted;
+
+   @Inject
+   public CleanupResources(AzureComputeApi azureComputeApi,
+                           @Named(TIMEOUT_NODE_TERMINATED) Predicate<URI> 
nodeTerminated,
+                           @Named(TIMEOUT_RESOURCE_DELETED) Predicate<URI> 
resourceDeleted) {
+
+      this.api = azureComputeApi;
+      this.nodeTerminated = nodeTerminated;
+      this.resourceDeleted = resourceDeleted;
+   }
+
+   @Override
+   public Boolean apply(String id) {
+
+      logger.debug("Destroying %s ...", id);
+      String storageAccountName = id.replaceAll("[^A-Za-z0-9 ]", "") + 
"storage";
+      int index = id.lastIndexOf("-");
+      String group = id.substring(0, index);
+
+      // Delete VM
+      URI uri = api.getVirtualMachineApi(group).delete(id);
+      if (uri != null){
+         boolean jobDone = nodeTerminated.apply(uri);
+
+         if (jobDone) {
+            // Delete storage account
+            api.getStorageAccountApi(group).delete(storageAccountName);
+
+            // Delete NIC
+            uri = api.getNetworkInterfaceCardApi(group).delete(id + "nic");
+            if (uri != null){
+               jobDone = resourceDeleted.apply(uri);
+               if (jobDone) {
+
+                  // Delete deployment
+                  uri = api.getDeploymentApi(group).delete(id);
+                  jobDone = resourceDeleted.apply(uri);
+                  if (jobDone) {
+                     // Delete public ip
+                     boolean ipDeleteStatus = 
api.getPublicIPAddressApi(group).delete(id + "publicip");
+
+                     // Delete Virtual network
+                     boolean vnetDeleteStatus = 
api.getVirtualNetworkApi(group).delete(group + "virtualnetwork");
+                     return ipDeleteStatus && vnetDeleteStatus;
+
+                  } else {
+                     return false;
+                  }
+               } else {
+                  return false;
+               }
+            } else {
+               return false;
+            }
+         } else {
+            return false;
+         }
+      } else {
+         return false;
+      }
+   }
+}

Reply via email to