diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java index feef8869bd..d7065b96dc 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java @@ -42,11 +42,9 @@ import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.domain.Location; import org.jclouds.functions.IdentityFunction; -import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import com.google.common.base.Supplier; import com.google.common.collect.ImmutableMap; import com.google.inject.Provides; import com.google.inject.TypeLiteral; diff --git a/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaComputeServiceContextModule.java b/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaComputeServiceContextModule.java index cede4c4e9e..6d596eaffd 100644 --- a/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaComputeServiceContextModule.java +++ b/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaComputeServiceContextModule.java @@ -78,7 +78,7 @@ public class CloudSigmaComputeServiceContextModule @Override protected TemplateBuilder provideTemplate(Injector injector, TemplateBuilder template) { - return template.osFamily(OsFamily.UBUNTU).imageNameMatches(".*automated SSH Access.*"); + return template.osFamily(OsFamily.UBUNTU).imageNameMatches(".*[Aa]utomated SSH Access.*").os64Bit(true); } @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java index 5601886c75..c62af90915 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java @@ -64,6 +64,7 @@ import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.predicates.Retryables; import org.jclouds.scriptbuilder.functions.InitAdminAccess; import com.google.common.annotations.VisibleForTesting; @@ -195,7 +196,7 @@ public class EC2ComputeService extends BaseComputeService { } } - protected void cleanUpIncidentalResources(String region, String group){ + protected void cleanUpIncidentalResources(final String region, final String group){ // For issue #445, tries to delete security groups first: ec2 throws exception if in use, but // deleting a key pair does not. // This is "belt-and-braces" because deleteKeyPair also does extractIdsFromInstances & usingKeyPairAndNotDead @@ -203,14 +204,26 @@ public class EC2ComputeService extends BaseComputeService { // There is (probably?) still a race if someone is creating instances at the same time as deleting them: // we may delete the key-pair just when the node-being-created was about to rely on the incidental // resources existing. - try { - logger.debug(">> deleting incidentalResources(%s @ %s)", region, group); - deleteSecurityGroup(region, group); - deleteKeyPair(region, group); // not executed if securityGroup was in use - logger.debug("<< deleted incidentalResources(%s @ %s)", region, group); - } catch (IllegalStateException e) { - logger.debug("<< inUse incidentalResources(%s @ %s)", region, group); - } + + // Also in #445, in aws-ec2 the deleteSecurityGroup sometimes fails after terminating the final VM using a + // given security group, if called very soon after the VM's state reports terminated. Emprically, it seems that + // waiting a small time (e.g. enabling logging or debugging!) then the tests pass. We therefore retry. + final int maxAttempts = 3; + Retryables.retryNumTimes(new Predicate() { + @Override + public boolean apply(Void input) { + try { + logger.debug(">> deleting incidentalResources(%s @ %s)", region, group); + deleteSecurityGroup(region, group); + deleteKeyPair(region, group); // not executed if securityGroup was in use + logger.debug("<< deleted incidentalResources(%s @ %s)", region, group); + return true; + } catch (IllegalStateException e) { + logger.debug("<< inUse incidentalResources(%s @ %s)", region, group); + return false; + } + } + }, (Void)null, maxAttempts); } /** diff --git a/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java b/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java index c899b8a3c9..1adf14abed 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java @@ -25,6 +25,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.net.URI; +import org.jclouds.javax.annotation.Nullable; + import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; @@ -50,6 +52,10 @@ public class Link { * a permanent link to a resource that is appropriate for long term storage. */ BOOKMARK, + /** + * + */ + DESCRIBEDBY, /** * an alternate representation of the resource. For example, an OpenStack Compute image may * have an alternate representation in the OpenStack Image service. @@ -75,9 +81,13 @@ public class Link { } public static Link create(Relation relation, URI href) { - return new Link(relation, href); + return new Link(relation, null, href); } - + + public static Link create(Relation relation,String type, URI href) { + return new Link(relation, type, href); + } + public static Builder builder() { return new Builder(); } @@ -88,6 +98,7 @@ public class Link { public static class Builder { protected Relation relation; + protected String type; protected URI href; /** @@ -98,25 +109,39 @@ public class Link { return this; } + /** + * @see Link#getType() + */ + public Builder type(String type) { + this.type = type; + return this; + } + /** * @see Link#getHref() */ - protected Builder href(URI href) { + public Builder href(URI href) { this.href = checkNotNull(href, "href"); return this; } + public Link build(){ + return new Link(relation, type, href); + } + public Builder fromLink(Link from) { - return relation(from.getRelation()).href(from.getHref()); + return relation(from.getRelation()).type(from.getType()).href(from.getHref()); } } @SerializedName("rel") protected final Relation relation; + protected final String type; protected final URI href; - protected Link(Relation relation, URI href) { + protected Link(Relation relation, @Nullable String type, URI href) { this.relation = checkNotNull(relation, "relation"); + this.type = type; this.href = checkNotNull(href, "href"); } @@ -134,7 +159,15 @@ public class Link { public Relation getRelation() { return relation; } - + + /** + * @return the type of the resource or null if not specified + */ + @Nullable + public String getType() { + return type; + } + /** * @return the href of the resource */ @@ -149,7 +182,7 @@ public class Link { } if (object instanceof Link) { final Link other = Link.class.cast(object); - return equal(relation, other.relation) && equal(href, other.href); + return equal(relation, other.relation) && equal(type, other.type) && equal(href, other.href); } else { return false; } @@ -157,12 +190,12 @@ public class Link { @Override public int hashCode() { - return Objects.hashCode(relation, href); + return Objects.hashCode(relation, type, href); } @Override public String toString() { - return toStringHelper("").add("relation", relation).add("href", href).toString(); + return toStringHelper("").add("relation", relation).add("type", type).add("href", href).toString(); } } diff --git a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java index a1d6d6f64e..80168be3a0 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java @@ -132,7 +132,7 @@ public class Resource implements Comparable { } if (object instanceof Resource) { final Resource other = Resource.class.cast(object); - return equal(id, other.id) && equal(name, other.name) && equal(links, other.links); + return equal(getId(), other.getId()) && equal(name, other.name) && equal(links, other.links); } else { return false; } @@ -140,12 +140,12 @@ public class Resource implements Comparable { @Override public int hashCode() { - return Objects.hashCode(id, name, links); + return Objects.hashCode(getId(), name, links); } @Override public String toString() { - return toStringHelper("").add("id", id).add("name", name).add("links", links).toString(); + return toStringHelper("").add("id", getId()).add("name", name).add("links", links).toString(); } @Override @@ -154,7 +154,7 @@ public class Resource implements Comparable { return 1; if (this == that) return 0; - return this.id.compareTo(that.id); + return this.getId().compareTo(that.getId()); } } diff --git a/common/openstack/src/main/java/org/jclouds/openstack/predicates/LinkPredicates.java b/common/openstack/src/main/java/org/jclouds/openstack/predicates/LinkPredicates.java new file mode 100644 index 0000000000..6cddfcf420 --- /dev/null +++ b/common/openstack/src/main/java/org/jclouds/openstack/predicates/LinkPredicates.java @@ -0,0 +1,103 @@ +/* + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; + +import org.jclouds.openstack.domain.Link; +import org.jclouds.openstack.domain.Link.Relation; + +import com.google.common.base.Predicate; + +/** + * Predicates handy when working with Link Types + * + * @author Adrian Cole + */ + +public class LinkPredicates { + /** + * matches links of the given relation + * + * @param rel relation of the link + * @return predicate that will match links of the given rel + */ + public static Predicate relationEquals(final Relation rel) { + checkNotNull(rel, "rel must be defined"); + + return new Predicate() { + @Override + public boolean apply(Link link) { + return rel.equals(link.getRelation()); + } + + @Override + public String toString() { + return "relEquals(" + rel + ")"; + } + }; + } + + /** + * matches links of the given href + * + * @param href + * @return predicate that will match links of the given href + */ + public static Predicate hrefEquals(final URI href) { + checkNotNull(href, "href must be defined"); + + return new Predicate() { + @Override + public boolean apply(Link link) { + return href.equals(link.getHref()); + } + + @Override + public String toString() { + return "hrefEquals(" + href + ")"; + } + }; + } + + /** + * matches links of the given type + * + * @param type + * ex. application/pdf + * @return predicate that will match links of the given type + */ + public static Predicate typeEquals(final String type) { + checkNotNull(type, "type must be defined"); + + return new Predicate() { + @Override + public boolean apply(Link link) { + return type.equals(link.getType()); + } + + @Override + public String toString() { + return "typeEquals(" + type + ")"; + } + }; + } +} diff --git a/common/openstack/src/test/java/org/jclouds/openstack/predicates/LinkPredicatesTest.java b/common/openstack/src/test/java/org/jclouds/openstack/predicates/LinkPredicatesTest.java new file mode 100644 index 0000000000..c1834831e9 --- /dev/null +++ b/common/openstack/src/test/java/org/jclouds/openstack/predicates/LinkPredicatesTest.java @@ -0,0 +1,51 @@ +package org.jclouds.openstack.predicates; + +import static org.jclouds.openstack.predicates.LinkPredicates.hrefEquals; +import static org.jclouds.openstack.predicates.LinkPredicates.relationEquals; +import static org.jclouds.openstack.predicates.LinkPredicates.typeEquals; + +import java.net.URI; + +import org.jclouds.openstack.domain.Link; +import org.jclouds.openstack.domain.Link.Relation; +import org.testng.annotations.Test; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "LinkPredicatesTest") +public class LinkPredicatesTest { + Link ref = Link.builder().type("application/pdf").relation(Relation.DESCRIBEDBY).href( + URI.create("http://docs.openstack.org/ext/keypairs/api/v1.1")).build(); + + @Test + public void testRelationEqualsWhenEqual() { + assert relationEquals(Relation.DESCRIBEDBY).apply(ref); + } + + @Test + public void testRelationEqualsWhenNotEqual() { + assert !relationEquals(Relation.UNRECOGNIZED).apply(ref); + } + + @Test + public void testTypeEqualsWhenEqual() { + assert typeEquals("application/pdf").apply(ref); + } + + @Test + public void testTypeEqualsWhenNotEqual() { + assert !typeEquals("foo").apply(ref); + } + + @Test + public void testHrefEqualsWhenEqual() { + assert hrefEquals(URI.create("http://docs.openstack.org/ext/keypairs/api/v1.1")).apply(ref); + } + + @Test + public void testHrefEqualsWhenNotEqual() { + assert !hrefEquals(URI.create("foo")).apply(ref); + } +} diff --git a/compute/src/main/java/org/jclouds/compute/ComputeServiceAdapter.java b/compute/src/main/java/org/jclouds/compute/ComputeServiceAdapter.java index a9fa7e71b8..f39e820e43 100644 --- a/compute/src/main/java/org/jclouds/compute/ComputeServiceAdapter.java +++ b/compute/src/main/java/org/jclouds/compute/ComputeServiceAdapter.java @@ -130,6 +130,7 @@ public interface ComputeServiceAdapter { N getNode(String id); + //TODO consider making reboot/resume/suspend return the node they affected void destroyNode(String id); void rebootNode(String id); diff --git a/compute/src/main/java/org/jclouds/compute/predicates/AtomicNodeSuspended.java b/compute/src/main/java/org/jclouds/compute/predicates/AtomicNodeSuspended.java index 0eb86b13e4..3e94459195 100644 --- a/compute/src/main/java/org/jclouds/compute/predicates/AtomicNodeSuspended.java +++ b/compute/src/main/java/org/jclouds/compute/predicates/AtomicNodeSuspended.java @@ -23,6 +23,7 @@ import javax.inject.Singleton; import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.strategy.GetNodeMetadataStrategy; +import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; /** @@ -36,6 +37,6 @@ public class AtomicNodeSuspended extends RefreshAndDoubleCheckOnFailUnlessStateI @Inject public AtomicNodeSuspended(GetNodeMetadataStrategy client) { - super(NodeState.SUSPENDED, client); + super(NodeState.SUSPENDED, ImmutableSet.of(NodeState.ERROR, NodeState.TERMINATED), client); } } diff --git a/compute/src/main/java/org/jclouds/compute/strategy/impl/AdaptingComputeServiceStrategies.java b/compute/src/main/java/org/jclouds/compute/strategy/impl/AdaptingComputeServiceStrategies.java index 5651be3689..0713c52abc 100644 --- a/compute/src/main/java/org/jclouds/compute/strategy/impl/AdaptingComputeServiceStrategies.java +++ b/compute/src/main/java/org/jclouds/compute/strategy/impl/AdaptingComputeServiceStrategies.java @@ -116,31 +116,36 @@ public class AdaptingComputeServiceStrategies implements CreateNodeW return nodeMetadataAdapter.apply(node); } + //TODO: make reboot/resume/suspend return the node they affected @Override public NodeMetadata rebootNode(String id) { NodeMetadata node = getNode(checkNotNull(id, "id")); - if (node == null || node.getState() == NodeState.TERMINATED) - return node; + checkStateAvailable(node); client.rebootNode(id); - return node; + // invalidate state of node + return getNode(checkNotNull(id, "id")); + } + + private void checkStateAvailable(NodeMetadata node) { + checkState(node != null && node.getState() != NodeState.TERMINATED, "node %s terminated or unavailable!", node); } @Override public NodeMetadata resumeNode(String id) { NodeMetadata node = getNode(checkNotNull(id, "id")); - if (node == null || node.getState() == NodeState.TERMINATED || node.getState() == NodeState.RUNNING) - return node; + checkStateAvailable(node); client.resumeNode(id); - return node; + // invalidate state of node + return getNode(checkNotNull(id, "id")); } @Override public NodeMetadata suspendNode(String id) { NodeMetadata node = getNode(checkNotNull(id, "id")); - if (node == null || node.getState() == NodeState.TERMINATED || node.getState() == NodeState.SUSPENDED) - return node; + checkStateAvailable(node); client.suspendNode(id); - return node; + // invalidate state of node + return getNode(checkNotNull(id, "id")); } @Override diff --git a/core/src/main/java/org/jclouds/predicates/RetryableNumTimesPredicate.java b/core/src/main/java/org/jclouds/predicates/RetryableNumTimesPredicate.java new file mode 100644 index 0000000000..01cf3bf95a --- /dev/null +++ b/core/src/main/java/org/jclouds/predicates/RetryableNumTimesPredicate.java @@ -0,0 +1,112 @@ +/** + * 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.predicates; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.util.Throwables2.getFirstThrowableOfType; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.annotation.Resource; + +import org.jclouds.logging.Logger; + +import com.google.common.base.Predicate; + +/** + * + * Retries a condition until it is met or the max number of retries have occurred. + * maxAttempts parameter is required. + * Initial retry period and retry maxPeriod are optionally configurable, + * defaulting to 50ms and 1000ms respectively, + * with the retrier increasing the interval by a factor of 1.5 each time within these constraints. + * + * @author Aled Sage + */ +public class RetryableNumTimesPredicate implements Predicate { + private final int maxAttempts; + private final long period; + private final long maxPeriod; + private final Predicate predicate; + + @Resource + protected Logger logger = Logger.NULL; + + public RetryableNumTimesPredicate(Predicate predicate, int maxAttempts, long period, long maxPeriod, TimeUnit unit) { + this.predicate = checkNotNull(predicate); + this.maxAttempts = maxAttempts; + this.period = unit.toMillis(period); + this.maxPeriod = unit.toMillis(maxPeriod); + checkArgument(maxAttempts >= 0, "maxAttempts must be greater than zero, but was "+maxAttempts); + checkArgument(period >= 0, "period must be greater than zero, but was "+period); + checkArgument(maxPeriod >= 0, "maxPeriod must be greater than zero, but was "+maxPeriod); + checkArgument(maxPeriod >= period, "maxPeriod must be greater than or equal to period, but was "+maxPeriod+" < "+period); + } + + public RetryableNumTimesPredicate(Predicate predicate, int maxAttempts, long period, TimeUnit unit) { + this(predicate, maxAttempts, period, period*10, unit); + } + + public RetryableNumTimesPredicate(Predicate predicate, int maxAttempts) { + this(predicate, maxAttempts, 50l, 1000l, TimeUnit.MILLISECONDS); + } + + @Override + public boolean apply(T input) { + try { + for (int i = 1; i <= maxAttempts; Thread.sleep(nextMaxInterval(i++))) { + if (predicate.apply(input)) { + return true; + } + } + return false; + + } catch (InterruptedException e) { + logger.warn(e, "predicate %s on %s interrupted, returning false", input, predicate); + Thread.currentThread().interrupt(); + } catch (RuntimeException e) { + if (getFirstThrowableOfType(e, ExecutionException.class) != null) { + logger.warn(e, "predicate %s on %s errored [%s], returning false", input, predicate, e.getMessage()); + return false; + } else if (getFirstThrowableOfType(e, IllegalStateException.class) != null) { + logger.warn(e, "predicate %s on %s illegal state [%s], returning false", input, predicate, e.getMessage()); + return false; + } else if (getFirstThrowableOfType(e, CancellationException.class) != null) { + logger.warn(e, "predicate %s on %s cancelled [%s], returning false", input, predicate, e.getMessage()); + return false; + } else if (getFirstThrowableOfType(e, TimeoutException.class) != null) { + logger.warn(e, "predicate %s on %s timed out [%s], returning false", input, predicate, e.getMessage()); + return false; + } else + throw e; + } + return false; + } + + protected long nextMaxInterval(long attempt) { + // Interval increases exponentially, at a rate of nextInterval *= 1.5 + // Note that attempt starts counting at 1 + long interval = (long) (period * Math.pow(1.5, (attempt-1))); + return (interval > maxPeriod ? maxPeriod : interval); + } +} diff --git a/core/src/main/java/org/jclouds/predicates/Retryables.java b/core/src/main/java/org/jclouds/predicates/Retryables.java index 2e9aedb0e3..0c92176c16 100644 --- a/core/src/main/java/org/jclouds/predicates/Retryables.java +++ b/core/src/main/java/org/jclouds/predicates/Retryables.java @@ -39,6 +39,14 @@ public class Retryables { return new RetryablePredicate(predicate, maxWait, period, unit).apply(input); } + public static boolean retryNumTimes(Predicate predicate, Input input, int maxAttempts) { + return new RetryableNumTimesPredicate(predicate, maxAttempts).apply(input); + } + + public static boolean retryNumTimes(Predicate predicate, Input input, int maxAttempts, long period, long maxPeriod, TimeUnit unit) { + return new RetryableNumTimesPredicate(predicate, maxAttempts, period, maxPeriod, unit).apply(input); + } + public static void assertEventually(Predicate predicate, Input input, long maxWaitMillis, String failureMessage) { if (!new RetryablePredicate(predicate, maxWaitMillis).apply(input)) @@ -51,11 +59,11 @@ public class Retryables { throw (AssertionError)new AssertionError(failureMessage).initCause(predicate.getLastFailure()); return predicate.getResult(); } + public static Result retryGettingResultOrFailing(PredicateWithResult predicate, Input input, long maxWait, long period, TimeUnit unit, String failureMessage) { if (!new RetryablePredicate(predicate, maxWait, period, unit).apply(input)) throw (AssertionError)new AssertionError(failureMessage).initCause(predicate.getLastFailure()); return predicate.getResult(); } - } diff --git a/core/src/test/java/org/jclouds/predicates/RetryableNumTimesPredicateTest.java b/core/src/test/java/org/jclouds/predicates/RetryableNumTimesPredicateTest.java new file mode 100644 index 0000000000..4d014a649b --- /dev/null +++ b/core/src/test/java/org/jclouds/predicates/RetryableNumTimesPredicateTest.java @@ -0,0 +1,170 @@ +/** + * 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.predicates; + +import static org.jclouds.predicates.RetryablePredicateTest.assertCallTimes; +import static org.testng.Assert.fail; + +import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.jclouds.predicates.RetryablePredicateTest.RepeatedAttemptsPredicate; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.base.Stopwatch; +import com.google.common.base.Supplier; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", singleThreaded = true) +public class RetryableNumTimesPredicateTest { + // Grace must be reasonably big; Thread.sleep can take a bit longer to wake up sometimes... + public static int SLOW_BUILD_SERVER_GRACE = 250; + + // Sometimes returns sooner than timer would predict (e.g. observed 2999ms, when expected 3000ms) + public static int EARLY_RETURN_GRACE = 10; + + private Stopwatch stopwatch; + + @BeforeMethod + public void setUp() { + stopwatch = new Stopwatch(); + } + + @Test + void testFalseOnIllegalStateExeception() { + ensureImmediateReturnFor(new IllegalStateException()); + } + + @SuppressWarnings("serial") + @Test + void testFalseOnExecutionException() { + ensureImmediateReturnFor(new ExecutionException() { + }); + } + + @SuppressWarnings("serial") + @Test + void testFalseOnTimeoutException() { + ensureImmediateReturnFor(new TimeoutException() { + }); + } + + @SuppressWarnings("serial") + @Test(expectedExceptions = RuntimeException.class) + void testPropagateOnException() { + ensureImmediateReturnFor(new Exception() { + }); + } + + private void ensureImmediateReturnFor(final Exception ex) { + RetryableNumTimesPredicate> predicate = new RetryableNumTimesPredicate>( + new Predicate>() { + + @Override + public boolean apply(Supplier input) { + return "goo".equals(input.get()); + } + + }, 3, 1L, TimeUnit.SECONDS); + + stopwatch.start(); + assert !predicate.apply(new Supplier() { + + @Override + public String get() { + throw new RuntimeException(ex); + } + + }); + long duration = stopwatch.elapsedMillis(); + assertOrdered(duration, SLOW_BUILD_SERVER_GRACE); + } + + @Test + void testAlwaysTrue() { + // will call once immediately + RetryableNumTimesPredicate predicate = new RetryableNumTimesPredicate(Predicates. alwaysTrue(), + 1, 1L, TimeUnit.SECONDS); + stopwatch.start(); + predicate.apply(""); + long duration = stopwatch.elapsedMillis(); + assertOrdered(duration, SLOW_BUILD_SERVER_GRACE); + } + + @Test + void testAlwaysFalseMillis() { + // maxAttempts=3; period=1; maxPeriod defaults to 1*10 + // will call at 0, 1, 1+(1*1.5) = 2.5secs + RetryableNumTimesPredicate predicate = new RetryableNumTimesPredicate(Predicates. alwaysFalse(), + 3, 1L, TimeUnit.SECONDS); + stopwatch.start(); + predicate.apply(""); + long duration = stopwatch.elapsedMillis(); + assertOrdered(2500-EARLY_RETURN_GRACE, duration, 2500+SLOW_BUILD_SERVER_GRACE); + } + + @Test + void testThirdTimeTrue() { + // maxAttempts=3; period=1; maxPeriod defaults to 1*10 + // will call at 0, 1, 1+(1*1.5) + RepeatedAttemptsPredicate rawPredicate = new RepeatedAttemptsPredicate(2); + RetryableNumTimesPredicate predicate = new RetryableNumTimesPredicate(rawPredicate, + 4, 1, TimeUnit.SECONDS); + + stopwatch.start(); + predicate.apply(""); + long duration = stopwatch.elapsedMillis(); + + assertOrdered(2500-EARLY_RETURN_GRACE, duration, 2500+SLOW_BUILD_SERVER_GRACE); + assertCallTimes(rawPredicate.callTimes, 0, 1000, 1000+1500); + } + + @Test + void testThirdTimeTrueLimitedMaxInterval() { + // maxAttempts=3; period=1; maxPeriod=1 + // will call at 0, 1, 1+1 + RepeatedAttemptsPredicate rawPredicate = new RepeatedAttemptsPredicate(2); + RetryableNumTimesPredicate predicate = new RetryableNumTimesPredicate(rawPredicate, + 3, 1L, 1L, TimeUnit.SECONDS); + + stopwatch.start(); + predicate.apply(""); + long duration = stopwatch.elapsedMillis(); + + assertOrdered(2000-EARLY_RETURN_GRACE, duration, 2000+SLOW_BUILD_SERVER_GRACE); + assertCallTimes(rawPredicate.callTimes, 0, 1000, 2000); + } + + private static void assertOrdered(long... values) { + long prevVal = values[0]; + for (long val : values) { + if (val < prevVal) { + fail(String.format("%s should be ordered", Arrays.toString(values))); + } + } + } +} diff --git a/core/src/test/java/org/jclouds/predicates/RetryablePredicateTest.java b/core/src/test/java/org/jclouds/predicates/RetryablePredicateTest.java index 01bb650b7d..8eb539ebde 100644 --- a/core/src/test/java/org/jclouds/predicates/RetryablePredicateTest.java +++ b/core/src/test/java/org/jclouds/predicates/RetryablePredicateTest.java @@ -159,7 +159,7 @@ public class RetryablePredicateTest { assertCallTimes(rawPredicate.callTimes, 0, 1000, 2000); } - private static class RepeatedAttemptsPredicate implements Predicate { + public static class RepeatedAttemptsPredicate implements Predicate { final List callTimes = new ArrayList(); private final int succeedOnAttempt; private final Stopwatch stopwatch; diff --git a/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeFloatingIPClientLiveTest.java b/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeFloatingIPClientLiveTest.java index 81856da6b1..0135c44035 100644 --- a/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeFloatingIPClientLiveTest.java +++ b/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeFloatingIPClientLiveTest.java @@ -18,7 +18,7 @@ */ package org.jclouds.hpcloud.compute.features; -import org.jclouds.openstack.nova.v1_1.features.FloatingIPClientLiveTest; +import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClientLiveTest; import org.testng.annotations.Test; /** diff --git a/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeImageClientLiveTest.java b/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeImageClientLiveTest.java new file mode 100644 index 0000000000..0989e653a4 --- /dev/null +++ b/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeImageClientLiveTest.java @@ -0,0 +1,33 @@ +/** + * 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.hpcloud.compute.features; + +import org.jclouds.openstack.nova.v1_1.features.ImageClientLiveTest; +import org.testng.annotations.Test; + +/** + * + * @author Michael Arnold + */ +@Test(groups = "live", testName = "HPCloudComputeImageClientLiveTest") +public class HPCloudComputeImageClientLiveTest extends ImageClientLiveTest { + public HPCloudComputeImageClientLiveTest() { + provider = "hpcloud-compute"; + } +} diff --git a/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeKeyPairClientLiveTest.java b/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeKeyPairClientLiveTest.java new file mode 100644 index 0000000000..0a7afe0704 --- /dev/null +++ b/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeKeyPairClientLiveTest.java @@ -0,0 +1,34 @@ +/** + * 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.hpcloud.compute.features; + +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClientLiveTest; +import org.testng.annotations.Test; + +/** + * + * @author Michael Arnold + */ +@Test(groups = "live", testName = "HPCloudComputeKeyPairClientLiveTest") +public class HPCloudComputeKeyPairClientLiveTest extends KeyPairClientLiveTest { + public HPCloudComputeKeyPairClientLiveTest() { + provider = "hpcloud-compute"; + } + +} diff --git a/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeSecurityGroupClientLiveTest.java b/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeSecurityGroupClientLiveTest.java new file mode 100644 index 0000000000..085982e277 --- /dev/null +++ b/labs/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/features/HPCloudComputeSecurityGroupClientLiveTest.java @@ -0,0 +1,33 @@ +/** + * 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.hpcloud.compute.features; + +import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClientLiveTest; +import org.testng.annotations.Test; + +/** + * + * @author Michael Arnold + */ +@Test(groups = "live", testName = "HPCloudComputeSecurityGroupClientLiveTest") +public class HPCloudComputeSecurityGroupClientLiveTest extends SecurityGroupClientLiveTest { + public HPCloudComputeSecurityGroupClientLiveTest() { + provider = "hpcloud-compute"; + } +} diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index 5c99d5bd83..71534e7aef 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -23,11 +23,12 @@ import java.util.Set; import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.Region; import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; +import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient; +import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; import org.jclouds.openstack.nova.v1_1.features.FlavorAsyncClient; -import org.jclouds.openstack.nova.v1_1.features.FloatingIPAsyncClient; import org.jclouds.openstack.nova.v1_1.features.ImageAsyncClient; -import org.jclouds.openstack.nova.v1_1.features.KeyPairAsyncClient; -import org.jclouds.openstack.nova.v1_1.features.SecurityGroupAsyncClient; import org.jclouds.openstack.nova.v1_1.features.ServerAsyncClient; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; @@ -65,6 +66,13 @@ public interface NovaAsyncClient { @Delegate FlavorAsyncClient getFlavorClientForRegion( @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region); + + /** + * Provides asynchronous access to Extension features. + */ + @Delegate + ExtensionAsyncClient getExtensionClientForRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region); /** * Provides asynchronous access to Image features. diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 3f2dab31d3..2cf57d5f40 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -25,11 +25,12 @@ import org.jclouds.concurrent.Timeout; import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.Region; import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; +import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient; +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; +import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient; +import org.jclouds.openstack.nova.v1_1.features.ExtensionClient; import org.jclouds.openstack.nova.v1_1.features.FlavorClient; -import org.jclouds.openstack.nova.v1_1.features.FloatingIPClient; import org.jclouds.openstack.nova.v1_1.features.ImageClient; -import org.jclouds.openstack.nova.v1_1.features.KeyPairClient; -import org.jclouds.openstack.nova.v1_1.features.SecurityGroupClient; import org.jclouds.openstack.nova.v1_1.features.ServerClient; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; @@ -68,6 +69,13 @@ public interface NovaClient { FlavorClient getFlavorClientForRegion( @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region); + /** + * Provides synchronous access to Extension features. + */ + @Delegate + ExtensionClient getExtensionClientForRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region); + /** * Provides synchronous access to Image features. */ diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerToNodeMetadata.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerToNodeMetadata.java index d5c2f57080..a1fb477dd0 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerToNodeMetadata.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerToNodeMetadata.java @@ -22,12 +22,9 @@ import java.util.Map; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadataBuilder; -import org.jclouds.compute.domain.NodeState; import org.jclouds.openstack.nova.v1_1.domain.Address; import org.jclouds.openstack.nova.v1_1.domain.Server; -import org.jclouds.openstack.nova.v1_1.domain.ServerStatus; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; @@ -40,21 +37,6 @@ import com.google.common.collect.Iterables; public class ServerToNodeMetadata implements Function { - @VisibleForTesting - public static final Map serverToNodeState = ImmutableMap.builder() - .put(ServerStatus.ACTIVE, NodeState.RUNNING) - .put(ServerStatus.SUSPENDED, NodeState.SUSPENDED) - .put(ServerStatus.DELETED, NodeState.TERMINATED) - .put(ServerStatus.RESIZE, NodeState.PENDING) - .put(ServerStatus.VERIFY_RESIZE, NodeState.PENDING) - .put(ServerStatus.BUILD, NodeState.PENDING) - .put(ServerStatus.PASSWORD, NodeState.PENDING) - .put(ServerStatus.REBUILD, NodeState.PENDING) - .put(ServerStatus.REBOOT, NodeState.PENDING) - .put(ServerStatus.HARD_REBOOT, NodeState.PENDING) - .put(ServerStatus.UNKNOWN, NodeState.UNRECOGNIZED) - .put(ServerStatus.UNRECOGNIZED, NodeState.UNRECOGNIZED).build(); - @Override public NodeMetadata apply(Server server) { @@ -66,6 +48,7 @@ public class ServerToNodeMetadata implements Function .publicAddresses(Iterables.transform(server.getPublicAddresses(), new AddressToStringTransformationFunction())) .privateAddresses(Iterables.transform(server.getPrivateAddresses(), new AddressToStringTransformationFunction())) .state(server.getStatus().getNodeState()) + .userMetadata(ImmutableMap.copyOf(server.getMetadata())) .build(); } diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java index 3e9ea93bac..2f57e346c6 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java @@ -28,16 +28,18 @@ import org.jclouds.http.annotation.ServerError; import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule; import org.jclouds.openstack.nova.v1_1.NovaAsyncClient; import org.jclouds.openstack.nova.v1_1.NovaClient; +import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient; +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; +import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient; +import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; +import org.jclouds.openstack.nova.v1_1.features.ExtensionClient; import org.jclouds.openstack.nova.v1_1.features.FlavorAsyncClient; import org.jclouds.openstack.nova.v1_1.features.FlavorClient; -import org.jclouds.openstack.nova.v1_1.features.FloatingIPAsyncClient; -import org.jclouds.openstack.nova.v1_1.features.FloatingIPClient; import org.jclouds.openstack.nova.v1_1.features.ImageAsyncClient; import org.jclouds.openstack.nova.v1_1.features.ImageClient; -import org.jclouds.openstack.nova.v1_1.features.KeyPairAsyncClient; -import org.jclouds.openstack.nova.v1_1.features.KeyPairClient; -import org.jclouds.openstack.nova.v1_1.features.SecurityGroupAsyncClient; -import org.jclouds.openstack.nova.v1_1.features.SecurityGroupClient; import org.jclouds.openstack.nova.v1_1.features.ServerAsyncClient; import org.jclouds.openstack.nova.v1_1.features.ServerClient; import org.jclouds.openstack.nova.v1_1.handlers.NovaErrorHandler; @@ -59,6 +61,7 @@ public class NovaRestClientModule extends RestClientModule + */ +public class Extension extends Resource { + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromExtension(this); + } + + public static class Builder extends Resource.Builder { + + private URI namespace; + private String alias; + private Date updated; + private String description; + + public Builder namespace(URI namespace) { + this.namespace = namespace; + return this; + } + + public Builder alias(String alias) { + this.alias = alias; + return this; + } + + public Builder updated(Date updated) { + this.updated = updated; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Extension build() { + return new Extension(name, links, namespace, alias, updated, description); + } + + public Builder fromExtension(Extension in) { + return fromResource(in).namespace(in.getNamespace()).alias(in.getAlias()).updated(in.getUpdated()) + .description(in.getDescription()); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder id(String id) { + return alias(id); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder name(String name) { + return Builder.class.cast(super.name(name)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder links(Set links) { + return Builder.class.cast(super.links(links)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder fromResource(Resource in) { + return Builder.class.cast(super.fromResource(in)); + } + } + + private URI namespace; + private String alias; + private Date updated; + private String description; + + protected Extension(String name, Set links, URI namespace, String alias, Date updated, + String description) { + super(alias, name, links); + this.namespace = namespace; + this.alias = alias; + this.updated = updated; + this.description = description; + } + + public URI getNamespace() { + return this.namespace; + } + + @Override + public String getId() { + return this.alias; + } + + public String getAlias() { + return this.alias; + } + + public Date getUpdated() { + return this.updated; + } + + public String getDescription() { + return this.description; + } + + @Override + public String toString() { + return toStringHelper("").add("id", getId()).add("name", name).add("links", links).add("namespace", namespace).add( + "alias", alias).add("updated", updated).add("description", description).toString(); + } + +} diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java new file mode 100644 index 0000000000..7cf5ef9611 --- /dev/null +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java @@ -0,0 +1,34 @@ +/** + * 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, Name 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.extensions; + +import java.net.URI; + +/** + * Extension namespaces + * + * @author Adrian Cole + * @see + */ +public interface ExtensionNamespaces { + public static URI KEYPAIRS = URI.create("http://docs.openstack.org/ext/keypairs/api/v1.1"); + public static URI VOLUMES = URI.create("http://docs.openstack.org/ext/volumes/api/v1.1"); + public static URI SECURITY_GROUPS = URI.create("http://docs.openstack.org/ext/securitygroups/api/v1.1"); + public static URI FLOATING_IPS = URI.create("http://docs.openstack.org/ext/floating_ips/api/v1.1"); +} diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClient.java similarity index 91% rename from labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPAsyncClient.java rename to labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClient.java index f70a68f12b..9bbb413dfa 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPAsyncClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClient.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v1_1.features; +package org.jclouds.openstack.nova.v1_1.extensions; import java.util.Set; @@ -31,6 +31,7 @@ import javax.ws.rs.core.MediaType; import org.jclouds.openstack.filters.AuthenticateRequest; import org.jclouds.openstack.nova.v1_1.domain.FloatingIP; +import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.Payload; import org.jclouds.rest.annotations.PayloadParam; @@ -48,6 +49,10 @@ import com.google.common.util.concurrent.ListenableFuture; * * @see FloatingIPClient * @author Jeremy Daggett + * @see ExtensionAsyncClient + * @see + * @see + * @see */ @SkipEncoding({ '/', '=' }) @RequestFilters(AuthenticateRequest.class) diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClient.java similarity index 97% rename from labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPClient.java rename to labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClient.java index 7c1e72681e..59934d94f3 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClient.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v1_1.features; +package org.jclouds.openstack.nova.v1_1.extensions; import java.util.Set; import java.util.concurrent.TimeUnit; diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/KeyPairAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairAsyncClient.java similarity index 88% rename from labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/KeyPairAsyncClient.java rename to labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairAsyncClient.java index 0827db62fa..5c7eaf7aa5 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/KeyPairAsyncClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairAsyncClient.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v1_1.features; +package org.jclouds.openstack.nova.v1_1.extensions; import java.util.Map; import java.util.Set; @@ -32,6 +32,7 @@ import javax.ws.rs.core.MediaType; import org.jclouds.openstack.filters.AuthenticateRequest; import org.jclouds.openstack.nova.v1_1.domain.KeyPair; +import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.Payload; import org.jclouds.rest.annotations.PayloadParam; @@ -49,6 +50,10 @@ import com.google.common.util.concurrent.ListenableFuture; * * @see KeyPairClient * @author Jeremy Daggett + * @see ExtensionAsyncClient + * @see + * @see + * @see */ @SkipEncoding({ '/', '=' }) @RequestFilters(AuthenticateRequest.class) diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/KeyPairClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClient.java similarity index 97% rename from labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/KeyPairClient.java rename to labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClient.java index 7c4cc70094..075272b3a8 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/KeyPairClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClient.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v1_1.features; +package org.jclouds.openstack.nova.v1_1.extensions; import java.util.Map; import java.util.Set; diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/SecurityGroupAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java similarity index 94% rename from labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/SecurityGroupAsyncClient.java rename to labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java index 9ce69670cb..a163861a58 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/SecurityGroupAsyncClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v1_1.features; +package org.jclouds.openstack.nova.v1_1.extensions; import java.util.Set; @@ -50,6 +50,9 @@ import com.google.common.util.concurrent.ListenableFuture; * * @see SecurityGroupClient * @author Jeremy Daggett + * @see + * @see + * @see */ @SkipEncoding({ '/', '=' }) @RequestFilters(AuthenticateRequest.class) diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/SecurityGroupClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClient.java similarity index 97% rename from labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/SecurityGroupClient.java rename to labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClient.java index afe97e09f2..8b78950119 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/SecurityGroupClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClient.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v1_1.features; +package org.jclouds.openstack.nova.v1_1.extensions; import java.util.Set; import java.util.concurrent.TimeUnit; diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ExtensionAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ExtensionAsyncClient.java new file mode 100644 index 0000000000..54973e2d03 --- /dev/null +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ExtensionAsyncClient.java @@ -0,0 +1,74 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.features; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; + +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.domain.Extension; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides asynchronous access to Extensions via their REST API. + *

+ * + * @see ExtensionClient + * @see + * @author Adrian Cole + */ +@SkipEncoding({ '/', '=' }) +@RequestFilters(AuthenticateRequest.class) +public interface ExtensionAsyncClient { + + /** + * @see ExtensionClient#listExtensions + */ + @GET + @SelectJson("extensions") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/extensions") + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listExtensions(); + + + /** + * @see ExtensionClient#getExtensionByAlias + */ + @GET + @SelectJson("extension") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/extensions/{alias}") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getExtensionByAlias(@PathParam("alias") String id); + +} diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClient.java new file mode 100644 index 0000000000..ce5b5bc413 --- /dev/null +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClient.java @@ -0,0 +1,55 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.features; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.nova.v1_1.domain.Extension; + +/** + * Provides asynchronous access to Extensions via their REST API. + *

+ * + * @see ExtensionClient + * @see + * @author Adrian Cole + */ +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +public interface ExtensionClient { + + /** + * List all available extensions + * + * @return all extensions + */ + Set listExtensions(); + + /** + * Extensions may also be queried individually by their unique alias. + * + * @param id + * id of the extension + * @return extension or null if not found + */ + Extension getExtensionByAlias(String alias); + +} diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FlavorClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FlavorClient.java index ffad009fb4..12780ce772 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FlavorClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/FlavorClient.java @@ -35,7 +35,7 @@ import org.jclouds.openstack.nova.v1_1.domain.Flavor; * /> * @author Jeremy Daggett */ -@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) public interface FlavorClient { /** diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ImageAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ImageAsyncClient.java index e62354d3e6..baa1806fa2 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ImageAsyncClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ImageAsyncClient.java @@ -75,7 +75,7 @@ public interface ImageAsyncClient { * @see ImageClient#getImage */ @GET - @SelectJson("flavor") + @SelectJson("image") @Consumes(MediaType.APPLICATION_JSON) @Path("/images/{id}") @ExceptionParser(ReturnNullOnNotFoundOr404.class) diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/predicates/ExtensionPredicates.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/predicates/ExtensionPredicates.java new file mode 100644 index 0000000000..443f644f6f --- /dev/null +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/predicates/ExtensionPredicates.java @@ -0,0 +1,82 @@ +/* + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; + +import org.jclouds.openstack.nova.v1_1.domain.Extension; + +import com.google.common.base.Predicate; + +/** + * Predicates handy when working with Extensions + * + * @author Adrian Cole + */ + +public class ExtensionPredicates { + + /** + * matches namespace of the given extension + * + * @param namespace + * ex {@code http://docs.openstack.org/ext/keypairs/api/v1.1} + * @return predicate that will match namespace of the given extension + */ + public static Predicate namespaceEquals(final URI namespace) { + checkNotNull(namespace, "namespace must be defined"); + + return new Predicate() { + @Override + public boolean apply(Extension ext) { + return namespace.equals(ext.getNamespace()); + } + + @Override + public String toString() { + return "namespaceEquals(" + namespace + ")"; + } + }; + } + + /** + * matches alias of the given extension + * + * @param alias + * ex. {@code os-keypairs} + * @return predicate that will alias of the given extension + */ + public static Predicate aliasEquals(final String alias) { + checkNotNull(alias, "alias must be defined"); + + return new Predicate() { + @Override + public boolean apply(Extension ext) { + return alias.equals(ext.getAlias()); + } + + @Override + public String toString() { + return "aliasEquals(" + alias + ")"; + } + }; + } +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerToNodeMetadataTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerToNodeMetadataTest.java index fb7f96be33..ed2104157d 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerToNodeMetadataTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerToNodeMetadataTest.java @@ -18,6 +18,8 @@ */ package org.jclouds.openstack.nova.v1_1.compute.functions; +import com.google.common.collect.ImmutableMap; + import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; @@ -47,6 +49,7 @@ public class ServerToNodeMetadataTest .privateAddresses(Address.createV4("10.0.0.1")) .publicAddresses(Address.createV4("1.0.1.1")) .status(ServerStatus.ACTIVE) + .metadata(ImmutableMap.of("test", "testing")) .build(); ServerToNodeMetadata converter = new ServerToNodeMetadata(); @@ -65,5 +68,9 @@ public class ServerToNodeMetadataTest assertNotNull(convertedNodeMetadata.getPublicAddresses()); assertEquals(convertedNodeMetadata.getPublicAddresses().size(), 1); assertEquals(convertedNodeMetadata.getPublicAddresses().iterator().next(), "1.0.1.1"); + + assertNotNull(convertedNodeMetadata.getUserMetadata()); + assertEquals(convertedNodeMetadata.getUserMetadata().size(), 1); + assertEquals(convertedNodeMetadata.getUserMetadata().get("test"),"testing"); } } diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java similarity index 99% rename from labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPClientExpectTest.java rename to labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java index ba637b22f4..e373ed1295 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPClientExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v1_1.features; +package org.jclouds.openstack.nova.v1_1.extensions; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPClientLiveTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientLiveTest.java similarity index 95% rename from labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPClientLiveTest.java rename to labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientLiveTest.java index fbfdd941a1..da46fdff87 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FloatingIPClientLiveTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientLiveTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v1_1.features; +package org.jclouds.openstack.nova.v1_1.extensions; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -30,6 +30,10 @@ import org.jclouds.openstack.nova.v1_1.domain.Address; import org.jclouds.openstack.nova.v1_1.domain.FloatingIP; import org.jclouds.openstack.nova.v1_1.domain.Server; import org.jclouds.openstack.nova.v1_1.domain.ServerStatus; +import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient; +import org.jclouds.openstack.nova.v1_1.features.FlavorClient; +import org.jclouds.openstack.nova.v1_1.features.ImageClient; +import org.jclouds.openstack.nova.v1_1.features.ServerClient; import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; import org.testng.annotations.Test; diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java new file mode 100644 index 0000000000..c9a7b70d43 --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java @@ -0,0 +1,166 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.extensions; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.v1_1.NovaClient; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.parse.ParseKeyPairListTest; +import org.jclouds.openstack.nova.v1_1.parse.ParseKeyPairTest; +import org.testng.annotations.Test; + +import java.net.URI; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +/** + * Tests annotation parsing of {@code KeyPairAsyncClient} + * + * @author Michael Arnold + */ +@Test(groups = "unit", testName = "KeyPairClientExpectTest") +public class KeyPairClientExpectTest extends BaseNovaRestClientExpectTest { + + public void testListKeyPairsWhenResponseIs2xx() throws Exception { + HttpRequest listKeyPairs = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/os-keypairs")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse listKeyPairsResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/keypair_list.json")).build(); + + NovaClient clientWhenServersExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + listKeyPairs, listKeyPairsResponse); + + assertEquals(clientWhenServersExist.getConfiguredRegions(), + ImmutableSet.of("North")); + + assertEquals(clientWhenServersExist.getKeyPairClientForRegion("North") + .listKeyPairs().toString(), new ParseKeyPairListTest().expected() + .toString()); + } + + public void testListKeyPairsWhenResponseIs404() throws Exception { + HttpRequest listKeyPairs = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/os-keypairs")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse listKeyPairsResponse = HttpResponse.builder().statusCode(404) + .build(); + + NovaClient clientWhenNoServersExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + listKeyPairs, listKeyPairsResponse); + + assertTrue(clientWhenNoServersExist.getKeyPairClientForRegion("North") + .listKeyPairs().isEmpty()); + + } + + public void testCreateKeyPair() throws Exception { + HttpRequest createKeyPair = HttpRequest + .builder() + .method("POST") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/os-keypairs")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()) + .payload(payloadFromStringWithContentType("{\"keypair\":{\"name\":\"testkeypair\"}}", + "application/json")).build(); + + HttpResponse createKeyPairResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/keypair_created.json")).build(); + + NovaClient clientWhenServersExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + createKeyPair, createKeyPairResponse); + + assertEquals(clientWhenServersExist.getKeyPairClientForRegion("North") + .createKeyPair("testkeypair").toString(), + new ParseKeyPairTest().expected().toString()); + + } + + public void testCreateKeyPairWithPublicKey() throws Exception { + HttpRequest createKeyPair = HttpRequest + .builder() + .method("POST") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/os-keypairs")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()) + .payload(payloadFromStringWithContentType("{\"keypair\":{\"name\":\"testkeypair\",\"public_key\":\"ssh-rsa AAAXB3NzaC1yc2EAAAADAQABAAAAgQDFNyGjgs6c9akgmZ2ou/fJf7Pdrc23hC95/gM/33OrG4GZABACE4DTioa/PGN+7rHv9YUavUCtXrWayhGniKq/wCuI5fo5TO4AmDNv7/sCGHIHFumADSIoLx0vFhGJIetXEWxL9r0lfFC7//6yZM2W3KcGjbMtlPXqBT9K9PzdyQ== nova@nv-aw2az1-api0001\n\"}}", + "application/json")).build(); + + HttpResponse createKeyPairResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/keypair_created.json")).build(); + + NovaClient clientWhenServersExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + createKeyPair, createKeyPairResponse); + + assertEquals(clientWhenServersExist.getKeyPairClientForRegion("North") + .createKeyPairWithPublicKey("testkeypair", "ssh-rsa AAAXB3NzaC1yc2EAAAADAQABAAAAgQDFNyGjgs6c9akgmZ2ou/fJf7Pdrc23hC95/gM/33OrG4GZABACE4DTioa/PGN+7rHv9YUavUCtXrWayhGniKq/wCuI5fo5TO4AmDNv7/sCGHIHFumADSIoLx0vFhGJIetXEWxL9r0lfFC7//6yZM2W3KcGjbMtlPXqBT9K9PzdyQ== nova@nv-aw2az1-api0001\n") + .toString(), + new ParseKeyPairTest().expected().toString()); + } + + public void testDeleteKeyPair() throws Exception { + HttpRequest deleteKeyPair = HttpRequest + .builder() + .method("DELETE") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/os-keypairs/testkeypair")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "*/*") + .put("X-Auth-Token", authToken).build()).build(); + + + HttpResponse deleteKeyPairResponse = HttpResponse.builder().statusCode(202).build(); + + NovaClient clientWhenServersExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + deleteKeyPair, deleteKeyPairResponse); + + assertTrue(clientWhenServersExist.getKeyPairClientForRegion("North").deleteKeyPair("testkeypair")); + } +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/KeyPairClientLiveTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientLiveTest.java similarity index 96% rename from labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/KeyPairClientLiveTest.java rename to labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientLiveTest.java index 937acebc44..be1797c75c 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/KeyPairClientLiveTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientLiveTest.java @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v1_1.features; +package org.jclouds.openstack.nova.v1_1.extensions; import org.jclouds.openstack.nova.v1_1.domain.KeyPair; +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; import org.testng.annotations.Test; diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java new file mode 100644 index 0000000000..dc738036a4 --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java @@ -0,0 +1,30 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.extensions; + +import org.testng.annotations.Test; + +/** + * Tests annotation parsing of {@code SecurityGroupAsyncClient} + * + * @author Michael Arnold + */ +@Test(groups = "unit", testName = "SecurityGroupClientExpectTest") +public class SecurityGroupClientExpectTest { +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientLiveTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientLiveTest.java new file mode 100644 index 0000000000..b11627b5c3 --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientLiveTest.java @@ -0,0 +1,89 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.extensions; + +import org.jclouds.openstack.nova.v1_1.domain.SecurityGroup; +import org.jclouds.openstack.nova.v1_1.domain.SecurityGroupRule; +import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.Test; + +import java.util.Set; + +import static org.testng.Assert.assertNotNull; + +/** + * Tests behavior of {@code SecurityGroupClient} + * + * @author Michael Arnold + */ +@Test(groups = "live", testName = "SecurityGroupClientLiveTest") +public class SecurityGroupClientLiveTest extends BaseNovaClientLiveTest { + + public static final String SECURITY_GROUP_NAME = "testsg"; + + public void listSecurityGroups() throws Exception { + for (String regionId : context.getApi().getConfiguredRegions()) { + SecurityGroupClient client = context.getApi().getSecurityGroupClientForRegion(regionId); + Set securityGroupsList = client.listSecurityGroups(); + assertNotNull(securityGroupsList); + } + } + + public void createGetAndDeleteSecurityGroup() throws Exception { + for(String regionId : context.getApi().getConfiguredRegions()) { + SecurityGroupClient client = context.getApi().getSecurityGroupClientForRegion(regionId); + SecurityGroup securityGroup = null; + String id; + try { + securityGroup = client.createSecurityGroup(SECURITY_GROUP_NAME, "test security group"); + assertNotNull(securityGroup); + id = securityGroup.getId(); + SecurityGroup theGroup = client.getSecurityGroup(id); + assertNotNull(theGroup); + } finally { + if (securityGroup != null) { + client.deleteSecurityGroup(securityGroup.getId()); + } + } + } + } + + public void createAndDeleteSecurityGroupRule() throws Exception { + for(String regionId : context.getApi().getConfiguredRegions()) { + SecurityGroupClient client = context.getApi().getSecurityGroupClientForRegion(regionId); + SecurityGroup securityGroup = null; + + try { + securityGroup = client.createSecurityGroup(SECURITY_GROUP_NAME, "test security group"); + assertNotNull(securityGroup); + + SecurityGroupRule rule = client.createSecurityGroupRule( + "tcp", "443", "443", "0.0.0.0/0", "", securityGroup.getId()); + assertNotNull(rule); + + } finally { + if (securityGroup != null) { + client.deleteSecurityGroup(securityGroup.getId()); + } + } + } + + } +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java new file mode 100644 index 0000000000..259c9732ba --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java @@ -0,0 +1,141 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.features; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.net.URI; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.v1_1.NovaClient; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.parse.ParseExtensionListTest; +import org.jclouds.openstack.nova.v1_1.parse.ParseExtensionTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; + +/** + * Tests annotation parsing of {@code ExtensionAsyncClient} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ExtensionClientExpectTest") +public class ExtensionClientExpectTest extends BaseNovaRestClientExpectTest { + + public void testListExtensionsWhenResponseIs2xx() throws Exception { + HttpRequest listExtensions = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/extensions")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse listExtensionsResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/extension_list.json")).build(); + + NovaClient clientWhenExtensionsExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + listExtensions, listExtensionsResponse); + + assertEquals(clientWhenExtensionsExist.getConfiguredRegions(), + ImmutableSet.of("North")); + + assertEquals(clientWhenExtensionsExist.getExtensionClientForRegion("North") + .listExtensions().toString(), new ParseExtensionListTest().expected() + .toString()); + } + + public void testListExtensionsWhenReponseIs404IsEmpty() throws Exception { + HttpRequest listExtensions = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/extensions")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse listExtensionsResponse = HttpResponse.builder().statusCode(404) + .build(); + + NovaClient clientWhenNoServersExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + listExtensions, listExtensionsResponse); + + assertTrue(clientWhenNoServersExist.getExtensionClientForRegion("North") + .listExtensions().isEmpty()); + } + + // TODO: gson deserializer for Multimap + public void testGetExtensionByAliasWhenResponseIs2xx() throws Exception { + + HttpRequest getExtension = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/extensions/RS-PIE")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse getExtensionResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/extension_details.json")).build(); + + NovaClient clientWhenExtensionsExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + getExtension, getExtensionResponse); + + assertEquals(clientWhenExtensionsExist.getExtensionClientForRegion("North") + .getExtensionByAlias("RS-PIE").toString(), + new ParseExtensionTest().expected().toString()); + } + + public void testGetExtensionByAliasWhenResponseIs404() throws Exception { + HttpRequest getExtension = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/extensions/RS-PIE")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse getExtensionResponse = HttpResponse.builder().statusCode(404) + .payload(payloadFromResource("/extension_details.json")).build(); + + NovaClient clientWhenNoExtensionsExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + getExtension, getExtensionResponse); + + assertNull(clientWhenNoExtensionsExist.getExtensionClientForRegion("North").getExtensionByAlias("RS-PIE")); + + } + +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientLiveTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientLiveTest.java new file mode 100644 index 0000000000..901601a8ff --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientLiveTest.java @@ -0,0 +1,62 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.features; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.jclouds.openstack.nova.v1_1.domain.Extension; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code ExtensionClient} + * + * @author Adrian Cole + */ +@Test(groups = "live", testName = "ExtensionClientLiveTest") +public class ExtensionClientLiveTest extends BaseNovaClientLiveTest { + + /** + * Tests the listing of Extensions (getExtension() is tested too!) + * + * @throws Exception + */ + @Test + public void testListExtensions() throws Exception { + for (String regionId : context.getApi().getConfiguredRegions()) { + ExtensionClient client = context.getApi().getExtensionClientForRegion(regionId); + Set response = client.listExtensions(); + assert null != response; + assertTrue(response.size() >= 0); + for (Extension extension : response) { + Extension newDetails = client.getExtensionByAlias(extension.getId()); + assertEquals(newDetails.getId(), extension.getId()); + assertEquals(newDetails.getName(), extension.getName()); + assertEquals(newDetails.getDescription(), extension.getDescription()); + assertEquals(newDetails.getNamespace(), extension.getNamespace()); + assertEquals(newDetails.getUpdated(), extension.getUpdated()); + assertEquals(newDetails.getLinks(), extension.getLinks()); + } + } + } + +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java new file mode 100644 index 0000000000..bad3704f80 --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java @@ -0,0 +1,138 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.features; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.v1_1.NovaClient; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.parse.ParseImageTest; +import org.jclouds.openstack.nova.v1_1.parse.ParseImageListTest; +import org.testng.annotations.Test; + +import java.net.URI; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +/** + * Tests annotation parsing of {@code ImageAsyncClient} + * + * @author Michael Arnold + */ +@Test(groups = "unit", testName = "ImageAsyncClientTest") +public class ImageClientExpectTest extends BaseNovaRestClientExpectTest { + public void testListImagesWhenResponseIs2xx() throws Exception { + HttpRequest listImages = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/images")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse listImagesResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/image_list.json")).build(); + + NovaClient clientWhenImagesExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + listImages, listImagesResponse); + + assertEquals(clientWhenImagesExist.getConfiguredRegions(), + ImmutableSet.of("North")); + + assertEquals(clientWhenImagesExist.getImageClientForRegion("North") + .listImages().toString(), new ParseImageListTest().expected() + .toString()); + } + + public void testListImagesWhenReponseIs404IsEmpty() throws Exception { + HttpRequest listImages = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/images")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse listImagesResponse = HttpResponse.builder().statusCode(404).build(); + + NovaClient clientWhenNoServersExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + listImages, listImagesResponse); + + assertTrue(clientWhenNoServersExist.getImageClientForRegion("North") + .listImages().isEmpty()); + } + + public void testGetImageWhenResponseIs2xx() throws Exception { + + HttpRequest getImage = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/images/52415800-8b69-11e0-9b19-734f5736d2a2")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse getImageResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/image_details.json")).build(); + + NovaClient clientWhenImagesExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + getImage, getImageResponse); + + assertEquals(clientWhenImagesExist.getImageClientForRegion("North") + .getImage("52415800-8b69-11e0-9b19-734f5736d2a2").toString(), + new ParseImageTest().expected().toString()); + } + + public void testGetImageWhenResponseIs404() throws Exception { + HttpRequest getImage = HttpRequest + .builder() + .method("GET") + .endpoint( + URI.create("https://compute.north.host/v1.1/3456/images/52415800-8b69-11e0-9b19-734f5736d2a2")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse getImageResponse = HttpResponse.builder().statusCode(404).build(); + + NovaClient clientWhenNoImagesExist = requestsSendResponses( + keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, + getImage, getImageResponse); + + assertNull(clientWhenNoImagesExist.getImageClientForRegion("North") + .getImage("52415800-8b69-11e0-9b19-734f5736d2a2")); + + } + + +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientLiveTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientLiveTest.java new file mode 100644 index 0000000000..b4bfc435a8 --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientLiveTest.java @@ -0,0 +1,81 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.features; + +import org.jclouds.openstack.domain.Resource; +import org.jclouds.openstack.nova.v1_1.domain.Image; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.Test; + +import java.util.Set; + +import static org.testng.Assert.*; + +/** + * Tests behavior of {@code ImageClient} + * + * @author Michael Arnold + */ +@Test(groups = "live", testName = "ImageClientLiveTest") +public class ImageClientLiveTest extends BaseNovaClientLiveTest { + + @Test + public void testListImages() throws Exception { + for (String regionId : context.getApi().getConfiguredRegions()) { + ImageClient client = context.getApi().getImageClientForRegion(regionId); + Set response = client.listImages(); + assertNotNull(response); + assertTrue(response.size() >= 0); + for (Resource image : response) { + Image newDetails = client.getImage(image.getId()); + assertNotNull(newDetails); + assertEquals(newDetails.getId(), image.getId()); + assertEquals(newDetails.getName(), image.getName()); + assertEquals(newDetails.getLinks(), image.getLinks()); + } + } + } + + @Test + public void testListImagesInDetail() throws Exception { + for (String regionId : context.getApi().getConfiguredRegions()) { + ImageClient client = context.getApi().getImageClientForRegion(regionId); + Set response = client.listImagesInDetail(); + assertNotNull(response); + assertTrue(response.size() >= 0); + for (Image image : response) { + Image newDetails = client.getImage(image.getId()); + assertNotNull(newDetails); + assertEquals(newDetails.getId(), image.getId()); + assertEquals(newDetails.getName(), image.getName()); + assertEquals(newDetails.getLinks(), image.getLinks()); + assertEquals(newDetails.getCreated(), image.getCreated()); + assertEquals(newDetails.getMinDisk(), image.getMinDisk()); + assertEquals(newDetails.getMinRam(), image.getMinRam()); + assertEquals(newDetails.getProgress(), image.getProgress()); + assertEquals(newDetails.getStatus(), image.getStatus()); + assertEquals(newDetails.getServer(), image.getServer()); + assertEquals(newDetails.getTenantId(), image.getTenantId()); + assertEquals(newDetails.getUpdated(), image.getUpdated()); + assertEquals(newDetails.getUserId(), image.getUserId()); + } + } + } +} + diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/KeyPairClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/KeyPairClientExpectTest.java deleted file mode 100644 index 4cf4f04aff..0000000000 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/KeyPairClientExpectTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.openstack.nova.v1_1.features; - -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpResponse; -import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; -import org.jclouds.openstack.nova.v1_1.parse.ParseKeyPairListTest; -import org.testng.annotations.Test; - -import java.net.URI; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; - -/** - * Tests annotation parsing of {@code KeyPairAsyncClient} - * - * @author Michael Arnold - */ -@Test(groups = "unit", testName = "KeyPairClientExpectTest") -public class KeyPairClientExpectTest extends BaseNovaRestClientExpectTest { - - public void testListKeyPairsWhenResponseIs2xx() throws Exception { - HttpRequest listKeyPairs = HttpRequest - .builder() - .method("GET") - .endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-keypairs")) - .headers( - ImmutableMultimap. builder() - .put("Accept", "application/json") - .put("X-Auth-Token", authToken).build()).build(); - - HttpResponse listKeyPairsResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResource("/keypair_list.json")).build(); - - NovaClient clientWhenFloatingIPsExist = requestsSendResponses( - keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, - listKeyPairs, listKeyPairsResponse); - - assertEquals(clientWhenFloatingIPsExist.getConfiguredRegions(), - ImmutableSet.of("North")); - - assertEquals(clientWhenFloatingIPsExist.getKeyPairClientForRegion("North") - .listKeyPairs().toString(), new ParseKeyPairListTest().expected() - .toString()); - } -} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseExtensionListNormalTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseExtensionListNormalTest.java new file mode 100644 index 0000000000..0497073264 --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseExtensionListNormalTest.java @@ -0,0 +1,103 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.parse; + +import java.net.URI; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.json.BaseSetParserTest; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.nova.v1_1.config.NovaParserModule; +import org.jclouds.openstack.nova.v1_1.domain.Extension; +import org.jclouds.rest.annotations.SelectJson; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ParseExtensionListNormalTest") +public class ParseExtensionListNormalTest extends BaseSetParserTest { + + @Override + public String resource() { + return "/extension_list_normal.json"; + } + + @Override + @SelectJson("extensions") + @Consumes(MediaType.APPLICATION_JSON) + public Set expected() { + return ImmutableSet + .of(Extension + .builder() + .alias("os-keypairs") + .name("Keypairs") + .namespace(URI.create("http://docs.openstack.org/ext/keypairs/api/v1.1")) + .updated( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2011-08-08T00:00:00+00:00")) + .description("Keypair Support") + .build(), + Extension + .builder() + .alias("os-volumes") + .name("Volumes") + .namespace(URI.create("http://docs.openstack.org/ext/volumes/api/v1.1")) + .updated( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2011-03-25T00:00:00+00:00")) + .description("Volumes support") + .build(), + Extension + .builder() + .alias("security_groups") + .name("SecurityGroups") + .namespace(URI.create("http://docs.openstack.org/ext/securitygroups/api/v1.1")) + .updated( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2011-07-21T00:00:00+00:00")) + .description("Security group support") + .build(), + Extension + .builder() + .alias("os-floating-ips") + .name("Floating_ips") + .namespace(URI.create("http://docs.openstack.org/ext/floating_ips/api/v1.1")) + .updated( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2011-06-16T00:00:00+00:00")) + .description("Floating IPs support") + .build() + ); + } + + protected Injector injector() { + return Guice.createInjector(new NovaParserModule(), new GsonModule()); + } + +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseExtensionListTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseExtensionListTest.java new file mode 100644 index 0000000000..b60b6f26eb --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseExtensionListTest.java @@ -0,0 +1,104 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.parse; + +import java.net.URI; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.json.BaseSetParserTest; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.domain.Link; +import org.jclouds.openstack.domain.Link.Relation; +import org.jclouds.openstack.nova.v1_1.config.NovaParserModule; +import org.jclouds.openstack.nova.v1_1.domain.Extension; +import org.jclouds.rest.annotations.SelectJson; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ParseExtensionListTest") +public class ParseExtensionListTest extends BaseSetParserTest { + + @Override + public String resource() { + return "/extension_list.json"; + } + + @Override + @SelectJson("extensions") + @Consumes(MediaType.APPLICATION_JSON) + public Set expected() { + return ImmutableSet + .of(Extension + .builder() + .alias("RAX-PIE") + .name("Public Image Extension") + .namespace(URI.create("http://docs.rackspacecloud.com/servers/api/ext/pie/v1.0")) + .updated( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2011-01-22T13:25:27-06:00")) + .description("Adds the capability to share an image with other users.") + .links( + ImmutableSet.of( + Link.create( + Relation.DESCRIBEDBY, + "application/pdf", + URI.create("http://docs.rackspacecloud.com/servers/api/ext/cs-pie-20111111.pdf")), + Link.create( + Relation.DESCRIBEDBY, + "application/vnd.sun.wadl+xml", + URI.create("http://docs.rackspacecloud.com/servers/api/ext/cs-pie.wadl")))) + .build(), + Extension + .builder() + .alias("RAX-CBS") + .name("Cloud Block Storage") + .namespace(URI.create("http://docs.rackspacecloud.com/servers/api/ext/cbs/v1.0")) + .updated( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2011-01-12T11:22:33-06:00")) + .description("Allows mounting cloud block storage volumes.") + .links( + ImmutableSet.of( + Link.create( + Relation.DESCRIBEDBY, + "application/pdf", + URI.create("http://docs.rackspacecloud.com/servers/api/ext/cs-cbs-20111201.pdf")), + Link.create( + Relation.DESCRIBEDBY, + "application/vnd.sun.wadl+xml", + URI.create("http://docs.rackspacecloud.com/servers/api/ext/cs-cbs.wadl")))) + .build()); + } + + protected Injector injector() { + return Guice.createInjector(new NovaParserModule(), new GsonModule()); + } + +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseExtensionTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseExtensionTest.java new file mode 100644 index 0000000000..53738aa06d --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseExtensionTest.java @@ -0,0 +1,81 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.parse; + +import java.net.URI; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.json.BaseItemParserTest; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.domain.Link; +import org.jclouds.openstack.domain.Link.Relation; +import org.jclouds.openstack.nova.v1_1.config.NovaParserModule; +import org.jclouds.openstack.nova.v1_1.domain.Extension; +import org.jclouds.rest.annotations.SelectJson; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ParseExtensionTest") +public class ParseExtensionTest extends BaseItemParserTest { + + @Override + public String resource() { + return "/extension_details.json"; + } + + @Override + @SelectJson("extension") + @Consumes(MediaType.APPLICATION_JSON) + public Extension expected() { + return Extension + .builder() + .alias("RS-PIE") + .name("Public Image Extension") + .namespace(URI.create("http://docs.rackspacecloud.com/servers/api/ext/pie/v1.0")) + .updated( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2011-01-22T13:25:27-06:00")) + .description("Adds the capability to share an image with other users.") + .links( + ImmutableSet.of( + Link.create( + Relation.DESCRIBEDBY, + "application/pdf", + URI.create("http://docs.rackspacecloud.com/servers/api/ext/cs-pie-20111111.pdf")), + Link.create( + Relation.DESCRIBEDBY, + "application/vnd.sun.wadl+xml", + URI.create("http://docs.rackspacecloud.com/servers/api/ext/cs-pie.wadl")))) + .build(); + } + + + protected Injector injector() { + return Guice.createInjector(new NovaParserModule(), new GsonModule()); + } +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseKeyPairTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseKeyPairTest.java new file mode 100644 index 0000000000..9901ba5f2d --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseKeyPairTest.java @@ -0,0 +1,64 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.parse; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.jclouds.json.BaseItemParserTest; +import org.jclouds.json.BaseParserTest; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.nova.v1_1.config.NovaParserModule; +import org.jclouds.openstack.nova.v1_1.domain.KeyPair; +import org.jclouds.rest.annotations.SelectJson; +import org.testng.annotations.Test; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +/** + * + * @author Michael Arnold + */ +@Test(groups = "unit", testName = "ParseKeyPairTest") +public class ParseKeyPairTest extends BaseItemParserTest { + + @Override + public String resource() { + return "/keypair_created.json"; + } + + @Override + @SelectJson("keypair") + @Consumes(MediaType.APPLICATION_JSON) + public KeyPair expected() { + return KeyPair + .builder() + .publicKey("ssh-rsa AAAXB3NzaC1yc2EAAAADAQABAAAAgQDFNyGjgs6c9akgmZ2ou/fJf7Pdrc23hC95/gM/33OrG4GZABACE4DTioa/PGN+7rHv9YUavUCtXrWayhGniKq/wCuI5fo5TO4AmDNv7/sCGHIHFumADSIoLx0vFhGJIetXEWxL9r0lfFC7//6yZM2W3KcGjbMtlPXqBT9K9PzdyQ== nova@nv-aw2az1-api0001\n") + .privateKey("-----BEGIN RSA PRIVATE KEY-----\nMIICXQIAAAKBgQDFNyGjgs6c9akgmZ2ou/fJf7Pdrc23hC95/gM/33OrG4GZABAC\nE4DTioa/PGN+7rHv9YUavUCtXrWayhGniKq/wCuI5fo5TO4AmDNv7/sCGHIHFumA\nDSIoLx0vFhGJIetXEWxL9r0lfFC7//6yZM2W3KcGjbMtlPXqBT9K9PzdyQIDAQAB\nAoGAW8Ww+KbpQK8smcgCTr/RqcmsSI8VeL2hXjJvDq0L5WbyYuFdkanDvCztUVZn\nsmyfDtwAqZXB4Ct/dN1tY7m8QpdyRaKRW4Q+hghGCAQpsG7rYDdvwdEyvMaW5RA4\ntucQyajMNyQ/tozU3wMx/v8A7RvGcE9tqoG0WK1C3kBu95UCQQDrOd+joYDkvccz\nFIVu5gNPMXEh3fGGzDxk225UlvESquYLzfz4TfmuUjH4Z1BL3wRiwfJsrrjFkm33\njIidDE8PAkEA1qHjxuaIS1yz/rfzErmcOVNlbFHMP4ihjGTTvh1ZctXlNeLwzENQ\nEDaQV3IpUY1KQR6rxcWb5AXgfF9D9PYFpwJBANucAqGAbRgh3lJgPFtXP4u2O0tF\nLPOOxmvbOdybt6KYD4LB5AXmts77SlACFMNhCXUyYaT6UuOSXDyb5gfJsB0CQQC3\nFaGXKU9Z+doQjhlq/6mjvN/nZl80Uvh7Kgb1RVPoAU1kihGeLE0/h0vZTCiyyDNv\nGRqtucMg32J+tUTi0HpBAkAwHiCZMHMeJWHUwIwlRQY/dnR86FWobRl98ViF2rCL\nDHkDVOeIser3Q6zSqU5/m99lX6an5g8pAh/R5LqnOQZC\n-----END RSA PRIVATE KEY-----\n") + .name("testkeypair") + .userId("65649731189278") + .fingerprint("d2:1f:c9:2b:d8:90:77:5f:15:64:27:e3:9f:77:1d:e4") + .build(); + } + + + protected Injector injector() { + return Guice.createInjector(new NovaParserModule(), new GsonModule()); + } +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/predicates/ExtensionPredicatesTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/predicates/ExtensionPredicatesTest.java new file mode 100644 index 0000000000..17e0beb003 --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/predicates/ExtensionPredicatesTest.java @@ -0,0 +1,58 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.nova.v1_1.predicates; + +import java.net.URI; +import static org.jclouds.openstack.nova.v1_1.predicates.ExtensionPredicates.*; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.openstack.nova.v1_1.domain.Extension; +import org.testng.annotations.Test; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ExtensionPredicatesTest") +public class ExtensionPredicatesTest { + Extension ref = Extension.builder().alias("os-keypairs").name("Keypairs").namespace( + URI.create("http://docs.openstack.org/ext/keypairs/api/v1.1")).updated( + new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-08-08T00:00:00+00:00")).description( + "Keypair Support").build(); + + @Test + public void testAliasEqualsWhenEqual() { + assert aliasEquals("os-keypairs").apply(ref); + } + + @Test + public void testAliasEqualsWhenNotEqual() { + assert !aliasEquals("foo").apply(ref); + } + + @Test + public void testNamespaceEqualsWhenEqual() { + assert namespaceEquals(URI.create("http://docs.openstack.org/ext/keypairs/api/v1.1")).apply(ref); + } + + @Test + public void testNamespaceEqualsWhenNotEqual() { + assert !namespaceEquals(URI.create("foo")).apply(ref); + } +} diff --git a/labs/openstack-nova/src/test/resources/extension_details.json b/labs/openstack-nova/src/test/resources/extension_details.json new file mode 100644 index 0000000000..6336a4267e --- /dev/null +++ b/labs/openstack-nova/src/test/resources/extension_details.json @@ -0,0 +1,21 @@ +{ + "extension" : { + "name" : "Public Image Extension", + "namespace" : "http://docs.rackspacecloud.com/servers/api/ext/pie/v1.0", + "alias" : "RS-PIE", + "updated" : "2011-01-22T13:25:27-06:00", + "description" : "Adds the capability to share an image with other users.", + "links" : [ + { + "rel" : "describedby", + "type" : "application/pdf", + "href" : "http://docs.rackspacecloud.com/servers/api/ext/cs-pie-20111111.pdf" + }, + { + "rel" : "describedby", + "type" : "application/vnd.sun.wadl+xml", + "href" : "http://docs.rackspacecloud.com/servers/api/ext/cs-pie.wadl" + } + ] + } +} \ No newline at end of file diff --git a/labs/openstack-nova/src/test/resources/extension_list.json b/labs/openstack-nova/src/test/resources/extension_list.json new file mode 100644 index 0000000000..4f3faa50ef --- /dev/null +++ b/labs/openstack-nova/src/test/resources/extension_list.json @@ -0,0 +1,42 @@ +{ + "extensions": [ + { + "name": "Public Image Extension", + "namespace": "http://docs.rackspacecloud.com/servers/api/ext/pie/v1.0", + "alias": "RAX-PIE", + "updated": "2011-01-22T13:25:27-06:00", + "description": "Adds the capability to share an image with other users.", + "links": [ + { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.rackspacecloud.com/servers/api/ext/cs-pie-20111111.pdf" + }, + { + "rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://docs.rackspacecloud.com/servers/api/ext/cs-pie.wadl" + } + ] + }, + { + "name": "Cloud Block Storage", + "namespace": "http://docs.rackspacecloud.com/servers/api/ext/cbs/v1.0", + "alias": "RAX-CBS", + "updated": "2011-01-12T11:22:33-06:00", + "description": "Allows mounting cloud block storage volumes.", + "links": [ + { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.rackspacecloud.com/servers/api/ext/cs-cbs-20111201.pdf" + }, + { + "rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://docs.rackspacecloud.com/servers/api/ext/cs-cbs.wadl" + } + ] + } + ] +} \ No newline at end of file diff --git a/labs/openstack-nova/src/test/resources/extension_list_normal.json b/labs/openstack-nova/src/test/resources/extension_list_normal.json new file mode 100644 index 0000000000..37fd53f152 --- /dev/null +++ b/labs/openstack-nova/src/test/resources/extension_list_normal.json @@ -0,0 +1,31 @@ +{ + "extensions": [{ + "updated": "2011-08-08T00:00:00+00:00", + "name": "Keypairs", + "links": [], + "namespace": "http://docs.openstack.org/ext/keypairs/api/v1.1", + "alias": "os-keypairs", + "description": "Keypair Support" + }, { + "updated": "2011-03-25T00:00:00+00:00", + "name": "Volumes", + "links": [], + "namespace": "http://docs.openstack.org/ext/volumes/api/v1.1", + "alias": "os-volumes", + "description": "Volumes support" + }, { + "updated": "2011-07-21T00:00:00+00:00", + "name": "SecurityGroups", + "links": [], + "namespace": "http://docs.openstack.org/ext/securitygroups/api/v1.1", + "alias": "security_groups", + "description": "Security group support" + }, { + "updated": "2011-06-16T00:00:00+00:00", + "name": "Floating_ips", + "links": [], + "namespace": "http://docs.openstack.org/ext/floating_ips/api/v1.1", + "alias": "os-floating-ips", + "description": "Floating IPs support" + }] +} \ No newline at end of file diff --git a/labs/openstack-nova/src/test/resources/logback.xml b/labs/openstack-nova/src/test/resources/logback.xml index c1bb1b78c4..d7d07e9668 100644 --- a/labs/openstack-nova/src/test/resources/logback.xml +++ b/labs/openstack-nova/src/test/resources/logback.xml @@ -4,7 +4,7 @@ target/test-data/jclouds.log - %d %-5p [%c] (%t) %m%n + %d %-5p [%c] [%thread] %m%n @@ -12,7 +12,7 @@ target/test-data/jclouds-wire.log - %d %-5p [%c] (%t) %m%n + %d %-5p [%c] [%thread] %m%n @@ -20,7 +20,7 @@ target/test-data/jclouds-compute.log - %d %-5p [%c] (%t) %m%n + %d %-5p [%c] [%thread] %m%n @@ -28,7 +28,7 @@ target/test-data/jclouds-ssh.log - %d %-5p [%c] (%t) %m%n + %d %-5p [%c] [%thread] %m%n diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorAsyncClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorAsyncClient.java index 10b75631fa..3e20e9fbe5 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorAsyncClient.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorAsyncClient.java @@ -20,16 +20,19 @@ package org.jclouds.vcloud.director.v1_5; import org.jclouds.rest.annotations.Delegate; import org.jclouds.vcloud.director.v1_5.domain.AdminOrg; +import org.jclouds.vcloud.director.v1_5.domain.AdminVdc; import org.jclouds.vcloud.director.v1_5.domain.Catalog; import org.jclouds.vcloud.director.v1_5.domain.Group; import org.jclouds.vcloud.director.v1_5.domain.Media; import org.jclouds.vcloud.director.v1_5.domain.Org; import org.jclouds.vcloud.director.v1_5.domain.Session; import org.jclouds.vcloud.director.v1_5.domain.Task; +import org.jclouds.vcloud.director.v1_5.domain.User; import org.jclouds.vcloud.director.v1_5.domain.Vdc; import org.jclouds.vcloud.director.v1_5.domain.ovf.Network; import org.jclouds.vcloud.director.v1_5.features.AdminCatalogAsyncClient; import org.jclouds.vcloud.director.v1_5.features.AdminOrgAsyncClient; +import org.jclouds.vcloud.director.v1_5.features.AdminVdcAsyncClient; import org.jclouds.vcloud.director.v1_5.features.CatalogAsyncClient; import org.jclouds.vcloud.director.v1_5.features.GroupAsyncClient; import org.jclouds.vcloud.director.v1_5.features.NetworkAsyncClient; @@ -37,6 +40,7 @@ import org.jclouds.vcloud.director.v1_5.features.OrgAsyncClient; import org.jclouds.vcloud.director.v1_5.features.QueryAsyncClient; import org.jclouds.vcloud.director.v1_5.features.TaskAsyncClient; import org.jclouds.vcloud.director.v1_5.features.UploadAsyncClient; +import org.jclouds.vcloud.director.v1_5.features.UserAsyncClient; import org.jclouds.vcloud.director.v1_5.features.VAppTemplateAsyncClient; import org.jclouds.vcloud.director.v1_5.features.VdcAsyncClient; @@ -128,4 +132,16 @@ public interface VCloudDirectorAsyncClient { */ @Delegate AdminOrgAsyncClient getAdminOrgClient(); + + /** + * @return asynchronous access to {@link User} features + */ + @Delegate + UserAsyncClient getUserClient(); + + /** + * @return asynchronous access to {@link AdminVdc} features + */ + @Delegate + AdminVdcAsyncClient getAdminVdcClient(); } diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorClient.java index 87fee2652a..041c4195fb 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorClient.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorClient.java @@ -23,16 +23,19 @@ import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; import org.jclouds.rest.annotations.Delegate; import org.jclouds.vcloud.director.v1_5.domain.AdminOrg; +import org.jclouds.vcloud.director.v1_5.domain.AdminVdc; import org.jclouds.vcloud.director.v1_5.domain.Catalog; import org.jclouds.vcloud.director.v1_5.domain.Group; import org.jclouds.vcloud.director.v1_5.domain.Media; import org.jclouds.vcloud.director.v1_5.domain.Org; import org.jclouds.vcloud.director.v1_5.domain.Session; import org.jclouds.vcloud.director.v1_5.domain.Task; +import org.jclouds.vcloud.director.v1_5.domain.User; import org.jclouds.vcloud.director.v1_5.domain.Vdc; import org.jclouds.vcloud.director.v1_5.domain.ovf.Network; import org.jclouds.vcloud.director.v1_5.features.AdminCatalogClient; import org.jclouds.vcloud.director.v1_5.features.AdminOrgClient; +import org.jclouds.vcloud.director.v1_5.features.AdminVdcClient; import org.jclouds.vcloud.director.v1_5.features.CatalogClient; import org.jclouds.vcloud.director.v1_5.features.GroupClient; import org.jclouds.vcloud.director.v1_5.features.MediaClient; @@ -41,6 +44,7 @@ import org.jclouds.vcloud.director.v1_5.features.OrgClient; import org.jclouds.vcloud.director.v1_5.features.QueryClient; import org.jclouds.vcloud.director.v1_5.features.TaskClient; import org.jclouds.vcloud.director.v1_5.features.UploadClient; +import org.jclouds.vcloud.director.v1_5.features.UserClient; import org.jclouds.vcloud.director.v1_5.features.VAppTemplateClient; import org.jclouds.vcloud.director.v1_5.features.VdcClient; @@ -131,4 +135,16 @@ public interface VCloudDirectorClient { */ @Delegate AdminOrgClient getAdminOrgClient(); + + /** + * @return synchronous access to {@link User} features + */ + @Delegate + UserClient getUserClient(); + + /** + * @return synchronous access to {@link AdminVdc} features + */ + @Delegate + AdminVdcClient getAdminVdcClient(); } diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java index be26e4b8c0..add7469d07 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java @@ -153,6 +153,12 @@ public class VCloudDirectorMediaType { public static final String ADMIN_ORG_NETWORK = "application/vnd.vmware.admin.orgNetwork+xml"; + public static final String USER = "application/vnd.vmware.admin.user+xml"; + + public static final String ROLE = "application/vnd.vmware.admin.role+xml"; + + public static final String ADMIN_VDC = "application/vnd.vmware.admin.vdc+xml"; + /** * All acceptable media types. * @@ -173,6 +179,6 @@ public class VCloudDirectorMediaType { PUBLISH_CATALOG_PARAMS, GROUP, ORG_VAPP_TEMPLATE_LEASE_SETTINGS, ORG_LEASE_SETTINGS, ORG_PASSWORD_POLICY_SETTINGS, ORG_LDAP_SETTINGS, ORG_GENERAL_SETTINGS, ORG_EMAIL_SETTINGS, ORG_SETTINGS, ADMIN_NETWORK, - ADMIN_ORG_NETWORK + ADMIN_ORG_NETWORK, USER, ROLE, ADMIN_VDC ); } diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/config/VCloudDirectorRestClientModule.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/config/VCloudDirectorRestClientModule.java index b77551ea0a..859060432d 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/config/VCloudDirectorRestClientModule.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/config/VCloudDirectorRestClientModule.java @@ -64,6 +64,8 @@ import org.jclouds.vcloud.director.v1_5.features.TaskAsyncClient; import org.jclouds.vcloud.director.v1_5.features.TaskClient; import org.jclouds.vcloud.director.v1_5.features.UploadAsyncClient; import org.jclouds.vcloud.director.v1_5.features.UploadClient; +import org.jclouds.vcloud.director.v1_5.features.UserAsyncClient; +import org.jclouds.vcloud.director.v1_5.features.UserClient; import org.jclouds.vcloud.director.v1_5.features.VAppTemplateAsyncClient; import org.jclouds.vcloud.director.v1_5.features.VAppTemplateClient; import org.jclouds.vcloud.director.v1_5.features.VdcAsyncClient; @@ -109,6 +111,7 @@ public class VCloudDirectorRestClientModule extends RestClientModuleJava class for AdminVdc complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="AdminVdc">
+ *   <complexContent>
+ *     <extension base="{http://www.vmware.com/vcloud/v1.5}VdcType">
+ *       <sequence>
+ *         <element name="ResourceGuaranteedMemory" type="{http://www.w3.org/2001/XMLSchema}double" minOccurs="0"/>
+ *         <element name="ResourceGuaranteedCpu" type="{http://www.w3.org/2001/XMLSchema}double" minOccurs="0"/>
+ *         <element name="VCpuInMhz" type="{http://www.w3.org/2001/XMLSchema}long" minOccurs="0"/>
+ *         <element name="IsThinProvision" type="{http://www.w3.org/2001/XMLSchema}boolean" minOccurs="0"/>
+ *         <element name="NetworkPoolReference" type="{http://www.vmware.com/vcloud/v1.5}ReferenceType" minOccurs="0"/>
+ *         <element name="ProviderVdcReference" type="{http://www.vmware.com/vcloud/v1.5}ReferenceType" minOccurs="0"/>
+ *         <element name="UsesFastProvisioning" type="{http://www.w3.org/2001/XMLSchema}boolean" minOccurs="0"/>
+ *       </sequence>
+ *       <anyAttribute processContents='lax' namespace='##other'/>
+ *     </extension>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "AdminVdc", propOrder = { + "resourceGuaranteedMemory", + "resourceGuaranteedCpu", + "vCpuInMhz", + "isThinProvision", + "networkPoolReference", + "providerVdcReference", + "usesFastProvisioning" +}) +public class AdminVdc extends Vdc { + public static Builder builder() { + return new ConcreteBuilder(); + } + + public static abstract class Builder> extends Vdc.Builder { + private Double resourceGuaranteedMemory; + private Double resourceGuaranteedCpu; + private Long vCpuInMhz; + private Boolean isThinProvision; + private Reference networkPoolReference; + private Reference providerVdcReference; + private Boolean usesFastProvisioning; + + /** + * @see AdminVdc#getResourceGuaranteedMemory() + */ + public T resourceGuaranteedMemory(Double resourceGuaranteedMemory) { + this.resourceGuaranteedMemory = resourceGuaranteedMemory; + return self(); + } + + /** + * @see AdminVdc#getResourceGuaranteedCpu() + */ + public T resourceGuaranteedCpu(Double resourceGuaranteedCpu) { + this.resourceGuaranteedCpu = resourceGuaranteedCpu; + return self(); + } + + /** + * @see AdminVdc#getVCpuInMhz() + */ + public T vCpuInMhz(Long vCpuInMhz) { + this.vCpuInMhz = vCpuInMhz; + return self(); + } + + /** + * @see AdminVdc#getIsThinProvision() + */ + public T isThinProvision(Boolean isThinProvision) { + this.isThinProvision = isThinProvision; + return self(); + } + + /** + * @see AdminVdc#getNetworkPoolReference() + */ + public T networkPoolReference(Reference networkPoolReference) { + this.networkPoolReference = networkPoolReference; + return self(); + } + + /** + * @see AdminVdc#getProviderVdcReference() + */ + public T providerVdcReference(Reference providerVdcReference) { + this.providerVdcReference = providerVdcReference; + return self(); + } + + /** + * @see AdminVdc#getUsesFastProvisioning() + */ + public T usesFastProvisioning(Boolean usesFastProvisioning) { + this.usesFastProvisioning = usesFastProvisioning; + return self(); + } + + public AdminVdc build() { + return new AdminVdc(this); + } + + public T fromAdminVdc(AdminVdc in) { + return fromVdc(in) + .resourceGuaranteedMemory(in.getResourceGuaranteedMemory()) + .resourceGuaranteedCpu(in.getResourceGuaranteedCpu()) + .vCpuInMhz(in.getVCpuInMhz()) + .isThinProvision(in.isThinProvision()) + .networkPoolReference(in.getNetworkPoolReference()) + .providerVdcReference(in.getProviderVdcReference()) + .usesFastProvisioning(in.usesFastProvisioning()); + } + } + + private static class ConcreteBuilder extends Builder { + @Override protected ConcreteBuilder self() { + return this; + } + } + + @SuppressWarnings("unused") + private AdminVdc() { + // For JAXB + } + + public AdminVdc(Builder b) { + super(b); + resourceGuaranteedMemory = b.resourceGuaranteedMemory; + resourceGuaranteedCpu = b.resourceGuaranteedCpu; + vCpuInMhz = b.vCpuInMhz; + isThinProvision = b.isThinProvision; + networkPoolReference = b.networkPoolReference; + providerVdcReference = b.providerVdcReference; + usesFastProvisioning = b.usesFastProvisioning; + } + + @XmlElement(name = "ResourceGuaranteedMemory") + protected Double resourceGuaranteedMemory; + @XmlElement(name = "ResourceGuaranteedCpu") + protected Double resourceGuaranteedCpu; + @XmlElement(name = "VCpuInMhz") + protected Long vCpuInMhz; + @XmlElement(name = "IsThinProvision") + protected Boolean isThinProvision; + @XmlElement(name = "NetworkPoolReference") + protected Reference networkPoolReference; + @XmlElement(name = "ProviderVdcReference") + protected Reference providerVdcReference; + @XmlElement(name = "UsesFastProvisioning") + protected Boolean usesFastProvisioning; + + /** + * Gets the value of the resourceGuaranteedMemory property. + * + * @return + * possible object is + * {@link Double } + * + */ + public Double getResourceGuaranteedMemory() { + return resourceGuaranteedMemory; + } + + /** + * Gets the value of the resourceGuaranteedCpu property. + * + * @return + * possible object is + * {@link Double } + * + */ + public Double getResourceGuaranteedCpu() { + return resourceGuaranteedCpu; + } + + /** + * Gets the value of the vCpuInMhz property. + * + * @return + * possible object is + * {@link Long } + * + */ + public Long getVCpuInMhz() { + return vCpuInMhz; + } + + /** + * Gets the value of the isThinProvision property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isThinProvision() { + return isThinProvision; + } + + /** + * Gets the value of the networkPoolReference property. + * + * @return + * possible object is + * {@link Reference } + * + */ + public Reference getNetworkPoolReference() { + return networkPoolReference; + } + + /** + * Gets the value of the providerVdcReference property. + * + * @return + * possible object is + * {@link Reference } + * + */ + public Reference getProviderVdcReference() { + return providerVdcReference; + } + + /** + * Gets the value of the usesFastProvisioning property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean usesFastProvisioning() { + return usesFastProvisioning; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + AdminVdc that = AdminVdc.class.cast(o); + return super.equals(that) && + equal(resourceGuaranteedMemory, that.resourceGuaranteedMemory) && + equal(resourceGuaranteedCpu, that.resourceGuaranteedCpu) && + equal(vCpuInMhz, that.vCpuInMhz) && + equal(isThinProvision, that.isThinProvision) && + equal(networkPoolReference, that.networkPoolReference) && + equal(providerVdcReference, that.providerVdcReference) && + equal(usesFastProvisioning, that.usesFastProvisioning); + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), + resourceGuaranteedMemory, + resourceGuaranteedCpu, + vCpuInMhz, + isThinProvision, + networkPoolReference, + providerVdcReference, + usesFastProvisioning); + } + + @Override + public ToStringHelper string() { + return super.string() + .add("resourceGuaranteedMemory", resourceGuaranteedMemory) + .add("resourceGuaranteedCpu", resourceGuaranteedCpu) + .add("vCpuInMhz", vCpuInMhz) + .add("isThinProvision", isThinProvision) + .add("networkPoolReference", networkPoolReference) + .add("providerVdcReference", providerVdcReference) + .add("usesFastProvisioning", usesFastProvisioning); + } + +} diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/User.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/User.java index f16e165bd1..2b5066da3d 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/User.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/User.java @@ -22,13 +22,17 @@ import static com.google.common.base.Objects.equal; import static com.google.common.base.Preconditions.checkNotNull; import java.net.URI; +import java.util.List; import java.util.Set; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; /** @@ -88,7 +92,7 @@ import com.google.common.collect.Sets; "deployedVmQuota", "role", "password", - "groupReferences" + "groups" }) public class User extends EntityType @@ -122,7 +126,7 @@ public class User private Integer deployedVmQuota; private Reference role; private String password; - private Object /* GroupsList */ groupReferences; + private List groups = Lists.newArrayList(); /** * @see User#getFullName() @@ -261,22 +265,37 @@ public class User } /** - * @see User#getGroupReferences() + * @see User#getGroups() */ - public Builder groupReferences(Object /* GroupsList */ groupReferences) { - this.groupReferences = groupReferences; + public Builder groups(List groups) { + this.groups = groups == null ? null : ImmutableList.copyOf(groups); + return this; + } + + /** + * @see User#getGroups() + */ + public Builder group(Reference group) { + this.groups.add(checkNotNull(group, "group")); return this; } - public User build() { return new User(href, type, links, description, tasks, id, name, fullName, emailAddress, telephone, isEnabled, isLocked, im, nameInSource, isAlertEnabled, alertEmailPrefix, alertEmail, isExternal, isDefaultCached, isGroupRole, storedVmQuota, deployedVmQuota, - role, password, groupReferences); + role, password, groups); } + /** + * @see EntityType#getName() + */ + @Override + public Builder name(String name) { + this.name = name; + return this; + } /** * @see EntityType#getId() @@ -357,7 +376,7 @@ public class User .deployedVmQuota(in.getDeployedVmQuota()) .role(in.getRole()) .password(in.getPassword()) - .groupReferences(in.getGroupReferences()); + .groups(in.getGroups()); } } @@ -395,14 +414,14 @@ public class User protected Reference role; @XmlElement(name = "Password") protected String password; - @XmlElement(name = "GroupReferences") - protected Object /* GroupsList */ groupReferences; + @XmlElementWrapper(name = "GroupReferences") + protected List groups; public User(URI href, String type, Set links, String description, Set tasks, String id, String name, String fullName, String emailAddress, String telephone, Boolean enabled, Boolean locked, String im, String nameInSource, Boolean alertEnabled, String alertEmailPrefix, String alertEmail, Boolean external, Boolean defaultCached, Boolean groupRole, Integer storedVmQuota, Integer deployedVmQuota, - Reference role, String password, Object groupReferences) { + Reference role, String password, List groups) { super(href, type, links, description, tasks, id, name); this.fullName = fullName; this.emailAddress = emailAddress; @@ -421,7 +440,7 @@ public class User this.deployedVmQuota = deployedVmQuota; this.role = role; this.password = password; - this.groupReferences = groupReferences; + this.groups = groups; } private User() { @@ -605,8 +624,8 @@ public class User * @return possible object is * {@link GroupsListType } */ - public Object /* GroupsList */ getGroupReferences() { - return groupReferences; + public List getGroups() { + return groups == null ? Lists.newArrayList() : groups; } @Override @@ -633,7 +652,7 @@ public class User equal(deployedVmQuota, that.deployedVmQuota) && equal(role, that.role) && equal(password, that.password) && - equal(groupReferences, that.groupReferences); + equal(groups, that.groups); } @Override @@ -655,7 +674,7 @@ public class User deployedVmQuota, role, password, - groupReferences); + groups); } @Override @@ -678,7 +697,6 @@ public class User .add("deployedVmQuota", deployedVmQuota) .add("role", role) .add("password", password) - .add("groupReferences", groupReferences).toString(); + .add("groups", groups).toString(); } - } diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Vdc.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Vdc.java index 6c3e431980..72f219e3a1 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Vdc.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Vdc.java @@ -21,9 +21,6 @@ package org.jclouds.vcloud.director.v1_5.domain; import static com.google.common.base.Objects.equal; -import java.net.URI; -import java.util.Set; - import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -31,6 +28,7 @@ import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlType; import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; /** @@ -78,23 +76,24 @@ import com.google.common.base.Objects; "isEnabled" }) @XmlSeeAlso({ -// AdminVdc.class + AdminVdc.class }) -public class Vdc - extends EntityType - -{ - public static Builder builder() { - return new Builder(); +public class Vdc extends EntityType { + public static Builder builder() { + return new ConcreteBuilder(); } - + @Override - public Builder toBuilder() { - return new Builder().fromVdc(this); + public Builder toNewBuilder() { + return new ConcreteBuilder(); } - - public static class Builder extends EntityType.Builder { - + + @Override + public ResourceType.Builder toBuilder() { + throw new UnsupportedOperationException("Use new builder"); + } + + public abstract static class Builder> extends EntityType.NewBuilder { private String allocationModel; private CapacityWithUsage storageCapacity; private ComputeCapacity computeCapacity; @@ -110,174 +109,96 @@ public class Vdc /** * @see Vdc#getAllocationModel() */ - public Builder allocationModel(String allocationModel) { + public T allocationModel(String allocationModel) { this.allocationModel = allocationModel; - return this; + return self(); } /** * @see Vdc#getStorageCapacity() */ - public Builder storageCapacity(CapacityWithUsage storageCapacity) { + public T storageCapacity(CapacityWithUsage storageCapacity) { this.storageCapacity = storageCapacity; - return this; + return self(); } /** * @see Vdc#getComputeCapacity() */ - public Builder computeCapacity(ComputeCapacity computeCapacity) { + public T computeCapacity(ComputeCapacity computeCapacity) { this.computeCapacity = computeCapacity; - return this; + return self(); } /** * @see Vdc#getResourceEntities() */ - public Builder resourceEntities(ResourceEntities resourceEntities) { + public T resourceEntities(ResourceEntities resourceEntities) { this.resourceEntities = resourceEntities; - return this; + return self(); } /** * @see Vdc#getAvailableNetworks() */ - public Builder availableNetworks(AvailableNetworks availableNetworks) { + public T availableNetworks(AvailableNetworks availableNetworks) { this.availableNetworks = availableNetworks; - return this; + return self(); } /** * @see Vdc#getCapabilities() */ - public Builder capabilities(Capabilities capabilities) { + public T capabilities(Capabilities capabilities) { this.capabilities = capabilities; - return this; + return self(); } /** * @see Vdc#getNicQuota() */ - public Builder nicQuota(int nicQuota) { + public T nicQuota(int nicQuota) { this.nicQuota = nicQuota; - return this; + return self(); } /** * @see Vdc#getNetworkQuota() */ - public Builder networkQuota(int networkQuota) { + public T networkQuota(int networkQuota) { this.networkQuota = networkQuota; - return this; + return self(); } /** * @see Vdc#getVmQuota() */ - public Builder vmQuota(Integer vmQuota) { + public T vmQuota(Integer vmQuota) { this.vmQuota = vmQuota; - return this; + return self(); } /** * @see Vdc#isEnabled() */ - public Builder isEnabled(Boolean isEnabled) { + public T isEnabled(Boolean isEnabled) { this.isEnabled = isEnabled; - return this; + return self(); } /** * @see Vdc#getStatus() */ - public Builder status(Integer status) { + public T status(Integer status) { this.status = status; - return this; + return self(); } public Vdc build() { - return new Vdc( - href, type, links, description, tasks, id, name, allocationModel, storageCapacity, - computeCapacity, resourceEntities, availableNetworks, capabilities, nicQuota, networkQuota, - vmQuota, isEnabled, status); + return new Vdc(this); } - /** - * @see EntityType#getName() - */ - public Builder name(String name) { - super.name(name); - return this; - } - - /** - * @see EntityType#getDescription() - */ - public Builder description(String description) { - super.description(description); - return this; - } - - /** - * @see EntityType#getId() - */ - @Override - public Builder id(String id) { - this.id = id; - return this; - } - - /** - * @see EntityType#getTasks() - */ - @Override - public Builder tasks(Set tasks) { - super.tasks(tasks); - return this; - } - - /** - * @see ReferenceType#getHref() - */ - @Override - public Builder href(URI href) { - this.href = href; - return this; - } - - /** - * @see ReferenceType#getType() - */ - @Override - public Builder type(String type) { - this.type = type; - return this; - } - - /** - * @see EntityType#getLinks() - */ - @Override - public Builder links(Set links) { - super.links(links); - return this; - } - - /** - * @see EntityType#getLinks() - */ - @Override - public Builder link(Link link) { - super.link(link); - return this; - } - - @Override - public Builder fromEntityType(EntityType in) { - return Builder.class.cast(super.fromEntityType(in)); - } - - public Builder fromVdc(Vdc in) { + public T fromVdc(Vdc in) { return fromEntityType(in) .allocationModel(in.getAllocationModel()) .storageCapacity(in.getStorageCapacity()) @@ -292,26 +213,31 @@ public class Vdc .status(in.getStatus()); } } - - public Vdc(URI href, String type, Set links, String description, Set tasks, String id, String name, String allocationModel, CapacityWithUsage storageCapacity, ComputeCapacity computeCapacity, ResourceEntities resourceEntities, AvailableNetworks availableNetworks, Capabilities capabilities, int nicQuota, int networkQuota, Integer vmQuota, Boolean enabled, Integer status) { - super(href, type, links, description, tasks, id, name); - this.allocationModel = allocationModel; - this.storageCapacity = storageCapacity; - this.computeCapacity = computeCapacity; - this.resourceEntities = resourceEntities; - this.availableNetworks = availableNetworks; - this.capabilities = capabilities; - this.nicQuota = nicQuota; - this.networkQuota = networkQuota; - this.vmQuota = vmQuota; - isEnabled = enabled; - this.status = status; + + private static class ConcreteBuilder extends Builder { + @Override protected ConcreteBuilder self() { + return this; + } } - - private Vdc() { + + protected Vdc() { // For JAXB } + public Vdc(Builder b) { + super(b.href, b.type, b.links, b.description, b.tasks, b.id, b.name); + this.allocationModel = b.allocationModel; + this.storageCapacity = b.storageCapacity; + this.computeCapacity = b.computeCapacity; + this.resourceEntities = b.resourceEntities; + this.availableNetworks = b.availableNetworks; + this.capabilities = b.capabilities; + this.nicQuota = b.nicQuota; + this.networkQuota = b.networkQuota; + this.vmQuota = b.vmQuota; + isEnabled = b.isEnabled; + this.status = b.status; + } @XmlElement(name = "AllocationModel", required = true) protected String allocationModel; @@ -447,7 +373,8 @@ public class Vdc if (o == null || getClass() != o.getClass()) return false; Vdc that = Vdc.class.cast(o); - return equal(allocationModel, that.allocationModel) && + return super.equals(that) && + equal(allocationModel, that.allocationModel) && equal(storageCapacity, that.storageCapacity) && equal(computeCapacity, that.computeCapacity) && equal(resourceEntities, that.resourceEntities) && @@ -462,7 +389,8 @@ public class Vdc @Override public int hashCode() { - return Objects.hashCode(allocationModel, + return Objects.hashCode(super.hashCode(), + allocationModel, storageCapacity, computeCapacity, resourceEntities, @@ -476,8 +404,8 @@ public class Vdc } @Override - public String toString() { - return Objects.toStringHelper("") + public ToStringHelper string() { + return super.string() .add("allocationModel", allocationModel) .add("storageCapacity", storageCapacity) .add("computeCapacity", computeCapacity) @@ -488,7 +416,7 @@ public class Vdc .add("networkQuota", networkQuota) .add("vmQuota", vmQuota) .add("isEnabled", isEnabled) - .add("status", status).toString(); + .add("status", status); } } diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcAsyncClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcAsyncClient.java new file mode 100644 index 0000000000..c2b70be3e7 --- /dev/null +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcAsyncClient.java @@ -0,0 +1,51 @@ +/** + * 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.vcloud.director.v1_5.features; + +import java.net.URI; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; + +import org.jclouds.rest.annotations.EndpointParam; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.JAXBResponseParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.vcloud.director.v1_5.domain.AdminVdc; +import org.jclouds.vcloud.director.v1_5.filters.AddVCloudAuthorizationToRequest; +import org.jclouds.vcloud.director.v1_5.functions.ThrowVCloudErrorOn4xx; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * @see AdminVdcClient + * @author danikov + */ +@RequestFilters(AddVCloudAuthorizationToRequest.class) +public interface AdminVdcAsyncClient extends VdcAsyncClient { + + /** + * @see AdminVdcClient#getVdc(URI) + */ + @GET + @Consumes + @JAXBResponseParser + @ExceptionParser(ThrowVCloudErrorOn4xx.class) + ListenableFuture getVdc(@EndpointParam URI vdcRef); +} diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClient.java new file mode 100644 index 0000000000..e0e4ea75a3 --- /dev/null +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClient.java @@ -0,0 +1,48 @@ +/** + * 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.vcloud.director.v1_5.features; + +import java.net.URI; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.vcloud.director.v1_5.domain.AdminVdc; + +/** + * Provides synchronous access to Network. + *

+ * + * @see NetworkAsyncClient + * @see + * @author danikov + */ +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +public interface AdminVdcClient extends VdcClient { + + /** + * Retrieves an admin view of virtual data center. The redwood admin can disable an + * organization vDC. This will prevent any further allocation to be used by the organization. + * Changing the state will not affect allocations already used. For example, if an organization + * vDC is disabled, an organization user cannot deploy or create a new virtual machine in the + * vDC (deploy uses memory and cpu allocations, and create uses storage allocation). + * + * @return the admin vDC or null if not found + */ + AdminVdc getVdc(URI vdcRef); +} diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserAsyncClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserAsyncClient.java new file mode 100644 index 0000000000..82c41560b3 --- /dev/null +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserAsyncClient.java @@ -0,0 +1,100 @@ +/** + * 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.vcloud.director.v1_5.features; + +import java.net.URI; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.EndpointParam; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.JAXBResponseParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.binders.BindToXMLPayload; +import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType; +import org.jclouds.vcloud.director.v1_5.domain.User; +import org.jclouds.vcloud.director.v1_5.filters.AddVCloudAuthorizationToRequest; +import org.jclouds.vcloud.director.v1_5.functions.ThrowVCloudErrorOn4xx; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * @see GroupClient + * @author danikov + */ +@RequestFilters(AddVCloudAuthorizationToRequest.class) +public interface UserAsyncClient { + /** + * @see UserClient#createUser(URI, User) + */ + @POST + @Path("/users") + @Consumes(VCloudDirectorMediaType.USER) + @Produces(VCloudDirectorMediaType.USER) + @JAXBResponseParser + @ExceptionParser(ThrowVCloudErrorOn4xx.class) + ListenableFuture createUser(@EndpointParam URI userRef, + @BinderParam(BindToXMLPayload.class) User user); + + /** + * @see UserClient#getUser(URI) + */ + @GET + @Consumes + @JAXBResponseParser + @ExceptionParser(ThrowVCloudErrorOn4xx.class) + ListenableFuture getUser(@EndpointParam URI userRef); + + /** + * @see UserClient#updateUser(URI, User) + */ + @PUT + @Consumes(VCloudDirectorMediaType.USER) + @Produces(VCloudDirectorMediaType.USER) + @JAXBResponseParser + @ExceptionParser(ThrowVCloudErrorOn4xx.class) + ListenableFuture updateUser(@EndpointParam URI userRef, + @BinderParam(BindToXMLPayload.class) User user); + + /** + * @see UserClient#deleteUser(URI) + */ + @DELETE + @Consumes + @JAXBResponseParser + @ExceptionParser(ThrowVCloudErrorOn4xx.class) + ListenableFuture deleteUser(@EndpointParam URI userRef); + + /** + * @see UserClient#unlockUser(URI) + */ + @POST + @Path("/action/unlock") + @Consumes + @JAXBResponseParser + @ExceptionParser(ThrowVCloudErrorOn4xx.class) + ListenableFuture unlockUser(@EndpointParam URI userRef); +} diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserClient.java new file mode 100644 index 0000000000..350e03af21 --- /dev/null +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserClient.java @@ -0,0 +1,90 @@ +/** + * 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.vcloud.director.v1_5.features; + +import java.net.URI; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.vcloud.director.v1_5.domain.Group; +import org.jclouds.vcloud.director.v1_5.domain.User; + +/** + * Provides synchronous access to {@link Group} objects. + * + * @see GroupAsyncClient + * @author danikov + */ +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +public interface UserClient { + /** + * Creates or imports a user in an organization. The user could be enabled or disabled. + * + *

+    * POST /admin/org/{id}/users
+    * 
+ * + * @param orgRef the reference for the org + * @return the created user + */ + User createUser(URI orgRef, User user); + + /** + * Retrieves a user. This entity could be enabled or disabled. + * + *
+    * GET /admin/user/{id}
+    * 
+ * + * @param userRef the reference for the user + * @return a user + */ + User getUser(URI userRef); + + /** + * Modifies a user. The user object could be enabled or disabled. + * Note: the lock status cannot be changed using this call: use unlockUser. + * + *
+    * PUT /admin/user/{id}
+    * 
+ * + * @param userRef the reference for the user + * @return the modified user + */ + User updateUser(URI userRef, User user); + + /** + * Deletes a user. Enabled and disabled users could be deleted. + * + *
+    * DELETE /admin/catalog/{id}
+    * 
+ */ + void deleteUser(URI userRef); + + /** + * Unlocks a user. + * + *
+    * POST /admin/user/{id}/action/unlock
+    * 
+ */ + void unlockUser(URI userRef); +} diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcAsyncClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcAsyncClient.java index 0f6e26b118..58fbd06bfa 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcAsyncClient.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcAsyncClient.java @@ -66,7 +66,7 @@ public interface VdcAsyncClient { @Consumes @JAXBResponseParser @ExceptionParser(ThrowVCloudErrorOn4xx.class) - ListenableFuture getVdc(@EndpointParam URI vdcURI); + ListenableFuture getVdc(@EndpointParam URI vdcURI); /** * @see VdcClient#captureVApp(URI, CaptureVAppParams) diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java index d6f966d534..d4b6a9555d 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java @@ -748,4 +748,112 @@ public class Checks { // parent type checkResourceType(settings); } + + public static void checkUser(User user) { + // Check optional fields + // NOTE fullName cannot be checked + // NOTE isEnabled cannot be checked + // NOTE isLocked cannot be checked + // NOTE im cannot be checked + // NOTE nameInSource cannot be checked + // NOTE isAlertEnabled cannot be checked + // NOTE alterEmailPrefix cannot be checked + // NOTE isExternal cannot be checked + // NOTE isDefaultCached cannot be checked + // NOTE isGroupRole cannot be checked + // NOTE password cannot be checked + + if (user.getEmailAddress() != null) { + checkEmailAddress(user.getEmailAddress()); + } + if (user.getTelephone() != null) { + checkTelephone(user.getTelephone()); + } + if (user.getAlertEmail() != null) { + checkEmailAddress(user.getAlertEmail()); + } + if (user.getStoredVmQuota() != null) { + assertTrue(user.getStoredVmQuota() >= 0, String.format(OBJ_FIELD_GTE_0, + "User", "storedVmQuota", user.getStoredVmQuota())); + } + if (user.getDeployedVmQuota() != null) { + assertTrue(user.getDeployedVmQuota() >= 0, String.format(OBJ_FIELD_GTE_0, + "User", "deployedVmQuota", user.getDeployedVmQuota())); + } + if (user.getRole() != null) { + checkReferenceType(user.getRole()); + } + if (user.getGroups() != null) { + for (Reference group : user.getGroups()) { + checkReferenceType(group); + } + } + + // parent type + checkEntityType(user); + } + + public static void checkTelephone(String number) { + // TODO: regex validate telephone + } + + public static void checkAdminVdc(AdminVdc vdc) { + // optional + // NOTE isThinProvision cannot be checked + // NOTE usesFastProvisioning cannot be checked + if (vdc.getResourceGuaranteedMemory() != null) { + // TODO: between 0 and 1 inc. + } + if (vdc.getResourceGuaranteedCpu() != null) { + // TODO: between 0 and 1 inc. + } + if (vdc.getVCpuInMhz() != null) { + assertTrue(vdc.getVCpuInMhz() >= 0, String.format(OBJ_FIELD_GTE_0, + "Vdc", "cCpuInMhz", vdc.getVCpuInMhz())); + } + if (vdc.getNetworkPoolReference() != null) { + checkReferenceType(vdc.getNetworkPoolReference()); + } + if (vdc.getProviderVdcReference() != null) { + checkReferenceType(vdc.getProviderVdcReference()); + } + + // parent type + checkVdc(vdc); + } + + public static void checkVdc(Vdc vdc) { + // required + assertNotNull(vdc.getAllocationModel(), String.format(OBJ_FIELD_REQ, "Vdc", "allocationModel")); + // one of: AllocationVApp, AllocationPool, ReservationPool + assertNotNull(vdc.getStorageCapacity(), String.format(OBJ_FIELD_REQ, "Vdc", "storageCapacity")); + checkCapacityWithUsage(vdc.getStorageCapacity()); + assertNotNull(vdc.getComputeCapacity(), String.format(OBJ_FIELD_REQ, "Vdc", "computeCapacity")); + checkComputeCapacity(vdc.getComputeCapacity()); + assertNotNull(vdc.getNicQuota(), String.format(OBJ_FIELD_REQ, "Vdc", "nicQuota")); + assertTrue(vdc.getNicQuota() >= 0, String.format(OBJ_FIELD_GTE_0, + "Vdc", "nicQuota", vdc.getNicQuota())); + assertNotNull(vdc.getNetworkQuota(), String.format(OBJ_FIELD_REQ, "Vdc", "networkQuota")); + assertTrue(vdc.getNetworkQuota() >= 0, String.format(OBJ_FIELD_GTE_0, + "Vdc", "networkQuota", vdc.getNetworkQuota())); + + // optional + // NOTE isEnabled cannot be checked + if (vdc.getResourceEntities() != null) { + checkResourceEntities(vdc.getResourceEntities()); + } + if (vdc.getAvailableNetworks() != null) { + checkAvailableNetworks(vdc.getAvailableNetworks()); + } + if (vdc.getCapabilities() != null) { + checkCapabilities(vdc.getCapabilities()); + } + if (vdc.getVmQuota() != null) { + assertTrue(vdc.getVmQuota() >= 0, String.format(OBJ_FIELD_GTE_0, + "Vdc", "vmQuota", vdc.getVmQuota())); + } + + // parent type + checkEntityType(vdc); + } } diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientExpectTest.java new file mode 100644 index 0000000000..2977902615 --- /dev/null +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientExpectTest.java @@ -0,0 +1,66 @@ +/* + * 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.vcloud.director.v1_5.features; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.jclouds.vcloud.director.v1_5.VCloudDirectorClient; +import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType; +import org.jclouds.vcloud.director.v1_5.domain.AdminVdc; +import org.jclouds.vcloud.director.v1_5.domain.Reference; +import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorRestClientExpectTest; +import org.testng.annotations.Test; + +/** + * Allows us to test a client via its side effects. + * + * @author danikov + */ +@Test(groups = { "unit", "admin", "vdc" }, singleThreaded = true, testName = "AdminVdcClientExpectTest") +public class AdminVdcClientExpectTest extends BaseVCloudDirectorRestClientExpectTest { + + private Reference vdcRef = Reference.builder() + .href(URI.create(endpoint + "???")) + .build(); + + @Test( enabled = false ) + public void testGetVdc() { + VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, + new VcloudHttpRequestPrimer() + .apiCommand("GET", "/admin/vdc/???") + .acceptAnyMedia() + .httpRequestBuilder().build(), + new VcloudHttpResponsePrimer() + .xmlFilePayload("/vdc/admin/vdc.xml", + VCloudDirectorMediaType.ADMIN_VDC) + .httpResponseBuilder().build()); + + AdminVdc expected = adminVdc(); + + assertEquals(client.getAdminVdcClient().getVdc(vdcRef.getHref()), expected); + } + + public static final AdminVdc adminVdc() { + return AdminVdc.builder().fromVdc(VdcClientExpectTest.getVdc()) + + .build(); + } +} diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientLiveTest.java new file mode 100644 index 0000000000..60f34d787c --- /dev/null +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientLiveTest.java @@ -0,0 +1,63 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + *(Link.builder().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(Link.builder().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.vcloud.director.v1_5.features; + +import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_REQ_LIVE; +import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.REF_REQ_LIVE; +import static org.testng.Assert.assertNotNull; + +import org.jclouds.vcloud.director.v1_5.domain.AdminVdc; +import org.jclouds.vcloud.director.v1_5.domain.Checks; +import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorClientLiveTest; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@link NetworkClient} + * + * @author danikov + */ +@Test(groups = { "live", "admin", "vdc" }, singleThreaded = true, testName = "AdminVdcClientLiveTest") +public class AdminVdcClientLiveTest extends BaseVCloudDirectorClientLiveTest { + + public static final String VDC = "admin vdc"; + + /* + * Convenience reference to API client. + */ + protected AdminVdcClient vdcClient; + + @Override + @BeforeClass(inheritGroups = true) + public void setupRequiredClients() { + vdcClient = context.getApi().getAdminVdcClient(); + } + + @Test(testName = "GET /admin/vdc/{id}", enabled = false) + public void testGetNetwork() { + // required for testing + assertNotNull(vdcURI, String.format(REF_REQ_LIVE, VDC)); + + AdminVdc vdc = vdcClient.getVdc(toAdminUri(vdcURI)); + assertNotNull(vdc, String.format(OBJ_REQ_LIVE, VDC)); + + // parent type + Checks.checkAdminVdc(vdc); + } +} diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java new file mode 100644 index 0000000000..fa95254858 --- /dev/null +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java @@ -0,0 +1,189 @@ +/* + * 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.vcloud.director.v1_5.features; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; +import java.util.Collections; + +import org.jclouds.vcloud.director.v1_5.VCloudDirectorClient; +import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType; +import org.jclouds.vcloud.director.v1_5.domain.Link; +import org.jclouds.vcloud.director.v1_5.domain.Reference; +import org.jclouds.vcloud.director.v1_5.domain.User; +import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorRestClientExpectTest; +import org.testng.annotations.Test; + +/** + * Test the {@link GroupClient} by observing its side effects. + * + * @author danikov + */ +@Test(groups = { "unit", "admin", "adminUser"}, singleThreaded = true, testName = "UserClientExpectTest") +public class UserClientExpectTest extends BaseVCloudDirectorRestClientExpectTest { + + private Reference orgRef = Reference.builder() + .href(URI.create(endpoint + "/admin/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0")) + .build(); + + private Reference userRef = Reference.builder() + .href(URI.create(endpoint + "/admin/user/b37223f3-8792-477a-820f-334998f61cd6")) + .build(); + + @Test + public void testCreateUser() { + VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, + new VcloudHttpRequestPrimer() + .apiCommand("POST", "/admin/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0/catalogs") + .xmlFilePayload("/user/createUserSource.xml", VCloudDirectorMediaType.USER) + .acceptMedia(VCloudDirectorMediaType.USER) + .httpRequestBuilder().build(), + new VcloudHttpResponsePrimer() + .xmlFilePayload("/user/createUser.xml", VCloudDirectorMediaType.USER) + .httpResponseBuilder().build()); + + User source = createUserSource(); + User expected = createUser(); + + assertEquals(client.getUserClient().createUser(orgRef.getHref(), source), expected); + } + + public static final User createUserSource() { + return User.builder() + .name("test") + .fullName("testFullName") + .emailAddress("test@test.com") + .telephone("555-1234") + .isEnabled(false) + .im("testIM") + .isAlertEnabled(false) + .alertEmailPrefix("testPrefix") + .alertEmail("testAlert@test.com") + .isExternal(false) + .isGroupRole(false) + .role(Reference.builder() + .type("application/vnd.vmware.admin.role+xml") + .name("vApp User") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/role/ff1e0c91-1288-3664-82b7-a6fa303af4d1")) + .build()) + .password("password") + .groups(Collections.emptyList()) + .build(); + } + + public static final User createUser() { + return createUserSource().toBuilder() + .id("urn:vcloud:user:b37223f3-8792-477a-820f-334998f61cd6") + .type("application/vnd.vmware.admin.user+xml") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/user/b37223f3-8792-477a-820f-334998f61cd6")) + .link(Link.builder() + .rel("edit") + .type("application/vnd.vmware.admin.user+xml") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/user/b37223f3-8792-477a-820f-334998f61cd6")) + .build()) + .link(Link.builder() + .rel("remove") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/user/b37223f3-8792-477a-820f-334998f61cd6")) + .build()) + .isLocked(false) + .isDefaultCached(false) + .storedVmQuota(0) + .deployedVmQuota(0) + .password(null) + .build(); + } + + @Test + public void testGetUser() { + VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, + new VcloudHttpRequestPrimer() + .apiCommand("GET", "/admin/user/b37223f3-8792-477a-820f-334998f61cd6") + .acceptAnyMedia() + .httpRequestBuilder().build(), + new VcloudHttpResponsePrimer() + .xmlFilePayload("/user/user.xml", VCloudDirectorMediaType.USER) + .httpResponseBuilder().build()); + + User expected = user(); + + assertEquals(client.getUserClient().getUser(userRef.getHref()), expected); + } + + public static final User user() { + return createUser().toBuilder() + .nameInSource("test") + .build(); + } + + @Test + public void testUpdateUser() { + VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, + new VcloudHttpRequestPrimer() + .apiCommand("PUT", "/admin/user/b37223f3-8792-477a-820f-334998f61cd6") + .xmlFilePayload("/user/updateUserSource.xml", VCloudDirectorMediaType.USER) + .acceptMedia(VCloudDirectorMediaType.USER) + .httpRequestBuilder().build(), + new VcloudHttpResponsePrimer() + .xmlFilePayload("/user/updateUser.xml", VCloudDirectorMediaType.USER) + .httpResponseBuilder().build()); + + User source = updateUserSource(); + User expected = updateUser(); + + assertEquals(client.getUserClient().updateUser(userRef.getHref(), source), expected); + } + + public static final User updateUserSource() { + return user().toBuilder() + .fullName("new"+user().getFullName()) + .emailAddress("new"+user().getEmailAddress()) + .telephone("1-"+user().getTelephone()) + .isEnabled(true) + .im("new"+user().getIM()) + .isAlertEnabled(true) + .alertEmailPrefix("new"+user().getAlertEmailPrefix()) + .alertEmail("new"+user().getAlertEmail()) + .storedVmQuota(1) + .deployedVmQuota(1) + .password("newPassword") + .build(); + } + + public static final User updateUser() { + return updateUserSource().toBuilder() + .password(null) + .build(); + } + +// POST /admin/user/{id}/action/unlock + + @Test + public void testDeleteUser() { + VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, + new VcloudHttpRequestPrimer() + .apiCommand("DELETE", "/admin/user/b37223f3-8792-477a-820f-334998f61cd6") + .acceptAnyMedia() + .httpRequestBuilder().build(), + new VcloudHttpResponsePrimer() + .httpResponseBuilder().statusCode(204).build()); + + client.getUserClient().deleteUser(userRef.getHref()); + } +} diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java new file mode 100644 index 0000000000..a635d4ed6c --- /dev/null +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java @@ -0,0 +1,204 @@ +/* + * 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.vcloud.director.v1_5.features; + +import static com.google.common.base.Objects.equal; +import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_DEL; +import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_FIELD_UPDATABLE; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.net.URI; + +import org.jclouds.vcloud.director.v1_5.VCloudDirectorException; +import org.jclouds.vcloud.director.v1_5.domain.Checks; +import org.jclouds.vcloud.director.v1_5.domain.Error; +import org.jclouds.vcloud.director.v1_5.domain.Owner; +import org.jclouds.vcloud.director.v1_5.domain.Reference; +import org.jclouds.vcloud.director.v1_5.domain.ReferenceType; +import org.jclouds.vcloud.director.v1_5.domain.User; +import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorClientLiveTest; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; + +/** + * Tests live behavior of {@link AdminGroupClient}. + * + * @author danikov + */ +@Test(groups = { "live", "admin", "adminUser" }, singleThreaded = true, testName = "UserClientLiveTest") +public class UserClientLiveTest extends BaseVCloudDirectorClientLiveTest { + + public static final String USER = "admin user"; + + /* + * Convenience references to API clients. + */ + UserClient userClient; + + /* + * Shared state between dependant tests. + */ + private ReferenceType orgRef; + private User user; + + @Override + @BeforeClass(inheritGroups = true) + public void setupRequiredClients() { + userClient = context.getApi().getUserClient(); + orgRef = Iterables.getFirst(context.getApi().getOrgClient().getOrgList().getOrgs(), null).toAdminReference(endpoint); + } + + @Test(testName = "POST /admin/org/{id}/users") + public void testCreateUser() { + User newUser = User.builder() + .name("test") + .fullName("testFullName") + .emailAddress("test@test.com") + .telephone("555-1234") + .isEnabled(false) + .im("testIM") + .isAlertEnabled(false) + .alertEmailPrefix("testPrefix") + .alertEmail("testAlert@test.com") + .isExternal(false) + .isGroupRole(false) + .role(Reference.builder() // FIXME: auto-fetch a role? or inject + .name("vApp User") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/role/ff1e0c91-1288-3664-82b7-a6fa303af4d1")) + .build()) + .password("password") +// .group() + .build(); + user = userClient.createUser(orgRef.getHref(), newUser); + + Checks.checkUser(user); + } + + @Test(testName = "GET /admin/user/{id}", + dependsOnMethods = { "testCreateUser" }) + public void testGetUser() { + user = userClient.getUser(user.getHref()); + + Checks.checkUser(user); + } + + @Test(testName = "PUT /admin/user/{id}", + dependsOnMethods = { "testGetUser" }) + public void testUpdateUser() { + User oldUser = user.toBuilder().build(); + User newUser = user.toBuilder() +// .name("new"+oldUser.getName()) + .fullName("new"+oldUser.getFullName()) + .emailAddress("new"+oldUser.getEmailAddress()) + .telephone("1-"+oldUser.getTelephone()) + .isEnabled(true) + .im("new"+oldUser.getIM()) + .isAlertEnabled(true) + .alertEmailPrefix("new"+oldUser.getAlertEmailPrefix()) + .alertEmail("new"+oldUser.getAlertEmail()) +// .role(Reference.builder() // FIXME: auto-fetch a role? or inject +// .name("vApp Author") +// .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/role/1bf4457f-a253-3cf1-b163-f319f1a31802")) +// .build()) + .storedVmQuota(1) + .deployedVmQuota(1) + .password("newPassword") + .build(); + + try { + userClient.updateUser(user.getHref(), newUser); + user = userClient.getUser(user.getHref()); + Checks.checkUser(user); +// assertTrue(equal(user.getName(), newUser.getName()), +// String.format(OBJ_FIELD_UPDATABLE, USER, "name")); + assertTrue(equal(user.getFullName(), newUser.getFullName()), + String.format(OBJ_FIELD_UPDATABLE, USER, "fullName")); + assertTrue(equal(user.getEmailAddress(), newUser.getEmailAddress()), + String.format(OBJ_FIELD_UPDATABLE, USER, "emailAddress")); + assertTrue(equal(user.getTelephone(), newUser.getTelephone()), + String.format(OBJ_FIELD_UPDATABLE, USER, "telephone")); + assertTrue(equal(user.isEnabled(), newUser.isEnabled()), + String.format(OBJ_FIELD_UPDATABLE, USER, "isEnabled")); + assertTrue(equal(user.getIM(), newUser.getIM()), + String.format(OBJ_FIELD_UPDATABLE, USER, "im")); + assertTrue(equal(user.isAlertEnabled(), newUser.isAlertEnabled()), + String.format(OBJ_FIELD_UPDATABLE, USER, "isAlertEnabled")); + assertTrue(equal(user.getAlertEmailPrefix(), newUser.getAlertEmailPrefix()), + String.format(OBJ_FIELD_UPDATABLE, USER, "alertEmailPrefix")); + assertTrue(equal(user.getAlertEmail(), newUser.getAlertEmail()), + String.format(OBJ_FIELD_UPDATABLE, USER, "alertEmail")); +// assertTrue(equal(user.getRole(), newUser.getRole()), +// String.format(OBJ_FIELD_UPDATABLE, USER, "role")); + assertTrue(equal(user.getStoredVmQuota(), newUser.getStoredVmQuota()), + String.format(OBJ_FIELD_UPDATABLE, USER, "storedVmQuota")); + assertTrue(equal(user.getDeployedVmQuota(), newUser.getDeployedVmQuota()), + String.format(OBJ_FIELD_UPDATABLE, USER, "deployedVmQuota")); + + // FIXME: assert password is changed with session client? + } finally { + userClient.updateUser(user.getHref(), oldUser); + user = userClient.getUser(user.getHref()); + } + } + +// + + + @Test(testName = "POST /admin/user/{id}/action/unlock", + dependsOnMethods = { "testUpdateUser" } ) + public void testUnlockUser() { + //TODO: check previous tests a) enabled lockout, b) set password + //TODO: attempt too many times with the wrong password + //TODO: verify access is denied + //TODO: unlock user + //TODO: verify access is renewed + } + + @Test(testName = "DELETE /admin/user/{id}", + dependsOnMethods = { "testUnlockUser" } ) + public void testDeleteUser() { + userClient.deleteUser(user.getHref()); + + Error expected = Error.builder() + .message("No access to entity \"(com.vmware.vcloud.entity.user:"+ + user.getId().substring("urn:vcloud:user:".length())+")\".") + .majorErrorCode(403) + .minorErrorCode("ACCESS_TO_RESOURCE_IS_FORBIDDEN") + .build(); + + try { + user = userClient.getUser(user.getHref()); + fail("Should give HTTP 403 error"); + } catch (VCloudDirectorException vde) { + assertEquals(vde.getError(), expected); + user = null; + } catch (Exception e) { + fail("Should have thrown a VCloudDirectorException"); + } + + if (user != null) { // guard against NPE on the .toStrings + assertNull(user, String.format(OBJ_DEL, USER, user.toString())); + } + } +} diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java index e153e4e221..9acaf4f8ed 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java @@ -36,6 +36,7 @@ import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType; import org.jclouds.vcloud.director.v1_5.domain.Link; import org.jclouds.vcloud.director.v1_5.domain.Org; import org.jclouds.vcloud.director.v1_5.domain.Reference; +import org.jclouds.vcloud.director.v1_5.domain.ReferenceType; import org.jclouds.vcloud.director.v1_5.domain.Session; import org.jclouds.vcloud.director.v1_5.domain.Task; import org.jclouds.vcloud.director.v1_5.predicates.ReferenceTypePredicates; @@ -135,4 +136,12 @@ public abstract class BaseVCloudDirectorClientLiveTest extends BaseVersionedServ if (context != null) context.close(); } + + public URI toAdminUri(ReferenceType ref) { + return toAdminUri(ref.getHref()); + } + + public URI toAdminUri(URI uri) { + return Reference.builder().href(uri).build().toAdminReference(endpoint).getHref(); + } } diff --git a/labs/vcloud-director/src/test/resources/user/createUser.xml b/labs/vcloud-director/src/test/resources/user/createUser.xml new file mode 100644 index 0000000000..7736d548bd --- /dev/null +++ b/labs/vcloud-director/src/test/resources/user/createUser.xml @@ -0,0 +1,21 @@ + + + + + testFullName + test@test.com + 555-1234 + false + false + testIM + false + testPrefix + testAlert@test.com + false + false + false + 0 + 0 + + + \ No newline at end of file diff --git a/labs/vcloud-director/src/test/resources/user/createUserSource.xml b/labs/vcloud-director/src/test/resources/user/createUserSource.xml new file mode 100644 index 0000000000..f3ff55dcc0 --- /dev/null +++ b/labs/vcloud-director/src/test/resources/user/createUserSource.xml @@ -0,0 +1,16 @@ + + + testFullName + test@test.com + 555-1234 + false + testIM + false + testPrefix + testAlert@test.com + false + false + + password + + \ No newline at end of file diff --git a/labs/vcloud-director/src/test/resources/user/updateUser.xml b/labs/vcloud-director/src/test/resources/user/updateUser.xml new file mode 100644 index 0000000000..9fe120f336 --- /dev/null +++ b/labs/vcloud-director/src/test/resources/user/updateUser.xml @@ -0,0 +1,21 @@ + + + + newtestFullName + newtest@test.com + 1-555-1234 + true + false + newtestIM + test + true + newtestPrefix + newtestAlert@test.com + false + false + false + 1 + 1 + + + \ No newline at end of file diff --git a/labs/vcloud-director/src/test/resources/user/updateUserSource.xml b/labs/vcloud-director/src/test/resources/user/updateUserSource.xml new file mode 100644 index 0000000000..1fd1fc4513 --- /dev/null +++ b/labs/vcloud-director/src/test/resources/user/updateUserSource.xml @@ -0,0 +1,23 @@ + + + + + newtestFullName + newtest@test.com + 1-555-1234 + true + false + newtestIM + test + true + newtestPrefix + newtestAlert@test.com + false + false + false + 1 + 1 + + newPassword + + \ No newline at end of file diff --git a/labs/vcloud-director/src/test/resources/user/user.xml b/labs/vcloud-director/src/test/resources/user/user.xml new file mode 100644 index 0000000000..4ee7b3d970 --- /dev/null +++ b/labs/vcloud-director/src/test/resources/user/user.xml @@ -0,0 +1,22 @@ + + + + + testFullName + test@test.com + 555-1234 + false + false + testIM + test + false + testPrefix + testAlert@test.com + false + false + false + 0 + 0 + + + \ No newline at end of file diff --git a/labs/vcloud-director/src/test/resources/vdc/admin/vdc.xml b/labs/vcloud-director/src/test/resources/vdc/admin/vdc.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java index 968cbcde5e..f28e279b13 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java @@ -19,18 +19,16 @@ package org.jclouds.virtualbox; -import static org.jclouds.Constants.*; +import static org.jclouds.Constants.PROPERTY_API_VERSION; import static org.jclouds.Constants.PROPERTY_BUILD_VERSION; -import static org.jclouds.Constants.PROPERTY_CREDENTIAL; import static org.jclouds.Constants.PROPERTY_ENDPOINT; -import static org.jclouds.Constants.PROPERTY_IDENTITY; import static org.jclouds.compute.reference.ComputeServiceConstants.PROPERTY_IMAGE_AUTHENTICATE_SUDO; import static org.jclouds.compute.reference.ComputeServiceConstants.PROPERTY_IMAGE_LOGIN_USER; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_DIR; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGES_DESCRIPTOR; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL; -import static org.jclouds.virtualbox.config.VirtualBoxConstants.*; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR; import java.io.File; import java.util.Properties; @@ -60,11 +58,9 @@ public class VirtualBoxPropertiesBuilder extends PropertiesBuilder { properties.put(PROPERTY_API_VERSION, "4.1.4"); properties.put(PROPERTY_BUILD_VERSION, "4.1.8r75467"); - properties.put(PROPERTY_IDENTITY, "toor"); - properties.put(PROPERTY_CREDENTIAL, "password"); - properties.put(PROPERTY_IMAGE_LOGIN_USER, "toor:password"); properties.put(PROPERTY_IMAGE_AUTHENTICATE_SUDO, "true"); + properties.put(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE, " " + "/install/vmlinuz noapic preseed/url=PRECONFIGURATION_URL " diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/BridgedIf.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/BridgedIf.java new file mode 100644 index 0000000000..2584986fa3 --- /dev/null +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/BridgedIf.java @@ -0,0 +1,203 @@ +/** + * 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.virtualbox.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Objects; + +/** + * Name: en1: Wi-Fi (AirPort) GUID: 00316e65-0000-4000-8000-28cfdaf2917a Dhcp: + * Disabled IPAddress: 192.168.57.1 NetworkMask: 255.255.255.0 IPV6Address: + * IPV6NetworkMaskPrefixLength: 0 HardwareAddress: 28:cf:da:f2:91:7a MediumType: + * Ethernet Status: Up VBoxNetworkName: HostInterfaceNetworking-en1: Wi-Fi + * (AirPort) + * + * @author Andrea Turli + * + */ +public class BridgedIf { + + private final String name; + private final String guid; + private final String dhcp; + private final String ipAddress; + private final String networkMask; + private final String ipv6Address; + private final String ipv6NetworkMask; + private final String mediumType; + private final String status; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String name; + private String guid; + private String dhcp; + private String ipAddress; + private String networkMask; + private String ipv6Address; + private String iv6NetworkMask; + private String mediumType; + private String status; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder guid(String guid) { + this.guid = guid; + return this; + } + + public Builder dhcp(String dhcp) { + this.dhcp = dhcp; + return this; + } + + public Builder ip(String ipAddress) { + this.ipAddress = ipAddress; + return this; + } + + public Builder networkMask(String networkMask) { + this.networkMask = networkMask; + return this; + } + + public Builder ipv6(String ipv6Address) { + this.ipv6Address = ipv6Address; + return this; + } + + public Builder ipv6networkMask(String iv6NetworkMask) { + this.iv6NetworkMask = iv6NetworkMask; + return this; + } + + public Builder mediumType(String mediumType) { + this.mediumType = mediumType; + return this; + } + + public Builder status(String status) { + this.status = status; + return this; + } + + public BridgedIf build() { + return new BridgedIf(name, guid, dhcp, ipAddress, networkMask, + ipv6Address, iv6NetworkMask, mediumType, status); + } + } + + public BridgedIf(String name, String guid, String dhcp, String ipAddress, + String networkMask, String ipv6Address, String iv6NetworkMask, + String mediumType, String status) { + this.name = checkNotNull(name, "bridgedIf name"); + this.guid = guid; + this.dhcp = dhcp; + this.ipAddress = checkNotNull(ipAddress, "bridgedIf ipAddress"); + this.networkMask = networkMask; + this.ipv6Address = ipv6Address; + this.ipv6NetworkMask = iv6NetworkMask; + this.mediumType = mediumType; + this.status = checkNotNull(status, "bridgedIf status"); + } + + public String getName() { + return name; + } + + public String getGuid() { + return guid; + } + + public String getDhcp() { + return dhcp; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getNetworkMask() { + return networkMask; + } + + public String getIpv6Address() { + return ipv6Address; + } + + public String getIpv6NetworkMask() { + return ipv6NetworkMask; + } + + public String getMediumType() { + return mediumType; + } + + public String getStatus() { + return status; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o instanceof VmSpec) { + BridgedIf other = (BridgedIf) o; + return Objects.equal(name, other.name) + && Objects.equal(guid, other.guid) + && Objects.equal(dhcp, other.dhcp) + && Objects.equal(ipAddress, other.ipAddress) + && Objects.equal(networkMask, other.networkMask) + && Objects.equal(ipv6Address, other.ipv6Address) + && Objects.equal(ipv6NetworkMask, other.ipv6NetworkMask) + && Objects.equal(mediumType, other.mediumType) + && Objects.equal(status, other.status); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(name, guid, dhcp, ipAddress, networkMask, ipv6Address, ipv6NetworkMask, mediumType, status); + } + + @Override + public String toString() { + return "BridgedIf{" + + "name=" + name + + ", dhcp=" + dhcp + + ", ipAddress=" + ipAddress + + ", networkMask=" + networkMask + + ", ipv6Address=" + ipv6Address + + ", ipv6NetworkMask=" + ipv6NetworkMask + + ", mediumType=" + mediumType + + ", status=" + status + + '}'; + } + +} diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/CloneSpec.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/CloneSpec.java index d84c72538f..b4727e4681 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/CloneSpec.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/CloneSpec.java @@ -117,6 +117,6 @@ public class CloneSpec { @Override public String toString() { - return "IMachineSpec{" + "vmSpec= " + vmSpec + ", networkSpec= " + networkSpec + '}'; + return "CloneSpec{" + "vmSpec= " + vmSpec + ", networkSpec= " + networkSpec + '}'; } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/BridgedIfStringToBridgedIf.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/BridgedIfStringToBridgedIf.java new file mode 100644 index 0000000000..c67a73591e --- /dev/null +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/BridgedIfStringToBridgedIf.java @@ -0,0 +1,100 @@ +/** + * 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.virtualbox.functions; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.virtualbox.domain.BridgedIf; + +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; + +@Singleton +public class BridgedIfStringToBridgedIf implements Function { + + private static final String BRIDGED_IF_STATUS = "Status"; + private static final String BRIDGED_IF_MEDIUM_TYPE = "MediumType"; + private static final String BRIDGED_IF_NETWORK_MASK = "NetworkMask"; + private static final String BRIDGED_IF_IP_ADDRESS = "IPAddress"; + private static final String BRIDGED_IF_GUID = "GUID"; + private static final String BRIDGED_IF_NAME = "Name"; + + @Inject + public BridgedIfStringToBridgedIf() { + } + + @Override + public BridgedIf apply(String rawBridgedIf) { + checkNotNull(rawBridgedIf, "rawBridgedIf"); + + String transformedBridgedIf = transformRawBridgedIf(rawBridgedIf); + Map bridegedIfMap = Splitter.on("\n") + .omitEmptyStrings().withKeyValueSeparator("=") + .split(transformedBridgedIf); + + return BridgedIf + .builder() + .name(getValueFromMap(bridegedIfMap, BRIDGED_IF_NAME)) + .guid(getValueFromMap(bridegedIfMap, BRIDGED_IF_GUID)) + .ip(getValueFromMap(bridegedIfMap, BRIDGED_IF_IP_ADDRESS)) + .networkMask(getValueFromMap(bridegedIfMap, BRIDGED_IF_NETWORK_MASK)) + .mediumType(getValueFromMap(bridegedIfMap, BRIDGED_IF_MEDIUM_TYPE)) + .status(getValueFromMap(bridegedIfMap, BRIDGED_IF_STATUS)) + .build(); + } + + private String getValueFromMap(Map map, String key) { + return map.get(key).trim(); + } + + /** + * This is an helper to simplify the split step of the raw bridgedIf + * Substitute first ':' with '=' + * + * @param rawBridgedIf + * @return + */ + private String transformRawBridgedIf(String rawBridgedIf) { + Iterable transformedLines = Iterables.transform( + Splitter.on("\n").split(rawBridgedIf), + new Function() { + + @Override + public String apply(String line) { + return line.replaceFirst(":", "="); + } + + }); + + StringBuilder stringBuilder = new StringBuilder(); + + for (String line : transformedLines) { + stringBuilder.append(line + "\n"); + } + return stringBuilder.toString(); + } + +} diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java index 446cd8e4ea..ed910ac965 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java @@ -33,18 +33,15 @@ import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import org.jclouds.ssh.SshClient; import org.jclouds.virtualbox.Preconfiguration; -import org.jclouds.virtualbox.domain.ExecutionType; import org.jclouds.virtualbox.domain.IsoSpec; import org.jclouds.virtualbox.domain.MasterSpec; import org.jclouds.virtualbox.domain.VmSpec; import org.jclouds.virtualbox.predicates.GuestAdditionsInstaller; +import org.jclouds.virtualbox.util.MachineController; import org.jclouds.virtualbox.util.MachineUtils; import org.virtualbox_4_1.IMachine; -import org.virtualbox_4_1.IProgress; -import org.virtualbox_4_1.ISession; import org.virtualbox_4_1.LockType; import org.virtualbox_4_1.VirtualBoxManager; -import org.virtualbox_4_1.jaxws.MachineState; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -56,124 +53,99 @@ import com.google.inject.Inject; @Singleton public class CreateAndInstallVm implements Function { - @Resource - @Named(ComputeServiceConstants.COMPUTE_LOGGER) - protected Logger logger = Logger.NULL; + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; - private final Supplier manager; - private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists; - private final GuestAdditionsInstaller guestAdditionsInstaller; - private final Predicate sshResponds; - private final ExecutionType executionType; - private LoadingCache preConfiguration; - private final Function sshClientForIMachine; - private final MachineUtils machineUtils; - private final IMachineToNodeMetadata imachineToNodeMetadata; + private final Supplier manager; + private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists; + private final GuestAdditionsInstaller guestAdditionsInstaller; + private final Predicate sshResponds; + private LoadingCache preConfiguration; + private final Function sshClientForIMachine; + private final MachineUtils machineUtils; + private final IMachineToNodeMetadata imachineToNodeMetadata; + private final MachineController machineController; + - @Inject - public CreateAndInstallVm(Supplier manager, - CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists, - GuestAdditionsInstaller guestAdditionsInstaller, IMachineToNodeMetadata imachineToNodeMetadata, - Predicate sshResponds, Function sshClientForIMachine, - ExecutionType executionType, MachineUtils machineUtils, - @Preconfiguration LoadingCache preConfiguration) { - this.manager = manager; - this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists; - this.sshResponds = sshResponds; - this.sshClientForIMachine = sshClientForIMachine; - this.executionType = executionType; - this.machineUtils = machineUtils; - this.preConfiguration = preConfiguration; - this.guestAdditionsInstaller = guestAdditionsInstaller; - this.imachineToNodeMetadata = imachineToNodeMetadata; - } + @Inject + public CreateAndInstallVm( + Supplier manager, + CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists, + GuestAdditionsInstaller guestAdditionsInstaller, + IMachineToNodeMetadata imachineToNodeMetadata, + Predicate sshResponds, + Function sshClientForIMachine, + MachineUtils machineUtils, + @Preconfiguration LoadingCache preConfiguration, MachineController machineController) { + this.manager = manager; + this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists; + this.sshResponds = sshResponds; + this.sshClientForIMachine = sshClientForIMachine; + this.machineUtils = machineUtils; + this.preConfiguration = preConfiguration; + this.guestAdditionsInstaller = guestAdditionsInstaller; + this.imachineToNodeMetadata = imachineToNodeMetadata; + this.machineController = machineController; + } - @Override - public IMachine apply(MasterSpec masterSpec) { + @Override + public IMachine apply(MasterSpec masterSpec) { - VmSpec vmSpec = masterSpec.getVmSpec(); - IsoSpec isoSpec = masterSpec.getIsoSpec(); - String vmName = vmSpec.getVmName(); + VmSpec vmSpec = masterSpec.getVmSpec(); + IsoSpec isoSpec = masterSpec.getIsoSpec(); + String vmName = vmSpec.getVmName(); - IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists.apply(masterSpec); + IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists + .apply(masterSpec); - // Launch machine and wait for it to come online - ensureMachineIsLaunched(vmName); + // Launch machine and wait for it to come online + machineController.ensureMachineIsLaunched(vmName); - URI uri = preConfiguration.getUnchecked(isoSpec); - String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL", - uri.toASCIIString()); + URI uri = preConfiguration.getUnchecked(isoSpec); + String installationKeySequence = isoSpec.getInstallationKeySequence() + .replace("PRECONFIGURATION_URL", uri.toASCIIString()); - configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence); - SshClient client = sshClientForIMachine.apply(vm); - - logger.debug(">> awaiting installation to finish node(%s)", vmName); + configureOsInstallationWithKeyboardSequence(vmName, + installationKeySequence); + SshClient client = sshClientForIMachine.apply(vm); - checkState(sshResponds.apply(client), "timed out waiting for guest %s to be accessible via ssh", vmName); + logger.debug(">> awaiting installation to finish node(%s)", vmName); - logger.debug(">> awaiting installation of guest additions on vm: %s", vmName); + checkState(sshResponds.apply(client), + "timed out waiting for guest %s to be accessible via ssh", + vmName); - checkState(guestAdditionsInstaller.apply(vm)); + //logger.debug(">> awaiting installation of guest additions on vm: %s", vmName); + //checkState(guestAdditionsInstaller.apply(vm)); - logger.debug(">> awaiting post-installation actions on vm: %s", vmName); + logger.debug(">> awaiting post-installation actions on vm: %s", vmName); - NodeMetadata vmMetadata = imachineToNodeMetadata.apply(vm); - - // TODO for now this is executed on installModuleAssistantIfNeeded as a workaround to some transient execution issue. -// ListenableFuture execFuture = machineUtils.runScriptOnNode(vmMetadata, call("cleanupUdevIfNeeded"), -// RunScriptOptions.NONE); - -// ExecResponse execResponse = Futures.getUnchecked(execFuture); -// checkState(execResponse.getExitCode() == 0); + NodeMetadata vmMetadata = imachineToNodeMetadata.apply(vm); - logger.debug("<< installation of image complete. Powering down node(%s)", vmName); + ListenableFuture execFuture = + machineUtils.runScriptOnNode(vmMetadata, call("cleanupUdevIfNeeded"), RunScriptOptions.NONE); - ensureMachineHasPowerDown(vmName); - return vm; - } + ExecResponse execResponse = Futures.getUnchecked(execFuture); + checkState(execResponse.getExitStatus() == 0); - private void configureOsInstallationWithKeyboardSequence(String vmName, String installationKeySequence) { - Iterable> scancodelist = transform(Splitter.on(" ").split(installationKeySequence), - new StringToKeyCode()); + logger.debug( + "<< installation of image complete. Powering down node(%s)", + vmName); - for (List scancodes : scancodelist) { - machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new SendScancodes(scancodes)); - } - } + machineController.ensureMachineHasPowerDown(vmName); + return vm; + } - /** - * ensureMachineHasPowerDown needs to have this delay just to ensure that the machine is - * completely powered off - * - * @param vmName - */ - private void ensureMachineHasPowerDown(String vmName) { - while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.POWERED_OFF)) { - try { - machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { - @Override - public Void apply(ISession session) { - IProgress powerDownProgress = session.getConsole().powerDown(); - powerDownProgress.waitForCompletion(-1); - return null; - } - }); - } catch (RuntimeException e) { - // sometimes the machine might be powered of between the while test and the call to - // lockSessionOnMachineAndApply - if (e.getMessage().contains("Invalid machine state: PoweredOff")) { - return; - } else if (e.getMessage().contains("VirtualBox error: The object is not ready")) { - continue; - } else { - throw e; - } - } - } - } + private void configureOsInstallationWithKeyboardSequence(String vmName, + String installationKeySequence) { + Iterable> scancodelist = transform(Splitter.on(" ") + .split(installationKeySequence), new StringToKeyCode()); - private void ensureMachineIsLaunched(String vmName) { - machineUtils.applyForMachine(vmName, new LaunchMachineIfNotAlreadyRunning(manager.get(), executionType, "")); - } + for (List scancodes : scancodelist) { + machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, + new SendScancodes(scancodes)); + } + } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java index a1d2417031..56e05d11f7 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java @@ -19,58 +19,123 @@ package org.jclouds.virtualbox.functions; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import java.util.List; import javax.annotation.Resource; import javax.inject.Named; import javax.inject.Singleton; +import org.jclouds.compute.callables.RunScriptOnNode; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.domain.LoginCredentials; import org.jclouds.logging.Logger; import org.jclouds.net.IPSocket; import org.jclouds.ssh.SshClient; +import org.jclouds.virtualbox.domain.BridgedIf; +import org.jclouds.virtualbox.statements.GetIPAddressFromMAC; +import org.jclouds.virtualbox.statements.ScanNetworkWithPing; import org.virtualbox_4_1.IMachine; import org.virtualbox_4_1.INetworkAdapter; +import org.virtualbox_4_1.NetworkAttachmentType; import com.google.common.base.Function; import com.google.common.base.Splitter; +import com.google.common.base.Supplier; import com.google.common.collect.Iterables; import com.google.inject.Inject; @Singleton public class IMachineToSshClient implements Function { - @Resource - @Named(ComputeServiceConstants.COMPUTE_LOGGER) - protected Logger logger = Logger.NULL; + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; - private final SshClient.Factory sshClientFactory; + private final SshClient.Factory sshClientFactory; + private final RunScriptOnNode.Factory scriptRunnerFactory; + private final Supplier hostSupplier; - @Inject - public IMachineToSshClient(SshClient.Factory sshClientFactory) { - this.sshClientFactory = sshClientFactory; - } + @Inject + public IMachineToSshClient(SshClient.Factory sshClientFactory, + RunScriptOnNode.Factory scriptRunnerFactory, + Supplier hostSupplier) { + this.sshClientFactory = sshClientFactory; + this.scriptRunnerFactory = scriptRunnerFactory; + this.hostSupplier = hostSupplier; + } - @Override - public SshClient apply(final IMachine vm) { - INetworkAdapter networkAdapter = vm.getNetworkAdapter(0L); + @Override + public SshClient apply(final IMachine vm) { + INetworkAdapter networkAdapter = vm.getNetworkAdapter(0L); - SshClient client = null; - checkNotNull(networkAdapter); - for (String nameProtocolnumberAddressInboudportGuestTargetport : networkAdapter.getNatDriver().getRedirects()) { - Iterable stuff = Splitter.on(',').split(nameProtocolnumberAddressInboudportGuestTargetport); - String protocolNumber = Iterables.get(stuff, 1); - String hostAddress = Iterables.get(stuff, 2); - String inboundPort = Iterables.get(stuff, 3); - String targetPort = Iterables.get(stuff, 5); - // TODO: we need a way to align the default login credentials from the iso with the - // vmspec - if ("1".equals(protocolNumber) && "22".equals(targetPort)) { - client = sshClientFactory.create(new IPSocket(hostAddress, Integer.parseInt(inboundPort)), LoginCredentials - .builder().user("toor").password("password").authenticateSudo(true).build()); - } - } - checkNotNull(client); - return client; - } + SshClient client = null; + checkNotNull(networkAdapter); + + String clientIpAddress = null; + String sshPort = "22"; + + // TODO: we need a way to align the default login credentials + // from the iso with the vmspec -> IMachineToNodeMetadata using YamlImage ? + LoginCredentials loginCredentials = LoginCredentials.builder() + .user("toor").password("password").authenticateSudo(true) + .build(); + + if (networkAdapter.getAttachmentType() + .equals(NetworkAttachmentType.NAT)) { + for (String nameProtocolnumberAddressInboudportGuestTargetport : networkAdapter + .getNatDriver().getRedirects()) { + Iterable stuff = Splitter.on(',').split( + nameProtocolnumberAddressInboudportGuestTargetport); + String protocolNumber = Iterables.get(stuff, 1); + String hostAddress = Iterables.get(stuff, 2); + String inboundPort = Iterables.get(stuff, 3); + String targetPort = Iterables.get(stuff, 5); + if ("1".equals(protocolNumber) && "22".equals(targetPort)) { + clientIpAddress = hostAddress; + sshPort = inboundPort; + } + } + } else if (networkAdapter.getAttachmentType().equals( + NetworkAttachmentType.Bridged)) { + String network = "1.1.1.1"; + clientIpAddress = getIpAddressFromBridgedNIC(networkAdapter, network); + } + + checkNotNull(clientIpAddress, "clientIpAddress"); + client = sshClientFactory.create( + new IPSocket(clientIpAddress, Integer.parseInt(sshPort)), + loginCredentials); + checkNotNull(client); + return client; + } + + private String getIpAddressFromBridgedNIC(INetworkAdapter networkAdapter, + String network) { + // RetrieveActiveBridgedInterfaces + List activeBridgedInterfaces = new RetrieveActiveBridgedInterfaces(scriptRunnerFactory).apply(hostSupplier.get()); + BridgedIf activeBrigedIf = checkNotNull(Iterables.get(activeBridgedInterfaces, 0), "activeBridgrdIf"); + network = activeBrigedIf.getIpAddress(); + + // scan ip + RunScriptOnNode ipScanRunScript = scriptRunnerFactory.create( + hostSupplier.get(), new ScanNetworkWithPing(network), + RunScriptOptions.NONE); + ExecResponse execResponse = ipScanRunScript.init().call(); + checkState(execResponse.getExitStatus() == 0); + + // retrieve ip from mac + RunScriptOnNode getIpFromMACAddressRunScript = scriptRunnerFactory + .create(hostSupplier.get(), new GetIPAddressFromMAC( + networkAdapter.getMACAddress()), + RunScriptOptions.NONE); + ExecResponse ipExecResponse = getIpFromMACAddressRunScript.init() + .call(); + checkState(ipExecResponse.getExitStatus() == 0); + return checkNotNull(ipExecResponse.getOutput(), "ipAddress"); + } } \ No newline at end of file diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java index 37651d62a5..9e60b907d3 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java @@ -23,15 +23,18 @@ import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import javax.inject.Inject; import javax.inject.Singleton; import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials; +import org.jclouds.compute.callables.RunScriptOnNode; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.domain.LoginCredentials; +import org.jclouds.virtualbox.domain.BridgedIf; import org.jclouds.virtualbox.domain.CloneSpec; import org.jclouds.virtualbox.domain.ExecutionType; import org.jclouds.virtualbox.domain.Master; @@ -65,33 +68,34 @@ public class NodeCreator implements Function manager; private final Function cloner; - private final AtomicInteger nodes; + private final AtomicInteger nodePorts; + private final AtomicInteger nodeIps; private MachineUtils machineUtils; private Function imachineToNodeMetadata; + private final RunScriptOnNode.Factory scriptRunnerFactory; + private final Supplier hostSupplier; + @Inject public NodeCreator(Supplier manager, Function cloner, - MachineUtils machineUtils, Function imachineToNodeMetadata) { + MachineUtils machineUtils, Function imachineToNodeMetadata, + RunScriptOnNode.Factory scriptRunnerFactory, Supplier hostSupplier) { this.manager = manager; this.cloner = cloner; - this.nodes = new AtomicInteger(0); + this.nodePorts = new AtomicInteger(NODE_PORT_INIT); + this.nodeIps = new AtomicInteger(1); this.machineUtils = machineUtils; this.imachineToNodeMetadata = imachineToNodeMetadata; + this.scriptRunnerFactory = scriptRunnerFactory; + this.hostSupplier = hostSupplier; + } - /** - * Creates a clone based on the {@link NodeSpec}. It is synchronized because it needs sole access - * to the master. Could be improved by locking on a master basis (would allow concurrent cloning - * as long as form different masters." - */ @Override - public synchronized NodeAndInitialCredentials apply(NodeSpec nodeSpec) { + public NodeAndInitialCredentials apply(NodeSpec nodeSpec) { checkNotNull(nodeSpec, "NodeSpec"); @@ -108,7 +112,6 @@ public class NodeCreator implements Function nodeAndInitialCredentials = new NodeAndInitialCredentials(cloned, cloneName, LoginCredentials.builder().user("toor").password("password").authenticateSudo(true).build()); return nodeAndInitialCredentials; } + + private NetworkSpec createNetworkSpecForHostOnlyNATNICs(NetworkInterfaceCard natIfaceCard, + NetworkInterfaceCard hostOnlyIfaceCard) { + return NetworkSpec.builder().addNIC(natIfaceCard).addNIC(hostOnlyIfaceCard).build(); + } + + private NetworkSpec createNetworkSpecForBridgedNIC() { + List activeBridgedInterfaces = new RetrieveActiveBridgedInterfaces(scriptRunnerFactory) + .apply(hostSupplier.get()); + BridgedIf bridgedActiveInterface = checkNotNull(activeBridgedInterfaces.get(0), "activeBridgedIf"); + + NetworkAdapter bridgedAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.Bridged) + .build(); + NetworkInterfaceCard bridgedNIC = NetworkInterfaceCard.builder().addNetworkAdapter(bridgedAdapter) + .addHostInterfaceName(bridgedActiveInterface.getName()).slot(0L).build(); + + NetworkSpec networkSpec = NetworkSpec.builder().addNIC(bridgedNIC).build(); + return networkSpec; + } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java index 886278b032..0cd9fc327c 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java @@ -37,6 +37,7 @@ import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.virtualbox.domain.BridgedIf; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -50,67 +51,56 @@ import com.google.inject.name.Named; /** * @author Andrea Turli */ -public class RetrieveActiveBridgedInterfaces implements Function> { +public class RetrieveActiveBridgedInterfaces implements Function> { - @Resource +@Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; private final Factory runScriptOnNodeFactory; @Inject - public RetrieveActiveBridgedInterfaces(Factory runScriptOnNodeFactory) { + public RetrieveActiveBridgedInterfaces(Factory runScriptOnNodeFactory) { this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory"); } @Override - public List apply(NodeMetadata host) { + public List apply(NodeMetadata host) { // Bridged Network Statement command = Statements.exec("VBoxManage list bridgedifs"); String bridgedIfBlocks = runScriptOnNodeFactory.create(host, command, runAsRoot(false).wrapInInitScript(false)) .init().call().getOutput(); - List bridgedInterfaces = retrieveBridgedInterfaceNames(bridgedIfBlocks); + List bridgedInterfaces = retrieveBridgedInterfaceNames(bridgedIfBlocks); checkNotNull(bridgedInterfaces); // union of bridgedNetwork with inet up and !loopback - List activeNetworkInterfaceNames = Lists.newArrayList(); + List activeNetworkInterfaces = Lists.newArrayList(); try { Enumeration nets = NetworkInterface.getNetworkInterfaces(); for (NetworkInterface inet : Collections.list(nets)) { - Iterable filteredBridgedInterface = filter(bridgedInterfaces, new IsActiveBridgedInterface(inet)); - Iterables.addAll(activeNetworkInterfaceNames, filteredBridgedInterface); + Iterable filteredBridgedInterface = filter(bridgedInterfaces, new IsActiveBridgedInterface(inet)); + Iterables.addAll(activeNetworkInterfaces, filteredBridgedInterface); } } catch (SocketException e) { logger.error(e, "Problem in listing network interfaces."); Throwables.propagate(e); assert false; } - return activeNetworkInterfaceNames; + return activeNetworkInterfaces; } - protected static List retrieveBridgedInterfaceNames(String bridgedIfBlocks) { - List bridgedInterfaceNames = Lists.newArrayList(); + protected static List retrieveBridgedInterfaceNames(String bridgedIfBlocks) { + List bridgedInterfaces = Lists.newArrayList(); // separate the different bridge block for (String bridgedIfBlock : Splitter.on(Pattern.compile("(?m)^[ \t]*\r?\n")).split(bridgedIfBlocks)) { - - Iterable bridgedIfName = filter(Splitter.on("\n").split(bridgedIfBlock), new Predicate() { - @Override - public boolean apply(String arg0) { - return arg0.startsWith("Name:"); - } - }); - for (String bridgedInterfaceName : bridgedIfName) { - for (String string : Splitter.on("Name:").split(bridgedInterfaceName)) { - if (!string.isEmpty()) - bridgedInterfaceNames.add(string.trim()); - } - } + if(!bridgedIfBlock.isEmpty()) + bridgedInterfaces.add(new BridgedIfStringToBridgedIf().apply(bridgedIfBlock)); } - return bridgedInterfaceNames; + return bridgedInterfaces; } - private class IsActiveBridgedInterface implements Predicate { + private class IsActiveBridgedInterface implements Predicate { private NetworkInterface networkInterface; @@ -119,10 +109,12 @@ public class RetrieveActiveBridgedInterfaces implements Function private final RetryIfSocketNotYetOpen socketTester; private final Supplier host; private final Supplier providerSupplier; - private final String identity; - private final String credential; +// private final String identity; +// private final String credential; private final Function, VirtualBoxManager> managerForNode; private transient VirtualBoxManager manager; @@ -67,14 +68,15 @@ public class StartVBoxIfNotAlreadyRunning implements Supplier @Inject public StartVBoxIfNotAlreadyRunning(Function, VirtualBoxManager> managerForNode, Factory runScriptOnNodeFactory, RetryIfSocketNotYetOpen socketTester, Supplier host, - @Provider Supplier providerSupplier, @Identity String identity, @Credential String credential) { + @Provider Supplier providerSupplier, @Nullable @Identity String identity, + @Nullable @Credential String credential) { this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory"); this.socketTester = checkNotNull(socketTester, "socketTester"); this.socketTester.seconds(3L); this.host = checkNotNull(host, "host"); this.providerSupplier = checkNotNull(providerSupplier, "endpoint to virtualbox websrvd is needed"); - this.identity = checkNotNull(identity, "identity"); - this.credential = checkNotNull(credential, "credential"); +// this.identity = checkNotNull(identity, "identity"); +// this.credential = checkNotNull(credential, "credential"); this.managerForNode = checkNotNull(managerForNode, "managerForNode"); } @@ -99,7 +101,7 @@ public class StartVBoxIfNotAlreadyRunning implements Supplier } } manager = managerForNode.apply(host); - manager.connect(provider.toASCIIString(), identity, credential); + manager.connect(provider.toASCIIString(), "", ""); if (logger.isDebugEnabled()) if (manager.getSessionObject().getState() != SessionState.Unlocked) logger.warn("manager is not in unlocked state " + manager.getSessionObject().getState()); diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java index b9c5b6108c..083d96ecbe 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java @@ -1,3 +1,22 @@ +/** + * 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.virtualbox.predicates; import javax.annotation.Resource; diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineController.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineController.java new file mode 100644 index 0000000000..16b58654d3 --- /dev/null +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineController.java @@ -0,0 +1,92 @@ +/** + * 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.virtualbox.util; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.virtualbox.domain.ExecutionType; +import org.jclouds.virtualbox.functions.LaunchMachineIfNotAlreadyRunning; +import org.virtualbox_4_1.IProgress; +import org.virtualbox_4_1.ISession; +import org.virtualbox_4_1.LockType; +import org.virtualbox_4_1.VirtualBoxManager; +import org.virtualbox_4_1.jaxws.MachineState; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.inject.Inject; + +/** + * Utilities to manage VirtualBox machine life cycle. + * + * @author Adrian Cole, Mattias Holmqvist, Andrea Turli + */ + +@Singleton +public class MachineController { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final Supplier manager; + private final MachineUtils machineUtils; + private final ExecutionType executionType; + + + @Inject + public MachineController(Supplier manager, MachineUtils machineUtils, ExecutionType executionType) { + this.manager = manager; + this.machineUtils = machineUtils; + this.executionType = executionType; + } + + public void ensureMachineIsLaunched(String vmName) { + machineUtils.applyForMachine(vmName, new LaunchMachineIfNotAlreadyRunning(manager.get(), executionType, "")); + } + + public void ensureMachineHasPowerDown(String vmName) { + while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.POWERED_OFF)) { + try { + machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { + @Override + public Void apply(ISession session) { + IProgress powerDownProgress = session.getConsole().powerDown(); + powerDownProgress.waitForCompletion(-1); + return null; + } + }); + } catch (RuntimeException e) { + // sometimes the machine might be powered of between the while test and the call to + // lockSessionOnMachineAndApply + if (e.getMessage().contains("Invalid machine state: PoweredOff")) { + return; + } else if (e.getMessage().contains("VirtualBox error: The object is not ready")) { + continue; + } else { + throw e; + } + } + } + } +} diff --git a/labs/virtualbox/src/main/resources/functions/cleanupUdevIfNeeded.sh b/labs/virtualbox/src/main/resources/functions/cleanupUdevIfNeeded.sh index 59f3d0b2ab..5c8d269744 100644 --- a/labs/virtualbox/src/main/resources/functions/cleanupUdevIfNeeded.sh +++ b/labs/virtualbox/src/main/resources/functions/cleanupUdevIfNeeded.sh @@ -1,9 +1,9 @@ function cleanupUdevIfNeeded { - unset OSNAME; - local OSNAME=`lsb_release -d -s | cut -d ' ' -f 1`; shift - if [ $OSNAME = 'Ubuntu' ] +# unset OSNAME; +# local OSNAME=`lsb_release -d -s | cut -d ' ' -f 1`; shift +# if [ $OSNAME = 'Ubuntu' ] + if [ -f '/etc/udev/rules.d/70-persistent-net.rules'] then - echo "OS is Ubuntu" rm /etc/udev/rules.d/70-persistent-net.rules; mkdir /etc/udev/rules.d/70-persistent-net.rules; rm -rf /dev/.udev/; diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java index 57d60b05a9..ab3ece2872 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java @@ -30,14 +30,11 @@ import javax.inject.Inject; import javax.inject.Named; import org.jclouds.Constants; -import org.jclouds.byon.Node; -import org.jclouds.byon.config.CacheNodeStoreModule; import org.jclouds.compute.BaseVersionedServiceLiveTest; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Template; import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate; import org.jclouds.concurrent.MoreExecutors; @@ -49,21 +46,16 @@ import org.jclouds.virtualbox.domain.IsoSpec; import org.jclouds.virtualbox.domain.Master; import org.jclouds.virtualbox.domain.VmSpec; import org.jclouds.virtualbox.functions.admin.UnregisterMachineIfExistsAndDeleteItsMedia; +import org.jclouds.virtualbox.util.MachineController; import org.jclouds.virtualbox.util.MachineUtils; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import org.virtualbox_4_1.IProgress; -import org.virtualbox_4_1.ISession; -import org.virtualbox_4_1.LockType; import org.virtualbox_4_1.VirtualBoxManager; -import org.virtualbox_4_1.jaxws.MachineState; -import com.google.common.base.Function; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.inject.Module; @@ -80,6 +72,9 @@ public class BaseVirtualBoxClientLiveTest extends BaseVersionedServiceLiveTest { } protected ComputeServiceContext context; + + @Inject + protected MachineController machineController; @Inject protected Supplier manager; @@ -160,30 +155,7 @@ public class BaseVirtualBoxClientLiveTest extends BaseVersionedServiceLiveTest { new UnregisterMachineIfExistsAndDeleteItsMedia(vmSpecification)); } - protected void ensureMachineHasPowerDown(String vmName) { - while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.POWERED_OFF)) { - try { - machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { - @Override - public Void apply(ISession session) { - IProgress powerDownProgress = session.getConsole().powerDown(); - powerDownProgress.waitForCompletion(-1); - return null; - } - }); - } catch (RuntimeException e) { - // sometimes the machine might be powered of between the while test and the call to - // lockSessionOnMachineAndApply - if (e.getMessage().contains("Invalid machine state: PoweredOff")) { - return; - } else if (e.getMessage().contains("VirtualBox error: The object is not ready")) { - continue; - } else { - throw e; - } - } - } - } + public String adminDisk(String vmName) { return workingDir + File.separator + vmName + ".vdi"; diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java index b4fe0f1577..c96b8e9b88 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java @@ -58,13 +58,13 @@ public class VirtualBoxExperimentLiveTest { @BeforeClass public void setUp() { - context = new ComputeServiceContextFactory().createContext("virtualbox", "toor", "password", + context = new ComputeServiceContextFactory().createContext("virtualbox", "", "", ImmutableSet. of(new SLF4JLoggingModule(), new SshjSshClientModule())); } @Test public void testLaunchCluster() throws RunNodesException { - int numNodes = 4; + int numNodes = 1; final String clusterName = "test-launch-cluster"; Set nodes = context.getComputeService().createNodesInGroup(clusterName, numNodes); assertEquals(numNodes, nodes.size(), "wrong number of nodes"); diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/BridgedIfStringToBridgedIfTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/BridgedIfStringToBridgedIfTest.java new file mode 100644 index 0000000000..7c72244942 --- /dev/null +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/BridgedIfStringToBridgedIfTest.java @@ -0,0 +1,52 @@ +package org.jclouds.virtualbox.functions; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.virtualbox.domain.BridgedIf; +import org.testng.annotations.Test; + +@Test(groups = "live", singleThreaded = true, testName = "BridgedIfStringToBridgedIfTest") +public class BridgedIfStringToBridgedIfTest { + + private static final String en0 = "Name: en0: Ethernet\n" + + "GUID: 00306e65-0000-4000-8000-3c0754205d2f\n" + + "Dhcp: Disabled\n" + + "IPAddress: 192.168.56.1\n" + + "NetworkMask: 255.255.255.0\n" + + "IPV6Address: \n" + + "IPV6NetworkMaskPrefixLength: 0\n" + + "HardwareAddress: 3c:07:54:20:5d:2f\n" + + "MediumType: Ethernet\n" + + "Status: Up\n" + + "VBoxNetworkName: HostInterfaceNetworking-en0: Ethernet\n"; + + private static final String en1 = "Name: en1: Wi-Fi (AirPort)\n" + + "GUID: 00316e65-0000-4000-8000-28cfdaf2917a\n" + + "Dhcp: Disabled\n" + + "IPAddress: 192.168.57.1\n" + + "NetworkMask: 255.255.255.0\n" + + "IPV6Address: \n" + + "IPV6NetworkMaskPrefixLength: 0\n" + + "HardwareAddress: 28:cf:da:f2:91:7a\n" + + "MediumType: Ethernet\n" + + "Status: Up\n" + + "VBoxNetworkName: HostInterfaceNetworking-en1: Wi-Fi (AirPort)\n"; + + private static final String p2p0 = "Name: p2p0\n" + + "GUID: 30703270-0000-4000-8000-0acfdaf2917a\n" + + "Dhcp: Disabled\n" + + "IPAddress: 192.168.58.1\n" + + "NetworkMask: 255.255.255.0\n" + + "IPV6Address: \n" + + "IPV6NetworkMaskPrefixLength: 0\n" + + "HardwareAddress: 0a:cf:da:f2:91:7a\n" + + "MediumType: Ethernet\n" + + "Status: Down\n" + + "VBoxNetworkName: HostInterfaceNetworking-p2p0\n"; + + @Test + public void transformRawBridgedifToBridgedIf() { + BridgedIf bridgedIfEn1 = new BridgedIfStringToBridgedIf().apply(en1); + assertEquals(bridgedIfEn1.getName(), "en1: Wi-Fi (AirPort)"); + } +} diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java index 87db6eee34..4396848ac2 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java @@ -26,8 +26,6 @@ import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTA import java.util.Map; -import javax.annotation.Nullable; - import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.OsFamily; @@ -153,21 +151,11 @@ public class CreateAndInstallVmLiveTest extends BaseVirtualBoxClientLiveTest { })); } finally { for (VmSpec spec : ImmutableSet.of(vmSpecification)) { - ensureMachineHasPowerDown(spec.getVmName()); + machineController.ensureMachineHasPowerDown(spec.getVmName()); } } } - private Function extractId() { - return new Function() { - - @Override - public String apply(@Nullable Image input) { - return input.getId(); - } - }; - } - private IMachine getVmWithGuestAdditionsInstalled() { try { Injector injector = context.utils().injector(); diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfacesLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfacesLiveTest.java index 09388f14f1..c88c71c782 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfacesLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfacesLiveTest.java @@ -25,6 +25,7 @@ import static org.testng.Assert.assertFalse; import java.util.List; import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest; +import org.jclouds.virtualbox.domain.BridgedIf; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; @@ -51,13 +52,13 @@ public class RetrieveActiveBridgedInterfacesLiveTest extends BaseVirtualBoxClien @Test public void retrieveBridgedInterfaceNamesTest() { - List activeBridgedInterfaceNames = retrieveBridgedInterfaceNames(TEST1); - assertEquals(activeBridgedInterfaceNames, expectedBridgedInterfaces); + List activeBridgedInterfaces = retrieveBridgedInterfaceNames(TEST1); + assertEquals(activeBridgedInterfaces, expectedBridgedInterfaces); } @Test public void retrieveAvailableBridgedInterfaceInfoTest() { - List bridgedInterface = context.utils().injector().getInstance(RetrieveActiveBridgedInterfaces.class) + List bridgedInterface = context.utils().injector().getInstance(RetrieveActiveBridgedInterfaces.class) .apply(host.get()); assertFalse(bridgedInterface.isEmpty()); } diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstallerLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstallerLiveTest.java index ce0d1504eb..30105e2969 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstallerLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstallerLiveTest.java @@ -40,7 +40,6 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.virtualbox_4_1.CleanupMode; import org.virtualbox_4_1.IMachine; -import org.virtualbox_4_1.IProgress; import org.virtualbox_4_1.ISession; import org.virtualbox_4_1.LockType; import org.virtualbox_4_1.NetworkAttachmentType; @@ -123,7 +122,7 @@ public class GuestAdditionsInstallerLiveTest extends })); } finally { for (VmSpec spec : ImmutableSet.of(sourceMachineSpec.getVmSpec())) { - ensureMachineHasPowerDown(spec.getVmName()); + machineController.ensureMachineHasPowerDown(spec.getVmName()); undoVm(spec); } } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java index 15e5252133..6c09e33d6c 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java @@ -168,7 +168,7 @@ public class AWSEC2TemplateBuilderLiveTest extends EC2TemplateBuilderLiveTest { Template defaultTemplate = context.getComputeService().templateBuilder().build(); assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate; - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "2011.09.2"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.rc-0"); assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.AMZN_LINUX); assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs"); @@ -183,7 +183,7 @@ public class AWSEC2TemplateBuilderLiveTest extends EC2TemplateBuilderLiveTest { Template defaultTemplate = context.getComputeService().templateBuilder().osFamily(OsFamily.AMZN_LINUX) .imageMatches(EC2ImagePredicates.rootDeviceType(RootDeviceType.INSTANCE_STORE)).build(); assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate; - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "2011.09.2"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.rc-0"); assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.AMZN_LINUX); assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "instance-store"); diff --git a/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaLasVegasTemplateBuilderLiveTest.java b/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaLasVegasTemplateBuilderLiveTest.java index 2f144c3a50..d365b692bb 100644 --- a/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaLasVegasTemplateBuilderLiveTest.java +++ b/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaLasVegasTemplateBuilderLiveTest.java @@ -53,14 +53,15 @@ public class CloudSigmaLasVegasTemplateBuilderLiveTest extends BaseTemplateBuild switch (input.family) { case UBUNTU: return (input.version.equals("11.04") && input.is64Bit) - || (input.version.equals("10.04") && !input.is64Bit) || input.version.equals(""); + || (input.version.equals("11.10") && !input.is64Bit) || input.version.equals("") + || input.version.equals("10.04"); case SOLARIS: return input.version.equals("") && input.is64Bit; case DEBIAN: return false; case CENTOS: - return (input.version.equals("") || input.version.equals("6.0")) - || (input.version.matches("5.[57]") && input.is64Bit); + return (input.version.equals("") || input.version.equals("5.7") || input.version.equals("6.0")) + && input.is64Bit; case WINDOWS: return (input.version.equals("2008 R2") || (input.version.equals("2003") || input.version.equals("")) && input.is64Bit) diff --git a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaZurichTemplateBuilderLiveTest.java b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaZurichTemplateBuilderLiveTest.java index 0e8be6fadd..a46f237689 100644 --- a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaZurichTemplateBuilderLiveTest.java +++ b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaZurichTemplateBuilderLiveTest.java @@ -38,7 +38,7 @@ import com.google.common.collect.ImmutableSet; * * @author Adrian Cole */ -@Test(groups = "live", testName = "CloudSigmaZurichTemplateBuilderLiveTest") +@Test(groups = "live", singleThreaded = true, testName = "CloudSigmaZurichTemplateBuilderLiveTest") public class CloudSigmaZurichTemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest { public CloudSigmaZurichTemplateBuilderLiveTest() { @@ -53,17 +53,15 @@ public class CloudSigmaZurichTemplateBuilderLiveTest extends BaseTemplateBuilder switch (input.family) { case UBUNTU: return input.version.equals("") || input.version.equals("10.04") - || ((input.version.equals("11.04") || input.version.equals("10.10")) && input.is64Bit); - case SOLARIS: - return input.version.equals("") && input.is64Bit; + || (input.version.equals("10.10") && input.is64Bit) + || (input.version.equals("11.04") && !input.is64Bit); case DEBIAN: - return input.version.equals("") || input.version.equals("5.0"); + return (input.version.equals("") || input.version.equals("6.0")) && input.is64Bit; case CENTOS: - return input.version.equals("") || (input.version.equals("5.7") && input.is64Bit); + return (input.version.equals("") || input.version.equals("5.7")) && input.is64Bit; case WINDOWS: - return input.version.equals("") || input.version.equals("2003") - || (input.version.equals("2008 R2") && input.is64Bit) - || (input.version.equals("2008") && !input.is64Bit); + return input.version.equals("") || input.version.matches("200[38]") + || (input.version.equals("2008 R2") && input.is64Bit); default: return false; } @@ -75,9 +73,9 @@ public class CloudSigmaZurichTemplateBuilderLiveTest extends BaseTemplateBuilder @Override public void testDefaultTemplateBuilder() throws IOException { Template defaultTemplate = context.getComputeService().templateBuilder().build(); - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "11.04"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), ""); assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); - assertEquals(defaultTemplate.getImage().getId(), "331f8cff-99c9-4fa9-9069-8f699795ef7e"); + assertEquals(defaultTemplate.getImage().getId(), "c9df6b90-420c-4c46-b7f2-8d9e99929a09"); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(defaultTemplate.getImage().getDefaultCredentials().identity, "root"); assertEquals(getCores(defaultTemplate.getHardware()), 1.0d);