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