openstack-nova: Adding Admin Actions client to allow extra actions to be performed on servers

This commit is contained in:
Adam Lowe 2012-05-04 16:21:01 +01:00
parent e95e6df805
commit 7678c6e776
13 changed files with 1295 additions and 127 deletions

View File

@ -23,6 +23,7 @@ import java.util.Set;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.Zone;
import org.jclouds.location.functions.ZoneToEndpoint;
import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient;
@ -145,4 +146,11 @@ public interface NovaAsyncClient {
Optional<ServerWithSecurityGroupsAsyncClient> getServerExtraDataExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides asynchronous access to Server Admin Actions features.
*/
@Delegate
Optional<AdminActionsAsyncClient> getAdminActionsExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
}

View File

@ -25,6 +25,7 @@ import org.jclouds.concurrent.Timeout;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.Zone;
import org.jclouds.location.functions.ZoneToEndpoint;
import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsClient;
import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient;
import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationClient;
import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient;
@ -148,4 +149,12 @@ public interface NovaClient {
Optional<ServerWithSecurityGroupsClient> getServerExtraDataExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides asynchronous access to Server Admin Actions features.
*/
@Delegate
Optional<AdminActionsClient> getAdminActionsExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
}

View File

@ -76,6 +76,7 @@ public class NovaRestClientModule extends RestClientModule<NovaClient, NovaAsync
.put(VolumeClient.class, VolumeAsyncClient.class)
.put(VirtualInterfaceClient.class, VirtualInterfaceAsyncClient.class)
.put(ServerWithSecurityGroupsClient.class, ServerWithSecurityGroupsAsyncClient.class)
.put(AdminActionsClient.class, AdminActionsAsyncClient.class)
.build();
public NovaRestClientModule() {

View File

@ -0,0 +1,43 @@
/**
* 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.v1_1.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.CaseFormat;
/**
* @author Adam Lowe
*/
public enum BackupType {
DAILY, WEEKLY;
public String value() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name());
}
@Override
public String toString() {
return value();
}
public static BackupType fromValue(String backupType) {
return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(backupType, "backupType")));
}
}

View File

