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/13 11:32:28 UTC

[2/2] jclouds-labs git commit: [JCLOUDS-873] Provided network security group operations + some important fixes around live test execution

[JCLOUDS-873] Provided network security group operations + some important fixes around live test execution


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

Branch: refs/heads/master
Commit: 29435e1c35609fc2d1ae0878243c3af56a8297cc
Parents: eae8910
Author: fmartelli <fa...@gmail.com>
Authored: Mon Mar 30 17:51:04 2015 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Mon Apr 13 11:28:18 2015 +0200

----------------------------------------------------------------------
 .../AzureManagementApiMetadata.java             |   2 +-
 .../jclouds/azurecompute/binders/RuleToXML.java |   6 +-
 .../compute/AzureComputeServiceAdapter.java     |  74 ++++-
 .../AzureComputeServiceContextModule.java       |  45 +--
 .../AzureComputeSecurityGroupExtension.java     | 126 +++++----
 ...ServiceAndVirtualNetworkThenCreateNodes.java |   2 +-
 .../domain/NetworkSecurityGroup.java            |  39 ++-
 .../org/jclouds/azurecompute/domain/Rule.java   |  96 ++++++-
 .../features/NetworkSecurityGroupApi.java       |  95 +++++--
 .../util/ConflictManagementPredicate.java       | 185 +++++++++----
 .../xml/NetworkSecurityGroupHandler.java        |   7 +-
 .../jclouds/azurecompute/xml/RuleHandler.java   |  20 +-
 .../AzureComputeServiceAdapterLiveTest.java     |  30 +-
 .../AzureComputeServiceContextLiveTest.java     |  25 +-
 .../features/AffinityGroupApiLiveTest.java      |   4 +-
 .../features/DeploymentApiLiveTest.java         |  12 +-
 .../NetworkSecurityGroupApiLiveTest.java        | 226 +++++++++++++++
 .../NetworkSecurityGroupApiMockTest.java        | 277 +++++++++++++++++++
 .../features/TrafficManagerApiLiveTest.java     |  10 +-
 .../features/VirtualMachineApiLiveTest.java     |  46 +--
 .../features/VirtualNetworkApiLiveTest.java     |  15 +-
 .../AbstractAzureComputeApiLiveTest.java        |   9 +-
 .../internal/BaseAzureComputeApiLiveTest.java   |  31 ++-
 .../ListNetworkSecurityGroupsHandlerTest.java   |  47 ++++
 .../xml/NetworkSecurityGroupHandlerTest.java    |  68 +++--
 .../src/test/resources/networksecuritygroup.xml |  98 +------
 .../resources/networksecuritygroupforsubnet.xml |   4 +
 .../networksecuritygroupfulldetails.xml         |  97 +++++++
 .../test/resources/networksecuritygroups.xml    |  12 +
 29 files changed, 1307 insertions(+), 401 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java
