Merge pull request #312 from chamerling/master

Adding keyname when creating server and some floating IPs related methods
This commit is contained in:
Adrian Cole 2012-01-13 09:30:23 -08:00
commit 2d4e385e1f
10 changed files with 481 additions and 3 deletions

View File

@ -317,5 +317,28 @@ public interface NovaAsyncClient {
@Path("/servers/{id}/ips/private") @Path("/servers/{id}/ips/private")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<? extends Set<String>> listPrivateAddresses(@PathParam("id") int serverId); ListenableFuture<? extends Set<String>> listPrivateAddresses(@PathParam("id") int serverId);
@POST
@Path("/servers/{id}/action")
@Consumes
@Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"addFloatingIp\":%7B\"address\":\"{address}\"%7D%7D")
ListenableFuture<Void> addFloatingIP(@PathParam("id") int serverId, @PayloadParam("address") String ip);
@GET
@Unwrap
@Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json")
@Path("/os-floating-ips")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<? extends Set<FloatingIP>> listFloatingIPs();
@GET
@Unwrap
@Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json")
@Path("/os-floating-ips/{id}")
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<FloatingIP> getFloatingIP(@PathParam("id") int id);
} }

View File

@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout; import org.jclouds.concurrent.Timeout;
import org.jclouds.openstack.nova.domain.Addresses; import org.jclouds.openstack.nova.domain.Addresses;
import org.jclouds.openstack.nova.domain.Flavor; import org.jclouds.openstack.nova.domain.Flavor;
import org.jclouds.openstack.nova.domain.FloatingIP;
import org.jclouds.openstack.nova.domain.Image; import org.jclouds.openstack.nova.domain.Image;
import org.jclouds.openstack.nova.domain.RebootType; import org.jclouds.openstack.nova.domain.RebootType;
import org.jclouds.openstack.nova.domain.Server; import org.jclouds.openstack.nova.domain.Server;
@ -281,4 +282,35 @@ public interface NovaClient {
*/ */
Set<String> listPrivateAddresses(int serverId); Set<String> listPrivateAddresses(int serverId);
/**
* Add a floating IP to the given server. The floating IP can just be added
* if the server has a fixed IP. It means that it is not possible to
* directly add the floating IP just after creating the server but have to
* poll if the server has an IP.
*
* @see <a href="http://wiki.openstack.org/os_api_floating_ip">http://wiki.openstack.org/os_api_floating_ip</a>
* @since 2011.3 "Diablo" release, OpenStack API 1.1
* @param serverId
* @param ip
*/
void addFloatingIP(int serverId, String ip);
/**
* Get all the defined floating IPs in nova
*
* @see <a href="http://wiki.openstack.org/os_api_floating_ip">http://wiki.openstack.org/os_api_floating_ip</a>
* @since 2011.3 "Diablo" release, OpenStack API 1.1
* @return all the available floating IP for the current tenant
*/
Set<FloatingIP> listFloatingIPs();
/**
* Get floating IP details from its ID
*
* @see <a href="http://wiki.openstack.org/os_api_floating_ip">http://wiki.openstack.org/os_api_floating_ip</a>
* @since 2011.3 "Diablo" release, OpenStack API 1.1
* @param id the floating IP id
* @return the floating IP or null if not found
*/
FloatingIP getFloatingIP(int id);
} }

View File

