http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java
new file mode 100644
index 0000000..cb78ece
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java
@@ -0,0 +1,179 @@
+/*
+ * 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.cloudstack.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Sets.newHashSet;
+import static org.jclouds.location.predicates.LocationPredicates.idEquals;
+import static org.jclouds.util.InetAddresses2.isPrivateIPAddress;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.cloudstack.domain.IPForwardingRule;
+import org.jclouds.cloudstack.domain.NIC;
+import org.jclouds.cloudstack.domain.VirtualMachine;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadata.Status;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.domain.Location;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.jclouds.util.Throwables2;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.base.Throwables;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
+@Singleton
+public class VirtualMachineToNodeMetadata implements Function<VirtualMachine, 
NodeMetadata> {
+
+   public static final Map<VirtualMachine.State, Status> vmStateToNodeStatus = 
ImmutableMap
+         .<VirtualMachine.State, Status> 
builder().put(VirtualMachine.State.STARTING, Status.PENDING)
+         .put(VirtualMachine.State.RUNNING, 
Status.RUNNING).put(VirtualMachine.State.STOPPING, Status.PENDING)
+         .put(VirtualMachine.State.STOPPED, Status.SUSPENDED)
+         .put(VirtualMachine.State.DESTROYED, Status.TERMINATED)
+         .put(VirtualMachine.State.EXPUNGING, Status.TERMINATED)
+         .put(VirtualMachine.State.MIGRATING, 
Status.PENDING).put(VirtualMachine.State.ERROR, Status.ERROR)
+         .put(VirtualMachine.State.UNKNOWN, Status.UNRECOGNIZED)
+         // TODO: is this really a state?
+         .put(VirtualMachine.State.SHUTDOWNED, Status.PENDING)
+         .put(VirtualMachine.State.UNRECOGNIZED, Status.UNRECOGNIZED).build();
+
+   private final Supplier<Set<? extends Location>> locations;
+   private final Supplier<Set<? extends Image>> images;
+   private final LoadingCache<String, Set<IPForwardingRule>> 
getIPForwardingRulesByVirtualMachine;
+   private final GroupNamingConvention nodeNamingConvention;
+
+   @Inject
+   VirtualMachineToNodeMetadata(@Memoized Supplier<Set<? extends Location>> 
locations,
+         @Memoized Supplier<Set<? extends Image>> images,
+         LoadingCache<String, Set<IPForwardingRule>> 
getIPForwardingRulesByVirtualMachine,
+         GroupNamingConvention.Factory namingConvention) {
+      this.nodeNamingConvention = checkNotNull(namingConvention, 
"namingConvention").createWithoutPrefix();
+      this.locations = checkNotNull(locations, "locations");
+      this.images = checkNotNull(images, "images");
+      this.getIPForwardingRulesByVirtualMachine = 
checkNotNull(getIPForwardingRulesByVirtualMachine,
+            "getIPForwardingRulesByVirtualMachine");
+   }
+
+   @Override
+   public NodeMetadata apply(final VirtualMachine from) {
+      // convert the result object to a jclouds NodeMetadata
+      NodeMetadataBuilder builder = new NodeMetadataBuilder();
+      builder.ids(from.getId() + "");
+      builder.name(from.getName());
+      // TODO: in cloudstack 2.2.12, when "name" was set fine on the backend,
+      // but wrong API response was returned to the user
+      // http://bugs.cloud.com/show_bug.cgi?id=11664
+      //
+      // we set displayName to the same value as name, but this could be wrong
+      // on hosts not started with jclouds
+      builder.hostname(from.getDisplayName());
+      
builder.location(FluentIterable.from(locations.get()).firstMatch(idEquals(from.getZoneId())).orNull());
+      if (from.getGroup() != null) {
+         builder.group(from.getGroup());
+      } else if (from.getDisplayName() != null) {
+         
builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getDisplayName()));
+      }
+      Image image = FluentIterable.from(images.get()).firstMatch(new 
Predicate<Image>() {
+         @Override
+         public boolean apply(Image input) {
+            return input.getProviderId().equals(from.getTemplateId() + "")
+            // either location free image (location is null) or in the same 
zone as the VM
+                  && (input.getLocation() == null || 
input.getId().equals(from.getZoneId() + ""));
+         }
+      }).orNull();
+      if (image != null) {
+         builder.imageId(image.getId());
+         builder.operatingSystem(image.getOperatingSystem());
+      }
+
+      builder.hardware(new HardwareBuilder()
+        .ids(from.getServiceOfferingId() + "")
+        .name(from.getServiceOfferingName() + "")
+         // .tags() TODO
+        .processors(ImmutableList.of(new Processor(from.getCpuCount(), 
from.getCpuSpeed())))
+        .ram((int)from.getMemory())//
+        .hypervisor(from.getHypervisor())//
+        .build());
+
+      builder.status(vmStateToNodeStatus.get(from.getState()));
+
+      Set<String> publicAddresses = newHashSet();
+      Set<String> privateAddresses = newHashSet();
+      if (from.getIPAddress() != null) {
+         boolean isPrivate = isPrivateIPAddress(from.getIPAddress());
+         if (isPrivate) {
+            privateAddresses.add(from.getIPAddress());
+         } else {
+            publicAddresses.add(from.getIPAddress());
+         }
+      }
+      if (from.getPublicIP() != null) {
+          publicAddresses.add(from.getPublicIP());
+      }
+      for (NIC nic : from.getNICs()) {
+         if (nic.getIPAddress() != null) {
+            if (isPrivateIPAddress(nic.getIPAddress())) {
+               privateAddresses.add(nic.getIPAddress());
+            } else {
+               publicAddresses.add(nic.getIPAddress());
+            }
+         }
+      }
+      try {
+         /* Also add to the list of public IPs any public IP address that has a
+            forwarding rule that links to this machine */
+         Iterables.addAll(publicAddresses, transform(
+            
filter(getIPForwardingRulesByVirtualMachine.getUnchecked(from.getId()),
+               new Predicate<IPForwardingRule>() {
+                  @Override
+                  public boolean apply(IPForwardingRule rule) {
+                     return !"Deleting".equals(rule.getState());
+                  }
+               }), new Function<IPForwardingRule, String>() {
+            @Override
+            public String apply(IPForwardingRule rule) {
+               return rule.getIPAddress();
+            }
+         }));
+      } catch (UncheckedExecutionException e) {
+         if (Throwables2.getFirstThrowableOfType(e, 
ResourceNotFoundException.class) == null) {
+            Throwables.propagateIfPossible(e.getCause());
+            throw e;
+         }
+      }
+      return 
builder.privateAddresses(privateAddresses).publicAddresses(publicAddresses).build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java
new file mode 100644
index 0000000..ae46a67
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.cloudstack.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.inject.Inject;
+
+import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.location.suppliers.all.JustProvider;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+/**
+ * Converts an Zone into a Location.
+ */
+public class ZoneToLocation implements Function<Zone, Location> {
+   private final JustProvider provider;
+
+   // allow us to lazy discover the provider of a resource
+   @Inject
+   public ZoneToLocation(JustProvider provider) {
+      this.provider = checkNotNull(provider, "provider");
+   }
+
+   @Override
+   public Location apply(Zone zone) {
+      return new 
LocationBuilder().scope(LocationScope.ZONE).metadata(ImmutableMap.<String, 
Object> of())
+          .description(zone.getName()).id(zone.getId())
+            .parent(Iterables.getOnlyElement(provider.get())).build();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java
new file mode 100644
index 0000000..81d51d8
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java
@@ -0,0 +1,64 @@
+/*
+ * 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.cloudstack.compute.loaders;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.jclouds.cloudstack.CloudStackApi;
+import org.jclouds.cloudstack.domain.SshKeyPair;
+
+import com.google.common.base.Throwables;
+import com.google.common.cache.CacheLoader;
+import com.google.inject.Inject;
+
+@Singleton
+public class CreateUniqueKeyPair extends CacheLoader<String, SshKeyPair> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+   protected final CloudStackApi client;
+
+   @Inject
+   public CreateUniqueKeyPair(CloudStackApi client) {
+      this.client = checkNotNull(client, "client");
+   }
+
+   @Override
+   public SshKeyPair load(String input) {
+      SshKeyPair keyPair = null;
+      while (keyPair == null) {
+         try {
+            keyPair = client.getSSHKeyPairApi().createSSHKeyPair(input);
+            logger.debug(">> creating SSH key pair with name %s", input);
+         } catch (IllegalStateException e) {
+            logger.error(e, "<< error creating SSH key pair with name %s: ",
+                         Throwables.getRootCause(e).getMessage());
+            throw Throwables.propagate(e);
+         }
+      }
+
+      logger.debug("<< created keyPair(%s)", keyPair.getName());
+      return keyPair;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java
new file mode 100644
index 0000000..93f32a9
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java
@@ -0,0 +1,74 @@
+/*
+ * 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.cloudstack.compute.loaders;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.jclouds.cloudstack.CloudStackApi;
+import org.jclouds.cloudstack.domain.SecurityGroup;
+import org.jclouds.cloudstack.domain.ZoneAndName;
+import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
+
+import com.google.common.base.Function;
+import com.google.common.cache.CacheLoader;
+
+public class FindSecurityGroupOrCreate extends CacheLoader<ZoneAndName, 
SecurityGroup> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+   protected final CloudStackApi client;
+   protected final Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup> 
groupCreator;
+
+   @Inject
+   public FindSecurityGroupOrCreate(CloudStackApi client,
+                                    Function<ZoneSecurityGroupNamePortsCidrs, 
SecurityGroup> groupCreator) {
+      this.client = checkNotNull(client, "client");
+      this.groupCreator = checkNotNull(groupCreator, "groupCreator");
+   }
+
+   @Override
+   public SecurityGroup load(ZoneAndName in) {
+      SecurityGroup group = 
client.getSecurityGroupApi().getSecurityGroupByName(in.getName());
+      if (group != null) {
+         return group;
+      } else {
+         return createNewSecurityGroup(in);
+      }
+   }
+
+   private SecurityGroup createNewSecurityGroup(ZoneAndName in) {
+      checkState(
+               checkNotNull(in, "ZoneSecurityGrioupNamePortsCidrs") instanceof 
ZoneSecurityGroupNamePortsCidrs,
+               "programming error: when issuing get to this cacheloader, you 
need to pass an instance of ZoneSecurityGroupNamePortsCidrs, not %s",
+               in);
+      ZoneSecurityGroupNamePortsCidrs zoneSecurityGroupNamePortsCidrs = 
ZoneSecurityGroupNamePortsCidrs.class.cast(in);
+      return groupCreator.apply(zoneSecurityGroupNamePortsCidrs);
+   }
+
+   @Override
+   public String toString() {
+      return "returnExistingSecurityGroupInZoneOrCreateAsNeeded()";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java
new file mode 100644
index 0000000..1bcad0c
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java
@@ -0,0 +1,509 @@
+/*
+ * 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.cloudstack.compute.options;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.compute.options.TemplateOptions;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Contains options supported by the
+ * {@link ComputeService#createNodesInGroup(String, int, TemplateOptions)} and
+ * {@link ComputeService#createNodesInGroup(String, int, TemplateOptions)}
+ * operations on the <em>gogrid</em> provider.
+ * 
+ * <h2>Usage</h2> The recommended way to instantiate a
+ * {@link CloudStackTemplateOptions} object is to statically import
+ * {@code CloudStackTemplateOptions.*} and invoke a static creation method
+ * followed by an instance mutator (if needed):
+ * <p>
+ * 
+ * <pre>
+ * import static 
org.jclouds.compute.options.CloudStackTemplateOptions.Builder.*;
+ * ComputeService client = // get connection
+ * templateBuilder.options(inboundPorts(22, 80, 8080, 443));
+ * Set&lt;? extends NodeMetadata&gt; set = client.createNodesInGroup(tag, 2, 
templateBuilder.build());
+ * </pre>
+ */
+public class CloudStackTemplateOptions extends TemplateOptions implements 
Cloneable {
+
+   protected Set<String> securityGroupIds = Sets.<String> newLinkedHashSet();
+   protected Map<String, String> ipsToNetworks = Maps.<String, 
String>newLinkedHashMap();
+   protected String ipOnDefaultNetwork;
+   protected String keyPair;
+   protected boolean setupStaticNat = true;
+   protected String account;
+   protected String domainId;
+   protected boolean generateKeyPair = false;
+   protected boolean generateSecurityGroup = false;
+   protected String diskOfferingId;
+   protected int dataDiskSize;
+   
+   @Override
+   public CloudStackTemplateOptions clone() {
+      CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+      copyTo(options);
+      return options;
+   }
+
+   @Override
+   public void copyTo(TemplateOptions to) {
+      super.copyTo(to);
+      if (to instanceof CloudStackTemplateOptions) {
+         CloudStackTemplateOptions eTo = 
CloudStackTemplateOptions.class.cast(to);
+         eTo.securityGroupIds(this.securityGroupIds);
+         eTo.ipsToNetworks(this.ipsToNetworks);
+         eTo.ipOnDefaultNetwork(this.ipOnDefaultNetwork);
+         eTo.keyPair(this.keyPair);
+         eTo.generateKeyPair(shouldGenerateKeyPair());
+         eTo.generateSecurityGroup(shouldGenerateSecurityGroup());
+         eTo.account(this.account);
+         eTo.domainId(this.domainId);
+         eTo.setupStaticNat(setupStaticNat);
+         eTo.diskOfferingId(diskOfferingId);
+         eTo.dataDiskSize(dataDiskSize);
+      }
+   }
+
+   /**
+    * @see 
org.jclouds.cloudstack.options.DeployVirtualMachineOptions#diskOfferingId
+    */
+   public CloudStackTemplateOptions diskOfferingId(String diskOfferingId) {
+      this.diskOfferingId = diskOfferingId;
+      return this;
+   }
+
+   public String getDiskOfferingId() {
+      return diskOfferingId;
+   }
+
+   /**
+    * @see DeployVirtualMachineOptions#dataDiskSize
+    */
+   public CloudStackTemplateOptions dataDiskSize(int dataDiskSize) {
+      this.dataDiskSize = dataDiskSize;
+      return this;
+   }
+
+   public int getDataDiskSize() {
+      return dataDiskSize;
+   }
+
+   /**
+    * @see DeployVirtualMachineOptions#securityGroupId
+    */
+   public CloudStackTemplateOptions securityGroupId(String securityGroupId) {
+      this.securityGroupIds.add(securityGroupId);
+      return this;
+   }
+
+   /**
+    * @see DeployVirtualMachineOptions#securityGroupIds
+    */
+   public CloudStackTemplateOptions securityGroupIds(Iterable<String> 
securityGroupIds) {
+      Iterables.addAll(this.securityGroupIds, checkNotNull(securityGroupIds, 
"securityGroupIds was null"));
+      return this;
+   }
+
+   public Set<String> getSecurityGroupIds() {
+      return securityGroupIds;
+   }
+
+   /**
+    * @see #shouldGenerateKeyPair()
+    */
+   public CloudStackTemplateOptions generateSecurityGroup(boolean enable) {
+      this.generateSecurityGroup = enable;
+      return this;
+   }
+
+   /**
+    * @return true if auto generation of keypairs is enabled
+    */
+   public boolean shouldGenerateSecurityGroup() {
+      return generateSecurityGroup;
+   }
+
+   /**
+    * @deprecated See TemplateOptions#networks
+    * @see DeployVirtualMachineOptions#networkId
+    */
+   @Deprecated
+   public CloudStackTemplateOptions networkId(String networkId) {
+      this.networks.add(networkId);
+      return this;
+   }
+
+   /**
+    * @deprecated See TemplateOptions#networks
+    * @see DeployVirtualMachineOptions#networkIds
+    */
+   @Deprecated
+   public CloudStackTemplateOptions networkIds(Iterable<String> networkIds) {
+      Iterables.addAll(this.networks, checkNotNull(networkIds, "networkIds was 
null"));
+      return this;
+   }
+
+   /**
+    * @deprecated See TemplateOptions#getNetworks
+    */
+   @Deprecated
+   public Set<String> getNetworkIds() {
+      return this.getNetworks();
+   }
+
+   public CloudStackTemplateOptions setupStaticNat(boolean setupStaticNat) {
+      this.setupStaticNat = setupStaticNat;
+      return this;
+   }
+
+   public boolean shouldSetupStaticNat() {
+      return this.setupStaticNat;
+   }
+
+   /**
+    * @see DeployVirtualMachineOptions#ipOnDefaultNetwork
+    */
+   public CloudStackTemplateOptions ipOnDefaultNetwork(String 
ipOnDefaultNetwork) {
+      this.ipOnDefaultNetwork = ipOnDefaultNetwork;
+      return this;
+   }
+
+   public String getIpOnDefaultNetwork() {
+      return ipOnDefaultNetwork;
+   }
+
+   /**
+    * @see DeployVirtualMachineOptions#ipOnDefaultNetwork(String)
+    */
+   public CloudStackTemplateOptions ipsToNetworks(Map<String, String> 
ipsToNetworks) {
+      this.ipsToNetworks.putAll(ipsToNetworks);
+      return this;
+   }
+
+   public Map<String, String> getIpsToNetworks() {
+      return ipsToNetworks;
+   }
+
+   /**
+    * @see DeployVirtualMachineOptions#keyPair(String)
+    */
+   public CloudStackTemplateOptions keyPair(String keyPair) {
+      this.keyPair = keyPair;
+      return this;
+   }
+
+   public String getKeyPair() {
+      return keyPair;
+   }
+
+   /**
+    * @see #shouldGenerateKeyPair()
+    */
+   public CloudStackTemplateOptions generateKeyPair(boolean enable) {
+      this.generateKeyPair = enable;
+      return this;
+   }
+
+   /**
+    * @return true if auto generation of keypairs is enabled
+    */
+   public boolean shouldGenerateKeyPair() {
+      return generateKeyPair;
+   }
+
+   /**
+    * @see DeployVirtualMachineOptions#accountInDomain(String,String)
+    */
+   public CloudStackTemplateOptions account(String account) {
+      this.account = account;
+      return this;
+   }
+
+   public String getAccount() {
+      return account;
+   }
+
+   /**
+    * @see DeployVirtualMachineOptions#accountInDomain(String,String)
+    * @see DeployVirtualMachineOptions#domainId(String)
+    */
+   public CloudStackTemplateOptions domainId(String domainId) {
+      this.domainId = domainId;
+      return this;
+   }
+
+   public String getDomainId() {
+      return domainId;
+   }
+
+   public static final CloudStackTemplateOptions NONE = new 
CloudStackTemplateOptions();
+
+   public static class Builder {
+
+      /**
+       * @see CloudStackTemplateOptions#diskOfferingId
+       */
+      public static CloudStackTemplateOptions diskOfferingId(String 
diskOfferingId) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.diskOfferingId(diskOfferingId);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#dataDiskSize
+       */
+      public static CloudStackTemplateOptions dataDiskSize(int dataDiskSize) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.dataDiskSize(dataDiskSize);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#securityGroupId
+       */
+      public static CloudStackTemplateOptions securityGroupId(String id) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.securityGroupId(id);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#securityGroupIds
+       */
+      public static CloudStackTemplateOptions 
securityGroupIds(Iterable<String> securityGroupIds) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.securityGroupIds(securityGroupIds);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#shouldGenerateSecurityGroup() 
+       */
+      public static CloudStackTemplateOptions generateSecurityGroup(boolean 
enable) {
+         return new CloudStackTemplateOptions().generateSecurityGroup(enable);
+      }
+
+      /**
+       * @deprecated See TemplateOptions#networks
+       * @see CloudStackTemplateOptions#networkId
+       */
+      @Deprecated
+      public static CloudStackTemplateOptions networkId(String id) {
+         return networks(id);
+      }
+
+      /**
+       * @deprecated see TemplateOptions#networks
+       * @see CloudStackTemplateOptions#networkIds
+       */
+      @Deprecated
+      public static CloudStackTemplateOptions networkIds(Iterable<String> 
networkIds) {
+         return networks(networkIds);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#ipOnDefaultNetwork
+       */
+      public static CloudStackTemplateOptions ipOnDefaultNetwork(String 
ipAddress) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.ipOnDefaultNetwork(ipAddress);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#ipsToNetworks
+       */
+      public static CloudStackTemplateOptions ipsToNetworks(Map<String, 
String> ipToNetworkMap) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.ipsToNetworks(ipToNetworkMap);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#setupStaticNat
+       */
+      public static CloudStackTemplateOptions setupStaticNat(boolean 
setupStaticNat) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.setupStaticNat(setupStaticNat);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#keyPair
+       */
+      public static CloudStackTemplateOptions keyPair(String keyPair) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.keyPair(keyPair);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#shouldGenerateKeyPair() 
+       */
+      public static CloudStackTemplateOptions generateKeyPair(boolean enable) {
+         return new CloudStackTemplateOptions().generateKeyPair(enable);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#account
+       */
+      public static CloudStackTemplateOptions account(String account) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.account(account);
+      }
+
+      /**
+       * @see CloudStackTemplateOptions#domainId
+       */
+      public static CloudStackTemplateOptions domainId(String domainId) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return options.domainId(domainId);
+      }
+
+      // methods that only facilitate returning the correct object type
+
+      /**
+       * @see TemplateOptions#inboundPorts(int...)
+       */
+      public static CloudStackTemplateOptions inboundPorts(int... ports) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return 
CloudStackTemplateOptions.class.cast(options.inboundPorts(ports));
+      }
+
+      /**
+       * @see TemplateOptions#blockOnPort(int, int)
+       */
+      public static CloudStackTemplateOptions blockOnPort(int port, int 
seconds) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return CloudStackTemplateOptions.class.cast(options.blockOnPort(port, 
seconds));
+      }
+
+      /**
+       * @see TemplateOptions#userMetadata(Map)
+       */
+      public static CloudStackTemplateOptions userMetadata(Map<String, String> 
userMetadata) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return 
CloudStackTemplateOptions.class.cast(options.userMetadata(userMetadata));
+      }
+
+      /**
+       * @see TemplateOptions#userMetadata(String, String)
+       */
+      public static CloudStackTemplateOptions userMetadata(String key, String 
value) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return CloudStackTemplateOptions.class.cast(options.userMetadata(key, 
value));
+      }
+
+      /**
+       * @see TemplateOptions#nodeNames(Iterable)
+       */
+      public static CloudStackTemplateOptions nodeNames(Iterable<String> 
nodeNames) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return 
CloudStackTemplateOptions.class.cast(options.nodeNames(nodeNames));
+      }
+
+      /**
+       * @see TemplateOptions#networks(Iterable)
+       */
+      public static CloudStackTemplateOptions networks(Iterable<String> 
networks) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return 
CloudStackTemplateOptions.class.cast(options.networks(networks));
+      }
+
+      /**
+       * @see TemplateOptions#networks(String...)
+       */
+      public static CloudStackTemplateOptions networks(String... networks) {
+         CloudStackTemplateOptions options = new CloudStackTemplateOptions();
+         return 
CloudStackTemplateOptions.class.cast(options.networks(networks));
+      }
+   }
+
+   // methods that only facilitate returning the correct object type
+
+   /**
+    * @see TemplateOptions#blockOnPort(int, int)
+    */
+   @Override
+   public CloudStackTemplateOptions blockOnPort(int port, int seconds) {
+      return CloudStackTemplateOptions.class.cast(super.blockOnPort(port, 
seconds));
+   }
+
+   /**
+    * @see TemplateOptions#inboundPorts(int...)
+    */
+   @Override
+   public CloudStackTemplateOptions inboundPorts(int... ports) {
+      return CloudStackTemplateOptions.class.cast(super.inboundPorts(ports));
+   }
+
+   /**
+    * @see TemplateOptions#authorizePublicKey(String)
+    */
+   @Override
+   public CloudStackTemplateOptions authorizePublicKey(String publicKey) {
+      return 
CloudStackTemplateOptions.class.cast(super.authorizePublicKey(publicKey));
+   }
+
+   /**
+    * @see TemplateOptions#installPrivateKey(String)
+    */
+   @Override
+   public CloudStackTemplateOptions installPrivateKey(String privateKey) {
+      return 
CloudStackTemplateOptions.class.cast(super.installPrivateKey(privateKey));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public CloudStackTemplateOptions userMetadata(Map<String, String> 
userMetadata) {
+      return 
CloudStackTemplateOptions.class.cast(super.userMetadata(userMetadata));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public CloudStackTemplateOptions userMetadata(String key, String value) {
+      return CloudStackTemplateOptions.class.cast(super.userMetadata(key, 
value));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public CloudStackTemplateOptions nodeNames(Iterable<String> nodeNames) {
+      return CloudStackTemplateOptions.class.cast(super.nodeNames(nodeNames));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public CloudStackTemplateOptions networks(Iterable<String> networks) {
+      return CloudStackTemplateOptions.class.cast(super.networks(networks));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public CloudStackTemplateOptions networks(String... networks) {
+      return CloudStackTemplateOptions.class.cast(super.networks(networks));
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java
new file mode 100644
index 0000000..6a4bd89
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.cloudstack.compute.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.and;
+import static com.google.common.collect.Iterables.all;
+import static org.jclouds.compute.predicates.NodePredicates.TERMINATED;
+import static org.jclouds.compute.predicates.NodePredicates.inGroup;
+import static org.jclouds.compute.predicates.NodePredicates.locationId;
+import static org.jclouds.compute.predicates.NodePredicates.parentLocationId;
+
+import javax.inject.Inject;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.ComputeMetadata;
+import org.jclouds.cloudstack.domain.ZoneAndName;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+
+public class AllNodesInGroupTerminated implements Predicate<ZoneAndName> {
+   private final ComputeService computeService;
+
+   
+   //TODO: TESTME
+   @Inject
+   public AllNodesInGroupTerminated(ComputeService computeService) {
+      this.computeService = checkNotNull(computeService, "computeService");
+   }
+
+   @Override
+   public boolean apply(ZoneAndName input) {
+      // new nodes can have the zone as their location, existing nodes, the 
parent is the
+      // location
+      return 
all(computeService.listNodesDetailsMatching(Predicates.<ComputeMetadata> 
or(locationId(input.getZone()),
+               parentLocationId(input.getZone()))), 
and(inGroup(input.getName()), TERMINATED));
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java
new file mode 100644
index 0000000..efbce39
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.cloudstack.compute.strategy;
+
+import com.google.common.collect.Iterables;
+import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
+
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Predicates.and;
+import static com.google.common.collect.Iterables.filter;
+import static 
org.jclouds.cloudstack.predicates.NetworkPredicates.defaultNetworkInZone;
+import static 
org.jclouds.cloudstack.predicates.NetworkPredicates.isIsolatedNetwork;
+import static 
org.jclouds.cloudstack.predicates.NetworkPredicates.supportsStaticNAT;
+
+/**
+ * Convert template options into DeployVirtualMachineOptions, when the target 
zone has advanced networking.
+ */
+public class AdvancedNetworkOptionsConverter implements OptionsConverter {
+   @Override
+   public DeployVirtualMachineOptions apply(CloudStackTemplateOptions 
templateOptions, Map<String, Network> networks, String zoneId, 
DeployVirtualMachineOptions options) {
+      // security groups not allowed.
+      // at least one network must be given to CloudStack,
+      // but jclouds will try to autodetect an appropriate network if none 
given.
+      checkArgument(templateOptions.getSecurityGroupIds().isEmpty(), "security 
groups cannot be specified for locations (zones) that use advanced networking");
+      if (!templateOptions.getNetworks().isEmpty()) {
+         options.networkIds(templateOptions.getNetworks());
+      } else if (templateOptions.getIpsToNetworks().isEmpty()) {
+         checkArgument(!networks.isEmpty(), "please setup a network for zone: 
" + zoneId);
+         Network defaultNetworkInZone = 
Iterables.getFirst(filter(networks.values(), and(defaultNetworkInZone(zoneId), 
supportsStaticNAT())), null);
+         if (defaultNetworkInZone == null) {
+             defaultNetworkInZone = 
Iterables.getFirst(filter(networks.values(), isIsolatedNetwork()), null);
+         }
+         if (defaultNetworkInZone == null) {
+             throw new IllegalArgumentException("please choose a specific 
network in zone " + zoneId + ": " + networks);
+         } else {
+             options.networkId(defaultNetworkInZone.getId());
+         }
+      }
+      return options;
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java
new file mode 100644
index 0000000..181ddc7
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java
@@ -0,0 +1,41 @@
+/*
+ * 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.cloudstack.compute.strategy;
+
+import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
+
+import java.util.Map;
+
+/**
+ * Convert template options into DeployVirtualMachineOptions, when the target 
zone has basic networking.
+ */
+public class BasicNetworkOptionsConverter implements OptionsConverter {
+   @Override
+   public DeployVirtualMachineOptions apply(CloudStackTemplateOptions 
templateOptions, Map<String, Network> networks, String zoneId, 
DeployVirtualMachineOptions options) {
+      // both security groups and networks are optional, and CloudStack will
+      // use the zone/user's default network/security group if none given
+      if (!templateOptions.getSecurityGroupIds().isEmpty()) {
+         options.securityGroupIds(templateOptions.getSecurityGroupIds());
+      }
+      if (!templateOptions.getNetworks().isEmpty()) {
+         options.networkIds(templateOptions.getNetworks());
+      }
+      return options;
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
new file mode 100644
index 0000000..29f5c85
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
@@ -0,0 +1,476 @@
+/*
+ * 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.cloudstack.compute.strategy;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.contains;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.get;
+import static 
org.jclouds.cloudstack.options.DeployVirtualMachineOptions.Builder.displayName;
+import static org.jclouds.cloudstack.options.ListTemplatesOptions.Builder.id;
+import static org.jclouds.cloudstack.predicates.TemplatePredicates.isReady;
+import static 
org.jclouds.cloudstack.predicates.ZonePredicates.supportsSecurityGroups;
+import static org.jclouds.ssh.SshKeys.fingerprintPrivateKey;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.cloudstack.CloudStackApi;
+import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.Capabilities;
+import org.jclouds.cloudstack.domain.FirewallRule;
+import org.jclouds.cloudstack.domain.IPForwardingRule;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.domain.NetworkType;
+import org.jclouds.cloudstack.domain.Project;
+import org.jclouds.cloudstack.domain.PublicIPAddress;
+import org.jclouds.cloudstack.domain.SecurityGroup;
+import org.jclouds.cloudstack.domain.ServiceOffering;
+import org.jclouds.cloudstack.domain.SshKeyPair;
+import org.jclouds.cloudstack.domain.Template;
+import org.jclouds.cloudstack.domain.VirtualMachine;
+import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.domain.ZoneAndName;
+import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
+import org.jclouds.cloudstack.features.TemplateApi;
+import org.jclouds.cloudstack.functions.CreateFirewallRulesForIP;
+import org.jclouds.cloudstack.functions.CreatePortForwardingRulesForIP;
+import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
+import 
org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork.Factory;
+import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
+import org.jclouds.cloudstack.options.ListFirewallRulesOptions;
+import org.jclouds.cloudstack.options.ListTemplatesOptions;
+import org.jclouds.cloudstack.strategy.BlockUntilJobCompletesAndReturnResult;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Sets;
+import com.google.common.primitives.Ints;
+
+/**
+ * defines the connection between the {@link CloudStackApi} implementation
+ * and the jclouds {@link ComputeService}
+ */
+@Singleton
+public class CloudStackComputeServiceAdapter implements
+   ComputeServiceAdapter<VirtualMachine, ServiceOffering, Template, Zone> {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final CloudStackApi client;
+   private final Predicate<String> jobComplete;
+   private final Supplier<Map<String, Network>> networkSupplier;
+   private final Supplier<Map<String, Project>> projectSupplier;
+   private final BlockUntilJobCompletesAndReturnResult 
blockUntilJobCompletesAndReturnResult;
+   private final Factory staticNATVMInNetwork;
+   private final CreatePortForwardingRulesForIP setupPortForwardingRulesForIP;
+   private final CreateFirewallRulesForIP setupFirewallRulesForIP;
+   private final LoadingCache<String, Set<IPForwardingRule>> vmToRules;
+   private final Map<String, Credentials> credentialStore;
+   private final Map<NetworkType, ? extends OptionsConverter> 
optionsConverters;
+   private final Supplier<LoadingCache<String, Zone>> zoneIdToZone;
+   private final LoadingCache<ZoneAndName, SecurityGroup> securityGroupCache;
+   private final LoadingCache<String, SshKeyPair> keyPairCache;
+   private final GroupNamingConvention.Factory namingConvention;
+
+   @Inject
+   public CloudStackComputeServiceAdapter(CloudStackApi client, 
Predicate<String> jobComplete,
+                                          @Memoized Supplier<Map<String, 
Network>> networkSupplier,
+                                          @Memoized Supplier<Map<String, 
Project>> projectSupplier,
+                                          
BlockUntilJobCompletesAndReturnResult blockUntilJobCompletesAndReturnResult,
+                                          
StaticNATVirtualMachineInNetwork.Factory staticNATVMInNetwork,
+                                          CreatePortForwardingRulesForIP 
setupPortForwardingRulesForIP,
+                                          CreateFirewallRulesForIP 
setupFirewallRulesForIP,
+                                          LoadingCache<String, 
Set<IPForwardingRule>> vmToRules,
+                                          Map<String, Credentials> 
credentialStore,
+                                          Map<NetworkType, ? extends 
OptionsConverter> optionsConverters,
+                                          Supplier<LoadingCache<String, Zone>> 
zoneIdToZone,
+                                          LoadingCache<ZoneAndName, 
SecurityGroup> securityGroupCache,
+                                          LoadingCache<String, SshKeyPair> 
keyPairCache,
+                                          GroupNamingConvention.Factory 
namingConvention) {
+      this.client = checkNotNull(client, "client");
+      this.jobComplete = checkNotNull(jobComplete, "jobComplete");
+      this.networkSupplier = checkNotNull(networkSupplier, "networkSupplier");
+      this.projectSupplier = checkNotNull(projectSupplier, "projectSupplier");
+      this.blockUntilJobCompletesAndReturnResult = 
checkNotNull(blockUntilJobCompletesAndReturnResult,
+         "blockUntilJobCompletesAndReturnResult");
+      this.staticNATVMInNetwork = checkNotNull(staticNATVMInNetwork, 
"staticNATVMInNetwork");
+      this.setupPortForwardingRulesForIP = 
checkNotNull(setupPortForwardingRulesForIP, "setupPortForwardingRulesForIP");
+      this.setupFirewallRulesForIP = checkNotNull(setupFirewallRulesForIP, 
"setupFirewallRulesForIP");
+      this.vmToRules = checkNotNull(vmToRules, "vmToRules");
+      this.credentialStore = checkNotNull(credentialStore, "credentialStore");
+      this.securityGroupCache = checkNotNull(securityGroupCache, 
"securityGroupCache");
+      this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
+      this.optionsConverters = optionsConverters;
+      this.zoneIdToZone = zoneIdToZone;
+      this.namingConvention = namingConvention;
+   }
+
+   @Override
+   public NodeAndInitialCredentials<VirtualMachine> 
createNodeWithGroupEncodedIntoName(String group, String name,
+                                                                               
        org.jclouds.compute.domain.Template template) {
+
+      checkNotNull(template, "template was null");
+      checkNotNull(template.getOptions(), "template options was null");
+      
checkArgument(template.getOptions().getClass().isAssignableFrom(CloudStackTemplateOptions.class),
+         "options class %s should have been assignable from 
CloudStackTemplateOptions", template.getOptions()
+         .getClass());
+      Map<String, Network> networks = networkSupplier.get();
+
+      final String zoneId = template.getLocation().getId();
+      Zone zone = zoneIdToZone.get().getUnchecked(zoneId);
+
+      CloudStackTemplateOptions templateOptions = 
template.getOptions().as(CloudStackTemplateOptions.class);
+
+      checkState(optionsConverters.containsKey(zone.getNetworkType()), "no 
options converter configured for network type %s", zone.getNetworkType());
+      DeployVirtualMachineOptions options = displayName(name).name(name);
+      if (templateOptions.getAccount() != null) {
+          options.accountInDomain(templateOptions.getAccount(), 
templateOptions.getDomainId());
+      } else if (templateOptions.getDomainId() != null) {
+          options.domainId(templateOptions.getDomainId());
+      }
+
+      OptionsConverter optionsConverter = 
optionsConverters.get(zone.getNetworkType());
+      options = optionsConverter.apply(templateOptions, networks, zoneId, 
options);
+
+      options.group(group);
+
+      if (templateOptions.getIpOnDefaultNetwork() != null) {
+         options.ipOnDefaultNetwork(templateOptions.getIpOnDefaultNetwork());
+      }
+
+      if (!templateOptions.getIpsToNetworks().isEmpty()) {
+         options.ipsToNetworks(templateOptions.getIpsToNetworks());
+      }
+
+      if (templateOptions.getKeyPair() != null) {
+         SshKeyPair keyPair = null;
+         if (templateOptions.getLoginPrivateKey() != null) {
+            String pem = templateOptions.getLoginPrivateKey();
+            keyPair = SshKeyPair.builder().name(templateOptions.getKeyPair())
+               
.fingerprint(fingerprintPrivateKey(pem)).privateKey(pem).build();
+            keyPairCache.asMap().put(keyPair.getName(), keyPair);
+            options.keyPair(keyPair.getName());
+         } else if 
(client.getSSHKeyPairApi().getSSHKeyPair(templateOptions.getKeyPair()) != null) 
{
+            keyPair = 
client.getSSHKeyPairApi().getSSHKeyPair(templateOptions.getKeyPair());
+         }
+         if (keyPair != null) {
+            keyPairCache.asMap().put(keyPair.getName(), keyPair);
+            options.keyPair(keyPair.getName());
+         }
+      } else if (templateOptions.shouldGenerateKeyPair()) {
+         SshKeyPair keyPair = 
keyPairCache.getUnchecked(namingConvention.create()
+                                                        
.sharedNameForGroup(group));
+         keyPairCache.asMap().put(keyPair.getName(), keyPair);
+         templateOptions.keyPair(keyPair.getName());
+         options.keyPair(keyPair.getName());
+      }
+
+      if (templateOptions.getDiskOfferingId() != null) {
+         options.diskOfferingId(templateOptions.getDiskOfferingId());
+         if (templateOptions.getDataDiskSize() > 0) {
+            options.dataDiskSize(templateOptions.getDataDiskSize());
+         }
+      }
+
+      if (supportsSecurityGroups().apply(zone)) {
+         List<Integer> inboundPorts = 
Ints.asList(templateOptions.getInboundPorts());
+
+         if (templateOptions.getSecurityGroupIds().isEmpty()
+             && !inboundPorts.isEmpty()
+             && templateOptions.shouldGenerateSecurityGroup()) {
+            String securityGroupName = 
namingConvention.create().sharedNameForGroup(group);
+            SecurityGroup sg = 
securityGroupCache.getUnchecked(ZoneSecurityGroupNamePortsCidrs.builder()
+                                                               
.zone(zone.getId())
+                                                               
.name(securityGroupName)
+                                                               
.ports(ImmutableSet.copyOf(inboundPorts))
+                                                               
.cidrs(ImmutableSet.<String> of()).build());
+            options.securityGroupId(sg.getId());
+         }
+      }
+
+      String templateId = template.getImage().getId();
+      String serviceOfferingId = template.getHardware().getId();
+
+      logger.debug("serviceOfferingId %s, templateId %s, zoneId %s, options 
%s%n", serviceOfferingId, templateId,
+         zoneId, options);
+      AsyncCreateResponse job = 
client.getVirtualMachineApi().deployVirtualMachineInZone(zoneId, 
serviceOfferingId,
+         templateId, options);
+      VirtualMachine vm = 
blockUntilJobCompletesAndReturnResult.<VirtualMachine>apply(job);
+      logger.debug("--- virtualmachine: %s", vm);
+      LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder();
+      if (templateOptions.getKeyPair() != null) {
+         SshKeyPair keyPair = 
keyPairCache.getUnchecked(templateOptions.getKeyPair());
+         credentialsBuilder.privateKey(keyPair.getPrivateKey());
+      } else if (vm.isPasswordEnabled()) {
+         assert vm.getPassword() != null : vm;
+         credentialsBuilder.password(vm.getPassword());
+      }
+
+      try {
+          if (templateOptions.shouldSetupStaticNat()) {
+             Capabilities capabilities = 
client.getConfigurationApi().listCapabilities();
+             // TODO: possibly not all network ids, do we want to do this
+             for (String networkId : options.getNetworkIds()) {
+                logger.debug(">> creating static NAT for virtualMachine(%s) in 
network(%s)", vm.getId(), networkId);
+                PublicIPAddress ip = 
staticNATVMInNetwork.create(networks.get(networkId)).apply(vm);
+                logger.trace("<< static NATed IPAddress(%s) to 
virtualMachine(%s)", ip.getId(), vm.getId());
+                vm = 
client.getVirtualMachineApi().getVirtualMachine(vm.getId());
+                List<Integer> ports = 
Ints.asList(templateOptions.getInboundPorts());
+                if (capabilities.getCloudStackVersion().startsWith("2")) {
+                   logger.debug(">> setting up IP forwarding for IPAddress(%s) 
rules(%s)", ip.getId(), ports);
+                   Set<IPForwardingRule> rules = 
setupPortForwardingRulesForIP.apply(ip, ports);
+                   logger.trace("<< setup %d IP forwarding rules on 
IPAddress(%s)", rules.size(), ip.getId());
+                } else {
+                   logger.debug(">> setting up firewall rules for 
IPAddress(%s) rules(%s)", ip.getId(), ports);
+                   Set<FirewallRule> rules = setupFirewallRulesForIP.apply(ip, 
ports);
+                   logger.trace("<< setup %d firewall rules on IPAddress(%s)", 
rules.size(), ip.getId());
+                }
+             }
+          }
+      } catch (RuntimeException re) {
+          logger.error("-- exception after node has been created, trying to 
destroy the created virtualMachine(%s)", vm.getId());
+          try {
+              destroyNode(vm.getId());
+          } catch (RuntimeException re2) {
+              logger.debug("-- exception in exceptionHandler while executing 
destroyNode for virtualMachine(%s), ignoring and rethrowing original 
exception", vm.getId());
+          }
+          throw re;
+      }
+      return new NodeAndInitialCredentials<VirtualMachine>(vm, vm.getId() + 
"", credentialsBuilder.build());
+   }
+
+   @Override
+   public Iterable<ServiceOffering> listHardwareProfiles() {
+      // TODO: we may need to filter these
+      return client.getOfferingApi().listServiceOfferings();
+   }
+
+   @Override
+   public Iterable<Template> listImages() {
+      TemplateApi templateApi = client.getTemplateApi();
+      ImmutableSet.Builder<Template> templates = ImmutableSet.builder();
+      templates.addAll(templateApi.listTemplates());
+      for (String project : projectSupplier.get().keySet()) {
+         
templates.addAll(templateApi.listTemplates(ListTemplatesOptions.Builder.projectId(project)));
+      }
+
+      return filter(templates.build(), isReady());
+   }
+
+   @Override
+   public Template getImage(String id) {
+      return get(client.getTemplateApi().listTemplates(id(id)), 0, null);
+   }
+
+   @Override
+   public Iterable<VirtualMachine> listNodes() {
+      return client.getVirtualMachineApi().listVirtualMachines();
+   }
+
+   @Override
+   public Iterable<VirtualMachine> listNodesByIds(final Iterable<String> ids) {
+      return filter(listNodes(), new Predicate<VirtualMachine>() {
+
+            @Override
+            public boolean apply(VirtualMachine vm) {
+               return contains(ids, vm.getId());
+            }
+         });
+   }
+
+   @Override
+   public Iterable<Zone> listLocations() {
+      // TODO: we may need to filter these
+      return client.getZoneApi().listZones();
+   }
+
+   @Override
+   public VirtualMachine getNode(String id) {
+      String virtualMachineId = id;
+      return client.getVirtualMachineApi().getVirtualMachine(virtualMachineId);
+   }
+
+   @Override
+   public void destroyNode(String id) {
+      String virtualMachineId = id;
+      // There was a bug in 2.2.12 release happening when static nat IP address
+      // was being released, and corresponding firewall rules were left behind.
+      // So next time the same IP is allocated, it might be not be static nat
+      // enabled, but there are still rules associated with it. And when you 
try
+      // to release this IP, the release will fail.
+      //
+      // The bug was fixed in 2.2.13 release only, and the current system 
wasn't
+      // updated yet.
+      //
+      // To avoid the issue, every time you release a static nat ip address, do
+      // the following:
+
+      // 1) Delete IP forwarding rules associated with IP.
+      Set<String> ipAddresses = 
deleteIPForwardingRulesForVMAndReturnDistinctIPs(virtualMachineId);
+
+      // 2) Delete firewall rules associated with IP.
+      
ipAddresses.addAll(deleteFirewallRulesForVMAndReturnDistinctIPs(virtualMachineId));
+
+      // 3) Disable static nat rule for the IP.
+      disableStaticNATOnIPAddresses(ipAddresses);
+
+      // 4) Only after 1 and 2 release the IP address.
+      disassociateIPAddresses(ipAddresses);
+
+      destroyVirtualMachine(virtualMachineId);
+
+      vmToRules.invalidate(virtualMachineId);
+   }
+
+   public void disassociateIPAddresses(Set<String> ipAddresses) {
+      for (String ipAddress : ipAddresses) {
+         logger.debug(">> disassociating IPAddress(%s)", ipAddress);
+         client.getAddressApi().disassociateIPAddress(ipAddress);
+      }
+   }
+
+   public void destroyVirtualMachine(String virtualMachineId) {
+
+      String destroyVirtualMachine = 
client.getVirtualMachineApi().destroyVirtualMachine(virtualMachineId);
+      if (destroyVirtualMachine != null) {
+         logger.debug(">> destroying virtualMachine(%s) job(%s)", 
virtualMachineId, destroyVirtualMachine);
+         awaitCompletion(destroyVirtualMachine);
+      } else {
+         logger.trace("<< virtualMachine(%s) not found", virtualMachineId);
+      }
+
+   }
+
+   public void disableStaticNATOnIPAddresses(Set<String> ipAddresses) {
+      Builder<String> jobsToTrack = ImmutableSet.builder();
+      for (String ipAddress : ipAddresses) {
+         String disableStaticNAT = 
client.getNATApi().disableStaticNATOnPublicIP(ipAddress);
+         if (disableStaticNAT != null) {
+            logger.debug(">> disabling static NAT IPAddress(%s) job(%s)", 
ipAddress, disableStaticNAT);
+            jobsToTrack.add(disableStaticNAT);
+         }
+      }
+      awaitCompletion(jobsToTrack.build());
+   }
+
+   public Set<String> deleteIPForwardingRulesForVMAndReturnDistinctIPs(String 
virtualMachineId) {
+      Builder<String> jobsToTrack = ImmutableSet.builder();
+
+      // immutable doesn't permit duplicates
+      Set<String> ipAddresses = Sets.newLinkedHashSet();
+
+      Set<IPForwardingRule> forwardingRules = 
client.getNATApi().getIPForwardingRulesForVirtualMachine(
+         virtualMachineId);
+      for (IPForwardingRule rule : forwardingRules) {
+         if (!"Deleting".equals(rule.getState())) {
+            ipAddresses.add(rule.getIPAddressId());
+            String deleteForwardingRule = 
client.getNATApi().deleteIPForwardingRule(rule.getId());
+            if (deleteForwardingRule != null) {
+               logger.debug(">> deleting IPForwardingRule(%s) job(%s)", 
rule.getId(), deleteForwardingRule);
+               jobsToTrack.add(deleteForwardingRule);
+            }
+         }
+      }
+      awaitCompletion(jobsToTrack.build());
+      return ipAddresses;
+   }
+
+   public Set<String> deleteFirewallRulesForVMAndReturnDistinctIPs(String 
virtualMachineId) {
+      // immutable doesn't permit duplicates
+      Set<String> ipAddresses = Sets.newLinkedHashSet();
+
+      String publicIpId = 
client.getVirtualMachineApi().getVirtualMachine(virtualMachineId).getPublicIPId();
+      if (publicIpId != null) {
+         Set<FirewallRule> firewallRules = client.getFirewallApi()
+            
.listFirewallRules(ListFirewallRulesOptions.Builder.ipAddressId(client.getVirtualMachineApi().getVirtualMachine(virtualMachineId).getPublicIPId()));
+
+         for (FirewallRule rule : firewallRules) {
+            if (rule.getState() != FirewallRule.State.DELETING) {
+               ipAddresses.add(rule.getIpAddressId());
+               client.getFirewallApi().deleteFirewallRule(rule.getId());
+               logger.debug(">> deleting FirewallRule(%s)", rule.getId());
+            }
+         }
+      }
+      return ipAddresses;
+   }
+
+   public void awaitCompletion(Iterable<String> jobs) {
+      logger.debug(">> awaiting completion of jobs(%s)", jobs);
+      for (String job : jobs)
+         awaitCompletion(job);
+      logger.trace("<< completed jobs(%s)", jobs);
+   }
+
+   public void awaitCompletion(String job) {
+      boolean completed = jobComplete.apply(job);
+      logger.trace("<< job(%s) complete(%s)", job, completed);
+   }
+
+   @Override
+   public void rebootNode(String id) {
+      String virtualMachineId = id;
+      String job = 
client.getVirtualMachineApi().rebootVirtualMachine(virtualMachineId);
+      if (job != null) {
+         logger.debug(">> rebooting virtualMachine(%s) job(%s)", 
virtualMachineId, job);
+         awaitCompletion(job);
+      }
+   }
+
+   @Override
+   public void resumeNode(String id) {
+      String virtualMachineId = id;
+      String job = client.getVirtualMachineApi().startVirtualMachine(id);
+      if (job != null) {
+         logger.debug(">> starting virtualMachine(%s) job(%s)", 
virtualMachineId, job);
+         awaitCompletion(job);
+      }
+   }
+
+   @Override
+   public void suspendNode(String id) {
+      String virtualMachineId = id;
+      String job = client.getVirtualMachineApi().stopVirtualMachine(id);
+      if (job != null) {
+         logger.debug(">> stopping virtualMachine(%s) job(%s)", 
virtualMachineId, job);
+         awaitCompletion(job);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java
new file mode 100644
index 0000000..4cc6b9f
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.cloudstack.compute.strategy;
+
+import java.util.Map;
+
+import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
+
+/**
+ * Convert template options into DeployVirtualMachineOptions. Expressed as an 
interface, because in
+ * CloudStack different zone network types have different requirements when it 
comes to networks and
+ * security groups.
+ */
+public interface OptionsConverter {
+
+   /**
+    * Convert a CloudStackTemplateOptions and apply to a 
DeployVirtualMachineOptions instance.
+    *
+    * @param templateOptions the input set of options
+    * @param networks the networks available
+    * @param zoneId the zone of the new virtual machine
+    * @param options where the resulting set of options will be applied
+    * @return same as "options" parameter
+    */
+   DeployVirtualMachineOptions apply(CloudStackTemplateOptions 
templateOptions, Map<String, Network> networks, String zoneId, 
DeployVirtualMachineOptions options);
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackHttpApiModule.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackHttpApiModule.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackHttpApiModule.java
new file mode 100644
index 0000000..95f186e
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackHttpApiModule.java
@@ -0,0 +1,154 @@
+/*
+ * 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.cloudstack.config;
+
+import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
+
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.Constants;
+import org.jclouds.cloudstack.CloudStackApi;
+import org.jclouds.cloudstack.CloudStackDomainApi;
+import org.jclouds.cloudstack.CloudStackGlobalApi;
+import org.jclouds.cloudstack.domain.LoginResponse;
+import org.jclouds.cloudstack.features.SessionApi;
+import org.jclouds.cloudstack.filters.AddSessionKeyAndJSessionIdToRequest;
+import org.jclouds.cloudstack.filters.AuthenticationFilter;
+import org.jclouds.cloudstack.filters.QuerySigner;
+import org.jclouds.cloudstack.handlers.CloudStackErrorHandler;
+import 
org.jclouds.cloudstack.handlers.InvalidateSessionAndRetryOn401AndLogoutOnClose;
+import org.jclouds.cloudstack.loaders.LoginWithPasswordCredentials;
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpRetryHandler;
+import org.jclouds.http.annotation.ClientError;
+import org.jclouds.http.annotation.Redirection;
+import org.jclouds.http.annotation.ServerError;
+import org.jclouds.location.Provider;
+import org.jclouds.location.suppliers.ImplicitLocationSupplier;
+import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone;
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.rest.ApiContext;
+import org.jclouds.rest.config.HttpApiModule;
+import org.jclouds.rest.internal.ApiContextImpl;
+
+import com.google.common.base.Supplier;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.LoadingCache;
+import com.google.inject.Inject;
+import com.google.inject.Provides;
+import com.google.inject.Scopes;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+
+/**
+ * Configures the cloudstack connection.
+ */
+@ConfiguresHttpApi
+public class CloudStackHttpApiModule extends HttpApiModule<CloudStackApi> {
+
+   @Override
+   protected void configure() {
+      bind(new TypeLiteral<ApiContext<CloudStackDomainApi>>() {
+      }).to(new TypeLiteral<ApiContextImpl<CloudStackDomainApi>>() {
+      });
+      bind(new TypeLiteral<ApiContext<CloudStackGlobalApi>>() {
+      }).to(new TypeLiteral<ApiContextImpl<CloudStackGlobalApi>>() {
+      });
+      
bind(CredentialType.class).toProvider(CredentialTypeFromPropertyOrDefault.class);
+      // session client is used directly for filters and retry handlers, so 
let's bind it explicitly
+      bindHttpApi(binder(), SessionApi.class);
+      bindHttpApi(binder(), CloudStackDomainApi.class);
+      bindHttpApi(binder(), CloudStackGlobalApi.class);
+      
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(InvalidateSessionAndRetryOn401AndLogoutOnClose.class);
+      
+      super.configure();
+   }
+
+   @Override
+   protected void installLocations() {
+      super.installLocations();
+      
bind(ImplicitLocationSupplier.class).to(OnlyLocationOrFirstZone.class).in(Scopes.SINGLETON);
+   }
+
+   @Override
+   protected void bindErrorHandlers() {
+      
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(CloudStackErrorHandler.class);
+      
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(CloudStackErrorHandler.class);
+      
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(CloudStackErrorHandler.class);
+   }
+
+   @Singleton
+   static class CredentialTypeFromPropertyOrDefault implements 
javax.inject.Provider<CredentialType> {
+      /**
+       * use optional injection to supply a default value for credential type. 
so that we don't have
+       * to set a default property.
+       */
+      @Inject(optional = true)
+      @Named(CloudStackProperties.CREDENTIAL_TYPE)
+      String credentialType = 
CredentialType.API_ACCESS_KEY_CREDENTIALS.toString();
+
+      @Override
+      public CredentialType get() {
+         return CredentialType.fromValue(credentialType);
+      }
+   }
+
+   /**
+    * we use the type of credentials specified at login to determine which way 
we want to filter the
+    * request. <br/>
+    * for ex, if we are getting passwords, we know we will need to 
login/logout. Otherwise we are
+    * signing requests.
+    */
+   @Provides
+   @Singleton
+   protected AuthenticationFilter 
authenticationFilterForCredentialType(CredentialType credentialType,
+            AddSessionKeyAndJSessionIdToRequest 
addSessionKeyAndJSessionIdToRequest, QuerySigner querySigner) {
+      switch (credentialType) {
+         case PASSWORD_CREDENTIALS:
+            return addSessionKeyAndJSessionIdToRequest;
+         case API_ACCESS_KEY_CREDENTIALS:
+            return querySigner;
+         default:
+            throw new IllegalArgumentException("credential type not supported: 
" + credentialType);
+      }
+   }
+
+   // PROPERTY_SESSION_INTERVAL is default to 60 seconds
+   @Provides
+   @Singleton
+   protected LoadingCache<Credentials, LoginResponse> 
provideLoginResponseCache(
+            LoginWithPasswordCredentials getLoginResponse,
+            @Named(Constants.PROPERTY_SESSION_INTERVAL) int seconds) {
+      return CacheBuilder.newBuilder().expireAfterWrite(seconds, 
TimeUnit.SECONDS).build(getLoginResponse);
+   }
+
+   // Temporary conversion of a cache to a supplier until there is a 
single-element cache
+   // http://code.google.com/p/guava-libraries/issues/detail?id=872
+   @Provides
+   @Singleton
+   protected Supplier<LoginResponse> provideLoginResponseSupplier(final 
LoadingCache<Credentials, LoginResponse> cache,
+         @Provider final Supplier<Credentials> creds) {
+      return new Supplier<LoginResponse>() {
+         @Override
+         public LoginResponse get() {
+            return cache.getUnchecked(creds.get());
+         }
+      };
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java
new file mode 100644
index 0000000..ab1fac0
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java
@@ -0,0 +1,105 @@
+/*
+ * 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.cloudstack.config;
+
+import java.io.IOException;
+import java.util.Date;
+
+import javax.inject.Inject;
+
+import org.jclouds.date.DateService;
+import org.jclouds.json.config.GsonModule.DateAdapter;
+import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
+import 
org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapter;
+import 
org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapterFactory;
+
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import com.google.inject.AbstractModule;
+
+public class CloudStackParserModule extends AbstractModule {
+
+   @Override
+   protected void configure() {
+      bind(DateAdapter.class).to(CloudStackDateAdapter.class);
+      
bind(IterableTypeAdapterFactory.class).to(CommaDelimitedOKIterableTypeAdapterFactory.class);
+   }
+
+   /**
+    * Data adapter for the date formats used by CloudStack.
+    * 
+    * Essentially this is a workaround for the CloudStack getUsage() API call 
returning a corrupted form of ISO-8601
+    * dates, which have an unexpected pair of apostrophes, like 
2011-12-12'T'00:00:00+00:00
+    * 
+    */
+   public static class CloudStackDateAdapter extends Iso8601DateAdapter {
+
+      @Inject
+      private CloudStackDateAdapter(DateService dateService) {
+         super(dateService);
+      }
+
+      public Date read(JsonReader reader) throws IOException {
+         return parseDate(reader.nextString().replaceAll("'T'", "T"));
+      }
+
+   }
+
+   /**
+    * Handles types that were previously strings and now arrays (ex. tags)
+    * 
+    */
+   public static class CommaDelimitedOKIterableTypeAdapterFactory extends 
IterableTypeAdapterFactory {
+
+      @Override
+      @SuppressWarnings("unchecked")
+      protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> 
elementAdapter) {
+         return (TypeAdapter<I>) new Adapter<E>(elementAdapter);
+      }
+
+      public static final class Adapter<E> extends TypeAdapter<Iterable<E>> {
+
+         private final IterableTypeAdapter<E> delegate;
+
+         public Adapter(TypeAdapter<E> elementAdapter) {
+            this.delegate = new IterableTypeAdapter<E>(elementAdapter);
+            nullSafe();
+         }
+
+         public void write(JsonWriter out, Iterable<E> value) throws 
IOException {
+            this.delegate.write(out, value);
+         }
+
+         @SuppressWarnings("unchecked")
+         @Override
+         public Iterable<E> read(JsonReader in) throws IOException {
+            // HACK as cloudstack changed a field from String to Set!
+            if (in.peek() == JsonToken.STRING) {
+               String val = Strings.emptyToNull(in.nextString());
+               return (Iterable<E>) (val != null ? Splitter.on(',').split(val) 
: ImmutableSet.of());
+            } else {
+               return delegate.read(in);
+            }
+         }
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java
new file mode 100644
index 0000000..e123847
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.cloudstack.config;
+
+
+/**
+ * Configuration properties and constants used in CloudStack connections.
+ */
+public final class CloudStackProperties {
+
+   /**
+    * Type of credentials specified during {@link 
ComputeServiceContextBuilder#overrides}. If
+    * {@link CredentialType#API_ACCESS_KEY_CREDENTIALS}, the request signing 
is used. If
+    * {@link CredentialType#PASSWORD_CREDENTIALS}, login will happen and a 
session will be
+    * persisted.
+    * 
+    * <h3>valid values</h3>
+    * <ul>
+    * <li>apiAccessKeyCredentials</li>
+    * <li>passwordCredentials</li>
+    * </ul>
+    * 
+    * @see CredentialType
+    * @see <a 
href="http://docs.cloud.com/CloudStack_Documentation/Customizing_the_CloudStack_UI#Cross_Site_Request_Forgery_%28CSRF%29";
+    *      />
+    */
+   public static final String CREDENTIAL_TYPE = 
"jclouds.cloudstack.credential-type";
+
+   /**
+    * Whenever a node is created, automatically generate keypairs for groups, 
as needed, also
+    * delete the keypair(s) when the last node in the group is destroyed.
+    */
+   public static final String AUTO_GENERATE_KEYPAIRS = 
"jclouds.cloudstack.auto-generate-keypairs";
+
+   private CloudStackProperties() {
+      throw new AssertionError("intentionally unimplemented");
+   }
+}

Reply via email to