@ -0,0 +1,167 @@
/**
* 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.v1_1.extensions;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jclouds.concurrent.Timeout;
import org.jclouds.openstack.filters.AuthenticateRequest;
import org.jclouds.openstack.nova.v1_1.domain.BackupType;
import org.jclouds.openstack.nova.v1_1.functions.ParseImageIdFromLocationHeader;
import org.jclouds.openstack.nova.v1_1.options.CreateBackupOfServerOptions;
import org.jclouds.openstack.services.Extension;
import org.jclouds.openstack.services.ServiceType;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.Payload;
import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provide access to Admin Server Actions via REST API
*
* @author Adam Lowe
* @see org.jclouds.openstack.nova.v1_1.extensions.AdminActionsClient
*/
@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.ADMIN_ACTIONS)
@SkipEncoding( { '/', '=' })
@RequestFilters(AuthenticateRequest.class)
@Path("/servers/{id}/action")
public interface AdminActionsAsyncClient {
/**
* @see AdminActionsClient#suspendServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"suspend\":null}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> suspendServer(@PathParam("id") String id);
/**
* @see AdminActionsClient#resumeServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"resume\":null}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> resumeServer(@PathParam("id") String id);
/**
* @see AdminActionsClient#migrateServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"migrate\":null}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> migrateServer(@PathParam("id") String id);
/**
* @see AdminActionsClient#suspendServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"lock\":null}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> lockServer(@PathParam("id") String id);
/**
* @see AdminActionsClient#unlockServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"unlock\":null}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> unlockServer(@PathParam("id") String id);
/**
* @see AdminActionsClient#resetNetworkOfServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"resetNetwork\":null}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> resetNetworkOfServer(@PathParam("id") String id);
/**
* @see AdminActionsClient#createBackupOfServer
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"createBackup\":%7B\"name\":\"{name}\",\"backup_type\":\"{backup_type}\",\"rotation\":{rotation}%7D%7D")
@ExceptionParser(MapHttp4xxCodesToExceptions.class)
@ResponseParser(ParseImageIdFromLocationHeader.class)
ListenableFuture<String> createBackupOfServer(@PathParam("id") String id,
@PayloadParam("name") String imageName,
@PayloadParam("backup_type") BackupType backupType,
@PayloadParam("rotation") int rotation,
CreateBackupOfServerOptions... options);
/**
* @see AdminActionsClient#pauseServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"pause\":null}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> pauseServer(@PathParam("id") String id);
/**
* @see AdminActionsClient#unpauseServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"unpause\":null}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> unpauseServer(@PathParam("id") String id);
/**
* @see AdminActionsClient#suspendServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"injectNetworkInfo\":null}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> injectNetworkInfoIntoServer(@PathParam("id") String id);
/**
* @see AdminActionsClient#migrateServer(String)
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
@Payload("%7B\"os-migrateLive\":%7B\"host\":\"{host}\",\"block_migration\":\"{bm}\",\"disk_over_commit\":\"{doc}\"%7D%7D")
ListenableFuture<Boolean> liveMigrateServer(@PathParam("id") String id,
@PayloadParam("host") String host,
@PayloadParam("bm") boolean blockMigration,
@PayloadParam("doc") boolean diskOverCommit);
}

View File

@ -0,0 +1,123 @@
/**
* 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.v1_1.extensions;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.openstack.nova.v1_1.domain.BackupType;
import org.jclouds.openstack.nova.v1_1.options.CreateBackupOfServerOptions;
import org.jclouds.openstack.services.Extension;
import org.jclouds.openstack.services.ServiceType;
/**
* Provide additional actions for servers:
* 'suspend', 'resume', 'migrate', 'lock', 'unlock', 'resetNetwork', 'createBackup', 'pause', 'migrateLive',
* 'injectNetworkInfo', 'unpause'
*
* @author Adam Lowe
* @see org.jclouds.openstack.nova.v1_1.extensions.AdminActionsAsyncClient
*/
@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.ADMIN_ACTIONS)
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
public interface AdminActionsClient {
/**
* Suspend a server.
*
* @param id id of the server
*/
Boolean suspendServer(String id);
/**
* Resume a server.
*
* @param id id of the server
*/
Boolean resumeServer(String id);
/**
* Migrate a server.
*
* @param id id of the server
*/
Boolean migrateServer(String id);
/**
* Lock a server.
*
* @param id id of the server
*/
Boolean lockServer(String id);
/**
* Unlock a server.
*
* @param id id of the server
*/
Boolean unlockServer(String id);
/**
* Reset network of a server.
*
* @param id id of the server
*/
Boolean resetNetworkOfServer(String id);
/**
* Create backup of a server.
*
* @param id id of the server
* @param imageName the name of the image to create
* @param backupType the type of backup
* @param rotation the number of images to retain (0 to simply overwrite)
* @param options optional rotation and/or metadata parameters
* @return the id of the newly created image
*/
String createBackupOfServer(String id, String imageName, BackupType backupType, int rotation, CreateBackupOfServerOptions... options);
/**
* Pause a server.
*
* @param id id of the server
*/
Boolean pauseServer(String id);
/**
* Unpause a server.
*
* @param id id of the server
*/
Boolean unpauseServer(String id);
/**
* Live migrate a server.
*
* @param id id of the server
*/
Boolean liveMigrateServer(String id, String host, boolean blockMigration, boolean diskOverCommit);
/**
* Inject network info into a server.
*
* @param id id of the server
*/
Boolean injectNetworkInfoIntoServer(String id);
}

View File

@ -85,5 +85,8 @@ public interface ExtensionNamespaces {
* Instance rescue mode
*/
public static final String RESCUE = "http://docs.openstack.org/ext/rescue/api/v1.1";
/**
* Admin Action extension
*/
public static final String ADMIN_ACTIONS = "http://docs.openstack.org/ext/admin-actions/api/v1.1";
}

View File

@ -72,6 +72,8 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
URI.create("http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1"))
.put(URI.create(ExtensionNamespaces.CREATESERVEREXT),
URI.create("http://docs.openstack.org/compute/ext/createserverext/api/v1.1"))
.put(URI.create(ExtensionNamespaces.ADMIN_ACTIONS),
URI.create("http://docs.openstack.org/compute/ext/admin-actions/api/v1.1"))
.build();
@Inject

