A few years ago, I found myself attaching volumes to instances with some frequency. The volume often came from a snapshot which contained some test data. Like any lazy programmer, I didn’t want to do this work over and over again! I wrote this little utility which would examine the user data and mount a pre-existing volume, or create a new volume from a snapshot and attach that. Here’s the code;
import java.io.IOException; import java.util.List; import java.util.StringTokenizer; import com.xerox.amazonws.ec2.AttachmentInfo; import com.xerox.amazonws.ec2.EC2Exception; import com.xerox.amazonws.ec2.EC2Utils; import com.xerox.amazonws.ec2.Jec2; import com.xerox.amazonws.ec2.VolumeInfo; public class AttachVolume { public static void main(String [] args) { try { String userData = EC2Utils.getInstanceUserdata(); StringTokenizer st = new StringTokenizer(userData); String accessId = st.nextToken(); String secretKey = st.nextToken(); String volumeOrSnapId = st.nextToken(); Jec2 ec2 = new Jec2(accessId, secretKey); String volumeId = null; if (volumeOrSnapId.startsWith("snap-")) { String zone = EC2Utils.getInstanceMetadata("placement/availability-zone"); // create volume from snapshot and wait VolumeInfo vinf = ec2.createVolume(null, volumeOrSnapId, zone); volumeId = vinf.getVolumeId(); List<VolumeInfo> vols = ec2.describeVolumes(new String [] {volumeId}); while (!vols.get(0).getStatus().equals("available")) { System.out.println(vols.get(0).getStatus()); try { Thread.sleep(2); } catch (InterruptedException ex) {} vols = ec2.describeVolumes(new String [] {volumeId}); } } if (volumeOrSnapId.startsWith("vol-")) { volumeId = volumeOrSnapId; } // attach volume and wait String instanceId = EC2Utils.getInstanceMetadata("instance-id"); ec2.attachVolume(volumeId, instanceId, "/dev/sdh"); List<VolumeInfo> vols = ec2.describeVolumes(new String [] {volumeId}); while (!vols.get(0).getAttachmentInfo().get(0).getStatus().equals("attached")) { System.out.println(vols.get(0).getAttachmentInfo().get(0).getStatus()); try { Thread.sleep(2); } catch (InterruptedException ex) {} vols = ec2.describeVolumes(new String [] {volumeId}); } } catch (Exception ex) { System.err.println("Couldn't complete the attach : "+ex.getMessage()); ex.printStackTrace(); System.exit(-1); } } }
Requirements
- Java Runtime Environment (1.5 or greater)
- Typica + and it’s dependencies
- This utility (compiled)
A Few Words About the Code
The first thing you’ll notice is that user data is being parsed. The expectations are that the following items are passed via user data;
- access id – AWS Access Id
- secret key – AWS Secret Key
- volumeOrSnapId – either a volume ID or snapshot ID
The code inspects the last parameter to see if it is a snapshot id. If so, it creates a volume and waits for it to become “available”. One that’s done, it gets the instance ID from meta data and attaches the volume at a hard-coded device. (obviously, this could be in user data which is an exercise I’ll leave to the reader)
On a linux machines, I’d often call this from the /etc/rc.local script. I should also note that this works just as well with Eucalyptus due to its API fidelity with Amazon EC2
There you have it!