ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.37
Committed: Sat Sep 5 22:47:08 2009 UTC (14 years, 8 months ago) by root
Branch: MAIN
Changes since 1.36: +47 -17 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #!/opt/bIn/perl
2
3 =head1 NAME
4
5 aemp - AnyEvent:MP utility
6
7 =head1 SYNOPSIS
8
9 aemp command args...
10
11 # protocol commands
12 aemp snd <port> <arg...> # send a message
13 aemp mon <port> # wait till port is killed
14 aemp rpc <port> <arg...> # send message, append reply
15 aemp eval <node> <expr...> # evaluate expression
16 aemp trace <node> # trace the network topology
17
18 # run a node
19 aemp run configure_args... # run a node
20 aemp restart <node> # restart a node if running under watchdog
21
22 # node configuration: node ID
23 aemp setnodeid <nodeid> # configure the real node id
24 aemp delnodeid # reset node id to default (= inherit)
25
26 # node configuration: secret
27 aemp gensecret # generate a random shared secret
28 aemp setsecret <secret> # set the shared secret
29 aemp delsecret # remove the secret (= inherit)
30
31 # node configuration: TLS
32 aemp gencert # generate a random certificate
33 aemp setcert <file> # set a certificate (key.pem + certificate.pem)
34 aemp delcert # remove certificate (= inherit)
35
36 # node configuration: seed addresses for bootstrapping
37 aemp setseeds <host:port>,... # set seeds
38 aemp delseeds # clear all seeds (= inherit)
39 aemp addseed <host:port> # add a seed
40 aemp delseed <host:port> # remove seed
41
42 # node configuration: bind addresses
43 aemp setbinds <host:port>,... # set binds
44 aemp delbinds # clear all binds (= inherit)
45 aemp addbind <host:port> # add a bind address
46 aemp delbind <host:port> # remove a bind address
47
48 # node configuration: services
49 aemp setservices initfunc,... # set service functions
50 aemp delservices # clear all services (= inherit)
51 aemp addservice <initfunc> # add an instance of a service
52 aemp delservice <initfunc> # delete one instance of a service
53
54 # profile-specific configuration
55 aemp profile <name> <command>... # apply command to profile only
56 aemp setparent <name> # specify a parent profile
57 aemp delparent # clear parent again
58 aemp delprofile <name> # eradicate the named profile
59 aemp showprofile <name> # display given profile
60 aemp showconfig <name> ... # display effective config
61
62 =head1 DESCRIPTION
63
64 With aemp you can configure various aspects of AnyEvent::MP and its
65 protocol, send various messages and even run a node.
66
67 The F<aemp> utility works like F<cvs>, F<svn> or other commands: the first
68 argument defines which operation (subcommand) is requested, after which
69 arguments for this operation are expected. When a subcommand does not eat
70 all remaining arguments, the remaining arguments will again be interpreted
71 as subcommand and so on.
72
73 This means you can chain multiple commands, which is handy for profile
74 configuration, e.g.:
75
76 aemp gensecret profile xyzzy binds 4040,4041 nodeid anon/
77
78 Please note that all C<setxxx> subcommands have an alias without the
79 C<set> prefix.
80
81 All configuration data is stored in a human-readable (JSON) config file
82 stored in F<~/.perl-anyevent-mp> (or F<%appdata%/perl-anyevent-mp>
83 on loser systems). Feel free to look at it or edit it, the format is
84 relatively simple.
85
86 =head2 SPECIFYING ARGUMENTS
87
88 Arguments can be specified just as with any other shell command, with a
89 few special cases:
90
91 If the I<first> argument starts with a literal C<[>-character, then it is
92 interpreted as a UTF-8 encoded JSON text. The resulting array replaces all
93 arguments.
94
95 Otherwise, if I<any> argument starts with one of C<[>, C<{> or C<">, then
96 it is interpreted as UTF-8 encoded JSON text (or a single value in case of
97 C<">), and the resulting reference or scalar replaces the argument.
98
99 This allows you, for example, to specify binds in F<aemp run> (using POSIX
100 shell syntax):
101
102 aemp run binds '["*:4040"]'
103
104 =head2 RUNNING A NODE
105
106 This can be used to run a node - together with some services, this makes
107 it unnecessary to write any wrapper programs.
108
109 =over 4
110
111 =item run <configure_args>...
112
113 Runs a node by calling C<AnyEvent::MP::Kernel::configure> with the given
114 arguments. The node runs under L<AnyEvent::Watchdog>, can be restarted
115 (and autorestarted, see the L<AnyEvent::Watchdog> manual). A very common
116 invocation is to just specify a profile using the profile name
117
118 aemp run database-backend
119
120 ... but you can use most arguments that C<configure> understands:
121
122 aemp run nodeid mynode2 profile someprofile
123
124 Care has been taken to load (almost) no modules other than
125 L<AnyEvent::Watchdog> and the modules it loads, so everything (including
126 the L<AnyEvent::MP> modules themselves) will be freshly loaded on restart,
127 which makes upgrading everything except the perl binary easy.
128
129 =item restart <node>
130
131 Restarts the node using C<AnyEvent::Watchdog::Util::restart>. This works
132 for nodes started by C<aemp run>, but also for any other node that uses
133 L<AnyEvent::Watchdog>.
134
135 =back
136
137 =head2 PROTOCOL COMMANDS
138
139 These commands actually communicate with other nodes. They all use a node
140 profile name of C<aemp> (specifying a default node ID of C<anon/> and a
141 binds list containing C<*:*> only).
142
143 They all use a timeout of five seconds, after which they give up.
144
145 =over 4
146
147 =item snd <port> <arguments...>
148
149 Simply send a message to the given port - where you get the port ID from
150 is your problem.
151
152 Exits after ensuring that the message has been delivered to its node.
153
154 Most useful to take advantage of some undocumented functionality inside
155 nodes, such as node ports being able to call any method:
156
157 aemp snd doomed AnyEvent::Watchdog::restart 1
158
159 =item rpc <port> <arg...>
160
161 Like F<aemp snd>, but appends a local reply port to the message and waits
162 for a message to it.
163
164 Any return values will be JSON-encoded and printed separated by commas
165 (kind of like a JSON array without []-brackets).
166
167 Example: ask the (undocumented) time service of a node for it's current
168 time.
169
170 aemp rpc mynode time
171
172 =item mon <port>
173
174 Monitors the port and exits when it's monitorign callback is called. Most
175 useful to monitor node ports.
176
177 Example: monitor some node.
178
179 aemp mon doomed
180
181 =item eval <node> <expr...>
182
183 Joins all remaining arguments into a string and evaluates it on the given
184 node. Return values are handled as with F<aemp rpc>.
185
186 Example: find the unix process ID of the node called posicks.
187
188 aemp eval posicks '$$'
189
190 =item trace <node>
191
192 Asks the given node for all currently connected nodes, then asks those
193 nodes for the same, thus tracing all node connections.
194
195 =back
196
197 =head2 CONFIGURATION/NODE ID/SECRET/CERTIFICATE
198
199 These commands deal with rather basic settings, the node ID, the shared
200 secret and the TLS certificate.
201
202 =over 4
203
204 =item setnodeid <nodeid>
205
206 Set the node ID to the given string.
207
208 =item delnodeid
209
210 Removes the node ID again, which means it is inherited again from it's
211 parent profile, or stays unset.
212
213 =item gensecret
214
215 Generates a random shared secret and sets it. The shared secret is used to
216 authenticate nodes to each other when TLS is not required.
217
218 =item setsecret <secret>
219
220 Sets the shared secret tot he given string, which can be anything.
221
222 =item delsecret
223
224 Removes the shared secret again, which means it is inherited again from
225 it's parent profile, or stays unset.
226
227 =item gencert
228
229 Generates a self-signed certificate and key, and sets it. This works
230 similarly to a shared secret: when all nodes have it, TLS will be used to
231 authenticate and encrypt all traffic.
232
233 =item setcert <file>
234
235 Set a node certificate (and optionally any CA certificates) from the given
236 file. The file must contain the key, followed by the certificate, followed
237 by any CA certificates you want to trust, all in PEM format.
238
239 See L<AnyEvent::TLS> for some more details - this sets the C<cert> and
240 C<ca_cert> options.
241
242 =item delcert
243
244 Removes the certificate(s) again, which means it is inherited again from
245 it's parent profile, or stays unset.
246
247 =back
248
249 =head2 CONFIGURATION/SEEDS
250
251 To discover the network you have to specify some seed addresses, which are
252 basically C<host:port> pairs where you expect some long-running nodes. It
253 does no harm to have a node as its own seed (they will eventually be
254 ignored).
255
256 =over 4
257
258 =item setseeds <host:port>,...
259
260 Sets or replaces the list of seeds, which must be specified as a
261 comma-separated list of C<host:port> pairs. The C<host> can be a hostname,
262 an IP address, or C<*> to signify all local host addresses (which makes
263 little sense for seeds, outside some examples, but a lot of sense for
264 binds).
265
266 An empty list is allowed.
267
268 Example: use C<doomed> with default port as only seednode.
269
270 aemp setseeds doomed
271
272 =item delseeds
273
274 Removes the seed list again, which means it is inherited again from it's
275 parent profile, or stays unset.
276
277 =item addseed <host:port>
278
279 Adds a single seed address.
280
281 =item delseed <host:port>
282
283 Deletes the given seed address, if it exists.
284
285 =back
286
287 =head2 CONFIGURATION/BINDS
288
289 To be able to be reached from other nodes, a node must I<bind> itself
290 to some listening socket(s). The list of these can either bs specified
291 manually, or AnyEvent::MP can guess them. Nodes without any binds are
292 possible to some extent.
293
294 =over 4
295
296 =item setbinds <host:port>,...
297
298 Sets the list of bind addresses explicitly - see the F<aemp setseeds>
299 command for the exact syntax. In addition, a value of C<*> for the port,
300 or not specifying a port, means to use a dynamically-assigned port.
301
302 Note that the C<*>, C<*:*> or C<*:port> patterns are very useful here.
303
304 Example: bind on a ephemeral port on all local interfaces.
305
306 aemp setbinds "*"
307
308 Example: bind on a random port on all local interfaces.
309
310 aemp setbinds "*:*"
311
312 Example: resolve "doomed.mydomain" and try to bind on port C<4040> of all
313 IP addressess returned.
314
315 aep setbinds doomed.mydomain:4040
316
317 =item delbinds
318
319 Removes the bind list again, which means it is inherited again from it's
320 parent profile, or stays unset.
321
322 =item addbind <host:port>
323
324 Adds a single bind address.
325
326 =item delbind <host:port>
327
328 Deletes the given bind address, if it exists.
329
330 =back
331
332 =head2 CONFIGURATION/SERVICES
333
334 Services are modules (or functions) that are automatically loaded (or
335 executed) when a node starts. They are especially useful when used in
336 conjunction with F<aemp run>, to configure which services a node should
337 run.
338
339 =over 4
340
341 =item setservices <initfunc>...
342
343 Sets or replaces the list of services, which must be specified as a
344 comma-separated list or a JSON array.
345
346 Each string entry in the list is interpreted as either a module name to
347 load (when it ends with C<::>) or a function to call (all other cases).
348
349 Each entry which is an array itself (you need to use JSON format to
350 specify those) is interpreted as a function name and the arguments to
351 pass.
352
353 The algorithm to find the function is the same as used for C<<
354 L<AnyEvent::MP>::spawn >>.
355
356 Example: run the globa service (which runs anyway, but this doesn't hurt).
357
358 aemp setservices AnyEvent::MP::Global::
359
360 Example: call the mymod::myfun function with arguments 1, 2 and 3.
361
362 aemp setservices '[["mymod::myfun", 1,2,3]]'
363
364 =item delservices
365
366 Removes the service list again, which means it is inherited again from
367 it's parent profile, or stays unset.
368
369 =item addservice <initfunc>
370
371 Adds a single service.
372
373 =item delservice <initfunc>
374
375 Deletes the given service, if it exists.
376
377 =back
378
379 =head2 CONFIGURATION/PROFILE MANAGEMENT
380
381 All the above configuration functions by default affect the I<global
382 default configuration>, which is basically used to augment every profile
383 and node configuration.
384
385 =over 4
386
387 =item profile <name> ...
388
389 This subcommand makes the following subcommands act only on a specific
390 named profile, instead of on the global default. The profile is created if
391 necessary.
392
393 Example: create a C<server> profile, give it a random node name, some seed
394 nodes and bind it on an unspecified port on all local interfaces. You
395 should add some services then and run the node...
396
397 aemp profile server nodeid anon/ seeds doomed,10.0.0.2:5000 binds "*:*"
398
399 =item delprofile <name>
400
401 Deletes the profile of the given name.
402
403 =item setparent <name>
404
405 Sets the parent profile to use - values not specified in a profile will be
406 taken from the parent profile (even recursively, with the global default
407 config being the default parent). This is useful to configure profile
408 I<classes> and then to inherit from them for individual nodes.
409
410 Note that you can specify circular parent chains and even a parent for the
411 global configuration. Neither will do you any good, however.
412
413 Example: inherit all values not specified in the C<doomed> profile from
414 the C<server> profile.
415
416 aemp profile doomed setparent server
417
418 =item delparent
419
420 Removes the parent again from the profile, if any was set, so the profile
421 inherits directly from the global default config again.
422
423 =item showprofile <name>
424
425 Shows the values of the given profile, and only those, no inherited
426 values.
427
428 =item showconfig <name> <key value...>
429
430 Shows the I<effective> config, i.e. the values as used by a node started
431 with the given profile name. Any additional key-value pairs specified
432 augment the configuration, just as with C<configure>.
433
434 If all arguments are omitted, show the global default config.
435
436 =back
437
438 =cut
439
440 use common::sense;
441
442 # should come before anything else, so all modules
443 # will be loaded on each restart
444 BEGIN {
445 if (@ARGV == 1 && $ARGV[0] =~ /^\[/) {
446 require JSON::XS;
447 @ARGV = @{ JSON::XS->new->utf8->decode (shift) };
448 } else {
449 for (@ARGV) {
450 if (/^[\[\{\"]/) {
451 require JSON::XS;
452 $_ = JSON::XS->new->utf8->allow_nonref->decode ($_);
453 }
454 }
455 }
456
457 if ($ARGV[0] eq "run") {
458 shift;
459
460 # d'oh
461 require AnyEvent::Watchdog;
462 # now we can load extra modules
463
464 require AnyEvent::Watchdog::Util;
465 AnyEvent::Watchdog::Util::autorestart (1);
466 AnyEvent::Watchdog::Util::heartbeat (300);
467
468 require AnyEvent;
469 require AnyEvent::MP::Kernel;
470 AnyEvent::MP::Kernel::configure (@ARGV);
471
472 AnyEvent::detect () eq "AnyEvent::Impl::EV"
473 ? EV::loop ()
474 : AE::cv ()->recv;
475 }
476 }
477
478 use Carp ();
479
480 use JSON::XS;
481
482 use AnyEvent;
483 use AnyEvent::Util;
484
485 use AnyEvent::MP;
486 use AnyEvent::MP::Config;
487
488 sub my_run_cmd {
489 my ($cmd) = @_;
490
491 my $cv = &run_cmd;
492 my $status = $cv->recv;
493
494 $status
495 and die "@$cmd: command failed with exit status $status.";
496 }
497
498 sub gen_cert {
499 my_run_cmd [qw(openssl req
500 -new -nodes -x509 -days 3650
501 -newkey rsa:2048 -keyout /dev/fd/3
502 -batch -subj /CN=AnyEvent::MP
503 )],
504 "<", "/dev/null",
505 ">" , \my $cert,
506 "3>", \my $key,
507 "2>", "/dev/null";
508
509 "$cert$key"
510 }
511
512 sub init {
513 configure profile => "aemp", nodeid => "anon/";
514 }
515
516 our $cfg = AnyEvent::MP::Config::config;
517 our $profile = $cfg;
518
519 sub trace {
520 my ($node) = @_;
521 my $cv = AE::cv;
522 my %seen;
523
524 my $to = AE::timer 15, 0, sub { exit 1 };
525
526 init;
527
528 my $reply = port {
529 my ($node, @neigh) = @_;
530
531 @neigh = grep $_ ne $NODE, @neigh;
532
533 print +(join " ", $node, @neigh), "\n";
534
535 for (@neigh) {
536 unless ($seen{$_}++) {
537 $cv->begin;
538 snd $_, up_nodes => $SELF => $_;
539 }
540 }
541
542 $cv->end;
543 };
544
545 $cv->begin;
546 snd $reply, seed => $node;
547
548 $cv->recv;
549 }
550
551 sub node_eval {
552 my ($node, $expr) = @_;
553
554 init;
555
556 my $cv = AE::cv;
557 my $to = AE::timer 5, 0, sub { exit 1 };
558 AnyEvent::MP::Kernel::eval_on $node, $expr, port { &$cv };
559 mon $node, $cv;
560
561 my ($err, @res) = $cv->recv;
562
563 die $err if length $err;
564
565 print +(substr JSON::XS->new->encode (\@res), 1, -1), "\n";
566 }
567
568 sub docmd;
569
570 our %CMD = (
571 snd => sub {
572 my $port = shift @ARGV;
573 init;
574
575 snd $port, @ARGV; @ARGV = ();
576
577 my $cv = AE::cv;
578 my $to = AE::timer 5, 0, sub { exit 1 };
579 mon $port, $cv;
580 my $reply = port sub { &$cv };
581 snd node_of $port, snd => $reply, "message sent successfully";
582
583 print join " ", $cv->recv, "\n";
584 },
585
586 rpc => sub {
587 my $port = shift @ARGV;
588 init;
589
590 my $cv = AE::cv;
591 my $to = AE::timer 5, 0, sub { exit 1 };
592 snd $port, @ARGV, port { &$cv }; @ARGV = ();
593 mon $port, $cv;
594
595 print +(substr JSON::XS->new->encode ([$cv->recv]), 1, -1), "\n";
596 },
597
598 mon => sub {
599 my $port = shift @ARGV;
600 init;
601
602 mon $port, my $cv = AE::cv;
603 print join " ", $cv->recv, "\n";
604 },
605
606 eval => sub {
607 my $node = node_of shift @ARGV;
608 my $expr = join " ", @ARGV; @ARGV = ();
609 node_eval $node, $expr;
610 },
611
612 trace => sub {
613 @ARGV >= 1
614 or die "node id missing\n";
615
616 trace shift @ARGV;
617 },
618 restart => sub {
619 my $node = node_of shift @ARGV;
620 node_eval $node, "AnyEvent::Watchdog::Util::restart";
621 },
622
623 setnodeid => sub {
624 @ARGV >= 1
625 or die "shared secret missing\n";
626
627 $profile->{nodeid} = shift @ARGV;
628 ++$cfg->{dirty};
629 },
630 delnodeid => sub {
631 delete $profile->{nodeid};
632 ++$cfg->{dirty};
633 },
634
635 setsecret => sub {
636 @ARGV >= 1
637 or die "shared secret missing\n";
638
639 $profile->{secret} = shift @ARGV;
640 ++$cfg->{dirty};
641 },
642 gensecret => sub {
643 $profile->{secret} = AnyEvent::MP::Kernel::alnumbits AnyEvent::MP::Kernel::nonce 64;
644 ++$cfg->{dirty};
645 },
646 delsecret => sub {
647 delete $profile->{secret};
648 ++$cfg->{dirty};
649 },
650
651 setcert => sub {
652 @ARGV >= 1
653 or die "key+certificate pem filename missing\n";
654
655 open my $fh, "<", $ARGV[0]
656 or die "$ARGV[0]: $!";
657
658 local $/;
659 $profile->{cert} = <$fh>;
660 ++$cfg->{dirty};
661 },
662 gencert => sub {
663 $profile->{cert} = gen_cert;
664 ++$cfg->{dirty};
665 },
666 delcert => sub {
667 delete $profile->{cert};
668 ++$cfg->{dirty};
669 },
670
671 setbinds => sub {
672 @ARGV >= 1
673 or die "bind addresses missing\n";
674 $profile->{binds} = [split /,/, shift @ARGV];
675 ++$cfg->{dirty};
676 },
677 delbinds => sub {
678 delete $profile->{binds};
679 ++$cfg->{dirty};
680 },
681 addbind => sub {
682 @ARGV >= 1
683 or die "bind address missing\n";
684 my $bind = shift @ARGV;
685
686 @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
687 push @{ $profile->{binds} }, $bind;
688 ++$cfg->{dirty};
689 },
690 delbind => sub {
691 @ARGV >= 1
692 or die "bind address missing\n";
693 my $bind = shift @ARGV;
694
695 @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
696 ++$cfg->{dirty};
697 },
698
699 setseeds => sub {
700 @ARGV >= 1
701 or die "seed addresses missing\n";
702 $profile->{seeds} = [split /,/, shift @ARGV];
703 ++$cfg->{dirty};
704 },
705 delseeds => sub {
706 delete $profile->{seeds};
707 ++$cfg->{dirty};
708 },
709 addseed => sub {
710 @ARGV >= 1
711 or die "seed address missing\n";
712 my $seed = shift @ARGV;
713
714 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
715 push @{ $profile->{seeds} }, $seed;
716 ++$cfg->{dirty};
717 },
718 delseed => sub {
719 @ARGV >= 1
720 or die "seed address missing\n";
721 my $seed = shift @ARGV;
722
723 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
724 ++$cfg->{dirty};
725 },
726
727 setservices => sub {
728 @ARGV >= 1
729 or die "service specifications missing\n";
730 $profile->{services} = [split /,/, shift @ARGV];
731 ++$cfg->{dirty};
732 },
733 delservices => sub {
734 delete $profile->{services};
735 ++$cfg->{dirty};
736 },
737 addservice => sub {
738 @ARGV >= 1
739 or die "service specification missing\n";
740 my $service = shift @ARGV;
741 push @{ $profile->{services} }, $service;
742 ++$cfg->{dirty};
743 },
744 delservice => sub {
745 @ARGV >= 1
746 or die "service specification missing\n";
747 my $service = shift @ARGV;
748 for (0 .. $#{ $profile->{services} }) {
749 next unless $profile->{services}[$_] eq $service;
750 splice @{ $profile->{services} }, $_, 1;
751 last;
752 }
753 ++$cfg->{dirty};
754 },
755
756 profile => sub {
757 @ARGV >= 1
758 or die "profile name is missing\n";
759 my $name = shift @ARGV;
760
761 $profile = $cfg->{profile}{$name} ||= {};
762 ++$cfg->{dirty};
763 },
764 delprofile => sub {
765 @ARGV >= 1
766 or die "profile name is missing\n";
767 my $name = shift @ARGV;
768
769 delete $cfg->{profile}{$name};
770 ++$cfg->{dirty};
771 },
772 setparent => sub {
773 @ARGV >= 1
774 or die "profile name is missing\n";
775
776 $profile->{parent} = shift @ARGV;
777 ++$cfg->{dirty};
778 },
779 delparent => sub {
780 delete $profile->{parent};
781 ++$cfg->{dirty};
782 },
783 showprofile => sub {
784 @ARGV >= 1
785 or die "profile name is missing\n";
786 my $name = shift @ARGV;
787
788 print JSON::XS->new->pretty->encode ($cfg->{profile}{$name} || {});
789 },
790 showconfig => sub {
791 my $name = @ARGV ? shift @ARGV : AnyEvent::MP::Kernel::_nodename;
792
793 my $profile = AnyEvent::MP::Config::find_profile $name, @ARGV;
794 @ARGV = ();
795
796 # make it look nicer:
797 delete $profile->{profile};
798 delete $profile->{parent};
799
800 print JSON::XS->new->pretty->encode ($profile);
801 },
802
803 # undocumented
804 _resolve => sub {
805 print +(join ",", (AnyEvent::MP::Kernel::_resolve shift @ARGV)->recv), "\n";
806 },
807 );
808
809 for (keys %CMD) {
810 $CMD{$1} = $CMD{$_} if /^set(.*)$/;
811 }
812
813 sub docmd {
814 my $cmd = shift @ARGV;
815
816 $CMD{$cmd}
817 or die "$cmd: no such aemp command (try perldoc aemp, or man aemp)";
818
819 $CMD{$cmd}();
820 }
821
822 @ARGV
823 or die "Usage: aemp subcommand ... (try perldoc aemp, or man aemp)\n";
824
825 docmd while @ARGV;
826
827