@ -0,0 +1,170 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.domain;
import com.google.gson.annotations.SerializedName;
/**
* Check <a href="http://wiki.openstack.org/os_api_floating_ip">Floating IP Wiki
* page</a>. Available since OpenStack Diablo release and API 1.1.
*
* @author chamerling
*
*/
public class FloatingIP extends Resource {
private int id;
private String ip;
@SerializedName(value="fixed_ip")
private String fixedIP;
@SerializedName(value = "instance_id")
private int instanceID;
@SuppressWarnings("unused")
private FloatingIP() {
}
public FloatingIP(int id, String ip, String fixedIP, int instanceID) {
this.id = id;
this.ip = ip;
this.fixedIP = fixedIP;
this.instanceID = instanceID;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* @return the ip
*/
public String getIp() {
return ip;
}
/**
* @param ip the ip to set
*/
public void setIp(String ip) {
this.ip = ip;
}
/**
* @return the fixedIP
*/
public String getFixedIP() {
return fixedIP;
}
/**
* @param fixedIP the fixedIP to set
*/
public void setFixedIP(String fixedIP) {
this.fixedIP = fixedIP;
}
/**
* @return the instanceID
*/
public int getInstanceID() {
return instanceID;
}
/**
* @param instanceID the instanceID to set
*/
public void setInstanceID(int instanceID) {
this.instanceID = instanceID;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("FloatingIP [id=");
builder.append(id);
builder.append(", ip=");
builder.append(ip);
builder.append(", fixedIP=");
builder.append(fixedIP);
builder.append(", instanceID=");
builder.append(instanceID);
builder.append("]");
return builder.toString();
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fixedIP == null) ? 0 : fixedIP.hashCode());
result = prime * result + id;
result = prime * result + instanceID;
result = prime * result + ((ip == null) ? 0 : ip.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
FloatingIP other = (FloatingIP) obj;
if (fixedIP == null) {
if (other.fixedIP != null)
return false;
} else if (!fixedIP.equals(other.fixedIP))
return false;
if (id != other.id)
return false;
if (instanceID != other.instanceID)
return false;
if (ip == null) {
if (other.ip != null)
return false;
} else if (!ip.equals(other.ip))
return false;
return true;
}
}

View File

