| 22 | | # this won't work with Term::ReadLine::Perl |
| 23 | | # If there is Term::ReadLine::Gnu, be sure to do : export "PERL_RL=Gnu o=0" |
| 24 | | eval { $term->stifle_history($HISTSIZE);}; |
| 25 | | |
| 26 | | if (@!){ |
| 27 | | $self->{context}->log('debug' => "You will need Term::ReadLine::Gnu"); |
| 28 | | }else{ |
| 29 | | if (-f $HISTFILE) { |
| 30 | | $term->ReadHistory($HISTFILE) or $self->{context}->log('warn' => "cannot read history file: $!"); |
| 31 | | } |
| 32 | | } |
| 33 | | |
| 34 | | while ( defined( my $line = $term->readline('archer> ') )) { |
| | 22 | # this won't work with Term::ReadLine::Perl |
| | 23 | # If there is Term::ReadLine::Gnu, be sure to do : export "PERL_RL=Gnu o=0" |
| | 24 | eval { $term->stifle_history( $HISTSIZE ); }; |
| | 25 | |
| | 26 | if ( @! ) { |
| | 27 | $self->{ context }->log( 'debug' => "You will need Term::ReadLine::Gnu" ); |
| | 28 | } else { |
| | 29 | if ( -f $HISTFILE ) { |
| | 30 | $term->ReadHistory( $HISTFILE ) |
| | 31 | or $self->{ context }->log( 'warn' => "cannot read history file: $!" ); |
| | 32 | } |
| | 33 | } |
| | 34 | |
| | 35 | while ( defined( my $line = $term->readline( 'archer> ' ) ) ) { |
| 53 | | $parallel->use or die $@; |
| 54 | | |
| 55 | | my $manager = $parallel->new; |
| 56 | | $manager->run({ |
| 57 | | elems => $self->{servers}, |
| 58 | | callback => sub { |
| 59 | | my $server = shift; |
| 60 | | $self->callback($server, $cmd); |
| 61 | | }, |
| 62 | | num => $self->{context}->{parallel_num}, |
| 63 | | }); |
| | 55 | $self->{ parallel }->use or die $@; |
| | 56 | |
| | 57 | if ( $cmd =~ /^on/ ) { |
| | 58 | if ( $cmd =~ /^on\s(.*)\sdo\s(.*)$/ ) { |
| | 59 | $self->process_host( $1, $2 ); |
| | 60 | } else { |
| | 61 | print "[WARNING] error in your syntax, see help\n"; |
| | 62 | } |
| | 63 | } elsif ( $cmd =~ /^with/ ) { |
| | 64 | if ( $cmd =~ /^with\s(.*)\sdo\s(.*)$/ ) { |
| | 65 | $self->process_role( $1, $2 ); |
| | 66 | } else { |
| | 67 | print "[WARNING] error in your syntax, see help\n"; |
| | 68 | } |
| | 69 | } elsif ( $cmd =~ /^help/ ) { |
| | 70 | $self->help(); |
| | 71 | } elsif ( $cmd =~ /^(quit|exit)/ ) { |
| | 72 | print "bye bye\n"; |
| | 73 | exit; |
| | 74 | } elsif ( $cmd =~ /^!/ ) { |
| | 75 | if ( $cmd =~ /^!(\w+)\s?(on|with)?\s?(.*)?$/ ) { |
| | 76 | my $task = $1; |
| | 77 | my $action = $2; |
| | 78 | my $machines = $3; |
| | 79 | if ( defined $action |
| | 80 | && defined $machines |
| | 81 | && length( $machines ) < 1 ) |
| | 82 | { |
| | 83 | return print "[WARNING] error in your syntax, see help\n"; |
| | 84 | } |
| | 85 | my $executed = 0; |
| | 86 | for |
| | 87 | my $plugin ( @{ $self->{ config }->{ tasks }->{ process } } ) |
| | 88 | { |
| | 89 | next if $plugin->{ name } ne $task; |
| | 90 | $executed = 1; |
| | 91 | if ( defined $action ) { |
| | 92 | if ( $action eq "on" ) { |
| | 93 | my @hosts = split " ", $machines; |
| | 94 | for my $host ( @hosts ) { |
| | 95 | $self->process_task( $plugin, $host ); |
| | 96 | } |
| | 97 | } else { |
| | 98 | my @roles = split " ", $machines; |
| | 99 | for my $role ( @roles ) { |
| | 100 | for my $host ( |
| | 101 | @{ $self->{ servers }->{ $role } } ) |
| | 102 | { |
| | 103 | $self->process_task( $plugin, $host ); |
| | 104 | } |
| | 105 | } |
| | 106 | } |
| | 107 | } else { |
| | 108 | for my $host ( |
| | 109 | @{ $self->{ servers } |
| | 110 | ->{ $plugin->{ config }->{ role } } |
| | 111 | } ) |
| | 112 | { |
| | 113 | $self->process_task( $plugin, $host ); |
| | 114 | } |
| | 115 | } |
| | 116 | } |
| | 117 | if ( $executed == 0 ) { |
| | 118 | print "[WARNING] unable to find the requested task: $task\n"; |
| | 119 | } |
| | 120 | } else { |
| | 121 | print "[WARNING] error in your syntax\n"; |
| | 122 | } |
| | 123 | } else { |
| | 124 | $self->process_command( $cmd ); |
| | 125 | } |
| | 126 | } |
| | 127 | |
| | 128 | sub process_host { |
| | 129 | my ( $self, $hosts, $cmd ) = @_; |
| | 130 | |
| | 131 | my @hosts = split /\s/, $hosts; |
| | 132 | |
| | 133 | # check if hosts are in our config. |
| | 134 | for my $host ( @hosts ) { |
| | 135 | for my $role ( keys %{ $self->{ servers } } ) { |
| | 136 | @hosts = grep ( /$host/, @{ $self->{ servers }->{ $role } } ); |
| | 137 | } |
| | 138 | } |
| | 139 | |
| | 140 | if ( @hosts ) { |
| | 141 | $self->process_command( $cmd, \@hosts ); |
| | 142 | } |
| | 143 | } |
| | 144 | |
| | 145 | sub process_role { |
| | 146 | my ( $self, $roles, $cmd ) = @_; |
| | 147 | |
| | 148 | my @roles = split /\s/, $roles; |
| | 149 | my @hosts = (); |
| | 150 | my @inexistant = (); |
| | 151 | for my $role ( @roles ) { |
| | 152 | if ( !defined $self->{ servers }->{ $role } ) { |
| | 153 | push( @inexistant, $role ); |
| | 154 | next; |
| | 155 | } |
| | 156 | for my $host ( @{ $self->{ servers }->{ $role } } ) { |
| | 157 | push @hosts, $host; |
| | 158 | } |
| | 159 | } |
| | 160 | if ( @inexistant ) { |
| | 161 | print "[WARNING] inexisting role(s) for " |
| | 162 | . join( ' ', @inexistant ) . "\n"; |
| | 163 | } |
| | 164 | $self->process_command( $cmd, \@hosts ); |
| | 165 | } |
| | 166 | |
| | 167 | sub process_command { |
| | 168 | my ( $self, $cmd, $hosts ) = @_; |
| | 169 | my $manager = $self->{parallel}->new; |
| | 170 | |
| | 171 | if ( !$hosts ) { |
| | 172 | for my $role ( |
| | 173 | keys |
| | 174 | %{ $self->{config}->{projects}->{ $self->{context}->{project} } } |
| | 175 | ) |
| | 176 | { |
| | 177 | for my $host ( |
| | 178 | @{ $self->{config}->{projects} |
| | 179 | ->{ $self->{context}->{project} }->{$role} |
| | 180 | } |
| | 181 | ) |
| | 182 | { |
| | 183 | push @{$hosts}, $host; |
| | 184 | } |
| | 185 | } |
| | 186 | } |
| | 187 | |
| | 188 | $manager->run( |
| | 189 | { elems => $hosts, |
| | 190 | callback => sub { |
| | 191 | my $server = shift; |
| | 192 | $self->callback( $server, $cmd ); |
| | 193 | }, |
| | 194 | num => $self->{context}->{parallel_num}, |
| | 195 | } |
| | 196 | ); |
| | 197 | } |
| | 198 | |
| | 199 | sub process_task { |
| | 200 | my ( $self, $plugin, $host ) = @_; |
| | 201 | my $class = "Archer::Plugin::$plugin->{module}"; |
| | 202 | $class->use or die $@; |
| | 203 | $class->new( { config => $plugin->{ config }, |
| | 204 | project => $self->{ context }->{ project }, |
| | 205 | server => $host |
| | 206 | } )->run( $self->{ context } ); |
| | 221 | sub help { |
| | 222 | my ( $self ) = @_; |
| | 223 | my $help = <<HELP; |
| | 224 | To quit, just type quit, exit, or press ctrl-D. |
| | 225 | This shell is still experimental. |
| | 226 | |
| | 227 | execute a command on all servers, just type it directly, like: |
| | 228 | |
| | 229 | archer> ping |
| | 230 | |
| | 231 | To execute a command on a specific set of servers, specify an 'on' clause. |
| | 232 | Note that if you specify more than one host name, they must be |
| | 233 | space-delimited. |
| | 234 | |
| | 235 | archer> on app1.foo.com app2.foo.com do ping |
| | 236 | |
| | 237 | To execute a command on all servers matching a set of roles: |
| | 238 | |
| | 239 | archer> with web db do ping |
| | 240 | |
| | 241 | To execute an Archer task, prefix the name with a bang, by default it |
| | 242 | will be executed only on the role applyed to this task. |
| | 243 | |
| | 244 | archer> !restart |
| | 245 | |
| | 246 | To execute an Archer task on a specific set of servers: |
| | 247 | |
| | 248 | archer> !restart on app1.foo.com app2.foo.com |
| | 249 | |
| | 250 | To execute an Archer task on all servers matching a set of roles: |
| | 251 | |
| | 252 | archer> !restart with web db |
| | 253 | |
| | 254 | HELP |
| | 255 | print $help; |
| | 256 | } |
| | 257 | |