source: server/trunk/admin/lib/Sophie/Cli.pm @ 248

Last change on this file since 248 was 248, checked in by nanardon, 14 years ago
  • don't hardcode admin password in admin tools (it has been changed)
File size: 12.4 KB
Line 
1package Sophie::Cli;
2
3# $Id: Cli.pm 4321 2010-10-26 16:28:15Z nanardon $
4
5use strict;
6use warnings;
7use Term::ReadLine;
8use Text::ParseWords;
9use Getopt::Long;
10use RPC::XML;
11$RPC::XML::FORCE_STRING_ENCODING = 1;
12
13{
14    open (my $fh, "/dev/tty" )
15        or eval 'sub Term::ReadLine::findConsole { ("&STDIN", "&STDERR") }';
16    die $@ if $@;
17    close ($fh);
18}
19
20my $term = Term::ReadLine->new('LA CLI');
21$term->MinLine(99999);
22my $OUT = $term->OUT || \*STDOUT;
23
24sub denv {
25    my ($base, $dist, $vers, $arch) = @_;
26    my $env = __PACKAGE__->new(
27        {
28            prompt => sub {
29                sprintf('%s - %s - %s > ',
30                    $_[0]->{dist}{distribution},
31                    $_[0]->{dist}{release},
32                    $_[0]->{dist}{arch},
33                );
34            },
35            dist => {
36                distribution => $dist,
37                release => $vers,
38                arch => $arch,
39            },
40        },
41        $base,
42    );
43
44    $env->add_func('show', {
45            code => sub {
46                my ($self) = @_;
47
48                my $res = $self->xmlreq('distrib.struct',
49                    $self->{dist},
50                );
51                print $OUT join ('', map { "$_->{label}\n" } @{ $res->value });
52            }
53        }
54    );
55
56    $env->add_func('setpath', {
57            code => sub {
58                my ($self, $media, $path) = @_;
59                my $res = $self->xmlreq('admin.media_path',
60                    $self->{dist},
61                    $media,
62                    $path,
63                );
64            },
65            completion => sub {
66                my ($self, $start, $media) = @_;
67                if ($media) {
68                    my $res = $self->xmlreq('admin.ls_local', $start);
69                    return @{ $res->value };
70                } else {
71                my $res = $self->xmlreq('distrib.struct',
72                    $self->{dist},
73                );
74                return map { $_->{label} } @{ $res->value };
75                }
76            },
77        }
78    );
79    $env->add_func('unsetpath', {
80            code => sub {
81                my ($self, $media, $path) = @_;
82                my $res = $self->xmlreq('admin.media_remove_path',
83                    $self->{dist},
84                    $media,
85                    $path,
86                );
87            },
88            completion => sub {
89                my ($self, $start, $media) = @_;
90                if ($media) {
91                    my $res = $self->xmlreq('admin.list_path',
92                        $self->{dist},
93                        $media,
94                    );
95                    return @{ $res->value };
96                } else {
97                my $res = $self->xmlreq('distrib.struct',
98                    $self->{dist},
99                );
100                return map { $_->{label} } @{ $res->value };
101                }
102            },
103        }
104    );
105    $env->add_func('listpath', {
106            code => sub {
107                my ($self, $media) = @_;
108                my $res = $self->xmlreq('admin.list_path',
109                    $self->{dist},
110                    $media,
111                );
112                print $OUT join ('', map { "$_\n" } @{ $res->value });
113            },
114            completion => sub {
115                my ($self, $start) = @_;
116                my $res = $self->xmlreq('distrib.struct',
117                    $self->{dist},
118                );
119                return map { $_->{label} } @{ $res->value };
120            },
121        }
122    );
123    $env->add_func('replace_path',
124        {
125            help => '',
126            code => sub {
127                my ($self, $path, $newpath) = @_;
128                my $res = $self->xmlreq(
129                    '/admin/replace_path', $path, $newpath);
130                print $OUT join (' ', map { $_ } @{ $res->value });
131                print $OUT "\n";
132            },
133            completion => sub {
134                my ($self, $start, $oldpath) = @_;
135                if ($oldpath) {
136                    my $res = $self->xmlreq('admin.ls_local', $start);
137                    return @{ $res->value };
138                } else {
139                    my $res = $self->xmlreq('admin.list_path',
140                        $self->{dist},
141                    );
142                    return @{ $res->value };
143                }
144            },
145        }
146    );
147    $env->add_func('remove_media',
148        {
149            help => '',
150            code => sub {
151                my ($self, $media) = @_;
152                my $res = $self->xmlreq(
153                    '/admin/remove_media', $self->{dist}, $media);
154                print $OUT join (' ', map { $_ } @{ $res->value });
155                print $OUT "\n";
156            },
157            completion => sub {
158                my ($self) = @_;
159                my $res = $self->xmlreq('distrib.struct',
160                    $self->{dist},
161                );
162                return map { $_->{label} } @{ $res->value };
163            },
164        }
165    );
166    $env->add_func('addmedia', {
167            code => sub {
168                my ($self, $media, $group) = @_;
169                my $res = $self->xmlreq('admin.add_media',$self->{dist},
170                    { label => $media,
171                      group_label => $group },
172                );
173                print $OUT join ('', map { "$_->{label}\n" } @{ $res->value });
174            },
175            completion => sub {
176                my ($self, $start, $label) = @_;
177                my $res = $self->xmlreq('distrib.struct',
178                    $self->{dist},
179                );
180                return ((map { $_->{label} } @{ $res->value }), $label ?
181                    ($label) : ())
182            },
183        }
184    );
185
186
187    $env
188}
189
190sub globalenv {
191    my ($base) = @_;
192    my $env = __PACKAGE__->new({}, $base);
193
194    $env->add_func('list_user',
195        {
196            code => sub {
197                my ($self, $match) = @_;
198                my $res = $self->xmlreq('admin.list_user', $match);
199                if ($res) {
200                    print $OUT (map { "$_\n" } @{ $res->value });
201                }
202            }
203        }
204    );
205    $env->add_func('delete_user',
206        {
207            code => sub {
208                my ($self, $match) = @_;
209                my $res = $self->xmlreq('admin.delete_user', $match);
210                if ($res) {
211                    print $OUT $res->value . "\n";
212                }
213            },
214            completion => sub {
215                my ($self, undef, @args) = @_;
216                my $res = $self->xmlreq(
217                    'admin.list_user');
218                return @{$res->value};
219            },
220        }
221    );
222    $env->add_func('set_password',
223        {
224            code => sub {
225                my ($self, $match, $password) = @_;
226                my $res = $self->xmlreq('admin.set_user_password', $match,
227                    $password);
228                if ($res) {
229                    print $OUT $res->value . "\n";
230                }
231            },
232            completion => sub {
233                my ($self, undef, $user) = @_;
234                if (!$user) {
235                my $res = $self->xmlreq(
236                    'admin.list_user');
237                return @{$res->value};
238                } else { return }
239            },
240        }
241    );
242    $env->add_func('create_user',
243        {
244            code => sub {
245                my ($self, $user, $password) = @_;
246                my $res = $self->xmlreq('admin.create_user', $user, $password);
247                if ($res) {
248                    print $OUT $res->value . "\n";
249                }
250            },
251        }
252    );
253    $env->add_func('select',
254        {
255            code => sub {
256                my ($self, $dist, $ver, $arch) = @_;
257                if (!($dist && $ver && $arch)) {
258                    print $OUT "missing argument\n";
259                    return;
260                }
261                denv($self->base, $dist, $ver, $arch)->cli();
262            },
263            completion => sub {
264                my ($self, undef, @args) = @_;
265                my $res = $self->xmlreq(
266                    'distrib.list', @args);
267                return @{$res->value};
268            },
269        },
270    );
271    $env->add_func('create',
272        {
273            code => sub {
274                my ($self, $dist, $ver, $arch, $label, ) = @_;
275                my $res = $self->xmlreq('admin.create', $dist, $ver, $arch);
276                print $OUT join (' ', map { $_ } @{ $res->value });
277            },
278            completion => sub {
279                my ($self, undef, @args) = @_;
280                my $res = $self->xmlreq(
281                    'distrib.list', @args);
282                return  map { $_ } @{$res->value};
283            },
284        },
285    );
286    $env->add_func('content',
287        { 
288            help => '',
289            code => sub {
290                my ($self, $dist, $ver) = @_;
291                my $res = $self->xmlreq(
292                    'distrib.list', $dist, $ver);
293                print $OUT join (' ', map { $_ } @{ $res->value });
294            },
295            completion => sub {
296                my ($self, undef, @args) = @_;
297                my $res = $self->xmlreq(
298                    'distrib.list', @args);
299                return  map { $_ } @{$res->value};
300            },
301        }
302    );
303
304    $env
305}
306
307sub new {
308    my ($class, $env, $base) = @_;
309    bless($env, $class);
310    $env->{_base} = $base;
311    $env->add_func('quit', { help => 'quit - exit the tool',
312            code => sub { print "\n"; exit(0) }, });
313    $env->add_func('exit', { help => "exit current mode",
314            code => sub { return "EXIT" }, });
315    $env->add_func('help', {
316        help => 'help [command] - print help about command',
317        completion => sub {
318            if (!$_[2]) { return sort keys %{ $_[0]->{funcs} || {}} }
319        },
320        code => sub {
321            my ($self, $name) = @_;
322            if (!$name) {
323                print $OUT join(', ', sort keys %{ $self->{funcs} || {}}) . "\n";
324            } elsif ($self->{funcs}{$name}{alias}) {
325                print $OUT "$name is an alias for " . join(' ',
326                    @{$self->{funcs}{$name}{alias}}) . "\n";
327            } elsif ($self->{funcs}{$name}{help}) {
328                print $OUT $self->{funcs}{$name}{help} . "\n";
329            } else {
330                print $OUT "No help availlable\n";
331            }
332        },
333    });
334
335    $env;
336}
337
338sub base { $_[0]->{_base} }
339
340sub cli {
341    my ($self) = @_;
342    while (1) {
343        $term->Attribs->{completion_function} = sub {
344            $self->complete($_[0], shellwords(substr($_[1], 0, $_[2])));
345        };
346        defined (my $line = $term->readline($self->prompt)) or do {
347            print $OUT "\n";
348            return;
349        };
350        $term->addhistory($line);
351        my $res = $self->run(shellwords($line));
352        $self->rollback;
353        if ($res && $res eq 'EXIT') { print $OUT "\n"; return }
354    }
355}
356
357sub prompt {
358    my ($self) = @_;
359    if (!$self->{prompt}) {
360        return "LA cli > ";
361    } else {
362        $self->{prompt}->($self);
363    }
364}
365
366sub add_func {
367    my ($self, $name, $param) = @_;
368    $self->{funcs}{$name} = $param;
369}
370
371sub getoption {
372    my ($self, $opt, @args) = @_;
373    local @ARGV = @args;
374    Getopt::Long::Configure("pass_through");
375    GetOptions(%{ $opt });
376
377    return @ARGV;
378}
379
380sub parse_arg {
381    my ($self, $name, @args) = @_;
382    return @args;
383}
384
385sub complete {
386    my ($self, $lastw, $name, @args) = @_;
387    if (!$name) {
388        return grep { /^\Q$lastw\E/ } sort
389            (keys %{ $self->{funcs} || {}});
390    } elsif ($self->{funcs}{$name}{alias}) {
391        $self->complete($lastw, @{$self->{funcs}{$name}{alias}}, @args);
392    } elsif ($self->{funcs}{$name}{completion}) {
393        my @res;
394        eval {
395        my @pargs = $self->parse_arg($name, @args);
396        @res = $self->{funcs}{$name}{completion}->($self, $lastw, @pargs);
397        };
398        return map { my $t = $_; $t =~ s/\s/\\ /g; $t } grep { $_ &&
399            /^\Q$lastw\E/ } @res;
400       
401    } else {
402        return ();
403    }
404}
405
406sub run {
407    my ($self, $name, @args) = @_;
408    return if (!$name);
409    if (!exists($self->{funcs}{$name})) {
410        print $OUT "No command $name found\n";
411    } elsif ($self->{funcs}{$name}{alias}) {
412        $self->run(@{$self->{funcs}{$name}{alias}}, @args);
413    } elsif ($self->{funcs}{$name}{code}) {
414        eval {
415        my @pargs = $self->parse_arg($name, @args);
416        $self->{funcs}{$name}{code}->($self, @args);
417        };
418    } else {
419        print $OUT "No command $name found\n";
420    }
421}
422
423sub xmlreq {
424    my ($self, $code, @args) = @_;
425    my $res = $self->base->send_request(
426        $code, @args,
427    );
428}
429
430sub commit {
431    my ($self) = @_;
432}
433
434sub rollback {
435    my ($self) = @_;
436}
437
4381;
Note: See TracBrowser for help on using the repository browser.