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 2015/04/01 11:38:26 UTC

jclouds-labs git commit: [JCLOUDS-841] AffinityGroups operations implemented

Repository: jclouds-labs
Updated Branches:
  refs/heads/master 09ed03d95 -> d928dbcc2


[JCLOUDS-841] AffinityGroups operations implemented


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

Branch: refs/heads/master
Commit: d928dbcc2c5874dd2d1c0035f0851303bf1bb518
Parents: 09ed03d
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Mar 12 13:48:26 2015 +0100
Committer: Ignasi Barrera <na...@apache.org>
Committed: Wed Apr 1 11:34:50 2015 +0200

----------------------------------------------------------------------
 .../jclouds/azurecompute/AzureComputeApi.java   |   9 ++
 .../binders/CreateAffinityGroupParamsToXML.java |  50 ++++++
 .../binders/StorageServiceParamsToXML.java      |   3 +-
 .../binders/UpdateAffinityGroupParamsToXML.java |  50 ++++++
 .../azurecompute/domain/AffinityGroup.java      | 137 ++++++++++++++++
 .../domain/CreateAffinityGroupParams.java       | 115 +++++++++++++
 .../domain/UpdateAffinityGroupParams.java       |  84 ++++++++++
 .../azurecompute/features/AffinityGroupApi.java | 122 ++++++++++++++
 .../azurecompute/xml/AffinityGroupHandler.java  | 136 ++++++++++++++++
 .../xml/ComputeCapabilitiesHandler.java         |  87 ++++++++++
 .../xml/ListAffinityGroupsHandler.java          |  76 +++++++++
 .../features/AffinityGroupApiLiveTest.java      |  95 +++++++++++
 .../features/AffinityGroupApiMockTest.java      | 161 +++++++++++++++++++
 .../azurecompute/features/DiskApiLiveTest.java  |  25 ++-
 .../features/LocationApiLiveTest.java           |   7 +-
 .../features/OSImageApiLiveTest.java            |  21 ++-
 .../features/SubscriptionApiLiveTest.java       |   7 +-
 .../AbstractAzureComputeApiLiveTest.java        |  47 ++++++
 .../internal/BaseAzureComputeApiLiveTest.java   |  32 ++--
 .../xml/ListAffinityGroupsHandlerTest.java      |  90 +++++++++++
 .../src/test/resources/affinityGroup.xml        |  22 +++
 .../src/test/resources/affinityGroups.xml       |  45 ++++++
 .../resources/createaffinitygroupparams.xml     |   1 +
 .../resources/updateaffinitygroupparams.xml     |   1 +
 24 files changed, 1381 insertions(+), 42 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
index 7985726..ab93cbe 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
@@ -20,6 +20,7 @@ import java.io.Closeable;
 
 import javax.ws.rs.PathParam;
 
+import org.jclouds.azurecompute.features.AffinityGroupApi;
 import org.jclouds.azurecompute.features.CloudServiceApi;
 import org.jclouds.azurecompute.features.DeploymentApi;
 import org.jclouds.azurecompute.features.DiskApi;