View File

@ -0,0 +1,107 @@
/**
* 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.v1_1.options;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import java.util.Map;
import javax.inject.Inject;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.MapBinder;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
/**
* @author Adam Lowe
*/
public class CreateBackupOfServerOptions implements MapBinder {
public static final CreateBackupOfServerOptions NONE = new CreateBackupOfServerOptions();
@Inject
protected BindToJsonPayload jsonBinder;
private Map<String, String> metadata = ImmutableMap.of();
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
Map<String, Object> data = Maps.newHashMap();
data.putAll(postParams);
data.put("metadata", metadata);
return jsonBinder.bindToRequest(request, ImmutableMap.of("createBackup", data));
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object toBind) {
throw new IllegalStateException("createBackupOfServer is a POST operation");
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof CreateBackupOfServerOptions)) return false;
final CreateBackupOfServerOptions other = CreateBackupOfServerOptions.class.cast(object);
return equal(metadata, other.metadata);
}
@Override
public int hashCode() {
return Objects.hashCode(metadata);
}
protected ToStringHelper string() {
return toStringHelper("").add("metadata", metadata);
}
@Override
public String toString() {
return string().toString();
}
/** @see #getMetadata() */
public CreateBackupOfServerOptions metadata(Map<String, String> metadata) {
this.metadata = metadata;
return this;
}
/**
* Extra image properties to include
*/
public Map<String, String> getMetadata() {
return metadata;
}
public static class Builder {
/**
* @see CreateBackupOfServerOptions#getMetadata()
*/
public static CreateBackupOfServerOptions metadata(Map<String, String> metadata) {
return new CreateBackupOfServerOptions().metadata(metadata);
}
}
}

View File

