Merge branch 'issue174'

This commit is contained in:
Alex Yarmula 2010-03-16 20:34:29 -07:00
commit e1903f9473
12 changed files with 1032 additions and 0 deletions

View File

@ -37,6 +37,9 @@
<module>createlamp</module> <module>createlamp</module>
<module>speedtest-sqs</module> <module>speedtest-sqs</module>
<module>createandlistbuckets</module> <module>createandlistbuckets</module>
<module>resize-ebs/jruby-client</module>
<module>resize-ebs/resize-ebs-java</module>
</modules> </modules>
<dependencies> <dependencies>
<dependency> <dependency>

View File

@ -0,0 +1,16 @@
This demo is used to show how to change volume size for EBS-backed instance. Basically, jclouds API is used to run a sequence of commands, as described in an excellent article: http://alestic.com/2009/12/ec2-ebs-boot-resize.
There are 2 directories:
First one, /resize-ebs-java, has all necessary classes for the demo (under org.jclouds.tools.ebsresize), and also a sample Java launcher (EbsResizeMain). Please change the variables in EbsResizeMain to run the demo.
Second directory, /jruby-client, has a JRuby launcher for the same Java libraries. Please change the variables in launcher.rb to run the demo.
To manage the maven dependencies, run as:
$ mvn jruby:run
Pre-requisites:
- running EBS-backed instance at Amazon EC2
- Ubuntu image
- proper settings in place (EbsResizeMain or launcher.rb)
- file system is ext3 (otherwise, change is needed in org.jclouds.tools.ebsresize.InstanceVolumeManager)
- the demo doesn't take care of freezing the database, filesystem and application data

View File

@ -0,0 +1,66 @@
<!--
Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
====================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
====================================================================
-->
<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-aws-demos-project</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>jclouds-aws-demo-ebsresize-ruby</artifactId>
<groupId>org.jclouds</groupId>
<name>JRuby client for demo to change volume size for EBS-based instances</name>
<description>JRuby is used to run jclouds-aws-demo-ebsresize demo</description>
<repositories>
<repository>
<id>jclouds</id>
<url>http://jclouds.googlecode.com/svn/repo</url>
</repository>
<repository>
<id>jclouds-snapshot.repository</id>
<url>http://jclouds.rimuhosting.com/maven2/snapshots</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jruby-maven-plugin</artifactId>
<version>1.0-beta-4</version>
<configuration>
<script>src/main/scripts/launcher.rb</script>
</configuration>
<dependencies>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-aws-demo-ebsresize</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,59 @@
=begin
Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
====================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
====================================================================
=end
=begin
For more information, refer to:
http://alestic.com/2009/12/ec2-ebs-boot-resize
=end
class EbsVolumesJRuby
def initialize access_key_id, secret_key
@access_key_id = access_key_id
@secret_key = secret_key
end
def resize instance_id, region, new_size
@manager = org.jclouds.tools.ebsresize.InstanceVolumeManager.new(@access_key_id, @secret_key)
ebs_api = @manager.get_ebs_api
instance_api = @manager.get_instance_api
api = @manager.get_api
@instance = instance_api.get_instance_by_id_and_region instance_id, region
instance_api.stop_instance @instance
volume = ebs_api.get_root_volume_for_instance @instance
ebs_api.detach_volume_from_stopped_instance volume, @instance
new_volume = ebs_api.clone_volume_with_new_size volume, new_size
ebs_api.attach_volume_to_stopped_instance new_volume, @instance
instance_api.start_instance @instance
api.get_elastic_block_store_services().delete_volume_in_region @instance.get_region(), volume.get_id()
end
def run_remote_resize_commands credentials, key_pair
@manager.run_remote_resize_commands @instance, credentials, key_pair
end
end

View File

@ -0,0 +1,38 @@
=begin
Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
====================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
====================================================================
=end
require "src/main/scripts/ebs_volumes_j_ruby.rb"
instance_id = "AMAZON_INSTANCE_ID (i-xxxxxx)"
region = org.jclouds.aws.domain.Region::US_EAST_1
new_size = 6
remote_login = "ubuntu"
remote_password = ""
path_to_key = ""
ebs_volumes = EbsVolumesJRuby.new("YOUR_ACCESS_KEY_ID", "YOUR_SECRET_KEY")
ebs_volumes.resize instance_id, region, new_size
key_pair = org.jclouds.util.Utils::toStringAndClose(
java.io.FileInputStream.new(path_to_key))
credentials = org.jclouds.domain.Credentials.new(remote_login, remote_password)
java.lang.Thread::sleep(25000);
ebs_volumes.run_remote_resize_commands credentials, key_pair

