refactored async job processing in cloudstack

This commit is contained in:
Adrian Cole 2011-02-27 12:14:09 -08:00
parent c6032bc1d5
commit ff10f03efc
21 changed files with 3810 additions and 285 deletions

View File

@ -44,6 +44,7 @@ public class AsyncJob<T> {
private T result; private T result;
private int resultCode = -1; private int resultCode = -1;
private String resultType; private String resultType;
private AsyncJobError error;
private int status = -1; private int status = -1;
private int userId = -1; private int userId = -1;
@ -72,6 +73,11 @@ public class AsyncJob<T> {
return this; return this;
} }
public Builder<T> error(AsyncJobError error) {
this.error = error;
return this;
}
public Builder<T> instanceType(String instanceType) { public Builder<T> instanceType(String instanceType) {
this.instanceType = instanceType; this.instanceType = instanceType;
return this; return this;
@ -109,13 +115,13 @@ public class AsyncJob<T> {
public AsyncJob<T> build() { public AsyncJob<T> build() {
return new AsyncJob<T>(accountId, cmd, created, id, instanceId, instanceType, progress, result, resultCode, return new AsyncJob<T>(accountId, cmd, created, id, instanceId, instanceType, progress, result, resultCode,
resultType, status, userId); resultType, status, userId, error);
} }
public static <T> Builder<T> fromAsyncJobUntyped(AsyncJob<T> in) { public static <T> Builder<T> fromAsyncJobUntyped(AsyncJob<T> in) {
return new Builder<T>().accountId(in.accountId).cmd(in.cmd).created(in.created).id(in.id) return new Builder<T>().accountId(in.accountId).cmd(in.cmd).created(in.created).id(in.id).instanceId(
.instanceId(in.instanceId).instanceType(in.instanceType).progress(in.progress).result(in.result) in.instanceId).instanceType(in.instanceType).progress(in.progress).result(in.result).resultCode(
.resultCode(in.resultCode).resultType(in.resultType).status(in.status).userId(in.userId); in.resultCode).resultType(in.resultType).status(in.status).userId(in.userId).error(in.error);
} }
} }
@ -141,9 +147,10 @@ public class AsyncJob<T> {
private int status = -1; private int status = -1;
@SerializedName("userid") @SerializedName("userid")
private int userId = -1; private int userId = -1;
private AsyncJobError error;
public AsyncJob(long accountId, String cmd, Date created, long id, long instanceId, String instanceType, public AsyncJob(long accountId, String cmd, Date created, long id, long instanceId, String instanceType,
int progress, T result, int resultCode, String resultType, int status, int userId) { int progress, T result, int resultCode, String resultType, int status, int userId, AsyncJobError error) {
this.accountId = accountId; this.accountId = accountId;
this.cmd = cmd; this.cmd = cmd;
this.created = created; this.created = created;
@ -156,6 +163,7 @@ public class AsyncJob<T> {
this.resultType = resultType; this.resultType = resultType;
this.status = status; this.status = status;
this.userId = userId; this.userId = userId;
this.error = error;
} }
/** /**
@ -250,6 +258,15 @@ public class AsyncJob<T> {
return userId; return userId;
} }
/**
*
*
* @return the error related to this command, or null if no error or error not yet encountered.
*/
public AsyncJobError getError() {
return error;
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
@ -260,6 +277,7 @@ public class AsyncJob<T> {
result = prime * result + (int) (id ^ (id >>> 32)); result = prime * result + (int) (id ^ (id >>> 32));
result = prime * result + (int) (instanceId ^ (instanceId >>> 32)); result = prime * result + (int) (instanceId ^ (instanceId >>> 32));
result = prime * result + ((instanceType == null) ? 0 : instanceType.hashCode()); result = prime * result + ((instanceType == null) ? 0 : instanceType.hashCode());
result = prime * result + ((error == null) ? 0 : error.hashCode());
result = prime * result + progress; result = prime * result + progress;
result = prime * result + ((this.result == null) ? 0 : this.result.hashCode()); result = prime * result + ((this.result == null) ? 0 : this.result.hashCode());
result = prime * result + resultCode; result = prime * result + resultCode;
@ -299,6 +317,11 @@ public class AsyncJob<T> {
return false; return false;
} else if (!instanceType.equals(other.instanceType)) } else if (!instanceType.equals(other.instanceType))
return false; return false;
if (error == null) {
if (other.error != null)
return false;
} else if (!error.equals(other.error))
return false;
if (progress != other.progress) if (progress != other.progress)
return false; return false;
if (result == null) { if (result == null) {
@ -323,9 +346,9 @@ public class AsyncJob<T> {
@Override @Override
public String toString() { public String toString() {
return "[accountId=" + accountId + ", cmd=" + cmd + ", created=" + created + ", id=" + id + ", instanceId=" return "[accountId=" + accountId + ", cmd=" + cmd + ", created=" + created + ", id=" + id + ", instanceId="
+ instanceId + ", instanceType=" + instanceType + ", progress=" + progress + ", result=" + result + instanceId + ", instanceType=" + instanceType + ", error=" + error + ", progress=" + progress
+ ", resultCode=" + resultCode + ", resultType=" + resultType + ", status=" + status + ", userId=" + userId + ", result=" + result + ", resultCode=" + resultCode + ", resultType=" + resultType + ", status="
+ "]"; + status + ", userId=" + userId + "]";
} }
} }

View File

@ -0,0 +1,88 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.gson.annotations.SerializedName;
/**
*
* @author Adrian Cole
*/
public class AsyncJobError {
@SerializedName("errorcode")
private int errorCode;
@SerializedName("errortext")
private String errorText;
/**
* present only for serializer
*
*/
AsyncJobError() {
}
public AsyncJobError(int errorCode, String errorText) {
this.errorCode = errorCode;
this.errorText = errorText;
}
public int getErrorCode() {
return errorCode;
}
public String getErrorText() {
return errorText;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + errorCode;
result = prime * result + ((errorText == null) ? 0 : errorText.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AsyncJobError other = (AsyncJobError) obj;
if (errorCode != other.errorCode)
return false;
if (errorText == null) {
if (other.errorText != null)
return false;
} else if (!errorText.equals(other.errorText))
return false;
return true;
}
@Override
public String toString() {
return "[errorCode=" + errorCode + ", errorText=" + errorText + "]";
}
}

View File

@ -28,6 +28,7 @@ import javax.annotation.Nullable;
import com.google.common.base.CaseFormat; import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
/** /**
@ -289,10 +290,11 @@ public class VirtualMachine implements Comparable<VirtualMachine> {
public VirtualMachine build() { public VirtualMachine build() {
return new VirtualMachine(id, account, cpuCount, cpuSpeed, cpuUsed, displayName, created, domain, domainId, return new VirtualMachine(id, account, cpuCount, cpuSpeed, cpuUsed, displayName, created, domain, domainId,
usesVirtualNetwork, group, groupId, guestOSId, HAEnabled, hostId, hostname, IPAddress, ISODisplayText, usesVirtualNetwork, group, groupId, guestOSId, HAEnabled, hostId, hostname, IPAddress,
ISOId, ISOName, jobId, jobStatus, memory, name, networkKbsRead, networkKbsWrite, password, ISODisplayText, ISOId, ISOName, jobId, jobStatus, memory, name, networkKbsRead, networkKbsWrite,
passwordEnabled, rootDeviceId, rootDeviceType, securityGroups, serviceOfferingId, serviceOfferingName, password, passwordEnabled, rootDeviceId, rootDeviceType, securityGroups, serviceOfferingId,
state, templateDisplayText, templateId, templateName, zoneId, zoneName, nics, hypervisor); serviceOfferingName, state, templateDisplayText, templateId, templateName, zoneId, zoneName, nics,
hypervisor);
} }
} }
@ -389,13 +391,13 @@ public class VirtualMachine implements Comparable<VirtualMachine> {
private Set<SecurityGroup> securityGroups = ImmutableSet.<SecurityGroup> of(); private Set<SecurityGroup> securityGroups = ImmutableSet.<SecurityGroup> of();
public VirtualMachine(long id, String account, long cpuCount, long cpuSpeed, Long cpuUsed, String displayName, public VirtualMachine(long id, String account, long cpuCount, long cpuSpeed, Long cpuUsed, String displayName,
Date created, String domain, long domainId, boolean usesVirtualNetwork, String group, long groupId, Date created, String domain, long domainId, boolean usesVirtualNetwork, String group, long groupId,
long guestOSId, boolean hAEnabled, long hostId, String hostname, String iPAddress, String iSODisplayText, long guestOSId, boolean hAEnabled, long hostId, String hostname, String iPAddress, String iSODisplayText,
long iSOId, String iSOName, Long jobId, Integer jobStatus, long memory, String name, Long networkKbsRead, long iSOId, String iSOName, Long jobId, Integer jobStatus, long memory, String name, Long networkKbsRead,
Long networkKbsWrite, String password, boolean passwordEnabled, long rootDeviceId, String rootDeviceType, Long networkKbsWrite, String password, boolean passwordEnabled, long rootDeviceId, String rootDeviceType,
Set<SecurityGroup> securityGroups, long serviceOfferingId, String serviceOfferingName, State state, Set<SecurityGroup> securityGroups, long serviceOfferingId, String serviceOfferingName, State state,
String templateDisplayText, long templateId, String templateName, long zoneId, String zoneName, Set<NIC> nics, String templateDisplayText, long templateId, String templateName, long zoneId, String zoneName,
String hypervisor) { Set<NIC> nics, String hypervisor) {
this.id = id; this.id = id;
this.account = account; this.account = account;
this.cpuCount = cpuCount; this.cpuCount = cpuCount;
@ -563,7 +565,13 @@ public class VirtualMachine implements Comparable<VirtualMachine> {
* @return the ip address of the virtual machine * @return the ip address of the virtual machine
*/ */
public String getIPAddress() { public String getIPAddress() {
return IPAddress; if (IPAddress != null)
return IPAddress;
// some versions of 2.2.0 do not populate the IP address field
if (getNICs().size() > 0) {
return Iterables.get(getNICs(), 0).getIPAddress();
}
return null;
} }
/** /**
@ -948,17 +956,18 @@ public class VirtualMachine implements Comparable<VirtualMachine> {
@Override @Override
public String toString() { public String toString() {
return "[id=" + id + ", account=" + account + ", cpuCount=" + cpuCount + ", cpuSpeed=" + cpuSpeed + ", cpuUsed=" return "[id=" + id + ", account=" + account + ", cpuCount=" + cpuCount + ", cpuSpeed=" + cpuSpeed + ", cpuUsed="
+ cpuUsed + ", displayName=" + displayName + ", created=" + created + ", domain=" + domain + ", domainId=" + cpuUsed + ", displayName=" + displayName + ", created=" + created + ", domain=" + domain
+ domainId + ", usesVirtualNetwork=" + usesVirtualNetwork + ", group=" + group + ", groupId=" + groupId + ", domainId=" + domainId + ", usesVirtualNetwork=" + usesVirtualNetwork + ", group=" + group
+ ", guestOSId=" + guestOSId + ", HAEnabled=" + HAEnabled + ", hostId=" + hostId + ", hostname=" + hostname + ", groupId=" + groupId + ", guestOSId=" + guestOSId + ", HAEnabled=" + HAEnabled + ", hostId="
+ ", IPAddress=" + IPAddress + ", ISODisplayText=" + ISODisplayText + ", ISOId=" + ISOId + ", ISOName=" + hostId + ", hostname=" + hostname + ", IPAddress=" + IPAddress + ", ISODisplayText=" + ISODisplayText
+ ISOName + ", jobId=" + jobId + ", jobStatus=" + jobStatus + ", memory=" + memory + ", name=" + name + ", ISOId=" + ISOId + ", ISOName=" + ISOName + ", jobId=" + jobId + ", jobStatus=" + jobStatus
+ ", networkKbsRead=" + networkKbsRead + ", networkKbsWrite=" + networkKbsWrite + ", password=" + password + ", memory=" + memory + ", name=" + name + ", networkKbsRead=" + networkKbsRead + ", networkKbsWrite="
+ ", passwordEnabled=" + passwordEnabled + ", rootDeviceId=" + rootDeviceId + ", rootDeviceType=" + networkKbsWrite + ", password=" + password + ", passwordEnabled=" + passwordEnabled
+ rootDeviceType + ", serviceOfferingId=" + serviceOfferingId + ", serviceOfferingName=" + ", rootDeviceId=" + rootDeviceId + ", rootDeviceType=" + rootDeviceType + ", serviceOfferingId="
+ serviceOfferingName + ", state=" + state + ", templateDisplayText=" + templateDisplayText + serviceOfferingId + ", serviceOfferingName=" + serviceOfferingName + ", state=" + state
+ ", templateId=" + templateId + ", templateName=" + templateName + ", zoneId=" + zoneId + ", zoneName=" + ", templateDisplayText=" + templateDisplayText + ", templateId=" + templateId + ", templateName="
+ zoneName + ", nics=" + nics + ", hypervisor=" + hypervisor + ", securityGroups=" + securityGroups + "]"; + templateName + ", zoneId=" + zoneId + ", zoneName=" + zoneName + ", nics=" + nics + ", hypervisor="
+ hypervisor + ", securityGroups=" + securityGroups + "]";
} }
@Override @Override

View File

@ -28,13 +28,13 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.cloudstack.domain.AsyncJob; import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.cloudstack.filters.QuerySigner; import org.jclouds.cloudstack.filters.QuerySigner;
import org.jclouds.cloudstack.functions.ParseAsyncJob; import org.jclouds.cloudstack.functions.ParseAsyncJobFromHttpResponse;
import org.jclouds.cloudstack.functions.ParseAsyncJobsFromHttpResponse;
import org.jclouds.cloudstack.options.ListAsyncJobsOptions; import org.jclouds.cloudstack.options.ListAsyncJobsOptions;
import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.Unwrap;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
@ -57,8 +57,7 @@ public interface AsyncJobAsyncClient {
*/ */
@GET @GET
@QueryParams(keys = "command", values = "listAsyncJobs") @QueryParams(keys = "command", values = "listAsyncJobs")
@Unwrap(depth = 2) @ResponseParser(ParseAsyncJobsFromHttpResponse.class)
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<AsyncJob<?>>> listAsyncJobs(ListAsyncJobsOptions... options); ListenableFuture<Set<AsyncJob<?>>> listAsyncJobs(ListAsyncJobsOptions... options);
@ -68,7 +67,7 @@ public interface AsyncJobAsyncClient {
@GET @GET
@QueryParams(keys = "command", values = "queryAsyncJobResult") @QueryParams(keys = "command", values = "queryAsyncJobResult")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@ResponseParser(ParseAsyncJob.class) @ResponseParser(ParseAsyncJobFromHttpResponse.class)
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class)
<T> ListenableFuture<AsyncJob<T>> getAsyncJob(@QueryParam("jobid") long id); <T> ListenableFuture<AsyncJob<T>> getAsyncJob(@QueryParam("jobid") long id);

View File

@ -0,0 +1,57 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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 static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import javax.inject.Singleton;
import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.domain.JsonBall;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import com.google.common.base.Function;
import com.google.inject.Inject;
/**
*
* @author Adrian Cole
*/
@Singleton
public class ParseAsyncJobFromHttpResponse implements Function<HttpResponse, AsyncJob<?>> {
private final UnwrapOnlyJsonValue<AsyncJob<Map<String, JsonBall>>> parser;
private final ParseTypedAsyncJob parseTyped;
@Inject
public ParseAsyncJobFromHttpResponse(ParseTypedAsyncJob parseTyped, UnwrapOnlyJsonValue<AsyncJob<Map<String, JsonBall>>> parser) {
this.parseTyped = checkNotNull(parseTyped, "parseTyped");
this.parser = checkNotNull(parser, "parser");
}
public AsyncJob<?> apply(HttpResponse response) {
checkNotNull(response, "response");
AsyncJob<Map<String, JsonBall>> toParse = parser.apply(response);
checkNotNull(toParse, "parsed result from %s", response);
return parseTyped.apply(toParse);
}
}

View File

@ -0,0 +1,61 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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 static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import java.util.Set;
import javax.inject.Singleton;
import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.domain.JsonBall;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
/**
*
* @author Adrian Cole
*/
@Singleton
public class ParseAsyncJobsFromHttpResponse implements Function<HttpResponse, Set<AsyncJob<?>>> {
private final UnwrapOnlyNestedJsonValue<Set<AsyncJob<Map<String, JsonBall>>>> parser;
private final ParseTypedAsyncJob parseTyped;
@Inject
public ParseAsyncJobsFromHttpResponse(ParseTypedAsyncJob parseTyped,
UnwrapOnlyNestedJsonValue<Set<AsyncJob<Map<String, JsonBall>>>> parser) {
this.parseTyped = checkNotNull(parseTyped, "parseTyped");
this.parser = checkNotNull(parser, "parser");
}
public Set<AsyncJob<?>> apply(HttpResponse response) {
checkNotNull(response, "response");
Set<AsyncJob<Map<String, JsonBall>>> toParse = parser.apply(response);
checkNotNull(toParse, "parsed result from %s", response);
return ImmutableSet.copyOf(Iterables.transform(toParse, parseTyped));
}
}

View File

@ -26,18 +26,19 @@ import java.util.Map.Entry;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.cloudstack.domain.AsyncJob; import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.cloudstack.domain.AsyncJob.Builder; import org.jclouds.cloudstack.domain.AsyncJobError;
import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.Network; import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.PortForwardingRule; import org.jclouds.cloudstack.domain.PortForwardingRule;
import org.jclouds.cloudstack.domain.PublicIPAddress; import org.jclouds.cloudstack.domain.PublicIPAddress;
import org.jclouds.cloudstack.domain.SecurityGroup; import org.jclouds.cloudstack.domain.SecurityGroup;
import org.jclouds.cloudstack.domain.Template; import org.jclouds.cloudstack.domain.Template;
import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.domain.AsyncJob.Builder;
import org.jclouds.domain.JsonBall; import org.jclouds.domain.JsonBall;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.json.Json; import org.jclouds.json.Json;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
@ -51,49 +52,57 @@ import com.google.inject.Inject;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class ParseAsyncJob implements Function<HttpResponse, AsyncJob<?>> { @Singleton
public class ParseTypedAsyncJob implements Function<AsyncJob<Map<String, JsonBall>>, AsyncJob<?>> {
@Resource @Resource
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private final Json json;
private final UnwrapOnlyJsonValue<AsyncJob<Map<String, JsonBall>>> parser;
@Inject(optional = true) @Inject(optional = true)
@VisibleForTesting @VisibleForTesting
@Named("jclouds.cloudstack.jobresult-type-map") @Named("jclouds.cloudstack.jobresult-type-map")
Map<String, Class<?>> typeMap = ImmutableMap.<String, Class<?>> builder().put("securitygroup", SecurityGroup.class) Map<String, Class<?>> typeMap = ImmutableMap.<String, Class<?>> builder().put("securitygroup", SecurityGroup.class)
.put("portforwardingrule", PortForwardingRule.class).put("template", Template.class) .put("portforwardingrule", PortForwardingRule.class).put("ipforwardingrule", IPForwardingRule.class).put(
.put("network", Network.class).put("ipaddress", PublicIPAddress.class) "template", Template.class).put("network", Network.class).put("ipaddress", PublicIPAddress.class)
.put("virtualmachine", VirtualMachine.class).build(); .put("virtualmachine", VirtualMachine.class).build();
private final Json json;
@Inject @Inject
public ParseAsyncJob(Json json, UnwrapOnlyJsonValue<AsyncJob<Map<String, JsonBall>>> parser) { public ParseTypedAsyncJob(Json json) {
this.json = checkNotNull(json, "json"); this.json = checkNotNull(json, "json");
this.parser = checkNotNull(parser, "parser");
} }
public AsyncJob<?> apply(HttpResponse response) { public AsyncJob<?> apply(AsyncJob<Map<String, JsonBall>> toParse) {
checkNotNull(response, "response");
AsyncJob<Map<String, JsonBall>> toParse = parser.apply(response);
checkNotNull(toParse, "parsed result from %s", response);
AsyncJob<?> result = toParse; AsyncJob<?> result = toParse;
if (toParse.getResult() != null) { if (toParse.getResult() != null) {
if (toParse.getResult().size() == 1) { if (toParse.getResult().size() == 1) {
Entry<String, JsonBall> entry = Iterables.get(toParse.getResult().entrySet(), 0); @SuppressWarnings( { "unchecked", "rawtypes" })
@SuppressWarnings({ "unchecked", "rawtypes" })
Builder<Object> builder = AsyncJob.Builder.fromAsyncJobUntyped((AsyncJob) toParse); Builder<Object> builder = AsyncJob.Builder.fromAsyncJobUntyped((AsyncJob) toParse);
if (typeMap.containsKey(entry.getKey())) { if (toParse.getResult().containsKey("success")) {
builder.result(json.fromJson(entry.getValue().toString(), typeMap.get(entry.getKey()))); builder.result(null);
} else { } else {
logger.warn( Entry<String, JsonBall> entry = Iterables.get(toParse.getResult().entrySet(), 0);
"type key % not configured. please override default for Map<String, Class<?>> bound to name jclouds.cloudstack.jobresult-type-map", if (typeMap.containsKey(entry.getKey())) {
entry.getKey()); builder.result(json.fromJson(entry.getValue().toString(), typeMap.get(entry.getKey())));
builder.result(entry.getValue().toString()); } else {
logger
.warn(
"type key %s not configured. please override default for Map<String, Class<?>> bound to name jclouds.cloudstack.jobresult-type-map",
entry.getKey());
builder.result(entry.getValue().toString());
}
} }
result = builder.build(); result = builder.build();
} else if (toParse.getResult().containsKey("errorcode")) {
@SuppressWarnings( { "unchecked", "rawtypes" })
Builder<Object> builder = AsyncJob.Builder.fromAsyncJobUntyped((AsyncJob) toParse);
builder.result(null);// avoid classcastexceptions
builder.error(new AsyncJobError(Integer.parseInt(toParse.getResult().get("errorcode").toString()), toParse
.getResult().containsKey("errortext") ? toParse.getResult().get("errortext").toString().replace(
"\"", "") : null));
result = builder.build();
} else if (toParse.getResult().size() > 1) { } else if (toParse.getResult().size() > 1) {
logger.warn("unexpected size of async job result; expecting a map with a single element", logger.warn("unexpected size of async job result; expecting a map with a single element", toParse
toParse.getResult()); .getResult());
} }
} }
return result; return result;

View File

@ -29,9 +29,10 @@ import java.util.concurrent.TimeUnit;
import org.jclouds.cloudstack.CloudStackClient; import org.jclouds.cloudstack.CloudStackClient;
import org.jclouds.cloudstack.domain.AsyncCreateResponse; import org.jclouds.cloudstack.domain.AsyncCreateResponse;
import org.jclouds.cloudstack.domain.NetworkType; import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.NetworkService;
import org.jclouds.cloudstack.domain.PublicIPAddress; import org.jclouds.cloudstack.domain.PublicIPAddress;
import org.jclouds.cloudstack.domain.Zone; import org.jclouds.cloudstack.options.AssociateIPAddressOptions;
import org.jclouds.cloudstack.options.ListPublicIPAddressesOptions; import org.jclouds.cloudstack.options.ListPublicIPAddressesOptions;
import org.jclouds.cloudstack.predicates.JobComplete; import org.jclouds.cloudstack.predicates.JobComplete;
import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.RetryablePredicate;
@ -40,6 +41,7 @@ import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
/** /**
* Tests behavior of {@code PublicIPAddressClientLiveTest} * Tests behavior of {@code PublicIPAddressClientLiveTest}
@ -62,12 +64,20 @@ public class AddressClientLiveTest extends BaseCloudStackClientLiveTest {
checkIP(ip); checkIP(ip);
} }
public static PublicIPAddress createPublicIPAddress(CloudStackClient client, RetryablePredicate<Long> jobComplete) { public static PublicIPAddress createPublicIPAddress(final CloudStackClient client,
Zone zone = find(client.getZoneClient().listZones(), new Predicate<Zone>() { RetryablePredicate<Long> jobComplete) {
Network network = find(client.getNetworkClient().listNetworks(), new Predicate<Network>() {
@Override @Override
public boolean apply(Zone arg0) { public boolean apply(Network arg0) {
return arg0.getNetworkType() == NetworkType.ADVANCED; return Iterables.any(arg0.getServices(), new Predicate<NetworkService>() {
@Override
public boolean apply(NetworkService input) {
return input.getName().equals("Firewall") && "true".equals(input.getCapabilities().get("StaticNat"));
}
});
} }
@Override @Override
@ -75,10 +85,16 @@ public class AddressClientLiveTest extends BaseCloudStackClientLiveTest {
return "networkType(ADVANCED)"; return "networkType(ADVANCED)";
} }
}); });
AsyncCreateResponse job = client.getAddressClient().associateIPAddress(zone.getId()); return createPublicIPAddressInNetwork(network, client, jobComplete);
}
public static PublicIPAddress createPublicIPAddressInNetwork(Network network, CloudStackClient client,
RetryablePredicate<Long> jobComplete) {
AsyncCreateResponse job = client.getAddressClient().associateIPAddress(network.getZoneId(),
AssociateIPAddressOptions.Builder.networkId(network.getId()));
assert jobComplete.apply(job.getJobId()); assert jobComplete.apply(job.getJobId());
PublicIPAddress ip = client.getAddressClient().getPublicIPAddress(job.getId()); PublicIPAddress ip = client.getAsyncJobClient().<PublicIPAddress> getAsyncJob(job.getJobId()).getResult();
assertEquals(ip.getZoneId(), zone.getId()); assertEquals(ip.getZoneId(), network.getZoneId());
return ip; return ip;
} }

View File

@ -22,10 +22,10 @@ package org.jclouds.cloudstack.features;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.jclouds.cloudstack.functions.ParseAsyncJob; import org.jclouds.cloudstack.functions.ParseAsyncJobFromHttpResponse;
import org.jclouds.cloudstack.functions.ParseAsyncJobsFromHttpResponse;
import org.jclouds.cloudstack.options.ListAsyncJobsOptions; import org.jclouds.cloudstack.options.ListAsyncJobsOptions;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.rest.internal.RestAnnotationProcessor;
@ -51,7 +51,7 @@ public class AsyncJobAsyncClientTest extends BaseCloudStackAsyncClientTest<Async
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null, null, false); assertPayloadEquals(httpRequest, null, null, false);
assertResponseParserClassEquals(method, httpRequest, ParseAsyncJob.class); assertResponseParserClassEquals(method, httpRequest, ParseAsyncJobFromHttpResponse.class);
assertSaxResponseParserClassEquals(method, null); assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class); assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
@ -65,10 +65,10 @@ public class AsyncJobAsyncClientTest extends BaseCloudStackAsyncClientTest<Async
assertRequestLineEquals(httpRequest, assertRequestLineEquals(httpRequest,
"GET http://localhost:8080/client/api?response=json&command=listAsyncJobs HTTP/1.1"); "GET http://localhost:8080/client/api?response=json&command=listAsyncJobs HTTP/1.1");
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); assertNonPayloadHeadersEqual(httpRequest, "");
assertPayloadEquals(httpRequest, null, null, false); assertPayloadEquals(httpRequest, null, null, false);
assertResponseParserClassEquals(method, httpRequest, UnwrapOnlyNestedJsonValue.class); assertResponseParserClassEquals(method, httpRequest, ParseAsyncJobsFromHttpResponse.class);
assertSaxResponseParserClassEquals(method, null); assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
@ -83,10 +83,10 @@ public class AsyncJobAsyncClientTest extends BaseCloudStackAsyncClientTest<Async
assertRequestLineEquals(httpRequest, assertRequestLineEquals(httpRequest,
"GET http://localhost:8080/client/api?response=json&command=listAsyncJobs&account=adrian&domainid=5 HTTP/1.1"); "GET http://localhost:8080/client/api?response=json&command=listAsyncJobs&account=adrian&domainid=5 HTTP/1.1");
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); assertNonPayloadHeadersEqual(httpRequest, "");
assertPayloadEquals(httpRequest, null, null, false); assertPayloadEquals(httpRequest, null, null, false);
assertResponseParserClassEquals(method, httpRequest, UnwrapOnlyNestedJsonValue.class); assertResponseParserClassEquals(method, httpRequest, ParseAsyncJobsFromHttpResponse.class);
assertSaxResponseParserClassEquals(method, null); assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);

View File

@ -41,27 +41,31 @@ public class AsyncJobClientLiveTest extends BaseCloudStackClientLiveTest {
long asyncJobCount = response.size(); long asyncJobCount = response.size();
assertTrue(asyncJobCount >= 0); assertTrue(asyncJobCount >= 0);
for (AsyncJob<?> asyncJob : response) { for (AsyncJob<?> asyncJob : response) {
assert asyncJob.getCmd() != null : asyncJob;
assert asyncJob.getUserId() >= 0 : asyncJob;
checkJob(asyncJob);
AsyncJob<?> query = client.getAsyncJobClient().getAsyncJob(asyncJob.getId()); AsyncJob<?> query = client.getAsyncJobClient().getAsyncJob(asyncJob.getId());
assertEquals(query.getId(), asyncJob.getId()); assertEquals(query.getId(), asyncJob.getId());
assert query.getStatus() >= 0 : query;
assert query.getResultCode() >= 0 : query;
assert query.getResultType() != null : query;
assert query.getProgress() >= 0 : query;
assert query.getResult() != null : query;
assert asyncJob.getId() > 0 : asyncJob; assert query.getResultType() != null :query;
assert asyncJob.getAccountId() >= 0 : asyncJob; checkJob(query);
assert asyncJob.getCmd() != null : asyncJob; }
assert asyncJob.getCreated() != null : asyncJob; }
if (asyncJob.getProgress() > 0) {
assert asyncJob.getResult() == null : asyncJob; private void checkJob(AsyncJob<?> query) {
assert asyncJob.getResultCode() == -1 : asyncJob; assert query.getStatus() >= 0 : query;
} else { assert query.getResultCode() >= 0 : query;
assert asyncJob.getResult() != null : asyncJob; assert query.getProgress() >= 0 : query;
assert asyncJob.getResultCode() >= 0 : asyncJob; if (query.getResultCode() == 0) {
} if (query.getResult() != null)// null is ok for result of success = true
assert asyncJob.getStatus() >= 0 : asyncJob; // ensure we parsed properly
assert asyncJob.getUserId() >= 0 : asyncJob; assert (query.getResult().getClass().getPackage().equals(AsyncJob.class.getPackage())) : query;
} else if (query.getResultCode() > 400) {
assert query.getResult() == null : query;
assert query.getError() != null : query;
} else {
assert query.getResult() == null : query;
} }
} }

View File

@ -69,6 +69,7 @@ public class BaseCloudStackClientLiveTest {
protected RetryablePredicate<VirtualMachine> virtualMachineRunning; protected RetryablePredicate<VirtualMachine> virtualMachineRunning;
protected RetryablePredicate<VirtualMachine> virtualMachineDestroyed; protected RetryablePredicate<VirtualMachine> virtualMachineDestroyed;
protected SshClient.Factory sshFactory; protected SshClient.Factory sshFactory;
protected String password = "password";
protected void setupCredentials() { protected void setupCredentials() {
identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider
@ -80,10 +81,9 @@ public class BaseCloudStackClientLiveTest {
apiversion = System.getProperty("test." + provider + ".apiversion"); apiversion = System.getProperty("test." + provider + ".apiversion");
} }
protected void checkSSH(String address, int port) { protected void checkSSH(IPSocket socket) {
IPSocket socket = new IPSocket(address, port);
socketTester.apply(socket); socketTester.apply(socket);
SshClient client = sshFactory.create(socket, new Credentials("root", "password")); SshClient client = sshFactory.create(socket, new Credentials("root", password));
try { try {
client.connect(); client.connect();
ExecResponse exec = client.exec("echo hello"); ExecResponse exec = client.exec("echo hello");

View File

@ -56,6 +56,8 @@ public class FirewallClientLiveTest extends BaseCloudStackClientLiveTest {
prefix += "rule"; prefix += "rule";
ip = AddressClientLiveTest.createPublicIPAddress(client, jobComplete); ip = AddressClientLiveTest.createPublicIPAddress(client, jobComplete);
vm = VirtualMachineClientLiveTest.createVirtualMachine(client, jobComplete, virtualMachineRunning); vm = VirtualMachineClientLiveTest.createVirtualMachine(client, jobComplete, virtualMachineRunning);
if (vm.getPassword() != null)
password = vm.getPassword();
} }
public void testCreatePortForwardingRule() throws Exception { public void testCreatePortForwardingRule() throws Exception {
@ -70,7 +72,7 @@ public class FirewallClientLiveTest extends BaseCloudStackClientLiveTest {
checkRule(rule); checkRule(rule);
IPSocket socket = new IPSocket(ip.getIPAddress(), 22); IPSocket socket = new IPSocket(ip.getIPAddress(), 22);
socketTester.apply(socket); socketTester.apply(socket);
SshClient client = sshFactory.create(socket, new Credentials("root", "password")); SshClient client = sshFactory.create(socket, new Credentials("root", password));
try { try {
client.connect(); client.connect();
ExecResponse exec = client.exec("echo hello"); ExecResponse exec = client.exec("echo hello");

View File

@ -60,6 +60,8 @@ public class LoadBalancerClientLiveTest extends BaseCloudStackClientLiveTest {
prefix += "rule"; prefix += "rule";
ip = AddressClientLiveTest.createPublicIPAddress(client, jobComplete); ip = AddressClientLiveTest.createPublicIPAddress(client, jobComplete);
vm = VirtualMachineClientLiveTest.createVirtualMachine(client, jobComplete, virtualMachineRunning); vm = VirtualMachineClientLiveTest.createVirtualMachine(client, jobComplete, virtualMachineRunning);
if (vm.getPassword() != null)
password = vm.getPassword();
} }
public void testCreateLoadBalancerRule() throws Exception { public void testCreateLoadBalancerRule() throws Exception {

View File

@ -55,6 +55,8 @@ public class NATClientLiveTest extends BaseCloudStackClientLiveTest {
prefix += "nat"; prefix += "nat";
ip = AddressClientLiveTest.createPublicIPAddress(client, jobComplete); ip = AddressClientLiveTest.createPublicIPAddress(client, jobComplete);
vm = VirtualMachineClientLiveTest.createVirtualMachine(client, jobComplete, virtualMachineRunning); vm = VirtualMachineClientLiveTest.createVirtualMachine(client, jobComplete, virtualMachineRunning);
if (vm.getPassword() != null)
password = vm.getPassword();
} }
public void testCreateIPForwardingRule() throws Exception { public void testCreateIPForwardingRule() throws Exception {
@ -74,7 +76,7 @@ public class NATClientLiveTest extends BaseCloudStackClientLiveTest {
checkRule(rule); checkRule(rule);
IPSocket socket = new IPSocket(ip.getIPAddress(), 22); IPSocket socket = new IPSocket(ip.getIPAddress(), 22);
socketTester.apply(socket); socketTester.apply(socket);
SshClient client = sshFactory.create(socket, new Credentials("root", "password")); SshClient client = sshFactory.create(socket, new Credentials("root",password));
try { try {
client.connect(); client.connect();
ExecResponse exec = client.exec("echo hello"); ExecResponse exec = client.exec("echo hello");

View File

@ -55,7 +55,7 @@ public class SecurityGroupClientLiveTest extends BaseCloudStackClientLiveTest {
})) { })) {
for (SecurityGroup securityGroup : client.getSecurityGroupClient().listSecurityGroups( for (SecurityGroup securityGroup : client.getSecurityGroupClient().listSecurityGroups(
ListSecurityGroupsOptions.Builder.named(prefix))) ListSecurityGroupsOptions.Builder.named(prefix)))
client.getSecurityGroupClient().deleteSecurityGroup(securityGroup.getId()); client.getSecurityGroupClient().deleteSecurityGroup(securityGroup.getId());
group = client.getSecurityGroupClient().createSecurityGroup(prefix); group = client.getSecurityGroupClient().createSecurityGroup(prefix);
@ -72,7 +72,7 @@ public class SecurityGroupClientLiveTest extends BaseCloudStackClientLiveTest {
client.getSecurityGroupClient().createSecurityGroup(prefix); client.getSecurityGroupClient().createSecurityGroup(prefix);
assert false; assert false;
} catch (HttpResponseException e) { } catch (HttpResponseException e) {
assertEquals(e.getResponse().getStatusCode(), 530);
} }
} }
@ -85,7 +85,7 @@ public class SecurityGroupClientLiveTest extends BaseCloudStackClientLiveTest {
assertTrue(groupCount >= 0); assertTrue(groupCount >= 0);
for (SecurityGroup group : response) { for (SecurityGroup group : response) {
SecurityGroup newDetails = Iterables.getOnlyElement(client.getSecurityGroupClient().listSecurityGroups( SecurityGroup newDetails = Iterables.getOnlyElement(client.getSecurityGroupClient().listSecurityGroups(
ListSecurityGroupsOptions.Builder.id(group.getId()))); ListSecurityGroupsOptions.Builder.id(group.getId())));
assertEquals(group.getId(), newDetails.getId()); assertEquals(group.getId(), newDetails.getId());
// sometimes this comes up different // sometimes this comes up different
// assertEquals(group,newDetails); // assertEquals(group,newDetails);

View File

@ -36,15 +36,15 @@ import org.jclouds.cloudstack.domain.AsyncCreateResponse;
import org.jclouds.cloudstack.domain.GuestIPType; import org.jclouds.cloudstack.domain.GuestIPType;
import org.jclouds.cloudstack.domain.NIC; import org.jclouds.cloudstack.domain.NIC;
import org.jclouds.cloudstack.domain.Network; import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.NetworkType;
import org.jclouds.cloudstack.domain.SecurityGroup; import org.jclouds.cloudstack.domain.SecurityGroup;
import org.jclouds.cloudstack.domain.ServiceOffering; import org.jclouds.cloudstack.domain.ServiceOffering;
import org.jclouds.cloudstack.domain.Template; import org.jclouds.cloudstack.domain.Template;
import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.domain.Zone;
import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
import org.jclouds.cloudstack.options.ListVirtualMachinesOptions; import org.jclouds.cloudstack.options.ListVirtualMachinesOptions;
import org.jclouds.net.IPSocket;
import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.util.InetAddresses2;
import org.testng.annotations.AfterGroups; import org.testng.annotations.AfterGroups;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -52,6 +52,7 @@ import com.google.common.base.Predicate;
import com.google.common.collect.ComparisonChain; import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.net.HostSpecifier;
/** /**
* Tests behavior of {@code VirtualMachineClientLiveTest} * Tests behavior of {@code VirtualMachineClientLiveTest}
@ -64,14 +65,46 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
static final Ordering<ServiceOffering> DEFAULT_SIZE_ORDERING = new Ordering<ServiceOffering>() { static final Ordering<ServiceOffering> DEFAULT_SIZE_ORDERING = new Ordering<ServiceOffering>() {
public int compare(ServiceOffering left, ServiceOffering right) { public int compare(ServiceOffering left, ServiceOffering right) {
return ComparisonChain.start().compare(left.getCpuNumber(), right.getCpuNumber()) return ComparisonChain.start().compare(left.getCpuNumber(), right.getCpuNumber()).compare(left.getMemory(),
.compare(left.getMemory(), right.getMemory()).result(); right.getMemory()).result();
} }
}; };
public static VirtualMachine createVirtualMachine(CloudStackClient client, RetryablePredicate<Long> jobComplete, public static VirtualMachine createVirtualMachine(CloudStackClient client, RetryablePredicate<Long> jobComplete,
RetryablePredicate<VirtualMachine> virtualMachineRunning) { RetryablePredicate<VirtualMachine> virtualMachineRunning) {
final Zone zone = get(client.getZoneClient().listZones(), 0); Set<Network> networks = client.getNetworkClient().listNetworks();
if (networks.size() > 0) {
return createVirtualMachineInNetwork(Iterables.get(networks, 0), client, jobComplete, virtualMachineRunning);
} else {
return createVirtualMachineWithSecurityGroup(find(client.getSecurityGroupClient().listSecurityGroups(),
new Predicate<SecurityGroup>() {
@Override
public boolean apply(SecurityGroup arg0) {
return arg0.getName().equals("default");
}
}), client, jobComplete, virtualMachineRunning);
}
}
public static VirtualMachine createVirtualMachineWithSecurityGroup(SecurityGroup group, CloudStackClient client,
RetryablePredicate<Long> jobComplete, RetryablePredicate<VirtualMachine> virtualMachineRunning) {
return createVirtualMachineWithOptionsInZone(new DeployVirtualMachineOptions().securityGroupId(group.getId()),
get(client.getZoneClient().listZones(), 0).getId(), client, jobComplete, virtualMachineRunning);
}
public static VirtualMachine createVirtualMachineInNetwork(Network network, CloudStackClient client,
RetryablePredicate<Long> jobComplete, RetryablePredicate<VirtualMachine> virtualMachineRunning) {
DeployVirtualMachineOptions options = new DeployVirtualMachineOptions();
long zoneId = network.getZoneId();
options.networkId(network.getId());
return createVirtualMachineWithOptionsInZone(options, zoneId, client, jobComplete, virtualMachineRunning);
}
public static VirtualMachine createVirtualMachineWithOptionsInZone(DeployVirtualMachineOptions options,
final long zoneId, CloudStackClient client, RetryablePredicate<Long> jobComplete,
RetryablePredicate<VirtualMachine> virtualMachineRunning) {
long serviceOfferingId = DEFAULT_SIZE_ORDERING.min(client.getOfferingClient().listServiceOfferings()).getId(); long serviceOfferingId = DEFAULT_SIZE_ORDERING.min(client.getOfferingClient().listServiceOfferings()).getId();
@ -79,8 +112,8 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
@Override @Override
public boolean apply(Template arg0) { public boolean apply(Template arg0) {
return arg0.getZoneId() == zone.getId() && arg0.isFeatured() && arg0.isReady() return arg0.isReady() && (arg0.isCrossZones() || arg0.getZoneId() == zoneId)
&& or(equalTo("Ubuntu 10.04 (64-bit)"), equalTo("CentOS 5.3 (64-bit)")).apply(arg0.getOSType()); && or(equalTo("Ubuntu 10.04 (64-bit)"), equalTo("CentOS 5.3 (64-bit)")).apply(arg0.getOSType());
} }
}); });
@ -102,48 +135,45 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
templateId = get(templates, 0).getId(); templateId = get(templates, 0).getId();
} }
DeployVirtualMachineOptions options = new DeployVirtualMachineOptions();
if (zone.getNetworkType() == NetworkType.ADVANCED) {
options.networkId(find(client.getNetworkClient().listNetworks(), new Predicate<Network>() {
@Override
public boolean apply(Network arg0) {
return arg0.getZoneId() == zone.getId();
}
}).getId());
} else {
options.securityGroupId(find(client.getSecurityGroupClient().listSecurityGroups(),
new Predicate<SecurityGroup>() {
@Override
public boolean apply(SecurityGroup arg0) {
return arg0.getName().equals("default");
}
}).getId());
}
System.out.printf("serviceOfferingId %d, templateId %d, zoneId %d, options %s%n", serviceOfferingId, templateId, System.out.printf("serviceOfferingId %d, templateId %d, zoneId %d, options %s%n", serviceOfferingId, templateId,
zone.getId(), options); zoneId, options);
AsyncCreateResponse job = client.getVirtualMachineClient().deployVirtualMachine(serviceOfferingId, templateId, AsyncCreateResponse job = client.getVirtualMachineClient().deployVirtualMachine(serviceOfferingId, templateId,
zone.getId(), options); zoneId, options);
assert jobComplete.apply(job.getJobId()); assert jobComplete.apply(job.getJobId());
VirtualMachine vm = client.getAsyncJobClient().<VirtualMachine> getAsyncJob(job.getJobId()).getResult(); VirtualMachine vm = client.getAsyncJobClient().<VirtualMachine> getAsyncJob(job.getJobId()).getResult();
if (vm.isPasswordEnabled()) if (vm.isPasswordEnabled()) {
assert vm.getPassword() != null : vm; assert vm.getPassword() != null : vm;
}
assert virtualMachineRunning.apply(vm); assert virtualMachineRunning.apply(vm);
assertEquals(vm.getServiceOfferingId(), serviceOfferingId); assertEquals(vm.getServiceOfferingId(), serviceOfferingId);
assertEquals(vm.getTemplateId(), templateId); assertEquals(vm.getTemplateId(), templateId);
assertEquals(vm.getZoneId(), zone.getId()); assertEquals(vm.getZoneId(), zoneId);
return vm; return vm;
} }
public void testCreateVirtualMachine() throws Exception { public void testCreateVirtualMachine() throws Exception {
vm = createVirtualMachine(client, jobComplete, virtualMachineRunning); vm = createVirtualMachine(client, jobComplete, virtualMachineRunning);
if (vm.getPassword() != null) {
conditionallyCheckSSH();
}
assert or(equalTo("NetworkFilesystem"), equalTo("IscsiLUN")).apply(vm.getRootDeviceType()) : vm; assert or(equalTo("NetworkFilesystem"), equalTo("IscsiLUN")).apply(vm.getRootDeviceType()) : vm;
checkVm(vm); checkVm(vm);
} }
private void conditionallyCheckSSH() {
password = vm.getPassword();
assert HostSpecifier.isValid(vm.getIPAddress());
if (!InetAddresses2.isPrivateIPAddress(vm.getIPAddress())) {
// not sure if the network is public or not, so we have to test
IPSocket socket = new IPSocket(vm.getIPAddress(), 22);
System.err.printf("testing socket %s%n", socket);
System.err.printf("testing ssh %s%n", socket);
this.checkSSH(socket);
} else {
System.err.printf("skipping ssh %s, as private%n", vm.getIPAddress());
}
}
@Test(dependsOnMethods = "testCreateVirtualMachine") @Test(dependsOnMethods = "testCreateVirtualMachine")
public void testLifeCycle() throws Exception { public void testLifeCycle() throws Exception {
Long job = client.getVirtualMachineClient().stopVirtualMachine(vm.getId()); Long job = client.getVirtualMachineClient().stopVirtualMachine(vm.getId());
@ -153,8 +183,10 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
job = client.getVirtualMachineClient().resetPasswordForVirtualMachine(vm.getId()); job = client.getVirtualMachineClient().resetPasswordForVirtualMachine(vm.getId());
assert jobComplete.apply(job); assert jobComplete.apply(job);
vm = client.getVirtualMachineClient().getVirtualMachine(vm.getId()); vm = client.getAsyncJobClient().<VirtualMachine> getAsyncJob(job).getResult();
// TODO: check if error or not base on isPasswordEnabled if (vm.getPassword() != null) {
conditionallyCheckSSH();
}
job = client.getVirtualMachineClient().startVirtualMachine(vm.getId()); job = client.getVirtualMachineClient().startVirtualMachine(vm.getId());
assert jobComplete.apply(job); assert jobComplete.apply(job);
@ -184,7 +216,7 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
assertTrue(response.size() >= 0); assertTrue(response.size() >= 0);
for (VirtualMachine vm : response) { for (VirtualMachine vm : response) {
VirtualMachine newDetails = getOnlyElement(client.getVirtualMachineClient().listVirtualMachines( VirtualMachine newDetails = getOnlyElement(client.getVirtualMachineClient().listVirtualMachines(
ListVirtualMachinesOptions.Builder.id(vm.getId()))); ListVirtualMachinesOptions.Builder.id(vm.getId())));
assertEquals(vm.getId(), newDetails.getId()); assertEquals(vm.getId(), newDetails.getId());
checkVm(vm); checkVm(vm);
} }
@ -204,7 +236,6 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
assert vm.getZoneName() != null : vm; assert vm.getZoneName() != null : vm;
assert vm.getTemplateId() > 0 : vm; assert vm.getTemplateId() > 0 : vm;
assert vm.getTemplateName() != null : vm; assert vm.getTemplateName() != null : vm;
assert vm.getTemplateDisplayText() != null : vm;
assert vm.getServiceOfferingId() > 0 : vm; assert vm.getServiceOfferingId() > 0 : vm;
assert vm.getServiceOfferingName() != null : vm; assert vm.getServiceOfferingName() != null : vm;
assert vm.getCpuCount() > 0 : vm; assert vm.getCpuCount() > 0 : vm;
@ -222,22 +253,22 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
assert nic.getTrafficType() != null : vm; assert nic.getTrafficType() != null : vm;
assert nic.getGuestIPType() != null : vm; assert nic.getGuestIPType() != null : vm;
switch (vm.getState()) { switch (vm.getState()) {
case RUNNING: case RUNNING:
assert nic.getNetmask() != null : vm;
assert nic.getGateway() != null : vm;
assert nic.getIPAddress() != null : vm;
break;
default:
if (nic.getGuestIPType() == GuestIPType.VIRTUAL) {
assert nic.getNetmask() != null : vm; assert nic.getNetmask() != null : vm;
assert nic.getGateway() != null : vm; assert nic.getGateway() != null : vm;
assert nic.getIPAddress() != null : vm; assert nic.getIPAddress() != null : vm;
} else { break;
assert nic.getNetmask() == null : vm; default:
assert nic.getGateway() == null : vm; if (nic.getGuestIPType() == GuestIPType.VIRTUAL) {
assert nic.getIPAddress() == null : vm; assert nic.getNetmask() != null : vm;
} assert nic.getGateway() != null : vm;
break; assert nic.getIPAddress() != null : vm;
} else {
assert nic.getNetmask() == null : vm;
assert nic.getGateway() == null : vm;
assert nic.getIPAddress() == null : vm;
}
break;
} }
} }

View File

@ -0,0 +1,171 @@
/**
*
* Copyright (C) 2010 Cloud Conscious) LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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 static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.cloudstack.domain.AsyncJobError;
import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.PublicIPAddress;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.domain.JsonBall;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit")
public class ParseAsyncJobFromHttpResponseTest {
Injector i = Guice.createInjector(new GsonModule() {
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);
super.configure();
}
});
public void testWithNoResult() {
String input = "{ \"queryasyncjobresultresponse\" : {\"jobid\":860,\"jobstatus\":0,\"jobprocstatus\":0,\"jobresultcode\":0} }";
AsyncJob<PublicIPAddress> expects = AsyncJob.<PublicIPAddress> builder().id(860).status(0).progress(0)
.resultCode(0).build();
ParseAsyncJobFromHttpResponse parser = i.getInstance(ParseAsyncJobFromHttpResponse.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newStringPayload(input)));
assertEquals(response, expects);
}
public void testWithSuccessTrueResultSetsNullResult() {
String input = "{ \"queryasyncjobresultresponse\" : {\"jobid\":1138,\"jobstatus\":1,\"jobprocstatus\":0,\"jobresultcode\":0,\"jobresulttype\":\"object\",\"jobresult\":{\"success\":true}} }";
AsyncJob<PublicIPAddress> expects = AsyncJob.<PublicIPAddress> builder().id(1138).status(1).progress(0)
.resultType("object").resultCode(0).build();
ParseAsyncJobFromHttpResponse parser = i.getInstance(ParseAsyncJobFromHttpResponse.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newStringPayload(input)));
assertEquals(response, expects);
}
public void testWithErrorSetsResultNullSoToAvoidClassCastExceptions() {
String input = "{ \"queryasyncjobresultresponse\" : {\"jobid\":1103,\"jobstatus\":2,\"jobprocstatus\":0,\"jobresultcode\":530,\"jobresulttype\":\"object\",\"jobresult\":{\"errorcode\":530,\"errortext\":\"Internal error executing command, please contact your system administrator\"}} }";
AsyncJob<PublicIPAddress> expects = AsyncJob.<PublicIPAddress> builder().id(1103).status(2).progress(0)
.resultType("object").error(
new AsyncJobError(530,
"Internal error executing command, please contact your system administrator"))
.resultCode(530).build();
ParseAsyncJobFromHttpResponse parser = i.getInstance(ParseAsyncJobFromHttpResponse.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newStringPayload(input)));
assertEquals(response, expects);
}
public void testWithUnknownResultReturnsStringifiedJson() {
String input = "{ \"queryasyncjobresultresponse\" : {\"jobid\":860,\"jobstatus\":0,\"jobprocstatus\":0,\"jobresultcode\":0,\"jobresult\":{\"foo\":{\"bar\":1}}}}";
AsyncJob<?> expects = AsyncJob.builder().id(860).status(0).progress(0).resultCode(0).result("{\"bar\":1}")
.build();
ParseAsyncJobFromHttpResponse parser = i.getInstance(ParseAsyncJobFromHttpResponse.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newStringPayload(input)));
assertEquals(response, expects);
}
public void testWithBadResultReturnsMap() {
// Not the best result object, but this is an unexpected error case. Cloud.com have verified
// that this case will not happen. This code is only here to prevent exceptions from being
// thrown in case they change their minds.
String input = "{ \"queryasyncjobresultresponse\" : {\"jobid\":860,\"jobstatus\":0,\"jobprocstatus\":0,\"jobresultcode\":0,\"jobresult\":{\"foo\":{\"bar\":1},\"foo2\":{\"bar2\":2}}}}";
AsyncJob<?> expects = AsyncJob.builder().id(860).status(0).progress(0).resultCode(0).result(
ImmutableMap.of("foo", new JsonBall("{\"bar\":1}"), "foo2", new JsonBall("{\"bar2\":2}"))).build();
ParseAsyncJobFromHttpResponse parser = i.getInstance(ParseAsyncJobFromHttpResponse.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newStringPayload(input)));
assertEquals(response, expects);
}
public void testPublicIPAddress() {
InputStream is = getClass().getResourceAsStream("/queryasyncjobresultresponse-ipaddress.json");
AsyncJob<PublicIPAddress> expects = AsyncJob.<PublicIPAddress> builder().id(860).status(1).progress(0)
.resultType("object").resultCode(0).result(
PublicIPAddress.builder().id(6).IPAddress("72.52.126.35").allocated(
new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-02-23T20:15:01-0800"))
.zoneId(1).zoneName("San Jose 1").isSourceNAT(false).account("adrian").domainId(1)
.domain("ROOT").usesVirtualNetwork(true).isStaticNAT(false).associatedNetworkId(204)
.networkId(200).state(PublicIPAddress.State.ALLOCATING).build()
).build();
ParseAsyncJobFromHttpResponse parser = i.getInstance(ParseAsyncJobFromHttpResponse.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newInputStreamPayload(is)));
assertEquals(response, expects);
}
public void testIPForwardingRule() {
InputStream is = getClass().getResourceAsStream("/queryasyncjobresultresponse-ipforwardingrule.json");
AsyncJob<IPForwardingRule> expects = AsyncJob.<IPForwardingRule> builder().id(1133).status(1).progress(0)
.resultType("object").resultCode(0).result(
IPForwardingRule.builder().id(109).protocol("tcp").virtualMachineId(226).virtualMachineName(
"i-3-226-VM").IPAddressId(36).IPAddress("72.52.126.65").startPort(22).endPort(22)
.state("Active").build()
).build();
ParseAsyncJobFromHttpResponse parser = i.getInstance(ParseAsyncJobFromHttpResponse.class);
@SuppressWarnings("unchecked")
AsyncJob<IPForwardingRule> response = (AsyncJob<IPForwardingRule>) parser.apply(new HttpResponse(200, "ok",
Payloads.newInputStreamPayload(is)));
assertEquals(response, expects);
}
}

View File

@ -1,130 +0,0 @@
/**
*
* Copyright (C) 2010 Cloud Conscious) LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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 static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.cloudstack.domain.PublicIPAddress;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.domain.JsonBall;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit")
public class ParseAsyncJobTest {
Injector i = Guice.createInjector(new GsonModule() {
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);
super.configure();
}
});
public void testWithNoResult() {
String input = "{ \"queryasyncjobresultresponse\" : {\"jobid\":860,\"jobstatus\":0,\"jobprocstatus\":0,\"jobresultcode\":0} }";
AsyncJob<PublicIPAddress> expects = AsyncJob.<PublicIPAddress> builder().id(860).status(0).progress(0)
.resultCode(0).build();
ParseAsyncJob parser = i.getInstance(ParseAsyncJob.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newStringPayload(input)));
assertEquals(response, expects);
}
public void testWithUnknownResultReturnsStringifiedJson() {
String input = "{ \"queryasyncjobresultresponse\" : {\"jobid\":860,\"jobstatus\":0,\"jobprocstatus\":0,\"jobresultcode\":0,\"jobresult\":{\"foo\":{\"bar\":1}}}}";
AsyncJob<?> expects = AsyncJob.builder().id(860).status(0).progress(0).resultCode(0).result("{\"bar\":1}")
.build();
ParseAsyncJob parser = i.getInstance(ParseAsyncJob.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newStringPayload(input)));
assertEquals(response, expects);
}
public void testWithBadResultReturnsMap() {
// Not the best result object, but this is an unexpected error case. Cloud.com have verified
// that this case will not happen. This code is only here to prevent exceptions from being
// thrown in case they change their minds.
String input = "{ \"queryasyncjobresultresponse\" : {\"jobid\":860,\"jobstatus\":0,\"jobprocstatus\":0,\"jobresultcode\":0,\"jobresult\":{\"foo\":{\"bar\":1},\"foo2\":{\"bar2\":2}}}}";
AsyncJob<?> expects = AsyncJob.builder().id(860).status(0).progress(0).resultCode(0)
.result(ImmutableMap.of("foo", new JsonBall("{\"bar\":1}"), "foo2", new JsonBall("{\"bar2\":2}"))).build();
ParseAsyncJob parser = i.getInstance(ParseAsyncJob.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newStringPayload(input)));
assertEquals(response, expects);
}
public void testPublicIPAddress() {
InputStream is = getClass().getResourceAsStream("/queryasyncjobresultresponse-ipaddress.json");
AsyncJob<PublicIPAddress> expects = AsyncJob
.<PublicIPAddress> builder()
.id(860)
.status(1)
.progress(0)
.resultType("object")
.resultCode(0)
.result(
PublicIPAddress
.builder()
.id(6)
.IPAddress("72.52.126.35")
.allocated(
new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-02-23T20:15:01-0800"))
.zoneId(1).zoneName("San Jose 1").isSourceNAT(false).account("adrian").domainId(1)
.domain("ROOT").usesVirtualNetwork(true).isStaticNAT(false).associatedNetworkId(204)
.networkId(200).state(PublicIPAddress.State.ALLOCATING).build()
).build();
ParseAsyncJob parser = i.getInstance(ParseAsyncJob.class);
@SuppressWarnings("unchecked")
AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) parser.apply(new HttpResponse(200, "ok",
Payloads.newInputStreamPayload(is)));
assertEquals(response, expects);
}
}

View File

@ -0,0 +1,62 @@
/**
*
* Copyright (C) 2010 Cloud Conscious) LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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 static org.testng.Assert.assertEquals;
import java.io.InputStream;
import java.util.Set;
import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit")
public class ParseAsyncJobsFromHttpResponseTest {
Injector i = Guice.createInjector(new GsonModule() {
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);
super.configure();
}
});
public void testCanParse() {
InputStream is = getClass().getResourceAsStream("/listasyncjobsresponse.json");
ParseAsyncJobsFromHttpResponse parser = i.getInstance(ParseAsyncJobsFromHttpResponse.class);
Set<AsyncJob<?>> response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is)));
assertEquals(response.size(), 77);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
{ "queryasyncjobresultresponse" : {"jobid":1133,"jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"ipforwardingrule":{"id":109,"protocol":"tcp","virtualmachineid":226,"virtualmachinename":"i-3-226-VM","ipaddressid":36,"ipaddress":"72.52.126.65","startport":22,"endport":22,"state":"Active"}}} }