@ -19,6 +19,7 @@
package org.jclouds.openstack.nova.domain; package org.jclouds.openstack.nova.domain;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.gson.annotations.SerializedName;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@ -45,6 +46,9 @@ public class Server extends Resource {
private Flavor flavor; private Flavor flavor;
private Image image; private Image image;
@SerializedName(value="key_name")
private String keyName;
private Date created; private Date created;
private Date updated; private Date updated;
@ -191,6 +195,14 @@ public class Server extends Resource {
public void setImage(Image image) { public void setImage(Image image) {
this.image = image; this.image = image;
} }
public String getKeyName() {
return keyName;
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
@Override @Override
public int hashCode() { public int hashCode() {
@ -204,6 +216,7 @@ public class Server extends Resource {
result = prime * result + ((imageRef == null) ? 0 : imageRef.hashCode()); result = prime * result + ((imageRef == null) ? 0 : imageRef.hashCode());
result = prime * result + ((metadata == null) ? 0 : metadata.hashCode()); result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode()); result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
result = prime * result + ((keyName == null) ? 0 : keyName.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((flavor == null) ? 0 : flavor.hashCode()); result = prime * result + ((flavor == null) ? 0 : flavor.hashCode());
result = prime * result + ((image == null) ? 0 : image.hashCode()); result = prime * result + ((image == null) ? 0 : image.hashCode());
@ -256,6 +269,11 @@ public class Server extends Resource {
return false; return false;
} else if (!uuid.equals(other.uuid)) } else if (!uuid.equals(other.uuid))
return false; return false;
if (keyName == null) {
if (other.keyName != null)
return false;
} else if (!keyName.equals(other.keyName))
return false;
if (name == null) { if (name == null) {
if (other.name != null) if (other.name != null)
return false; return false;
@ -282,7 +300,7 @@ public class Server extends Resource {
public String toString() { public String toString() {
return "Server [addresses=" + addresses + ", adminPass=" + adminPass + ", flavorRef=" return "Server [addresses=" + addresses + ", adminPass=" + adminPass + ", flavorRef="
+ flavorRef + ", hostId=" + hostId + ", id=" + id + ", imageRef=" + imageRef + flavorRef + ", hostId=" + hostId + ", id=" + id + ", imageRef=" + imageRef
+ ", metadata=" + metadata + ", uuid=" + uuid + ", name=" + name + "]"; + ", metadata=" + metadata + ", uuid=" + uuid + ", name=" + name + ", keyName=" + keyName + "]";
} }
} }

View File

@ -76,6 +76,7 @@ public class CreateServerOptions implements MapBinder {
final String flavorRef; final String flavorRef;
Map<String, String> metadata; Map<String, String> metadata;
List<File> personality; List<File> personality;
String key_name;
private ServerRequest(String name, String imageRef, String flavorRef) { private ServerRequest(String name, String imageRef, String flavorRef) {
this.name = name; this.name = name;
@ -87,6 +88,7 @@ public class CreateServerOptions implements MapBinder {
private Map<String, String> metadata = Maps.newHashMap(); private Map<String, String> metadata = Maps.newHashMap();
private List<File> files = Lists.newArrayList(); private List<File> files = Lists.newArrayList();
private String keyName;
@Override @Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) { public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
@ -97,6 +99,8 @@ public class CreateServerOptions implements MapBinder {
server.metadata = metadata; server.metadata = metadata;
if (files.size() > 0) if (files.size() > 0)
server.personality = files; server.personality = files;
if (keyName != null)
server.key_name = keyName;
return bindToRequest(request, ImmutableMap.of("server", server)); return bindToRequest(request, ImmutableMap.of("server", server));
} }
@ -144,6 +148,19 @@ public class CreateServerOptions implements MapBinder {
return this; return this;
} }
/**
* A keypair name can be defined when creating a server. This key will be
* linked to the server and used to SSH connect to the machine
*
* @param keyName
* @return
*/
public CreateServerOptions withKeyName(String keyName) {
checkNotNull(keyName, "keyName");
this.keyName = keyName;
return this;
}
public static class Builder { public static class Builder {
/** /**
@ -161,6 +178,14 @@ public class CreateServerOptions implements MapBinder {
CreateServerOptions options = new CreateServerOptions(); CreateServerOptions options = new CreateServerOptions();
return options.withMetadata(metadata); return options.withMetadata(metadata);
} }
/**
* @see CreateServerOptions#withKeyName(String)
*/
public static CreateServerOptions withKeyName(String keyName) {
CreateServerOptions options = new CreateServerOptions();
return options.withKeyName(keyName);
}
} }
@Override @Override

View File

@ -58,6 +58,7 @@ import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.wit
import static org.jclouds.openstack.nova.options.ListOptions.Builder.changesSince; import static org.jclouds.openstack.nova.options.ListOptions.Builder.changesSince;
import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails; import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails;
import static org.jclouds.openstack.nova.options.RebuildServerOptions.Builder.withImage; import static org.jclouds.openstack.nova.options.RebuildServerOptions.Builder.withImage;
//import static org.junit.Assert.*;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
/** /**
@ -132,6 +133,26 @@ public class NovaAsyncClientTest extends RestClientTest<NovaAsyncClient> {
checkFilters(request); checkFilters(request);
}
@Test
public void testCreateServerWithKeyName() throws Exception {
Method method = NovaAsyncClient.class.getMethod("createServer", String.class, String.class, String.class,
createServerOptionsVarargsClass);
HttpRequest request = processor.createRequest(method, "ralphie", 2, 1,
withMetadata(ImmutableMap.of("foo", "bar")).withKeyName("mykey"));
assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers?format=json HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
assertPayloadEquals(request,
"{\"server\":{\"name\":\"ralphie\",\"imageRef\":\"2\",\"flavorRef\":\"1\",\"metadata\":{\"foo\":\"bar\"},\"key_name\":\"mykey\"}}",
"application/json", false);
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(request);
} }
public void testDeleteImage() throws IOException, SecurityException, NoSuchMethodException { public void testDeleteImage() throws IOException, SecurityException, NoSuchMethodException {
@ -635,8 +656,44 @@ public class NovaAsyncClientTest extends RestClientTest<NovaAsyncClient> {
assertExceptionParserClassEquals(method, null); assertExceptionParserClassEquals(method, null);
checkFilters(request); checkFilters(request);
} }
public void testGetFloatingIP() throws SecurityException,
NoSuchMethodException {
Method method = NovaAsyncClient.class.getMethod("getFloatingIP",
int.class);
HttpRequest request = processor.createRequest(method, 2);
assertRequestLineEquals(request,
"GET http://endpoint/vapi-version/os-floating-ips/2?format=json HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
assertPayloadEquals(request, null, null, false);
assertResponseParserClassEquals(method, request,
UnwrapOnlyJsonValue.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method,
ReturnNullOnNotFoundOr404.class);
checkFilters(request);
}
public void testListFloatingIPs() throws SecurityException, NoSuchMethodException {
Method method = NovaAsyncClient.class.getMethod("listFloatingIPs");
HttpRequest request = processor.createRequest(method);
assertRequestLineEquals(request,
"GET http://endpoint/vapi-version/os-floating-ips?format=json HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
assertPayloadEquals(request, null, null, false);
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
checkFilters(request);
}
@Override @Override
protected TypeLiteral<RestAnnotationProcessor<NovaAsyncClient>> createTypeLiteral() { protected TypeLiteral<RestAnnotationProcessor<NovaAsyncClient>> createTypeLiteral() {
return new TypeLiteral<RestAnnotationProcessor<NovaAsyncClient>>() { return new TypeLiteral<RestAnnotationProcessor<NovaAsyncClient>>() {

View File

@ -0,0 +1,72 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.functions;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.io.IOException;
import java.io.InputStream;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule;
import org.jclouds.openstack.nova.domain.FloatingIP;
import org.testng.annotations.Test;
import com.google.gson.Gson;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
/**
* @author chamerling
*
*/
@Test(groups = "unit")
public class ParseFloatingIPFromJsonResponse {
@Test
public void testParseFloatingIPFromJsonResponseTest() throws IOException {
FloatingIP response = parseFLoatingIP();
String json = new Gson().toJson(response);
assertNotNull(json);
assertEquals(response.getId(), 1);
assertEquals(response.getFixedIP(), "10.0.0.2");
assertEquals(response.getInstanceID(), 123);
assertEquals(response.getIp(), "10.0.0.3");
}
public static FloatingIP parseFLoatingIP() {
Injector i = Guice.createInjector(new GsonModule());
InputStream is = ParseFloatingIPFromJsonResponse.class
.getResourceAsStream("/test_get_floatingip.json");
UnwrapOnlyJsonValue<FloatingIP> parser = i.getInstance(Key
.get(new TypeLiteral<UnwrapOnlyJsonValue<FloatingIP>>() {
}));
return parser.apply(new HttpResponse(200, "ok", Payloads
.newInputStreamPayload(is)));
}
}

View File

@ -0,0 +1,62 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.functions;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import java.net.UnknownHostException;
import java.util.List;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule;
import org.jclouds.openstack.nova.domain.FloatingIP;
import org.testng.annotations.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
/**
* @author chamerling
*
*/
@Test(groups = "unit")
public class ParseFloatingIPListFromJsonResponse {
Injector i = Guice.createInjector(new GsonModule());
@Test
public void testParseFloatingIPListFromJsonResponseTest() throws UnknownHostException {
InputStream is = getClass().getResourceAsStream(
"/test_list_floatingips.json");
UnwrapOnlyJsonValue<List<FloatingIP>> parser = i.getInstance(Key
.get(new TypeLiteral<UnwrapOnlyJsonValue<List<FloatingIP>>>() {
}));
List<FloatingIP> response = parser.apply(new HttpResponse(200, "ok",
Payloads.newInputStreamPayload(is)));
assertEquals(response.size(), 1);
FloatingIP floatingIP = new FloatingIP(1, "10.0.0.3", "11.0.0.1", 12);
assertEquals(response.get(0), floatingIP);
}
}

View File

@ -0,0 +1,9 @@
{
"floating_ip" :
{
"id" : 1,
"ip" : "10.0.0.3",
"fixed_ip" : "10.0.0.2",
"instance_id" : 123
}
}

View File

@ -0,0 +1,10 @@
{
"floating_ips" : [
{
"id" : 1,
"ip" : "10.0.0.3",
"fixed_ip": "11.0.0.1",
"instance_id": 12
}
]
}