diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostAsyncClient.java index a6fc4d05f0..871818ae21 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostAsyncClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostAsyncClient.java @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.ListenableFuture; import org.jclouds.cloudstack.domain.Cluster; import org.jclouds.cloudstack.domain.Host; import org.jclouds.cloudstack.filters.QuerySigner; +import org.jclouds.cloudstack.options.AddHostOptions; import org.jclouds.cloudstack.options.ListClustersOptions; import org.jclouds.cloudstack.options.ListHostsOptions; import org.jclouds.rest.annotations.ExceptionParser; @@ -32,6 +33,7 @@ import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import java.util.Set; @@ -57,6 +59,23 @@ public interface GlobalHostAsyncClient { @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) ListenableFuture> listHosts(ListHostsOptions... options); + /** + * Adds a new host. + * + * @param zoneId the Zone ID for the host + * @param url the host URL + * @param hypervisor hypervisor type of the host + * @param username the username for the host + * @param password the password for the host + * @param options optional arguments + * @return the new host. + */ + @GET + @QueryParams(keys = "command", values = "addHost") + @SelectJson("host") + @Consumes(MediaType.APPLICATION_JSON) + ListenableFuture addHost(@QueryParam("zoneid") long zoneId, @QueryParam("url") String url, @QueryParam("hypervisor") String hypervisor, @QueryParam("username") String username, @QueryParam("password") String password, AddHostOptions... options); + /** * @see GlobalHostClient#listClusters */ diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostClient.java index 414ce8b6fa..6006c91458 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostClient.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack.features; import org.jclouds.cloudstack.domain.Cluster; import org.jclouds.cloudstack.domain.Host; +import org.jclouds.cloudstack.options.AddHostOptions; import org.jclouds.cloudstack.options.ListClustersOptions; import org.jclouds.cloudstack.options.ListHostsOptions; import org.jclouds.concurrent.Timeout; @@ -48,6 +49,19 @@ public interface GlobalHostClient { */ Set listHosts(ListHostsOptions... options); + /** + * Adds a new host. + * + * @param zoneId the Zone ID for the host + * @param url the host URL + * @param hypervisor hypervisor type of the host + * @param username the username for the host + * @param password the password for the host + * @param options optional arguments + * @return the new host. + */ + Host addHost(long zoneId, String url, String hypervisor, String username, String password, AddHostOptions... options); + /** * Lists clusters * diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/AddHostOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/AddHostOptions.java new file mode 100644 index 0000000000..5cdf4bb70b --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/AddHostOptions.java @@ -0,0 +1,115 @@ +/** + * 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.cloudstack.options; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; +import org.jclouds.cloudstack.domain.Host; +import org.jclouds.http.options.BaseHttpRequestOptions; + +import java.util.Set; + +/** + * Options to the GlobalHostClient.addHost() API call + * + * @author Richard Downer + */ +public class AddHostOptions extends BaseHttpRequestOptions { + + public static final AddHostOptions NONE = new AddHostOptions(); + + /** + * @param allocationState Allocation state of this Host for allocation of new resources + */ + public AddHostOptions allocationState(Host.AllocationState allocationState) { + this.queryParameters.replaceValues("allocationstate", ImmutableSet.of(allocationState.toString())); + return this; + } + + /** + * @param clusterId the cluster ID for the host + */ + public AddHostOptions clusterId(long clusterId) { + this.queryParameters.replaceValues("clusterid", ImmutableSet.of(clusterId + "")); + return this; + } + + /** + * @param clusterName the cluster name for the host + */ + public AddHostOptions clusterName(String clusterName) { + this.queryParameters.replaceValues("clustername", ImmutableSet.of(clusterName)); + return this; + } + + /** + * @param hostTags list of tags to be added to the host + */ + public AddHostOptions hostTags(Set hostTags) { + this.queryParameters.replaceValues("hosttags", ImmutableSet.of(Joiner.on(',').join(hostTags))); + return this; + } + + /** + * @param podId the Pod ID for the host + */ + public AddHostOptions podId(long podId) { + this.queryParameters.replaceValues("podid", ImmutableSet.of(podId + "")); + return this; + } + + public static class Builder { + + /** + * @param allocationState Allocation state of this Host for allocation of new resources + */ + public static AddHostOptions allocationState(Host.AllocationState allocationState) { + return new AddHostOptions().allocationState(allocationState); + } + + /** + * @param clusterId the cluster ID for the host + */ + public static AddHostOptions clusterId(long clusterId) { + return new AddHostOptions().clusterId(clusterId); + } + + /** + * @param clusterName the cluster name for the host + */ + public static AddHostOptions clusterName(String clusterName) { + return new AddHostOptions().clusterName(clusterName); + } + + /** + * @param hostTags list of tags to be added to the host + */ + public static AddHostOptions hostTags(Set hostTags) { + return new AddHostOptions().hostTags(hostTags); + } + + /** + * @param podId the Pod ID for the host + */ + public static AddHostOptions podId(long podId) { + return new AddHostOptions().podId(podId); + } + + } +} diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientExpectTest.java index 89ac050f04..a2c5c82490 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientExpectTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientExpectTest.java @@ -25,12 +25,14 @@ import org.jclouds.cloudstack.CloudStackContext; import org.jclouds.cloudstack.domain.Cluster; import org.jclouds.cloudstack.domain.ConfigurationEntry; import org.jclouds.cloudstack.domain.Host; +import org.jclouds.cloudstack.options.AddHostOptions; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.testng.annotations.Test; import java.net.URI; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.Set; import java.util.TimeZone; @@ -94,6 +96,27 @@ public class GlobalHostClientExpectTest extends BaseCloudStackRestClientExpectTe assertEquals(client.listHosts(), ImmutableSet.of()); } + @Test + public void testAddHostWhenResponseIs2xx() { + HttpRequest request = HttpRequest.builder() + .method("GET") + .endpoint(URI.create("http://localhost:8080/client/api?response=json&command=addHost&zoneid=1&hypervisor=XenServer&url=http%3A%2F%2Fexample.com&username=fred&password=sekrit&hosttags=&allocationstate=Enabled&clusterid=1&clustername=Xen%20Clust%201&podid=1&apiKey=identity&signature=ExGaljKKQIlVbWk5hd0BnnjmBzs%3D")) + .headers(ImmutableMultimap.builder().put("Accept", "application/json").build()) + .build(); + HttpResponse response = HttpResponse.builder() + .payload(payloadFromResource("/addhostresponse.json")) + .statusCode(200).build(); + + Date lastPinged = makeDate(1970, Calendar.JANUARY, 16, 0, 54, 43, "UTC"); + Date created = makeDate(2011, Calendar.NOVEMBER, 26, 23, 28, 36, "UTC"); + Host expected = Host.builder().id(1).name("cs2-xevsrv.alucloud.local").state(Host.State.UP).type(Host.Type.ROUTING).ipAddress("10.26.26.107").zoneId(1).zoneName("Dev Zone 1").podId(1).podName("Dev Pod 1").version("2.2.12.20110928142833").hypervisor("XenServer").cpuNumber(24).cpuSpeed(2266).cpuAllocated("2.76%").cpuUsed("0.1%").cpuWithOverProvisioning(54384.0F).networkKbsRead(4443).networkKbsWrite(15048).memoryTotal(100549733760L).memoryAllocated(3623878656L).memoryUsed(3623878656L).capabilities("xen-3.0-x86_64 , xen-3.0-x86_32p , hvm-3.0-x86_32 , hvm-3.0-x86_32p , hvm-3.0-x86_64").lastPinged(lastPinged).managementServerId(223098941760041L).clusterId(1).clusterName("Xen Clust 1").clusterType(Host.ClusterType.CLOUD_MANAGED).localStorageActive(false).created(created).events("PrepareUnmanaged; HypervisorVersionChanged; ManagementServerDown; PingTimeout; AgentDisconnected; MaintenanceRequested; HostDown; AgentConnected; StartAgentRebalance; ShutdownRequested; Ping").hostTags("").hasEnoughCapacity(false).allocationState(Host.AllocationState.ENABLED).build(); + + Host actual = requestSendsResponse(request, response).addHost(1, "http://example.com", "XenServer", "fred", "sekrit", + AddHostOptions.Builder.hostTags(Collections.emptySet()).allocationState(Host.AllocationState.ENABLED).clusterId(1).clusterName("Xen Clust 1").podId(1)); + + assertEquals(actual, expected); + } + @Test public void testListClustersWhenResponseIs2xx() { HttpRequest request = HttpRequest.builder() diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/AddHostOptionsTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/AddHostOptionsTest.java new file mode 100644 index 0000000000..7dfd22fb7e --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/AddHostOptionsTest.java @@ -0,0 +1,87 @@ +/** + * 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.cloudstack.options; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import org.jclouds.cloudstack.domain.Host; +import org.testng.annotations.Test; + +import static org.jclouds.cloudstack.options.AddHostOptions.Builder.*; +import static org.testng.Assert.assertEquals; + +/** + * Tests behavior of {@code AddHostOptions} + * + * @author Richard Downer + */ +@Test(groups = "unit") +public class AddHostOptionsTest { + + public void testAllocationState() { + AddHostOptions options = new AddHostOptions().allocationState(Host.AllocationState.ENABLED); + assertEquals(ImmutableList.of("Enabled"), options.buildQueryParameters().get("allocationstate")); + } + + public void testAllocationStateStatic() { + AddHostOptions options = allocationState(Host.AllocationState.ENABLED); + assertEquals(ImmutableList.of("Enabled"), options.buildQueryParameters().get("allocationstate")); + } + + public void testClusterId() { + AddHostOptions options = new AddHostOptions().clusterId(42L); + assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("clusterid")); + } + + public void testClusterIdStatic() { + AddHostOptions options = clusterId(42L); + assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("clusterid")); + } + + public void testClusterName() { + AddHostOptions options = new AddHostOptions().clusterName("Cluster Name"); + assertEquals(ImmutableList.of("Cluster Name"), options.buildQueryParameters().get("clustername")); + } + + public void testClusterNameStatic() { + AddHostOptions options = clusterName("Cluster Name"); + assertEquals(ImmutableList.of("Cluster Name"), options.buildQueryParameters().get("clustername")); + } + + public void testHostTags() { + AddHostOptions options = new AddHostOptions().hostTags(ImmutableSet.of("foo", "bar", "baz")); + assertEquals(ImmutableList.of("foo,bar,baz"), options.buildQueryParameters().get("hosttags")); + } + + public void testHostTagsStatic() { + AddHostOptions options = hostTags(ImmutableSet.of("foo", "bar", "baz")); + assertEquals(ImmutableList.of("foo,bar,baz"), options.buildQueryParameters().get("hosttags")); + } + + public void testPodId() { + AddHostOptions options = new AddHostOptions().podId(42L); + assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("podid")); + } + + public void testPodIdStatic() { + AddHostOptions options = podId(42L); + assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("podid")); + } + +} diff --git a/apis/cloudstack/src/test/resources/addhostresponse.json b/apis/cloudstack/src/test/resources/addhostresponse.json new file mode 100644 index 0000000000..7508410682 --- /dev/null +++ b/apis/cloudstack/src/test/resources/addhostresponse.json @@ -0,0 +1,2 @@ +{ "addhostresponse" : { "host" : + {"warning":"This test data is fabricated","id":1,"name":"cs2-xevsrv.alucloud.local","state":"Up","type":"Routing","ipaddress":"10.26.26.107","zoneid":1,"zonename":"Dev Zone 1","podid":1,"podname":"Dev Pod 1","version":"2.2.12.20110928142833","hypervisor":"XenServer","cpunumber":24,"cpuspeed":2266,"cpuallocated":"2.76%","cpuused":"0.1%","cpuwithoverprovisioning":"54384.0","networkkbsread":4443,"networkkbswrite":15048,"memorytotal":100549733760,"memoryallocated":3623878656,"memoryused":3623878656,"capabilities":"xen-3.0-x86_64 , xen-3.0-x86_32p , hvm-3.0-x86_32 , hvm-3.0-x86_32p , hvm-3.0-x86_64","lastpinged":"1970-01-16T00:54:43+0200","managementserverid":223098941760041,"clusterid":1,"clustername":"Xen Clust 1","clustertype":"CloudManaged","islocalstorageactive":false,"created":"2011-11-26T23:28:36+0200","events":"PrepareUnmanaged; HypervisorVersionChanged; ManagementServerDown; PingTimeout; AgentDisconnected; MaintenanceRequested; HostDown; AgentConnected; StartAgentRebalance; ShutdownRequested; Ping","hosttags":"","hasEnoughCapacity":false,"allocationstate":"Enabled"} } } \ No newline at end of file