Repository: jclouds
Updated Branches:
  refs/heads/master 04e6620fb -> d99ad1b94


JCLOUDS-486 This will allow booting up nova servers with fixed IPs and ports.


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

Branch: refs/heads/master
Commit: d99ad1b94a5e3186b521c01ddf9f1a8b1201dafa
Parents: 04e6620
Author: Zack Shoylev <[email protected]>
Authored: Wed Mar 5 13:13:22 2014 -0600
Committer: Zack Shoylev <[email protected]>
Committed: Tue Mar 11 14:58:22 2014 -0500

----------------------------------------------------------------------
 .../v2_0/compute/NovaComputeServiceAdapter.java |   7 +-
 .../compute/options/NovaTemplateOptions.java    |  48 +++-
 .../openstack/nova/v2_0/domain/Network.java     | 173 +++++++++++
 .../nova/v2_0/options/CreateServerOptions.java  | 103 +++++--
 .../NovaComputeServiceAdapterExpectTest.java    |  49 ++++
 .../nova/v2_0/features/ServerApiLiveTest.java   | 288 ++++++++++---------
 .../resources/new_server_nova_networks.json     |  41 +++
 7 files changed, 553 insertions(+), 156 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/d99ad1b9/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
 
b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
index 0241c17..e4ceade 100644
--- 
a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
+++ 
b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
@@ -110,7 +110,12 @@ public class NovaComputeServiceAdapter implements
       options.userData(templateOptions.getUserData());
       options.diskConfig(templateOptions.getDiskConfig());
       options.configDrive(templateOptions.getConfigDrive());
-      options.networks(templateOptions.getNetworks());
+      if (templateOptions.getNovaNetworks() != null) {
+         options.novaNetworks(templateOptions.getNovaNetworks());
+      }
+      if (templateOptions.getNetworks() != null) {
+         options.networks(templateOptions.getNetworks());
+      }
 
       Optional<String> privateKey = Optional.absent();
       if (templateOptions.getKeyPairName() != null) {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/d99ad1b9/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java
 
b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java
index d82d901..24328b8 100644
--- 
a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java
+++ 
b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java
@@ -27,6 +27,8 @@ import java.util.Set;
 
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.domain.LoginCredentials;
+import org.jclouds.openstack.nova.v2_0.domain.Network;
+import org.jclouds.openstack.nova.v2_0.options.CreateServerOptions;
 import org.jclouds.scriptbuilder.domain.Statement;
 
 import com.google.common.base.Objects;
@@ -76,6 +78,7 @@ public class NovaTemplateOptions extends TemplateOptions 
implements Cloneable {
          }
 
          eTo.configDrive(getConfigDrive());
+         eTo.novaNetworks(getNovaNetworks());
       }
    }
 
