ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/AnyEvent-Porttracker/Porttracker.pm
(Generate patch)

Comparing cvsroot/AnyEvent-Porttracker/Porttracker.pm (file contents):
Revision 1.3 by root, Mon Nov 15 19:49:36 2010 UTC vs.
Revision 1.10 by root, Tue Nov 16 02:07:31 2010 UTC

3AnyEvent::Porttracker - Porttracker/PortIQ API client interface. 3AnyEvent::Porttracker - Porttracker/PortIQ API client interface.
4 4
5=head1 SYNOPSIS 5=head1 SYNOPSIS
6 6
7 use AnyEvent::Porttracker; 7 use AnyEvent::Porttracker;
8
9 my $api = new AnyEvent::Porttracker
10 host => "10.0.0.1",
11 user => "admin",
12 pass => "31331",
13 tls => 1,
14 ;
15
16 # Example 1
17 # a simple request: ping the server
18
19 $api->req ("ping", sub {
20 my ($api, $ok, $timestamp, $pid) = @_;
21 ...
22 });
23
24 # Example 2
25 # find all realms, start a discovery on all of them
26 # and wait until all discovery processes have finished
27
28 my $cv = AE::cv;
29
30 $cv->begin;
31 # find all realms
32 $api->req (realm_info => ["gid", "name"], sub {
33 my ($api, @realms) = @_;
34
35 # start discovery on all realms
36 for my $realm (@realms) {
37 my ($gid, $name) = @$realm;
38
39 $cv->begin;
40 $api->req (realm_discover => $realm->[0], sub {
41 warn "discovery for realm '$realm->[1]' finished\n";
42 $cv->end;
43 });
44 }
45
46 $cv->end;
47 });
48
49 $cv->recv;
50
51 # Example 3
52 # subscribe to realm_poll_stop events and report each occurance
53
54 $api->req (subscribe => "realm_poll_stop", sub {});
55 $api->on (realm_poll_stop_event => sub {
56 my ($api, $gid) = @_;
57 warn "this just in: poll for realm <$gid> finished.\n";
58 });
8 59
9=head1 DESCRIPTION 60=head1 DESCRIPTION
10 61
11Porttracker (L<http://www.porttracker.com/>) is a product that (among 62Porttracker (L<http://www.porttracker.com/>) is a product that (among
12other things) scans switches and routers in a network and gives a coherent 63other things) scans switches and routers in a network and gives a coherent
25 76
26To quickly understand how this module works you should read how to 77To quickly understand how this module works you should read how to
27construct a new connection object and then read about the event/callback 78construct a new connection object and then read about the event/callback
28system. 79system.
29 80
81The actual low-level protocol and, more importantly, the existing
82requests and responses, are documented in the official Porttracker
83API documentation (a copy of which is included in this module as
84L<AnyEvent::Porttracker::protocol>.
85
30=head1 THE AnyEvent::Porttracker CLASS 86=head1 THE AnyEvent::Porttracker CLASS
31 87
32The AnyEvent::Porttracker class represents a single connection. 88The AnyEvent::Porttracker class represents a single connection.
33 89
34=over 4 90=over 4
58 : ($type = (UNIVERSAL::can $self, $type)) 114 : ($type = (UNIVERSAL::can $self, $type))
59 ? $type->($self, @args) 115 ? $type->($self, @args)
60 : () 116 : ()
61} 117}
62 118
63=item new AnyEvent::Porttracker [key => value...] 119=item $api = new AnyEvent::Porttracker [key => value...]
64 120
65Creates a new porttracker API connection object and tries to connect to 121Creates a new porttracker API connection object and tries to connect to
66the specified host (see below). After the connection has been established, 122the specified host (see below). After the connection has been established,
67the TLS handshake (if requested) will take place, followed by a login 123the TLS handshake (if requested) will take place, followed by a login
68attempt using either the C<none>, C<login_cram_md6> or C<login> methods, 124attempt using either the C<none>, C<login_cram_md6> or C<login> methods,
88=item user => $string, pass => $string 144=item user => $string, pass => $string
89 145
90These are the username and password to use when authentication is required 146These are the username and password to use when authentication is required
91(which it is in almost all cases, so these keys are normally mandatory). 147(which it is in almost all cases, so these keys are normally mandatory).
92 148
93=item tls => ... 149=item tls => $bool
94 150
95#TODO# 151Enables or disables TLS (default: disables). When enabled, then the
152connection will try to handshake a TLS connection before logging in. If
153unsuccessful a fatal error will be raised.
154
155Since most Porttracker/PortIQ boxes will not have a sensible/verifiable
156certificate, no attempt at verifying it will be done (which means
157man-in-the-middle-attacks will be trivial). If you want some form of
158verification you need to provide your own C<tls_ctx> object with C<<
159verify => 1, verify_peername => [1, 1, 1] >> or whatever verification mode
160you wish to use.
161
162=item tls_ctx => $tls_ctx
163
164The L<AnyEvent::TLS> object to use. See C<tls>, above.
96 165
97=item on_XYZ => $coderef 166=item on_XYZ => $coderef
98 167
99You can specify event callbacks either by subclassing and overriding the 168You can specify event callbacks either by subclassing and overriding the
100respective methods or by specifying coderefs as key-value pairs when 169respective methods or by specifying coderefs as key-value pairs when
101constructing the object. 170constructing the object. You add or remove event handlers at any time with
171the C<event> method.
102 172
103=back 173=back
104 174
105=cut 175=cut
106 176
107sub new { 177sub new {
108 my $class = shift; 178 my $class = shift;
109 179
110 my $self = bless { 180 my $self = bless {
111 id => "a", 181 id => "a",
182 ids => [],
112 queue => [], # ininitially queue everything 183 queue => [], # ininitially queue everything
113 @_, 184 @_,
114 }, $class; 185 }, $class;
115 186
116 { 187 {
117 Scalar::Util::weaken (my $self = $self); 188 Scalar::Util::weaken (my $self = $self);
118 189
119 $self->{hdl} = new AnyEvent::Handle 190 $self->{hdl} = new AnyEvent::Handle
120 connect => [$self->{host}, $self->{port} || "porttracker=55"], 191 connect => [$self->{host}, $self->{port} || "porttracker=55"],
121 on_error => sub { 192 on_error => sub {
122 $self->error (); 193 $self->error ($_[2]);
123 }, 194 },
124 on_connect => sub { 195 on_connect => sub {
125 if ($self->{tls}) { 196 if ($self->{tls}) {
126 $self->_req (start_tls => sub { 197 $self->_req (start_tls => sub {
127 $_[1] 198 $_[1]
137 my $id = shift @$msg; 208 my $id = shift @$msg;
138 209
139 if (defined $id) { 210 if (defined $id) {
140 my $cb = delete $self->{cb}{$id} 211 my $cb = delete $self->{cb}{$id}
141 or return $self->error ("received unexpected reply msg with id $id"); 212 or return $self->error ("received unexpected reply msg with id $id");
213
214 push @{ $self->{ids} }, $id;
142 215
143 $cb->($self, @$msg); 216 $cb->($self, @$msg);
144 } else { 217 } else {
145 $msg->[0] = "on_$msg->[0]_notify"; 218 $msg->[0] = "on_$msg->[0]_notify";
146 call $self, @$msg; 219 call $self, @$msg;
161} 234}
162 235
163sub error { 236sub error {
164 my ($self, $msg) = @_; 237 my ($self, $msg) = @_;
165 238
166 call on_error => $msg; 239 call $self, on_error => $msg;
167 240
168 () 241 ()
169} 242}
170 243
171sub _req { 244sub _req {
172 my $self = shift; 245 my $self = shift;
173 my $cb = pop; 246 my $cb = pop;
174 247
175 my $id = ++$self->{id}; 248 my $id = (pop @{ $self->{ids} }) || $self->{id}++;
176 249
177 unshift @_, $id; 250 unshift @_, $id;
178 $self->{cb}{$id} = $cb; 251 $self->{cb}{$id} = $cb;
179 252
180 my $msg = JSON::encode_json \@_; 253 my $msg = JSON::encode_json \@_;
181 254
182 $self->{hdl}->push_write ($msg); 255 $self->{hdl}->push_write ($msg);
183} 256}
184 257
258=item $api->req ($type => @args, $callback->($api, @reply))
259
260Sends a generic request of type C<$type> to the server. When the server
261responds, the API object and the response arguments (without the success
262status) are passed to the callback, which is the last argument to this
263method.
264
265If the request fails, then a fatal error will be raised. If you want to
266handle failures gracefully, you need to use C<< ->req_failok >> instead.
267
268The available requests are documented in the Porttracker API
269documentation (a copy of which is included in this module as
270L<AnyEvent::Porttracker::protocol>.
271
272It is permissible to call this (or any other request function) at any
273time, even before the connection has been established - the API object
274always waits until after login before it actually sends the requests, and
275queues them until then.
276
277Example: ping the porttracker server.
278
279 $api->req ("ping", sub {
280 my ($api, $ok, $timestamp, $pid) = @_;
281 ...
282 });
283
284Example: determine the product ID.
285
286 $api->req (product_id => sub {
287 my ($api, $ok, $branding, $product_id) = @_;
288 ...
289 });
290
291Example: set a new license.
292
293 $api->req (set_license => $LICENSE_STRING, sub {
294 my ($api, $ok) = @_;
295
296 $ok or die "failed to set license";
297 });
298
299=cut
300
185sub req { 301sub req {
302 my $cb = pop;
303 push @_, sub {
304 splice @_, 1, 1
305 or $_[0]->error ($_[1]);
306
307 &$cb
308 };
309
186 $_[0]{queue} 310 $_[0]{queue}
187 ? push @{ $_[0]{queue} }, [@_] 311 ? push @{ $_[0]{queue} }, [@_]
188 : &_req 312 : &_req
189} 313}
190 314
315=item $api->req_failok ($type => @args, $callback->($api, $success, @reply))
316
317Just like C<< ->req >>, with two differences: first, a failure will not
318raise an error, second, the initial status reply which indicates success
319or failure is not removed before calling the callback.
320
321=cut
322
323sub req_failok {
324 $_[0]{queue}
325 ? push @{ $_[0]{queue} }, [@_]
326 : &_req
327}
328
329=item $api->on (XYZ => $callback)
330
331Overwrites any currently registered handler for C<on_XYZ> or
332installs a new one. Or, when C<$callback> is undef, unregisters any
333currently-registered handler.
334
335Example: replace/set the handler for C<on_discover_stop_event>.
336
337 $api->on (discover_stop_event => sub {
338 my ($api, $gid) = @_;
339 ...
340 });
341
342=cut
343
344sub on {
345 my $self = shift;
346
347 while (@_) {
348 my ($event, $cb) = splice @_, 0, 2;
349 $event =~ s/^on_//;
350
351 $self->{"on_$event"} = $cb;
352 }
353}
354
191sub on_start_tls_notify { 355sub on_start_tls_notify {
192 my ($self) = @_; 356 my ($self) = @_;
193 357
194 $self->{hdl}->starttls ("connect"); 358 $self->{hdl}->starttls (connect => $self->{tls_ctx});
195 $self->{tls} ||= 1; 359 $self->{tls} ||= 1;
196 360
197 $self->_login; 361 $self->_login;
198} 362}
199 363
291 455
292 $msg =~ s/\n$//; 456 $msg =~ s/\n$//;
293 $self->error ("login failed: $msg"); 457 $self->error ("login failed: $msg");
294} 458}
295 459
460sub on_event_notify {
461 my ($self, $event, @args) = @_;
462
463 call $self, "on_${event}_event", @args;
464}
465
296=back 466=back
297 467
298=head2 EVENTS 468=head1 EVENTS
299 469
300AnyEvent::Porttracker conenctions are fully event-driven, and naturally 470AnyEvent::Porttracker conenctions are fully event-driven, and naturally
301there are a number of events that can occur. All these events have a name 471there are a number of events that can occur. All these events have a name
302starting with C<on_> (example: C<on_login_failure>). 472starting with C<on_> (example: C<on_login_failure>).
303 473
372 542
373Called when the server wants to start TLS negotiation. This is used 543Called when the server wants to start TLS negotiation. This is used
374internally and - while it is possible to override it - should not be 544internally and - while it is possible to override it - should not be
375overriden. 545overriden.
376 546
547=item on_event_notify $api, $eventname, @args
548
549Called when the server broadcasts an event the API object is subscribed
550to. The default implementation (which should not be overridden) simply
551re-issues an "on_eventname_event" event with the @args.
552
377=item on_XYZ_notify $api, ... 553=item on_XYZ_notify $api, ...
378 554
379In general, any protocol notification will result in an event of the form 555In general, any protocol notification will result in an event of the form
380C<on_NOTIFICATION_notify>. 556C<on_NOTIFICATION_notify>.
381 557
558=item on_XYZ_event $api, ...
559
560Called when the server broadcasts the named (XYZ) event.
561
382=back 562=back
383 563
384=head1 SEE ALSO 564=head1 SEE ALSO
385 565
386L<AnyEvent>, L<http://www.porttracker.com/>, L<http://www.infoblox.com/en/products/portiq.html>. 566L<AnyEvent>, L<http://www.porttracker.com/>, L<http://www.infoblox.com/en/products/portiq.html>.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines