Created
February 6, 2016 13:16
-
-
Save okura3/290b9c3928477a25f1c0 to your computer and use it in GitHub Desktop.
さくらのクラウドディスクアーカイブスクリプト
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!env perl | |
# sacloud_archive.pl | |
# さくらのクラウドのディスクをアーカイブする | |
# アーカイブファイル名は、「ディスク名-YYYYMMDDThhmmss」とする。 | |
# $KEEP_ARCHIVES 世代をアーカイブとして保存する。 | |
# TODO アーカイブするのを自サーバのディスクだけに制限する。 | |
use strict; | |
use warnings; | |
use Mojo::UserAgent; | |
use Mojo::Log; | |
use Mojo::Util qw/dumper/; | |
use POSIX qw/strftime/; | |
my $LOGGER = Mojo::Log->new; | |
my $KEEP_ARCHIVES = 1; | |
my ( $SACLOUD_TOKEN, $SACLOUD_SECRET, $SACLOUD_ZONE ) | |
= @ENV{qw/SACLOUD_TOKEN SACLOUD_SECRET SACLOUD_ZONE/}; | |
my ( $SLEEP_TIME, $MAX_SLEEP_TIME ) = ( 30, 3600 ); | |
my $SAKURA_API_BASE | |
= 'https://' | |
. $SACLOUD_TOKEN . ':' | |
. $SACLOUD_SECRET | |
. '@secure.sakura.ad.jp/cloud/zone/' | |
. $SACLOUD_ZONE | |
. '/api/cloud/1.1/'; | |
if ( !defined $SACLOUD_TOKEN or !defined $SACLOUD_SECRET ) { | |
$LOGGER->fatal('SACLOUD_TOKEN and SACLOUD_SECRET are required.'); | |
exit 1; | |
} | |
if ( !defined $SACLOUD_ZONE ) { | |
$SACLOUD_ZONE //= 'tk1v'; | |
$LOGGER->warn('SACLOUD_ZONE does not defined. Assume tk1v sandbox'); | |
} | |
my $UA = Mojo::UserAgent->new; | |
my $disks = sacloud_disks(); | |
$LOGGER->debug( dumper( +{ line => __LINE__, disks => $disks } ) ); | |
foreach my $disk (@$disks) { | |
my $datetime = datetime(); | |
my ( $disk_id, $disk_name ) = @$disk{qw/id name/}; | |
$LOGGER->info("sacloud_archive_create $disk_id($disk_name)"); | |
my $archive = sacloud_archive_create( | |
+{ name => "${disk_name}-$datetime", | |
description => "DAILY-BACKUP", | |
sourcedisk_id => $disk_id | |
} | |
); | |
$LOGGER->debug( dumper( +{ line => __LINE__, archive => $archive } ) ); | |
my $availability = sacloud_archive_waiting( $archive->{id} ); | |
if ( $availability eq 'available' ) { | |
$LOGGER->info("sacloud_archive_create available $archive->{id}"); | |
my $archives = sacloud_archives_owned_by_user(); | |
my @sorted_archives = sort { $b->{name} cmp $a->{name} } | |
grep { $_->{name} =~ m/\A${disk_name}\-\d{8}T\d{6}\z/ } @$archives; | |
foreach my $old_archive ( | |
@sorted_archives[ 0 .. ( $#sorted_archives - $KEEP_ARCHIVES ) ] ) | |
{ | |
$LOGGER->info("sacloud_archive_delete $old_archive->{id}"); | |
my $archive_delete = sacloud_archive_delete( $old_archive->{id} ); | |
} | |
} | |
elsif ( $availability eq 'failed' ) { | |
$LOGGER->error("sacloud_archive_create failed $archive->{id}"); | |
} | |
else { | |
$LOGGER->error( | |
"sacloud_archive_create timeout($availability) $archive->{id}"); | |
} | |
} | |
exit; | |
sub datetime { | |
return strftime( "%Y%m%dT%H%M%S", localtime ); # YYYYMMDDhhmmss | |
} | |
sub sacloud_archive_create { | |
my ($params) = @_; | |
my $archive_cmd = +{ | |
Archive => +{ | |
Name => $params->{name}, | |
Description => $params->{description}, | |
SourceDisk => +{ ID => $params->{sourcedisk_id} } | |
} | |
}; | |
my $outer_tx; | |
$UA->post( | |
"$SAKURA_API_BASE/archive" => json => $archive_cmd => sub { | |
my ( $ua, $tx ) = @_; | |
$outer_tx = $tx; | |
} | |
); | |
Mojo::IOLoop->start unless Mojo::IOLoop->is_running; | |
my $json = $outer_tx->res->json; | |
$LOGGER->debug( dumper( +{ line => __LINE__, json => $json } ) ); | |
return +{ id => $json->{Archive}{ID}, name => $json->{Archive}{Name} }; | |
} | |
sub sacloud_archive_delete { | |
my ($archiveid) = @_; | |
my $delete | |
= $UA->delete("$SAKURA_API_BASE/archive/$archiveid")->res->json; | |
$LOGGER->debug( dumper( +{ line => __LINE__, delete => $delete } ) ); | |
return $delete; | |
} | |
sub sacloud_archive_waiting { | |
my ($id) = @_; | |
my $archive_availability; | |
my $sleep = 0; | |
while ( $sleep < $MAX_SLEEP_TIME ) { | |
my $archive = $UA->get("$SAKURA_API_BASE/archive/$id")->res->json; | |
$LOGGER->debug( | |
dumper( +{ line => __LINE__, archive => $archive } ) ); | |
$archive_availability = $archive->{Archive}{Availability}; | |
last if grep { $archive_availability eq $_ } (qw/available failed/); | |
my ( $max, $min ) | |
= @{ $archive->{Archive}{JobStatus}{Delays}{Finish} } | |
{qw/Max Min/}; | |
my $min_sleep = ( $min < $SLEEP_TIME ) ? $min : $SLEEP_TIME; | |
$LOGGER->info("sacloud_archive_waiting sleep $min_sleep sec $id"); | |
sleep $min_sleep; | |
$sleep += $min_sleep; | |
} | |
$LOGGER->error("ARCHIVE ID $id FAILED") | |
if $archive_availability eq 'failed'; | |
$LOGGER->error("ARCHIVE ID $id TOO LONG WAIT($MAX_SLEEP_TIME sec)") | |
if $sleep >= $MAX_SLEEP_TIME; | |
return $archive_availability; | |
} | |
sub sacloud_archives_owned_by_user { | |
my $archives = $UA->get("$SAKURA_API_BASE/archive")->res->json; | |
my @archives = map { +{ id => $_->{ID}, name => $_->{Name} } } | |
grep { $_->{Scope} eq 'user' } @{ $archives->{Archives} }; | |
$LOGGER->debug( dumper( +{ line => __LINE__, archives => \@archives } ) ); | |
return [ | |
map { +{ id => $_->{ID}, name => $_->{Name} } } | |
grep { $_->{Scope} eq 'user' } @{ $archives->{Archives} } | |
]; | |
} | |
sub sacloud_disk_create { | |
my ($params) = @_; | |
my $disk_cmd = +{ | |
Disk => { | |
Name => $params->{name}, | |
Description => $params->{description}, | |
} | |
}; | |
$disk_cmd->{Disk}{SourceDisk}{ID} = $params->{sourcedisk_id} | |
if defined $params->{sourcedisk_id}; | |
$disk_cmd->{Disk}{Plan}{ID} = $params->{plan_id} | |
if defined $params->{plan_id}; | |
$disk_cmd->{Disk}{SizeMB} = $params->{sizemb} | |
if defined $params->{sizemb}; | |
$disk_cmd->{Disk}{Connection} = $params->{connection} | |
if defined $params->{connection}; | |
my $outer_tx; | |
$UA->post( | |
"$SAKURA_API_BASE/disk" => json => $disk_cmd => sub { | |
my ( $ua, $tx ) = @_; | |
$outer_tx = $tx; | |
} | |
); | |
Mojo::IOLoop->start unless Mojo::IOLoop->is_running; | |
my $json = $outer_tx->res->json; | |
return +{ id => $json->{Disk}{ID}, name => $json->{Disk}{Name} }; | |
} | |
sub sacloud_disk_waiting { | |
my ($id) = @_; | |
my ( $SLEEP_TIME, $MAX_SLEEP_TIME ) = ( 30, 3600 ); | |
my $disk_availability; | |
my $sleep = 0; | |
while ( $sleep < $MAX_SLEEP_TIME ) { | |
my $disk = $UA->get("$SAKURA_API_BASE/disk/$id")->res->json; | |
$LOGGER->debug( dumper( +{ line => __LINE__, disk => $disk } ) ); | |
$disk_availability = $disk->{Availability}; | |
last if grep { $disk_availability eq $_ } (qw/available failed/); | |
$LOGGER->info("sacloud_disk_waiting sleep $id"); | |
sleep $SLEEP_TIME; | |
$sleep += $SLEEP_TIME; | |
} | |
$LOGGER->error("DISK ID $id FAILED") if $disk_availability eq 'failed'; | |
$LOGGER->error("DISK ID $id TOO LONG WAIT($MAX_SLEEP_TIME sec)") | |
if $sleep >= $MAX_SLEEP_TIME; | |
return $disk_availability; | |
} | |
sub sacloud_disks { | |
my $disks = $UA->get("$SAKURA_API_BASE/disk")->res->json; | |
return [ map { +{ id => $_->{ID}, name => $_->{Name} } } | |
@{ $disks->{Disks} } ]; | |
} | |
__END__ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment