diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersConnection.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersConnection.java index 36b77121b0..f6a6f6846b 100755 --- a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersConnection.java +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersConnection.java @@ -36,16 +36,20 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import org.jclouds.http.functions.ReturnFalseOn404; +import org.jclouds.rackspace.cloudservers.binders.BackupScheduleBinder; import org.jclouds.rackspace.cloudservers.binders.ChangeAdminPassBinder; import org.jclouds.rackspace.cloudservers.binders.ChangeServerNameBinder; +import org.jclouds.rackspace.cloudservers.binders.CreateImageBinder; import org.jclouds.rackspace.cloudservers.binders.ShareIpBinder; import org.jclouds.rackspace.cloudservers.domain.Addresses; +import org.jclouds.rackspace.cloudservers.domain.BackupSchedule; import org.jclouds.rackspace.cloudservers.domain.Flavor; import org.jclouds.rackspace.cloudservers.domain.Image; import org.jclouds.rackspace.cloudservers.domain.Server; import org.jclouds.rackspace.cloudservers.domain.SharedIpGroup; import org.jclouds.rackspace.cloudservers.functions.IpAddress; import org.jclouds.rackspace.cloudservers.functions.ParseAddressesFromGsonResponse; +import org.jclouds.rackspace.cloudservers.functions.ParseBackupScheduleFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseFlavorFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseFlavorListFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseImageFromGsonResponse; @@ -220,7 +224,8 @@ public interface CloudServersConnection { @DELETE @ExceptionParser(ReturnFalseOn404.class) @Path("/servers/{id}/ips/public/{address}") - // TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized(401), badRequest (400), + // TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized(401), badRequest + // (400), // badMediaType(415), overLimit (413) boolean unshareIp( @PathParam("address") @ParamParser(IpAddress.class) InetAddress addressToShare, @@ -319,6 +324,37 @@ public interface CloudServersConnection { // (400) Image getImage(@PathParam("id") int id); + /** + * + * This operation creates a new image for the given server ID. Once complete, a new image will be + * available that can be used to rebuild or create servers. Specifying the same image name as an + * existing custom image replaces the image. The image creation status can be queried by + * performing a GET on /images/id and examining the status and progress attributes. + * + * Status Transition: + *

+ * QUEUED - PREPARING - SAVING - ACTIVE + *

+ * QUEUED - PREPARING - SAVING - FAILED (on error) + *

+ * Note: At present, image creation is an asynchronous operation, so coordinating the creation + * with data quiescence, etc. is currently not possible. + * + * @return {@link Image#NOT_FOUND} if the server is not found + * @see Image + */ + @POST + @ResponseParser(ParseImageFromGsonResponse.class) + @Query(key = "format", value = "json") + @ExceptionParser(ReturnImageNotFoundOn404.class) + @MapBinder(CreateImageBinder.class) + @Path("/images") + // TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest + // (400), badMediaType(415), buildInProgress (409), serverCapacityUnavailable (503), overLimit + // (413), resizeNotAllowed (403), backupOrResizeInProgress (409) + Image createImageFromServer(@MapEntityParam("imageName") String imageName, + @MapEntityParam("serverId") int serverId); + /** * * List shared IP groups (IDs and names only) @@ -381,6 +417,45 @@ public interface CloudServersConnection { // (400) boolean deleteSharedIpGroup(@PathParam("id") int id); + /** + * List the backup schedule for the specified server + */ + @GET + @ResponseParser(ParseBackupScheduleFromGsonResponse.class) + @Query(key = "format", value = "json") + @Path("/servers/{id}/backup_schedule") + // TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest + // (400), itemNotFound (404) + BackupSchedule listBackupSchedule(@PathParam("id") int serverId); + + /** + * Delete backup schedule for the specified server. + *

+ * Web Hosting #119571 currently disables the schedule, not deletes it. + * + * @return false if the server is not found + */ + @DELETE + @ExceptionParser(ReturnFalseOn404.class) + @Path("/servers/{id}/backup_schedule") + // TODO:cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest + // (400), buildInProgress (409), serverCapacityUnavailable (503), backupOrResizeInProgress(409) + boolean deleteBackupSchedule(@PathParam("id") int serverId); + + /** + * Enable/update the backup schedule for the specified server + * + * @return false if the server is not found + */ + @POST + @ExceptionParser(ReturnFalseOn404.class) + @Path("/servers/{id}/backup_schedule") + // TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest + // (400), badMediaType(415), buildInProgress (409), serverCapacityUnavailable (503), + // backupOrResizeInProgress(409), resizeNotAllowed (403). overLimit (413) + boolean replaceBackupSchedule(@PathParam("id") int id, + @EntityParam(BackupScheduleBinder.class) BackupSchedule backupSchedule); + /** * List all server addresses */ diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/binders/BackupScheduleBinder.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/binders/BackupScheduleBinder.java new file mode 100644 index 0000000000..09db59e0bf --- /dev/null +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/binders/BackupScheduleBinder.java @@ -0,0 +1,55 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.rackspace.cloudservers.binders; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.Map; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.binders.JsonBinder; +import org.jclouds.rackspace.cloudservers.domain.BackupSchedule; + +import com.google.common.collect.ImmutableMap; + +/** + * + * @author Adrian Cole + * + */ +public class BackupScheduleBinder extends JsonBinder { + + @Override + public void addEntityToRequest(Map postParams, HttpRequest request) { + throw new IllegalStateException( + "Replace Backup Schedule needs an BackupSchedule object, not a Map"); + } + + @Override + public void addEntityToRequest(Object toBind, HttpRequest request) { + checkArgument(toBind instanceof BackupSchedule, + "this binder is only valid for BackupSchedules!"); + super.addEntityToRequest(ImmutableMap.of("backupSchedule", toBind), request); + } +} diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/binders/CreateImageBinder.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/binders/CreateImageBinder.java new file mode 100644 index 0000000000..f6cb91deb3 --- /dev/null +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/binders/CreateImageBinder.java @@ -0,0 +1,66 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.rackspace.cloudservers.binders; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.binders.JsonBinder; + +import com.google.common.collect.ImmutableMap; + +/** + * + * @author Adrian Cole + * + */ +public class CreateImageBinder extends JsonBinder { + + @SuppressWarnings("unused") + private class CreateImageRequest { + final int serverId; + final String name; + + private CreateImageRequest(int serverId, String name) { + this.serverId = serverId; + this.name = name; + } + + } + + @Override + public void addEntityToRequest(Map postParams, HttpRequest request) { + CreateImageRequest createRequest = new CreateImageRequest(Integer + .parseInt(checkNotNull(postParams.get("serverId"))), checkNotNull(postParams + .get("imageName"))); + super.addEntityToRequest(ImmutableMap.of("image", createRequest), request); + } + + @Override + public void addEntityToRequest(Object toBind, HttpRequest request) { + throw new IllegalArgumentException("image is needs parameters"); + } +} diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/BackupSchedule.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/BackupSchedule.java index 1f8e1ea6ef..bed6f9e348 100644 --- a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/BackupSchedule.java +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/BackupSchedule.java @@ -30,10 +30,18 @@ package org.jclouds.rackspace.cloudservers.domain; * @author Adrian Cole */ public class BackupSchedule { - - protected DailyBackup daily; + protected DailyBackup daily = DailyBackup.DISABLED; protected boolean enabled; - protected String weekly; + protected WeeklyBackup weekly = WeeklyBackup.DISABLED; + + public BackupSchedule() { + } + + public BackupSchedule(WeeklyBackup weekly, DailyBackup daily, boolean enabled) { + this.weekly = weekly; + this.daily = daily; + this.enabled = enabled; + } public DailyBackup getDaily() { return daily; @@ -51,12 +59,51 @@ public class BackupSchedule { this.enabled = value; } - public String getWeekly() { + public WeeklyBackup getWeekly() { return weekly; } - public void setWeekly(String value) { + public void setWeekly(WeeklyBackup value) { this.weekly = value; } + @Override + public String toString() { + return "BackupSchedule [daily=" + daily + ", enabled=" + enabled + ", weekly=" + weekly + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((daily == null) ? 0 : daily.hashCode()); + result = prime * result + (enabled ? 1231 : 1237); + result = prime * result + ((weekly == null) ? 0 : weekly.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BackupSchedule other = (BackupSchedule) obj; + if (daily == null) { + if (other.daily != null) + return false; + } else if (!daily.equals(other.daily)) + return false; + if (enabled != other.enabled) + return false; + if (weekly == null) { + if (other.weekly != null) + return false; + } else if (!weekly.equals(other.weekly)) + return false; + return true; + } + } diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/RateLimit.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/RateLimit.java index 681c5f8541..58c725399f 100644 --- a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/RateLimit.java +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/RateLimit.java @@ -25,6 +25,23 @@ package org.jclouds.rackspace.cloudservers.domain; import org.jclouds.http.HttpMethod; +/** + * + * RateLimit. + *

+ * we specify rate limits in terms of both a human readable wild-card URI and a machine processable + * regular expression. The regular expression boundary matcher '^' takes affect after the root URI + * path. For example, the regular expression ^/servers would match the bolded portion of the + * following URI: https://servers.api.rackspacecloud.com/v1.0/3542812 /servers . + *

+ * Rate limits are applied in order relative to the verb, going from least to most specific. For + * example, although the threshold for POST to /servers is 25 per day, one cannot POST to /servers + * more than 10 times within a single minute because the rate limits for any POST is 10/min. In the + * event you exceed the thresholds established for your account, a 413 Rate Control HTTP response + * will be returned with a Reply-After header to notify the client when theyagain. + * + * @author Adrian Cole + */ public class RateLimit { private final String uri; diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/WeeklyBackup.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/WeeklyBackup.java new file mode 100644 index 0000000000..b6246af6c2 --- /dev/null +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/WeeklyBackup.java @@ -0,0 +1,39 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.rackspace.cloudservers.domain; + +public enum WeeklyBackup { + + DISABLED, SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; + + public String value() { + return name(); + } + + public static WeeklyBackup fromValue(String v) { + return valueOf(v); + } + +} diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/functions/ParseBackupScheduleFromGsonResponse.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/functions/ParseBackupScheduleFromGsonResponse.java new file mode 100644 index 0000000000..2ec0bab893 --- /dev/null +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/functions/ParseBackupScheduleFromGsonResponse.java @@ -0,0 +1,60 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.rackspace.cloudservers.functions; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; + +import org.jclouds.http.functions.ParseJson; +import org.jclouds.rackspace.cloudservers.domain.BackupSchedule; + +import com.google.gson.Gson; +import com.google.inject.Inject; + +/** + * This parses {@link BackupSchedule} from a gson string. + * + * @author Adrian Cole + */ +public class ParseBackupScheduleFromGsonResponse extends ParseJson { + + @Inject + public ParseBackupScheduleFromGsonResponse(Gson gson) { + super(gson); + } + + private static class BackupScheduleResponse { + BackupSchedule backupSchedule; + } + + public BackupSchedule apply(InputStream stream) { + + try { + return gson.fromJson(new InputStreamReader(stream, "UTF-8"), BackupScheduleResponse.class).backupSchedule; + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("jclouds requires UTF-8 encoding", e); + } + } +} \ No newline at end of file diff --git a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionLiveTest.java b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionLiveTest.java index 6712e4e3df..452f54648e 100755 --- a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionLiveTest.java +++ b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionLiveTest.java @@ -42,11 +42,15 @@ import java.util.Map; import org.jclouds.http.HttpResponseException; import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.rackspace.cloudservers.domain.BackupSchedule; +import org.jclouds.rackspace.cloudservers.domain.DailyBackup; import org.jclouds.rackspace.cloudservers.domain.Flavor; import org.jclouds.rackspace.cloudservers.domain.Image; +import org.jclouds.rackspace.cloudservers.domain.ImageStatus; import org.jclouds.rackspace.cloudservers.domain.Server; import org.jclouds.rackspace.cloudservers.domain.ServerStatus; import org.jclouds.rackspace.cloudservers.domain.SharedIpGroup; +import org.jclouds.rackspace.cloudservers.domain.WeeklyBackup; import org.jclouds.ssh.ExecResponse; import org.jclouds.ssh.SshConnection; import org.jclouds.ssh.SshException; @@ -303,10 +307,10 @@ public class CloudServersConnectionLiveTest { adminPass = server.getAdminPass(); ip = server.getAddresses().getPublicAddresses().iterator().next(); assertEquals(server.getStatus(), ServerStatus.BUILD); - blockUntilActive(serverId); + blockUntilServerActive(serverId); } - private void blockUntilActive(int serverId) throws InterruptedException { + private void blockUntilServerActive(int serverId) throws InterruptedException { Server currentDetails = null; for (currentDetails = connection.getServer(serverId); currentDetails.getStatus() != ServerStatus.ACTIVE; currentDetails = connection .getServer(serverId)) { @@ -320,6 +324,15 @@ public class CloudServersConnectionLiveTest { Thread.sleep(10 * 1000); } + private void blockUntilImageActive(int imageId) throws InterruptedException { + Image currentDetails = null; + for (currentDetails = connection.getImage(imageId); currentDetails.getStatus() != ImageStatus.ACTIVE; currentDetails = connection + .getImage(imageId)) { + System.out.printf("blocking on status active%n%s%n", currentDetails); + Thread.sleep(5 * 1000); + } + } + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateServer") public void testServerDetails() throws Exception { Server server = connection.getServer(serverId); @@ -342,8 +355,7 @@ public class CloudServersConnectionLiveTest { // check metadata assertEquals(server.getMetadata(), metadata); - // [Web Hosting #119335] ssh timeouts after server changes status to ACTIVE - // checkPassOk(server, adminPass); + checkPassOk(server, adminPass); } /** @@ -392,14 +404,14 @@ public class CloudServersConnectionLiveTest { Server server = connection.getServer(serverId); String oldName = server.getName(); assertTrue(connection.renameServer(serverId, oldName + "new")); - blockUntilActive(serverId); + blockUntilServerActive(serverId); assertEquals(oldName + "new", connection.getServer(serverId).getName()); } @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateServer") public void testChangePassword() throws Exception { assertTrue(connection.changeAdminPass(serverId, "elmo")); - blockUntilActive(serverId); + blockUntilServerActive(serverId); checkPassOk(connection.getServer(serverId), "elmo"); this.adminPass = "elmo"; } @@ -425,7 +437,7 @@ public class CloudServersConnectionLiveTest { assertNotNull(server.getAdminPass()); serverId2 = server.getId(); adminPass2 = server.getAdminPass(); - blockUntilActive(serverId2); + blockUntilServerActive(serverId2); assertIpConfigured(server, adminPass2); assert server.getAddresses().getPublicAddresses().contains(ip) : server.getAddresses() + " doesn't contain " + ip; @@ -447,7 +459,7 @@ public class CloudServersConnectionLiveTest { @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateServerIp") public void testUnshare() throws Exception { connection.unshareIp(ip, serverId2); - blockUntilActive(serverId2); + blockUntilServerActive(serverId2); Server server = connection.getServer(serverId2); assert !server.getAddresses().getPublicAddresses().contains(ip) : server.getAddresses(); assertIpNotConfigured(server, adminPass2); @@ -468,7 +480,7 @@ public class CloudServersConnectionLiveTest { @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = "testUnshare") public void testShareConfig() throws Exception { connection.shareIp(ip, serverId2, sharedIpGroupId, true); - blockUntilActive(serverId2); + blockUntilServerActive(serverId2); Server server = connection.getServer(serverId2); assert server.getAddresses().getPublicAddresses().contains(ip) : server.getAddresses(); assertIpConfigured(server, adminPass2); @@ -478,15 +490,37 @@ public class CloudServersConnectionLiveTest { @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = "testShareConfig") public void testShareNoConfig() throws Exception { connection.shareIp(ip, serverId2, sharedIpGroupId, false); - blockUntilActive(serverId2); + blockUntilServerActive(serverId2); Server server = connection.getServer(serverId2); assert server.getAddresses().getPublicAddresses().contains(ip) : server.getAddresses(); assertIpNotConfigured(server, adminPass2); testUnshare(); } - // must be last!. do not rely on positional order. @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = "testShareNoConfig") + public void testBackup() throws Exception { + assertEquals(new BackupSchedule(), connection.listBackupSchedule(serverId)); + BackupSchedule dailyWeekly = new BackupSchedule(); + dailyWeekly.setEnabled(true); + dailyWeekly.setWeekly(WeeklyBackup.FRIDAY); + dailyWeekly.setDaily(DailyBackup.H_0400_0600); + assertEquals(true, connection.replaceBackupSchedule(serverId, dailyWeekly)); + connection.deleteBackupSchedule(serverId); + // disables, doesn't delete: Web Hosting #119571 + assertEquals(connection.listBackupSchedule(serverId).isEnabled(), false); + } + + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = "testBackup") + public void testCreateImage() throws Exception { + Image image = connection.createImageFromServer("hoofie", serverId); + assertEquals("hoofie", image.getName()); + assertEquals(new Integer(serverId), image.getServerId()); + int imageId = image.getId(); + blockUntilImageActive(imageId); + } + + // must be last!. do not rely on positional order. + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateImage") void deleteServers() { if (serverId > 0) { connection.deleteServer(serverId); diff --git a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionTest.java b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionTest.java index 0011c18ae5..d6b43212a4 100755 --- a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionTest.java +++ b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionTest.java @@ -49,7 +49,11 @@ import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.functions.ReturnFalseOn404; import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.rackspace.Authentication; +import org.jclouds.rackspace.cloudservers.domain.BackupSchedule; +import org.jclouds.rackspace.cloudservers.domain.DailyBackup; +import org.jclouds.rackspace.cloudservers.domain.WeeklyBackup; import org.jclouds.rackspace.cloudservers.functions.ParseAddressesFromGsonResponse; +import org.jclouds.rackspace.cloudservers.functions.ParseBackupScheduleFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseFlavorFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseFlavorListFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseImageFromGsonResponse; @@ -467,6 +471,41 @@ public class CloudServersConnectionTest { assertEquals(processor.createResponseParser(method).getClass(), ReturnTrueIf2xx.class); } + public void testReplaceBackupSchedule() throws SecurityException, NoSuchMethodException { + Method method = CloudServersConnection.class.getMethod("replaceBackupSchedule", int.class, + BackupSchedule.class); + URI endpoint = URI.create("http://localhost"); + HttpRequest httpMethod = processor.createRequest(endpoint, method, new Object[] { 2, + new BackupSchedule(WeeklyBackup.MONDAY, DailyBackup.H_0800_1000, true) }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/servers/2/backup_schedule"); + assertEquals(httpMethod.getMethod(), HttpMethod.POST); + assertEquals(httpMethod.getHeaders().size(), 2); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections + .singletonList(httpMethod.getEntity().toString().getBytes().length + "")); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections + .singletonList(MediaType.APPLICATION_JSON)); + assertEquals( + "{\"backupSchedule\":{\"daily\":\"H_0800_1000\",\"enabled\":true,\"weekly\":\"MONDAY\"}}", + httpMethod.getEntity()); + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnFalseOn404.class); + assertEquals(processor.createResponseParser(method).getClass(), ReturnTrueIf2xx.class); + } + + public void testDeleteBackupSchedule() throws SecurityException, NoSuchMethodException { + Method method = CloudServersConnection.class.getMethod("deleteBackupSchedule", int.class); + URI endpoint = URI.create("http://localhost"); + HttpRequest httpMethod = processor.createRequest(endpoint, method, new Object[] { 2 }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/servers/2/backup_schedule"); + assertEquals(httpMethod.getMethod(), HttpMethod.DELETE); + assertEquals(httpMethod.getHeaders().size(), 0); + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnFalseOn404.class); + assertEquals(processor.createResponseParser(method).getClass(), ReturnTrueIf2xx.class); + } + public void testChangeAdminPass() throws SecurityException, NoSuchMethodException { Method method = CloudServersConnection.class.getMethod("changeAdminPass", int.class, String.class); @@ -679,6 +718,41 @@ public class CloudServersConnectionTest { ParseInetAddressListFromGsonResponse.class); } + public void testListBackupSchedule() throws SecurityException, NoSuchMethodException { + Method method = CloudServersConnection.class.getMethod("listBackupSchedule", int.class); + URI endpoint = URI.create("http://localhost"); + HttpRequest httpMethod = processor.createRequest(endpoint, method, new Object[] { 2 }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/servers/2/backup_schedule"); + assertEquals(httpMethod.getEndpoint().getQuery(), "format=json"); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 0); + assertEquals(processor.createResponseParser(method).getClass(), + ParseBackupScheduleFromGsonResponse.class); + } + + public void testCreateImageWithIpGroup() throws SecurityException, NoSuchMethodException { + Method method = CloudServersConnection.class.getMethod("createImageFromServer", String.class, + int.class); + URI endpoint = URI.create("http://localhost"); + HttpRequest httpMethod = processor.createRequest(endpoint, method, new Object[] { "ralphie", + 2 }); + assertEquals("{\"image\":{\"serverId\":2,\"name\":\"ralphie\"}}", httpMethod.getEntity()); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/images"); + assertEquals(httpMethod.getEndpoint().getQuery(), "format=json"); + assertEquals(httpMethod.getMethod(), HttpMethod.POST); + assertEquals(httpMethod.getHeaders().size(), 2); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections + .singletonList(httpMethod.getEntity().toString().getBytes().length + "")); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections + .singletonList(MediaType.APPLICATION_JSON)); + assertEquals(processor.createResponseParser(method).getClass(), + ParseImageFromGsonResponse.class); + assertNotNull(processor.createExceptionParserOrNullIfNotFound(method)); + assertNotNull(processor.getMapEntityBinderOrNull(method, new Object[] { "", 2 })); + } + JaxrsAnnotationProcessor processor; @BeforeClass diff --git a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/binders/CreateImageBinderTest.java b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/binders/CreateImageBinderTest.java new file mode 100644 index 0000000000..a8694bf71e --- /dev/null +++ b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/binders/CreateImageBinderTest.java @@ -0,0 +1,75 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.rackspace.cloudservers.binders; + +import static org.testng.Assert.assertEquals; + +import java.io.File; +import java.net.URI; + +import org.jclouds.http.HttpMethod; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.functions.config.ParserModule; +import org.jclouds.rackspace.cloudservers.binders.CreateImageBinder; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * Tests behavior of {@code CreateImageBinder} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "cloudservers.CreateImageBinderTest") +public class CreateImageBinderTest { + + Injector injector = Guice.createInjector(new ParserModule()); + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testMustBeMap() { + CreateImageBinder binder = new CreateImageBinder(); + injector.injectMembers(binder); + HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("/")); + binder.addEntityToRequest(new File("foo"), request); + } + + @Test + public void testCorrect() { + CreateImageBinder binder = new CreateImageBinder(); + injector.injectMembers(binder); + HttpRequest request = new HttpRequest(HttpMethod.PUT, URI.create("/")); + binder.addEntityToRequest(ImmutableMap.of("imageName", "foo", "serverId", "2"), request); + assertEquals("{\"image\":{\"serverId\":2,\"name\":\"foo\"}}", request.getEntity()); + } + + @Test(expectedExceptions = { NullPointerException.class, IllegalStateException.class }) + public void testNullIsBad() { + CreateImageBinder binder = new CreateImageBinder(); + injector.injectMembers(binder); + HttpRequest request = new HttpRequest(HttpMethod.PUT, URI.create("/")); + binder.addEntityToRequest(null, request); + } +} diff --git a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/functions/ParseBackupScheduleFromGsonResponseTest.java b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/functions/ParseBackupScheduleFromGsonResponseTest.java new file mode 100644 index 0000000000..7bddbd7606 --- /dev/null +++ b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/functions/ParseBackupScheduleFromGsonResponseTest.java @@ -0,0 +1,70 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.rackspace.cloudservers.functions; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.net.UnknownHostException; + +import org.apache.commons.io.IOUtils; +import org.jclouds.http.functions.config.ParserModule; +import org.jclouds.rackspace.cloudservers.domain.BackupSchedule; +import org.jclouds.rackspace.cloudservers.domain.DailyBackup; +import org.jclouds.rackspace.cloudservers.domain.WeeklyBackup; +import org.testng.annotations.Test; + +import com.google.gson.Gson; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * Tests behavior of {@code ParseBackupScheduleFromGsonResponse} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "cloudservers.ParseBackupScheduleFromGsonResponseTest") +public class ParseBackupScheduleFromGsonResponseTest { + + Injector i = Guice.createInjector(new ParserModule()); + + public void testApplyInputStreamDetails() throws UnknownHostException { + InputStream is = getClass().getResourceAsStream("/test_list_backupschedule.json"); + + ParseBackupScheduleFromGsonResponse parser = new ParseBackupScheduleFromGsonResponse(i + .getInstance(Gson.class)); + BackupSchedule response = parser.apply(is); + assertEquals(new BackupSchedule(WeeklyBackup.THURSDAY, DailyBackup.H_0400_0600, true), + response); + } + + public void testNoSchedule() throws UnknownHostException { + + ParseBackupScheduleFromGsonResponse parser = new ParseBackupScheduleFromGsonResponse(i + .getInstance(Gson.class)); + BackupSchedule response = parser.apply(IOUtils + .toInputStream("{\"backupSchedule\":{\"enabled\" : false}}")); + assertEquals(new BackupSchedule(), response); + } +} diff --git a/rackspace/cloudservers/core/src/test/resources/test_list_backupschedule.json b/rackspace/cloudservers/core/src/test/resources/test_list_backupschedule.json new file mode 100644 index 0000000000..c2f893a506 --- /dev/null +++ b/rackspace/cloudservers/core/src/test/resources/test_list_backupschedule.json @@ -0,0 +1,7 @@ +{ + "backupSchedule" : { + "enabled" : true, + "weekly" : "THURSDAY", + "daily" : "H_0400_0600" + } +} \ No newline at end of file