@@ -42,6 +43,14 @@ import org.jclouds.rest.annotations.Delegate;
 public interface AzureComputeApi extends Closeable {
 
    /**
+    * The Service Management API includes operations for managing affinity groups in your subscription.
+    *
+    * @see <a href="http://msdn.microsoft.com/en-us/library/azure/ee460798">docs</a>
+    */
+   @Delegate
+   AffinityGroupApi getAffinityGroupApi();
+   
+   /**
     * The Service Management API includes operations for listing the available data center locations for a cloud service
     * in your subscription.
     *

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/binders/CreateAffinityGroupParamsToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/CreateAffinityGroupParamsToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/CreateAffinityGroupParamsToXML.java
new file mode 100644
index 0000000..5b71be2
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/CreateAffinityGroupParamsToXML.java
@@ -0,0 +1,50 @@
+/*
+ * 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.binders;
+
+import static com.google.common.base.Throwables.propagate;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.BaseEncoding;
+
+import com.jamesmurty.utils.XMLBuilder;
+
+import org.jclouds.azurecompute.domain.CreateAffinityGroupParams;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.Binder;
+
+public final class CreateAffinityGroupParamsToXML implements Binder {
+
+   @Override
+   @SuppressWarnings("unchecked")
+   public <R extends HttpRequest> R bindToRequest(final R request, final Object input) {
+      final CreateAffinityGroupParams params = CreateAffinityGroupParams.class.cast(input);
+
+      try {
+         XMLBuilder builder = XMLBuilder.create("CreateAffinityGroup", "http://schemas.microsoft.com/windowsazure")
+                 .e("Name").t(params.name()).up()
+                 .e("Label").t(BaseEncoding.base64().encode(params.label().getBytes(Charsets.UTF_8))).up();
+         if (params.description() != null) {
+            builder.e("Description").t(params.description()).up();
+         }
+         String xml = builder.e("Location").t(params.location()).up().asString();
+         return (R) request.toBuilder().payload(xml).build();
+      } catch (Exception e) {
+         throw propagate(e);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/binders/StorageServiceParamsToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/StorageServiceParamsToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/StorageServiceParamsToXML.java
index 480b8d3..ea06e50 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/StorageServiceParamsToXML.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/StorageServiceParamsToXML.java
@@ -17,6 +17,7 @@
 package org.jclouds.azurecompute.binders;
 
 import static com.google.common.base.Throwables.propagate;
+
 import org.jclouds.azurecompute.domain.StorageServiceParams;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.rest.Binder;
@@ -37,7 +38,7 @@ public final class StorageServiceParamsToXML implements Binder {
                  "CreateStorageServiceInput", "http://schemas.microsoft.com/windowsazure")
                  .e("ServiceName").t(params.name()).up()
                  //.e("Description").up()
-                 .e("Label").t(BaseEncoding.base64().encode(params.label().getBytes(Charsets.UTF_16))).up()
+                 .e("Label").t(BaseEncoding.base64().encode(params.label().getBytes(Charsets.UTF_8))).up()
                  .e("Location").t(params.location()).up()
                  //.e("GeoReplicationEnabled").up()
                  //.e("ExtendedProperties").up()

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/binders/UpdateAffinityGroupParamsToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/UpdateAffinityGroupParamsToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/UpdateAffinityGroupParamsToXML.java
new file mode 100644
index 0000000..8e8d591
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/UpdateAffinityGroupParamsToXML.java
@@ -0,0 +1,50 @@
+/*
+ * 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.binders;
+
+import static com.google.common.base.Throwables.propagate;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.BaseEncoding;
+
+import com.jamesmurty.utils.XMLBuilder;
+
+import org.jclouds.azurecompute.domain.UpdateAffinityGroupParams;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.Binder;
+
+public final class UpdateAffinityGroupParamsToXML implements Binder {
+
+   @Override
+   @SuppressWarnings("unchecked")
+   public <R extends HttpRequest> R bindToRequest(final R request, final Object input) {
+      final UpdateAffinityGroupParams params = UpdateAffinityGroupParams.class.cast(input);
+
+      try {
+         XMLBuilder builder = XMLBuilder.create("UpdateAffinityGroup", "http://schemas.microsoft.com/windowsazure");
+         if (params.label() != null) {
+            builder.e("Label").t(BaseEncoding.base64().encode(params.label().getBytes(Charsets.UTF_8))).up();
+         }
+         if (params.description() != null) {
+            builder.e("Description").t(params.description()).up();
+         }
+         return (R) request.toBuilder().payload(builder.asString()).build();
+      } catch (Exception e) {
+         throw propagate(e);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/domain/AffinityGroup.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/domain/AffinityGroup.java b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/AffinityGroup.java
new file mode 100644
index 0000000..93c7788
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/AffinityGroup.java
@@ -0,0 +1,137 @@
+/*
+ * 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.domain;
+
+import static com.google.common.collect.ImmutableList.copyOf;
+
+import java.util.List;
+
+import com.google.auto.value.AutoValue;
+import java.util.Date;
+import org.jclouds.javax.annotation.Nullable;
+
+/**
+ * Affinity group.
+ *
+ * @see <a href="http://msdn.microsoft.com/en-us/library/azure/ee460797">api</a>
+ */
+@AutoValue
+public abstract class AffinityGroup {
+
+   public enum Capability {
+
+      /**
+       * Enables an affinity group to support Virtual Machines.
+       */
+      PersistentVMRole,
+      /**
+       * Enables the affinity group to support Virtual Machines that use high memory relative to the number of CPUs.
+       */
+      HighMemory;
+
+   }
+
+   @AutoValue
+   public abstract static class ComputeCapabilities {
+
+      ComputeCapabilities() {
+      } // For AutoValue only!
+
+      /**
+       * Specifies the role size that is available for the type of deployment.
+       *
+       * @return the role size that is available for the type of deployment
+       */
+      public abstract List<RoleSize.Type> virtualMachineRoleSizes();
+
+      /**
+       * Specifies the role size that is available for the type of deployment.
+       *
+       * @return the role size that is available for the type of deployment
+       */
+      public abstract List<RoleSize.Type> webWorkerRoleSizes();
+
+      public static ComputeCapabilities create(
+              final List<RoleSize.Type> virtualMachineRoleSizes, final List<RoleSize.Type> webWorkerRoleSizes) {
+
+         return new AutoValue_AffinityGroup_ComputeCapabilities(
+                 copyOf(virtualMachineRoleSizes), copyOf(webWorkerRoleSizes));
+      }
+   }
+
+   AffinityGroup() {
+   } // For AutoValue only!
+
+   /**
+    * Specifies the name of the affinity group.
+    *
+    * @return the name of the affinity group
+    */
+   public abstract String name();
+
+   /**
+    * Specifies the base-64-encoded identifier of the affinity group.
+    *
+    * @return the identifier of the affinity group
+    */
+   public abstract String label();
+
+   /**
+    * Specified the description of this affinity group.
+    *
+    * @return the description of this affinity group
+    */
+   @Nullable
+   public abstract String description();
+
+   /**
+    * Specifies the data center in which the affinity group is located.
+    *
+    * @return the data center in which the affinity group is located
+    */
+   public abstract String location();
+
+   /**
+    * Specifies the capabilities that of the affinity group.
+    *
+    * @return the capabilities that of the affinity group
+    */
+   public abstract List<Capability> capabilities();
+
+   /**
+    * Specifies in UTC format when the affinity group was created.
+    *
+    * @return when the affinity group was created (in UTC)
+    */
+   public abstract Date createdTime();
+
+   /**
+    * Specifies the roles sizes that are available for deployments in the affinity group.
+    *
+    * @return the roles sizes that are available for deployments in the affinity group
+    */
+   @Nullable
+   public abstract ComputeCapabilities computeCapabilities();
+
+   public static AffinityGroup create(final String name, final String label, final String description,
+           final String location, final List<Capability> capabilities, final Date createdTime,
+           final ComputeCapabilities computeCapabilities) {
+
+      return new AutoValue_AffinityGroup(name, label, description, location, copyOf(capabilities),
+              createdTime, computeCapabilities);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/domain/CreateAffinityGroupParams.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/domain/CreateAffinityGroupParams.java b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/CreateAffinityGroupParams.java
new file mode 100644
index 0000000..57e853c
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/CreateAffinityGroupParams.java
@@ -0,0 +1,115 @@
+/*
+ * 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.domain;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.javax.annotation.Nullable;
+
+/**
+ * To create a new affinity group.
+ */
+@AutoValue
+public abstract class CreateAffinityGroupParams {
+
+   CreateAffinityGroupParams() {
+   } // For AutoValue only!
+
+   /**
+    * Specifies the name of the affinity group.
+    *
+    * @return the name of the affinity group
+    */
+   public abstract String name();
+
+   /**
+    * Specifies the base-64-encoded identifier of the affinity group.
+    *
+    * @return the identifier of the affinity group
+    */
+   public abstract String label();
+
+   /**
+    * Specified the description of this affinity group.
+    *
+    * @return the description of this affinity group
+    */
+   @Nullable
+   public abstract String description();
+
+   /**
+    * Specifies the data center in which the affinity group is located.
+    *
+    * @return the data center in which the affinity group is located
+    */
+   public abstract String location();
+
+   public Builder toBuilder() {
+      return builder().fromAffinityGroupParams(this);
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static final class Builder {
+
+      private String name;
+
+      private String label;
+
+      private String description;
+
+      private String location;
+
+      public Builder name(final String name) {
+         this.name = name;
+         return this;
+      }
+
+      public Builder label(final String label) {
+         this.label = label;
+         return this;
+      }
+
+      public Builder description(final String description) {
+         this.description = description;
+         return this;
+      }
+
+      public Builder location(final String location) {
+         this.location = location;
+         return this;
+      }
+
+      public CreateAffinityGroupParams build() {
+         return CreateAffinityGroupParams.create(name, label, description, location);
+      }
+
+      public Builder fromAffinityGroupParams(final CreateAffinityGroupParams in) {
+         return name(in.name())
+                 .label(in.label())
+                 .description(in.description())
+                 .location(in.location());
+      }
+   }
+
+   private static CreateAffinityGroupParams create(
+           final String name, final String label, final String description, final String location) {
+
+      return new AutoValue_CreateAffinityGroupParams(name, label, description, location);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/domain/UpdateAffinityGroupParams.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/domain/UpdateAffinityGroupParams.java b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/UpdateAffinityGroupParams.java
new file mode 100644
index 0000000..7beddb5
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/UpdateAffinityGroupParams.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.azurecompute.domain;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.javax.annotation.Nullable;
+
+/**
+ * To update an affinity group.
+ */
+@AutoValue
+public abstract class UpdateAffinityGroupParams {
+
+   UpdateAffinityGroupParams() {
+   } // For AutoValue only!
+
+   /**
+    * Specifies the base-64-encoded identifier of the affinity group.
+    *
+    * @return the identifier of the affinity group
+    */
+   @Nullable
+   public abstract String label();
+
+   /**
+    * Specified the description of this affinity group.
+    *
+    * @return the description of this affinity group
+    */
+   @Nullable
+   public abstract String description();
+
+   public Builder toBuilder() {
+      return builder().fromUpdateAffinityGroupParams(this);
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static final class Builder {
+
+      private String label;
+
+      private String description;
+
+      public Builder label(final String label) {
+         this.label = label;
+         return this;
+      }
+
+      public Builder description(final String description) {
+         this.description = description;
+         return this;
+      }
+
+      public UpdateAffinityGroupParams build() {
+         return UpdateAffinityGroupParams.create(label, description);
+      }
+
+      public Builder fromUpdateAffinityGroupParams(final UpdateAffinityGroupParams in) {
+         return label(in.label()).
+                 description(in.description());
+      }
+   }
+
+   private static UpdateAffinityGroupParams create(final String label, final String description) {
+      return new AutoValue_UpdateAffinityGroupParams(label, description);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/features/AffinityGroupApi.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/features/AffinityGroupApi.java b/azurecompute/src/main/java/org/jclouds/azurecompute/features/AffinityGroupApi.java
new file mode 100644
index 0000000..cce1714
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/features/AffinityGroupApi.java
@@ -0,0 +1,122 @@
+/*
+ * 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.features;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+import static org.jclouds.Fallbacks.EmptyListOnNotFoundOr404;
+import static org.jclouds.Fallbacks.NullOnNotFoundOr404;
+
+import java.util.List;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import org.jclouds.azurecompute.binders.CreateAffinityGroupParamsToXML;
+import org.jclouds.azurecompute.binders.UpdateAffinityGroupParamsToXML;
+import org.jclouds.azurecompute.domain.AffinityGroup;
+import org.jclouds.azurecompute.domain.CreateAffinityGroupParams;
+import org.jclouds.azurecompute.domain.UpdateAffinityGroupParams;
+import org.jclouds.azurecompute.functions.ParseRequestIdHeader;
+import org.jclouds.azurecompute.xml.AffinityGroupHandler;
+import org.jclouds.azurecompute.xml.ListAffinityGroupsHandler;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.Headers;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.XMLResponseParser;
+
+/**
+ * The Service Management API includes operations for managing affinity groups in your subscription.
+ *
+ * @see <a href="http://msdn.microsoft.com/en-us/library/azure/ee460798">docs</a>
+ */
+@Path("/affinitygroups")
+@Headers(keys = "x-ms-version", values = "{jclouds.api-version}")
+@Consumes(APPLICATION_XML)
+@Produces(APPLICATION_XML)
+public interface AffinityGroupApi {
+
+   /**
+    * The List Affinity Groups operation lists the affinity groups that are associated with the specified subscription.
+    *
+    * @return the affinity groups that are associated with the specified subscription
+    */
+   @Named("ListAffinityGroups")
+   @GET
+   @XMLResponseParser(ListAffinityGroupsHandler.class)
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<AffinityGroup> list();
+
+   /**
+    * The Get Affinity Group Properties operation returns the system properties that are associated with the specified
+    * affinity group.
+    *
+    * @param name name of the affinity group
+    * @return the system properties that are associated with the specified affinity group
+    */
+   @Named("GetAffinityGroup")
+   @GET
+   @Path("/{name}")
+   @XMLResponseParser(AffinityGroupHandler.class)
+   @Fallback(NullOnNotFoundOr404.class)
+   AffinityGroup get(@PathParam("name") String name);
+
+   /**
+    * The Create Affinity Group operation creates a new affinity group for the specified subscription.
+    *
+    * @param params the affinity group to be created
+    * @return a value that uniquely identifies a request made against the management service
+    */
+   @Named("AddAffinityGroup")
+   @POST
+   @ResponseParser(ParseRequestIdHeader.class)
+   String add(@BinderParam(CreateAffinityGroupParamsToXML.class) CreateAffinityGroupParams params);
+
+   /**
+    * The Update Affinity Group operation updates the label or the description for an affinity group in the specified
+    * subscription.
+    *
+    * @param params the affinity group to be created
+    * @return a value that uniquely identifies a request made against the management service
+    */
+   @Named("UpdateAffinityGroup")
+   @PUT
+   @Path("/{name}")
+   @ResponseParser(ParseRequestIdHeader.class)
+   String update(@PathParam("name") String name,
+           @BinderParam(UpdateAffinityGroupParamsToXML.class) UpdateAffinityGroupParams params);
+
+   /**
+    * The Delete Affinity Group operation deletes an affinity group in the specified subscription.
+    *
+    * @param name name of the affinity group
+    * @return a value that uniquely identifies a request made against the management service
+    */
+   @Named("DeleteAffinityGroup")
+   @DELETE
+   @Path("/{name}")
+   @Fallback(NullOnNotFoundOr404.class)
+   @ResponseParser(ParseRequestIdHeader.class)
+   String delete(@PathParam("name") String name);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/xml/AffinityGroupHandler.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/xml/AffinityGroupHandler.java b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/AffinityGroupHandler.java
new file mode 100644
index 0000000..ea9bd6d
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/AffinityGroupHandler.java
@@ -0,0 +1,136 @@
+/*
+ * 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.xml;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.io.BaseEncoding.base64;
+import static org.jclouds.util.SaxUtils.currentOrNull;
+
+import java.util.Date;
+import java.util.List;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.azurecompute.domain.AffinityGroup;
+import org.jclouds.azurecompute.domain.AffinityGroup.Capability;
+import org.jclouds.azurecompute.domain.AffinityGroup.ComputeCapabilities;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @see <a href="http://msdn.microsoft.com/en-us/library/jj157191">api</a>
+ */
+public final class AffinityGroupHandler extends ParseSax.HandlerForGeneratedRequestWithResult<AffinityGroup> {
+
+   private final StringBuilder currentText = new StringBuilder();
+
+   private final ComputeCapabilitiesHandler computeCapabilitiesHandler;
+
+   private String name;
+
+   private String label;
+
+   private String description;
+
+   private String location;
+
+   private final List<Capability> capabilities = Lists.newArrayList();
+
+   private Date createdTime;
+
+   private ComputeCapabilities computeCapabilities;
+
+   private boolean inComputeCapabilities = false;
+
+   @Inject
+   AffinityGroupHandler(ComputeCapabilitiesHandler computeCapabilitiesHandler) {
+      this.computeCapabilitiesHandler = computeCapabilitiesHandler;
+   }
+
+   @Override
+   public AffinityGroup getResult() {
+      final AffinityGroup result = AffinityGroup.create(
+              name, label, description, location, capabilities, createdTime, computeCapabilities);
+      resetState(); // handler is called in a loop.
+      return result;
+   }
+
+   private void resetState() {
+      name = null;
+      label = null;
+      description = null;
+      location = null;
+      capabilities.clear();
+      createdTime = null;
+      computeCapabilities = null;
+   }
+
+   @Override
+   public void startElement(final String uri, final String localName, final String qName, final Attributes attributes)
+           throws SAXException {
+
+      if ("ComputeCapabilities".equals(qName)) {
+         inComputeCapabilities = true;
+      } else if (inComputeCapabilities) {
+         computeCapabilitiesHandler.startElement(uri, localName, qName, attributes);
+      }
+   }
+
+   @Override
+   public void endElement(final String ignoredUri, final String ignoredName, final String qName) {
+      if ("Name".equals(qName)) {
+         name = currentOrNull(currentText);
+      } else if ("Label".equals(qName)) {
+         label = new String(base64().decode(currentOrNull(currentText)), UTF_8);
+      } else if ("Description".equals(qName)) {
+         description = currentOrNull(currentText);
+      } else if ("Location".equals(qName)) {
+         location = currentOrNull(currentText);
+      } else if ("Capability".equals(qName)) {
+         final String capabilityText = currentOrNull(currentText);
+         if (capabilityText != null) {
+            capabilities.add(Capability.valueOf(capabilityText));
+         }
+      } else if ("CreatedTime".equals(qName)) {
+         final String createdTimeText = currentOrNull(currentText);
+         if (createdTimeText != null) {
+            createdTime = new SimpleDateFormatDateService().iso8601DateOrSecondsDateParse(createdTimeText);
+         }
+      } else if ("ComputeCapabilities".equals(qName)) {
+         inComputeCapabilities = false;
+         computeCapabilities = computeCapabilitiesHandler.getResult();
+      } else if (inComputeCapabilities) {
+         computeCapabilitiesHandler.endElement(ignoredUri, ignoredName, qName);
+      }
+
+      currentText.setLength(0);
+   }
+
+   @Override
+   public void characters(final char ch[], final int start, final int length) {
+      if (inComputeCapabilities) {
+         computeCapabilitiesHandler.characters(ch, start, length);
+      } else {
+         currentText.append(ch, start, length);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ComputeCapabilitiesHandler.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ComputeCapabilitiesHandler.java b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ComputeCapabilitiesHandler.java
new file mode 100644
index 0000000..8a2f8b1
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ComputeCapabilitiesHandler.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+
+import com.google.common.collect.Lists;
+import java.util.List;
+import org.jclouds.azurecompute.domain.AffinityGroup.ComputeCapabilities;
+import org.jclouds.azurecompute.domain.RoleSize;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+public class ComputeCapabilitiesHandler
+        extends ParseSax.HandlerForGeneratedRequestWithResult<ComputeCapabilities> {
+
+   private final StringBuilder currentText = new StringBuilder();
+
+   private final List<RoleSize.Type> virtualMachineRoleSizes = Lists.newArrayList();
+
+   private final List<RoleSize.Type> webWorkerRoleSizes = Lists.newArrayList();
+
+   private boolean inVirtualMachineRoleSizes = false;
+
+   private boolean inWebWorkerRoleSizes = false;
+
+   @Override
+   public ComputeCapabilities getResult() {
+      final ComputeCapabilities result = ComputeCapabilities.create(virtualMachineRoleSizes, webWorkerRoleSizes);
+      resetState(); // handler is called in a loop.
+      return result;
+   }
+
+   private void resetState() {
+      virtualMachineRoleSizes.clear();
+      webWorkerRoleSizes.clear();
+   }
+
+   @Override
+   public void startElement(final String uri, final String localName, final String qName, final Attributes attributes)
+           throws SAXException {
+
+      if ("WebWorkerRoleSizes".equals(qName)) {
+         inWebWorkerRoleSizes = true;
+      } else if ("VirtualMachinesRoleSizes".equals(qName)) {
+         inVirtualMachineRoleSizes = true;
+      }
+   }
+
+   @Override
+   public void endElement(final String ignoredUri, final String ignoredName, final String qName) {
+      if ("RoleSize".equals(qName)) {
+         final RoleSize.Type roleSizeType = RoleSize.Type.fromString(currentOrNull(currentText));
+         if (inVirtualMachineRoleSizes) {
+            virtualMachineRoleSizes.add(roleSizeType);
+         } else if (inWebWorkerRoleSizes) {
+            webWorkerRoleSizes.add(roleSizeType);
+         }
+      } else if ("WebWorkerRoleSizes".equals(qName)) {
+         inWebWorkerRoleSizes = false;
+      } else if ("VirtualMachinesRoleSizes".equals(qName)) {
+         inVirtualMachineRoleSizes = false;
+      }
+
+      currentText.setLength(0);
+   }
+
+   @Override
+   public void characters(final char ch[], final int start, final int length) {
+      currentText.append(ch, start, length);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ListAffinityGroupsHandler.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ListAffinityGroupsHandler.java b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ListAffinityGroupsHandler.java
new file mode 100644
index 0000000..4f30de5
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ListAffinityGroupsHandler.java
@@ -0,0 +1,76 @@
+/*
+ * 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.xml;
+
+import java.util.List;
+
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.Attributes;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.inject.Inject;
+import org.jclouds.azurecompute.domain.AffinityGroup;
+import org.xml.sax.SAXException;
+
+public final class ListAffinityGroupsHandler
+        extends ParseSax.HandlerForGeneratedRequestWithResult<List<AffinityGroup>> {
+
+   private final Builder<AffinityGroup> affinityGroups = ImmutableList.builder();
+
+   private final AffinityGroupHandler affinityGroupHandler;
+
+   private boolean inAffinityGroup;
+
+   @Inject
+   ListAffinityGroupsHandler(AffinityGroupHandler affinityGroupHandler) {
+      this.affinityGroupHandler = affinityGroupHandler;
+   }
+
+   @Override
+   public List<AffinityGroup> getResult() {
+      return affinityGroups.build();
+   }
+
+   @Override
+   public void startElement(final String url, final String name, final String qName, final Attributes attributes)
+           throws SAXException {
+
+      if ("AffinityGroup".equals(qName)) {
+         inAffinityGroup = true;
+      } else if (inAffinityGroup) {
+         affinityGroupHandler.startElement(url, qName, qName, attributes);
+      }
+   }
+
+   @Override
+   public void endElement(final String uri, final String name, final String qName) {
+      if ("AffinityGroup".equals(qName)) {
+         inAffinityGroup = false;
+         affinityGroups.add(affinityGroupHandler.getResult());
+      } else if (inAffinityGroup) {
+         affinityGroupHandler.endElement(uri, name, qName);
+      }
+   }
+
+   @Override
+   public void characters(final char ch[], final int start, final int length) {
+      if (inAffinityGroup) {
+         affinityGroupHandler.characters(ch, start, length);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiLiveTest.java
new file mode 100644
index 0000000..cd677c7
--- /dev/null
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiLiveTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertNotNull;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+import java.util.List;
+
+import org.jclouds.azurecompute.domain.AffinityGroup;
+import org.jclouds.azurecompute.domain.CreateAffinityGroupParams;
+import org.jclouds.azurecompute.domain.UpdateAffinityGroupParams;
+import org.jclouds.azurecompute.internal.AbstractAzureComputeApiLiveTest;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+@Test(groups = "live", testName = "AffinityGroupApiLiveTest")
+public class AffinityGroupApiLiveTest extends AbstractAzureComputeApiLiveTest {
+
+   private static final String GROUP_NAME = String.format("%3.24s",
+           System.getProperty("user.name") + RAND + "-securityGroup");
+
+   private AffinityGroupApi api() {
+      return api.getAffinityGroupApi();
+   }
+
+   @Test(dependsOnMethods = "testCreate")
+   public void testList() {
+      final List<AffinityGroup> groups = api().list();
+      assertFalse(groups.isEmpty());
+
+      final AffinityGroup matching = Iterables.find(groups, new Predicate<AffinityGroup>() {
+
+         @Override
+         public boolean apply(final AffinityGroup group) {
+            return GROUP_NAME.equals(group.name());
+         }
+      });
+      assertNotNull(matching);
+   }
+
+   @Test(dependsOnMethods = "testCreate")
+   public void testRead() {
+      final AffinityGroup group = api().get(GROUP_NAME);
+      assertNotNull(group);
+      assertEquals(group.name(), GROUP_NAME);
+   }
+
+   public void testCreate() {
+      final CreateAffinityGroupParams params = CreateAffinityGroupParams.builder().
+              name(GROUP_NAME).
+              label(GROUP_NAME).
+              location("West Europe").
+              build();
+
+      final String requestId = api().add(params);
+      assertTrue(operationSucceeded.apply(requestId), requestId);
+   }
+
+   @Test(dependsOnMethods = "testCreate")
+   public void testUpdate() {
+      final UpdateAffinityGroupParams params = UpdateAffinityGroupParams.builder().
+              description(GROUP_NAME + " description").
+              build();
+
+      final String requestId = api().update(GROUP_NAME, params);
+      assertTrue(operationSucceeded.apply(requestId), requestId);
+   }
+
+   @AfterClass(alwaysRun = true)
+   public void testDelete() throws Exception {
+      final String requestId = api().delete(GROUP_NAME);
+      assertTrue(operationSucceeded.apply(requestId), requestId);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiMockTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiMockTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiMockTest.java
new file mode 100644
index 0000000..65960c2
--- /dev/null
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiMockTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertNull;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+import org.jclouds.azurecompute.internal.BaseAzureComputeApiMockTest;
+import org.jclouds.azurecompute.xml.ListAffinityGroupsHandlerTest;
+import org.jclouds.azurecompute.domain.CreateAffinityGroupParams;
+import org.jclouds.azurecompute.domain.UpdateAffinityGroupParams;
+
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "AffinityGroupApiMockTest")
+public class AffinityGroupApiMockTest extends BaseAzureComputeApiMockTest {
+
+   public void testList() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(xmlResponse("/affinityGroups.xml"));
+
+      try {
+         final AffinityGroupApi api = api(server.getUrl("/")).getAffinityGroupApi();
+
+         assertEquals(api.list(), ListAffinityGroupsHandlerTest.expected());
+
+         assertSent(server, "GET", "/affinitygroups");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testEmptyList() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      try {
+         final AffinityGroupApi api = api(server.getUrl("/")).getAffinityGroupApi();
+
+         assertTrue(api.list().isEmpty());
+
+         assertSent(server, "GET", "/affinitygroups");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testRead() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(xmlResponse("/affinityGroup.xml"));
+
+      try {
+         final AffinityGroupApi api = api(server.getUrl("/")).getAffinityGroupApi();
+
+         assertEquals(api.get("Test1"), ListAffinityGroupsHandlerTest.expected().get(0));
+
+         assertSent(server, "GET", "/affinitygroups/Test1");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testNullRead() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      try {
+         final AffinityGroupApi api = api(server.getUrl("/")).getAffinityGroupApi();
+
+         assertNull(api.get("Test1"));
+
+         assertSent(server, "GET", "/affinitygroups/Test1");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testAdd() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(requestIdResponse("request-1"));
+
+      try {
+         final AffinityGroupApi api = api(server.getUrl("/")).getAffinityGroupApi();
+
+         final CreateAffinityGroupParams params = CreateAffinityGroupParams.builder().name("mygroup").label("foo").
+                 location("West Europe").build();
+
+         assertEquals(api.add(params), "request-1");
+
+         assertSent(server, "POST", "/affinitygroups", "/createaffinitygroupparams.xml");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testUpdate() throws Exception {
+      MockWebServer server = mockAzureManagementServer();
+      server.enqueue(requestIdResponse("request-2"));
+
+      try {
+         final AffinityGroupApi api = api(server.getUrl("/")).getAffinityGroupApi();
+
+         final UpdateAffinityGroupParams params = UpdateAffinityGroupParams.builder().label("foo").
+                 description("mygroup description").build();
+
+         assertEquals(api.update("mygroup", params), "request-2");
+
+         assertSent(server, "PUT", "/affinitygroups/mygroup", "/updateaffinitygroupparams.xml");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testDelete() throws Exception {
+      MockWebServer server = mockAzureManagementServer();
+      server.enqueue(requestIdResponse("request-3"));
+
+      try {
+         final AffinityGroupApi api = api(server.getUrl("/")).getAffinityGroupApi();
+
+         assertEquals(api.delete("mygroup"), "request-3");
+
+         assertSent(server, "DELETE", "/affinitygroups/mygroup");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testNullDelete() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      try {
+         final AffinityGroupApi api = api(server.getUrl("/")).getAffinityGroupApi();
+
+         assertNull(api.delete("mygroup"));
+
+         assertSent(server, "DELETE", "/affinitygroups/mygroup");
+      } finally {
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/java/org/jclouds/azurecompute/features/DiskApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/DiskApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/DiskApiLiveTest.java
index e987482..1943077 100644
--- a/azurecompute/src/test/java/org/jclouds/azurecompute/features/DiskApiLiveTest.java
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/DiskApiLiveTest.java
@@ -20,10 +20,12 @@ import static com.google.common.collect.Iterables.transform;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
+import org.jclouds.azurecompute.domain.AffinityGroup;
 import org.jclouds.azurecompute.domain.Disk;
 import org.jclouds.azurecompute.domain.Location;
 import org.jclouds.azurecompute.domain.OSImage;
-import org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest;
+import org.jclouds.azurecompute.internal.AbstractAzureComputeApiLiveTest;
+
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -31,12 +33,14 @@ import com.google.common.base.Function;
 import com.google.common.collect.ImmutableSet;
 
 @Test(groups = "live", testName = "DiskApiLiveTest")
-public class DiskApiLiveTest extends BaseAzureComputeApiLiveTest {
+public class DiskApiLiveTest extends AbstractAzureComputeApiLiveTest {
 
    private ImmutableSet<String> locations;
 
    private ImmutableSet<String> images;
 
+   private ImmutableSet<String> groups;
+
    @BeforeClass(groups = {"integration", "live"})
    @Override
    public void setup() {
@@ -46,15 +50,22 @@ public class DiskApiLiveTest extends BaseAzureComputeApiLiveTest {
               new Function<Location, String>() {
 
                  @Override
-                 public String apply(Location in) {
-                    return in.name();
+                 public String apply(final Location location) {
+                    return location.name();
                  }
               }));
       images = ImmutableSet.copyOf(transform(api.getOSImageApi().list(), new Function<OSImage, String>() {
 
          @Override
-         public String apply(OSImage in) {
-            return in.name();
+         public String apply(final OSImage image) {
+            return image.name();
+         }
+      }));
+      groups = ImmutableSet.copyOf(transform(api.getAffinityGroupApi().list(), new Function<AffinityGroup, String>() {
+
+         @Override
+         public String apply(final AffinityGroup group) {
+            return group.name();
          }
       }));
    }
@@ -91,7 +102,7 @@ public class DiskApiLiveTest extends BaseAzureComputeApiLiveTest {
       }
 
       if (disk.affinityGroup() != null) {
-         // TODO: list affinityGroups and check if there
+         assertTrue(groups.contains(disk.affinityGroup()), "AffinityGroup not in " + groups + " :" + disk);
       }
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/java/org/jclouds/azurecompute/features/LocationApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/LocationApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/LocationApiLiveTest.java
index e73b995..8b43292 100644
--- a/azurecompute/src/test/java/org/jclouds/azurecompute/features/LocationApiLiveTest.java
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/LocationApiLiveTest.java
@@ -23,11 +23,12 @@ import java.util.Arrays;
 import java.util.List;
 
 import org.jclouds.azurecompute.domain.Location;
-import org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest;
+import org.jclouds.azurecompute.internal.AbstractAzureComputeApiLiveTest;
+
 import org.testng.annotations.Test;
 
 @Test(groups = "live", testName = "LocationApiLiveTest")
-public class LocationApiLiveTest extends BaseAzureComputeApiLiveTest {
+public class LocationApiLiveTest extends AbstractAzureComputeApiLiveTest {
 
    private static final List<String> KNOWN_SERVICES = Arrays
            .asList("Compute", "Storage", "PersistentVMRole", "HighMemory");
@@ -39,7 +40,7 @@ public class LocationApiLiveTest extends BaseAzureComputeApiLiveTest {
       }
    }
 
-   private void checkLocation(Location location) {
+   private void checkLocation(final Location location) {
       assertNotNull(location.name(), "Name cannot be null for a Location.");
       assertNotNull(location.displayName(), "DisplayName cannot be null for: " + location);
       assertNotNull(location.availableServices(), "AvailableServices cannot be null for: " + location.name());

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/java/org/jclouds/azurecompute/features/OSImageApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/OSImageApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/OSImageApiLiveTest.java
index 69941db..ab1f8df 100644
--- a/azurecompute/src/test/java/org/jclouds/azurecompute/features/OSImageApiLiveTest.java
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/OSImageApiLiveTest.java
@@ -22,9 +22,11 @@ import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
+import org.jclouds.azurecompute.domain.AffinityGroup;
 import org.jclouds.azurecompute.domain.Location;
 import org.jclouds.azurecompute.domain.OSImage;
-import org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest;
+import org.jclouds.azurecompute.internal.AbstractAzureComputeApiLiveTest;
+
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -34,10 +36,12 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
 @Test(groups = "live", testName = "OSImageApiLiveTest")
-public class OSImageApiLiveTest extends BaseAzureComputeApiLiveTest {
+public class OSImageApiLiveTest extends AbstractAzureComputeApiLiveTest {
 
    private ImmutableSet<String> locations;
 
+   private ImmutableSet<String> groups;
+
    @BeforeClass(groups = {"integration", "live"})
    @Override
    public void setup() {
@@ -46,8 +50,15 @@ public class OSImageApiLiveTest extends BaseAzureComputeApiLiveTest {
       locations = ImmutableSet.copyOf(transform(api.getLocationApi().list(), new Function<Location, String>() {
 
          @Override
-         public String apply(final Location in) {
-            return in.name();
+         public String apply(final Location location) {
+            return location.name();
+         }
+      }));
+      groups = ImmutableSet.copyOf(transform(api.getAffinityGroupApi().list(), new Function<AffinityGroup, String>() {
+
+         @Override
+         public String apply(final AffinityGroup group) {
+            return group.name();
          }
       }));
    }
@@ -80,7 +91,7 @@ public class OSImageApiLiveTest extends BaseAzureComputeApiLiveTest {
       // Ex. Dirty data in RightScale eula field comes out as an empty string.
       assertFalse(osImage.eula().contains(""));
       if (osImage.affinityGroup() != null) {
-         // TODO: list getAffinityGroups and check if there
+         assertTrue(locations.contains(osImage.affinityGroup()), "No " + osImage.affinityGroup() + " in " + locations);
       }
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/java/org/jclouds/azurecompute/features/SubscriptionApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/SubscriptionApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/SubscriptionApiLiveTest.java
index a0a29a6..7ae63f8 100644
--- a/azurecompute/src/test/java/org/jclouds/azurecompute/features/SubscriptionApiLiveTest.java
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/SubscriptionApiLiveTest.java
@@ -17,12 +17,13 @@
 package org.jclouds.azurecompute.features;
 
 import static org.testng.Assert.assertNotNull;
+
 import org.jclouds.azurecompute.domain.RoleSize;
-import org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest;
+import org.jclouds.azurecompute.internal.AbstractAzureComputeApiLiveTest;
 import org.testng.annotations.Test;
 
 @Test(groups = "live", testName = "SubscriptionApiLiveTest")
-public class SubscriptionApiLiveTest extends BaseAzureComputeApiLiveTest {
+public class SubscriptionApiLiveTest extends AbstractAzureComputeApiLiveTest {
 
    @Test
    public void testList() {
@@ -31,7 +32,7 @@ public class SubscriptionApiLiveTest extends BaseAzureComputeApiLiveTest {
       }
    }
 
-   private void checkLocation(RoleSize roleSize) {
+   private void checkLocation(final RoleSize roleSize) {
       assertNotNull(roleSize.name(), "Name cannot be null for a Location.");
       assertNotNull(roleSize.label(), "Label cannot be null for: " + roleSize);
       assertNotNull(roleSize.cores(), "Cores cannot be null for: " + roleSize.name());

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/java/org/jclouds/azurecompute/internal/AbstractAzureComputeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/internal/AbstractAzureComputeApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/internal/AbstractAzureComputeApiLiveTest.java
new file mode 100644
index 0000000..40c6706
--- /dev/null
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/internal/AbstractAzureComputeApiLiveTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.internal;
+
+import com.google.common.base.Predicate;
+import java.util.Random;
+import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.azurecompute.AzureComputeApi;
+import org.jclouds.azurecompute.compute.config.AzureComputeServiceContextModule;
+import org.testng.annotations.BeforeClass;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.util.Predicates2.retry;
+
+public abstract class AbstractAzureComputeApiLiveTest extends BaseApiLiveTest<AzureComputeApi> {
+
+   protected static final int RAND = new Random().nextInt(999);
+
+   protected Predicate<String> operationSucceeded;
+
+   public AbstractAzureComputeApiLiveTest() {
+      provider = "azurecompute";
+   }
+
+   @BeforeClass
+   @Override
+   public void setup() {
+      super.setup();
+
+      operationSucceeded = retry(
+              new AzureComputeServiceContextModule.OperationSucceededPredicate(api), 600, 5, 5, SECONDS);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/java/org/jclouds/azurecompute/internal/BaseAzureComputeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/internal/BaseAzureComputeApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/internal/BaseAzureComputeApiLiveTest.java
index c0cb9b0..8222623 100644
--- a/azurecompute/src/test/java/org/jclouds/azurecompute/internal/BaseAzureComputeApiLiveTest.java
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/internal/BaseAzureComputeApiLiveTest.java
@@ -24,11 +24,13 @@ import static org.jclouds.util.Predicates2.retry;
 import static org.testng.Assert.assertTrue;
 
 import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+
 import java.util.List;
 import java.util.logging.Logger;
+import java.util.UUID;
+import java.util.logging.Level;
 
-import org.jclouds.apis.BaseApiLiveTest;
-import org.jclouds.azurecompute.AzureComputeApi;
 import org.jclouds.azurecompute.domain.CloudService;
 import org.jclouds.azurecompute.domain.Deployment;
 import org.jclouds.azurecompute.domain.DeploymentParams;
@@ -36,25 +38,16 @@ import org.jclouds.azurecompute.domain.NetworkConfiguration;
 import org.jclouds.azurecompute.domain.NetworkConfiguration.VirtualNetworkConfiguration;
 import org.jclouds.azurecompute.domain.StorageService;
 import org.jclouds.azurecompute.domain.StorageServiceParams;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-
-import java.util.Random;
-import java.util.UUID;
-import java.util.logging.Level;
 import org.jclouds.azurecompute.AzureTestUtils;
 import org.jclouds.azurecompute.AzureTestUtils.SameVirtualNetworkSiteNamePredicate;
-import org.jclouds.azurecompute.compute.config.AzureComputeServiceContextModule;
 import org.jclouds.azurecompute.domain.NetworkConfiguration.AddressSpace;
 import org.jclouds.azurecompute.domain.NetworkConfiguration.Subnet;
 import org.jclouds.azurecompute.util.ConflictManagementPredicate;
 
-public class BaseAzureComputeApiLiveTest extends BaseApiLiveTest<AzureComputeApi> {
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
 
-   protected static final int RAND = new Random().nextInt(999);
+public class BaseAzureComputeApiLiveTest extends AbstractAzureComputeApiLiveTest {
 
    public static final String DEFAULT_ADDRESS_SPACE = "10.0.0.0/20";
 
@@ -71,16 +64,10 @@ public class BaseAzureComputeApiLiveTest extends BaseApiLiveTest<AzureComputeApi
 
    protected StorageService storageService;
 
-   protected Predicate<String> operationSucceeded;
-
    protected VirtualNetworkSite virtualNetworkSite;
 
    private String storageServiceName = null;
 
-   public BaseAzureComputeApiLiveTest() {
-      provider = "azurecompute";
-   }
-
    protected String getStorageServiceName() {
       if (storageServiceName == null) {
          storageServiceName = String.format("%3.24s",
@@ -94,9 +81,6 @@ public class BaseAzureComputeApiLiveTest extends BaseApiLiveTest<AzureComputeApi
    public void setup() {
       super.setup();
 
-      operationSucceeded = retry(
-              new AzureComputeServiceContextModule.OperationSucceededPredicate(api), 600, 5, 5, SECONDS);
-
       virtualNetworkSite = getOrCreateVirtualNetworkSite(VIRTUAL_NETWORK_NAME, LOCATION);
 
       final StorageServiceParams params = StorageServiceParams.builder().
@@ -111,6 +95,8 @@ public class BaseAzureComputeApiLiveTest extends BaseApiLiveTest<AzureComputeApi
    @AfterClass(alwaysRun = true)
    @Override
    protected void tearDown() {
+      super.tearDown();
+      
       retry(new ConflictManagementPredicate(operationSucceeded) {
 
          @Override

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ListAffinityGroupsHandlerTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ListAffinityGroupsHandlerTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ListAffinityGroupsHandlerTest.java
new file mode 100644
index 0000000..101f658
--- /dev/null
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ListAffinityGroupsHandlerTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.xml;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.jclouds.azurecompute.domain.AffinityGroup;
+import org.jclouds.azurecompute.domain.AffinityGroup.ComputeCapabilities;
+import org.jclouds.azurecompute.domain.RoleSize;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.http.functions.BaseHandlerTest;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+@Test(groups = "unit", testName = "ListAffinityGroupsHandlerTest")
+public class ListAffinityGroupsHandlerTest extends BaseHandlerTest {
+
+   public void test() {
+      final InputStream input = getClass().getResourceAsStream("/affinityGroups.xml");
+      final List<AffinityGroup> result = factory.create(new ListAffinityGroupsHandler(
+              new AffinityGroupHandler(new ComputeCapabilitiesHandler()))).parse(input);
+
+      assertEquals(result, expected());
+   }
+
+   public static List<AffinityGroup> expected() {
+      return ImmutableList.of(
+              AffinityGroup.create(
+                      "Test1",
+                      "Test1",
+                      "Test 1 description",
+                      "West Europe",
+                      ImmutableList.of(
+                              AffinityGroup.Capability.PersistentVMRole,
+                              AffinityGroup.Capability.HighMemory
+                      ),
+                      new SimpleDateFormatDateService().iso8601DateOrSecondsDateParse("2015-03-09T15:15:29Z"),
+                      ComputeCapabilities.create(
+                              ImmutableList.of(
+                                      RoleSize.Type.A10,
+                                      RoleSize.Type.A11
+                              ),
+                              ImmutableList.of(
+                                      RoleSize.Type.A10,
+                                      RoleSize.Type.A11)
+                      )
+              ),
+              AffinityGroup.create(
+                      "Test2",
+                      "Test2",
+                      null,
+                      "Southeast Asia",
+                      ImmutableList.of(
+                              AffinityGroup.Capability.PersistentVMRole,
+                              AffinityGroup.Capability.HighMemory
+                      ),
+                      new SimpleDateFormatDateService().iso8601DateOrSecondsDateParse("2015-03-09T15:16:10Z"),
+                      ComputeCapabilities.create(
+                              ImmutableList.of(
+                                      RoleSize.Type.EXTRALARGE,
+                                      RoleSize.Type.EXTRASMALL
+                              ),
+                              ImmutableList.of(
+                                      RoleSize.Type.EXTRALARGE,
+                                      RoleSize.Type.EXTRASMALL
+                              )
+                      )
+              )
+      );
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/resources/affinityGroup.xml
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/resources/affinityGroup.xml b/azurecompute/src/test/resources/affinityGroup.xml
new file mode 100644
index 0000000..694cd98
--- /dev/null
+++ b/azurecompute/src/test/resources/affinityGroup.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<AffinityGroup xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
+   <Name>Test1</Name>
+   <Label>VGVzdDE=</Label>
+   <Description>Test 1 description</Description>
+   <Location>West Europe</Location>
+   <Capabilities>
+      <Capability>PersistentVMRole</Capability>
+      <Capability>HighMemory</Capability>
+   </Capabilities>
+   <CreatedTime>2015-03-09T15:15:29Z</CreatedTime>
+   <ComputeCapabilities>
+      <WebWorkerRoleSizes>
+         <RoleSize>A10</RoleSize>
+         <RoleSize>A11</RoleSize>
+      </WebWorkerRoleSizes>
+      <VirtualMachinesRoleSizes>
+         <RoleSize>A10</RoleSize>
+         <RoleSize>A11</RoleSize>
+      </VirtualMachinesRoleSizes>
+   </ComputeCapabilities>
+</AffinityGroup>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/resources/affinityGroups.xml
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/resources/affinityGroups.xml b/azurecompute/src/test/resources/affinityGroups.xml
new file mode 100644
index 0000000..75a1968
--- /dev/null
+++ b/azurecompute/src/test/resources/affinityGroups.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<AffinityGroups xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
+  <AffinityGroup>
+    <Name>Test1</Name>
+    <Label>VGVzdDE=</Label>
+    <Description>Test 1 description</Description>
+    <Location>West Europe</Location>
+    <Capabilities>
+      <Capability>PersistentVMRole</Capability>
+      <Capability>HighMemory</Capability>
+    </Capabilities>
+    <CreatedTime>2015-03-09T15:15:29Z</CreatedTime>
+    <ComputeCapabilities>
+      <WebWorkerRoleSizes>
+        <RoleSize>A10</RoleSize>
+        <RoleSize>A11</RoleSize>
+      </WebWorkerRoleSizes>
+      <VirtualMachinesRoleSizes>
+        <RoleSize>A10</RoleSize>
+        <RoleSize>A11</RoleSize>
+      </VirtualMachinesRoleSizes>
+    </ComputeCapabilities>
+  </AffinityGroup>
+  <AffinityGroup>
+    <Name>Test2</Name>
+    <Label>VGVzdDI=</Label>
+    <Description/>
+    <Location>Southeast Asia</Location>
+    <Capabilities>
+      <Capability>PersistentVMRole</Capability>
+      <Capability>HighMemory</Capability>
+    </Capabilities>
+    <CreatedTime>2015-03-09T15:16:10Z</CreatedTime>
+    <ComputeCapabilities>
+      <WebWorkerRoleSizes>
+        <RoleSize>ExtraLarge</RoleSize>
+        <RoleSize>ExtraSmall</RoleSize>
+      </WebWorkerRoleSizes>
+      <VirtualMachinesRoleSizes>
+        <RoleSize>ExtraLarge</RoleSize>
+        <RoleSize>ExtraSmall</RoleSize>
+      </VirtualMachinesRoleSizes>
+    </ComputeCapabilities>
+  </AffinityGroup>
+</AffinityGroups>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/resources/createaffinitygroupparams.xml
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/resources/createaffinitygroupparams.xml b/azurecompute/src/test/resources/createaffinitygroupparams.xml
new file mode 100644
index 0000000..40969e2
--- /dev/null
+++ b/azurecompute/src/test/resources/createaffinitygroupparams.xml
@@ -0,0 +1 @@
+<CreateAffinityGroup xmlns="http://schemas.microsoft.com/windowsazure"><Name>mygroup</Name><Label>Zm9v</Label><Location>West Europe</Location></CreateAffinityGroup>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/d928dbcc/azurecompute/src/test/resources/updateaffinitygroupparams.xml
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/resources/updateaffinitygroupparams.xml b/azurecompute/src/test/resources/updateaffinitygroupparams.xml
new file mode 100644
index 0000000..f49a6b9
--- /dev/null
+++ b/azurecompute/src/test/resources/updateaffinitygroupparams.xml
@@ -0,0 +1 @@
+<UpdateAffinityGroup xmlns="http://schemas.microsoft.com/windowsazure"><Label>Zm9v</Label><Description>mygroup description</Description></UpdateAffinityGroup>
\ No newline at end of file