index ebd0c74..406b029 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java
@@ -51,7 +51,7 @@ public class AzureManagementApiMetadata extends BaseHttpApiMetadata<AzureCompute
    public static Properties defaultProperties() {
       final Properties properties = BaseHttpApiMetadata.defaultProperties();
       // Sometimes SSH Authentication failure happens in Azure.
-      // It seems that the authorized key is injected after ssh has been started.  
+      // It seems that the authorized key is injected after ssh has been started.
       properties.setProperty("jclouds.ssh.max-retries", "15");
       properties.setProperty("jclouds.ssh.retry-auth", "true");
       return properties;

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RuleToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RuleToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RuleToXML.java
index 6822728..bcb1b3a 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RuleToXML.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RuleToXML.java
@@ -32,14 +32,14 @@ public final class RuleToXML implements Binder {
       Rule rule = Rule.class.cast(input);
       try {
          String xml = XMLBuilder.create("Rule", "http://schemas.microsoft.com/windowsazure")
-                 .e("Type").t(rule.type()).up()
+                 .e("Type").t(rule.type().name()).up()
                  .e("Priority").t(rule.priority()).up()
-                 .e("Action").t(rule.action()).up()
+                 .e("Action").t(rule.action().name()).up()
                  .e("SourceAddressPrefix").t(rule.sourceAddressPrefix()).up()
                  .e("SourcePortRange").t(rule.sourcePortRange()).up()
                  .e("DestinationAddressPrefix").t(rule.destinationAddressPrefix()).up()
                  .e("DestinationPortRange").t(rule.destinationPortRange()).up()
-                 .e("Protocol").t(rule.protocol()).up().asString();
+                 .e("Protocol").t(rule.protocol().getValue()).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/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java
index 30f2ca6..a045674 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java
@@ -44,7 +44,6 @@ import org.jclouds.azurecompute.domain.Deployment.RoleInstance;
 import org.jclouds.azurecompute.domain.Role;
 import org.jclouds.azurecompute.compute.functions.OSImageToImage;
 import org.jclouds.azurecompute.options.AzureComputeTemplateOptions;
-import org.jclouds.azurecompute.util.ConflictManagementPredicate;
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.Template;
@@ -60,6 +59,7 @@ import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import org.jclouds.azurecompute.util.ConflictManagementPredicate;
 
 /**
  * Defines the connection between the {@link AzureComputeApi} implementation and the jclouds
@@ -111,8 +111,8 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Deploym
       final String subnetName = templateOptions.getSubnetName().get();
 
       logger.debug("Creating a cloud service with name '%s', label '%s' in location '%s'", name, name, location);
-      final String createCloudServiceRequestId =
-              api.getCloudServiceApi().createWithLabelInLocation(name, name, location);
+      final String createCloudServiceRequestId
+              = api.getCloudServiceApi().createWithLabelInLocation(name, name, location);
       if (!operationSucceededPredicate.apply(createCloudServiceRequestId)) {
          final String message = generateIllegalStateExceptionMessage(
                  createCloudServiceRequestId, azureComputeConstants.operationTimeout());
@@ -142,13 +142,23 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Deploym
               .build();
 
       logger.debug("Creating a deployment with params '%s' ...", params);
-      retry(new ConflictManagementPredicate() {
 
+      if (!new ConflictManagementPredicate(api) {
          @Override
          protected String operation() {
             return api.getDeploymentApiForService(name).create(params);
          }
-      }, 30 * 60, 1, SECONDS).apply(name);
+      }.apply(name)) {
+         final String message = generateIllegalStateExceptionMessage(
+                 createCloudServiceRequestId, azureComputeConstants.operationTimeout());
+         logger.warn(message);
+         logger.debug("Deleting cloud service (%s) ...", name);
+         deleteCloudService(name);
+         logger.debug("Cloud service (%s) deleted.", name);
+         throw new IllegalStateException(message);
+      }
+
+      logger.info("Deployment created with name: %s", name);
 
       final Set<Deployment> deployments = Sets.newHashSet();
       if (!retry(new Predicate<String>() {
@@ -300,8 +310,8 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Deploym
 
          @Override
          public boolean apply(final CloudService input) {
-            final Deployment deployment =
-                    input.status() == CloudService.Status.DELETING || input.status() == CloudService.Status.DELETED
+            final Deployment deployment
+                    = input.status() == CloudService.Status.DELETING || input.status() == CloudService.Status.DELETED
                             ? null
                             : api.getDeploymentApiForService(input.name()).get(id);
             return deployment != null && deployment.status() != Deployment.Status.DELETING;
@@ -331,13 +341,19 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Deploym
 
          final String cloudServiceName = cloudService.name();
          logger.debug("Deleting deployment(%s) of cloud service (%s)", id, cloudServiceName);
-         retry(new ConflictManagementPredicate(operationSucceededPredicate) {
+
+         if (!new ConflictManagementPredicate(api, operationSucceededPredicate) {
 
             @Override
             protected String operation() {
                return api.getDeploymentApiForService(cloudServiceName).delete(id);
             }
-         }, 30 * 60, 1, SECONDS).apply(id);
+         }.apply(id)) {
+            final String message = generateIllegalStateExceptionMessage(
+                    "Delete deployment", azureComputeConstants.operationTimeout());
+            logger.warn(message);
+            throw new IllegalStateException(message);
+         }
 
          logger.debug("Deleting cloud service (%s) ...", cloudServiceName);
          trackRequest(api.getCloudServiceApi().delete(cloudServiceName));
@@ -347,13 +363,17 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Deploym
             for (Role role : deployment.roleList()) {
                final Role.OSVirtualHardDisk disk = role.osVirtualHardDisk();
                if (disk != null) {
-                  retry(new ConflictManagementPredicate(operationSucceededPredicate) {
+                  if (!new ConflictManagementPredicate(api, operationSucceededPredicate) {
 
                      @Override
                      protected String operation() {
                         return api.getDiskApi().delete(disk.diskName());
                      }
-                  }, 30 * 60, 1, SECONDS).apply(id);
+                  }.apply(id)) {
+                     final String message = generateIllegalStateExceptionMessage(
+                             "Delete disk", azureComputeConstants.operationTimeout());
+                     logger.warn(message);
+                  }
                }
             }
          }
@@ -425,4 +445,36 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Deploym
       return URI.create(
               String.format("https://%s.blob.core.windows.net/vhds/disk-%s.vhd", storageServiceName, diskName));
    }
+
+   private void deleteCloudService(final String name) {
+      if (!new ConflictManagementPredicate(api) {
+
+         @Override
+         protected String operation() {
+            return api.getCloudServiceApi().delete(name);
+         }
+
+      }.apply(name)) {
+         final String deleteMessage = generateIllegalStateExceptionMessage(
+                 "CloudService delete", azureComputeConstants.operationTimeout());
+         logger.warn(deleteMessage);
+         throw new IllegalStateException(deleteMessage);
+      }
+   }
+
+   private void deleteDeployment(final String id, final String cloudServiceName) {
+      if (!new ConflictManagementPredicate(api) {
+
+         @Override
+         protected String operation() {
+            return api.getDeploymentApiForService(cloudServiceName).delete(id);
+         }
+
+      }.apply(id)) {
+         final String deleteMessage = generateIllegalStateExceptionMessage(
+                 "Deployment delete", azureComputeConstants.operationTimeout());
+         logger.warn(deleteMessage);
+         throw new IllegalStateException(deleteMessage);
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java
index e129ada..1e07798 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java
@@ -16,7 +16,6 @@
  */
 package org.jclouds.azurecompute.compute.config;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.azurecompute.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
 import static org.jclouds.azurecompute.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
 import static org.jclouds.azurecompute.config.AzureComputeProperties.OPERATION_TIMEOUT;
@@ -39,7 +38,6 @@ import org.jclouds.azurecompute.compute.strategy.impl.AzureAdaptingComputeServic
 import org.jclouds.azurecompute.domain.Deployment;
 import org.jclouds.azurecompute.domain.Location;
 import org.jclouds.azurecompute.domain.OSImage;
-import org.jclouds.azurecompute.domain.Operation;
 import org.jclouds.azurecompute.domain.RoleSize;
 import org.jclouds.azurecompute.options.AzureComputeTemplateOptions;
 import org.jclouds.compute.ComputeServiceAdapter;
@@ -49,9 +47,8 @@ import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
-import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
 import org.jclouds.compute.strategy.impl.AdaptingComputeServiceStrategies;
-import org.jclouds.util.Predicates2;
+import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
 
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
@@ -61,6 +58,9 @@ import com.google.inject.Injector;
 import com.google.inject.Provides;
 import com.google.inject.TypeLiteral;
 
+import java.util.concurrent.TimeUnit;
+import org.jclouds.azurecompute.util.ConflictManagementPredicate;
+
 public class AzureComputeServiceContextModule
         extends ComputeServiceAdapterContextModule<Deployment, RoleSize, OSImage, Location> {
 
@@ -103,37 +103,12 @@ public class AzureComputeServiceContextModule
    @Singleton
    protected Predicate<String> provideOperationSucceededPredicate(
            final AzureComputeApi api, final AzureComputeConstants azureComputeConstants) {
-
-      return Predicates2.retry(new OperationSucceededPredicate(api),
-              azureComputeConstants.operationTimeout(), azureComputeConstants.operationPollInitialPeriod(),
-              azureComputeConstants.operationPollMaxPeriod());
-   }
-
-   public static class OperationSucceededPredicate implements Predicate<String> {
-
-      private final AzureComputeApi api;
-
-      public OperationSucceededPredicate(final AzureComputeApi api) {
-         this.api = checkNotNull(api, "api must not be null");
-      }
-
-      @Override
-      public boolean apply(final String input) {
-         final Operation operation = api.getOperationApi().get(input);
-         switch (operation.status()) {
-            case SUCCEEDED:
-               return true;
-
-            case IN_PROGRESS:
-            case FAILED:
-            case UNRECOGNIZED:
-               return false;
-
-            default:
-               throw new IllegalStateException("Operation is in invalid status: " + operation.status().name());
-         }
-      }
-
+      return new ConflictManagementPredicate(
+              api,
+              azureComputeConstants.operationTimeout(),
+              azureComputeConstants.operationPollInitialPeriod(),
+              azureComputeConstants.operationPollMaxPeriod(),
+              TimeUnit.MILLISECONDS);
    }
 
    @Singleton

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/compute/extensions/AzureComputeSecurityGroupExtension.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/extensions/AzureComputeSecurityGroupExtension.java b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/extensions/AzureComputeSecurityGroupExtension.java
index f6bab8f..8848127 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/extensions/AzureComputeSecurityGroupExtension.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/extensions/AzureComputeSecurityGroupExtension.java
@@ -17,9 +17,6 @@
 package org.jclouds.azurecompute.compute.extensions;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.jclouds.azurecompute.compute.AzureComputeServiceAdapter.generateIllegalStateExceptionMessage;
-import static org.jclouds.util.Predicates2.retry;
 
 import java.util.List;
 import java.util.Set;
@@ -56,6 +53,7 @@ import com.google.common.base.Splitter;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
+import static org.jclouds.azurecompute.compute.AzureComputeServiceAdapter.generateIllegalStateExceptionMessage;
 
 /**
  * An extension to compute service to allow for the manipulation of {@link org.jclouds.compute.domain.SecurityGroup}s.
@@ -100,7 +98,7 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio
 
    /**
     * @param name it represents both cloudservice and deployment name
-    * @return Set<SecurityGroup>
+    * @return Set&lt;SecurityGroup&gt;
     */
    @Override
    public Set<SecurityGroup> listSecurityGroupsForNode(final String name) {
@@ -152,7 +150,8 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio
       checkNotNull(name, "name");
       checkNotNull(location, "location");
 
-      final NetworkSecurityGroup networkSecurityGroup = NetworkSecurityGroup.create(name, name, location.getId(), null);
+      final NetworkSecurityGroup networkSecurityGroup = NetworkSecurityGroup.create(
+              name, name, location.getId(), null, null);
       final String createNSGRequestId = api.getNetworkSecurityGroupApi().create(networkSecurityGroup);
       if (!operationSucceededPredicate.apply(createNSGRequestId)) {
          final String message = generateIllegalStateExceptionMessage(
@@ -183,19 +182,23 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio
                if (virtualNetworkName != null && subnetName != null) {
                   final NetworkSecurityGroup networkSecurityGroupAppliedToSubnet = api.getNetworkSecurityGroupApi()
                           .getNetworkSecurityGroupAppliedToSubnet(virtualNetworkName, subnetName);
-                  if (networkSecurityGroupAppliedToSubnet != null) {
-                     if (!networkSecurityGroupAppliedToSubnet.name().equals(id)) {
-                        logger.debug("Removing a networkSecurityGroup %s is already applied to subnet '%s' ...",
-                                id, subnetName);
-                        // remove existing nsg from subnet
-                        String removeFromSubnetRequestId = api.getNetworkSecurityGroupApi().removeFromSubnet(
-                                virtualNetworkName, subnetName, networkSecurityGroupAppliedToSubnet.name());
-                        if (!operationSucceededPredicate.apply(removeFromSubnetRequestId)) {
-                           final String message = generateIllegalStateExceptionMessage(
-                                   removeFromSubnetRequestId, azureComputeConstants.operationTimeout());
-                           logger.warn(message);
-                           throw new IllegalStateException(message);
+                  if (networkSecurityGroupAppliedToSubnet != null
+                          && networkSecurityGroupAppliedToSubnet.name().equals(id)) {
+                     logger.debug("Removing a networkSecurityGroup %s is already applied to subnet '%s' ...",
+                             id, subnetName);
+
+                     // remove existing nsg from subnet
+                     if (!new ConflictManagementPredicate(api, operationSucceededPredicate) {
+                        @Override
+                        protected String operation() {
+                           return api.getNetworkSecurityGroupApi().removeFromSubnet(
+                                   virtualNetworkName, subnetName, id);
                         }
+                     }.apply(id)) {
+                        final String message = generateIllegalStateExceptionMessage(
+                                "Remove security group from subnet", azureComputeConstants.operationTimeout());
+                        logger.warn(message);
+                        throw new IllegalStateException(message);
                      }
                   }
                }
@@ -249,24 +252,27 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio
                      }
                   }
 
-                  retry(new ConflictManagementPredicate(
-                          operationSucceededPredicate,
-                          azureComputeConstants.operationTimeout()) {
-
-                             @Override
-                             protected String operation() {
-                                // Check for deployment validity
-                                final Deployment deployment = api.getDeploymentApiForService(
-                                        service.name()).get(service.name());
-                                if (deployment == null || deployment.status() == Status.DELETING) {
-                                   return null;
-                                } else {
-                                   return api.getVirtualMachineApiForDeploymentInService(
-                                           deployment.name(), deployment.name()).
+                  if (!new ConflictManagementPredicate(api, operationSucceededPredicate) {
+
+                     @Override
+                     protected String operation() {
+                        // Check for deployment validity
+                        final Deployment deployment = api.getDeploymentApiForService(
+                                service.name()).get(service.name());
+                        if (deployment == null || deployment.status() == Status.DELETING) {
+                           return null;
+                        } else {
+                           return api.getVirtualMachineApiForDeploymentInService(
+                                   deployment.name(), deployment.name()).
                                    updateRole(role.roleName(), role);
-                                }
-                             }
-                          }, 600, 30, 30, SECONDS).apply(role.roleName());
+                        }
+                     }
+                  }.apply(role.roleName())) {
+                     final String message = generateIllegalStateExceptionMessage(
+                             "Operation", azureComputeConstants.operationTimeout());
+                     logger.warn(message);
+                     throw new IllegalStateException(message);
+                  }
                }
             }
          }
@@ -329,24 +335,25 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio
                      }
                   }
 
-                  retry(new ConflictManagementPredicate(
-                          operationSucceededPredicate,
-                          azureComputeConstants.operationTimeout()) {
-
-                             @Override
-                             protected String operation() {
-                                // Check for deployment validity
-                                final Deployment deployment = api.getDeploymentApiForService(
-                                        service.name()).get(service.name());
-                                if (deployment == null || deployment.status() == Status.DELETING) {
-                                   return null;
-                                } else {
-                                   return api.getVirtualMachineApiForDeploymentInService(
-                                           deployment.name(), deployment.name()).
-                                   updateRole(role.roleName(), role);
-                                }
-                             }
-                          }, 600, 30, 30, SECONDS).apply(role.roleName());
+                  if (!new ConflictManagementPredicate(api, operationSucceededPredicate) {
+                     @Override
+                     protected String operation() {
+                        // Check for deployment validity
+                        final Deployment deployment = api.getDeploymentApiForService(
+                                service.name()).get(service.name());
+                        if (deployment == null || deployment.status() == Status.DELETING) {
+                           return null;
+                        } else {
+                           return api.getVirtualMachineApiForDeploymentInService(
+                                   deployment.name(), deployment.name()).updateRole(role.roleName(), role);
+                        }
+                     }
+                  }.apply(role.roleName())) {
+                     final String message = generateIllegalStateExceptionMessage(
+                             "Operation", azureComputeConstants.operationTimeout());
+                     logger.warn(message);
+                     throw new IllegalStateException(message);
+                  }
                }
             }
          }