View File

@ -0,0 +1,66 @@
<!--
Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
====================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
====================================================================
-->
<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-aws-demos-project</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>jclouds-aws-demo-ebsresize</artifactId>
<groupId>org.jclouds</groupId>
<name>jclouds EC2 demo, to change volume size for EBS-based instances</name>
<description>For a given running instance, resizes the volume (with several minutes of downtime)</description>
<repositories>
<repository>
<id>jclouds</id>
<url>http://jclouds.googlecode.com/svn/repo</url>
</repository>
<repository>
<id>jclouds-snapshot.repository</id>
<url>http://jclouds.rimuhosting.com/maven2/snapshots</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-jsch</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,59 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
import org.jclouds.aws.domain.Region;
import org.jclouds.domain.Credentials;
import org.jclouds.tools.ebsresize.InstanceVolumeManager;
import org.jclouds.util.Utils;
import java.io.*;
/**
* Launcher for EBS resize demo. This is the Java version of what
* can be written in Ruby (see <jclouds>/aws/demos/resize-ebs/jruby-client)
*
* @author Oleksiy Yarmula
*/
public class EbsResizeMain {
String accessKeyId = "YOUR_ACCESS_KEY_ID";
String secretKey = "YOUR_SECRET_KEY";
String instanceId = "AMAZON_INSTANCE_ID (i-xxxxxx)";
Region region = Region.US_EAST_1;
int newSize = 6;
String pathToKeyPair = "";
String remoteLogin = "ubuntu";
String remotePassword = "";
public static void main(String[] args) throws Exception {
new EbsResizeMain().launch();
}
public void launch() throws Exception {
InstanceVolumeManager manager = new InstanceVolumeManager(accessKeyId, secretKey);
String privateKey =
Utils.toStringAndClose(new FileInputStream(pathToKeyPair));
manager.resizeVolume(instanceId, region, new Credentials(remoteLogin, remotePassword),
privateKey, newSize);
}
}

View File

@ -0,0 +1,173 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.tools.ebsresize;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.inject.Module;
import com.google.inject.internal.ImmutableSet;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.EC2AsyncClient;
import org.jclouds.aws.ec2.EC2Client;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.ComputeServiceContextFactory;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.internal.NodeMetadataImpl;
import org.jclouds.domain.Credentials;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.predicates.SocketOpen;
import org.jclouds.rest.RestContext;
import org.jclouds.tools.ebsresize.facade.ElasticBlockStoreFacade;
import org.jclouds.tools.ebsresize.facade.InstanceFacade;
import org.jclouds.tools.ebsresize.util.SshExecutor;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
* Launches a sequence of commands to change the size of
* EBS volume.
*
* This results in several minutes of downtime of the instance.
* More details available at:
* <a href="http://alestic.com/2010/02/ec2-resize-running-ebs-root" />
*
*
* @author Oleksiy Yarmula
*/
public class InstanceVolumeManager {
private final ComputeServiceContext context;
@SuppressWarnings({"FieldCanBeLocal"})
private final RestContext<EC2AsyncClient, EC2Client> ec2context;
private final EC2Client api;
private final ElasticBlockStoreFacade ebsApi;
private final InstanceFacade instanceApi;
private final Predicate<InetSocketAddress> socketOpen =
new RetryablePredicate<InetSocketAddress>(new SocketOpen(), 180, 5, TimeUnit.SECONDS);
public InstanceVolumeManager(String accessKeyId, String secretKey) {
this(accessKeyId, secretKey, new Properties());
}
@VisibleForTesting
InstanceVolumeManager(String accessKeyId, String secretKey, Properties overridesForContext) {
try {
context = new ComputeServiceContextFactory()
.createContext("ec2", accessKeyId, secretKey,
ImmutableSet.<Module> of(), overridesForContext);
} catch(IOException e) { throw new RuntimeException(e); }
ec2context = context.getProviderSpecificContext();
api = ec2context.getApi();
ebsApi = new ElasticBlockStoreFacade(api.getElasticBlockStoreServices());
instanceApi = new InstanceFacade(api.getInstanceServices());
}
public void resizeVolume(String instanceId, Region region,
Credentials instanceCredentials, String pathToKeyPair, int newSize) {
RunningInstance instance = instanceApi.getInstanceByIdAndRegion(instanceId, region);
instanceApi.stopInstance(instance);
Volume volume = ebsApi.getRootVolumeForInstance(instance);
ebsApi.detachVolumeFromStoppedInstance(volume, instance);
Volume newVolume = ebsApi.cloneVolumeWithNewSize(volume, newSize);
ebsApi.attachVolumeToStoppedInstance(newVolume, instance);
instanceApi.startInstance(instance);
api.getElasticBlockStoreServices().deleteVolumeInRegion(instance.getRegion(), volume.getId());
runRemoteResizeCommands(instance, instanceCredentials, pathToKeyPair);
}
public void runRemoteResizeCommands(RunningInstance instance, Credentials instanceCredentials,
String keyPair) {
Map<String, ? extends ComputeMetadata> nodes = context.getComputeService().getNodes();
//if don't set it here, nodeMetadata.getCredentials = null
NodeMetadata nodeMetadata = addCredentials((NodeMetadata) nodes.get(instance.getId()),
instanceCredentials);
InetSocketAddress socket =
new InetSocketAddress(Iterables.getLast(nodeMetadata.getPublicAddresses()), 22);
SshExecutor sshExecutor = new SshExecutor(nodeMetadata, instanceCredentials,
keyPair, socket);
waitForSocket(socket);
sshExecutor.connect();
sshExecutor.execute("sudo resize2fs " + instance.getRootDeviceName());
}
private NodeMetadata addCredentials(NodeMetadata nodeMetadata, Credentials credentials) {
return new
NodeMetadataImpl(nodeMetadata.getId(), nodeMetadata.getName(),
nodeMetadata.getLocationId(),
nodeMetadata.getUri(),
nodeMetadata.getUserMetadata(), nodeMetadata.getTag(),
nodeMetadata.getState(), nodeMetadata.getPublicAddresses(),
nodeMetadata.getPrivateAddresses(), nodeMetadata.getExtra(),
credentials);
}
public void waitForSocket(InetSocketAddress socket) {
checkState(socketOpen.apply(socket),
/*or throw*/ "Couldn't connect to instance");
}
public void closeContext() {
context.close();
}
public ElasticBlockStoreFacade getEbsApi() {
return ebsApi;
}
public InstanceFacade getInstanceApi() {
return instanceApi;
}
public EC2Client getApi() {
return api;
}
public ComputeServiceContext getContext() {
return context;
}
}

