def _get_attached_volumes(connection, instance_id=None): """Returns all the volumes that are attached to a particular instance. Alternatively, this function could've been a generator with the yield keyword.""" if not instance_id: instance_id = common.get_self_instance_id() volumes = connection.get_all_volumes() attached_volumes = [] for volume in volumes: if (volume.attach_data.instance_id == instance_id and volume.attachment_state() == "attached"): attached_volumes.append(volume) return attached_volumes
def _email(subject, body): """Sends an email to _EMAIL_RECIPIENT with useful debugging info.""" if not _EMAIL_RECIPIENT: return if not _EMAIL_SENDER: return message_content = "Backup script message!<br/>" message_content += "from instance id {}<br/>--<br/><br/>".format( common.get_self_instance_id()) message_content += body message = email.mime.text.MIMEText(message_content, "html") message['From'] = _EMAIL_SENDER message['Subject'] = subject message['To'] = _EMAIL_RECIPIENT logging.info("Sending email to {}: {}".format(_EMAIL_RECIPIENT, subject)) smtp = smtplib.SMTP("127.0.0.1") smtp.sendmail(_EMAIL_SENDER, [_EMAIL_RECIPIENT], message.as_string()) smtp.quit()
def main(): # pylint: disable=R0914 _log("Running backup script. It is now {}".format( datetime.datetime.now().strftime(_DATETIME_FORMAT))) config = _load_config() connection = common.connect() self_instance_id = common.get_self_instance_id() self_instance = common.get_self_instance(connection) self_instance_name = (self_instance.tags['Name'] if 'Name' in self_instance.tags else self_instance_id) attached_volumes = _get_attached_volumes(connection, self_instance_id) mounted_storages = _get_mounted_storages() freezer = Freezer() for name, rules in config.iteritems(): try: path = rules['path'] if rules['path'] not in mounted_storages: raise Exception("Cannot find mount for {}".format(path)) extra_description = (rules['description'] if 'description' in rules else "") storage = mounted_storages[path] _run_before_commands(rules) volume_ids = [] for device in storage.devices: volume = _get_volume_used_by_device(device, attached_volumes) if not volume: raise Exception("Cannot find volume attached to {}".format( device)) volume_ids.append({ "device": device, "volume_id": volume.id, }) _log("Preparing to back up {}".format(storage.mount_point)) freezer.freeze(storage) for timing_rule in _TIMING_MAP: if timing_rule in rules: snapshots = _get_snapshots(connection, self_instance_name, timing_rule) if len(snapshots): # Check if we already took a recent snapshot for this duration most_recent_dt_string = snapshots[0].tags['Backup-Datetime'] most_recent_dt = datetime.datetime.strptime(most_recent_dt_string, _DATETIME_FORMAT) now_dt = datetime.datetime.now() duration_between_backups = _TIMING_MAP[timing_rule] if now_dt - most_recent_dt < duration_between_backups: _log("Not snapshotting {} / {} because it's too soon.".format( name, timing_rule)) continue for vol_and_device in volume_ids: volume_id = vol_and_device['volume_id'] device = vol_and_device['device'] snap_datetime = datetime.datetime.now().strftime(_DATETIME_FORMAT) full_desc = _build_full_description(name, self_instance_id, snap_datetime, timing_rule, storage, device, extra_description) short_name = " ".join([self_instance_name, timing_rule, device.replace("/dev/", ""), snap_datetime]) _snapshot_volume(connection, volume_id, full_desc, { 'Name': short_name, 'Backup-Datetime': snap_datetime, 'Backup-Device': device, 'Backup-Instance-Name': self_instance_name, 'Backup-Type': timing_rule }) _delete_old_snapshots(connection, snapshots, timing_rule, device, int(rules[timing_rule])) freezer.unfreeze(storage) _run_after_commands(rules) except Exception, err: _error(err) finally: