openstack-nova: Adding HostAggregates extension

This commit is contained in:
Adam Lowe 2012-05-11 15:13:03 +01:00
parent 5228a14fbb
commit d5b2968a54
15 changed files with 1020 additions and 41 deletions

View File

@ -23,15 +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;
import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.*;
import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.FlavorAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.ImageAsyncClient;
@ -153,4 +145,11 @@ public interface NovaAsyncClient {
Optional<AdminActionsAsyncClient> getAdminActionsExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides asynchronous access to HostAggregate features.
*/
@Delegate
Optional<HostAggregateAsyncClient> getHostAggregateExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
}

View File

@ -25,15 +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;
import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient;
import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsClient;
import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageClient;
import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceClient;
import org.jclouds.openstack.nova.v1_1.extensions.VolumeClient;
import org.jclouds.openstack.nova.v1_1.extensions.*;
import org.jclouds.openstack.nova.v1_1.features.ExtensionClient;
import org.jclouds.openstack.nova.v1_1.features.FlavorClient;
import org.jclouds.openstack.nova.v1_1.features.ImageClient;
@ -154,4 +146,11 @@ public interface NovaClient {
Optional<AdminActionsClient> getAdminActionsExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides synchronous access to Aggregate features.
*/
@Delegate
Optional<HostAggregateClient> getHostAggregateExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
}

View File

@ -0,0 +1,39 @@
/**
* 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.binders;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.json.Json;
import com.google.inject.TypeLiteral;
/**
* @author Adam Lowe
*/
@Singleton
public class BindAggregateMetadataToJsonPayload extends BindObjectToJsonPayload<Map<String,String>> {
@Inject
public BindAggregateMetadataToJsonPayload(Json jsonBinder) {
super(jsonBinder, "metadata", new TypeLiteral<Map<String, String>>(){}, "set_metadata");
}
}

View File

@ -0,0 +1,86 @@
/**
* 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.binders;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import org.jclouds.http.HttpRequest;
import org.jclouds.json.Json;
import org.jclouds.rest.MapBinder;
import org.jclouds.rest.binders.BindToJsonPayload;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Iterables;
import com.google.inject.TypeLiteral;
/**
* @author Adam Lowe
*/
public abstract class BindObjectToJsonPayload<T> extends BindToJsonPayload implements MapBinder {
private final String fieldName;
private final String wrapperName;
private final TypeLiteral<T> type;
/** Bind a specific argument to the json payload
*
* @param fieldName the name of the output json field
* @param fieldType the type of the object to select from the method arguments
* @param wrapperName the name of the json field wrapper (if any)
*/
public BindObjectToJsonPayload(Json jsonBinder, String fieldName, TypeLiteral<T> fieldType, String wrapperName) {
super(jsonBinder);
this.fieldName = fieldName;
this.wrapperName = wrapperName;
this.type = fieldType;
}
public BindObjectToJsonPayload(Json jsonBinder, String fieldName, TypeLiteral<T> fieldType) {
this(jsonBinder, fieldName, fieldType, null);
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object toBind) {
throw new IllegalStateException("BindMapToJsonPayload needs parameters");
}
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
Builder<String, Object> payload = ImmutableMap.builder();
payload.putAll(postParams);
checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest<?>,
"this binder is only valid for GeneratedHttpRequests!");
GeneratedHttpRequest<?> gRequest = (GeneratedHttpRequest<?>) request;
T specs = (T) Iterables.find(gRequest.getArgs(), Predicates.instanceOf(type.getRawType()));
payload.put(fieldName, specs);
if (wrapperName != null) {
return super.bindToRequest(request, ImmutableMap.of(wrapperName, payload.build()));
}
return super.bindToRequest(request, payload.build());
}
}

View File

@ -35,24 +35,7 @@ import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
import org.jclouds.openstack.nova.v1_1.NovaAsyncClient;
import org.jclouds.openstack.nova.v1_1.NovaClient;
import org.jclouds.openstack.nova.v1_1.domain.Extension;
import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsClient;
import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient;
import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationClient;
import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient;
import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient;
import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsClient;
import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageClient;
import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceClient;
import org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.VolumeClient;
import org.jclouds.openstack.nova.v1_1.extensions.*;
import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.ExtensionClient;
import org.jclouds.openstack.nova.v1_1.features.FlavorAsyncClient;
@ -94,6 +77,7 @@ public class NovaRestClientModule extends RestClientModule<NovaClient, NovaAsync
.put(VirtualInterfaceClient.class, VirtualInterfaceAsyncClient.class)
.put(ServerWithSecurityGroupsClient.class, ServerWithSecurityGroupsAsyncClient.class)
.put(AdminActionsClient.class, AdminActionsAsyncClient.class)
.put(HostAggregateClient.class, HostAggregateAsyncClient.class)
.build();
public NovaRestClientModule() {

View File

@ -0,0 +1,269 @@
/**
* 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 java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.annotations.SerializedName;
/**
* Aggregates can be manipulated using the Aggregate Extension to Nova (alias "OS-AGGREGATES")
*
* @see org.jclouds.openstack.nova.v1_1.extensions.HostAggregateClient
*/
public class HostAggregate {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromAggregate(this);
}
public static abstract class Builder<T extends Builder<T>> {
protected abstract T self();
private String id;
private String name;
private String availabilityZone;
private Set<String> hosts = ImmutableSet.of();
private String state;
private Date created = new Date();
private Date updated;
private Map<String, String> metadata = ImmutableMap.of();
/**
* @see HostAggregate#getId()
*/
public T id(String id) {
this.id = id;
return self();
}
/**
* @see HostAggregate#getName()
*/
public T name(String name) {
this.name = name;
return self();
}
/**
* @see HostAggregate#getAvailabilityZone()
*/
public T availabilityZone(String availabilityZone) {
this.availabilityZone = availabilityZone;
return self();
}
/**
* @see HostAggregate#getHosts()
*/
public T hosts(String... hosts) {
return hosts(ImmutableSet.copyOf(hosts));
}
/**
* @see HostAggregate#getHosts()
*/
public T hosts(Set<String> hosts) {
this.hosts = hosts;
return self();
}
/**
* @see HostAggregate#getState()
*/
public T state(String state) {
this.state = state;
return self();
}
/**
* @see HostAggregate#getCreated()
*/
public T created(Date created) {
this.created = created;
return self();
}
/**
* @see HostAggregate#getUpdated()
*/
public T updated(Date updated) {
this.updated = updated;
return self();
}
/**
* @see HostAggregate#getMetadata()
*/
public T metadata(Map<String, String> metadata) {
this.metadata = metadata;
return self();
}
public HostAggregate build() {
return new HostAggregate(this);
}
public T fromAggregate(HostAggregate in) {
return this
.id(in.getId())
.name(in.getName())
.availabilityZone(in.getAvailabilityZone())
.hosts(in.getHosts())
.state(in.getState())
.created(in.getCreated())
.updated(in.getUpdated().orNull())
.metadata(in.getMetadata());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
private final String id;
private final String name;
@SerializedName(value = "availability_zone")
private final String availabilityZone;
private final Set<String> hosts;
@SerializedName(value = "operational_state")
private final String state;
@SerializedName(value = "created_at")
private final Date created;
@SerializedName(value = "updated_at")
private final Optional<Date> updated;
private final Map<String, String> metadata;
protected HostAggregate(Builder<?> builder) {
this.id = checkNotNull(builder.id, "id");
this.name = checkNotNull(builder.name, "name");
this.availabilityZone = checkNotNull(builder.availabilityZone, "availabilityZone");
this.hosts = ImmutableSet.copyOf(checkNotNull(builder.hosts, "hosts"));
this.state = checkNotNull(builder.state, "state");
this.created = checkNotNull(builder.created, "created");
this.updated = Optional.fromNullable(builder.updated);
this.metadata = ImmutableMap.copyOf(checkNotNull(builder.metadata, "metadata"));
}
// Ensure GSON parsed objects don't have null collections or optionals
protected HostAggregate() {
this.id = null;
this.name = null;
this.availabilityZone = null;
this.hosts = ImmutableSet.of();
this.state = null;
this.created = null;
this.updated = Optional.absent();
this.metadata = ImmutableMap.of();
}
public String getId() {
return this.id;
}
public String getName() {
return this.name;
}
/**
* note: an "Availability Zone" is different from a Nova "Zone"
*
* @return the availability zone this aggregate is in
*/
public String getAvailabilityZone() {
return this.availabilityZone;
}
public Set<String> getHosts() {
return Collections.unmodifiableSet(this.hosts);
}
public String getState() {
return this.state;
}
public Date getCreated() {
return this.created;
}
public Optional<Date> getUpdated() {
return this.updated;
}
public Map<String, String> getMetadata() {
return this.metadata;
}
@Override
public int hashCode() {
return Objects.hashCode(id, name, availabilityZone, hosts, state, created, updated, metadata);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
HostAggregate that = HostAggregate.class.cast(obj);
return Objects.equal(this.id, that.id)
&& Objects.equal(this.name, that.name)
&& Objects.equal(this.availabilityZone, that.availabilityZone)
&& Objects.equal(this.hosts, that.hosts)
&& Objects.equal(this.state, that.state)
&& Objects.equal(this.created, that.created)
&& Objects.equal(this.updated, that.updated)
&& Objects.equal(this.metadata, that.metadata)
;
}
protected ToStringHelper string() {
return Objects.toStringHelper("")
.add("id", id)
.add("name", name)
.add("availabilityZone", availabilityZone)
.add("hosts", hosts)
.add("state", state)
.add("created", created)
.add("updated", updated)
.add("metadata", metadata);
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -93,6 +93,20 @@ public interface ExtensionNamespaces {
/**
* Extended Server Status extension
*/
public static final String EXTENDED_STATUS = "http://docs.openstack.org/ext/extended_status/api/v1.1";
public static final String EXTENDED_STATUS = "http://docs.openstack.org/compute/ext/extended_status/api/v1.1";
/**
* Quota Classes extension
*/
public static final String QUOTA_CLASSES = "http://docs.openstack.org/ext/quota-classes-sets/api/v1.1";
/**
* Disk Config extension
*/
public static final String DISK_CONFIG = "http://docs.openstack.org/compute/ext/disk_config/api/v1.1";
/**
* Aggregates extension
*/
public static final String AGGREGATES = "http://docs.openstack.org/ext/aggregates/api/v1.1";
}

View File

@ -0,0 +1,155 @@
/**
* 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.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
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.binders.BindAggregateMetadataToJsonPayload;
import org.jclouds.openstack.nova.v1_1.domain.HostAggregate;
import org.jclouds.openstack.services.Extension;
import org.jclouds.openstack.services.ServiceType;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.Payload;
import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provide access to Aggregates in Nova.
*
* @author Adam Lowe
* @see HostAggregateClient
*/
@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.AGGREGATES)
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
@RequestFilters(AuthenticateRequest.class)
@Path("/os-aggregates")
public interface HostAggregateAsyncClient {
/**
* @see HostAggregateClient#listAggregates()
*/
@GET
@SelectJson("aggregates")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<HostAggregate>> listAggregates();
/**
* @see HostAggregateClient#getAggregate(String)
*/
@GET
@Path("/{id}")
@SelectJson("aggregate")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<HostAggregate> getAggregate(@PathParam("id") String id);
/**
* @see HostAggregateClient#createAggregate(String, String)
*/
@POST
@SelectJson("aggregate")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"aggregate\":%7B\"name\":\"{name}\",\"availability_zone\":\"{zone}\"%7D%7D")
ListenableFuture<HostAggregate> createAggregate(@PayloadParam("name") String name, @PayloadParam("zone") String availablityZone);
/**
* @see HostAggregateClient#updateName
*/
@POST
@Path("/{id}")
@SelectJson("aggregate")
@Consumes(MediaType.APPLICATION_JSON)
@Payload("%7B\"aggregate\":%7B\"name\":\"{name}\"%7D%7D")
ListenableFuture<HostAggregate> updateName(@PathParam("id") String id, @PayloadParam("name") String name);
/**
* @see HostAggregateClient#updateAvailabilityZone
*/
@POST
@Path("/{id}")
@SelectJson("aggregate")
@Consumes(MediaType.APPLICATION_JSON)
@Payload("%7B\"aggregate\":%7B\"availability_zone\":\"{zone}\"%7D%7D")
ListenableFuture<HostAggregate> updateAvailabilityZone(@PathParam("id") String id, @PayloadParam("zone") String availabilityZone);
/**
* @see HostAggregateClient#deleteAggregate(String)
*/
@DELETE
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> deleteAggregate(@PathParam("id") String id);
/**
* @see HostAggregateClient#addHost(String,String)
*/
@POST
@Path("/{id}/action")
@SelectJson("aggregate")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"add_host\":%7B\"host\":\"{host}\"%7D%7D")
ListenableFuture<HostAggregate> addHost(@PathParam("id") String id, @PayloadParam("host") String host);
/**
* @see HostAggregateClient#removeHost(String,String)
*/
@POST
@Path("/{id}/action")
@SelectJson("aggregate")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"remove_host\":%7B\"host\":\"{host}\"%7D%7D")
ListenableFuture<HostAggregate> removeHost(@PathParam("id") String id, @PayloadParam("host") String host);
/**
* @see HostAggregateClient#setMetadata
*/
@POST
@Path("/{id}/action")
@SelectJson("aggregate")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@MapBinder(BindAggregateMetadataToJsonPayload.class)
ListenableFuture<HostAggregate> setMetadata(@PathParam("id") String id, Map<String, String> metadata);
}

View File

@ -0,0 +1,93 @@
/**
* 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.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.openstack.filters.AuthenticateRequest;
import org.jclouds.openstack.nova.v1_1.domain.HostAggregate;
import org.jclouds.openstack.services.Extension;
import org.jclouds.openstack.services.ServiceType;
import org.jclouds.rest.annotations.RequestFilters;
/**
* Provide access to Host Aggregates in Nova (alias "OS-AGGREGATES")
*
* @author Adam Lowe
* @see HostAggregateAsyncClient
* @see <a href="http://nova.openstack.org/api_ext/ext_aggregates.html"/>
* @see <a href="http://wiki.openstack.org/host-aggregates"/>
*/
@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.AGGREGATES)
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
@RequestFilters(AuthenticateRequest.class)
public interface HostAggregateClient {
/**
* @return the set of host aggregates.
*/
Set<HostAggregate> listAggregates();
/**
* Retrieves the details of an aggregate, hosts and metadata included.
*
* @return the details of the aggregate requested.
*/
HostAggregate getAggregate(String id);
/**
* Creates an aggregate, given its name and availability zone.
*
* @return the newly created Aggregate
*/
HostAggregate createAggregate(String name, String availabilityZone);
/**
* Updates the name of an aggregate.
*/
HostAggregate updateName(String id, String name);
/**
* Updates the availability zone an aggregate.
*/
HostAggregate updateAvailabilityZone(String id, String availabilityZone);
/**
* Removes an aggregate.
*/
Boolean deleteAggregate(String id);
/**
* Adds a host to an aggregate
*/
HostAggregate addHost(String id, String host);
/**
* Removes a host from an aggregate
*/
HostAggregate removeHost(String id, String host);
/**
* Adds metadata to an aggregate
*/
HostAggregate setMetadata(String id, Map<String, String> metadata);
}