View File

@ -0,0 +1,150 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.tools.ebsresize.facade;
import static com.google.common.base.Preconditions.*;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.jclouds.aws.ec2.domain.*;
import org.jclouds.aws.ec2.options.CreateSnapshotOptions;
import org.jclouds.aws.ec2.options.DetachVolumeOptions;
import org.jclouds.aws.ec2.predicates.SnapshotCompleted;
import org.jclouds.aws.ec2.predicates.VolumeAttached;
import org.jclouds.aws.ec2.predicates.VolumeAvailable;
import org.jclouds.aws.ec2.services.ElasticBlockStoreClient;
import org.jclouds.predicates.RetryablePredicate;
import java.util.concurrent.TimeUnit;
/**
* Aggregates several methods of jClouds' EC2 functionality
* to work with elastic block store.
*
* @author Oleksiy Yarmula
*/
public class ElasticBlockStoreFacade {
final private ElasticBlockStoreClient elasticBlockStoreServices;
final private Predicate<Volume> volumeAvailable;
final private Predicate<Attachment> volumeAttached;
final private Predicate<Snapshot> snapshotCompleted;
public ElasticBlockStoreFacade(ElasticBlockStoreClient elasticBlockStoreServices) {
this.elasticBlockStoreServices = elasticBlockStoreServices;
this.volumeAvailable =
new RetryablePredicate<Volume>(new VolumeAvailable(elasticBlockStoreServices),
600, 10, TimeUnit.SECONDS);
this.volumeAttached =
new RetryablePredicate<Attachment>(new VolumeAttached(elasticBlockStoreServices),
600, 10, TimeUnit.SECONDS);
this.snapshotCompleted = new RetryablePredicate<Snapshot>(new SnapshotCompleted(
elasticBlockStoreServices), 600, 10, TimeUnit.SECONDS);
}
/**
* Returns the root volume for instance with EBS type of root device.
*
* @param runningInstance
* instance of EBS type that has volume(s) attached
* @return root device volume
*/
public Volume getRootVolumeForInstance(RunningInstance runningInstance) {
checkArgument(runningInstance.getRootDeviceType() == RootDeviceType.EBS,
"Only storage for instances with EBS type of root device can be resized.");
String rootDevice = checkNotNull(runningInstance.getRootDeviceName());
//get volume id
String volumeId = null;
for(String ebsVolumeId : runningInstance.getEbsBlockDevices().keySet()) {
if(! rootDevice.equals(ebsVolumeId)) continue;
RunningInstance.EbsBlockDevice device = runningInstance.getEbsBlockDevices().get(ebsVolumeId);
volumeId = checkNotNull(device.getVolumeId(), "Device's volume id must not be null.");
}
//return volume by volume id
return Iterables.getOnlyElement(elasticBlockStoreServices.
describeVolumesInRegion(runningInstance.getRegion(), volumeId));
}
/**
* Detaches volume from an instance.
*
* This method blocks until the operations are fully completed.
* @param volume
* volume to detach
* @param stoppedInstance
* instance to which the volume is currently attached
*/
public void detachVolumeFromStoppedInstance(Volume volume, RunningInstance stoppedInstance) {
elasticBlockStoreServices.detachVolumeInRegion(stoppedInstance.getRegion(), volume.getId(), false,
DetachVolumeOptions.Builder.fromInstance(stoppedInstance.getId()));
checkState(volumeAvailable.apply(volume),
/*or throw*/ "Couldn't detach the volume from instance");
}
/**
* Makes a 'copy' of current volume with different size.
* Behind the scenes, if creates a snapshot of current volume,
* and then creates a new volume with given size from the snapshot.
*
* This method blocks until the operations are fully completed.
*
* @param volume volume to be cloned
* @param newSize size of new volume
* @return newly created volume
*/
public Volume cloneVolumeWithNewSize(Volume volume, int newSize) {
Snapshot createdSnapshot =
elasticBlockStoreServices.createSnapshotInRegion(volume.getRegion(), volume.getId(),
CreateSnapshotOptions.Builder.withDescription("snapshot to test extending volumes"));
checkState(snapshotCompleted.apply(createdSnapshot),
/*or throw*/ "Couldn't create a snapshot");
Volume newVolume = elasticBlockStoreServices.createVolumeFromSnapshotInAvailabilityZone(
volume.getAvailabilityZone(), newSize,
createdSnapshot.getId());
checkState(volumeAvailable.apply(newVolume),
/*or throw*/ "Couldn't create a volume from the snapshot");
elasticBlockStoreServices.deleteSnapshotInRegion(volume.getRegion(), createdSnapshot.getId());
return newVolume;
}
/**
* Attaches volume to a (stopped) instance.
*
* This method blocks until the operations are fully completed.
* @param volume
* volume to attach
* @param instance
* instance to which the volume is to be attached, must be in
* a 'stopped' state
*/
public void attachVolumeToStoppedInstance(Volume volume, RunningInstance instance) {
Attachment volumeAttachment = elasticBlockStoreServices.
attachVolumeInRegion(instance.getRegion(), volume.getId(), instance.getId(),
instance.getRootDeviceName());
checkState(volumeAttached.apply(volumeAttachment),
/*or throw*/ "Couldn't attach volume back to the instance");
}
}