@ -0,0 +1,352 @@
/**
* 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.v1_1.extensions;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.net.URI;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.nova.v1_1.domain.BackupType;
import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest;
import org.jclouds.openstack.nova.v1_1.options.CreateBackupOfServerOptions;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
/**
* Tests parsing and guice wiring of AdminActionsClient
*
* @author Adam Lowe
*/
@Test(groups = "unit", testName = "AdminActionsClientExpectTest")
public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest {
public void testSuspend() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "suspend").build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.suspendServer("1"));
}
public void testSuspendFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "suspend").build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.suspendServer("1"));
}
@Test(expectedExceptions = AuthorizationException.class)
public void testSuspendFailsNotAuthorized() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "suspend").build(),
standardResponseBuilder(403).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
client.suspendServer("1");
}
public void testResume() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "resume").build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.resumeServer("1"));
}
public void testResumeFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "resume").build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.resumeServer("1"));
}
@Test(expectedExceptions = AuthorizationException.class)
public void testResumeFailsNotAuthorized() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "resume").build(),
standardResponseBuilder(403).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
client.resumeServer("1");
}
public void testLock() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "lock").build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.lockServer("1"));
}
public void testLockFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "lock").build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.lockServer("1"));
}
public void testUnlock() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "unlock").build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.unlockServer("1"));
}
public void testUnlockFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "unlock").build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.unlockServer("1"));
}
public void testPause() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "pause").build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.pauseServer("1"));
}
public void testPauseFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "pause").build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.pauseServer("1"));
}
public void testUnpause() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "unpause").build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.unpauseServer("1"));
}
public void testUnpauseFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "unpause").build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.unpauseServer("1"));
}
public void testMigrateServer() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "migrate").build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.migrateServer("1"));
}
public void testMigrateServerFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "migrate").build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.migrateServer("1"));
}
public void testResetNetworkOfServer() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "resetNetwork").build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.resetNetworkOfServer("1"));
}
public void testResetNetworkOfServerFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "resetNetwork").build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.resetNetworkOfServer("1"));
}
public void testInjectNetworkInfoIntoServer() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "injectNetworkInfo").build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.injectNetworkInfoIntoServer("1"));
}
public void testInjectNetworkInfoIntoServerFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "injectNetworkInfo").build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.injectNetworkInfoIntoServer("1"));
}
public void testBackupServer() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"createBackup\":{\"backup_type\":\"weekly\",\"rotation\":\"3\",\"name\":\"mybackup\",\"metadata\":{\"some\":\"data or other\"}}}", MediaType.APPLICATION_JSON)).build(),
standardResponseBuilder(202).headers(ImmutableMultimap.of("Location", "http://172.16.89.149:8774/v2/images/1976b3b3-409a-468d-b16c-a9172c341b46")).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
String imageId = client.createBackupOfServer("1", "mybackup", BackupType.WEEKLY, 3, CreateBackupOfServerOptions.Builder.metadata(ImmutableMap.of("some", "data or other")));
assertEquals(imageId, "1976b3b3-409a-468d-b16c-a9172c341b46");
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testBackupServerFailNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"createBackup\":{\"backup_type\":\"weekly\",\"rotation\":\"3\",\"name\":\"mybackup\",\"metadata\":{\"some\":\"data or other\"}}}", MediaType.APPLICATION_JSON)).build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
client.createBackupOfServer("1", "mybackup", BackupType.WEEKLY, 3, CreateBackupOfServerOptions.Builder.metadata(ImmutableMap.of("some", "data or other")));
}
public void testLiveMigrateServer() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "GONNAOVERWRITE")
.payload(payloadFromStringWithContentType("{\"os-migrateLive\":{\"host\":\"bighost\",\"block_migration\":\"true\",\"disk_over_commit\":\"false\"}}", MediaType.APPLICATION_JSON)).build(),
standardResponseBuilder(202).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.liveMigrateServer("1", "bighost", true, false));
}
public void testLiveMigrateServerFailsNotFound() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action");
AdminActionsClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardActionRequestBuilderVoidResponse(endpoint, "GONNAOVERWRITE")
.payload(payloadFromStringWithContentType("{\"os-migrateLive\":{\"host\":\"bighost\",\"block_migration\":\"true\",\"disk_over_commit\":\"false\"}}", MediaType.APPLICATION_JSON)).build(),
standardResponseBuilder(404).build()
).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.liveMigrateServer("1", "bighost", true, false));
}
protected HttpRequest.Builder standardActionRequestBuilderVoidResponse(URI endpoint, String actionName) {
return HttpRequest.builder().method("POST")
.headers(ImmutableMultimap.of("X-Auth-Token", authToken))
.payload(payloadFromStringWithContentType("{\"" + actionName + "\":null}", MediaType.APPLICATION_JSON))
.endpoint(endpoint);
}
}

View File

