From f5c9ba6c0a9bdc2a285386f09e1d02707d30c260 Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Fri, 16 Dec 2011 15:17:05 +0000 Subject: [PATCH 1/7] Add empty feature for Usage (global admin) --- .../CloudStackGlobalAsyncClient.java | 6 +++ .../cloudstack/CloudStackGlobalClient.java | 6 +++ .../config/CloudStackRestClientModule.java | 3 ++ .../features/GlobalUsageAsyncClient.java | 49 +++++++++++++++++++ .../features/GlobalUsageClient.java | 42 ++++++++++++++++ .../features/GlobalUsageAsyncClientTest.java | 47 ++++++++++++++++++ 6 files changed, 153 insertions(+) create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClient.java create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java create mode 100644 apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalAsyncClient.java index fe7af3cff1..677309887c 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalAsyncClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalAsyncClient.java @@ -24,6 +24,7 @@ import org.jclouds.cloudstack.features.GlobalCapacityAsyncClient; import org.jclouds.cloudstack.features.GlobalHostAsyncClient; import org.jclouds.cloudstack.features.GlobalOfferingAsyncClient; import org.jclouds.cloudstack.features.GlobalStoragePoolAsyncClient; +import org.jclouds.cloudstack.features.GlobalUsageAsyncClient; import org.jclouds.cloudstack.features.GlobalUserAsyncClient; import org.jclouds.rest.annotations.Delegate; @@ -84,4 +85,9 @@ public interface CloudStackGlobalAsyncClient extends CloudStackDomainAsyncClient @Delegate GlobalStoragePoolAsyncClient getStoragePoolClient(); + /** + * Provides asynchronous access to Usage + */ + @Delegate + GlobalUsageAsyncClient getUsageClient(); } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalClient.java index fa05591ad6..a1f73671dc 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalClient.java @@ -26,6 +26,7 @@ import org.jclouds.cloudstack.features.GlobalCapacityClient; import org.jclouds.cloudstack.features.GlobalHostClient; import org.jclouds.cloudstack.features.GlobalOfferingClient; import org.jclouds.cloudstack.features.GlobalStoragePoolClient; +import org.jclouds.cloudstack.features.GlobalUsageClient; import org.jclouds.cloudstack.features.GlobalUserClient; import org.jclouds.concurrent.Timeout; import org.jclouds.rest.annotations.Delegate; @@ -88,4 +89,9 @@ public interface CloudStackGlobalClient extends CloudStackDomainClient { @Delegate GlobalStoragePoolClient getStoragePoolClient(); + /** + * Provides synchronous access to Usage + */ + @Delegate + GlobalUsageClient getUsageClient(); } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java index 92a2f1fe60..5ac8a16919 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java @@ -60,6 +60,8 @@ import org.jclouds.cloudstack.features.GlobalOfferingAsyncClient; import org.jclouds.cloudstack.features.GlobalOfferingClient; import org.jclouds.cloudstack.features.GlobalStoragePoolAsyncClient; import org.jclouds.cloudstack.features.GlobalStoragePoolClient; +import org.jclouds.cloudstack.features.GlobalUsageAsyncClient; +import org.jclouds.cloudstack.features.GlobalUsageClient; import org.jclouds.cloudstack.features.GlobalUserAsyncClient; import org.jclouds.cloudstack.features.GlobalUserClient; import org.jclouds.cloudstack.features.GuestOSAsyncClient; @@ -155,6 +157,7 @@ public class CloudStackRestClientModule extends RestClientModule + * + * @see GlobalOfferingAsyncClient + * @see + * @author Richard Downer + */ +@RequestFilters(QuerySigner.class) +@QueryParams(keys = "response", values = "json") +public interface GlobalUsageAsyncClient { + +} diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java new file mode 100644 index 0000000000..ccb9e34158 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java @@ -0,0 +1,42 @@ +/** + * 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.features; + +import org.jclouds.cloudstack.domain.JobResult; +import org.jclouds.cloudstack.domain.UsageRecord; +import org.jclouds.cloudstack.options.GenerateUsageRecordsOptions; +import org.jclouds.cloudstack.options.ListUsageRecordsOptions; +import org.jclouds.concurrent.Timeout; + +import java.util.Date; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Provides synchronous access to CloudStack usage features. + *

+ * + * @see org.jclouds.cloudstack.features.GlobalOfferingAsyncClient + * @see + * @author Richard Downer + */ +@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) +public interface GlobalUsageClient { + +} diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java new file mode 100644 index 0000000000..4d11519c63 --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java @@ -0,0 +1,47 @@ +/** + * 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.features; + +import com.google.inject.TypeLiteral; +import org.jclouds.cloudstack.options.GenerateUsageRecordsOptions; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.functions.ParseFirstJsonValueNamed; +import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions; +import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.testng.annotations.Test; + +import java.lang.reflect.Method; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** + * Tests behavior of {@code GlobalUsageAsyncClient} + * + * @author Richard Downer + */ +@Test(groups = "unit", testName = "GlobalUsageAsyncClientTest") +public class GlobalUsageAsyncClientTest extends BaseCloudStackAsyncClientTest { + + @Override + protected TypeLiteral> createTypeLiteral() { + return new TypeLiteral>() { + }; + } +} From e64807ee59c5d681f58bcc7ff80a868a037115dd Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Fri, 16 Dec 2011 15:17:27 +0000 Subject: [PATCH 2/7] Add GenerateUsageRecordsOptions + test --- .../options/GenerateUsageRecordsOptions.java | 44 +++++++++++++++++++ .../GenerateUsageRecordsOptionsTest.java | 44 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/GenerateUsageRecordsOptions.java create mode 100644 apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/GenerateUsageRecordsOptionsTest.java diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/GenerateUsageRecordsOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/GenerateUsageRecordsOptions.java new file mode 100644 index 0000000000..58e01e434e --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/GenerateUsageRecordsOptions.java @@ -0,0 +1,44 @@ +/** + * 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.ImmutableSet; +import org.jclouds.http.options.BaseHttpRequestOptions; + +/** + * Options to the GlobalUsageClient.generateUsageOptions() API call + * + * @author Richard Downer + */ +public class GenerateUsageRecordsOptions extends BaseHttpRequestOptions { + + public static final GenerateUsageRecordsOptions NONE = new GenerateUsageRecordsOptions(); + + public static class Builder { + public static GenerateUsageRecordsOptions domainId(long domainId) { + GenerateUsageRecordsOptions options = new GenerateUsageRecordsOptions(); + return options.domainId(domainId); + } + } + + public GenerateUsageRecordsOptions domainId(long domainId) { + this.queryParameters.replaceValues("domainid", ImmutableSet.of(domainId + "")); + return this; + } +} diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/GenerateUsageRecordsOptionsTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/GenerateUsageRecordsOptionsTest.java new file mode 100644 index 0000000000..d06407afae --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/GenerateUsageRecordsOptionsTest.java @@ -0,0 +1,44 @@ +/** + * 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.ImmutableSet; +import org.testng.annotations.Test; + +import static org.jclouds.cloudstack.options.GenerateUsageRecordsOptions.Builder.domainId; +import static org.testng.Assert.assertEquals; + +/** + * Tests behavior of {@code GenerateUsageRecordsOptions} + * + * @author Richard Downer + */ +@Test(groups = "unit") +public class GenerateUsageRecordsOptionsTest { + + public void testDomainId() { + GenerateUsageRecordsOptions options = new GenerateUsageRecordsOptions().domainId(42); + assertEquals(ImmutableSet.of("42"), options.buildQueryParameters().get("domainid")); + } + + public void testDomainIdStatic() { + GenerateUsageRecordsOptions options = domainId(42); + assertEquals(ImmutableSet.of("42"), options.buildQueryParameters().get("domainid")); + } +} From 41329c3528928c7cbb928e1ecbf4741aa824538e Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Fri, 16 Dec 2011 15:18:15 +0000 Subject: [PATCH 3/7] Add generateUsageRecords API call and tests --- .../features/GlobalUsageAsyncClient.java | 12 +++-- .../features/GlobalUsageClient.java | 2 + .../cloudstack/functions/DateToYyyyMmDd.java | 43 ++++++++++++++++ .../features/GlobalUsageAsyncClientTest.java | 50 +++++++++++++++++++ 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/DateToYyyyMmDd.java diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClient.java index 1148a5482d..1e2a27b820 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClient.java @@ -19,18 +19,18 @@ package org.jclouds.cloudstack.features; import com.google.common.util.concurrent.ListenableFuture; -import org.jclouds.cloudstack.binders.BindEndDateAsYyyyMmDdToQueryParams; -import org.jclouds.cloudstack.binders.BindStartDateAsYyyyMmDdToQueryParams; import org.jclouds.cloudstack.domain.JobResult; import org.jclouds.cloudstack.filters.QuerySigner; +import org.jclouds.cloudstack.functions.DateToYyyyMmDd; import org.jclouds.cloudstack.options.GenerateUsageRecordsOptions; -import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.ParamParser; import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import java.util.Date; @@ -46,4 +46,10 @@ import java.util.Date; @QueryParams(keys = "response", values = "json") public interface GlobalUsageAsyncClient { + @GET + @QueryParams(keys = "command", values = "generateUsageRecords") + @SelectJson("generateusagerecordsresponse") + @Consumes(MediaType.APPLICATION_JSON) + ListenableFuture generateUsageRecords(@QueryParam("startdate") @ParamParser(DateToYyyyMmDd.class) Date start, @QueryParam("enddate") @ParamParser(DateToYyyyMmDd.class) Date end, GenerateUsageRecordsOptions... options); + } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java index ccb9e34158..bf79959721 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java @@ -39,4 +39,6 @@ import java.util.concurrent.TimeUnit; @Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) public interface GlobalUsageClient { + JobResult generateUsageRecords(Date start, Date end, GenerateUsageRecordsOptions... options); + } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/DateToYyyyMmDd.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/DateToYyyyMmDd.java new file mode 100644 index 0000000000..a92d2b02ec --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/DateToYyyyMmDd.java @@ -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.cloudstack.functions; + +import com.google.common.base.Function; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Convert a Date object into a "yyyy-MM-dd" String + * + * @author Richard Downer + */ +public class DateToYyyyMmDd implements Function { + + public String apply(Object input) { + checkNotNull(input, "input cannot be null"); + checkArgument(input instanceof Date, "input must be a Date"); + + return new SimpleDateFormat("yyyy-MM-dd").format((Date)input); + } + +} diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java index 4d11519c63..da69020c4e 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java @@ -39,6 +39,56 @@ import java.util.TimeZone; @Test(groups = "unit", testName = "GlobalUsageAsyncClientTest") public class GlobalUsageAsyncClientTest extends BaseCloudStackAsyncClientTest { + public void testGenerateUsageRecords() throws Exception { + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + c.set(Calendar.YEAR, 2012); + c.set(Calendar.MONTH, Calendar.JANUARY); + c.set(Calendar.DAY_OF_MONTH, 1); + Date start = c.getTime(); + c.set(Calendar.DAY_OF_MONTH, 31); + Date end = c.getTime(); + + Method method = GlobalUsageAsyncClient.class.getMethod("generateUsageRecords", + Date.class, Date.class, GenerateUsageRecordsOptions[].class); + HttpRequest httpRequest = processor.createRequest(method, start, end); + + assertRequestLineEquals(httpRequest, + "GET http://localhost:8080/client/api?response=json&command=generateUsageRecords&startdate=2012-01-01&enddate=2012-01-31 HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertResponseParserClassEquals(method, httpRequest, ParseFirstJsonValueNamed.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, MapHttp4xxCodesToExceptions.class); + + checkFilters(httpRequest); + } + + public void testGenerateUsageRecordsOptions() throws Exception { + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + c.set(Calendar.YEAR, 2012); + c.set(Calendar.MONTH, Calendar.JANUARY); + c.set(Calendar.DAY_OF_MONTH, 1); + Date start = c.getTime(); + c.set(Calendar.DAY_OF_MONTH, 31); + Date end = c.getTime(); + + Method method = GlobalUsageAsyncClient.class.getMethod("generateUsageRecords", + Date.class, Date.class, GenerateUsageRecordsOptions[].class); + HttpRequest httpRequest = processor.createRequest(method, start, end, GenerateUsageRecordsOptions.Builder.domainId(42)); + + assertRequestLineEquals(httpRequest, + "GET http://localhost:8080/client/api?response=json&command=generateUsageRecords&startdate=2012-01-01&enddate=2012-01-31&domainid=42 HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertResponseParserClassEquals(method, httpRequest, ParseFirstJsonValueNamed.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, MapHttp4xxCodesToExceptions.class); + + checkFilters(httpRequest); + } + @Override protected TypeLiteral> createTypeLiteral() { return new TypeLiteral>() { From 5c00a029c69021fe47a74ad5937f5e5a5370afa4 Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Fri, 16 Dec 2011 15:01:08 +0000 Subject: [PATCH 4/7] Add CloudStackDateAdapter to work around strange date format emitted by CloudStack --- .../config/CloudStackDateAdapter.java | 65 +++++++++++++++++++ .../config/CloudStackRestClientModule.java | 3 +- 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackDateAdapter.java diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackDateAdapter.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackDateAdapter.java new file mode 100644 index 0000000000..4835a509b4 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackDateAdapter.java @@ -0,0 +1,65 @@ +/** + * 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.config; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import org.jclouds.date.DateService; +import org.jclouds.json.config.GsonModule; + +import javax.inject.Inject; +import java.lang.reflect.Type; +import java.util.Date; + +/** + * Data adapter for the date formats used by CloudStack. + * + * Essentially this is a workaround for the CloudStack getUsage() API call returning a + * corrupted formn of ISO-8601 dates, which have an unexpected pair of apostrophes, like + * 2011-12-12'T'00:00:00+00:00 + * + * @author Richard Downer + */ +public class CloudStackDateAdapter implements GsonModule.DateAdapter { + private final DateService dateService; + + @Inject + private CloudStackDateAdapter(DateService dateService) { + this.dateService = dateService; + } + + public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(dateService.iso8601DateFormat(src)); + } + + public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + String toParse = json.getAsJsonPrimitive().getAsString(); + toParse = toParse.replaceAll("'T'", "T"); + try { + return dateService.iso8601DateParse(toParse); + } catch (RuntimeException e) { + return dateService.iso8601SecondsDateParse(toParse); + } + } + +} diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java index 5ac8a16919..5b2bc37fe7 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java @@ -103,7 +103,6 @@ import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.ServerError; import org.jclouds.json.config.GsonModule.DateAdapter; -import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; import org.jclouds.location.Provider; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.RestContext; @@ -186,7 +185,7 @@ public class CloudStackRestClientModule extends RestClientModule>() { }).to(new TypeLiteral>() { }); From 1fbc029bc866f0da534954796409a7659e753f42 Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Tue, 13 Dec 2011 15:45:08 +0000 Subject: [PATCH 5/7] Add UsageRecord to the domain model --- .../cloudstack/domain/UsageRecord.java | 438 ++++++++++++++++++ .../parse/ListUsageRecordsResponseTest.java | 84 ++++ .../resources/listusagerecordsresponse.json | 1 + 3 files changed, 523 insertions(+) create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/UsageRecord.java create mode 100644 apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListUsageRecordsResponseTest.java create mode 100644 apis/cloudstack/src/test/resources/listusagerecordsresponse.json diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/UsageRecord.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/UsageRecord.java new file mode 100644 index 0000000000..a07c28cb20 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/UsageRecord.java @@ -0,0 +1,438 @@ +/** + * 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.domain; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.gson.annotations.SerializedName; + +import java.util.Date; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Represents a usage record from CloudStack + * + * @author Richard Downer + */ +public class UsageRecord implements Comparable { + + public enum UsageType { + RUNNING_VM(1), + ALLOCATED_VM(2), + IP_ADDRESS(3), + NETWORK_BYTES_SENT(4), + NETWORK_BYTES_RECEIVED(5), + VOLUME(6), + TEMPLATE(7), + ISO(8), + SNAPSHOT(9), + SECURITY_GROUP(10), + LOAD_BALANCER_POLICY(11), + PORT_FORWARDING_RULE(12), + NETWORK_OFFERING(13), + VPN_USERS(14), + UNRECOGNIZED(0); + + private int code; + + private static final Map INDEX = Maps.uniqueIndex(ImmutableSet.copyOf(UsageType.values()), + new Function() { + + @Override + public Integer apply(UsageType input) { + return input.code; + } + + }); + + UsageType(int code) { + this.code = code; + } + + @Override + public String toString() { + return "" + code; + } + + public static UsageType fromValue(String usageType) { + Integer code = new Integer(checkNotNull(usageType, "usageType")); + return INDEX.containsKey(code) ? INDEX.get(code) : UNRECOGNIZED; + } + + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private Builder() { + } + + private long id; + private String description; + private long accountId; + private String accountName; + private long domainId; + private Date startDate; + private Date endDate; + private Date assignDate; + private long releaseDate; + private long zoneId; + private long virtualMachineId; + private String virtualMachineName; + private long serviceOfferingId; + private long templateId; + private String ipAddress; + private boolean isSourceNAT; + private double rawUsageHours; + private String usage; + private String type; + private UsageType usageType; + + public Builder id(long id) { + this.id = id; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder accountId(long accountId) { + this.accountId = accountId; + return this; + } + + public Builder accountName(String accountName) { + this.accountName = accountName; + return this; + } + + public Builder domainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder startDate(Date startDate) { + this.startDate = startDate; + return this; + } + + public Builder endDate(Date endDate) { + this.endDate = endDate; + return this; + } + + public Builder assignDate(Date assignDate) { + this.assignDate = assignDate; + return this; + } + + public Builder releaseDate(long releaseDate) { + this.releaseDate = releaseDate; + return this; + } + + public Builder zoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder virtualMachineId(long virtualMachineId) { + this.virtualMachineId = virtualMachineId; + return this; + } + + public Builder virtualMachineName(String virtualMachineName) { + this.virtualMachineName = virtualMachineName; + return this; + } + + public Builder serviceOfferingId(long serviceOfferingId) { + this.serviceOfferingId = serviceOfferingId; + return this; + } + + public Builder templateId(long templateId) { + this.templateId = templateId; + return this; + } + + public Builder ipAddress(String ipAddress) { + this.ipAddress = ipAddress; + return this; + } + + public Builder surceNAT(boolean sourceNAT) { + isSourceNAT = sourceNAT; + return this; + } + + public Builder rawUsageHours(double rawUsageHours) { + this.rawUsageHours = rawUsageHours; + return this; + } + + public Builder usage(String usage) { + this.usage = usage; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public Builder usageType(UsageType usageType) { + this.usageType = usageType; + return this; + } + + public UsageRecord build() { + return new UsageRecord(id, description, accountId, accountName, domainId, startDate, endDate, assignDate, releaseDate, zoneId, virtualMachineId, virtualMachineName, serviceOfferingId, templateId, ipAddress, isSourceNAT, rawUsageHours, usage, type, usageType); + } + } + + @SerializedName("usageid") private long id; + private String description; + @SerializedName("accountid") private long accountId; + @SerializedName("account") private String accountName; + @SerializedName("domainid") private long domainId; + @SerializedName("startdate") private Date startDate; + @SerializedName("enddate") private Date endDate; + @SerializedName("assigndate") private Date assignDate; + @SerializedName("releasedate") private long releaseDate; + @SerializedName("zoneid") private long zoneId; + @SerializedName("virtualmachineid") private long virtualMachineId; + @SerializedName("name") private String virtualMachineName; + @SerializedName("offeringid") private long serviceOfferingId; + @SerializedName("templateid") private long templateId; + @SerializedName("ipaddress") private String ipAddress; + @SerializedName("issourcenat") private boolean isSourceNAT; + @SerializedName("rawusage") private double rawUsageHours; + @SerializedName("usage") private String usage; + private String type; + @SerializedName("usagetype") private UsageType usageType; + + /* Exists only for the deserializer */ + UsageRecord(){ + } + + public UsageRecord(long id, String description, long accountId, String accountName, long domainId, Date startDate, Date endDate, Date assignDate, long releaseDate, long zoneId, long virtualMachineId, String virtualMachineName, long serviceOfferingId, long templateId, String ipAddress, boolean sourceNAT, double rawUsageHours, String usage, String type, UsageType usageType) { + this.id = id; + this.description = description; + this.accountId = accountId; + this.accountName = accountName; + this.domainId = domainId; + this.startDate = startDate; + this.endDate = endDate; + this.assignDate = assignDate; + this.releaseDate = releaseDate; + this.zoneId = zoneId; + this.virtualMachineId = virtualMachineId; + this.virtualMachineName = virtualMachineName; + this.serviceOfferingId = serviceOfferingId; + this.templateId = templateId; + this.ipAddress = ipAddress; + isSourceNAT = sourceNAT; + this.rawUsageHours = rawUsageHours; + this.usage = usage; + this.type = type; + this.usageType = usageType; + } + + public long getId() { + return id; + } + + public String getDescription() { + return description; + } + + public long getAccountId() { + return accountId; + } + + public String getAccountName() { + return accountName; + } + + public long getDomainId() { + return domainId; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public Date getAssignDate() { + return assignDate; + } + + public long getReleaseDate() { + return releaseDate; + } + + public long getZoneId() { + return zoneId; + } + + public long getVirtualMachineId() { + return virtualMachineId; + } + + public String getVirtualMachineName() { + return virtualMachineName; + } + + public long getServiceOfferingId() { + return serviceOfferingId; + } + + public long getTemplateId() { + return templateId; + } + + public String getIpAddress() { + return ipAddress; + } + + public boolean isSourceNAT() { + return isSourceNAT; + } + + public double getRawUsageHours() { + return rawUsageHours; + } + + public String getUsage() { + return usage; + } + + public String getType() { + return type; + } + + public UsageType getUsageType() { + return usageType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UsageRecord that = (UsageRecord) o; + + if (accountId != that.accountId) return false; + if (domainId != that.domainId) return false; + if (id != that.id) return false; + if (isSourceNAT != that.isSourceNAT) return false; + if (Double.compare(that.rawUsageHours, rawUsageHours) != 0) return false; + if (releaseDate != that.releaseDate) return false; + if (serviceOfferingId != that.serviceOfferingId) return false; + if (templateId != that.templateId) return false; + if (virtualMachineId != that.virtualMachineId) return false; + if (zoneId != that.zoneId) return false; + if (accountName != null ? !accountName.equals(that.accountName) : that.accountName != null) return false; + if (assignDate != null ? !assignDate.equals(that.assignDate) : that.assignDate != null) return false; + if (description != null ? !description.equals(that.description) : that.description != null) return false; + if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) return false; + if (ipAddress != null ? !ipAddress.equals(that.ipAddress) : that.ipAddress != null) return false; + if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + if (usage != null ? !usage.equals(that.usage) : that.usage != null) return false; + if (usageType != that.usageType) return false; + if (virtualMachineName != null ? !virtualMachineName.equals(that.virtualMachineName) : that.virtualMachineName != null) + return false; + + return true; + } + + @Override + public int hashCode() { + int result; + long temp; + result = (int) (id ^ (id >>> 32)); + result = 31 * result + (description != null ? description.hashCode() : 0); + result = 31 * result + (int) (accountId ^ (accountId >>> 32)); + result = 31 * result + (accountName != null ? accountName.hashCode() : 0); + result = 31 * result + (int) (domainId ^ (domainId >>> 32)); + result = 31 * result + (startDate != null ? startDate.hashCode() : 0); + result = 31 * result + (endDate != null ? endDate.hashCode() : 0); + result = 31 * result + (assignDate != null ? assignDate.hashCode() : 0); + result = 31 * result + (int) (releaseDate ^ (releaseDate >>> 32)); + result = 31 * result + (int) (zoneId ^ (zoneId >>> 32)); + result = 31 * result + (int) (virtualMachineId ^ (virtualMachineId >>> 32)); + result = 31 * result + (virtualMachineName != null ? virtualMachineName.hashCode() : 0); + result = 31 * result + (int) (serviceOfferingId ^ (serviceOfferingId >>> 32)); + result = 31 * result + (int) (templateId ^ (templateId >>> 32)); + result = 31 * result + (ipAddress != null ? ipAddress.hashCode() : 0); + result = 31 * result + (isSourceNAT ? 1 : 0); + temp = rawUsageHours != +0.0d ? Double.doubleToLongBits(rawUsageHours) : 0L; + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + (usage != null ? usage.hashCode() : 0); + result = 31 * result + (type != null ? type.hashCode() : 0); + result = 31 * result + (usageType != null ? usageType.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "UsageRecord{" + + "id=" + id + + ", description='" + description + '\'' + + ", accountId=" + accountId + + ", accountName='" + accountName + '\'' + + ", domainId=" + domainId + + ", startDate=" + startDate + + ", endDate=" + endDate + + ", assignDate=" + assignDate + + ", releaseDate=" + releaseDate + + ", zoneId=" + zoneId + + ", virtualMachineId=" + virtualMachineId + + ", virtualMachineName='" + virtualMachineName + '\'' + + ", serviceOfferingId=" + serviceOfferingId + + ", templateId=" + templateId + + ", ipAddress='" + ipAddress + '\'' + + ", isSourceNAT=" + isSourceNAT + + ", rawUsageHours=" + rawUsageHours + + ", usage='" + usage + '\'' + + ", type='" + type + '\'' + + ", usageType=" + usageType + + '}'; + } + + @Override + public int compareTo(UsageRecord other) { + return Long.valueOf(this.id).compareTo(other.id); + } +} diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListUsageRecordsResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListUsageRecordsResponseTest.java new file mode 100644 index 0000000000..fa914bfe95 --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListUsageRecordsResponseTest.java @@ -0,0 +1,84 @@ +/** + * 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.parse; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.jclouds.cloudstack.config.CloudStackDateAdapter; +import org.jclouds.cloudstack.domain.UsageRecord; +import org.jclouds.json.BaseSetParserTest; +import org.jclouds.json.config.GsonModule; +import org.jclouds.rest.annotations.SelectJson; +import org.testng.annotations.Test; + +import java.util.Calendar; +import java.util.Date; +import java.util.Set; +import java.util.TimeZone; + +/** + * + * @author Richard Downer + */ +@Test(groups = "unit") +public class ListUsageRecordsResponseTest extends BaseSetParserTest { + + @Override + public String resource() { + return "/listusagerecordsresponse.json"; + } + + @Override + @SelectJson("usagerecord") + public Set expected() { + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + c.set(Calendar.YEAR, 2011); + c.set(Calendar.MONTH, Calendar.DECEMBER); + c.set(Calendar.DAY_OF_MONTH, 15); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + Date start = c.getTime(); + c.add(Calendar.DAY_OF_MONTH, 1); + c.add(Calendar.SECOND, -1); + Date end = c.getTime(); + + return ImmutableSet.of(UsageRecord.builder() + .accountName("admin").accountId(2L).domainId(1L).zoneId(1L) + .description("Template Id:203 Size:3117171712") + .usage("24 Hrs").usageType(UsageRecord.UsageType.TEMPLATE).rawUsageHours(24) + .templateId(0L).id(203).startDate(start).endDate(end).build()); + + } + + @Override + protected Injector injector() { + return Guice.createInjector(new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(CloudStackDateAdapter.class); + super.configure(); + } + + }); + } +} diff --git a/apis/cloudstack/src/test/resources/listusagerecordsresponse.json b/apis/cloudstack/src/test/resources/listusagerecordsresponse.json new file mode 100644 index 0000000000..5175f709e0 --- /dev/null +++ b/apis/cloudstack/src/test/resources/listusagerecordsresponse.json @@ -0,0 +1 @@ +{ "listusagerecordsresponse" : { "count":1 ,"usagerecord" : [ {"account":"admin","accountid":2,"domainid":1,"zoneid":1,"description":"Template Id:203 Size:3117171712","usage":"24 Hrs","usagetype":7,"rawusage":"24","templateid":0,"usageid":203,"size":3117171712,"startdate":"2011-12-15'T'00:00:00+00:00","enddate":"2011-12-15'T'23:59:59+00:00"} ] } } \ No newline at end of file From 35e034a29a191402cbe6b99f0b6aecf1abb0738a Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Tue, 13 Dec 2011 15:46:01 +0000 Subject: [PATCH 6/7] Add the listUsageRecords API operation --- .../features/GlobalUsageAsyncClient.java | 9 +++ .../features/GlobalUsageClient.java | 2 + .../options/ListUsageRecordsOptions.java | 73 ++++++++++++++++++ .../features/GlobalUsageAsyncClientTest.java | 51 +++++++++++++ .../options/ListUsageRecordsOptionsTest.java | 76 +++++++++++++++++++ 5 files changed, 211 insertions(+) create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListUsageRecordsOptions.java create mode 100644 apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListUsageRecordsOptionsTest.java diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClient.java index 1e2a27b820..9a21188deb 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClient.java @@ -20,9 +20,11 @@ package org.jclouds.cloudstack.features; import com.google.common.util.concurrent.ListenableFuture; import org.jclouds.cloudstack.domain.JobResult; +import org.jclouds.cloudstack.domain.UsageRecord; import org.jclouds.cloudstack.filters.QuerySigner; import org.jclouds.cloudstack.functions.DateToYyyyMmDd; import org.jclouds.cloudstack.options.GenerateUsageRecordsOptions; +import org.jclouds.cloudstack.options.ListUsageRecordsOptions; import org.jclouds.rest.annotations.ParamParser; import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.RequestFilters; @@ -33,6 +35,7 @@ import javax.ws.rs.GET; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import java.util.Date; +import java.util.Set; /** * Provides asynchronous access to CloudStack usage features. @@ -52,4 +55,10 @@ public interface GlobalUsageAsyncClient { @Consumes(MediaType.APPLICATION_JSON) ListenableFuture generateUsageRecords(@QueryParam("startdate") @ParamParser(DateToYyyyMmDd.class) Date start, @QueryParam("enddate") @ParamParser(DateToYyyyMmDd.class) Date end, GenerateUsageRecordsOptions... options); + @GET + @QueryParams(keys = "command", values = "listUsageRecords") + @SelectJson("usagerecord") + @Consumes(MediaType.APPLICATION_JSON) + ListenableFuture> listUsageRecords(@QueryParam("startdate") @ParamParser(DateToYyyyMmDd.class) Date start, @QueryParam("enddate") @ParamParser(DateToYyyyMmDd.class) Date end, ListUsageRecordsOptions... options); + } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java index bf79959721..2736732a7f 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalUsageClient.java @@ -41,4 +41,6 @@ public interface GlobalUsageClient { JobResult generateUsageRecords(Date start, Date end, GenerateUsageRecordsOptions... options); + Set listUsageRecords(Date start, Date end, ListUsageRecordsOptions... options); + } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListUsageRecordsOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListUsageRecordsOptions.java new file mode 100644 index 0000000000..13993b9de3 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListUsageRecordsOptions.java @@ -0,0 +1,73 @@ +/** + * 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.ImmutableSet; + +/** + * Options to the GlobalUsageClient.listUsageOptions() API call + * + * @author Richard Downer + */ +public class ListUsageRecordsOptions extends AccountInDomainOptions { + + public static final ListUsageRecordsOptions NONE = new ListUsageRecordsOptions(); + + public static class Builder { + public static ListUsageRecordsOptions accountInDomain(String account, long domainId) { + ListUsageRecordsOptions options = new ListUsageRecordsOptions(); + return options.accountInDomain(account, domainId); + } + + public static ListUsageRecordsOptions domainId(long domainId) { + ListUsageRecordsOptions options = new ListUsageRecordsOptions(); + return options.domainId(domainId); + } + + public static ListUsageRecordsOptions accountId(long accountId) { + ListUsageRecordsOptions options = new ListUsageRecordsOptions(); + return options.accountId(accountId); + } + + public static ListUsageRecordsOptions keyword(String keyword) { + ListUsageRecordsOptions options = new ListUsageRecordsOptions(); + return options.keyword(keyword); + } + } + + @Override + public ListUsageRecordsOptions accountInDomain(String account, long domain) { + return (ListUsageRecordsOptions) super.accountInDomain(account, domain); + } + + @Override + public ListUsageRecordsOptions domainId(long domainId) { + return (ListUsageRecordsOptions) super.domainId(domainId); + } + + public ListUsageRecordsOptions accountId(long accountId) { + this.queryParameters.replaceValues("accountid", ImmutableSet.of(accountId + "")); + return this; + } + + public ListUsageRecordsOptions keyword(String keyword) { + this.queryParameters.replaceValues("keyword", ImmutableSet.of(keyword)); + return this; + } +} diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java index da69020c4e..5ec4bc582b 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack.features; import com.google.inject.TypeLiteral; import org.jclouds.cloudstack.options.GenerateUsageRecordsOptions; +import org.jclouds.cloudstack.options.ListUsageRecordsOptions; import org.jclouds.http.HttpRequest; import org.jclouds.http.functions.ParseFirstJsonValueNamed; import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions; @@ -89,6 +90,56 @@ public class GlobalUsageAsyncClientTest extends BaseCloudStackAsyncClientTest> createTypeLiteral() { return new TypeLiteral>() { diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListUsageRecordsOptionsTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListUsageRecordsOptionsTest.java new file mode 100644 index 0000000000..ff3272baeb --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListUsageRecordsOptionsTest.java @@ -0,0 +1,76 @@ +/** + * 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.ImmutableSet; +import org.testng.annotations.Test; + +import static org.jclouds.cloudstack.options.ListUsageRecordsOptions.Builder.*; +import static org.testng.Assert.assertEquals; + +/** + * Options to the GlobalUsageClient.listUsageOptions() API call + * + * @author Richard Downer + */ +@Test(groups = "unit") +public class ListUsageRecordsOptionsTest { + + public void testAccountInDomain() { + ListUsageRecordsOptions options = new ListUsageRecordsOptions().accountInDomain("fred", 42); + assertEquals(ImmutableSet.of("fred"), options.buildQueryParameters().get("account")); + assertEquals(ImmutableSet.of("42"), options.buildQueryParameters().get("domainid")); + } + + public void testAccountInDomainStatic() { + ListUsageRecordsOptions options = accountInDomain("fred", 42); + assertEquals(ImmutableSet.of("fred"), options.buildQueryParameters().get("account")); + assertEquals(ImmutableSet.of("42"), options.buildQueryParameters().get("domainid")); + } + + public void testDomainId() { + ListUsageRecordsOptions options = new ListUsageRecordsOptions().domainId(42); + assertEquals(ImmutableSet.of("42"), options.buildQueryParameters().get("domainid")); + } + + public void testDomainIdStatic() { + ListUsageRecordsOptions options = domainId(42); + assertEquals(ImmutableSet.of("42"), options.buildQueryParameters().get("domainid")); + } + + public void testAccountId() { + ListUsageRecordsOptions options = new ListUsageRecordsOptions().accountId(41); + assertEquals(ImmutableSet.of("41"), options.buildQueryParameters().get("accountid")); + } + + public void testAccountIdStatic() { + ListUsageRecordsOptions options = accountId(41); + assertEquals(ImmutableSet.of("41"), options.buildQueryParameters().get("accountid")); + } + + public void testKeyword() { + ListUsageRecordsOptions options = new ListUsageRecordsOptions().keyword("bob"); + assertEquals(ImmutableSet.of("bob"), options.buildQueryParameters().get("keyword")); + } + + public void testKeywordStatic() { + ListUsageRecordsOptions options = keyword("bob"); + assertEquals(ImmutableSet.of("bob"), options.buildQueryParameters().get("keyword")); + } +} From d596cd18f14d57e415baed9d26e0eb0984de3595 Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Tue, 13 Dec 2011 15:46:25 +0000 Subject: [PATCH 7/7] Add live tests for the Usage API --- .../features/GlobalUsageClientLiveTest.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageClientLiveTest.java diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageClientLiveTest.java new file mode 100644 index 0000000000..e696588dcf --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageClientLiveTest.java @@ -0,0 +1,73 @@ +/** + * 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.features; + +import com.google.common.collect.ImmutableSet; +import org.jclouds.cloudstack.domain.DiskOffering; +import org.jclouds.cloudstack.domain.JobResult; +import org.jclouds.cloudstack.domain.NetworkOffering; +import org.jclouds.cloudstack.domain.NetworkOfferingAvailabilityType; +import org.jclouds.cloudstack.domain.ServiceOffering; +import org.jclouds.cloudstack.domain.StorageType; +import org.jclouds.cloudstack.domain.UsageRecord; +import org.jclouds.cloudstack.options.GenerateUsageRecordsOptions; +import org.jclouds.cloudstack.options.ListUsageRecordsOptions; +import org.jclouds.cloudstack.options.UpdateDiskOfferingOptions; +import org.jclouds.cloudstack.options.UpdateNetworkOfferingOptions; +import org.jclouds.cloudstack.options.UpdateServiceOfferingOptions; +import org.jclouds.logging.Logger; +import org.testng.annotations.Test; + +import java.util.Calendar; +import java.util.Date; +import java.util.Set; +import java.util.TimeZone; + +import static com.google.common.collect.Iterables.getFirst; +import static org.jclouds.cloudstack.domain.NetworkOfferingAvailabilityType.OPTIONAL; +import static org.jclouds.cloudstack.domain.NetworkOfferingAvailabilityType.REQUIRED; +import static org.jclouds.cloudstack.options.CreateDiskOfferingOptions.Builder.diskSizeInGB; +import static org.jclouds.cloudstack.options.CreateServiceOfferingOptions.Builder.highlyAvailable; +import static org.testng.Assert.*; + +/** + * Tests behavior of {@code GlobalUsageClient} + * + * @author Richard Downer + */ +@Test(groups = "live", singleThreaded = true, testName = "GlobalUsageClientLiveTest") +public class GlobalUsageClientLiveTest extends BaseCloudStackClientLiveTest { + + @Test(groups = "live", enabled = true) + public void testListUsage() { + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + Date end = c.getTime(); + c.add(Calendar.MONTH, -1); + Date start = c.getTime(); + + JobResult result = globalAdminClient.getUsageClient().generateUsageRecords(start, end, GenerateUsageRecordsOptions.NONE); + assertNotNull(result); + assertTrue(result.getSuccess(), result.getDisplayText()); + + Set records = globalAdminClient.getUsageClient().listUsageRecords(start, end, ListUsageRecordsOptions.NONE); + assertNotNull(records); + assertTrue(records.size() > 0); + } + +}