View File

@ -0,0 +1,105 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.tools.ebsresize.facade;
import static com.google.common.base.Preconditions.*;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.domain.Reservation;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.predicates.InstanceStateRunning;
import org.jclouds.aws.ec2.predicates.InstanceStateStopped;
import org.jclouds.aws.ec2.services.InstanceClient;
import org.jclouds.predicates.RetryablePredicate;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Aggregates several methods of jClouds' EC2 functionality
* to work with instance client (instance store). These
* features are specific to instances with EBS root device.
*
* @author Oleksiy Yarmula
*/
public class InstanceFacade {
final private InstanceClient instanceServices;
final private Predicate<RunningInstance> instanceRunning;
final private Predicate<RunningInstance> instanceStopped;
public InstanceFacade(InstanceClient instanceServices) {
this.instanceServices = instanceServices;
this.instanceRunning =
new RetryablePredicate<RunningInstance>(new InstanceStateRunning(instanceServices),
600, 10, TimeUnit.SECONDS);
this.instanceStopped =
new RetryablePredicate<RunningInstance>(new InstanceStateStopped(instanceServices),
600, 10, TimeUnit.SECONDS);
}
/**
* Starts an instance, given that it has an EBS volume attached.
* This command is only available for EC2 instances with
* EBS root device.
*
* This method blocks until the operations are fully completed.
*
* @param instance instance to start
* @see #stopInstance(org.jclouds.aws.ec2.domain.RunningInstance)
*/
public void startInstance(RunningInstance instance) {
instanceServices.startInstancesInRegion(instance.getRegion(), instance.getId());
checkState(instanceRunning.apply(instance),
/*or throw*/ "Couldn't start the instance");
}
/**
* Stops an instance.
* This command is only available for EC2 instances with
* EBS root device.
*
* This method blocks until the operations are fully completed.
*
* @param instance instance to stop
* @see #startInstance(org.jclouds.aws.ec2.domain.RunningInstance)
*/
public void stopInstance(RunningInstance instance) {
instanceServices.stopInstancesInRegion(instance.getRegion(), false, instance.getId());
checkState(instanceStopped.apply(instance),
/*or throw*/ "Couldn't stop the instance");
}
/**
* Given an instance id and its {@link Region}, returns a {@link RunningInstance}.
*
* @param instanceId id of instance
* @param region region of the instance
* @return instance, corresponding to instanceId and region
*/
public RunningInstance getInstanceByIdAndRegion(String instanceId, Region region) {
Set<Reservation> reservations = instanceServices.describeInstancesInRegion(region, instanceId);
Reservation reservation = checkNotNull(Iterables.getOnlyElement(reservations));
return Iterables.getOnlyElement(reservation);
}
}

