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