1
# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
3
# Permission is hereby granted, free of charge, to any person obtaining a
4
# copy of this software and associated documentation files (the
5
# "Software"), to deal in the Software without restriction, including
6
# without limitation the rights to use, copy, modify, merge, publish, dis-
7
# tribute, sublicense, and/or sell copies of the Software, and to permit
8
# persons to whom the Software is furnished to do so, subject to the fol-
11
# The above copyright notice and this permission notice shall be included
12
# in all copies or substantial portions of the Software.
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
Automated installer to attach, format and mount an EBS volume.
24
This installer assumes that you want the volume formatted as
25
an XFS file system. To drive this installer, you need the
26
following section in the boto config passed to the new instance.
27
You also need to install dateutil by listing python-dateutil
28
in the list of packages to be installed in the Pyami seciont
29
of your boto config file.
31
If there is already a device mounted at the specified mount point,
32
the installer assumes that it is the ephemeral drive and unmounts
33
it, remounts it as /tmp and chmods it to 777.
38
volume_id = <the id of the EBS volume, should look like vol-xxxxxxxx>
39
logical_volume_name = <the name of the logical volume that contaings
40
a reference to the physical volume to be mounted. If this parameter
41
is supplied, it overrides the volume_id setting.>
42
device = <the linux device the EBS volume should be mounted on>
43
mount_point = <directory to mount device, defaults to /ebs>
47
from boto.manage.volume import Volume
49
from boto.pyami.installers.ubuntu.installer import Installer
50
from string import Template
52
BackupScriptTemplate = """#!/usr/bin/env python
55
from boto.pyami.scriptbase import ScriptBase
58
class Backup(ScriptBase):
62
ec2 = boto.connect_ec2()
63
self.run("/usr/sbin/xfs_freeze -f ${mount_point}")
64
snapshot = ec2.create_snapshot('${volume_id}')
65
boto.log.info("Snapshot created: %s " % snapshot)
67
self.notify(subject="${instance_id} Backup Failed", body=traceback.format_exc())
68
boto.log.info("Snapshot created: ${volume_id}")
70
self.notify(subject="${instance_id} Backup Failed", body=traceback.format_exc())
72
self.run("/usr/sbin/xfs_freeze -u ${mount_point}")
74
if __name__ == "__main__":
79
BackupCleanupScript= """#!/usr/bin/env python
81
from boto.manage.volume import Volume
83
# Cleans Backups of EBS volumes
85
for v in Volume.all():
86
v.trim_snapshots(True)
89
class EBSInstaller(Installer):
94
def __init__(self, config_file=None):
95
Installer.__init__(self, config_file)
96
self.instance_id = boto.config.get('Instance', 'instance-id')
97
self.device = boto.config.get('EBS', 'device', '/dev/sdp')
98
self.volume_id = boto.config.get('EBS', 'volume_id')
99
self.logical_volume_name = boto.config.get('EBS', 'logical_volume_name')
100
self.mount_point = boto.config.get('EBS', 'mount_point', '/ebs')
103
ec2 = boto.connect_ec2()
104
if self.logical_volume_name:
105
# if a logical volume was specified, override the specified volume_id
106
# (if there was one) with the current AWS volume for the logical volume:
107
logical_volume = Volume.find(name = self.logical_volume_name).next()
108
self.volume_id = logical_volume._volume_id
109
volume = ec2.get_all_volumes([self.volume_id])[0]
110
# wait for the volume to be available. The volume may still be being created
112
while volume.update() != 'available':
113
boto.log.info('Volume %s not yet available. Current status = %s.' % (volume.id, volume.status))
115
ec2.attach_volume(self.volume_id, self.instance_id, self.device)
116
# now wait for the volume device to appear
117
while not os.path.exists(self.device):
118
boto.log.info('%s still does not exist, waiting 10 seconds' % self.device)
122
boto.log.info('make_fs...')
123
has_fs = self.run('fsck %s' % self.device)
125
self.run('mkfs -t xfs %s' % self.device)
127
def create_backup_script(self):
128
t = Template(BackupScriptTemplate)
129
s = t.substitute(volume_id=self.volume_id, instance_id=self.instance_id,
130
mount_point=self.mount_point)
131
fp = open('/usr/local/bin/ebs_backup', 'w')
134
self.run('chmod +x /usr/local/bin/ebs_backup')
136
def create_backup_cleanup_script(self):
137
fp = open('/usr/local/bin/ebs_backup_cleanup', 'w')
138
fp.write(BackupCleanupScript)
140
self.run('chmod +x /usr/local/bin/ebs_backup_cleanup')
142
def handle_mount_point(self):
143
boto.log.info('handle_mount_point')
144
if not os.path.isdir(self.mount_point):
145
boto.log.info('making directory')
146
# mount directory doesn't exist so create it
147
self.run("mkdir %s" % self.mount_point)
149
boto.log.info('directory exists already')
151
lines = self.last_command.output.split('\n')
154
if t and t[2] == self.mount_point:
155
# something is already mounted at the mount point
156
# unmount that and mount it as /tmp
157
if t[0] != self.device:
158
self.run('umount %s' % self.mount_point)
159
self.run('mount %s /tmp' % t[0])
160
self.run('chmod 777 /tmp')
162
# Mount up our new EBS volume onto mount_point
163
self.run("mount %s %s" % (self.device, self.mount_point))
164
self.run('xfs_growfs %s' % self.mount_point)
166
def update_fstab(self):
167
f = open("/etc/fstab", "a")
168
f.write('%s\t%s\txfs\tdefaults 0 0\n' % (self.mount_point, self.device))
172
# First, find and attach the volume
175
# Install the xfs tools
176
self.run('apt-get -y install xfsprogs xfsdump')
178
# Check to see if the filesystem was created or not
181
# create the /ebs directory for mounting
182
self.handle_mount_point()
184
# create the backup script
185
self.create_backup_script()
187
# Set up the backup script
188
minute = boto.config.get('EBS', 'backup_cron_minute', '0')
189
hour = boto.config.get('EBS', 'backup_cron_hour', '4,16')
190
self.add_cron("ebs_backup", "/usr/local/bin/ebs_backup", minute=minute, hour=hour)
192
# Set up the backup cleanup script
193
minute = boto.config.get('EBS', 'backup_cleanup_cron_minute')
194
hour = boto.config.get('EBS', 'backup_cleanup_cron_hour')
195
if (minute != None) and (hour != None):
196
self.create_backup_cleanup_script();
197
self.add_cron("ebs_backup_cleanup", "/usr/local/bin/ebs_backup_cleanup", minute=minute, hour=hour)
203
if not os.path.exists(self.device):
206
boto.log.info("Device %s is already attached, skipping EBS Installer" % self.device)