Merge branch 'master' of https://github.com/jclouds/jclouds into 1.5.x

* 'master' of https://github.com/jclouds/jclouds:
  Issue 1024:Not possible to create hvm Linux nodes on Amazon
  Remove duplicated hexadecimal conversion code
  Ensure we close streams via InputSupplier methods
  Reduce filesystem and transient differences
  expect tests: removing requirement that requests are unique (so orderedRequestsSendResponses can tee up different responses for the same request when testing functions)
  Initial implementation of controlling SmartOS over SSH connection.
  Harmonize filesystem and transient putBlob
  remove stray @Test on base class as it's causing warnings due to its public non-test methods
  Issue 1011: remove PagedIterator
  Added optional timeout to EC2 listNodes strategy and made awaitCompletion update latch in finally block
  added concat to PagedIterable
This commit is contained in:
Adrian Cole 2012-07-17 23:17:46 -04:00
commit 00d2ac2df7
52 changed files with 2326 additions and 454 deletions

View File

@ -26,7 +26,6 @@ import org.jclouds.cloudwatch.features.MetricApi;
import org.jclouds.cloudwatch.options.ListMetricsOptions;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterables;
import org.jclouds.collect.PagedIterators;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
@ -47,7 +46,7 @@ public class CloudWatch {
* @return iterable of metrics fitting the criteria
*/
public static Iterable<Metric> listMetrics(final MetricApi metricApi, final ListMetricsOptions options) {
return Iterables.concat(PagedIterables.create(PagedIterators.advancing(metricApi.list(options),
return Iterables.concat(PagedIterables.advance(metricApi.list(options),
new Function<Object, IterableWithMarker<Metric>>() {
@Override
@ -59,7 +58,7 @@ public class CloudWatch {
public String toString() {
return "listMetrics(" + options + ")";
}
})));
}));
}
/**

View File

@ -38,8 +38,6 @@ import org.jclouds.http.HttpResponse;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
/**
* @author Jeremy Whitlock, Adrian Cole
*/
@ -118,7 +116,7 @@ public class MetricApiExpectTest extends BaseCloudWatchApiExpectTest {
CloudWatchApi apiWhenMetricsExist = requestsSendResponses(
listMetrics, listMetricsResponse, listMetrics2, listMetrics2Response);
assertEquals(Iterables.concat(apiWhenMetricsExist.getMetricApiForRegion(null).list()).toString(),
assertEquals(apiWhenMetricsExist.getMetricApiForRegion(null).list().concat().toString(),
"[Metric{namespace=AWS/EC2, metricName=CPUUtilization, dimension=[Dimension{name=InstanceId, value=i-689fcf0f}]}, Metric{namespace=AWS/EC2, metricName=CPUUtilization, dimension=[Dimension{name=InstanceId, value=i-689fcf0f}]}]");
}

View File

@ -133,10 +133,9 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen
public Map<?, Future<Void>> execute(String group, int count, Template template, Set<NodeMetadata> goodNodes,
Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
Template mutableTemplate = templateBuilderProvider.get().imageId(template.getImage().getId()).fromTemplate(template)
.build();
Template mutableTemplate = template.clone();
Iterable<String> ips = allocateElasticIpsInRegion(count, template);
Iterable<String> ips = allocateElasticIpsInRegion(count, mutableTemplate);
Iterable<? extends RunningInstance> started = createKeyPairAndSecurityGroupsAsNeededThenRunInstances(group,
count, mutableTemplate);

View File

@ -31,7 +31,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
@ -51,6 +50,7 @@ import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
/**
*
@ -58,10 +58,15 @@ import com.google.common.collect.ImmutableSet;
*/
@Singleton
public class EC2ListNodesStrategy implements ListNodesStrategy {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
@Inject(optional = true)
@Named(Constants.PROPERTY_REQUEST_TIMEOUT)
protected static Long maxTime;
protected final EC2AsyncClient client;
protected final Supplier<Set<String>> regions;
protected final Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata;
@ -100,7 +105,7 @@ public class EC2ListNodesStrategy implements ListNodesStrategy {
return castToSpecificTypedFuture(client.getInstanceServices().describeInstancesInRegion(from));
}
}, executor, null, logger, "reservations");
}, executor, maxTime, logger, "reservations");
return concat(concat(reservations));
}

View File