View File

@ -0,0 +1,90 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.tools.ebsresize.util;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.predicates.RunScriptRunning;
import org.jclouds.compute.util.ComputeUtils;
import org.jclouds.domain.Credentials;
import org.jclouds.logging.Logger;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.ssh.SshClient;
import org.jclouds.ssh.jsch.JschSshClient;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
/**
* Allows remote command execution on instance.
*
* This is based on {@link org.jclouds.ssh.SshClient} and
* {@link org.jclouds.compute.util.ComputeUtils}, with
* some convenience methods.
*
* @see org.jclouds.ssh.SshClient
* @see org.jclouds.compute.util.ComputeUtils
*
* @author Oleksiy Yarmula
*/
public class SshExecutor {
private final Predicate<SshClient> runScriptRunning =
new RetryablePredicate<SshClient>(Predicates.not(new RunScriptRunning()),
600, 3, TimeUnit.SECONDS);
private final NodeMetadata nodeMetadata;
private final SshClient sshClient;
private final ComputeUtils utils;
public SshExecutor(NodeMetadata nodeMetadata,
Credentials instanceCredentials,
String keyPair,
InetSocketAddress socket) {
this.nodeMetadata = nodeMetadata;
this.sshClient =
new JschSshClient(socket, 60000,
instanceCredentials.account, keyPair.getBytes());
this.utils = new ComputeUtils(null, runScriptRunning, null);
}
public void connect() {
sshClient.connect();
}
public void execute(String command) {
ComputeUtils.RunScriptOnNode script = utils.runScriptOnNode(nodeMetadata,
"basicscript.sh", command.getBytes());
script.setConnection(sshClient, Logger.CONSOLE);
try {
script.call();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,207 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.tools.ebsresize;
import com.google.common.base.Charsets;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.domain.*;
import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.aws.ec2.predicates.InstanceStateRunning;
import org.jclouds.aws.ec2.predicates.InstanceStateTerminated;
import org.jclouds.aws.ec2.reference.EC2Constants;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.*;
import org.jclouds.domain.Credentials;
import org.jclouds.predicates.RetryablePredicate;
import org.testng.annotations.*;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import java.util.SortedSet;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* Tests the resizing of instance's root EBS device.
*
* This test creates an instance, then resizes its root volume
* (which includes stopping it) and terminates it afterwards.
*
* @author Oleksiy Yarmula
*/
@Test(groups = {"live" }, enabled = true, testName = "ec2.demo.InstanceVolumeManagerLiveTest")
public class InstanceVolumeManagerLiveTest {
private final int NEW_SIZE = 6;
private String tag;
private String secret;
private InstanceVolumeManager manager;
private ComputeService client;
private Template template;
private Predicate<RunningInstance> instanceRunning;
private Predicate<RunningInstance> instanceTerminated;
private RunningInstance instanceCreated;
private Volume volumeAttached;
@BeforeTest
public void setupClient() throws IOException {
//set up the constants needed to run
String user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user");
String password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key");
String secretKeyFile = checkNotNull(System.getProperty("jclouds.test.ssh.keyfile"),
"jclouds.test.ssh.keyfile");
tag = "ec2";
//ended setting up the constants
secret = Files.toString(new File(secretKeyFile), Charsets.UTF_8);
assert secret.startsWith("-----BEGIN RSA PRIVATE KEY-----") : "invalid key:\n" + secret;
//create a new manager
Properties testOnlyProperties = new Properties();
testOnlyProperties.put(EC2Constants.PROPERTY_EC2_AMI_OWNERS, "819060954727");
manager = new InstanceVolumeManager(user, password, testOnlyProperties);
client = manager.getContext().getComputeService();
TemplateBuilder templateBuilder = client.templateBuilder();
template =
templateBuilder.imageId("ami-2675974f").
build();
template.getOptions().installPrivateKey(secret);
instanceRunning =
new RetryablePredicate<RunningInstance>(new InstanceStateRunning(manager.getApi().
getInstanceServices()),
600, 10, TimeUnit.SECONDS);
instanceTerminated =
new RetryablePredicate<RunningInstance>(new InstanceStateTerminated(manager.getApi().
getInstanceServices()),
600, 10, TimeUnit.SECONDS);
}
@Test
void testResizeVolume() {
SortedSet<NodeMetadata> nodes = Sets.newTreeSet(
client.runNodesWithTag(tag, 1, template).values()
);
assert nodes.size() == 1 : "Expected to have 1 nodes created; found: " + nodes.size();
NodeMetadata launchedNode = Iterables.getOnlyElement(nodes);
AvailabilityZone availabilityZone = AvailabilityZone.fromValue(launchedNode.getLocationId());
Region region = getRegionNameForAvailabilityZone(availabilityZone);
instanceCreated = getOnlyInstance(launchedNode.getId(), region);
waitForInstanceInRunningState();
manager.resizeVolume(launchedNode.getId(), region, new Credentials("ubuntu", ""),
secret, NEW_SIZE);
// re-fetch the instance after the resize
// NOTE: this step is essential
instanceCreated = getOnlyInstance(launchedNode.getId(), region);
volumeAttached = manager.getEbsApi().getRootVolumeForInstance(instanceCreated);
checkState(volumeAttached.getSize() == NEW_SIZE,
String.format("The size of the new volume expected: " +
"%d. Found: %d", NEW_SIZE, volumeAttached.getSize()));
}
@AfterTest
public void close() {
manager.getApi().getInstanceServices().terminateInstancesInRegion
(instanceCreated.getRegion(), instanceCreated.getId());
checkState(instanceTerminated.apply(instanceCreated), "" +
/*or throw*/ "Couldn't terminate the instance");
if(volumeAttached != null) {
manager.getApi().getElasticBlockStoreServices().deleteVolumeInRegion
(volumeAttached.getRegion(), volumeAttached.getId());
}
manager.closeContext();
}
/**
* Returns region that has the given availability zone, or null,
* when nothing is found.
* This operates under the assumption that names of availability zones are
* unique, or else it returns the first matched region.
*
* @param zone
* zone that needs to be matched with region. This can not be null.
* @return region
* that has the provided zone
*/
private Region getRegionNameForAvailabilityZone(AvailabilityZone zone) {
for (Region region : ImmutableSet.of(Region.DEFAULT, Region.EU_WEST_1, Region.US_EAST_1,
Region.US_WEST_1)) {
SortedSet<AvailabilityZoneInfo> allResults = Sets.newTreeSet(manager.getApi().
getAvailabilityZoneAndRegionServices()
.describeAvailabilityZonesInRegion(region));
for(AvailabilityZoneInfo zoneInfo : allResults) {
if(zone == zoneInfo.getZone()) return zoneInfo.getRegion();
}
}
return null; /*by contract*/
}
/**
* Blocks until {@link #instanceCreated} transitions
* into 'running' state.
* NOTE: {@link #instanceCreated} can not be null.
*/
private void waitForInstanceInRunningState() {
checkState(instanceRunning.apply(instanceCreated),
/*or throw*/ "Couldn't run the instance");
}
/**
* Retrieves a {@link RunningInstance} object by instanceId and
* region.
*
* @param instanceId
* id of launched instance
* @param region
* region where the instance was launched
* @return corresponding {@link RunningInstance} object
*/
private RunningInstance getOnlyInstance(String instanceId, Region region) {
return Iterables.getOnlyElement(
Iterables.getOnlyElement(
manager.getApi().
getInstanceServices().
describeInstancesInRegion(region, instanceId
)
)
);
}
}