@@ -86,6 +89,7 @@ public class NovaTemplateOptions extends TemplateOptions 
implements Cloneable {
    protected byte[] userData;
    protected String diskConfig;
    protected boolean configDrive;
+   protected Set<Network> novaNetworks;
 
    @Override
    public boolean equals(Object o) {
@@ -100,12 +104,13 @@ public class NovaTemplateOptions extends TemplateOptions 
implements Cloneable {
             && equal(this.keyPairName, that.keyPairName)
             && Arrays.equals(this.userData, that.userData)
             && equal(this.diskConfig, that.diskConfig)
-            && equal(this.configDrive, that.configDrive);
+            && equal(this.configDrive, that.configDrive)
+            && equal(this.novaNetworks, that.novaNetworks);
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(super.hashCode(), autoAssignFloatingIp, 
securityGroupNames, generateKeyPair, keyPairName, userData, diskConfig, 
configDrive);
+      return Objects.hashCode(super.hashCode(), autoAssignFloatingIp, 
securityGroupNames, generateKeyPair, keyPairName, userData, diskConfig, 
configDrive, novaNetworks);
    }
 
    @Override
@@ -121,6 +126,7 @@ public class NovaTemplateOptions extends TemplateOptions 
implements Cloneable {
       toString.add("userData", userData);
       toString.add("diskConfig", diskConfig);
       toString.add("configDrive", configDrive);
+      toString.add("novaNetworks", novaNetworks);
       return toString;
    }
 
@@ -228,6 +234,13 @@ public class NovaTemplateOptions extends TemplateOptions 
implements Cloneable {
       return configDrive;
    }
 
+   /**
+    * @see CreateServerOptions#getNetworks()
+    */
+   public Set<Network> getNovaNetworks() {
+      return novaNetworks;
+   }
+
    public static class Builder {
 
       /**
@@ -396,6 +409,14 @@ public class NovaTemplateOptions extends TemplateOptions 
implements Cloneable {
          NovaTemplateOptions options = new NovaTemplateOptions();
          return 
NovaTemplateOptions.class.cast(options.configDrive(configDrive));
       }
+
+      /**
+       * @see 
org.jclouds.openstack.nova.v2_0.options.CreateServerOptions#getNetworks()
+       */
+      public static NovaTemplateOptions novaNetworks(Set<Network> 
novaNetworks) {
+         NovaTemplateOptions options = new NovaTemplateOptions();
+         return 
NovaTemplateOptions.class.cast(options.novaNetworks(novaNetworks));
+      }
    }
 
    // methods that only facilitate returning the correct object type
@@ -537,7 +558,8 @@ public class NovaTemplateOptions extends TemplateOptions 
implements Cloneable {
    }
 
    /**
-    * {@inheritDoc}
+    * <br>Ensures NovaTemplateOptions can work with networks specified as 
Strings.
+    * Also provides for compatibility with the abstraction layer.
     */
    @Override
    public NovaTemplateOptions networks(Iterable<String> networks) {
@@ -545,6 +567,15 @@ public class NovaTemplateOptions extends TemplateOptions 
implements Cloneable {
    }
 
    /**
+    * <br>Ensures NovaTemplateOptions can work with networks specified as 
Strings.
+    * Also provides for compatibility with the abstraction layer.
+    */
+   @Override
+   public NovaTemplateOptions networks(String... networks) {
+      return NovaTemplateOptions.class.cast(super.networks(networks));
+   }
+
+   /**
     * User data as bytes (not base64-encoded)
     */
    public NovaTemplateOptions userData(byte[] userData) {
@@ -575,4 +606,15 @@ public class NovaTemplateOptions extends TemplateOptions 
implements Cloneable {
       this.configDrive = configDrive;
       return this;
    }
+
+   /**
+    * @param novaNetworks The list of network declarations.
+    * Nova-specific network declarations allow for specifying network UUIDs, 
port UUIDs, and fixed IPs.
+    * Unline {@link #networks(Iterable)} this supports setting additional 
network parameters and not just network UUIDs.
+    * @see CreateServerOptions#getNetworks()
+    */
+   public NovaTemplateOptions novaNetworks(Set<Network> novaNetworks) {
+      this.novaNetworks = novaNetworks;
+      return this;
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/d99ad1b9/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Network.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Network.java
 
b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Network.java
new file mode 100644
index 0000000..516f6e1
--- /dev/null
+++ 
b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Network.java
@@ -0,0 +1,173 @@
+/*
+ * 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.openstack.nova.v2_0.domain;
+
+import java.beans.ConstructorProperties;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Nova (or Neutron) network definition
+ * Used to provide support for network, port, and fixed_ip when booting Nova 
servers.
+ * OpenStack will support either a Nova Network or Neutron, but not both at 
the same time.
+ * Specifying a port is only possible with Neutron.
+ * @author Zack Shoylev
+ */
+public class Network implements Comparable<Network> {
+   private final String networkUuid;
+   private final String portUuid;
+   private final String fixedIp;
+
+   @ConstructorProperties({
+      "networkUuid", "portUuid", "fixedIp"
+   })
+   protected Network(String networkUuid, String portUuid, String fixedIp) {
+      checkArgument(networkUuid != null || portUuid != null, "At least one of 
networkUuid or portUuid should be specified");
+      this.networkUuid = networkUuid;
+      this.portUuid = portUuid;
+      this.fixedIp = fixedIp;
+   }
+
+   /**
+    * @return the network uuid - Neutron or Nova
+    */
+   public String getNetworkUuid() {
+      return this.networkUuid;
+   }
+
+   /**
+    * @return the port uuid - Neutron only
+    */
+   public String getPortUuid() {
+      return this.portUuid;
+   }
+   
+   /**
+    * @return the fixed IP address - Neutron or Nova
+    */
+   public String getFixedIp() {
+      return this.fixedIp;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(networkUuid, portUuid, fixedIp);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Network that = Network.class.cast(obj);
+      return Objects.equal(this.networkUuid, that.networkUuid) && 
+            Objects.equal(this.portUuid, that.portUuid) &&
+            Objects.equal(this.fixedIp, that.fixedIp);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("networkUuid", networkUuid)
+            .add("portUuid", portUuid)
+            .add("fixedIp", fixedIp);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   /**
+    * @return A new builder object
+    */
+   public static Builder builder() { 
+      return new Builder();
+   }
+
+   /**
+    * @return A new Builder object from another Network
+    */
+   public Builder toBuilder() { 
+      return new Builder().fromNetwork(this);
+   }
+
+   /**
+    * Implements the Builder pattern for this class
+    */
+   public static class Builder {
+      protected String networkUuid;
+      protected String portUuid;
+      protected String fixedIp;
+
+      /** 
+       * @param networkUuid The UUID for the Nova network or Neutron subnet to 
be attached. 
+       * @return The builder object.
+       * @see Network#getNetworkUuid()
+       */
+      public Builder networkUuid(String networkUuid) {
+         this.networkUuid = networkUuid;
+         return this;
+      }
+
+      /** 
+       * @param portUuid The port UUID for this Neutron Network.
+       * @return The builder object.
+       * @see Network#getPortUuid()
+       */
+      public Builder portUuid(String portUuid) {
+         this.portUuid = portUuid;
+         return this;
+      }
+      
+      /** 
+       * @param fixedIp The fixed IP address for this Network (if any). 
+       * Service automatically assigns IP address if this is not provided.
+       * Fixed IP is compatible with both Nova Network and Neutron.
+       * @return The builder object.
+       * @see Network#getFixedIp()
+       */
+      public Builder fixedIp(String fixedIp) {
+         this.fixedIp = fixedIp;
+         return this;
+      }
+
+      /**
+       * @return A new Network object.
+       */
+      public Network build() {
+         return new Network(networkUuid, portUuid, fixedIp);
+      }
+
+      /**
+       * @param in The target Network
+       * @return A Builder from the provided Network
+       */
+      public Builder fromNetwork(Network in) {
+         return this
+               .networkUuid(in.getNetworkUuid())
+               .portUuid(in.getPortUuid())
+               .fixedIp(in.getFixedIp());
+      }        
+   }
+
+   @Override
+   public int compareTo(Network that) {
+      return this.toString().compareTo(that.toString());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/d99ad1b9/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
 
b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
index 57aadcc..1e3147e 100644
--- 
a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
+++ 
b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
@@ -16,24 +16,6 @@
  */
 package org.jclouds.openstack.nova.v2_0.options;
 
-import com.google.common.base.Objects;
-import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.collect.ForwardingObject;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.jclouds.http.HttpRequest;
-import org.jclouds.rest.MapBinder;
-import org.jclouds.rest.binders.BindToJsonPayload;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
 import static com.google.common.base.Objects.equal;
 import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkArgument;
@@ -42,9 +24,34 @@ import static 
com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Strings.emptyToNull;
 import static com.google.common.io.BaseEncoding.base64;
 
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.openstack.nova.v2_0.NovaApi;
+import org.jclouds.openstack.nova.v2_0.domain.Network;
+import org.jclouds.openstack.nova.v2_0.domain.Server;
+import org.jclouds.rest.MapBinder;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
 /**
  * @author Adrian Cole
  * @author Inbar Stolberg
+ * @author Zack Shoylev
  */
 public class CreateServerOptions implements MapBinder {
    @Inject
@@ -106,6 +113,7 @@ public class CreateServerOptions implements MapBinder {
    private byte[] userData;
    private String diskConfig;
    private Set<String> networks = ImmutableSet.of();
+   private Set<Network> novaNetworks = ImmutableSet.of();
    private String availabilityZone;
    private boolean configDrive;
 
@@ -216,8 +224,22 @@ public class CreateServerOptions implements MapBinder {
          server.diskConfig = diskConfig;
       }
 
-      if (!networks.isEmpty()) {
+      if (!networks.isEmpty() || !novaNetworks.isEmpty()) {
          server.networks = Sets.newLinkedHashSet(); // ensures ordering is 
preserved - helps testing and more intuitive for users.
+         for (Network network : novaNetworks) {
+            // Avoid serializing null values, which are common here.
+            ImmutableMap.Builder<String, String> networkMap = new 
ImmutableMap.Builder<String, String>();
+            if(network.getNetworkUuid() != null) {
+               networkMap.put("uuid", network.getNetworkUuid());
+            }
+            if(network.getPortUuid() != null) {
+               networkMap.put("port", network.getPortUuid());
+            }
+            if(network.getFixedIp() != null) {
+               networkMap.put("fixed_ip", network.getFixedIp());
+            }
+            server.networks.add(networkMap.build());
+         }
          for (String network : networks) {
             server.networks.add(ImmutableMap.of("uuid", network));
          }
@@ -364,6 +386,16 @@ public class CreateServerOptions implements MapBinder {
    public Set<String> getNetworks() {
       return networks;
    }
+   
+   /**
+    * Get custom networks specified for the server.
+    *
+    * @return A set of uuids defined by Neutron (previously Quantum)
+    * @see <a 
href="https://wiki.openstack.org/wiki/Neutron/APIv2-specification#Network";>Neutron
 Networks<a/>
+    */
+   public Set<Network> getNovaNetworks() {
+      return novaNetworks;
+   }
 
    /**
     * @see #getSecurityGroupNames
@@ -406,22 +438,37 @@ public class CreateServerOptions implements MapBinder {
    }
 
    /**
-    * @see #getNetworks
+    * Determines if a configuration drive will be attached to the server or 
not.
+    * This can be used for cloud-init or other configuration purposes.  
     */
-   public CreateServerOptions networks(String... networks) {
-      return networks(ImmutableSet.copyOf(networks));
+   public boolean getConfigDrive() {
+      return configDrive;
    }
 
    /**
     * @see #getNetworks
     */
    public CreateServerOptions networks(Iterable<String> networks) {
-      for (String network : checkNotNull(networks, "networks"))
-         checkNotNull(emptyToNull(network), "all networks must be non-empty");
       this.networks = ImmutableSet.copyOf(networks);
       return this;
    }
 
+   /**
+    * @see #getNetworks
+    * Overwrites networks supplied by {@link #networks(Iterable)}
+    */
+   public CreateServerOptions novaNetworks(Iterable<Network> networks) {
+      this.novaNetworks = ImmutableSet.copyOf(networks);
+      return this;
+   }
+
+   /**
+    * @see #getNetworks
+    */
+   public CreateServerOptions networks(String... networks) {
+      return networks(ImmutableSet.copyOf(networks));
+   }
+
    public static class Builder {
 
       /**
@@ -494,6 +541,14 @@ public class CreateServerOptions implements MapBinder {
       }
 
       /**
+       * @see CreateServerOptions#getNetworks
+       */
+      public static CreateServerOptions novaNetworks(Iterable<Network> 
networks) {
+         CreateServerOptions options = new CreateServerOptions();
+         return CreateServerOptions.class.cast(options.novaNetworks(networks));
+      }
+
+      /**
        * @see 
org.jclouds.openstack.nova.v2_0.options.CreateServerOptions#getAvailabilityZone()
        */
       public static CreateServerOptions availabilityZone(String 
availabilityZone) {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/d99ad1b9/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java
 
b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java
index 52190cb..a9dce03 100644
--- 
a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java
+++ 
b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java
@@ -32,6 +32,7 @@ import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
 import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
+import org.jclouds.openstack.nova.v2_0.domain.Network;
 import org.jclouds.openstack.nova.v2_0.domain.Server;
 import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ServerInZone;
 import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ZoneAndName;
@@ -40,6 +41,7 @@ import org.testng.annotations.Test;
 
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.TypeLiteral;
@@ -165,6 +167,53 @@ public class NovaComputeServiceAdapterExpectTest extends 
BaseNovaComputeServiceC
       assertNotNull(server);
    }
 
+   public void testCreateNodeWithGroupEncodedIntoNameWithNovaNetworks() throws 
Exception {
+
+      HttpRequest createServer = HttpRequest
+         .builder()
+         .method("POST")
+         
.endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers";)
+         .addHeader("Accept", "application/json")
+         .addHeader("X-Auth-Token", authToken)
+         .payload(payloadFromStringWithContentType(
+                  
"{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\",\"networks\":[{\"uuid\":\"12345\",
 \"port\":\"67890\", \"fixed_ip\":\"192.168.0.1\"},{\"uuid\":\"54321\", 
\"port\":\"09876\", 
\"fixed_ip\":\"192.168.0.2\"},{\"uuid\":\"non-nova-uuid\"}]}}","application/json"))
+         .build();
+
+      HttpResponse createServerResponse = 
HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
+         
.payload(payloadFromResourceWithContentType("/new_server_nova_networks.json","application/json;
 charset=UTF-8")).build();
+
+      Map<HttpRequest, HttpResponse> requestResponseMap = 
ImmutableMap.<HttpRequest, HttpResponse> builder()
+               .put(keystoneAuthWithUsernameAndPasswordAndTenantName, 
responseWithKeystoneAccess)
+               .put(extensionsOfNovaRequest, extensionsOfNovaResponse)
+               .put(listDetail, listDetailResponse)
+               .put(listFlavorsDetail, listFlavorsDetailResponse)
+               .put(createServer, createServerResponse)
+               .put(serverDetail, serverDetailResponse).build();
+
+      Injector forNovaNetworks = requestsSendResponses(requestResponseMap);
+
+      Template template = 
forNovaNetworks.getInstance(TemplateBuilder.class).build();
+      template.getOptions().as(NovaTemplateOptions.class)
+         .networks("non-nova-uuid")
+         .novaNetworks(
+               ImmutableSet.of(
+                     Network.builder()
+                        .networkUuid("12345")
+                        .portUuid("67890")
+                        .fixedIp("192.168.0.1")
+                        .build(),
+                     Network.builder()
+                        .networkUuid("54321")
+                        .portUuid("09876")
+                        .fixedIp("192.168.0.2")
+                        .build()));
+
+      NovaComputeServiceAdapter adapter = 
forNovaNetworks.getInstance(NovaComputeServiceAdapter.class);
+
+      NodeAndInitialCredentials<ServerInZone> server = 
adapter.createNodeWithGroupEncodedIntoName("test", "test-e92", template);
+      assertNotNull(server);
+   }
+
    public void 
testCreateNodeWithGroupEncodedIntoNameWhenSecurityGroupsArePresent() throws 
Exception {
 
       HttpRequest createServer = HttpRequest

http://git-wip-us.apache.org/repos/asf/jclouds/blob/d99ad1b9/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
 
b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
index 7f8ea9c..3553d18 100644
--- 
a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
+++ 
b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
@@ -21,6 +21,7 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
+import org.jclouds.openstack.nova.v2_0.domain.Network;
 import org.jclouds.openstack.nova.v2_0.domain.Server;
 import org.jclouds.openstack.nova.v2_0.domain.ServerCreated;
 import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiLiveTest;
@@ -31,6 +32,7 @@ import org.jclouds.openstack.v2_0.domain.Resource;
 import org.jclouds.openstack.v2_0.predicates.LinkPredicates;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 
 /**
@@ -42,133 +44,163 @@ import com.google.common.collect.Iterables;
 @Test(groups = "live", testName = "ServerApiLiveTest")
 public class ServerApiLiveTest extends BaseNovaApiLiveTest {
 
-    @Test(description = "GET /v${apiVersion}/{tenantId}/servers")
-    public void testListServers() throws Exception {
-       for (String zoneId : zones) {
-          ServerApi serverApi = api.getServerApiForZone(zoneId);
-          for (Resource server : serverApi.list().concat()) {
-             checkResource(server);
-          }
-       }
-    }
-
-    @Test(description = "GET /v${apiVersion}/{tenantId}/servers/detail")
-    public void testListServersInDetail() throws Exception {
-       for (String zoneId : zones) {
-          ServerApi serverApi = api.getServerApiForZone(zoneId);
-          for (Server server : serverApi.listInDetail().concat()) {
-             checkServer(server);
-          }
-       }
-    }
-
-    @Test(description = "GET /v${apiVersion}/{tenantId}/servers/{id}", 
dependsOnMethods = { "testListServersInDetail" })
-    public void testGetServerById() throws Exception {
-       for (String zoneId : zones) {
-          ServerApi serverApi = api.getServerApiForZone(zoneId);
-          for (Resource server : serverApi.list().concat()) {
-             Server details = serverApi.get(server.getId());
-             assertEquals(details.getId(), server.getId());
-             assertEquals(details.getName(), server.getName());
-             assertEquals(details.getLinks(), server.getLinks());
-             checkServer(details);
-          }
-       }
-    }
-
-    @Test
-    public void testCreateInAvailabilityZone() {
-        String serverId = null;
-        for (String zoneId : zones) {
-            ServerApi serverApi = api.getServerApiForZone(zoneId);
-            try {
-                serverId = createServer(zoneId, "nova", 
Server.Status.ACTIVE).getId();
-                Server server = serverApi.get(serverId);
-                assertEquals(server.getStatus(), Server.Status.ACTIVE);
-            } finally {
-                serverApi.delete(serverId);
+   @Test(description = "GET /v${apiVersion}/{tenantId}/servers")
+   public void testListServers() throws Exception {
+      for (String zoneId : zones) {
+         ServerApi serverApi = api.getServerApiForZone(zoneId);
+         for (Resource server : serverApi.list().concat()) {
+            checkResource(server);
+         }
+      }
+   }
+
+   @Test(description = "GET /v${apiVersion}/{tenantId}/servers/detail")
+   public void testListServersInDetail() throws Exception {
+      for (String zoneId : zones) {
+         ServerApi serverApi = api.getServerApiForZone(zoneId);
+         for (Server server : serverApi.listInDetail().concat()) {
+            checkServer(server);
+         }
+      }
+   }
+
+   @Test(description = "GET /v${apiVersion}/{tenantId}/servers/{id}", 
dependsOnMethods = { "testListServersInDetail" })
+   public void testGetServerById() throws Exception {
+      for (String zoneId : zones) {
+         ServerApi serverApi = api.getServerApiForZone(zoneId);
+         for (Resource server : serverApi.list().concat()) {
+            Server details = serverApi.get(server.getId());
+            assertEquals(details.getId(), server.getId());
+            assertEquals(details.getName(), server.getName());
+            assertEquals(details.getLinks(), server.getLinks());
+            checkServer(details);
+         }
+      }
+   }
+
+   @Test
+   public void testCreateInAvailabilityZone() {
+      String serverId = null;
+      for (String zoneId : zones) {
+         ServerApi serverApi = api.getServerApiForZone(zoneId);
+         try {
+            serverId = createServer(zoneId, "nova", 
Server.Status.ACTIVE).getId();
+            Server server = serverApi.get(serverId);
+            assertEquals(server.getStatus(), Server.Status.ACTIVE);
+         } finally {
+            serverApi.delete(serverId);
+         }
+      }
+   }
+
+   /**
+    * This needs to be supported by the provider, and is usually not supported.
+    * However this can be tested on devstack:
+    * In apis/openstack-nova:
+    * mvn -Plive clean install 
"-Dtest.openstack-nova.endpoint=http://localhost:5000/v2.0"; 
"-Dtest.openstack-nova.identity=demo:demo" 
"-Dtest.openstack-nova.credential=devstack" 
"-Dtest=org.jclouds.openstack.nova.v2_0.features.ServerApiLiveTest#testCreateWithNetworkOptions"
+    */
+   @Test(enabled = false)
+   public void testCreateWithNetworkOptions() {
+      String serverId = null;
+      for (String zoneId : zones) {
+         ServerApi serverApi = api.getServerApiForZone(zoneId);
+         try {
+            CreateServerOptions options = 
CreateServerOptions.Builder.novaNetworks(
+                  // This network UUID must match an existing network.
+                  
ImmutableSet.of(Network.builder().networkUuid("bc4cfa2b-2b27-4671-8e8f-73009623def0").fixedIp("192.168.55.56").build())
+                  );
+            ServerCreated server = serverApi.create(hostName, 
imageIdForZone(zoneId), "1", options);
+            serverId = server.getId();
+
+            blockUntilServerInState(server.getId(), serverApi, 
Server.Status.ACTIVE);
+            Server serverCheck = serverApi.get(serverId);
+            assertEquals(serverCheck.getStatus(), Server.Status.ACTIVE);
+         } finally {
+            if (serverId != null) {
+               serverApi.delete(serverId);
             }
-        }
-    }
-
-    @Test
-    public void testCreateInWrongAvailabilityZone() {
-        String serverId = null;
-        for (String zoneId : zones) {
-            ServerApi serverApi = api.getServerApiForZone(zoneId);
-            try {
-                serverId = createServer(zoneId, "err", 
Server.Status.ERROR).getId();
-                Server server = serverApi.get(serverId);
-                assertEquals(server.getStatus(), Server.Status.ERROR);
-            } finally {
-                serverApi.delete(serverId);
-            }
-        }
-    }
-
-    @Test
-    public void testRebuildServer() {
-
-        String serverId = null;
-
-        for (String zoneId : zones) {
-            ServerApi serverApi = api.getServerApiForZone(zoneId);
-            try {
-                serverId = createServer(zoneId, Server.Status.ACTIVE).getId();
-
-                Server server = serverApi.get(serverId);
-
-                assertEquals(server.getStatus(), Server.Status.ACTIVE);
-
-                RebuildServerOptions options = new RebuildServerOptions().
-                        withImage(server.getImage().getId()).
-                        name("newName").
-                        adminPass("password").
-                        ipv4Address("1.1.1.1").
-                        ipv6Address("fe80::100");
-
-                serverApi.rebuild(serverId, options);
-
-                Server rebuiltServer = serverApi.get(serverId);
-
-                assertEquals("newName", rebuiltServer.getName());
-                assertEquals("1.1.1.1", rebuiltServer.getAccessIPv4());
-                assertEquals("fe80::100", rebuiltServer.getAccessIPv6());
-
-            } finally {
-                serverApi.delete(serverId);
-            }
-        }
-    }
-
-    private Server createServer(String regionId, Server.Status serverStatus) {
-        ServerApi serverApi = api.getServerApiForZone(regionId);
-        CreateServerOptions options = new CreateServerOptions();
-        ServerCreated server = serverApi.create(hostName, 
imageIdForZone(regionId), flavorRefForZone(regionId), options);
-
-        blockUntilServerInState(server.getId(), serverApi, serverStatus);
-
-        return serverApi.get(server.getId());
-    }
-
-    private Server createServer(String regionId, String availabilityZoneId, 
Server.Status serverStatus) {
-        ServerApi serverApi = api.getServerApiForZone(regionId);
-        CreateServerOptions options = new CreateServerOptions();
-        options = options.availabilityZone(availabilityZoneId);
-        ServerCreated server = serverApi.create(hostName, 
imageIdForZone(regionId), flavorRefForZone(regionId), options);
-        blockUntilServerInState(server.getId(), serverApi, serverStatus);
-        return serverApi.get(server.getId());
-    }
-
-    private void checkResource(Resource resource) {
-       assertNotNull(resource.getId());
-       assertNotNull(resource.getName());
-       assertNotNull(resource.getLinks());
-       assertTrue(Iterables.any(resource.getLinks(), 
LinkPredicates.relationEquals(Relation.SELF)));
-    }
-
-    private void checkServer(Server server) {
-       checkResource(server);
-       assertFalse(server.getAddresses().isEmpty());
-    }
+         }
+      }
+   }
+
+   @Test
+   public void testCreateInWrongAvailabilityZone() {
+      String serverId = null;
+      for (String zoneId : zones) {
+         ServerApi serverApi = api.getServerApiForZone(zoneId);
+         try {
+            serverId = createServer(zoneId, "err", 
Server.Status.ERROR).getId();
+            Server server = serverApi.get(serverId);
+            assertEquals(server.getStatus(), Server.Status.ERROR);
+         } finally {
+            serverApi.delete(serverId);
+         }
+      }
+   }
+
+   @Test
+   public void testRebuildServer() {
+
+      String serverId = null;
+
+      for (String zoneId : zones) {
+         ServerApi serverApi = api.getServerApiForZone(zoneId);
+         try {
+            serverId = createServer(zoneId, Server.Status.ACTIVE).getId();
+
+            Server server = serverApi.get(serverId);
+
+            assertEquals(server.getStatus(), Server.Status.ACTIVE);
+
+            RebuildServerOptions options = new RebuildServerOptions().
+                  withImage(server.getImage().getId()).
+                  name("newName").
+                  adminPass("password").
+                  ipv4Address("1.1.1.1").
+                  ipv6Address("fe80::100");
+
+            serverApi.rebuild(serverId, options);
+
+            Server rebuiltServer = serverApi.get(serverId);
+
+            assertEquals("newName", rebuiltServer.getName());
+            assertEquals("1.1.1.1", rebuiltServer.getAccessIPv4());
+            assertEquals("fe80::100", rebuiltServer.getAccessIPv6());
+
+         } finally {
+            serverApi.delete(serverId);
+         }
+      }
+   }
+
+   private Server createServer(String regionId, Server.Status serverStatus) {
+      ServerApi serverApi = api.getServerApiForZone(regionId);
+      CreateServerOptions options = new CreateServerOptions();
+      ServerCreated server = serverApi.create(hostName, 
imageIdForZone(regionId), flavorRefForZone(regionId), options);
+
+      blockUntilServerInState(server.getId(), serverApi, serverStatus);
+
+      return serverApi.get(server.getId());
+   }
+
+   private Server createServer(String regionId, String availabilityZoneId, 
Server.Status serverStatus) {
+      ServerApi serverApi = api.getServerApiForZone(regionId);
+      CreateServerOptions options = new CreateServerOptions();
+      options = options.availabilityZone(availabilityZoneId);
+      ServerCreated server = serverApi.create(hostName, 
imageIdForZone(regionId), flavorRefForZone(regionId), options);
+      blockUntilServerInState(server.getId(), serverApi, serverStatus);
+      return serverApi.get(server.getId());
+   }
+
+   private void checkResource(Resource resource) {
+      assertNotNull(resource.getId());
+      assertNotNull(resource.getName());
+      assertNotNull(resource.getLinks());
+      assertTrue(Iterables.any(resource.getLinks(), 
LinkPredicates.relationEquals(Relation.SELF)));
+   }
+
+   private void checkServer(Server server) {
+      checkResource(server);
+      assertFalse(server.getAddresses().isEmpty());
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/d99ad1b9/apis/openstack-nova/src/test/resources/new_server_nova_networks.json
----------------------------------------------------------------------
diff --git 
a/apis/openstack-nova/src/test/resources/new_server_nova_networks.json 
b/apis/openstack-nova/src/test/resources/new_server_nova_networks.json
new file mode 100644
index 0000000..d0f4322
--- /dev/null
+++ b/apis/openstack-nova/src/test/resources/new_server_nova_networks.json
@@ -0,0 +1,41 @@
+{
+    "server": {
+        "status": "BUILD(scheduling)",
+        "updated": "2012-03-19T06:21:13Z",
+        "hostId": "",
+        "user_id": "54297837463082",
+        "name": "test-e92",
+        "links": [{
+            "href": 
"https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/37936628937291/servers/71752";,
+            "rel": "self"
+        }, {
+            "href": 
"https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/servers/71752";,
+            "rel": "bookmark"
+        }],
+        "addresses": {},
+        "tenant_id": "37936628937291",
+        "image": {
+            "id": "1241",
+            "links": [{
+                "href": 
"https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/images/1241";,
+                "rel": "bookmark"
+            }]
+        },
+        "created": "2012-03-19T06:21:13Z",
+        "uuid": "47491020-6a78-4f63-9475-23195ac4515c",
+        "accessIPv4": "",
+        "accessIPv6": "",
+        "key_name": null,
+        "adminPass": "ZWuHcmTMQ7eXoHeM",
+        "flavor": {
+            "id": "100",
+            "links": [{
+                "href": 
"https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/flavors/100";,
+                "rel": "bookmark"
+            }]
+        },
+        "id": 71752,
+        "metadata": {},
+        "OS-DCF:diskConfig": "AUTO"
+    }
+}

Reply via email to