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