@ -0,0 +1,191 @@
/**
* 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.v1_1.extensions;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import org.jclouds.http.HttpResponseException;
import org.jclouds.openstack.nova.v1_1.domain.BackupType;
import org.jclouds.openstack.nova.v1_1.domain.Image;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.features.ExtensionClient;
import org.jclouds.openstack.nova.v1_1.features.ImageClient;
import org.jclouds.openstack.nova.v1_1.features.ServerClient;
import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest;
import org.jclouds.openstack.nova.v1_1.options.CreateBackupOfServerOptions;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
/**
* Tests behavior of HostAdministrationClient
*
* TODO test migration methods
*
* @author Adam Lowe
*/
@Test(groups = "live", testName = "AdminActionsClientLiveTest", singleThreaded = true)
public class AdminActionsClientLiveTest extends BaseNovaClientLiveTest {
private ImageClient imageClient;
private ServerClient serverClient;
private ExtensionClient extensionClient;
private Optional<AdminActionsClient> clientOption;
private String zone;
private String testServerId;
private String backupImageId;
@BeforeGroups(groups = {"integration", "live"})
@Override
public void setupContext() {
super.setupContext();
zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova");
serverClient = novaContext.getApi().getServerClientForZone(zone);
extensionClient = novaContext.getApi().getExtensionClientForZone(zone);
imageClient = novaContext.getApi().getImageClientForZone(zone);
clientOption = novaContext.getApi().getAdminActionsExtensionForZone(zone);
if (clientOption.isPresent()) {
testServerId = createServerInZone(zone).getId();
}
}
@AfterGroups(groups = "live", alwaysRun = true)
@Override
protected void tearDown() {
if (clientOption.isPresent()) {
if (testServerId != null) {
assertTrue(novaContext.getApi().getServerClientForZone(zone).deleteServer(testServerId));
}
if (backupImageId != null) {
imageClient.deleteImage(backupImageId);
}
}
super.tearDown();
}
@AfterMethod(alwaysRun = true)
public void ensureServerIsActiveAgain() {
blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE);
}
public void testSuspendAndResume() {
if (clientOption.isPresent()) {
AdminActionsClient client = clientOption.get();
// Suspend-resume
try {
client.resumeServer(testServerId);
fail("Resumed an active server!");
} catch (HttpResponseException e) {
}
assertTrue(client.suspendServer(testServerId));
blockUntilServerInState(testServerId, serverClient, Server.Status.SUSPENDED);
try {
client.suspendServer(testServerId);
fail("Suspended an already suspended server!");
} catch (HttpResponseException e) {
}
assertTrue(client.resumeServer(testServerId));
blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE);
try {
client.resumeServer(testServerId);
fail("Resumed an already resumed server!");
} catch (HttpResponseException e) {
}
}
}
public void testLockAndUnlock() {
if (clientOption.isPresent()) {
AdminActionsClient client = clientOption.get();
// TODO should we be able to double-lock (as it were)
assertTrue(client.unlockServer(testServerId));
assertTrue(client.unlockServer(testServerId));
assertTrue(client.lockServer(testServerId));
assertTrue(client.lockServer(testServerId));
assertTrue(client.unlockServer(testServerId));
assertTrue(client.unlockServer(testServerId));
}
}
public void testResetNetworkAndInjectNetworkInfo() {
if (clientOption.isPresent()) {
AdminActionsClient client = clientOption.get();
assertTrue(client.resetNetworkOfServer(testServerId));
assertTrue(client.injectNetworkInfoIntoServer(testServerId));
}
}
@Test
public void testPauseAndUnpause() {
if (clientOption.isPresent()) {
AdminActionsClient client = clientOption.get();
// Unlock and lock (double-checking error contitions too)
try {
client.unpauseServer(testServerId);
fail("Unpaused active server!");
} catch (HttpResponseException e) {
}
assertTrue(client.pauseServer(testServerId));
blockUntilServerInState(testServerId, serverClient, Server.Status.PAUSED);
try {
client.pauseServer(testServerId);
fail("paused a paused server!");
} catch (HttpResponseException e) {
}
assertTrue(client.unpauseServer(testServerId));
blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE);
try {
client.unpauseServer(testServerId);
fail("Unpaused a server we just unpaused!");
} catch (HttpResponseException e) {
}
}
}
@Test
public void testCreateBackupOfServer() throws InterruptedException {
if (clientOption.isPresent()) {
backupImageId = clientOption.get().createBackupOfServer(testServerId, "jclouds-test-backup", BackupType.DAILY, 0,
CreateBackupOfServerOptions.Builder.metadata(ImmutableMap.of("test", "metadata")));
assertNotNull(backupImageId);
// If we don't have extended task status, we'll have to wait here!
if (extensionClient.getExtensionByAlias("OS-EXT-STS") == null) {
Thread.sleep(30000);
}
blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE);
Image backupImage = imageClient.getImage(backupImageId);
assertEquals(backupImage.getId(), backupImageId);
}
}
}

View File