@@ -410,7 +417,8 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio
             builder.fromPort(extractPort(rule.name(), 0))
                     .toPort(extractPort(rule.name(), 1));
          }
-         builder.ipProtocol(rule.protocol().equals("*") ? IpProtocol.ALL : IpProtocol.valueOf(rule.protocol()));
+         builder.ipProtocol(rule.protocol().equals(Rule.Protocol.ALL)
+                 ? IpProtocol.ALL : IpProtocol.valueOf(rule.protocol().getValue()));
          if (rule.destinationAddressPrefix().equals("*")) {
             builder.cidrBlock("0.0.0.0/0");
          } else {
@@ -450,24 +458,20 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio
       final String destinationPortRange = ipPermission.getFromPort() == ipPermission.getToPort()
               ? String.valueOf(ipPermission.getToPort())
               : String.format("%s-%s", ipPermission.getFromPort(), ipPermission.getToPort());
-      final String destinationAddressPrefix =
-              ipPermission.getCidrBlocks().isEmpty()
+      final String destinationAddressPrefix = ipPermission.getCidrBlocks().isEmpty()
               || Iterables.get(ipPermission.getCidrBlocks(), 0).equals("0.0.0.0/0")
                       ? "*"
                       : Iterables.get(ipPermission.getCidrBlocks(), 0);
       final String setRuleToNSGRequestId = api.getNetworkSecurityGroupApi().
               setRule(networkSecurityGroupId, ruleName, Rule.create(ruleName, // name
-                              "Inbound", // type
+                              Rule.Type.Inbound, // type
                               String.valueOf(priority), // priority
-                              "Allow", // action
+                              Rule.Action.Allow, // action
                               "INTERNET", // sourceAddressPrefix
                               "*", // sourcePortRange
                               destinationAddressPrefix, // destinationAddressPrefix
                               destinationPortRange, // destinationPortRange
-                              protocol, // protocol
-                              "Active", // state
-                              true // isDefault
-                      ));
+                              Rule.Protocol.fromString(protocol)));
       if (!operationSucceededPredicate.apply(setRuleToNSGRequestId)) {
          final String message = generateIllegalStateExceptionMessage(
                  setRuleToNSGRequestId, azureComputeConstants.operationTimeout());

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/compute/strategy/GetOrCreateStorageServiceAndVirtualNetworkThenCreateNodes.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/strategy/GetOrCreateStorageServiceAndVirtualNetworkThenCreateNodes.java b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/strategy/GetOrCreateStorageServiceAndVirtualNetworkThenCreateNodes.java
index 5a8c681..b2301b6 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/strategy/GetOrCreateStorageServiceAndVirtualNetworkThenCreateNodes.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/strategy/GetOrCreateStorageServiceAndVirtualNetworkThenCreateNodes.java
@@ -166,7 +166,7 @@ public class GetOrCreateStorageServiceAndVirtualNetworkThenCreateNodes
          logger.debug("Adding a networkSecurityGroup %s is already applied to subnet '%s' of virtual network %s ...",
                  networkSecurityGroupName, subnetName, virtualNetworkName);
          final String addToSubnetId = api.getNetworkSecurityGroupApi().addToSubnet(virtualNetworkName, subnetName,
-                 NetworkSecurityGroup.create(networkSecurityGroupName, null, null, null));
+                 networkSecurityGroupName);
          if (!operationSucceededPredicate.apply(addToSubnetId)) {
             final String warnMessage = format("Add networkSecurityGroup(%s) to subnet(%s) has not been completed "
                     + "within %sms.", networkSecurityGroupName, subnetName, azureComputeConstants.operationTimeout());

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/domain/NetworkSecurityGroup.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/domain/NetworkSecurityGroup.java b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/NetworkSecurityGroup.java
index 41e0193..1d490a7 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/domain/NetworkSecurityGroup.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/NetworkSecurityGroup.java
@@ -16,16 +16,37 @@
  */
 package org.jclouds.azurecompute.domain;
 
-import static com.google.common.collect.ImmutableList.copyOf;
 import java.util.List;
 
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
 
 @AutoValue
 public abstract class NetworkSecurityGroup {
 
+   public enum State {
+
+      CREATED,
+      CREATING,
+      UPDATING,
+      DELETING,
+      UNAVAILABLE,
+      UNRECOGNIZED;
+
+      public static State fromString(final String text) {
+         if (text != null) {
+            for (State status : State.values()) {
+               if (text.equalsIgnoreCase(status.name())) {
+                  return status;
+               }
+            }
+         }
+         return UNRECOGNIZED;
+      }
+   }
+
    NetworkSecurityGroup() {
    } // For AutoValue only!
 
@@ -38,11 +59,23 @@ public abstract class NetworkSecurityGroup {
    public abstract String location();
 
    @Nullable
+   public abstract State state();
+
+   @Nullable
    public abstract List<Rule> rules();
 
+   public static NetworkSecurityGroup create(final String name) {
+      return new AutoValue_NetworkSecurityGroup(name, null, null, null, null);
+   }
+
    public static NetworkSecurityGroup create(
-           final String name, final String label, String location, final List<Rule> rules) {
+           final String name, final String label, String location, final State state, final List<Rule> rules) {
 
-      return new AutoValue_NetworkSecurityGroup(name, label, location, rules == null ? null : copyOf(rules));
+      return new AutoValue_NetworkSecurityGroup(
+              name,
+              label,
+              location,
+              state,
+              rules == null ? ImmutableList.<Rule>of() : ImmutableList.copyOf(rules));
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/domain/Rule.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/domain/Rule.java b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/Rule.java
index 1d81c14..a58cb3b 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/domain/Rule.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/Rule.java
@@ -23,13 +23,78 @@ import com.google.auto.value.AutoValue;
 @AutoValue
 public abstract class Rule {
 
+   public enum Action {
+
+      Allow,
+      Deny,
+      UNRECOGNIZED;
+
+      public static Action fromString(final String text) {
+         if (text != null) {
+            for (Action action : Action.values()) {
+               if (text.equalsIgnoreCase(action.name())) {
+                  return action;
+               }
+            }
+         }
+         return UNRECOGNIZED;
+      }
+   }
+
+   public enum Type {
+
+      Inbound,
+      Outbound,
+      UNRECOGNIZED;
+
+      public static Type fromString(final String text) {
+         if (text != null) {
+            for (Type type : Type.values()) {
+               if (text.equalsIgnoreCase(type.name())) {
+                  return type;
+               }
+            }
+         }
+         return UNRECOGNIZED;
+      }
+   }
+
+   public enum Protocol {
+
+      TCP("TCP"),
+      UDP("UDP"),
+      ALL("*"),
+      UNRECOGNIZED("");
+
+      private final String value;
+
+      Protocol(final String value) {
+         this.value = value;
+      }
+
+      public static Protocol fromString(final String text) {
+         if (text != null) {
+            for (Protocol protocol : Protocol.values()) {
+               if (text.equalsIgnoreCase(protocol.value)) {
+                  return protocol;
+               }
+            }
+         }
+         return UNRECOGNIZED;
+      }
+
+      public String getValue() {
+         return value;
+      }
+   }
+
    public abstract String name();
 
-   public abstract String type();
+   public abstract Type type();
 
    public abstract String priority();
 
-   public abstract String action();
+   public abstract Action action();
 
    public abstract String sourceAddressPrefix();
 
@@ -39,7 +104,7 @@ public abstract class Rule {
 
    public abstract String destinationPortRange();
 
-   public abstract String protocol();
+   public abstract Protocol protocol();
 
    public abstract String state();
 
@@ -49,9 +114,30 @@ public abstract class Rule {
    Rule() {
    } // For AutoValue only!
 
-   public static Rule create(final String name, final String type, final String priority, final String action,
+   /**
+    * Use this method to create a new rule to be added to a network security group.
+    * @param name
+    * @param type
+    * @param priority
+    * @param action
+    * @param sourceAddressPrefix
+    * @param sourcePortRange
+    * @param destinationAddressPrefix
+    * @param destinationPortRange
+    * @param protocol
+    * @return 
+    */
+   public static Rule create(final String name, final Type type, final String priority, final Action action,
+           final String sourceAddressPrefix, final String sourcePortRange, final String destinationAddressPrefix,
+           final String destinationPortRange, final Protocol protocol) {
+
+      return new AutoValue_Rule(name, type, priority, action, sourceAddressPrefix, sourcePortRange,
+              destinationAddressPrefix, destinationPortRange, protocol, "Active", null);
+   }
+
+   public static Rule create(final String name, final Type type, final String priority, final Action action,
            final String sourceAddressPrefix, final String sourcePortRange, final String destinationAddressPrefix,
-           final String destinationPortRange, final String protocol, final String state, final Boolean isDefault) {
+           final String destinationPortRange, final Protocol protocol, final String state, final Boolean isDefault) {
 
       return new AutoValue_Rule(name, type, priority, action, sourceAddressPrefix, sourcePortRange,
               destinationAddressPrefix, destinationPortRange, protocol, state, isDefault);

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/features/NetworkSecurityGroupApi.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/features/NetworkSecurityGroupApi.java b/azurecompute/src/main/java/org/jclouds/azurecompute/features/NetworkSecurityGroupApi.java
index 4eede4b..aa50bf2 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/features/NetworkSecurityGroupApi.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/features/NetworkSecurityGroupApi.java
@@ -41,18 +41,28 @@ import org.jclouds.azurecompute.xml.NetworkSecurityGroupHandler;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.Headers;
+import org.jclouds.rest.annotations.Payload;
+import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.QueryParams;
 import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.XMLResponseParser;
 
+/**
+ * The Service Management API includes operations for managing the network security groups in your subscription.
+ *
+ * @see https://msdn.microsoft.com/en-us/library/azure/dn913824.aspx.
+ */
 @Path("/services/networking")
-@Headers(keys = "x-ms-version", values = "{jclouds.api-version}")
+@Produces(MediaType.APPLICATION_XML)
 @Consumes(MediaType.APPLICATION_XML)
+@Headers(keys = "x-ms-version", values = "{jclouds.api-version}")
 public interface NetworkSecurityGroupApi {
 
    /**
-    * Lists all of the Network Security Groups for the subscription.
+    * The List Network Security Groups operation returns a list of the network security groups in the specified
+    * subscription.
     *
+    * @return network security group list.
     */
    @Named("ListNetworkSecurityGroups")
    @Path("/networksecuritygroups")
@@ -61,10 +71,17 @@ public interface NetworkSecurityGroupApi {
    @Fallback(EmptyListOnNotFoundOr404.class)
    List<NetworkSecurityGroup> list();
 
+   /**
+    * The Create Network Security Group operation creates a new network security group within the context of the
+    * specified subscription. For more information, see
+    * <a href="https://msdn.microsoft.com/en-us/library/azure/dn848316.aspx">About Network Security Groups</a>.
+    *
+    * @param networkSecurityGroup network security group.
+    * @return request id.
+    */
    @Named("CreateNetworkSecurityGroup")
    @Path("/networksecuritygroups")
    @POST
-   @Produces(MediaType.APPLICATION_XML)
    @ResponseParser(ParseRequestIdHeader.class)
    String create(@BinderParam(NetworkSecurityGroupToXML.class) NetworkSecurityGroup networkSecurityGroup);
 
@@ -78,17 +95,18 @@ public interface NetworkSecurityGroupApi {
     */
    @Named("CreateNetworkSecurityGroup")
    @Path("/networksecuritygroups/{networkSecurityGroupName}")
+   @Fallback(NullOnNotFoundOr404.class)
    @DELETE
-   @Produces(MediaType.APPLICATION_XML)
    @ResponseParser(ParseRequestIdHeader.class)
    String delete(@PathParam("networkSecurityGroupName") String networkSecurityGroupName);
 
    /**
-    * Gets the Network Security Group applied to a specific subnet.
+    * The Get Network Security Group for Subnet operation returns information about the network security group
+    * associated with a subnet.
     *
-    * @param virtualNetworkName
-    * @param subnetName
-    * @return
+    * @param virtualNetworkName virtual network name.
+    * @param subnetName subnet name.
+    * @return network security group.
     */
    @Named("GetsNetworkSecurityGroupAppliedToSubnet")
    @Path("/virtualnetwork/{virtualNetworkName}/subnets/{subnetName}/networksecuritygroups")
@@ -99,10 +117,10 @@ public interface NetworkSecurityGroupApi {
            @PathParam("virtualNetworkName") String virtualNetworkName, @PathParam("subnetName") String subnetName);
 
    /**
-    * Gets the details for the specified Network Security Group in the subscription
+    * The Get Network Security Group operation returns information about the specified network security group and rules.
     *
-    * @param networkSecurityGroupName
-    * @return
+    * @param networkSecurityGroupName network security group name.
+    * @return network security group.
     */
    @Named("GetDetailsNetworkSecurityGroup")
    @Path("/networksecuritygroups/{networkSecurityGroupName}")
@@ -113,53 +131,84 @@ public interface NetworkSecurityGroupApi {
    NetworkSecurityGroup getFullDetails(@PathParam("networkSecurityGroupName") String networkSecurityGroupName);
 
    /**
-    * Adds a Network Security Group to a subnet.
+    * The Get Network Security Group operation returns information about the specified network security group.
+    *
+    * @param networkSecurityGroupName network security group name.
+    * @return network security group.
+    */
+   @Named("GetDetailsNetworkSecurityGroup")
+   @Path("/networksecuritygroups/{networkSecurityGroupName}")
+   @GET
+   @XMLResponseParser(NetworkSecurityGroupHandler.class)
+   @Fallback(NullOnNotFoundOr404.class)
+   NetworkSecurityGroup get(@PathParam("networkSecurityGroupName") String networkSecurityGroupName);
+
+   /**
+    * The Add Network Security Group to Subnet operation associates the network security group with specified subnet in
+    * a virtual network. For more information, see
+    * <a href="https://msdn.microsoft.com/en-us/library/azure/dn848316.aspx">About Network Security Groups</a>.
     *
-    * @param virtualNetworkName
-    * @return
+    * @param virtualNetworkName virtual network name.
+    * @param subnetName subnet name.
+    * @param networkSecurityGroupName network security group name.
+    * @return request id.
     */
    @Named("AddNetworkSecurityGroupToSubnet")
    @Path("/virtualnetwork/{virtualNetworkName}/subnets/{subnetName}/networksecuritygroups")
+   @Payload("<NetworkSecurityGroup xmlns=\"http://schemas.microsoft.com/windowsazure\">"
+           + "<Name>{networkSecurityGroupName}</Name></NetworkSecurityGroup>")
    @POST
-   @Produces(MediaType.APPLICATION_XML)
    @ResponseParser(ParseRequestIdHeader.class)
    String addToSubnet(@PathParam("virtualNetworkName") String virtualNetworkName,
            @PathParam("subnetName") String subnetName,
-           @BinderParam(NetworkSecurityGroupToXML.class) NetworkSecurityGroup networkSecurityGroup);
+           @PayloadParam("networkSecurityGroupName") String networkSecurityGroupName);
 
    /**
-    * Removes a Network Security Group from a subnet
+    * The Remove Network Security Group from Subnet operation removes the association of the specified network security
+    * group from the specified subnet.
+    *
+    * @param virtualNetworkName virtual network name.
+    * @param subnetName subnet name.
+    * @param networkSecurityGroupName network security group name.
+    * @return request id.
     */
    @Named("RemoveNetworkSecurityGroupToSubnet")
    @Path("/virtualnetwork/{virtualNetworkName}/subnets/{subnetName}/networksecuritygroups/{networkSecurityGroupName}")
+   @Fallback(NullOnNotFoundOr404.class)
    @DELETE
-   @Produces(MediaType.APPLICATION_XML)
    @ResponseParser(ParseRequestIdHeader.class)
    String removeFromSubnet(@PathParam("virtualNetworkName") String virtualNetworkName,
            @PathParam("subnetName") String subnetName,
            @PathParam("networkSecurityGroupName") String networkSecurityGroupName);
 
    /**
-    * Sets a new Network Security Rule to existing Network Security Group
-    *
+    * The Set Network Security Rule operation adds or updates a network security rule that is associated with the
+    * specified network security group.
     *
+    * @param networkSecurityGroupName network security group name.
+    * @param ruleName rule name.
+    * @param rule rule.
+    * @return request id.
     */
    @Named("SetNetworkSecurityRuleToNetworkSecurityGroup")
    @Path("/networksecuritygroups/{networkSecurityGroupName}/rules/{ruleName}")
    @PUT
-   @Produces(MediaType.APPLICATION_XML)
    @ResponseParser(ParseRequestIdHeader.class)
    String setRule(@PathParam("networkSecurityGroupName") String networkSecurityGroupName,
            @PathParam("ruleName") String ruleName, @BinderParam(RuleToXML.class) Rule rule);
 
    /**
-    * Deletes a rule from the specified Network Security Group.
+    * The Delete Network Security Rule operation deletes a network security group rule from the specified network
+    * security group.
     *
+    * @param networkSecurityGroupName network security group name.
+    * @param ruleName name of the rule to be deleted.
+    * @return request id.
     */
    @Named("SetNetworkSecurityRuleToNetworkSecurityGroup")
    @Path("/networksecuritygroups/{networkSecurityGroupName}/rules/{ruleName}")
+   @Fallback(NullOnNotFoundOr404.class)
    @DELETE
-   @Produces(MediaType.APPLICATION_XML)
    @ResponseParser(ParseRequestIdHeader.class)
    String deleteRule(@PathParam("networkSecurityGroupName") String networkSecurityGroupName,
            @PathParam("ruleName") String ruleName);

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/util/ConflictManagementPredicate.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/util/ConflictManagementPredicate.java b/azurecompute/src/main/java/org/jclouds/azurecompute/util/ConflictManagementPredicate.java
index 4949130..c859c50 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/util/ConflictManagementPredicate.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/util/ConflictManagementPredicate.java
@@ -16,30 +16,42 @@
  */
 package org.jclouds.azurecompute.util;
 
-import static org.jclouds.azurecompute.compute.AzureComputeServiceAdapter.generateIllegalStateExceptionMessage;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.azurecompute.domain.Operation.Status.FAILED;
 
 import com.google.common.base.Predicate;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
 import javax.annotation.Resource;
 import javax.inject.Named;
+
+import org.jclouds.azurecompute.AzureComputeApi;
+import org.jclouds.azurecompute.domain.Operation;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.http.HttpResponseException;
 import org.jclouds.logging.Logger;
+import org.jclouds.util.Predicates2;
 
 /**
  * Conflict errors (409 response status code) management predicate.
  */
-public abstract class ConflictManagementPredicate implements Predicate<String> {
-
-   protected final Predicate<String> operationSucceeded;
-
-   private Long timeout = null;
+public class ConflictManagementPredicate implements Predicate<String> {
 
    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
-   private boolean raiseException = false;
+   private AzureComputeApi api;
+
+   private Predicate<String> operationSucceeded;
+
+   private final long timeout = 600000;
+   private final long interval = 15000;
 
    public ConflictManagementPredicate() {
       this(null);
@@ -48,25 +60,35 @@ public abstract class ConflictManagementPredicate implements Predicate<String> {
    /**
     * Constructor.
     *
-    * @param operationSucceeded predicate to be applied to the requestId.
+    * @param api azure api.
     */
-   public ConflictManagementPredicate(final Predicate<String> operationSucceeded) {
-      this.operationSucceeded = operationSucceeded;
+   public ConflictManagementPredicate(final AzureComputeApi api) {
+      this(api, Predicates2.retry(new OperationSucceededPredicate(api), 600, 5, 5, SECONDS));
    }
 
    /**
-    * Constructor to be used to raise an IllegalStateException in case of the given predicate evaluation is false.
+    * Constructor.
     *
-    * @param operationSucceeded predicate to be applied to the requestId.
-    * @param timeout timeout of the predicate.
+    * @param api azure api.
+    * @param timeout predicate timeout.
+    * @param period predicate period.
+    * @param maxPeriod max period
+    * @param unit timeout and period time unit.
     */
    public ConflictManagementPredicate(
-           final Predicate<String> operationSucceeded,
-           final Long timeout) {
+           final AzureComputeApi api, long timeout, long period, long maxPeriod, TimeUnit unit) {
+      this(api, Predicates2.retry(new OperationSucceededPredicate(api), timeout, period, maxPeriod, unit));
+   }
 
+   /**
+    * Constructor.
+    *
+    * @param api azure api.
+    * @param operationSucceeded predicate to be applied to the requestId.
+    */
+   public ConflictManagementPredicate(final AzureComputeApi api, final Predicate<String> operationSucceeded) {
+      this.api = api;
       this.operationSucceeded = operationSucceeded;
-      this.timeout = timeout;
-      this.raiseException = true;
    }
 
    /**
@@ -74,49 +96,116 @@ public abstract class ConflictManagementPredicate implements Predicate<String> {
     *
     * @return requestId.
     */
-   protected abstract String operation();
+   protected String operation() {
+      throw new UnsupportedOperationException();
+   }
 
    /**
     * {@inheritDoc }
     *
-    * @param name interested object/operaton descripton.
+    * @param input interested object/operaton descripton or requestId.
     * @return predicate evaluation.
     */
    @Override
-   public final boolean apply(final String name) {
-      try {
-         final String requestId = operation();
-         logger.info("Executed operation on {0}", name);
+   public final boolean apply(final String input) {
+      Operation operation = null;
+      String requestId = null;
+
+      boolean retry = true;
+
+      long now = System.currentTimeMillis();
+      long end = now + timeout;
+
+      while (retry && now < end) {
+         try {
+            requestId = operation();
+            logger.debug("Executed operation on %s", input);
+
+            // If request id is not available let's assume operation succeeded.
+            if (requestId == null) {
+               logger.debug("No request id available. Assume operation succeeded.");
+               return true;
+            }
 
-         if (requestId == null) {
-            return true;
+            operation = api.getOperationApi().get(requestId);
+            logger.debug("Operation %s status: %s", operation.id(), operation.status().name());
+
+            if (operation.status() == FAILED) {
+               // rise an exception based on HTTP status code
+               if (operation.httpStatusCode() == 409 || operation.httpStatusCode() == 500) {
+                  logger.info("Retry operation %s with (code %d)", operation.id(), operation.httpStatusCode());
+               } else {
+                  logger.info("Not retriable operation %s (code %d)", operation.id(), operation.httpStatusCode());
+                  retry = false;
+               }
+            } else {
+               logger.debug("Tracking for operation %s ...", operation.id());
+               retry = false;
+            }
+         } catch (UnsupportedOperationException e) {
+            requestId = input;
+            retry = false;
+            logger.debug("Tracking for operation %s ...", input);
+         } catch (RuntimeException e) {
+            final HttpResponseException re = (e instanceof HttpResponseException)
+                    ? HttpResponseException.class.cast(e) : (e.getCause() instanceof HttpResponseException)
+                            ? HttpResponseException.class.cast(e.getCause())
+                            : null;
+            if (re == null) {
+               throw e;
+            } else {
+               final HttpResponse res = re.getResponse();
+               logger.info("[%s (%d)] Performing operation on %s", res.getStatusLine(), res.getStatusCode(), input);
+               if (res.getStatusCode() == 409 || res.getStatusCode() == 500) {
+                  logger.info("Retry operation %s", operation == null ? "" : operation.id(), res.getStatusCode());
+               } else {
+                  throw re;
+               }
+            }
          }
 
-         final boolean res = operationSucceeded == null ? true : operationSucceeded.apply(requestId);
-         if (!res && raiseException) {
-            final String message = generateIllegalStateExceptionMessage(requestId, timeout);
-            logger.warn(message);
-            throw new IllegalStateException(message);
-         } else {
-            return res;
+         if (retry) {
+            try {
+               Thread.sleep(interval);
+            } catch (InterruptedException ex) {
+               // ignore
+            }
+
+            now = System.currentTimeMillis();
          }
-      } catch (RuntimeException e) {
-         final HttpResponseException re = (e instanceof HttpResponseException)
-                 ? HttpResponseException.class.cast(e) : (e.getCause() instanceof HttpResponseException)
-                         ? HttpResponseException.class.cast(e.getCause())
-                         : null;
-         if (re == null) {
-            throw e;
-         } else {
-            final HttpResponse res = re.getResponse();
-            logger.info("[{0} (core: {1})] while performing operation on {2}",
-                    new Object[]{res.getStatusLine(), res.getStatusCode(), name});
-            if (res.getStatusCode() == 409) {
-               logger.info("[{0}] Retry operation on {1}", new Object[]{e.getMessage(), name});
+      }
+
+      if (now >= end) {
+         throw new RuntimeException(new TimeoutException(requestId));
+      }
+
+      return operationSucceeded.apply(requestId);
+   }
+
+   private static class OperationSucceededPredicate implements Predicate<String> {
+
+      private final AzureComputeApi api;
+
+      public OperationSucceededPredicate(final AzureComputeApi api) {
+         this.api = checkNotNull(api, "api must not be null");
+      }
+
+      @Override
+      public boolean apply(final String input) {
+         final Operation operation = api.getOperationApi().get(input);
+         switch (operation.status()) {
+            case SUCCEEDED:
+               return true;
+
+            case IN_PROGRESS:
+            case UNRECOGNIZED:
                return false;
-            } else {
-               throw re;
-            }
+
+            case FAILED:
+               throw new RuntimeException(new CancellationException(input));
+
+            default:
+               throw new IllegalStateException("Operation is in invalid status: " + operation.status().name());
          }
       }
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/xml/NetworkSecurityGroupHandler.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/xml/NetworkSecurityGroupHandler.java b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/NetworkSecurityGroupHandler.java
index f893ac5..421bde2 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/xml/NetworkSecurityGroupHandler.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/NetworkSecurityGroupHandler.java
@@ -34,6 +34,8 @@ public class NetworkSecurityGroupHandler extends ParseSax.HandlerForGeneratedReq
 
    private String location;
 
+   private NetworkSecurityGroup.State state;
+
    private List<Rule> rules = Lists.newArrayList();
 
    private boolean inRule;
@@ -51,8 +53,9 @@ public class NetworkSecurityGroupHandler extends ParseSax.HandlerForGeneratedReq
 
    @Override
    public NetworkSecurityGroup getResult() {
-      NetworkSecurityGroup result = NetworkSecurityGroup.create(name, label, location, rules);
+      NetworkSecurityGroup result = NetworkSecurityGroup.create(name, label, location, state, rules);
       name = label = location = null; // handler is called in a loop.
+      this.state = null;
       rules = Lists.newArrayList();
       return result;
    }
@@ -70,6 +73,8 @@ public class NetworkSecurityGroupHandler extends ParseSax.HandlerForGeneratedReq
          label = currentOrNull(currentText);
       } else if (qName.equals("Location")) {
          location = currentOrNull(currentText);
+      } else if (qName.equals("State")) {
+         state = NetworkSecurityGroup.State.fromString(currentOrNull(currentText));
       }
       currentText.setLength(0);
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/main/java/org/jclouds/azurecompute/xml/RuleHandler.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/xml/RuleHandler.java b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/RuleHandler.java
index 7dc43f6..758b8d1 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/xml/RuleHandler.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/RuleHandler.java
@@ -25,11 +25,11 @@ final class RuleHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Ru
 
    private String name;
 
-   private String type;
+   private Rule.Type type;
 
    private String priority;
 
-   private String action;
+   private Rule.Action action;
 
    private String sourceAddressPrefix;
 
@@ -39,7 +39,7 @@ final class RuleHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Ru
 
    private String destinationPortRange;
 
-   private String protocol;
+   private Rule.Protocol protocol;
 
    private String state;
 
@@ -51,8 +51,12 @@ final class RuleHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Ru
    public Rule getResult() {
       Rule result = Rule.create(name, type, priority, action, sourceAddressPrefix, sourcePortRange,
               destinationAddressPrefix, destinationPortRange, protocol, state, isDefault);
-      name = type = priority = action = sourceAddressPrefix = sourcePortRange = destinationAddressPrefix
-              = destinationPortRange = protocol = state = null; // handler is called in a loop.
+      
+      name = priority = sourceAddressPrefix = sourcePortRange = destinationAddressPrefix
+              = destinationPortRange = state = null; // handler is called in a loop.
+      protocol = null;
+      action = null;
+      type = null;
       isDefault = false;
       return result;
    }
@@ -62,11 +66,11 @@ final class RuleHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Ru
       if (qName.equals("Name")) {
          name = currentOrNull(currentText);
       } else if (qName.equals("Type")) {
-         type = currentOrNull(currentText);
+         type = Rule.Type.fromString(currentOrNull(currentText));
       } else if (qName.equals("Priority")) {
          priority = currentOrNull(currentText);
       } else if (qName.equals("Action")) {
-         action = currentOrNull(currentText);
+         action = Rule.Action.fromString(currentOrNull(currentText));
       } else if (qName.equals("SourceAddressPrefix")) {
          sourceAddressPrefix = currentOrNull(currentText);
       } else if (qName.equals("SourcePortRange")) {
@@ -76,7 +80,7 @@ final class RuleHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Ru
       } else if (qName.equals("DestinationPortRange")) {
          destinationPortRange = currentOrNull(currentText);
       } else if (qName.equals("Protocol")) {
-         protocol = currentOrNull(currentText);
+         protocol = Rule.Protocol.fromString(currentOrNull(currentText));
       } else if (qName.equals("State")) {
          state = currentOrNull(currentText);
       } else if (qName.equals("IsDefault")) {

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapterLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapterLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapterLiveTest.java
index fcdf2bc..f4b6f56 100644
--- a/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapterLiveTest.java
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapterLiveTest.java
@@ -16,22 +16,12 @@
  */
 package org.jclouds.azurecompute.compute;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.jclouds.util.Predicates2.retry;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 
 import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
-import com.google.common.net.HostAndPort;
-import com.google.common.net.InetAddresses;
-import com.google.inject.Injector;
-import com.google.inject.Module;
-
-import java.util.Arrays;
 import java.util.Properties;
 import java.util.Random;
 import java.util.Set;
@@ -52,6 +42,16 @@ import org.jclouds.sshj.config.SshjSshClientModule;
 import org.testng.annotations.Test;
 import org.testng.annotations.AfterGroups;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.net.HostAndPort;
+import com.google.common.net.InetAddresses;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import java.util.Arrays;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.util.Predicates2.retry;
+
 @Test(groups = "live", singleThreaded = true, testName = "AzureComputeServiceAdapterLiveTest")
 public class AzureComputeServiceAdapterLiveTest extends BaseAzureComputeApiLiveTest {
 
@@ -61,6 +61,16 @@ public class AzureComputeServiceAdapterLiveTest extends BaseAzureComputeApiLiveT
 
    private Factory sshFactory;
 
+   private String storageServiceName = null;
+
+   @Override
+   protected String getStorageServiceName() {
+      if (storageServiceName == null) {
+         storageServiceName = String.format("%3.20sacsa", System.getProperty("user.name") + RAND).toLowerCase();
+      }
+      return storageServiceName;
+   }
+
    @Override
    protected AzureComputeApi create(final Properties props, final Iterable<Module> modules) {
       final Injector injector = newBuilder().modules(modules).overrides(props).buildInjector();

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceContextLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceContextLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceContextLiveTest.java
index f73fd2b..0e204ba 100644
--- a/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceContextLiveTest.java
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/compute/AzureComputeServiceContextLiveTest.java
@@ -16,14 +16,8 @@
  */
 package org.jclouds.azurecompute.compute;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.jclouds.util.Predicates2.retry;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.google.common.reflect.TypeToken;
-import com.google.inject.Module;
+import static org.testng.Assert.assertTrue;
 
 import java.util.Arrays;
 import java.util.Random;
@@ -31,7 +25,6 @@ import java.util.Set;
 
 import org.jclouds.azurecompute.options.AzureComputeTemplateOptions;
 import org.jclouds.azurecompute.AzureComputeApi;
-import org.jclouds.azurecompute.compute.config.AzureComputeServiceContextModule;
 import org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest;
 import org.jclouds.azurecompute.util.ConflictManagementPredicate;
 import org.jclouds.compute.RunNodesException;
@@ -43,6 +36,10 @@ import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
 import org.jclouds.ssh.SshClient;
 import org.jclouds.sshj.config.SshjSshClientModule;
 
+import com.google.common.collect.Iterables;
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Module;
+
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -54,14 +51,11 @@ public class AzureComputeServiceContextLiveTest extends BaseComputeServiceContex
 
    private AzureComputeApi api;
 
-   private Predicate<String> operationSucceeded;
-
    private String storageServiceName = null;
 
    protected String getStorageServiceName() {
       if (storageServiceName == null) {
-         storageServiceName = String.format("%3.24s",
-                 System.getProperty("user.name") + RAND + this.getClass().getSimpleName()).toLowerCase();
+         storageServiceName = String.format("%3.20sacsc", System.getProperty("user.name") + RAND).toLowerCase();
       }
       return storageServiceName;
    }
@@ -76,20 +70,17 @@ public class AzureComputeServiceContextLiveTest extends BaseComputeServiceContex
                  private static final long serialVersionUID = 309104475566522958L;
 
               });
-
-      operationSucceeded = retry(
-              new AzureComputeServiceContextModule.OperationSucceededPredicate(api), 600, 5, 5, SECONDS);
    }
 
    @AfterClass(alwaysRun = true)
    public void tearDown() {
-      retry(new ConflictManagementPredicate(operationSucceeded) {
+      assertTrue(new ConflictManagementPredicate(api) {
 
          @Override
          protected String operation() {
             return api.getStorageAccountApi().delete(getStorageServiceName());
          }
-      }, 600, 5, 5, SECONDS).apply(getStorageServiceName());
+      }.apply(getStorageServiceName()));
    }
 
    @Override

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/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
index cd677c7..62bab24 100644
--- a/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiLiveTest.java
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/AffinityGroupApiLiveTest.java
@@ -21,6 +21,8 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.assertNotNull;
 
+import static org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest.LOCATION;
+
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 
@@ -70,7 +72,7 @@ public class AffinityGroupApiLiveTest extends AbstractAzureComputeApiLiveTest {
       final CreateAffinityGroupParams params = CreateAffinityGroupParams.builder().
               name(GROUP_NAME).
               label(GROUP_NAME).
-              location("West Europe").
+              location(LOCATION).
               build();
 
       final String requestId = api().add(params);

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/test/java/org/jclouds/azurecompute/features/DeploymentApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/DeploymentApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/DeploymentApiLiveTest.java
index 183beda..5cbf7f5 100644
--- a/azurecompute/src/test/java/org/jclouds/azurecompute/features/DeploymentApiLiveTest.java
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/DeploymentApiLiveTest.java
@@ -122,35 +122,35 @@ public class DeploymentApiLiveTest extends BaseAzureComputeApiLiveTest {
    public void testDelete() {
       final List<Role> roles = api.getDeploymentApiForService(cloudService.name()).get(DEPLOYMENT).roleList();
 
-      retry(new ConflictManagementPredicate(operationSucceeded) {
+      assertTrue(new ConflictManagementPredicate(api) {
 
          @Override
          protected String operation() {
             return api().delete(deployment.name());
          }
-      }, 600, 30, 30, SECONDS).apply(deployment.name());
+      }.apply(deployment.name()));
 
       assertTrue(deploymentGone.apply(deployment), deployment.toString());
       Logger.getAnonymousLogger().log(Level.INFO, "deployment deleted: {0}", deployment);
 
-      retry(new ConflictManagementPredicate(operationSucceeded) {
+      assertTrue(new ConflictManagementPredicate(api) {
 
          @Override
          protected String operation() {
             return api.getCloudServiceApi().delete(cloudService.name());
          }
-      }, 600, 30, 30, SECONDS).apply(cloudService.name());
+      }.apply(cloudService.name()));
 
       for (Role r : roles) {
          final Role.OSVirtualHardDisk disk = r.osVirtualHardDisk();
          if (disk != null) {
-            retry(new ConflictManagementPredicate(operationSucceeded) {
+            assertTrue(new ConflictManagementPredicate(api) {
 
                @Override
                protected String operation() {
                   return api.getDiskApi().delete(disk.diskName());
                }
-            }, 600, 30, 30, SECONDS).apply(disk.diskName());
+            }.apply(disk.diskName()));
          }
       }
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/29435e1c/azurecompute/src/test/java/org/jclouds/azurecompute/features/NetworkSecurityGroupApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/NetworkSecurityGroupApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/NetworkSecurityGroupApiLiveTest.java
new file mode 100644
index 0000000..763e1ce
--- /dev/null
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/NetworkSecurityGroupApiLiveTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.jclouds.azurecompute.domain.NetworkSecurityGroup;
+import org.jclouds.azurecompute.domain.Rule;
+import org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest;
+
+import static org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest.DEFAULT_SUBNET_NAME;
+import static org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest.LOCATION;
+import static org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest.VIRTUAL_NETWORK_NAME;
+import org.jclouds.azurecompute.util.ConflictManagementPredicate;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+@Test(groups = "live", testName = "NetworkSecurityGroupApiLiveTest", singleThreaded = true)
+public class NetworkSecurityGroupApiLiveTest extends BaseAzureComputeApiLiveTest {
+
+   private static final String GROUP1 = System.getProperty("user.name") + RAND + "-1";
+
+   private static final String GROUP2 = System.getProperty("user.name") + RAND + "-2";
+
+   public static final String ADDRESS_SPACE = "10.10.0.0/20";
+
+   public static final String SUBNET_ADDRESS_SPACE = "10.10.0.0/23";
+
+   public static final String NETWORK_NAME = "secgrp-vnetsite";
+
+   public static final String SUBNET_NAME = "secgrp-subnet";
+
+   @BeforeClass
+   public void groupSetup() {
+      // ----------------------------
+      // Clean before start
+      // ----------------------------
+      try {
+         final NetworkSecurityGroup group = api().
+                 getNetworkSecurityGroupAppliedToSubnet(VIRTUAL_NETWORK_NAME, DEFAULT_SUBNET_NAME);
+
+         if (group != null && (group.name().equals(GROUP1) || group.name().equals(GROUP2))) {
+            api().removeFromSubnet(VIRTUAL_NETWORK_NAME, DEFAULT_SUBNET_NAME, group.name());
+         }
+      } catch (Exception e) {
+         // ignore
+      }
+
+      try {
+         if (api().get(GROUP1) != null) {
+            operationSucceeded.apply(api.getNetworkSecurityGroupApi().delete(GROUP1));
+         }
+      } catch (Exception e) {
+         // ignore
+      }
+
+      try {
+         if (api().get(GROUP2) != null) {
+            operationSucceeded.apply(api.getNetworkSecurityGroupApi().delete(GROUP2));
+         }
+      } catch (Exception e) {
+         // ignore
+      }
+      // ----------------------------
+
+      String requestId = api().create(
+              NetworkSecurityGroup.create(GROUP1, GROUP1 + " security group", LOCATION, null, null));
+
+      assertTrue(operationSucceeded.apply(requestId), GROUP1);
+
+      requestId = api().create(
+              NetworkSecurityGroup.create(GROUP2, GROUP2 + " security group", LOCATION, null, null));
+
+      assertTrue(operationSucceeded.apply(requestId), GROUP2);
+   }
+
+   @Test
+   public void list() {
+      final List<NetworkSecurityGroup> groups = api().list();
+      assertFalse(groups.isEmpty());
+   }
+
+   @Test
+   public void get() {
+      final NetworkSecurityGroup group = api().get(GROUP1);
+      assertEquals(group.name(), GROUP1);
+      assertEquals(group.label(), GROUP1 + " security group");
+      assertEquals(group.location(), LOCATION);
+      assertNull(group.state());
+      assertTrue(group.rules().isEmpty());
+   }
+
+   @Test
+   public void getFullDetails() {
+      final NetworkSecurityGroup group = api().getFullDetails(GROUP2);
+      assertEquals(group.name(), GROUP2);
+      assertEquals(group.label(), GROUP2 + " security group");
+      assertEquals(group.location(), LOCATION);
+      assertNull(group.state());
+      assertFalse(group.rules().isEmpty());
+   }
+
+   @Test
+   public void setRule() {
+      final String ruleName = "newrule";
+
+      final String requestId = api().setRule(GROUP1, ruleName, Rule.create(
+              ruleName,
+              Rule.Type.Inbound,
+              "100",
+              Rule.Action.Allow,
+              "INTERNET",
+              "*",
+              "10.0.0.0/0",
+              "*",
+              Rule.Protocol.ALL));
+
+      assertTrue(operationSucceeded.apply(requestId), ruleName);
+
+      Rule newrule = null;
+
+      for (Rule rule : api().getFullDetails(GROUP1).rules()) {
+         if (ruleName.equals(rule.name())) {
+            newrule = rule;
+         }
+      }
+
+      assertNotNull(newrule);
+
+      assertNull(newrule.isDefault());
+      assertEquals(newrule.action(), Rule.Action.Allow);
+      assertEquals(newrule.type(), Rule.Type.Inbound);
+      assertEquals(newrule.protocol(), Rule.Protocol.ALL);
+      assertEquals(newrule.state(), "Active");
+   }
+
+   @Test(dependsOnMethods = {"setRule"})
+   public void removeRule() {
+      final String ruleName = "newrule";
+
+      final String requestId = api().deleteRule(GROUP1, ruleName);
+      assertTrue(operationSucceeded.apply(requestId), ruleName);
+
+      Rule newrule = null;
+
+      for (Rule rule : api().getFullDetails(GROUP1).rules()) {
+         if (ruleName.equals(rule.name())) {
+            newrule = rule;
+         }
+      }
+
+      assertNull(newrule);
+   }
+
+   @Test(dependsOnMethods = {"removeRule"})
+   public void addToSubnet() {
+      assertTrue(new ConflictManagementPredicate(api) {
+
+         @Override
+         protected String operation() {
+            return api().addToSubnet(VIRTUAL_NETWORK_NAME, DEFAULT_SUBNET_NAME, GROUP1);
+         }
+      }.apply(GROUP1));
+   }
+
+   @Test(dependsOnMethods = {"addToSubnet"})
+   public void getForSubnet() {
+      final NetworkSecurityGroup group = api().
+              getNetworkSecurityGroupAppliedToSubnet(VIRTUAL_NETWORK_NAME, DEFAULT_SUBNET_NAME);
+      assertEquals(group.state(), NetworkSecurityGroup.State.CREATED);
+   }
+
+   @Test(dependsOnMethods = {"getForSubnet"})
+   public void removeFromSubnet() {
+      assertTrue(new ConflictManagementPredicate(api) {
+
+         @Override
+         protected String operation() {
+            return api().removeFromSubnet(VIRTUAL_NETWORK_NAME, DEFAULT_SUBNET_NAME, GROUP1);
+         }
+      }.apply(GROUP1));
+   }
+
+   @AfterClass
+   public void cleanup() {
+      // no assertion is required: just to be sure to remove for subnet
+      final NetworkSecurityGroup group = api().
+              getNetworkSecurityGroupAppliedToSubnet(VIRTUAL_NETWORK_NAME, DEFAULT_SUBNET_NAME);
+
+      if (group != null) {
+         api().removeFromSubnet(VIRTUAL_NETWORK_NAME, DEFAULT_SUBNET_NAME, group.name());
+      }
+
+      String requestId = api.getNetworkSecurityGroupApi().delete(GROUP1);
+      assertTrue(operationSucceeded.apply(requestId), GROUP1);
+
+      requestId = api.getNetworkSecurityGroupApi().delete(GROUP2);
+      assertTrue(operationSucceeded.apply(requestId), GROUP2);
+   }
+
+   private NetworkSecurityGroupApi api() {
+      return api.getNetworkSecurityGroupApi();
+   }
+}