source: LATMOS-Accounts/lib/LATMOS/Accounts/Cli.pm @ 851

Last change on this file since 851 was 851, checked in by nanardon, 14 years ago
  • add primary subcommand to user/group()
  • Property svn:keywords set to Id
File size: 17.6 KB
Line 
1package LATMOS::Accounts::Cli;
2
3# $Id$
4
5use strict;
6use warnings;
7use LATMOS::Accounts::Log;
8use LATMOS::Accounts::Utils;
9use Term::ReadLine;
10use Text::ParseWords;
11
12my $term = Term::ReadLine->new('LA CLI');
13my $OUT = $term->OUT || \*STDOUT;
14
15sub globalenv {
16    my ($labase) = @_;
17    my $env = LATMOS::Accounts::Cli->new({ prompt => sub { $_[0]->base->label . " cli > " }, },
18        $labase);
19    $env->add_func('unexported', {
20        help => 'unexported yes|no|show - switch or show base mode regarding' .
21            ' unexported objects',
22        completion => sub {
23            if (!$_[2]) {
24                return qw(yes no show);
25            }
26        },
27        code => sub {
28            my ($env, $arg) = @_;
29            if ($arg eq 'yes') {
30                $env->base->unexported(1);
31                print $OUT "Unexported are now show";
32            } elsif ($arg eq 'no') {
33                $env->base->unexported(0);
34                print $OUT "Unexported are no longer show";
35            } elsif ($arg eq 'show') {
36                print $OUT "Unexported objects " . $env->base->unexported ?
37                "enable" : "disable";
38            } else {
39                print $OUT "wrong argument";
40            }
41        },
42    });
43    $env->add_func('ls', {
44            help => 'ls object_type - list object of type object_type', 
45            completion => sub {
46                if(!$_[2]) {
47                    return $_[0]->base->list_supported_objects
48                } else { () }
49            },
50            code => sub {
51                if ($_[1]) {
52                    print $OUT map { "$_\n" } $_[0]->base->list_objects($_[1]);
53                } else {
54                    print $OUT "Object type missing";
55                }
56            },
57        });
58    $env->add_func('search', {
59            help => 'search objecttype filter1 [filter2...] - search object according filter',
60            completion => sub {
61                if(!$_[2]) {
62                    return $_[0]->base->list_supported_objects
63                } else { return() }
64            },
65            code => sub {
66                my ($env, @args) = @_;
67                if ($_[1]) {
68                    my @res = $env->base->search_objects(@args);
69                    print $OUT map { "$_\n" } @res;
70                    $env->{_lastsearch} = \@res;
71                    $env->{_lastsearchtype} = $args[0];
72                } else {
73                    print $OUT "Object type missing";
74                }
75            },
76        });
77    $env->add_func('expired', {
78        help => 'expired [delay] - list expired account more than delay (default is now)',
79        code => sub {
80            my ($env, $expire) = @_;
81            my @users = $env->base->find_expired_users($expire);
82            print $OUT map { "$_\n" } @users;
83            $env->{_lastsearchtype} = 'user';
84            $env->{_lastsearch} = \@users;
85        },
86    }) if ($env->base->can('find_expired_users'));
87    $env->add_func('expires', {
88        help => 'expires [delay] - list account expiring before delay (default is 1 month)',
89        code => sub {
90            my ($env, $expire) = @_;
91            my @users = $env->base->find_next_expire_users($expire);
92            print $OUT map { "$_\n" } @users;
93            $env->{_lastsearchtype} = 'user';
94            $env->{_lastsearch} = \@users;
95        },
96    }) if ($env->base->can('find_next_expire_users'));
97    $env->add_func('select', {
98            help => 'select object_type - select objects to perform action on it',
99            completion => sub {
100                if ($_[2]) {
101                    return $_[0]->base->list_objects($_[2]);
102                } else {
103                    return '@', $_[0]->base->list_supported_objects;
104                }
105            },
106            code => sub {
107                my ($env, $otype, @ids) = @_;
108                my @objs;
109                if ($otype eq '@') {
110                    if (@{$env->{_lastsearch}}) {
111                        $otype = $env->{_lastsearchtype};
112                        @ids = @{$env->{_lastsearch}};
113                    } else {
114                        print $OUT "No results store from previous search";
115                        return;
116                    }
117                }
118                if (!@ids) {
119                    print $OUT 'not enough arguments';
120                    return;
121                }
122                foreach (@ids) {
123                    my $obj = $env->base->get_object($otype, $_) or do {
124                        print $OUT "Cannot get $otype $_";
125                        return;
126                    };
127                    push(@objs, $obj);
128                }
129                print $OUT "Selecting $otype " . join(', ', @ids);
130                objenv($_[0]->base, $otype, @objs)->cli();
131            },
132        });
133    $env->add_func('user',  { alias => [qw'select user' ] });
134    $env->add_func('group', { alias => [qw'select group'] });
135    return $env
136}
137
138sub objenv {
139    my ($labase, $otype, @objs) = @_;
140    my $objenv = LATMOS::Accounts::Cli->new(
141        {
142            prompt => sub {
143                sprintf("%s %s/%s > ",
144                    $_[0]->base->label,
145                    $_[0]->{_otype},
146                    @{$_[0]->{_objects}} > 1 ? '(' .
147                    scalar(@{$_[0]->{_objects}}) . ' obj.)' : $_[0]->{_objects}[0]->id,
148                );
149            },
150        },
151        $labase
152    );
153    $objenv->{_otype} = $otype;
154    $objenv->{_objects} = [ @objs ];
155    $objenv->add_func('show', {
156        help => 'show attributes - show an attributes of object',
157        code => sub {
158            my ($env, $attr) = @_;
159            if (!$attr) {
160                foreach (@{$env->{_objects}}) {
161                    print $OUT $_->dump;
162                }
163            } else {
164                foreach my $u (@{$env->{_objects}}) {
165                    print $OUT sort map { $u->id . ': ' .($_ || '') . "\n" } $u->get_attributes($attr);
166                }
167            }
168        },
169        completion => sub {
170            if (!$_[2]) {
171                return $_[0]->base->list_canonical_fields($_[0]->{_otype}, 'r')
172            }
173        },
174    });
175    $objenv->add_func('print', {
176        help => 'print fmt - show attributes using template',
177        code => sub {
178            my ($env, $fmt) = @_;
179            foreach (@{$env->{_objects}}) {
180                print $OUT $_->queryformat($fmt) . "\n";
181            }
182        },
183    });
184    $objenv->add_func('unset', {
185        help => 'unset attribute - unset specified attribute',
186        code => sub {
187            my ($env, $attr) = @_;
188            $attr or do {
189                print $OUT "Attributes must be specified";
190                return;
191            };
192            foreach (@{$env->{_objects}}) {
193                defined $_->set_c_fields($attr => undef) or do {
194                    print $OUT "cannot unset attributes $attr for " . $_->id;
195                    return;
196                };
197            }
198            $env->base->commit;
199            print $OUT "Changes applied";
200        },
201        completion => sub {
202            my ($env, $lastw, @args) = @_;
203            if (!$args[0]) {
204                return $env->base->list_canonical_fields($env->{_otype}, 'w')
205            }
206        },
207    });
208    $objenv->add_func('set', {
209        help => 'set attribute value - set an attributes to single value "value"',
210        code => sub {
211            my ($env, $attr, @value) = @_;
212            @value or do {
213                print $OUT "attribute and value must be specified";
214                return;
215            };
216            foreach (@{$env->{_objects}}) {
217                defined $_->set_c_fields($attr => @value <= 1 ? $value[0] :
218                    \@value) or do {
219                    $_->base->rollback;
220                    printf $OUT "Cannot set $attr to %s for %s", join(', ',
221                        @value), $_->id;
222                    return;
223                };
224            }
225            $env->base->commit;
226            print $OUT "done";
227        },
228        completion => sub {
229            my ($env, $lastw, @args) = @_;
230            if (!$args[0]) {
231                return $env->base->list_canonical_fields($env->{_otype}, 'w')
232            } else {
233                if ($env->base->obj_attr_allowed_values($env->{_otype}, $args[0])) {
234                    return $env->base->obj_attr_allowed_values($env->{_otype}, $args[0])
235                }
236                for ($args[0]) {
237                    /^manager|managedBy$/ and return
238                        $env->base->search_objects('user');
239                    /^department$/ and return
240                        $env->base->search_objects('group', 'sutype=dpmt');
241                    /^contratType$/ and return
242                        $env->base->search_objects('group', 'sutype=contrattype');
243                    /^site$/ and return
244                        $env->base->search_objects('site');
245                    if (@{$env->{_objects}} == 1) {
246                        return $env->{_objects}[0]->get_attributes($args[0]);
247                    }
248                }
249            }
250        },
251    });
252    $objenv->add_func('list', {
253        help => 'list current selected objects',
254        code => sub {
255            printf $OUT "%s: %s", $_[0]->{_otype}, join(', ', map { $_->id }
256            @{$_[0]->{_objects}});
257        }
258    });
259    $objenv->add_func('edit', {
260            help => 'edit [object] - edit selected object using vi',
261            completion => sub {
262                return map { $_->id } @{$_[0]->{_objects}}
263            },
264            code => sub {
265                my ($env, $id) = @_;
266                my $obj;
267                if ($id) {
268                    $obj = grep { $_->id = $id } @{$env->{_objects}} or do {
269                        print $OUT "$id is not part of selected objects";
270                        return;
271                    };
272                } elsif (@{$env->{_objects}} == 1) {
273                    $obj = $env->{_objects}[0]
274                } else {
275                    print $OUT "multiple objects selected but can edit only one,"
276                    . "please specify which one";
277                    return;
278                }
279                my $res = LATMOS::Accounts::Utils::dump_read_temp_file(
280                    sub {
281                        my ($fh) = @_;
282                        $obj->text_dump($fh,
283                            {
284                                empty_attr => 1,
285                                only_rw => 1,
286                            }
287                        );
288                    },
289                    sub {
290                        my ($fh) = @_;
291                        my %attr = LATMOS::Accounts::Utils::parse_obj_file($fh);
292                        my $res = $obj->set_c_fields(%attr);
293                        if ($res) {
294                            print $OUT "Changes applied";
295                            $env->base->commit;
296                        }
297                        else { print $OUT "Error applying changes" }
298                        return $res ? 1 : 0;
299                    }
300                );
301            },
302        });
303    $objenv->add_func('delete', {
304        help => 'delete - delete selected object',
305        code => sub {
306            my ($env) = @_;
307            printf $OUT "%s: %s\ndelete selected objects ? (yes/NO) ",
308            $env->{_otype}, join(', ', map { $_->id } @{$env->{_objects}});
309            my $reply = <STDIN> || ''; chomp($reply);
310            if ($reply eq 'yes') {
311                foreach (@{$env->{_objects}}) {
312                    $env->base->delete_object($env->{_otype}, $_->id) or do {
313                        print $OUT "Cannot delete " . $_->id;
314                        return;
315                    };
316                }
317                $env->base->commit;
318                return "EXIT";
319            } else {
320                print $OUT "cancel !"
321            }
322        },
323    });
324
325    if (lc($otype) eq 'user') {
326        $objenv->add_func('group', {
327            help => 'group add|remove|primary goupname',
328            code => sub {
329                my ($env, $action, @groups) = @_;
330                foreach my $obj (@{$env->{_objects}}) {
331                    if ($action eq 'primary') {
332                        my $gid = $groups[0];
333                        if ($gid !~ /^\d/) {
334                            my $gobj = $env->base->get_object('group', $gid) or
335                            do {
336                                print $OUT "Cannot find group $gid";
337                                return;
338                            };
339                            $gid = $gobj->get_attributes('gidNumber');
340                        }
341                        $obj->set_c_fields('gidNumber', $gid);
342                    } else {
343                        my %gr;
344                        foreach ($obj->get_attributes('memberOf')) {
345                            $gr{$_} = 1;
346                        }
347                        if ($action eq 'add') {
348                            $gr{$_} = 1 foreach(@groups);
349                        } elsif ($action eq 'remove') {
350                            delete($gr{$_}) foreach(@groups);
351                        } else {
352                            print $OUT 'invalid action';
353                            return;
354                        }
355                        defined $obj->set_c_fields('memberOf' => [ keys %gr ]) or do {
356                            print $OUT "cannot set memberOf attributes for " .
357                            $obj->id;
358                            return;
359                        };
360                    }
361                }
362                $env->base->commit;
363            },
364            completion => sub {
365                if (!$_[2]) {
366                    return (qw(add remove primary));
367                } else {
368                    return $_[0]->base->search_objects('group');
369                }
370            },
371        });
372    } elsif ($otype eq 'group') {
373        $objenv->add_func('member', {
374            help => 'member add|remove user',
375            code => sub {
376                my ($env, $action, @groups) = @_;
377                foreach my $obj (@{$env->{_objects}}) {
378                    my %gr;
379                    foreach ($obj->get_attributes('memberUID')) {
380                        $gr{$_} = 1;
381                    }
382                    if ($action eq 'add') {
383                        $gr{$_} = 1 foreach(@groups);
384                    } elsif ($action eq 'remove') {
385                        delete($gr{$_}) foreach(@groups);
386                    } else {
387                        print $OUT 'invalid action';
388                        return;
389                    }
390                    defined $obj->set_c_fields('memberUID' => [ keys %gr ]) or do {
391                        print $OUT "cannot set memberUID attributes for " .
392                        $obj->id;
393                        return;
394                    };
395                }
396                $env->base->commit;
397            },
398            completion => sub {
399                if (!$_[2]) {
400                    return (qw(add remove));
401                } else {
402                    return $_[0]->base->search_objects('user');
403                }
404            },
405        });
406    }
407
408    return $objenv;
409}
410
411sub new {
412    my ($class, $env, $labase) = @_;
413    bless($env, $class);
414    $env->{_labase} = $labase;
415    $env->add_func('quit', { help => 'quit - exit the tool',
416            code => sub { print "\n"; exit(0) }, });
417    $env->add_func('exit', { help => "exit current mode",
418            code => sub { return "EXIT" }, });
419    $env->add_func('help', {
420        help => 'help [command] - print help about command',
421        completion => sub {
422            if (!$_[2]) { return sort keys %{ $_[0]->{funcs} || {}} }
423        },
424        code => sub {
425            my ($self, $name) = @_;
426            if (!$name) {
427                print $OUT join(', ', sort keys %{ $self->{funcs} || {}});
428            } elsif ($self->{funcs}{$name}{alias}) {
429                print $OUT "$name is an alias for " . join(' ',
430                    @{$self->{funcs}{$name}{alias}});
431            } elsif ($self->{funcs}{$name}{help}) {
432                print $OUT $self->{funcs}{$name}{help};
433            } else {
434                print $OUT "No help availlable";
435            }
436        },
437    });
438
439    $env;
440}
441
442sub base { $_[0]->{_labase} }
443
444sub cli {
445    my ($self) = @_;
446    while (1) {
447        $term->Attribs->{completion_function} = sub {
448            $self->complete($_[0], shellwords(substr($_[1], 0, $_[2])));
449        };
450        defined (my $line = $term->readline($self->prompt)) or return;
451        my $res = $self->run(shellwords($line));
452        $self->base->rollback;
453        if ($res && $res eq 'EXIT') { return }
454    }
455}
456
457sub prompt {
458    my ($self) = @_;
459    if (!$self->{prompt}) {
460        return "LA cli > ";
461    } else {
462        $self->{prompt}->($self);
463    }
464}
465
466sub add_func {
467    my ($self, $name, $param) = @_;
468    $self->{funcs}{$name} = $param;
469}
470
471sub parse_arg {
472    my ($self, $name, @args) = @_;
473    if ($self->{funcs}{$name}{opt}) {
474        @ARGV = @args;
475    } else {
476        return @args;
477    }
478    return @ARGV;
479}
480
481sub complete {
482    my ($self, $lastw, $name, @args) = @_;
483    if (!$name) {
484        return grep { /^\Q$lastw\E/ } sort
485            (keys %{ $self->{funcs} || {}});
486    } elsif ($self->{funcs}{$name}{alias}) {
487        $self->complete($lastw, @{$self->{funcs}{$name}{alias}}, @args);
488    } elsif ($self->{funcs}{$name}{completion}) {
489        my @pargs = $self->parse_arg($name, @args);
490        return map { my $t = $_; $t =~ s/\s/\\ /g; $t } grep { $_ && /^\Q$lastw\E/ } $self->{funcs}{$name}{completion}->($self, $lastw, @pargs);
491    } else {
492        return ();
493    }
494}
495
496sub run {
497    my ($self, $name, @args) = @_;
498    return if (!$name);
499    if ($self->{funcs}{$name}{alias}) {
500        $self->run(@{$self->{funcs}{$name}{alias}}, @args);
501    } elsif ($self->{funcs}{$name}{code}) {
502        my @pargs = $self->parse_arg($name, @args);
503        $self->{funcs}{$name}{code}->($self, @args);
504    }
505}
506
5071;
Note: See TracBrowser for help on using the repository browser.