@ -95,9 +95,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest {
strategy.autoAllocateElasticIps = true;
// setup expectations
expect(templateBuilder.imageId(region + "/" + imageId)).andReturn(templateBuilder);
expect(templateBuilder.fromTemplate(input.template)).andReturn(templateBuilder);
expect(templateBuilder.build()).andReturn(input.template);
expect(input.template.clone()).andReturn(input.template);
expect(strategy.client.getInstanceServices()).andReturn(instanceClient).atLeastOnce();
expect(
strategy.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize
@ -106,7 +104,6 @@ public class EC2CreateNodesInGroupThenAddToSetTest {
expect(input.template.getLocation()).andReturn(input.location).atLeastOnce();
expect(input.template.getImage()).andReturn(input.image).atLeastOnce();
expect(input.image.getId()).andReturn(region + "/" + imageId).atLeastOnce();
expect(input.image.getProviderId()).andReturn(imageId).atLeastOnce();
// differences when ip allocation
@ -204,16 +201,13 @@ public class EC2CreateNodesInGroupThenAddToSetTest {
"reservationId");
// setup expectations
expect(templateBuilder.imageId(region + "/" + imageId)).andReturn(templateBuilder);
expect(templateBuilder.fromTemplate(input.template)).andReturn(templateBuilder);
expect(templateBuilder.build()).andReturn(input.template);
expect(input.template.clone()).andReturn(input.template);
expect(strategy.client.getInstanceServices()).andReturn(instanceClient).atLeastOnce();
expect(
strategy.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize
.execute(region, input.tag, input.template)).andReturn(ec2Options);
expect(input.template.getLocation()).andReturn(input.location).atLeastOnce();
expect(input.template.getImage()).andReturn(input.image).atLeastOnce();
expect(input.image.getId()).andReturn(region + "/" + imageId).atLeastOnce();
expect(input.image.getProviderId()).andReturn(imageId).atLeastOnce();
expect(instanceClient.runInstancesInRegion(region, zone, imageId, 1, input.count, ec2Options)).andReturn(
Reservation.class.cast(reservation));

View File

@ -402,8 +402,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
}
public static HttpResponseException returnResponseException(int code) {
HttpResponse response = null;
response = HttpResponse.builder().statusCode(code).build();
HttpResponse response = HttpResponse.builder().statusCode(code).build();
return new HttpResponseException(new HttpCommand() {
public int getRedirectCount() {
@ -457,17 +456,22 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
String blobKey = blob.getMetadata().getName();
logger.debug("Put blob with key [%s] to container [%s]", blobKey, containerName);
String eTag = getEtag(blob);
if (!storageStrategy.containerExists(containerName)) {
return Futures.immediateFailedFuture(new IllegalStateException("containerName not found: " + containerName));
}
try {
// TODO
// must override existing file?
storageStrategy.writePayloadOnFile(containerName, blobKey, blob.getPayload());
storageStrategy.putBlob(containerName, blob);
} catch (IOException e) {
logger.error(e, "An error occurred storing the new blob with name [%s] to container [%s].", blobKey,
containerName);
Throwables.propagate(e);
}
String eTag = getEtag(blob);
return immediateFuture(eTag);
}
@ -535,7 +539,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
if (options.getRanges() != null && options.getRanges().size() > 0) {
byte[] data;
try {
data = toByteArray(blob.getPayload().getInput());
data = toByteArray(blob.getPayload());
} catch (IOException e) {
return immediateFailedFuture(new RuntimeException(e));
}
@ -591,13 +595,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
}
}
private Blob copyBlob(Blob blob) {
Blob returnVal = blobFactory.create(copy(blob.getMetadata()));
returnVal.setPayload(blob.getPayload());
copyPayloadHeadersToBlob(blob.getPayload(), returnVal);
return returnVal;
}
/**
* Calculates the object MD5 and returns it as eTag
*
@ -616,6 +613,13 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
return eTag;
}
private Blob copyBlob(Blob blob) {
Blob returnVal = blobFactory.create(copy(blob.getMetadata()));
returnVal.setPayload(blob.getPayload());
copyPayloadHeadersToBlob(blob.getPayload(), returnVal);
return returnVal;
}
@Override
protected boolean deleteAndVerifyContainerGone(final String container) {
storageStrategy.deleteContainer(container);

View File

@ -169,11 +169,11 @@ public interface FilesystemStorageStrategy {
void removeBlob(String container, String key);
/**
* Write a {@link Blob} {@link Payload} into a file
* @param fileName
* @param payload
* Write a {@link Blob} into a file
* @param container
* @param blob
* @throws IOException
*/
void writePayloadOnFile(String container, String blobKey, Payload payload) throws IOException;
void putBlob(String containerName, Blob blob) throws IOException;
}

View File

@ -204,19 +204,13 @@ public class FilesystemStorageStrategyImpl implements FilesystemStorageStrategy
return blobFile;
}
/**
* Write a {@link Blob} {@link Payload} into a file
*
* @param container
* @param blobKey
* @param payload
* @throws IOException
*/
@Override
public void writePayloadOnFile(String container, String blobKey, Payload payload) throws IOException {
filesystemContainerNameValidator.validate(container);
public void putBlob(final String containerName, final Blob blob) throws IOException {
String blobKey = blob.getMetadata().getName();
Payload payload = blob.getPayload();
filesystemContainerNameValidator.validate(containerName);
filesystemBlobKeyValidator.validate(blobKey);
File outputFile = getFileForBlobKey(container, blobKey);
File outputFile = getFileForBlobKey(containerName, blobKey);
FileOutputStream output = null;
try {
Files.createParentDirs(outputFile);

View File

@ -740,6 +740,7 @@ public class FilesystemAsyncBlobStoreTest {
* can't be deleted. See http://code.google.com/p/jclouds/issues/detail?id=737
*/
final String containerName = "containerWithRanges";
blobStore.createContainerInLocation(null, containerName);
String payload = "abcdefgh";
InputStream is;
Blob blob = blobStore.blobBuilder("test").payload(new StringPayload(payload)).build();

View File

@ -371,8 +371,10 @@ public class FilesystemStorageStrategyImplTest {
blobKey = TestUtils.createRandomBlobKey("writePayload-", ".img");
sourceFile = TestUtils.getImageForBlobPayload();
filePayload = new FilePayload(sourceFile);
Blob blob = storageStrategy.newBlob(blobKey);
blob.setPayload(filePayload);
// write files
storageStrategy.writePayloadOnFile(CONTAINER_NAME, blobKey, filePayload);
storageStrategy.putBlob(CONTAINER_NAME, blob);
// verify that the files is equal
File blobFullPath = new File(TARGET_CONTAINER_NAME, blobKey);
InputSupplier<FileInputStream> expectedInput =

View File

@ -115,33 +115,33 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
protected final DateService dateService;
protected final Crypto crypto;
protected final Provider<UriBuilder> uriBuilders;
protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter;
protected final ContentMetadataCodec contentMetadataCodec;
protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName;
protected final Factory blobFactory;
protected final TransientStorageStrategy storageStrategy;
protected final ContentMetadataCodec contentMetadataCodec;
protected final Provider<UriBuilder> uriBuilders;
@Inject
protected TransientAsyncBlobStore(BlobStoreContext context,
DateService dateService, Crypto crypto,
HttpGetOptionsListToGetOptions httpGetOptionsConverter,
ContentMetadataCodec contentMetadataCodec,
IfDirectoryReturnNameStrategy ifDirectoryReturnName,
BlobUtils blobUtils,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service,
Supplier<Location> defaultLocation,
@Memoized Supplier<Set<? extends Location>> locations,
Factory blobFactory, Provider<UriBuilder> uriBuilders,
ContentMetadataCodec contentMetadataCodec) {
Factory blobFactory, Provider<UriBuilder> uriBuilders) {
super(context, blobUtils, service, defaultLocation, locations);
this.blobFactory = blobFactory;
this.dateService = dateService;
this.crypto = crypto;
this.uriBuilders = uriBuilders;
this.httpGetOptionsConverter = httpGetOptionsConverter;
this.contentMetadataCodec = contentMetadataCodec;
this.ifDirectoryReturnName = ifDirectoryReturnName;
this.storageStrategy = new TransientStorageStrategy(defaultLocation);
this.contentMetadataCodec = contentMetadataCodec;
this.uriBuilders = uriBuilders;
}
/**
@ -472,7 +472,8 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
storageStrategy.putBlob(containerName, blob);
return immediateFuture(Iterables.getOnlyElement(blob.getAllHeaders().get(HttpHeaders.ETAG)));
String eTag = getEtag(blob);
return immediateFuture(eTag);
}
private Blob createUpdatedCopyOfBlobInContainer(String containerName, Blob in) {
@ -579,7 +580,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
if (options.getRanges() != null && options.getRanges().size() > 0) {
byte[] data;
try {
data = toByteArray(blob.getPayload().getInput());
data = toByteArray(blob.getPayload());
} catch (IOException e) {
return immediateFailedFuture(new RuntimeException(e));
}
@ -635,6 +636,24 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
}
}
/**
* Calculates the object MD5 and returns it as eTag
*
* @param object
* @return
*/
private String getEtag(Blob object) {
try {
Payloads.calculateMD5(object, crypto.md5());
} catch (IOException ex) {
logger.error(ex, "An error occurred calculating MD5 for object with name %s.", object.getMetadata().getName());
Throwables.propagate(ex);
}
String eTag = CryptoStreams.hex(object.getPayload().getContentMetadata().getContentMD5());
return eTag;
}
private Blob copyBlob(Blob blob) {
Blob returnVal = blobFactory.create(copy(blob.getMetadata()));
returnVal.setPayload(blob.getPayload());

View File

@ -18,17 +18,39 @@
*/
package org.jclouds.collect;
import java.util.Iterator;
import com.google.common.annotations.Beta;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterators;
import com.google.common.collect.UnmodifiableIterator;
/**
* Extends {@link FluentIterable} allowing you to lazily advance through
* sequence of pages in a resultset. Typically used in apis that return only a
* certain number of records at a time.
*
* Simplest usage is to employ the {@link #concat} convenience function, and one
* of the methods from {@link FluentIterable}.
*
* <pre>
* // pull in new pages until it we see something interesting.
* Optional<StorageMetadata> firstInterestingBlob = blobstore
* .list(// options //)
* .concat()
* .firstMatch(isInterestingBlob());
* </pre>
*
* For those seeking manual control of page advances, don't use concat, and
* instead look at the value of {@link IterableWithMarker#nextToken}.
*
* <pre>
* PagedIterator<StorageMetadata> blobs = blobstore.list(...).iterator();
* while (blobs.hasNext()) {
* FluentIterable<StorageMetadata> page = blobs.next();
* IterableWithMarker<StorageMetadata> page = blobs.next();
* ProcessedResults results = process(page);
* if (results.shouldBeBookmarked() && blobs.nextMarker().isPresent()) {
* saveBookmark(blobs.nextMarker().get());
* if (results.shouldBeBookmarked() && page.nextMarker().isPresent()) {
* saveBookmark(page.nextMarker().get());
* }
* }
* </pre>
@ -36,9 +58,39 @@ import com.google.common.collect.FluentIterable;
* @author Adrian Cole
*/
@Beta
public abstract class PagedIterable<T> extends FluentIterable<IterableWithMarker<T>> {
public abstract class PagedIterable<E> extends FluentIterable<IterableWithMarker<E>> {
@Override
public abstract PagedIterator<T> iterator();
/**
* Combines all the pages into a single unmodifiable iterable. ex.
*
* <pre>
* FluentIterable<StorageMetadata> blobs = blobstore.list(...).concat();
* for (StorageMetadata blob : blobs) {
* process(blob);
* }
* </pre>
*
* @see Iterators#concat
*/
public FluentIterable<E> concat() {
final Iterator<IterableWithMarker<E>> iterator = iterator();
final UnmodifiableIterator<Iterator<E>> unmodifiable = new UnmodifiableIterator<Iterator<E>>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Iterator<E> next() {
return iterator.next().iterator();
}
};
return new FluentIterable<E>() {
@Override
public Iterator<E> iterator() {
return Iterators.concat(unmodifiable);
}
};
}
}

View File

@ -18,7 +18,15 @@
*/
package org.jclouds.collect;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Iterator;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableSet;
/**
* Utilities for using {@link PagedIterable}s.
@ -27,23 +35,120 @@ import com.google.common.annotations.Beta;
*/
@Beta
public class PagedIterables {
/**
* @param only
* the only page of data
*
* @param iterator
* how to advance pages
*
* @return iterable current data which continues if the user iterates beyond the first page
* @return iterable with only the one page
*/
public static <T> PagedIterable<T> create(final PagedIterator<T> iterator) {
public static <T> PagedIterable<T> of(final IterableWithMarker<T> only) {
return new PagedIterable<T>() {
@Override
public PagedIterator<T> iterator() {
return iterator;
public Iterator<IterableWithMarker<T>> iterator() {
return ImmutableSet.of(only).iterator();
}
};
}
/**
*
*
* @param initial
* the initial set current data
* @param markerToNext
* produces the next set based on the marker
*
* @return iterable current data which continues if the user iterates beyond
* the first page
*/
public static <T> PagedIterable<T> advance(final IterableWithMarker<T> initial,
final Function<Object, IterableWithMarker<T>> markerToNext) {
return new PagedIterable<T>() {
@Override
public Iterator<IterableWithMarker<T>> iterator() {
return advancingIterator(initial, markerToNext);
}
};
}
private static class AdvancingIterator<T> extends AbstractIterator<IterableWithMarker<T>> {
private final Function<Object, IterableWithMarker<T>> markerToNext;
private transient IterableWithMarker<T> current;
private transient boolean unread = true;
private AdvancingIterator(IterableWithMarker<T> initial, Function<Object, IterableWithMarker<T>> markerToNext) {
this.current = checkNotNull(initial, "initial iterable");
this.markerToNext = checkNotNull(markerToNext, "marker to next iterable");
}
/**
* {@inheritDoc}
*/
@Override
protected IterableWithMarker<T> computeNext() {
if (unread)
try {
return current;
} finally {
unread = false;
}
else if (current.nextMarker().isPresent())
return current = markerToNext.apply(current.nextMarker().get());
else
return endOfData();
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hashCode(current, unread);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
AdvancingIterator<?> other = AdvancingIterator.class.cast(obj);
return Objects.equal(this.current, other.current) && Objects.equal(this.unread, other.unread);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return Objects.toStringHelper("").add("current", current).add("unread", unread).toString();
}
}
/**
*
* @param initial
* the initial set current data
* @param markerToNext
* produces the next set based on the marker
*
* @return iterable current data which continues if the user iterates beyond
* the first page
*/
public static <T> Iterator<IterableWithMarker<T>> advancingIterator(IterableWithMarker<T> initial,
Function<Object, IterableWithMarker<T>> markerToNext) {
if (!initial.nextMarker().isPresent()) {
return ImmutableSet.of(initial).iterator();
}
return new AdvancingIterator<T>(initial, markerToNext);
}
}

View File

@ -1,40 +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.collect;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
import com.google.common.collect.AbstractIterator;
/**
*
* @author Adrian Cole
*/
@Beta
public abstract class PagedIterator<T> extends AbstractIterator<IterableWithMarker<T>> {
/**
* If there is a next marker, then the set is incomplete and you should issue another command to
* retrieve the rest, setting the option {@code marker} to this value
*
* @return next marker, or absent if list is complete
*/
public abstract Optional<Object> nextMarker();
}

View File

@ -1,184 +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 current 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.collect;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
/**
* Utilities for using {@link PagedIterator}s.
*
* @author Adrian Cole
*/
@Beta
public class PagedIterators {
private static class AdvancingPagedIterator<T> extends PagedIterator<T> {
private final Function<Object, IterableWithMarker<T>> markerToNext;
private transient IterableWithMarker<T> current;
private transient boolean unread = true;
private AdvancingPagedIterator(IterableWithMarker<T> initial, Function<Object, IterableWithMarker<T>> markerToNext) {
this.current = checkNotNull(initial, "initial iterable");
this.markerToNext = checkNotNull(markerToNext, "marker to next iterable");
}
/**
* {@inheritDoc}
*/
@Override
protected IterableWithMarker<T> computeNext() {
if (unread)
try {
return current;
} finally {
unread = false;
}
else if (nextMarker().isPresent())
return current = markerToNext.apply(nextMarker().get());
else
return endOfData();
}
@Override
public Optional<Object> nextMarker() {
return current.nextMarker();
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hashCode(current, unread);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
AdvancingPagedIterator<?> other = AdvancingPagedIterator.class.cast(obj);
return Objects.equal(this.current, other.current) && Objects.equal(this.unread, other.unread);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return Objects.toStringHelper("").add("current", current).add("unread", unread).toString();
}
}
/**
*
* @param initial
* the initial set current data
* @param markerToNext
* produces the next set based on the marker
*
* @return iterable current data which continues if the user iterates beyond the first page
*/
public static <T> PagedIterator<T> advancing(IterableWithMarker<T> initial,
Function<Object, IterableWithMarker<T>> markerToNext) {
if (!initial.nextMarker().isPresent()) {
return of(initial);
}
return new AdvancingPagedIterator<T>(initial, markerToNext);
}
/**
*
* @param initial
* the initial set current data
* @return iterable current data which only contains the single element
*/
public static <T> PagedIterator<T> of(IterableWithMarker<T> initial) {
return new OnlyElementIterator<T>(initial);
}
private static class OnlyElementIterator<T> extends PagedIterator<T> {
private transient IterableWithMarker<T> onlyElement;
private transient boolean unread = true;
private OnlyElementIterator(IterableWithMarker<T> onlyElement) {
this.onlyElement = checkNotNull(onlyElement, "onlyElement");
}
/**
* {@inheritDoc}
*/
@Override
protected IterableWithMarker<T> computeNext() {
if (unread)
try {
return onlyElement;
} finally {
unread = false;
}
else
return endOfData();
}
@Override
public Optional<Object> nextMarker() {
return onlyElement.nextMarker();
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hashCode(onlyElement);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
OnlyElementIterator<?> other = OnlyElementIterator.class.cast(obj);
return Objects.equal(this.onlyElement, other.onlyElement) && Objects.equal(this.unread, other.unread);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return Objects.toStringHelper("").add("onlyElement", onlyElement).add("unread", unread).toString();
}
}
}

View File

@ -40,7 +40,6 @@ package org.jclouds.collect.internal;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.collect.PagedIterables;
import org.jclouds.collect.PagedIterators;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
@ -61,7 +60,7 @@ public abstract class CallerArg0ToPagedIterable<T, I extends CallerArg0ToPagedIt
@Override
public PagedIterable<T> apply(IterableWithMarker<T> input) {
if (input.nextMarker() == null)
return PagedIterables.create(PagedIterators.of(input));
return PagedIterables.of(input);
Optional<String> arg0Option = Optional.absent();
if (request.getCaller().get().getArgs() != null && request.getCaller().get().getArgs().length > 0) {
@ -70,7 +69,7 @@ public abstract class CallerArg0ToPagedIterable<T, I extends CallerArg0ToPagedIt
arg0Option = Optional.of(arg0.toString());
}
final String arg0 = arg0Option.orNull();
return PagedIterables.create(PagedIterators.advancing(input, markerToNextForCallingArg0(arg0)));
return PagedIterables.advance(input, markerToNextForCallingArg0(arg0));
}
protected abstract Function<Object, IterableWithMarker<T>> markerToNextForCallingArg0(String arg0);

View File

@ -137,8 +137,9 @@ public class FutureIterables {
errors.incrementAndGet();
logException(logger, logPrefix, total, complete.get(), errors.get(), start, e);
errorMap.put(future.getKey(), e);
} finally {
doneSignal.countDown();
}
doneSignal.countDown();
}
@Override
@ -148,10 +149,11 @@ public class FutureIterables {
}, exec);
}
try {
if (maxTime != null)
if (maxTime != null) {
doneSignal.await(maxTime, TimeUnit.MILLISECONDS);
else
} else {
doneSignal.await();
}
if (errors.get() > 0) {
String message = message(logPrefix, total, complete.get(), errors.get(), start);
RuntimeException exception = new RuntimeException(message);

View File

@ -49,20 +49,21 @@ import com.google.common.io.InputSupplier;
@Beta
public class CryptoStreams {
public static String hex(byte[] in) {
byte[] hex = new byte[2 * in.length];
private static char[] hex(byte[] in, int offset, int len) {
char[] hex = new char[2 * len];
int index = 0;
for (byte b : in) {
for (int i = offset; i < offset + len; ++i) {
byte b = in[i];
int v = b & 0xFF;
hex[index++] = HEX_CHAR_TABLE[v >>> 4];
hex[index++] = HEX_CHAR_TABLE[v & 0xF];
}
try {
return new String(hex, "ASCII");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return hex;
}
public static String hex(byte[] in) {
return new String(hex(in, 0, in.length));
}
public static byte[] hex(String s) {
@ -295,9 +296,10 @@ public class CryptoStreams {
});
}
final static byte[] HEX_CHAR_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
(byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
(byte) 'f' };
private final static char[] HEX_CHAR_TABLE = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
};
/**
* Computes and returns the hex value for a supplied input stream.
@ -313,16 +315,7 @@ public class CryptoStreams {
final StringBuilder out = new StringBuilder();
return com.google.common.io.ByteStreams.readBytes(supplier, new ByteProcessor<String>() {
public boolean processBytes(byte[] buf, int off, int len) {
char[] hex = new char[2 * len];
int index = 0;
for (int i = off; i < off + len; i++) {
byte b = buf[i];
int v = b & 0xFF;
hex[index++] = (char) HEX_CHAR_TABLE[v >>> 4];
hex[index++] = (char) HEX_CHAR_TABLE[v & 0xF];
}
out.append(hex);
out.append(hex(buf, off, len));
return true;
}

View File

@ -9,7 +9,6 @@ import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* Tests behavior of {@code IterableWithMarkers}.
@ -28,7 +27,7 @@ public class PagedIterablesTest {
EasyMock.replay(markerToNext);
PagedIterable<String> iterable = PagedIterables.create(PagedIterators.advancing(initial, markerToNext));
PagedIterable<String> iterable = PagedIterables.advance(initial, markerToNext);
Assert.assertSame(iterable.get(0), initial);
@ -49,9 +48,9 @@ public class PagedIterablesTest {
EasyMock.replay(markerToNext);
PagedIterable<String> iterable = PagedIterables.create(PagedIterators.advancing(initial, markerToNext));
PagedIterable<String> iterable = PagedIterables.advance(initial, markerToNext);
Assert.assertEquals(ImmutableSet.copyOf(Iterables.concat(iterable)),
Assert.assertEquals(iterable.concat().toImmutableSet(),
ImmutableSet.of("foo", "bar", "boo", "baz", "ham", "cheeze"));
EasyMock.verify(markerToNext);

View File

@ -1,105 +0,0 @@
package org.jclouds.collect;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import org.easymock.EasyMock;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* Tests behavior of {@code IterableWithMarkers}.
*
* @author Adrian Cole
*/
@Test(testName = "PagedIteratorsTest")
public class PagedIteratorsTest {
@SuppressWarnings("unchecked")
@Test
public void testSinglePageResultReturnsSame() {
IterableWithMarker<String> initial = IterableWithMarkers.from(ImmutableSet.of("foo", "bar"));
Function<Object, IterableWithMarker<String>> markerToNext = createMock(Function.class);
EasyMock.replay(markerToNext);
Assert.assertSame(PagedIterators.advancing(initial, markerToNext).next(), initial);
EasyMock.verify(markerToNext);
}
@SuppressWarnings("unchecked")
@Test
public void testMultiPage2Pages() {
IterableWithMarker<String> initial = IterableWithMarkers.from(ImmutableSet.of("foo", "bar"), "MARKER1");
Function<Object, IterableWithMarker<String>> markerToNext = createMock(Function.class);
expect(markerToNext.apply("MARKER1")).andReturn(IterableWithMarkers.from(ImmutableSet.of("boo", "baz"), null));
EasyMock.replay(markerToNext);
Assert.assertEquals(ImmutableSet.copyOf(Iterables.concat(ImmutableSet.copyOf(PagedIterators.advancing(initial,
markerToNext)))), ImmutableSet.of("foo", "bar", "boo", "baz"));
EasyMock.verify(markerToNext);
}
@SuppressWarnings("unchecked")
@Test
public void testMultiPage3Pages() {
IterableWithMarker<String> initial = IterableWithMarkers.from(ImmutableSet.of("foo", "bar"), "MARKER1");
Function<Object, IterableWithMarker<String>> markerToNext = createMock(Function.class);
expect(markerToNext.apply("MARKER1")).andReturn(
IterableWithMarkers.from(ImmutableSet.of("boo", "baz"), "MARKER2"));
expect(markerToNext.apply("MARKER2")).andReturn(IterableWithMarkers.from(ImmutableSet.of("ham", "cheeze"), null));
EasyMock.replay(markerToNext);
Assert.assertEquals(ImmutableSet.copyOf(Iterables.concat(ImmutableSet.copyOf(PagedIterators.advancing(initial,
markerToNext)))), ImmutableSet.of("foo", "bar", "boo", "baz", "ham", "cheeze"));
EasyMock.verify(markerToNext);
}
@SuppressWarnings("unchecked")
@Test
public void testMultiPage3PagesNextMarkerSetCorrectly() {
IterableWithMarker<String> initial = IterableWithMarkers.from(ImmutableSet.of("foo", "bar"), "MARKER1");
Function<Object, IterableWithMarker<String>> markerToNext = createMock(Function.class);
IterableWithMarker<String> second = IterableWithMarkers.from(ImmutableSet.of("boo", "baz"), "MARKER2");
expect(markerToNext.apply("MARKER1")).andReturn(second);
IterableWithMarker<String> third = IterableWithMarkers.from(ImmutableSet.of("ham", "cheeze"), null);
expect(markerToNext.apply("MARKER2")).andReturn(third);
EasyMock.replay(markerToNext);
PagedIterator<String> iterator = PagedIterators.advancing(initial, markerToNext);
Assert.assertEquals(iterator.hasNext(), true);
Assert.assertEquals(iterator.nextMarker(), Optional.of("MARKER1"));
Assert.assertEquals(iterator.next(), initial);
Assert.assertEquals(iterator.hasNext(), true);
Assert.assertEquals(iterator.nextMarker(), Optional.of("MARKER2"));
Assert.assertEquals(iterator.next(), second);
Assert.assertEquals(iterator.hasNext(), true);
Assert.assertEquals(iterator.nextMarker(), Optional.absent());
Assert.assertEquals(iterator.next(), third);
Assert.assertEquals(iterator.hasNext(), false);
Assert.assertEquals(iterator.nextMarker(), Optional.absent());
EasyMock.verify(markerToNext);
}
}

View File

@ -80,7 +80,6 @@ import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@ -118,7 +117,6 @@ import com.google.inject.name.Names;
*
* @author Adrian Cole
*/
@Test(groups = "unit")
@Beta
public abstract class BaseRestApiExpectTest<S> {
@ -441,13 +439,14 @@ public abstract class BaseRestApiExpectTest<S> {
public S requestsSendResponses(final Map<HttpRequest, HttpResponse> requestToResponse, Module module,
Properties props) {
return createClient(new Function<HttpRequest, HttpResponse>() {
ImmutableBiMap<HttpRequest, HttpResponse> bimap = ImmutableBiMap.copyOf(requestToResponse);
@Override
public HttpResponse apply(HttpRequest input) {
HttpRequest matchedRequest = null;
HttpResponse response = null;
for (HttpRequest request : requestToResponse.keySet()) {
if (httpRequestsAreEqual(input, request)) {
matchedRequest = request;
response = requestToResponse.get(request);
}
}
@ -468,7 +467,7 @@ public abstract class BaseRestApiExpectTest<S> {
} else if (compareHttpRequestAsType(input) == HttpRequestComparisonType.DEFAULT) {
// in case hashCode/equals doesn't do a full content check
assertEquals(renderRequest(input), renderRequest(bimap.inverse().get(response)));
assertEquals(renderRequest(input), renderRequest(matchedRequest));
}
return response;

View File

@ -46,7 +46,6 @@ import org.jclouds.logging.Logger;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
/**
@ -88,14 +87,13 @@ public class ELBListLoadBalancersStrategy implements ListLoadBalancersStrategy {
@Override
public Iterable<LoadBalancerInRegion> apply(PagedIterable<LoadBalancer> input) {
return Iterables.transform(Iterables.concat(input),
new Function<LoadBalancer, LoadBalancerInRegion>() {
@Override
public LoadBalancerInRegion apply(LoadBalancer lb) {
return new LoadBalancerInRegion(lb, from);
}
});
return input.concat()
.transform(new Function<LoadBalancer, LoadBalancerInRegion>() {
@Override
public LoadBalancerInRegion apply(LoadBalancer lb) {
return new LoadBalancerInRegion(lb, from);
}
});
}
}, executor);

View File

@ -145,7 +145,7 @@ public class LoadBalancerApiExpectTest extends BaseELBApiExpectTest {
LoadBalancer lb1 = new GetLoadBalancerResponseTest().expected().toBuilder().name("my-load-balancer-1").build();
LoadBalancer lb2 = new GetLoadBalancerResponseTest().expected();
assertEquals(ImmutableSet.copyOf(Iterables.concat(apiWhenExist.getLoadBalancerApi().list())), ImmutableSet.of(lb1, lb2));
assertEquals(apiWhenExist.getLoadBalancerApi().list().concat().toImmutableSet(), ImmutableSet.of(lb1, lb2));
}
public void testList2PagesWhenResponseIs2xxInEU() throws Exception {

View File

@ -20,7 +20,6 @@ package org.jclouds.elb.loadbalancer;
import static org.testng.Assert.assertEquals;
import org.jclouds.collect.PagedIterable;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.elb.ELBApi;
import org.jclouds.elb.ELBAsyncApi;
@ -30,9 +29,9 @@ import org.jclouds.rest.RestContext;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.collect.Iterables;
/**
*
@ -61,8 +60,8 @@ public class ELBLoadBalancerServiceLiveTest extends BaseLoadBalancerServiceLiveT
instanceIds.add(node.getProviderId());
}
PagedIterable<LoadBalancer> elbs = elbApi.getLoadBalancerApi().list();
for (LoadBalancer elb : Iterables.concat(elbs)) {
FluentIterable<LoadBalancer> elbs = elbApi.getLoadBalancerApi().list().concat();
for (LoadBalancer elb : elbs) {
if (elb.getName().equals(group))
assertEquals(elb.getInstanceIds(), instanceIds.build());
}

View File

@ -149,7 +149,7 @@ public class UserApiExpectTest extends BaseIAMApiExpectTest {
IAMApi apiWhenExist = requestsSendResponses(list, listResponse, list2, list2Response);
assertEquals(ImmutableList.copyOf(Iterables.concat(apiWhenExist.getUserApi().list())),
assertEquals(apiWhenExist.getUserApi().list().concat().toImmutableList(),
ImmutableList.copyOf(Iterables.concat(new ListUsersResponseTest().expected(), new ListUsersResponseTest().expected())));
}

View File

@ -55,5 +55,6 @@
<module>nodepool</module>
<module>rds</module>
<module>aws-rds</module>
<module>smartos-ssh</module>
</modules>
</project>

View File

@ -140,7 +140,7 @@ public class InstanceApiExpectTest extends BaseRDSApiExpectTest {
RDSApi apiWhenExist = requestsSendResponses(
list, listResponse, list2, list2Response);
assertEquals(ImmutableList.copyOf(Iterables.concat(apiWhenExist.getInstanceApi().list())),
assertEquals(apiWhenExist.getInstanceApi().list().concat().toImmutableList(),
ImmutableList.copyOf(Iterables.concat(new DescribeDBInstancesResponseTest().expected(),
new DescribeDBInstancesResponseTest().expected())));
}

View File

@ -140,7 +140,7 @@ public class SecurityGroupApiExpectTest extends BaseRDSApiExpectTest {
RDSApi apiWhenExist = requestsSendResponses(
list, listResponse, list2, list2Response);
assertEquals(ImmutableList.copyOf(Iterables.concat(apiWhenExist.getSecurityGroupApi().list())),
assertEquals(apiWhenExist.getSecurityGroupApi().list().concat().toImmutableList(),
ImmutableList.copyOf(Iterables.concat(new DescribeDBSecurityGroupsResponseTest().expected(),
new DescribeDBSecurityGroupsResponseTest().expected())));
}

View File

@ -141,7 +141,7 @@ public class SubnetGroupApiExpectTest extends BaseRDSApiExpectTest {
RDSApi apiWhenExist = requestsSendResponses(
list, listResponse, list2, list2Response);
assertEquals(ImmutableList.copyOf(Iterables.concat(apiWhenExist.getSubnetGroupApi().list())),
assertEquals(apiWhenExist.getSubnetGroupApi().list().concat().toImmutableList(),
ImmutableList.copyOf(Iterables.concat(new DescribeDBSubnetGroupsResponseTest().expected(),
new DescribeDBSubnetGroupsResponseTest().expected())));
}

View File

@ -0,0 +1,20 @@
This is designed for interacting with a Joyent SmartOS host, in order to be able to leverage
the lightweight VM support (nee Solaris Zones).
It is planned to support KVM VMs in the future.
--------------
#Setup
Have a SmartOS installation, that you know either the root password for, or a secondary account with sufficient
permissions to be able to run the vm tools (vmadm, dsadm).
That's it!
--------------
#Notes:
- This is a work in progress, so please report any bugs that you find.

121
labs/smartos-ssh/pom.xml Normal file
View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-project</artifactId>
<version>1.5.0-SNAPSHOT</version>
<relativePath>../../project/pom.xml</relativePath>
</parent>
<groupId>org.jclouds.labs</groupId>
<artifactId>smartos-ssh</artifactId>
<name>smartos ssh api</name>
<description>jclouds components to access SmartOS over SSH</description>
<packaging>bundle</packaging>
<properties>
<test.smartos-ssh.endpoint>https://api.joyentcloud.com</test.smartos-ssh.endpoint>
<test.smartos-ssh.api-version>~6.5</test.smartos-ssh.api-version>
<test.smartos-ssh.build-version></test.smartos-ssh.build-version>
<test.smartos-ssh.identity>FIXME_IDENTITY</test.smartos-ssh.identity>
<test.smartos-ssh.credential>FIXME_CREDENTIALS</test.smartos-ssh.credential>
<jclouds.osgi.export>org.jclouds.joyent.cloudapi.v6_5*;version="${project.version}"</jclouds.osgi.export>
<jclouds.osgi.import>
org.jclouds.rest.internal;version="${project.version}",
org.jclouds*;version="${project.version}",
*
</jclouds.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-compute</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-compute</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds.driver</groupId>
<artifactId>jclouds-slf4j</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds.driver</groupId>
<artifactId>jclouds-sshj</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>live</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<systemPropertyVariables>
<test.smartos-ssh.endpoint>${test.smartos-ssh.endpoint}</test.smartos-ssh.endpoint>
<test.smartos-ssh.api-version>${test.smartos-ssh.api-version}</test.smartos-ssh.api-version>
<test.smartos-ssh.build-version>${test.smartos-ssh.build-version}</test.smartos-ssh.build-version>
<test.smartos-ssh.identity>${test.smartos-ssh.identity}</test.smartos-ssh.identity>
<test.smartos-ssh.credential>${test.smartos-ssh.credential}</test.smartos-ssh.credential>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,57 @@
package org.jclouds.smartos;
import java.net.URI;
import org.jclouds.apis.ApiMetadata;
import org.jclouds.apis.internal.BaseApiMetadata;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.smartos.compute.config.SmartOSComputeServiceContextModule;
/**
* Implementation of {@link ApiMetadata} for SmartOS
*
* @author Nigel Magnay
*/
public class SmartOSApiMetadata extends BaseApiMetadata {
/** The serialVersionUID */
private static final long serialVersionUID = 3606170564482119304L;
public static Builder builder() {
return new Builder();
}
@Override
public Builder toBuilder() {
return Builder.class.cast(builder().fromApiMetadata(this));
}
public SmartOSApiMetadata() {
super(builder());
}
protected SmartOSApiMetadata(Builder builder) {
super(builder);
}
public static class Builder extends BaseApiMetadata.Builder {
protected Builder(){
id("smartos-ssh")
.name("SmartOS SSH API")
.identityName("Username")
.defaultIdentity("root")
.defaultCredential("smartos")
.defaultEndpoint("http://localhost")
.documentation(URI.create("http://http://wiki.smartos.org/display/DOC/How+to+create+a+Virtual+Machine+in+SmartOS"))
.view(ComputeServiceContext.class)
.defaultModule(SmartOSComputeServiceContextModule.class);
}
@Override
public SmartOSApiMetadata build() {
return new SmartOSApiMetadata(this);
}
}
}

View File

@ -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.smartos.compute.config;
import com.google.common.base.Function;
import com.google.inject.TypeLiteral;
import org.jclouds.smartos.compute.domain.DataSet;
import org.jclouds.smartos.compute.domain.SmartOSHost;
import org.jclouds.smartos.compute.domain.VM;
import org.jclouds.smartos.compute.domain.VmSpecification;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.domain.Location;
import org.jclouds.smartos.compute.functions.DataSetToImage;
import org.jclouds.smartos.compute.functions.DatacenterToLocation;
import org.jclouds.smartos.compute.functions.VMToNodeMetadata;
import org.jclouds.smartos.compute.functions.VmSpecificationToHardware;
import org.jclouds.smartos.compute.strategy.SmartOSComputeServiceAdapter;
/**
*
* @author Nigel Magnay
*/
public class SmartOSComputeServiceContextModule extends
ComputeServiceAdapterContextModule<VM, VmSpecification, DataSet, SmartOSHost> {
@Override
protected void configure() {
super.configure();
bind(new TypeLiteral<ComputeServiceAdapter<VM, VmSpecification, DataSet, SmartOSHost>>() {
}).to(SmartOSComputeServiceAdapter.class);
bind(new TypeLiteral<Function<VM, NodeMetadata>>() {
}).to(VMToNodeMetadata.class);
bind(new TypeLiteral<Function<DataSet, org.jclouds.compute.domain.Image>>() {
}).to(DataSetToImage.class);
bind(new TypeLiteral<Function<VmSpecification, org.jclouds.compute.domain.Hardware>>() {
}).to(VmSpecificationToHardware.class);
bind(new TypeLiteral<Function<SmartOSHost, Location>>() {
}).to(DatacenterToLocation.class);
// to have the compute service adapter override default locations
//install(new LocationsFromComputeServiceAdapterModule<VM, VmSpecification, DataSet, SmartOSHost>(){});
}
}

View File

@ -0,0 +1,141 @@
package org.jclouds.smartos.compute.domain;
import com.google.common.base.Objects;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
/**
* Dataset is a pre-built image ready to be cloned.
*/
public class DataSet {
private final UUID uuid;
private final String os;
private final String published;
private final String urn;
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromDataSet(this);
}
public static class Builder {
public UUID uuid;
public String os;
public String published;
public String urn;
public Builder uuid(UUID uuid) {
this.uuid = uuid;
return this;
}
public Builder uuid(String uuid) {
this.uuid = UUID.fromString(uuid);
return this;
}
public Builder os(String os) {
this.os = os;
return this;
}
public Builder published(String published) {
this.published = published;
return this;
}
public Builder urn(String urn) {
this.urn = urn;
return this;
}
public Builder fromDsadmString(String string) {
String [] sections = string.split(" ");
uuid ( sections[0] );
os ( sections[1] );
published ( sections[2] );
urn ( sections[3] );
return this;
}
public DataSet build() {
return new DataSet(uuid, os, published, urn);
}
public Builder fromDataSet(DataSet in) {
return uuid(in.getUuid())
.os(in.getOs())
.published(in.getPublished())
.urn(in.getUrn());
}
}
protected DataSet(UUID uuid, String os, String published, String urn) {
this.uuid = uuid;
this.os = os;
this.published = published;
this.urn = urn;
}
public UUID getUuid() {
return uuid;
}
public String getOs() {
return os;
}
public String getPublished() {
return published;
}
public String getUrn() {
return urn;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
// UUID is primary key
return uuid.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
return uuid.equals(((DataSet)obj).uuid);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return Objects.toStringHelper(this).omitNullValues()
.add("uuid", uuid)
.add("os", os)
.add("published", published)
.add("urn", urn).toString();
}
}

View File

@ -0,0 +1,296 @@
package org.jclouds.smartos.compute.domain;
import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.HostAndPort;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.location.Provider;
import org.jclouds.rest.annotations.Credential;
import org.jclouds.rest.annotations.Identity;
import org.jclouds.ssh.SshClient;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A host machine that runs smartOS
*/
@Singleton
public class SmartOSHost {
protected final String hostname;
protected final String username;
protected final String password;
protected SshClient.Factory sshClientFactory;
private SshClient _connection;
public static class HostException extends RuntimeException {
public HostException(String s, Throwable throwable) {
super(s, throwable);
}
public HostException(String s) {
super(s);
}
}
public static class NoResponseException extends Exception {
public NoResponseException() {
}
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromSmartOSHost(this);
}
public static class Builder {
protected String hostname;
protected String username;
protected String password;
protected SshClient.Factory sshFactory;
public Builder hostname(String hostname) {
this.hostname = hostname;
return this;
}
public Builder username(String username) {
this.username = username;
return this;
}
public Builder password(String password) {
this.password = password;
return this;
}
public Builder sshFactory(SshClient.Factory sshFactory) {
this.sshFactory = sshFactory;
return this;
}
public SmartOSHost build() {
return new SmartOSHost(hostname, username, password, sshFactory);
}
public Builder fromSmartOSHost(SmartOSHost in) {
return this.hostname ( in.getHostname() )
.username ( in.getHostname() )
.password ( in.getPassword() )
.sshFactory( in.getSshClientFactory() );
}
}
@Inject
protected SmartOSHost(@Provider Supplier<URI> provider,
@Nullable @Identity String identity,
@Nullable @Credential String credential,
SshClient.Factory sshFactory) {
this.hostname = provider.get().getHost();
this.username = identity;
this.password = credential;
this.sshClientFactory = sshFactory;
}
protected SmartOSHost(String hostname, String username, String password, SshClient.Factory sshClientFactory) {
this.hostname = hostname;
this.username = username;
this.password = password;
this.sshClientFactory = sshClientFactory;
}
public String getDescription() {
return "SmartOS@" + hostname;
}
public String getHostname() {
return hostname;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public SshClient.Factory getSshClientFactory() {
return sshClientFactory;
}
protected SshClient getConnection() {
if (_connection == null) {
LoginCredentials credentials = new LoginCredentials.Builder()
.user(username)
.password(password)
.build();
_connection = getSshClientFactory().create(
HostAndPort.fromParts(hostname, 22),
credentials
);
_connection.connect();
}
return _connection;
}
public String exec(String cmd) {
return getConnection().exec(cmd).getOutput();
}
public String vmList() {
return exec("vmadm list -p");
}
public Map<String, String> getVMIpAddresses(UUID vmId)
{
ImmutableMap.Builder<String, String> netMapBuilder = ImmutableMap.builder();
String response = getConnection().exec("zlogin " + vmId.toString() + " ifconfig -a4").getOutput();
if( response.length() == 0)
return ImmutableMap.of();
Iterable<String> strings = Splitter.on("\n").split(response);
Pattern inetMatcher = Pattern.compile("inet [0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
String iface = "";
for(String line : strings )
{
if( line.length() > 0 && Character.isLetterOrDigit(line.charAt(0)) )
{
iface = line.substring(0, line.indexOf(":") );
}
else
{
Matcher matcher = inetMatcher.matcher(line);
if( matcher.find() )
netMapBuilder.put(iface, matcher.group().substring(5));
}
}
return netMapBuilder.build();
}
/**
* What remotely available images are there in the cloud?
*
* @return Collection of datasets
*/
public Iterable<DataSet> getAvailableImages() {
return toSpecList(exec("dsadm avail"));
}
public Iterable<DataSet> getLocalDatasets() {
return toSpecList(exec("dsadm list"));
}
public Iterable<VM> getVMs() {
return toVMList(exec("vmadm list -p"));
}
public VM createVM(VmSpecification specification) {
String response = getConnection().exec("(cat <<END\n" +
specification.toJSONSpecification() + "\nEND\n) | vmadm create").getOutput();
Pattern uuidPattern = Pattern.compile("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}");
Matcher matcher = uuidPattern.matcher(response);
if (matcher.find()) {
String uuid = matcher.group();
return getVM( UUID.fromString(uuid) );
} else {
throw new HostException("Error creating Host: response = " + response + "\n source = " + specification.toJSONSpecification());
}
}
private Iterable<DataSet> toSpecList(String string) {
try {
BufferedReader r = new BufferedReader(new StringReader(string));
r.readLine(); // Skip
String line;
ImmutableList.Builder resultBuilder = ImmutableList.builder();
while ((line = r.readLine()) != null) {
DataSet dataset = DataSet.builder().fromDsadmString(line).build();
resultBuilder.add(dataset);
}
return resultBuilder.build();
} catch (IOException e) {
throw new HostException("Error parsing response when building spec list", e);
}
}
private Iterable<VM> toVMList(String string) {
try {
BufferedReader r = new BufferedReader(new StringReader(string));
String line;
ImmutableList.Builder resultBuilder = ImmutableList.builder();
while ((line = r.readLine()) != null) {
VM vm = VM.builder().host(this).fromVmadmString(line).build();
resultBuilder.add(vm);
}
return resultBuilder.build();
} catch (IOException e) {
throw new HostException("Error parsing response when building VM list", e);
}
}
public VM getVM(UUID serverId) {
for (VM vm : getVMs())
if (vm.uuid.equals(serverId))
return vm;
return null;
}
public DataSet getDataSet(UUID imageId) {
for (DataSet ds : getLocalDatasets()) {
if (ds.getUuid().equals(imageId))
return ds;
}
return null;
}
public void destroyHost(UUID uuid) {
exec("vmadm delete " + uuid.toString());
}
public void rebootHost(UUID uuid) {
exec("vmadm reboot " + uuid.toString());
}
public void stopHost(UUID uuid) {
exec("vmadm stop -p");
}
public void startHost(UUID uuid) {
exec("vmadm start " + uuid.toString());
}
}

View File

@ -0,0 +1,222 @@
package org.jclouds.smartos.compute.domain;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
/**
* Representing a Virtual Machine (Zone / KVM )
**/
public class VM {
public enum State
{
RUNNING,
STOPPED,
INCOMPLETE
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromVM(this);
}
public static class Builder {
protected SmartOSHost host;
protected UUID uuid;
protected String type;
protected String ram;
protected State state = State.STOPPED;
protected String alias;
public Builder uuid(UUID uuid) {
this.uuid = uuid;
return this;
}
public Builder uuid(String uuid) {
this.uuid = UUID.fromString(uuid);
return this;
}
public Builder host(SmartOSHost host) {
this.host = host;
return this;
}
public Builder type(String type) {
this.type = type;
return this;
}
public Builder ram(String ram) {
this.ram = ram;
return this;
}
public Builder state(String state) {
this.state = State.valueOf(state.toUpperCase());
return this;
}
public Builder state(State state) {
this.state = state;
return this;
}
public Builder alias(String alias) {
this.alias = alias;
return this;
}
public Builder fromVmadmString(String string) {
String[] sections = string.split(":");
uuid(sections[0]);
type(sections[1]);
ram(sections[2]);
state(sections[3]);
if (sections.length > 4)
alias(sections[4]);
return this;
}
public VM build() {
return new VM(host,uuid,type,ram,state,alias);
}
public Builder fromVM(VM in) {
return host(in.getHost())
.uuid(in.getUuid())
.type(in.getType())
.ram(in.getRam())
.state(in.getState())
.alias(in.getAlias());
}
}
protected SmartOSHost host;
protected final UUID uuid;
protected String type;
protected String ram;
protected State state;
protected String alias;
public VM(SmartOSHost host, UUID uuid, String type, String ram, State state, String alias) {
this.host = host;
this.uuid = uuid;
this.type = type;
this.ram = ram;
this.state = state;
this.alias = alias;
}
public State getState() {
return state;
}
public void destroy() {
host.destroyHost(uuid);
}
public void reboot() {
host.rebootHost(uuid);
}
public void stop() {
host.stopHost(uuid);
}
public void start() {
host.startHost(uuid);
}
public Optional<String> getPublicAddress() throws InterruptedException {
Map<String, String> ipAddresses;
for( int i=0; i<30; i++ )
{
ipAddresses = host.getVMIpAddresses(uuid);
if( ipAddresses.isEmpty() )
{
// Got some
String ip = ipAddresses.get("net0");
if( ip != null && !ip.equals("0.0.0.0"))
return Optional.of(ip);
}
Thread.sleep(1000);
}
return Optional.absent();
}
public SmartOSHost getHost() {
return host;
}
public UUID getUuid() {
return uuid;
}
public String getType() {
return type;
}
public String getRam() {
return ram;
}
public String getAlias() {
return alias;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
// UUID is primary key
return uuid.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
return uuid.equals(((DataSet)obj).getUuid());
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return Objects.toStringHelper(this).omitNullValues()
.add("uuid", uuid)
.add("type", type)
.add("ram", ram)
.add("alias", alias).toString();
}
}

View File

@ -0,0 +1,110 @@
package org.jclouds.smartos.compute.domain;
import com.google.common.base.Objects;
import com.google.gson.annotations.SerializedName;
import com.google.inject.name.Named;
import java.util.UUID;
/**
* Specification of a network card.
*/
public class VmNIC {
@SerializedName("nic_tag")
protected final String tag;
protected final String ip;
protected final String netmask;
protected final String gateway;
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromVmNIC(this);
}
public static class Builder {
public String tag = "admin";
public String ip;
public String netmask;
public String gateway;
public Builder simpleDCHPNic() {
tag = "admin";
ip = "dhcp";
return this;
}
public Builder tag(String tag) {
this.tag = tag;
return this;
}
public Builder ip(String ip) {
this.ip = ip;
return this;
}
public Builder netmask(String netmask) {
this.netmask = netmask;
return this;
}
public Builder gateway(String gateway) {
this.gateway = gateway;
return this;
}
public VmNIC build() {
return new VmNIC(tag,ip,netmask,gateway);
}
public Builder fromVmNIC(VmNIC in) {
return tag ( in.getTag())
.ip ( in.getIp() )
.netmask( in.getNetmask() )
.gateway( in.getGateway() );
}
}
public VmNIC(String tag, String ip, String netmask, String gateway) {
this.tag = tag;
this.ip = ip;
this.netmask = netmask;
this.gateway = gateway;
}
public String getTag() {
return tag;
}
public String getIp() {
return ip;
}
public String getNetmask() {
return netmask;
}
public String getGateway() {
return gateway;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return Objects.toStringHelper(this).omitNullValues()
.add("tag", tag)
.add("ip", ip)
.add("netmask", netmask)
.add("gateway", gateway).toString();
}
}

View File

@ -0,0 +1,142 @@
package org.jclouds.smartos.compute.domain;
import com.google.common.collect.ImmutableList;
import com.google.gson.*;
import com.google.gson.annotations.SerializedName;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Specification of a VM to build, based on a dataset.
*/
public class VmSpecification {
protected final String alias;
protected final String brand;
@SerializedName("dataset_uuid")
protected final DataSet dataset;
protected final String dnsDomain;
protected final String quota;
protected final List<VmNIC> nics;
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromVmSpecification(this);
}
public static class Builder {
protected String alias;
protected String brand = "joyent";
protected DataSet dataset;
protected String dnsDomain = "local";
protected String quota = "10";
protected List<VmNIC> nics = new ArrayList<VmNIC>();
public Builder alias(String alias) {
this.alias = alias;
return this;
}
public Builder brand(String brand) {
this.brand = brand;
return this;
}
public Builder dataset(DataSet dataset) {
this.dataset = dataset;
return this;
}
public Builder dnsDomain(String dnsDomain) {
this.dnsDomain = dnsDomain;
return this;
}
public Builder quota(String quota) {
this.quota = quota;
return this;
}
public Builder nics(Collection<VmNIC> nic) {
this.nics.addAll(nics);
return this;
}
public Builder nic(VmNIC nic) {
this.nics.add(nic);
return this;
}
public VmSpecification build() {
return new VmSpecification(alias, brand, dataset, dnsDomain, quota, nics);
}
public Builder fromVmSpecification(VmSpecification in) {
return alias (in.getAlias())
.brand (in.getBrand())
.dataset (in.getDataset())
.dnsDomain(in.getDnsDomain())
.quota (in.getQuota())
.nics(in.getNics());
}
}
protected VmSpecification(String alias, String brand, DataSet dataset, String dnsDomain, String quota, List<VmNIC> nics) {
this.alias = alias;
this.brand = brand;
this.dataset = dataset;
this.dnsDomain = dnsDomain;
this.quota = quota;
this.nics = nics;
}
public String getAlias() {
return alias;
}
public String getBrand() {
return brand;
}
public DataSet getDataset() {
return dataset;
}
public String getDnsDomain() {
return dnsDomain;
}
public String getQuota() {
return quota;
}
public List<VmNIC> getNics() {
return ImmutableList.copyOf(nics);
}
public String toJSONSpecification() {
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(DataSet.class, new FlattenDataset() );
Gson g = gson.create();
return g.toJson(this);
}
public class FlattenDataset implements JsonSerializer<DataSet>
{
@Override
public JsonElement serialize(DataSet vmSpecification, Type type, JsonSerializationContext jsonSerializationContext) {
return new JsonPrimitive(dataset.getUuid().toString());
}
}
}

View File

@ -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
* 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.smartos.compute.functions;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.smartos.compute.domain.DataSet;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import com.google.common.base.Function;
/**
* @author Nigel Magnay
*/
@Singleton
public class DataSetToImage implements Function<DataSet, Image> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
@Override
public Image apply(DataSet from) {
ImageBuilder builder = new ImageBuilder();
builder.ids(from.getUuid() + "");
builder.name(from.getUrn());
builder.description(from.getUrn());
builder.status(Image.Status.AVAILABLE);
OsFamily family;
try {
family = OsFamily.SOLARIS;
builder.operatingSystem(new OperatingSystem.Builder().name(from.getUrn()).description(from.getUrn()).family(family).build());
} catch (IllegalArgumentException e) {
logger.debug("<< didn't match os(%s)", from);
}
return builder.build();
}
}

View File

@ -0,0 +1,53 @@
/**
* 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.smartos.compute.functions;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import org.jclouds.smartos.compute.domain.SmartOSHost;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Nigel Magnay
*/
@Singleton
public class DatacenterToLocation implements Function<SmartOSHost, Location> {
private final Provider<Supplier<Location>> provider;
// allow us to lazy discover the provider of a resource
@Inject
public DatacenterToLocation(Provider<Supplier<Location>> provider) {
this.provider = checkNotNull(provider, "provider");
}
@Override
public Location apply(SmartOSHost from) {
return new LocationBuilder().scope(LocationScope.ZONE).id(from.getHostname() + "").description(from.getDescription()).parent(
provider.get().get()).build();
}
}

View File

@ -0,0 +1,150 @@
/**
* 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.smartos.compute.functions;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.sun.corba.se.spi.activation.Server;
import org.jclouds.smartos.compute.domain.VM;
import org.jclouds.collect.FindResourceInSet;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.domain.*;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials;
import org.omg.PortableInterceptor.ACTIVE;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Nigel Magnay
*/
@Singleton
public class VMToNodeMetadata implements Function<VM, NodeMetadata> {
public static final Map<VM.State, NodeMetadata.Status> serverStatusToNodeStatus = ImmutableMap
.<VM.State, NodeMetadata.Status> builder()
.put(VM.State.RUNNING, NodeMetadata.Status.RUNNING)//
.put(VM.State.STOPPED, NodeMetadata.Status.SUSPENDED)//
.put(VM.State.INCOMPLETE, NodeMetadata.Status.PENDING)//
.build();
private final FindHardwareForServer findHardwareForServer;
private final FindLocationForServer findLocationForServer;
private final FindImageForServer findImageForServer;
private final Map<String, Credentials> credentialStore;
private final GroupNamingConvention nodeNamingConvention;
@Inject
VMToNodeMetadata(Map<String, Credentials> credentialStore, FindHardwareForServer findHardwareForServer,
FindLocationForServer findLocationForServer, FindImageForServer findImageForServer,
GroupNamingConvention.Factory namingConvention) {
this.nodeNamingConvention = checkNotNull(namingConvention, "namingConvention").createWithoutPrefix();
this.credentialStore = checkNotNull(credentialStore, "credentialStore");
this.findHardwareForServer = checkNotNull(findHardwareForServer, "findHardwareForServer");
this.findLocationForServer = checkNotNull(findLocationForServer, "findLocationForServer");
this.findImageForServer = checkNotNull(findImageForServer, "findImageForServer");
}
@Override
public NodeMetadata apply(VM from) {
// convert the result object to a jclouds NodeMetadata
NodeMetadataBuilder builder = new NodeMetadataBuilder();
builder.ids(from.getUuid() + "");
builder.name(from.getAlias());
builder.location(findLocationForServer.apply(from));
builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getType()));
builder.imageId(from.getType() + "");
Image image = findImageForServer.apply(from);
if (image != null)
builder.operatingSystem(image.getOperatingSystem());
builder.hardware(findHardwareForServer.apply(from));
builder.status(serverStatusToNodeStatus.get(from.getState()));
try {
if( from.getState() == VM.State.RUNNING )
{
Optional<String> ip = from.getPublicAddress();
if( ip.isPresent() ) {
builder.publicAddresses(ImmutableSet.<String> of(ip.get()));
builder.privateAddresses(ImmutableSet.<String> of(ip.get()));
}
}
}
catch(Exception ex)
{
// None?
}
//builder.privateAddresses(ImmutableSet.<String> of(from.privateAddress));
builder.credentials(LoginCredentials.fromCredentials(credentialStore.get(from.getUuid() + "")));
return builder.build();
}
@Singleton
public static class FindHardwareForServer extends FindResourceInSet<VM, Hardware> {
@Inject
public FindHardwareForServer(@Memoized Supplier<Set<? extends Hardware>> hardware) {
super(hardware);
}
@Override
public boolean matches(VM from, Hardware input) {
return input.getProviderId().equals(from.getUuid() + "");
}
}
@Singleton
public static class FindImageForServer extends FindResourceInSet<VM, Image> {
@Inject
public FindImageForServer(@Memoized Supplier<Set<? extends Image>> hardware) {
super(hardware);
}
@Override
public boolean matches(VM from, Image input) {
return input.getProviderId().equals(from.getUuid() + "");
}
}
@Singleton
public static class FindLocationForServer extends FindResourceInSet<VM, Location> {
@Inject
public FindLocationForServer(@Memoized Supplier<Set<? extends Location>> hardware) {
super(hardware);
}
@Override
public boolean matches(VM from, Location input) {
return input.getId().equals(from.getUuid() + "");
}
}
}

View File

@ -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.smartos.compute.functions;
import javax.inject.Singleton;
import org.jclouds.smartos.compute.domain.VmSpecification;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.HardwareBuilder;
import org.jclouds.compute.domain.Processor;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
/**
* @author Nigel Magnay
*/
@Singleton
public class VmSpecificationToHardware implements Function<VmSpecification, Hardware> {
@Override
public Hardware apply(VmSpecification from) {
HardwareBuilder builder = new HardwareBuilder();
builder.ids("AnID");
builder.name(from.getAlias());
builder.processors(ImmutableList.of(new Processor(1, 1.0)));
builder.ram(256);
//builder.volumes(ImmutableList.<Volume> of(new VolumeImpl(from.disk, true, false)));
return builder.build();
}
}

View File

@ -0,0 +1,128 @@
/**
* 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.smartos.compute.strategy;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.smartos.compute.domain.*;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template;
import org.jclouds.domain.LoginCredentials;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* defines the connection between the {@link org.jclouds.smartos.compute.domain.SmartOSHost} implementation and the jclouds
* {@link ComputeService}
*
*/
@Singleton
public class SmartOSComputeServiceAdapter implements ComputeServiceAdapter<VM, VmSpecification, DataSet, SmartOSHost> {
private final SmartOSHost host;
@Inject
public SmartOSComputeServiceAdapter(SmartOSHost host) {
this.host = checkNotNull(host, "host");
}
private SmartOSHost getHost() {
return host;
}
@Override
public NodeAndInitialCredentials<VM> createNodeWithGroupEncodedIntoName(String tag, String name, Template template) {
VmSpecification specification = VmSpecification.builder()
.alias(name)
.dataset(getHost().getDataSet(UUID.fromString(template.getImage().getProviderId())))
.nic(VmNIC.builder().simpleDCHPNic().build())
.build();
VM from = getHost().createVM(specification);
return new NodeAndInitialCredentials<VM>(from, from.getUuid() + "", LoginCredentials.builder().user("smartos")
.password("smartos").build());
}
@Override
public Iterable<VmSpecification> listHardwareProfiles() {
List<VmSpecification> specificationList = new ArrayList<VmSpecification>();
VmSpecification vs = VmSpecification.builder()
.alias("Standard Joyent VM")
.nic(VmNIC.builder().simpleDCHPNic().build())
.build();
specificationList.add(vs);
return specificationList;
}
@Override
public Iterable<DataSet> listImages() {
return getHost().getLocalDatasets();
}
@Override
public DataSet getImage(String id) {
return getHost().getDataSet(UUID.fromString(id));
}
@Override
public Iterable<VM> listNodes() {
return getHost().getVMs();
}
@Override
public Iterable<SmartOSHost> listLocations() {
return ImmutableSet.of();
}
@Override
public VM getNode(String id) {
return getHost().getVM(UUID.fromString(id));
}
@Override
public void destroyNode(String id) {
getHost().getVM(UUID.fromString(id)).destroy();
}
@Override
public void rebootNode(String id) {
getHost().getVM(UUID.fromString(id)).reboot();
}
@Override
public void resumeNode(String id) {
getHost().getVM(UUID.fromString(id)).start();
}
@Override
public void suspendNode(String id) {
getHost().getVM(UUID.fromString(id)).stop();
}
}

View File

@ -0,0 +1 @@
org.jclouds.smartos.SmartOSApiMetadata

View File

@ -0,0 +1,88 @@
/**
* 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.smartos.compute;
import static org.testng.Assert.assertEquals;
import java.util.Properties;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
import org.jclouds.ContextBuilder;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.internal.ContextImpl;
import org.jclouds.rest.internal.BaseRestClientTest;
import org.jclouds.smartos.SmartOSApiMetadata;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test;
/**
*
* @author Adrian Cole
*
*/
@Test(groups = "unit", testName = "ServerManagerContextBuilderTest")
public class SmartOSManagerComputeServiceContextBuilderTest {
@Test
public void testCanBuildWithApiMetadata() {
ComputeServiceContext context = ContextBuilder.newBuilder(
new SmartOSApiMetadata())
.modules(ImmutableSet.<Module>of(getSshModule()))
.build(ComputeServiceContext.class);
context.close();
}
@Test
public void testCanBuildById() {
ComputeServiceContext context = ContextBuilder.newBuilder("smartos-ssh")
.modules(ImmutableSet.<Module>of(getSshModule()))
.build(ComputeServiceContext.class);
context.close();
}
@Test
public void testCanBuildWithOverridingProperties() {
Properties overrides = new Properties();
overrides.setProperty("smartos-ssh.endpoint", "http://host");
overrides.setProperty("smartos-ssh.api-version", "1");
ComputeServiceContext context = ContextBuilder.newBuilder("smartos-ssh")
.modules(ImmutableSet.<Module>of(getSshModule()))
.overrides(overrides).build(ComputeServiceContext.class);
context.close();
}
@Test
public void testUnwrapIsCorrectType() {
ComputeServiceContext context = ContextBuilder.newBuilder("smartos-ssh")
.modules(ImmutableSet.<Module>of(getSshModule()))
.build(ComputeServiceContext.class);
assertEquals(context.unwrap().getClass(), ContextImpl.class);
context.close();
}
protected Module getSshModule() {
return new SshjSshClientModule();
}
}

View File

@ -0,0 +1,60 @@
/**
* 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.smartos.compute;
import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
import static org.testng.Assert.assertEquals;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
/**
* @author Adrian Cole
*/
@Test(groups = "live", enabled = true, singleThreaded = true)
public class SmartOSManagerComputeServiceLiveTest extends BaseComputeServiceLiveTest {
public SmartOSManagerComputeServiceLiveTest() {
provider = "smartos-ssh";
}
@Test
public void testTemplateBuilder() {
Template defaultTemplate = client.templateBuilder().build();
assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "5.3");
assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.CENTOS);
assertEquals(defaultTemplate.getLocation().getId(), "1");
assertEquals(getCores(defaultTemplate.getHardware()), 0.5d);
}
// smartos-ssh does not support metadata
@Override
protected void checkUserMetadataInNodeEquals(NodeMetadata node, ImmutableMap<String, String> userMetadata) {
assert node.getUserMetadata().equals(ImmutableMap.<String, String> of()) : String.format(
"node userMetadata did not match %s %s", userMetadata, node);
}
}

View File

@ -0,0 +1,36 @@
package org.jclouds.smartos.compute.domain;
import com.google.common.collect.ImmutableList;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.*;
import java.util.List;
import java.util.UUID;
@Test(groups = "unit", testName = "DataSetTest")
public class DataSetTest {
@Test
public void testParse() throws IOException {
// Response from console from a 'dsadm list'
InputStream is = getClass().getResourceAsStream("dsadm-list-response.txt");
BufferedReader r = new BufferedReader(new InputStreamReader(is));
String line = r.readLine(); // skip line
ImmutableList.Builder resultBuilder = ImmutableList.builder();
while ((line = r.readLine()) != null) {
DataSet ds = DataSet.builder().fromDsadmString(line).build();
resultBuilder.add(ds);
}
List<DataSet> dataSetList = resultBuilder.build();
Assert.assertEquals(10, dataSetList.size());
Assert.assertEquals(UUID.fromString("c0ffee88-883e-47cf-80d1-ad71cc872180"), dataSetList.get(0).getUuid());
Assert.assertEquals("nrm:nrm:realtime-jenkins:1.7", dataSetList.get(0).getUrn());
}
}

View File

@ -0,0 +1,38 @@
package org.jclouds.smartos.compute.domain;
import com.google.common.collect.ImmutableList;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.UUID;
@Test(groups = "unit", testName = "VMTest")
public class VMTest {
@Test
public void testParse() throws IOException {
// Response from console from a 'vmadm list -p'
InputStream is = getClass().getResourceAsStream("vmadm-list-response.txt");
BufferedReader r = new BufferedReader(new InputStreamReader(is));
String line = null;
ImmutableList.Builder resultBuilder = ImmutableList.builder();
while ((line = r.readLine()) != null) {
VM vm = VM.builder().fromVmadmString(line).build();
resultBuilder.add(vm);
}
List<VM> vmList = resultBuilder.build();
Assert.assertEquals(2, vmList.size());
Assert.assertEquals(UUID.fromString("60bd2ae5-4e4d-4952-88f9-1b850259d914"), vmList.get(0).getUuid());
Assert.assertEquals(VM.State.STOPPED, vmList.get(0).getState());
}
}

View File

@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--
For more configuration infromation and examples see the Apache
Log4j website: http://logging.apache.org/log4j/
-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="false">
<appender name="S" class="org.apache.log4j.ConsoleAppender">
<param name="Append" value="true"/>
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
<param name="Threshold" value="TRACE"/>
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n"/>
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-wire.log"/>
<param name="Append" value="true"/>
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
<param name="Threshold" value="TRACE"/>
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n"/>
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds.log"/>
<param name="Append" value="true"/>
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
<param name="Threshold" value="TRACE"/>
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n"/>
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="BLOBSTOREFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-blobstore.log"/>
<param name="Append" value="true"/>
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
<param name="Threshold" value="TRACE"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n"/>
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="COMPUTEFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-compute.log"/>
<param name="Append" value="true"/>
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
<param name="Threshold" value="TRACE"/>
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n"/>
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="SSHFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-ssh.log"/>
<param name="Append" value="true"/>
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
<param name="Threshold" value="TRACE"/>
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n"/>
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<appender name="ASYNCCOMPUTE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="COMPUTEFILE"/>
</appender>
<appender name="ASYNCSSH" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="SSHFILE"/>
</appender>
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="FILE"/>
</appender>
<appender name="ASYNCWIRE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="WIREFILE"/>
</appender>
<appender name="ASYNCBLOBSTORE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="BLOBSTOREFILE"/>
</appender>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<category name="org.jclouds">
<priority value="DEBUG"/>
<appender-ref ref="ASYNC"/>
</category>
<category name="jclouds.headers">
<priority value="DEBUG"/>
<appender-ref ref="ASYNCWIRE"/>
</category>
<category name="jclouds.ssh">
<priority value="DEBUG"/>
<appender-ref ref="ASYNCSSH"/>
</category>
<category name="jclouds.wire">
<priority value="DEBUG"/>
<appender-ref ref="ASYNCWIRE"/>
</category>
<category name="jclouds.blobstore">
<priority value="DEBUG"/>
<appender-ref ref="ASYNCBLOBSTORE"/>
</category>
<category name="jclouds.compute">
<priority value="TRACE"/>
<appender-ref ref="ASYNCCOMPUTE"/>
</category>
<!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<priority value="TRACE"/>
<appender-ref ref="S"/>
</root>
</log4j:configuration>

View File

@ -0,0 +1,11 @@
UUID OS PUBLISHED URN
c0ffee88-883e-47cf-80d1-ad71cc872180 smartos 2012-05-02 nrm:nrm:realtime-jenkins:1.7
c0ffee99-883e-47cf-80d1-ad71cc872180 smartos 2012-05-02 nrm:nrm:realtime-jenkins:1.7
bb5aa3d2-cc19-11e1-99e7-a752418da63a smartos 2012-05-02 nrm:nrm:jenkins-slave:1.0
c0ffee32-883e-47cf-80d1-ad71cc872180 smartos 2012-05-02 nrm:nrm:realtime-jenkins:1.6.3
467ca742-4873-11e1-80ea-37290b38d2eb smartos 2012-02-14 sdc:sdc:smartos64:1.5.3
56108678-1183-11e1-83c3-ff3185a5b47f linux 2011-11-18 sdc:sdc:ubuntu10.04:0.1.0
f9e4be48-9466-11e1-bc41-9f993f5dff36 smartos 2012-05-02 sdc:sdc:smartos64:1.6.3
c0ffee64-883e-47cf-80d1-ad71cc872180 smartos 2012-05-02 nrm:nrm:realtime-jenkins:1.6
a9380908-ea0e-11e0-aeee-4ba794c83c33 smartos 2011-09-28 sdc:sdc:percona:1.0.7
c0ffee00-883e-47cf-80d1-ad71cc872180 smartos 2012-05-02 sdc:sdc:realtime-jenkins:1.6.3

View File

@ -0,0 +1,2 @@
60bd2ae5-4e4d-4952-88f9-1b850259d914:OS:256:stopped:
a8799014-7680-481f-b7de-76b501dbd803:OS:256:running:instance1-3b5

View File

@ -29,7 +29,6 @@ import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
@ -45,6 +44,7 @@ import org.jclouds.location.Region;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.inject.Inject;
/**
*
@ -76,7 +76,7 @@ public class AWSEC2ListNodesStrategy extends EC2ListNodesStrategy {
.describeSpotInstanceRequestsInRegion(from);
}
}, executor, null, logger, "reservations")), spotConverter), notNull());
}, executor, maxTime, logger, "reservations")), spotConverter), notNull());
return concat(super.pollRunningInstances(), spots);
}