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