Die, Confess! Or the other way around?
[p5sagit/Oyster.git] / lib / Oyster / Provision / Rackspace.pm
1 package Oyster::Provision::Rackspace;
2 use Moose::Role;
3 use Net::RackSpace::CloudServers;
4 use Net::RackSpace::CloudServers::Server;
5 use MIME::Base64;
6
7 requires 'config';
8
9 has 'api_username' => ( is => 'ro', isa => 'Str', required => 1, lazy_build => 1);
10 sub _build_api_username {
11     return $ENV{CLOUDSERVERS_USER} if exists $ENV{CLOUDSERVERS_USER};
12     die "Need api_username or CLOUDSERVERS_USER in environment";
13 }
14
15 has 'api_password' => ( is => 'ro', isa => 'Str', required => 1, lazy_build => 1);
16 sub _build_api_password {
17     return $ENV{CLOUDSERVERS_KEY} if exists $ENV{CLOUDSERVERS_KEY};
18     die "Need api_password or CLOUDSERVERS_KEY in environment";
19 }
20
21 has '_rs' => ( is => 'rw', isa => 'Net::RackSpace::CloudServers', default => sub {
22     my $self = shift;
23     my $rs = Net::RackSpace::CloudServers->new(
24         user => $self->api_username,
25         key  => $self->api_password,
26     );
27     $rs;
28 });
29
30 sub create {
31    my $self = shift;
32
33    # Do nothing if the server named $self->name already exists
34    return if scalar grep { $_->name eq $self->name } $self->_rs->get_server();
35
36    # Check the ssh pub key exists and is <10K
37    die "SSH pubkey needs to exist" if !-f $self->pub_ssh;
38    my $pub_ssh = do {
39        local $/=undef;
40        open my $fh, '<', $self->pub_ssh or die "Cannot open ", $self->pub_ssh, ": $!";
41        my $_data = <$fh>;
42        close $fh or die "Cannot close ", $self->pub_ssh, ": $!";
43        $_data;
44    };
45    die "SSH pubkey needs to be < 10KiB" if length $pub_ssh > 10*1024;
46
47    # Build the server
48    my $server = Net::RackSpace::CloudServers::Server->new(
49       cloudservers => $self->_rs,
50       name         => $self->name,
51       flavorid     => $self->size,
52       imageid      => $self->image,
53       personality => [
54            {
55                path     => '/root/.ssh/authorized_keys',
56                contents => encode_base64($pub_ssh),
57            },
58       ],
59    );
60    my $newserver = $server->create_server;
61    warn "Server root password: ", $newserver->adminpass, "\n";
62
63    do {
64       $|=1;
65       my @tmpservers = $self->_rs->get_server_detail();
66       $server = ( grep { $_->name eq $self->name } @tmpservers )[0];
67       print "\rServer status: ", ($server->status || '?'), " progress: ", ($server->progress || '?');
68       if ( ( $server->status // '' ) ne 'ACTIVE' ) {
69         print " sleeping..";
70         sleep 2;
71       }
72    } while ( ( $server->status // '' ) ne 'ACTIVE' );
73
74    warn "Server public IP is: @{$server->public_address}\n";
75
76    # Connect to server and execute installation routines?
77    # Use Net::SSH?
78 }
79
80 sub delete {
81    my $self = shift;
82
83    # Die if the server named $self->name already exists
84    my ($server) = grep { $_->name eq $self->name } $self->_rs->get_server();
85    die "No such server: ", $self->name if !$server;
86
87    # Goodbye cruel user!
88    $server->delete_server();
89 }
90
91 sub resize {
92    my $self = shift;
93
94    $self->config();
95 }
96
97 1;
98
99 __END__
100
101 =head1 NAME
102
103 Oyster::Provision::Rackspace -- Provision your Oyster on Rackspace
104
105 =head1 SYNOPSIS
106
107 Use the Rackspace backend on your Oyster configuration file
108
109 =head1 REQUIRED PARAMETERS
110
111 The following are required to instantiate a backend:
112
113 =over
114
115 =item api_username
116
117 The rackspace API username, or C<$ENV{RACKSPACE_USER}> will be used if that is
118 not given
119
120 =item password
121
122 This is your rackspace API Key
123
124 The rackspace API key, or C<$ENV{RACKSPACE_KEY}> will be used if that is not
125 given
126
127 =item name
128
129 The name of your new/existing rackspace server.
130
131 =item size
132
133 The size ID of the rackspace server you want to create.
134 Use the following incantation to see them:
135
136     perl -MNet::RackSpace::CloudServers -e'
137         $r=Net::RackSpace::CloudServers->new(
138             user=>$ENV{CLOUDSERVERS_USER},
139             key=>$ENV{CLOUDSERVERS_KEY},
140         );
141         print map
142             { "id $_->{id} ram $_->{ram} disk $_->{disk}\n" }
143             $r->get_flavor_detail
144     '
145     id 1 ram 256 disk 10
146     id 2 ram 512 disk 20
147     id 3 ram 1024 disk 40
148     id 4 ram 2048 disk 80
149     id 5 ram 4096 disk 160
150     id 6 ram 8192 disk 320
151     id 7 ram 15872 disk 620
152
153 =item image
154
155 The image ID of the rackspace server you want to create.
156 Use the following incantation to see them:
157
158     perl -MNet::RackSpace::CloudServers -e'
159         $r=Net::RackSpace::CloudServers->new(
160             user=>$ENV{CLOUDSERVERS_USER},
161             key=>$ENV{CLOUDSERVERS_KEY},
162         );
163         print map
164             { "id $_->{id} name $_->{name}\n" }
165             $r->get_image_detail
166     '
167     id 29 name Windows Server 2003 R2 SP2 x86
168     id 69 name Ubuntu 10.10 (maverick)
169     id 41 name Oracle EL JeOS Release 5 Update 3
170     id 40 name Oracle EL Server Release 5 Update 4
171     id 187811 name CentOS 5.4
172     id 4 name Debian 5.0 (lenny)
173     id 10 name Ubuntu 8.04.2 LTS (hardy)
174     id 23 name Windows Server 2003 R2 SP2 x64
175     id 24 name Windows Server 2008 SP2 x64
176     id 49 name Ubuntu 10.04 LTS (lucid)
177     id 14362 name Ubuntu 9.10 (karmic)
178     id 62 name Red Hat Enterprise Linux 5.5
179     id 53 name Fedora 13
180     id 17 name Fedora 12
181     id 71 name Fedora 14
182     id 31 name Windows Server 2008 SP2 x86
183     id 51 name CentOS 5.5
184     id 14 name Red Hat Enterprise Linux 5.4
185     id 19 name Gentoo 10.1
186     id 28 name Windows Server 2008 R2 x64
187     id 55 name Arch 2010.05
188
189 Oyster only supports Linux images, specifically
190 Ubuntu 10.10 (maverick).
191
192 =item pub_ssh
193
194 The public ssh key you would like copied to the
195 new server's C</root/.ssh/authorized_keys> file
196 to allow you to ssh in the box without providing
197 a root password.
198
199 =back
200
201 =cut