View File

@ -74,8 +74,16 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
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"))
.put(URI.create(ExtensionNamespaces.EXTENDED_STATUS),
URI.create("http://docs.openstack.org/compute/ext/extended_status/api/v1.1"))
.put(URI.create(ExtensionNamespaces.AGGREGATES),
URI.create("http://docs.openstack.org/compute/ext/aggregates/api/v1.1"))
.put(URI.create(ExtensionNamespaces.FLAVOR_EXTRA_SPECS),
URI.create("http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1"))
.put(URI.create(ExtensionNamespaces.QUOTAS),
URI.create("http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1"))
.put(URI.create(ExtensionNamespaces.QUOTA_CLASSES),
URI.create("http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1"))
.put(URI.create(ExtensionNamespaces.VOLUME_TYPES),
URI.create("http://docs.openstack.org/compute/ext/volume_types/api/v1.1"))
.build();
@Inject

View File

@ -0,0 +1,180 @@
/**
* 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.assertNull;
import static org.testng.Assert.assertTrue;
import java.net.URI;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.DateService;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.openstack.nova.v1_1.domain.HostAggregate;
import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
/**
* Tests HostAggregateClient guice wiring and parsing
*
* @author Adam Lowe
*/
@Test(groups = "unit", testName = "HostAggregateClientExpectTest")
public class HostAggregateClientExpectTest extends BaseNovaClientExpectTest {
private DateService dateService = new SimpleDateFormatDateService();
public void testList() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_list.json")).build())
.getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
HostAggregate result = Iterables.getOnlyElement(client.listAggregates());
assertEquals(result, exampleHostAggregate());
}
public void testGet() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_with_host_details.json")).build())
.getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertEquals(client.getAggregate("1"), exampleHostAggregateWithHost());
}
public void testGetFailNotFound() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(404).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertNull(client.getAggregate("1"));
}
public void testCreateAggregate() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"aggregate\":{\"name\":\"ubuntu1\",\"availability_zone\":\"nova\"}}", MediaType.APPLICATION_JSON))
.endpoint(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build())
.getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertEquals(client.createAggregate("ubuntu1", "nova"), exampleHostAggregate());
}
public void testDeleteAggregate() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("DELETE").build(),
standardResponseBuilder(200).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.deleteAggregate("1"));
}
public void testDeleteAggregateFailNotFound() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("DELETE").build(),
standardResponseBuilder(404).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.deleteAggregate("1"));
}
public void testUpdateName() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"aggregate\":{\"name\":\"newaggregatename\"}}", MediaType.APPLICATION_JSON)).build(),
standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertEquals(client.updateName("1", "newaggregatename"), exampleHostAggregate());
}
public void testUpdateAvailabilityZone() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"aggregate\":{\"availability_zone\":\"zone1\"}}", MediaType.APPLICATION_JSON)).build(),
standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertEquals(client.updateAvailabilityZone("1", "zone1"), exampleHostAggregate());
}
public void testAddHost() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1/action");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"add_host\":{\"host\":\"ubuntu\"}}", MediaType.APPLICATION_JSON)).build(),
standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertEquals(client.addHost("1", "ubuntu"), exampleHostAggregate());
}
public void testRemoveHost() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1/action");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"remove_host\":{\"host\":\"ubuntu\"}}", MediaType.APPLICATION_JSON)).build(),
standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertEquals(client.removeHost("1", "ubuntu"), exampleHostAggregate());
}
public void testSetMetadata() {
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1/action");
HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"set_metadata\":{\"metadata\":{\"mykey\":\"some value or other\"}}}", MediaType.APPLICATION_JSON)).build(),
standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get();
assertEquals(client.setMetadata("1", ImmutableMap.of("mykey", "some value or other")), exampleHostAggregate());
}
public HostAggregate exampleHostAggregate() {
return HostAggregate.builder().name("jclouds-test-a").availabilityZone("nova")
.created(dateService.iso8601SecondsDateParse("2012-05-11 11:40:17"))
.updated(dateService.iso8601SecondsDateParse("2012-05-11 11:46:44"))
.state("created").id("1").metadata(ImmutableMap.of("somekey", "somevalue", "anotherkey", "another val")).build();
}
public HostAggregate exampleHostAggregateWithHost() {
return exampleHostAggregate().toBuilder().hosts("ubuntu").build();
}
}

