Implemented Nova compute attach interfaces extension.

This commit is contained in:
Evgeny Tarasenko 2014-08-27 19:34:37 +07:00 committed by Jeremy Daggett
parent cc277e7e69
commit a356ca9991
11 changed files with 629 additions and 6 deletions

View File

@ -21,6 +21,7 @@ import java.util.Set;
import org.jclouds.location.Region;
import org.jclouds.location.functions.RegionToEndpoint;
import org.jclouds.openstack.nova.v2_0.extensions.AttachInterfaceApi;
import org.jclouds.openstack.nova.v2_0.extensions.AvailabilityZoneApi;
import org.jclouds.openstack.nova.v2_0.extensions.ConsolesApi;
import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsApi;
@ -273,6 +274,13 @@ public interface NovaApi extends Closeable {
Optional<FloatingIPPoolApi> getFloatingIPPoolApi(
@EndpointParam(parser = RegionToEndpoint.class) String region);
/**
* Provides access to attach interface features.
*/
@Delegate
Optional<? extends AttachInterfaceApi> getAttachInterfaceApi(
@EndpointParam(parser = RegionToEndpoint.class) String region);
/**
* @return the Zone codes configured
* @deprecated Please use {@link #getConfiguredRegions()} instead. To be removed in jclouds 2.0.

View File

@ -93,6 +93,8 @@ public class NovaHttpApiModule extends HttpApiModule<NovaApi> {
URI.create("http://docs.openstack.org/compute/ext/availabilityzone/api/v1.1"))
.put(URI.create(ExtensionNamespaces.VOLUME_ATTACHMENTS),
URI.create("http://docs.openstack.org/compute/ext/os-volume-attachment-update/api/v2"))
.put(URI.create(ExtensionNamespaces.ATTACH_INTERFACES),
URI.create("http://docs.openstack.org/compute/ext/interfaces/api/v1.1"))
.build();
}

View File

@ -0,0 +1,136 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.openstack.nova.v2_0.domain;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import org.jclouds.javax.annotation.Nullable;
import javax.inject.Named;
import java.beans.ConstructorProperties;
/**
* Describes an fixed IP address
*/
public class FixedIP {
@Named("ip_address")
protected final String ipAddress;
@Named("subnet_id")
protected final String subnetId;
@ConstructorProperties({ "ip_address", "subnet_id" })
protected FixedIP(String ipAddress, String subnetId) {
this.ipAddress = ipAddress;
this.subnetId = subnetId;
}
/**
* @return the ipAddress of the IP
*/
@Nullable
public String getIpAddress() {
return ipAddress;
}
/**
* @return the subnetId of the IP
*/
@Nullable
public String getSubnetId() {
return subnetId;
}
@Override
public int hashCode() {
return Objects.hashCode(ipAddress, subnetId);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
FixedIP that = FixedIP.class.cast(obj);
return Objects.equal(this.ipAddress, that.ipAddress) && Objects.equal(this.subnetId, that.subnetId);
}
protected ToStringHelper string() {
return Objects.toStringHelper(this).add("ipAddress", ipAddress).add("subnetId", subnetId);
}
@Override
public String toString() {
return string().toString();
}
/**
* @return the Builder for IP
*/
public static Builder builder() {
return new Builder();
}
/**
* Gets a Builder configured as this object.
*/
public Builder toBuilder() {
return new Builder().fromIP(this);
}
public static class Builder {
protected String ipAddress;
protected String subnetId;
/**
* Provide the ipAddress to the IP's Builder.
*
* @return the Builder.
* @see FixedIP#getIpAddress()
*/
public Builder ipAddress(String ipAddress) {
this.ipAddress = ipAddress;
return this;
}
/**
* Provide the subnetId to the IP's Builder.
*
* @return the Builder.
* @see FixedIP#getSubnetId()
*/
public Builder subnetId(String subnetId) {
this.subnetId = subnetId;
return this;
}
/**
* @return a IP constructed with this Builder.
*/
public FixedIP build() {
return new FixedIP(ipAddress, subnetId);
}
/**
* @return a Builder from another IP.
*/
public Builder fromIP(FixedIP in) {
return this.ipAddress(in.getIpAddress()).subnetId(in.getSubnetId());
}
}
}

View File

@ -0,0 +1,173 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.openstack.nova.v2_0.domain;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import javax.inject.Named;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
public class InterfaceAttachment {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromInterfaceAttachment(this);
}
public abstract static class Builder<T extends Builder<T>> {
protected abstract T self();
private String networkId;
private String portId;
private PortState portState;
private String macAddress;
private ImmutableSet<FixedIP> fixedIps;
/**
* @see InterfaceAttachment#getNetworkId()
*/
public T networkId(String networkId) {
this.networkId = networkId;
return self();
}
/**
* @see InterfaceAttachment#getPortId()
*/
public T portId(String portId) {
this.portId = portId;
return self();
}
/**
* @see InterfaceAttachment#getPortState()
*/
public T portState(PortState portState) {
this.portState = portState;
return self();
}
/**
* @see InterfaceAttachment#getMacAddress()
*/
public T macAddress(String macAddress) {
this.macAddress = macAddress;
return self();
}
/**
* @see InterfaceAttachment#getFixedIps()
*/
public T fixedIps(ImmutableSet<FixedIP> fixedIps) {
this.fixedIps = fixedIps;
return self();
}
public InterfaceAttachment build() {
return new InterfaceAttachment(networkId, portId, portState, macAddress, fixedIps);
}
public T fromInterfaceAttachment(InterfaceAttachment in) {
return this.networkId(in.getNetworkId()).portId(in.getPortId()).portState(in.getPortState())
.macAddress(in.getMacAddress()).fixedIps(in.getFixedIps());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
@Named("net_id")
private String networkId;
@Named("port_id")
private String portId;
@Named("port_state")
private PortState portState;
@Named("mac_addr")
private String macAddress;
@Named("fixed_ips")
private ImmutableSet<FixedIP> fixedIps;
@ConstructorProperties({ "net_id", "port_id", "port_state", "mac_addr", "fixed_ips" })
protected InterfaceAttachment(String networkId, String portId, PortState portState,
String macAddress, ImmutableSet<FixedIP> fixedIps) {
this.networkId = networkId;
this.portId = checkNotNull(portId, "portId");
this.portState = portState;
this.macAddress = macAddress;
this.fixedIps = fixedIps;
}
public String getNetworkId() {
return this.networkId;
}
public String getPortId() {
return this.portId;
}
public PortState getPortState() {
return this.portState;
}
public String getMacAddress() {
return this.macAddress;
}
public ImmutableSet<FixedIP> getFixedIps() {
return this.fixedIps;
}
@Override
public int hashCode() {
return Objects.hashCode(networkId, portId, portState, macAddress, fixedIps);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
InterfaceAttachment that = InterfaceAttachment.class.cast(obj);
return Objects.equal(this.networkId, that.networkId) && Objects.equal(this.portId, that.portId)
&& Objects.equal(this.portState, that.portState) && Objects.equal(this.macAddress, that.macAddress)
&& Objects.equal(this.fixedIps, that.fixedIps);
}
protected Objects.ToStringHelper string() {
return toStringHelper(this).add("networkId", networkId).add("portId", portId).add("portState", portState)
.add("macAddress", macAddress).add("fixedIps", fixedIps);
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.openstack.nova.v2_0.domain;
public enum PortState {
ACTIVE("active"),
DOWN("down"),
BUILD("build"),
ERROR("error"),
/**
* Used by jclouds when the service returns an unknown value other than null.
*/
UNRECOGNIZED("unrecognized");
private final String name;
private PortState(String name) {
this.name = name;
}
@Override
public String toString() {
return name();
}
/**
* This provides GSON enum support in jclouds.
* @param name The string representation of this enum value.
* @return The corresponding enum value.
*/
public static PortState fromValue(String name) {
if (name != null) {
for (PortState value : PortState.values()) {
if (name.equalsIgnoreCase(value.name)) {
return value;
}
}
return UNRECOGNIZED;
}
return null;
}
}

View File

@ -0,0 +1,115 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.openstack.nova.v2_0.extensions;
import com.google.common.annotations.Beta;
import com.google.common.collect.FluentIterable;
import org.jclouds.Fallbacks;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.nova.v2_0.domain.InterfaceAttachment;
import org.jclouds.openstack.v2_0.ServiceType;
import org.jclouds.openstack.v2_0.services.Extension;
import org.jclouds.rest.annotations.Fallback;
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 javax.inject.Named;
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;
/**
* Provides access to the OpenStack Compute (Nova) Attach Interfaces API.
*/
@Beta
@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.ATTACH_INTERFACES)
@RequestFilters(AuthenticateRequest.class)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/servers")
public interface AttachInterfaceApi {
/**
* Returns list of port interfaces for given server
*
* @param serverId
* The Server ID
* @return list of port interfaces for given server
*/
@Named("attachInterface:list")
@GET
@Path("/{serverId}/os-interface")
@SelectJson("interfaceAttachments")
@Fallback(Fallbacks.EmptyFluentIterableOnNotFoundOr404.class)
FluentIterable<InterfaceAttachment> list(@PathParam("serverId") String serverId);
/**
* Returns information about a specified port interface for given server
*
* @param serverId
* The Server ID
* @param attachmentId
* The interface ID
* @return information about a specified port interface for given server
*/
@Named("attachInterface:get")
@GET
@Path("/{serverId}/os-interface/{attachmentId}")
@SelectJson("interfaceAttachment")
@Fallback(Fallbacks.NullOnNotFoundOr404.class)
@Nullable
InterfaceAttachment get(@PathParam("serverId") String serverId, @PathParam("attachmentId") String attachmentId);
/**
* Creates a new port interface and associate with the given port
*
* @param portId
* The port ID
* @return newly created port interface
*/
@Named("attachInterface:create")
@POST
@Path("/{serverId}/os-interface")
@SelectJson("interfaceAttachment")
@Payload("%7B\"interfaceAttachment\":%7B\"port_id\":\"{portId}\"%7D%7D")
@Produces(MediaType.APPLICATION_JSON)
InterfaceAttachment create(@PathParam("serverId") String serverId, @PayloadParam("portId") String portId);
/**
* Deletes a port interface for given server, return true if successful,
* false if server or interface not found
*
* @param serverId
* The Server ID
* @param attachmentId
* The interface ID
* @return true if successful, false if server or interface not found
*/
@Named("attachInterface:delete")
@DELETE
@Path("/{serverId}/os-interface/{attachmentId}")
@Fallback(Fallbacks.FalseOnNotFoundOr404.class)
boolean delete(@PathParam("serverId") String serverId, @PathParam("attachmentId") String attachmentId);
}

View File

@ -118,6 +118,11 @@ public final class ExtensionNamespaces {
*/
public static final String FLOATING_IP_POOLS = "http://docs.openstack.org/ext/floating_ip_pools/api/v1.1";
/**
* Attach interfaces extension
*/
public static final String ATTACH_INTERFACES = "http://docs.openstack.org/compute/ext/interfaces/api/v1.1";
private ExtensionNamespaces() {
throw new AssertionError("intentionally unimplemented");
}

View File

@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.openstack.nova.v2_0.extensions;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.domain.FixedIP;
import org.jclouds.openstack.nova.v2_0.domain.InterfaceAttachment;
import org.jclouds.openstack.nova.v2_0.domain.PortState;
import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiExpectTest;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@Test(groups = "unit", testName = "AttachInterfaceApiExpectTest")
public class AttachInterfaceApiExpectTest extends BaseNovaApiExpectTest {
public void testAttachInterfacesList() throws Exception {
HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456/servers/instance-1/os-interface")
.addHeader("Accept", "application/json").addHeader("X-Auth-Token", authToken).build();
HttpResponse listResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/attach_interfaces_list.json")).build();
NovaApi novaApi = requestsSendResponses(keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, list, listResponse);
FluentIterable<InterfaceAttachment> interfaceAttachments = novaApi.getAttachInterfaceApi("az-1.region-a.geo-1")
.get().list("instance-1");
Optional<? extends InterfaceAttachment> interfaceAttachment = interfaceAttachments.first();
assertTrue(interfaceAttachment.isPresent(), "Couldn't find interface attachment");
assertEquals(interfaceAttachment.get(), testInterfaceAttachment());
}
public void testAttachInterfaceGet() throws Exception {
HttpRequest list = HttpRequest
.builder()
.method("GET")
.endpoint(
"https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456/servers/instance-1/os-interface/ce531f90-199f-48c0-816c-13e38010b442")
.addHeader("Accept", "application/json").addHeader("X-Auth-Token", authToken).build();
HttpResponse listResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/attach_interface_details.json")).build();
NovaApi novaApi = requestsSendResponses(keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, list, listResponse);
InterfaceAttachment interfaceAttachment = novaApi.getAttachInterfaceApi("az-1.region-a.geo-1").get()
.get("instance-1", "ce531f90-199f-48c0-816c-13e38010b442");
assertEquals(interfaceAttachment, testInterfaceAttachment());
}
private InterfaceAttachment testInterfaceAttachment() {
return InterfaceAttachment
.builder()
.portId("ce531f90-199f-48c0-816c-13e38010b442")
.networkId("3cb9bc59-5699-4588-a4b1-b87f96708bc6")
.portState(PortState.ACTIVE)
.macAddress("fa:16:3e:4c:2c:30")
.fixedIps(
ImmutableSet.of(FixedIP.builder().ipAddress("192.168.1.3")
.subnetId("f8a6e8f8-c2ec-497c-9f23-da9616de54ef").build())).build();
}
}

View File

@ -0,0 +1,14 @@
{
"interfaceAttachment": {
"port_state": "ACTIVE",
"fixed_ips": [
{
"subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef",
"ip_address": "192.168.1.3"
}
],
"net_id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6",
"port_id": "ce531f90-199f-48c0-816c-13e38010b442",
"mac_addr": "fa:16:3e:4c:2c:30"
}
}

View File

@ -0,0 +1,16 @@
{
"interfaceAttachments": [
{
"port_state": "ACTIVE",
"fixed_ips": [
{
"subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef",
"ip_address": "192.168.1.3"
}
],
"net_id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6",
"port_id": "ce531f90-199f-48c0-816c-13e38010b442",
"mac_addr": "fa:16:3e:4c:2c:30"
}
]
}

View File

@ -281,12 +281,12 @@
"description": "1. Add availability_zone to the Create Server v1.1 API.\n 2. Add availability zones describing.\n "
},
{
"updated":"2013-07-08T00:00:00+00:00",
"name":"BlockDeviceMappingV2Boot",
"links":[],
"namespace":"http://docs.openstack.org/compute/ext/block_device_mapping_v2_boot/api/v2",
"alias":"os-block-device-mapping-v2-boot",
"description":"Allow boot with the new BDM data format."
"updated": "2013-07-08T00:00:00+00:00",
"name": "BlockDeviceMappingV2Boot",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/block_device_mapping_v2_boot/api/v2",
"alias": "os-block-device-mapping-v2-boot",
"description": "Allow boot with the new BDM data format."
},
{
"alias": "os-volume-attachment-update",
@ -295,6 +295,14 @@
"name": "VolumeAttachmentUpdate",
"namespace": "http://docs.openstack.org/compute/ext/os-volume-attachment-update/api/v2",
"updated": "2013-06-20T00:00:00Z"
},
{
"updated": "2012-07-22T00:00:00Z",
"name": "AttachInterfaces",
"links": [],
"namespace": "http://docs.openstack.org/compute/ext/interfaces/api/v1.1",
"alias": "os-attach-interfaces",
"description": "Attach interface support."
}
]
}