@ -83,15 +83,19 @@ public class BaseNovaClientLiveTest extends BaseComputeServiceContextLiveTest {
protected Server createServerInZone(String zoneId) {
ServerClient serverClient = novaContext.getApi().getServerClientForZone(zoneId);
Server server = serverClient.createServer("test", imageIdForZone(zoneId), flavorRefForZone(zoneId));
blockUntilServerActive(server.getId(), serverClient);
blockUntilServerInState(server.getId(), serverClient, Status.ACTIVE);
return server;
}
private void blockUntilServerActive(String serverId, ServerClient client) {
/**
* Will block until the requested server is in the correct state, if Extended Server Status extension is loaded
* this will continue to block while any task is in progress.
*/
protected void blockUntilServerInState(String serverId, ServerClient client, Status status) {
Server currentDetails = null;
for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != Status.ACTIVE; currentDetails = client
for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != status || currentDetails.getTaskState() != null; currentDetails = client
.getServer(serverId)) {
System.out.printf("blocking on status active%n%s%n", currentDetails);
System.out.printf("blocking on status %s%n%s%n", status, currentDetails);
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {

View File

@ -1,123 +1,281 @@
{"extensions": [
{
"extensions": [{
"updated": "2011-06-09T00:00:00+00:00",
"name": "Multinic",
"links": [],
"namespace": "https://docs.openstack.org/ext/multinic/api/v1.1",
"alias": "NMN",
"description": "Multiple network support"
}, {
"updated": "2011-06-29T00:00:00+00:00",
"name": "Hosts",
"links": [],
"namespace": "https://docs.openstack.org/ext/hosts/api/v1.1",
"alias": "os-hosts",
"description": "Host administration"
}, {
"updated": "2011-03-25T00:00:00+00:00",
"name": "Volumes",
"links": [],
"namespace": "https://docs.openstack.org/ext/volumes/api/v1.1",
"alias": "os-volumes",
"description": "Volumes support"
}, {
"updated": "2011-05-25 16:12:21.656723",
"name": "Admin Controller",
"links": [],
"namespace": "https:TODO/",
"alias": "ADMIN",
"description": "The Admin API Extension"
}, {
"updated": "2011-08-08T00:00:00+00:00",
"name": "Quotas",
"links": [],
"namespace": "https://docs.openstack.org/ext/quotas-sets/api/v1.1",
"alias": "os-quota-sets",
"description": "Quotas management support"
}, {
"updated": "2011-08-24T00:00:00+00:00",
"name": "VolumeTypes",
"links": [],
"namespace": "https://docs.openstack.org/ext/volume_types/api/v1.1",
"alias": "os-volume-types",
"description": "Volume types support"
}, {
"updated": "2011-06-23T00:00:00+00:00",
"name": "FlavorExtraSpecs",
"links": [],
"namespace": "https://docs.openstack.org/ext/flavor_extra_specs/api/v1.1",
"alias": "os-flavor-extra-specs",
"description": "Instance type (flavor) extra specs"
}, {
"updated": "2011-09-14T00:00:00+00:00",
"name": "FlavorExtraData",
"links": [],
"namespace": "https://docs.openstack.org/ext/flavor_extra_data/api/v1.1",
"alias": "os-flavor-extra-data",
"description": "Provide additional data for flavors"
}, {
"updated": "2011-08-17T00:00:00+00:00",
"name": "VirtualInterfaces",
"links": [],
"namespace": "https://docs.openstack.org/ext/virtual_interfaces/api/v1.1",
"alias": "virtual_interfaces",
"description": "Virtual interface support"
}, {
"updated": "2011-07-19T00:00:00+00:00",
"name": "Createserverext",
"links": [],
"namespace": "https://docs.openstack.org/ext/createserverext/api/v1.1",
"alias": "os-create-server-ext",
"description": "Extended support to the Create Server v1.1 API"
}, {
"updated": "2011-08-08T00:00:00+00:00",
"name": "Keypairs",
"links": [],
"namespace": "https://docs.openstack.org/ext/keypairs/api/v1.1",
"alias": "os-keypairs",
"description": "Keypair Support"
}, {
"updated": "2011-08-25T00:00:00+00:00",
"name": "VSAs",
"links": [],
"namespace": "https://docs.openstack.org/ext/vsa/api/v1.1",
"alias": "zadr-vsa",
"description": "Virtual Storage Arrays support"
}, {
"updated": "2011-08-19T00:00:00+00:00",
"name": "SimpleTenantUsage",
"links": [],
"namespace": "https://docs.openstack.org/ext/os-simple-tenant-usage/api/v1.1",
"alias": "os-simple-tenant-usage",
"description": "Simple tenant usage extension"
}, {
"updated": "2011-08-18T00:00:00+00:00",
"name": "Rescue",
"links": [],
"namespace": "https://docs.openstack.org/ext/rescue/api/v1.1",
"alias": "os-rescue",
"description": "Instance rescue mode"
}, {
"updated": "2011-07-21T00:00:00+00:00",
"name": "SecurityGroups",
"links": [],
"namespace": "https://docs.openstack.org/ext/securitygroups/api/v1.1",
"alias": "security_groups",
"description": "Security group support"
}, {
"updated": "2011-06-16T00:00:00+00:00",
"name": "Floating_ips",
"links": [],
"namespace": "https://docs.openstack.org/ext/floating_ips/api/v1.1",
"alias": "os-floating-ips",
"description": "Floating IPs support"
}, {
"updated": "2011-06-16T00:00:00+00:00",
"name": "Users",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/users/api/v1.1",
"alias": "os-users",
"description": "Users support"
}
]
}
"updated": "2011-09-27T00:00:00+00:00",
"name": "DiskConfig",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/disk_config/api/v1.1",
"alias": "OS-DCF",
"description": "Disk Management Extension"
},
{
"updated": "2011-06-29T00:00:00+00:00",
"name": "Hosts",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/hosts/api/v1.1",
"alias": "os-hosts",
"description": "Admin-only host administration"
},
{
"updated": "2011-07-19T00:00:00+00:00",
"name": "SchedulerHints",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/scheduler-hints/api/v2",
"alias": "os-scheduler-hints",
"description": "Pass arbitrary key/value pairs to the scheduler"
},
{
"updated": "2011-08-08T00:00:00+00:00",
"name": "Quotas",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1",
"alias": "os-quota-sets",
"description": "Quotas management support"
},
{
"updated": "2011-12-23T00:00:00+00:00",
"name": "Floating_ip_dns",
"links": [],
"namespace": "http://docs.openstack.org/ext/floating_ip_dns/api/v1.1",
"alias": "os-floating-ip-dns",
"description": "Floating IP DNS support"
},
{
"updated": "2011-09-14T00:00:00+00:00",
"name": "FlavorExtraData",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1",
"alias": "OS-FLV-EXT-DATA",
"description": "Provide additional data for flavors"
},
{
"updated": "2011-06-23T00:00:00+00:00",
"name": "FlavorExtraSpecs",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1",
"alias": "os-flavor-extra-specs",
"description": "Instance type (flavor) extra specs"
},
{
"updated": "2011-08-17T00:00:00+00:00",
"name": "VirtualInterfaces",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1",
"alias": "virtual_interfaces",
"description": "Virtual interface support"
},
{
"updated": "2011-12-23T00:00:00+00:00",
"name": "Accounts",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/accounts/api/v1.1",
"alias": "os-accounts",
"description": "Admin-only access to accounts"
},
{
"updated": "2011-03-25T00:00:00+00:00",
"name": "Volumes",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/volumes/api/v1.1",
"alias": "os-volumes",
"description": "Volumes support"
},
{
"updated": "2011-11-03T00:00:00+00:00",
"name": "ExtendedStatus",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/extended_status/api/v1.1",
"alias": "OS-EXT-STS",
"description": "Extended Status support"
},
{
"updated": "2011-12-23T00:00:00+00:00",
"name": "Consoles",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/os-consoles/api/v2",
"alias": "os-consoles",
"description": "Interactive Console support."
},
{
"updated": "2011-07-21T00:00:00+00:00",
"name": "SecurityGroups",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/securitygroups/api/v1.1",
"alias": "security_groups",
"description": "Security group support"
},
{
"updated": "2012-01-12T00:00:00+00:00",
"name": "Aggregates",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/aggregates/api/v1.1",
"alias": "os-aggregates",
"description": "Admin-only aggregate administration"
},
{
"updated": "2011-07-19T00:00:00+00:00",
"name": "Createserverext",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/createserverext/api/v1.1",
"alias": "os-create-server-ext",
"description": "Extended support to the Create Server v1.1 API"
},
{
"updated": "2011-09-01T00:00:00+00:00",
"name": "DeferredDelete",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/deferred-delete/api/v1.1",
"alias": "os-deferred-delete",
"description": "Instance deferred delete"
},
{
"updated": "2011-12-21T00:00:00+00:00",
"name": "ServerDiagnostics",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/server-diagnostics/api/v1.1",
"alias": "os-server-diagnostics",
"description": "Allow Admins to view server diagnostics through server action"
},
{
"updated": "2011-12-23T00:00:00+00:00",
"name": "Networks",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1",
"alias": "os-networks",
"description": "Admin-only Network Management Extension"
},
{
"updated": "2011-11-03T00:00:00+00:00",
"name": "ExtendedServerAttributes",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/extended_status/api/v1.1",
"alias": "OS-EXT-SRV-ATTR",
"description": "Extended Server Attributes support."
},
{
"updated": "2011-08-08T00:00:00+00:00",
"name": "Keypairs",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/keypairs/api/v1.1",
"alias": "os-keypairs",
"description": "Keypair Support"
},
{
"updated": "2011-08-24T00:00:00+00:00",
"name": "VolumeTypes",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/volume_types/api/v1.1",
"alias": "os-volume-types",
"description": "Volume types support"
},
{
"updated": "2011-08-19T00:00:00+00:00",
"name": "SimpleTenantUsage",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/os-simple-tenant-usage/api/v1.1",
"alias": "os-simple-tenant-usage",
"description": "Simple tenant usage extension"
},
{
"updated": "2012-01-04T00:00:00+00:00",
"name": "Floating_ip_pools",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/floating_ip_pools/api/v1.1",
"alias": "os-floating-ip-pools",
"description": "Floating IPs support"
},
{
"updated": "2012-01-23T00:00:00+00:00",
"name": "ServerStartStop",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/servers/api/v1.1",
"alias": "os-server-start-stop",
"description": "Start/Stop instance compute API support"
},
{
"updated": "2012-03-12T00:00:00+00:00",
"name": "QuotaClasses",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1",
"alias": "os-quota-class-sets",
"description": "Quota classes management support"
},
{
"updated": "2012-01-19T00:00:00+00:00",
"name": "Certificates",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/certificates/api/v1.1",
"alias": "os-certificates",
"description": "Certificates support"
},
{
"updated": "2011-08-18T00:00:00+00:00",
"name": "Rescue",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/rescue/api/v1.1",
"alias": "os-rescue",
"description": "Instance rescue mode"
},
{
"updated": "2012-01-19T00:00:00+00:00",
"name": "FlavorManage",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/flavor_manage/api/v1.1",
"alias": "os-flavor-manage",
"description": "\n Flavor create/delete API support\n "
},
{
"updated": "2011-12-16T00:00:00+00:00",
"name": "Cloudpipe",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/cloudpipe/api/v1.1",
"alias": "os-cloudpipe",
"description": "Adds actions to create cloudpipe instances.\n\n When running with the Vlan network mode, you need a mechanism to route\n from the public Internet to your vlans. This mechanism is known as a\n cloudpipe.\n\n At the time of creating this class, only OpenVPN is supported. Support for\n a SSH Bastion host is forthcoming.\n "
},
{
"updated": "2011-06-09T00:00:00+00:00",
"name": "Multinic",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/multinic/api/v1.1",
"alias": "NMN",
"description": "Multiple network support"
},
{
"updated": "2011-08-08T00:00:00+00:00",
"name": "Users",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/users/api/v1.1",
"alias": "os-users",
"description": "Allow admins to acces user information"
},
{
"updated": "2011-09-20T00:00:00+00:00",
"name": "AdminActions",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/admin-actions/api/v1.1",
"alias": "os-admin-actions",
"description": "Enable admin-only server actions\n\n Actions include: pause,unpause, suspend, resume, migrate,\n resetNetwork, injectNetworkInfo, lock, unlock, createBackup\n "
},
{
"updated": "2011-12-21T00:00:00+00:00",
"name": "ServerActionList",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/server-actions-list/api/v1.1",
"alias": "os-server-action-list",
"description": "Allow Admins to view pending server actions"
},
{
"updated": "2011-12-08T00:00:00+00:00",
"name": "Console_output",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/os-console-output/api/v2",
"alias": "os-console-output",
"description": "Console log output support, with tailing ability."
},
{
"updated": "2011-06-16T00:00:00+00:00",
"name": "Floating_ips",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/floating_ips/api/v1.1",
"alias": "os-floating-ips",
"description": "Floating IPs support"}
]}