View File

@ -0,0 +1,150 @@
/**
* 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 java.util.Map;
import java.util.Set;
import org.jclouds.openstack.nova.v1_1.domain.Host;
import org.jclouds.openstack.nova.v1_1.domain.HostAggregate;
import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest;
import org.testng.annotations.AfterGroups;
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.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* Tests behavior of AggregateClient
*
* @author Adam Lowe
*/
@Test(groups = "live", testName = "AggregateClientLiveTest", singleThreaded = true)
public class HostAggregateClientLiveTest extends BaseNovaClientLiveTest {
private Optional<HostAggregateClient> clientOption;
private Optional<HostAdministrationClient> hostAdminOption;
private HostAggregate testAggregate;
@BeforeGroups(groups = {"integration", "live"})
@Override
public void setupContext() {
super.setupContext();
String zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova");
clientOption = novaContext.getApi().getHostAggregateExtensionForZone(zone);
hostAdminOption = novaContext.getApi().getHostAdministrationExtensionForZone(zone);
}
@Override
@AfterGroups(groups = {"integration", "live"})
public void tearDown() {
if (testAggregate != null) {
assertTrue(clientOption.get().deleteAggregate(testAggregate.getId()));
}
super.tearDown();
}
public void testCreateAggregate() {
if (clientOption.isPresent()) {
// TODO assuming "nova" availability zone is present
testAggregate = clientOption.get().createAggregate("jclouds-test-a", "nova");
}
}
@Test(dependsOnMethods = "testCreateAggregate")
public void testListAndGetAggregate() {
if (clientOption.isPresent()) {
HostAggregateClient client = clientOption.get();
Set<HostAggregate> aggregates = client.listAggregates();
for (HostAggregate aggregate : aggregates) {
assertNotNull(aggregate.getId());
assertNotNull(aggregate.getName());
assertNotNull(aggregate.getAvailabilityZone());
HostAggregate details = client.getAggregate(aggregate.getId());
assertEquals(details.getId(), aggregate.getId());
assertEquals(details.getName(), aggregate.getName());
assertEquals(details.getAvailabilityZone(), aggregate.getAvailabilityZone());
assertEquals(details.getHosts(), aggregate.getHosts());
}
}
}
@Test(dependsOnMethods = "testCreateAggregate")
public void testModifyMetadata() {
if (clientOption.isPresent()) {
HostAggregateClient client = clientOption.get();
for (Map<String, String> theMetaData : ImmutableSet.of(
ImmutableMap.of("somekey", "somevalue"),
ImmutableMap.of("somekey", "some other value", "anotherkey", "another val")
)) {
// Apply changes
HostAggregate details = client.setMetadata(testAggregate.getId(), theMetaData);
// bug in openstack - metadata values are never removed, so we just checking what we've set
for (String key : theMetaData.keySet()) {
assertEquals(details.getMetadata().get(key), theMetaData.get(key));
}
// Re-fetch to double-check
details = client.getAggregate(testAggregate.getId());
for (String key : theMetaData.keySet()) {
assertEquals(details.getMetadata().get(key), theMetaData.get(key));
}
}
}
}
// Note the host will be added, but cannot remove it til
@Test(enabled = false, dependsOnMethods = "testCreateAggregate")
public void testModifyHosts() {
if (clientOption.isPresent() && hostAdminOption.isPresent()) {
HostAggregateClient client = clientOption.get();
Host host = Iterables.getFirst(hostAdminOption.get().listHosts(), null);
assertNotNull(host);
String host_id = host.getName();
assertNotNull(host_id);
HostAggregate details;
try {
details = client.addHost(testAggregate.getId(), host_id);
assertEquals(details.getHosts(), ImmutableSet.of(host_id));
// re-fetch to double-check
details = client.getAggregate(testAggregate.getId());
assertEquals(details.getHosts(), ImmutableSet.of(host_id));
// TODO wait until status of aggregate isn't CHANGING (hostAdministration.shutdown?)
} finally {
details = client.removeHost(testAggregate.getId(), host_id);
}
assertEquals(details.getHosts(), ImmutableSet.of());
}
}
}

View File

@ -0,0 +1 @@
{"aggregate": {"name": "jclouds-test-a", "availability_zone": "nova", "deleted": false, "created_at": "2012-05-11 11:40:17", "updated_at": "2012-05-11 11:46:44", "operational_state": "created", "hosts": [], "deleted_at": null, "id": 1, "metadata": {"somekey": "somevalue", "anotherkey": "another val"}}

View File

@ -0,0 +1 @@
{"aggregates": [{"name": "jclouds-test-a", "availability_zone": "nova", "deleted": false, "created_at": "2012-05-11 11:40:17", "updated_at": "2012-05-11 11:46:44", "operational_state": "created", "hosts": [], "deleted_at": null, "id": 1, "metadata": {"somekey": "somevalue", "anotherkey": "another val"}}]}

View File

@ -0,0 +1 @@
{"aggregate": {"name": "jclouds-test-a", "availability_zone": "nova", "deleted": false, "created_at": "2012-05-11 11:40:17", "updated_at": "2012-05-11 11:46:44", "operational_state": "created", "hosts": ["ubuntu"], "deleted_at": null, "id": 1, "metadata": {"somekey": "somevalue", "anotherkey": "another val"}}