depend on Net::SSH 0.08 for non-blocking STDERR read
[freeside.git] / FS / FS / part_export / router.pm
1 package FS::part_export::router;
2
3 =head1 FS::part_export::router
4
5 This export connects to a router and transmits commands via telnet or SSH.
6 It requires the following custom router fields:
7
8 =over 4
9
10 =item admin_address - IP address (or hostname) to connect
11
12 =item admin_user - username for admin access
13
14 =item admin_password - password for admin access
15
16 =back
17
18 The export itself needs the following options:
19
20 =over 4
21
22 =item insert, replace, delete - command strings (to be interpolated)
23
24 =item Prompt - prompt string to expect from router after successful login
25
26 =item Timeout - time to wait for prompt string
27
28 =back
29
30 (Prompt and Timeout are required only for telnet connections.)
31
32 =cut
33
34 use vars qw(@ISA @saltset);
35 use String::ShellQuote;
36 use FS::part_export;
37
38 @ISA = qw(FS::part_export);
39
40 @saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
41
42 sub rebless { shift; }
43
44 sub _export_insert {
45   my($self) = shift;
46   $self->_export_command('insert', @_);
47 }
48
49 sub _export_delete {
50   my($self) = shift;
51   $self->_export_command('delete', @_);
52 }
53
54 sub _export_suspend {
55   my($self) = shift;
56   $self->_export_command('suspend', @_);
57 }
58
59 sub _export_unsuspend {
60   my($self) = shift;
61   $self->_export_command('unsuspend', @_);
62 }
63
64 sub _export_command {
65   my ( $self, $action, $svc_broadband) = (shift, shift, shift);
66   my $command = $self->option($action);
67   return '' if $command =~ /^\s*$/;
68
69   no strict 'vars';
70   {
71     no strict 'refs';
72     ${$_} = $svc_broadband->getfield($_) foreach $svc_broadband->fields;
73   }
74   # fetch router info
75   my $router = $svc_broadband->addr_block->router;
76   my %r;
77   $r{$_} = $router->getfield($_) foreach $router->virtual_fields;
78   #warn qq("$command");
79   #warn eval(qq("$command"));
80
81   warn "admin_address: '$r{admin_address}'";
82
83   if ($r{admin_address} ne '') {
84     $self->router_queue( $svc_broadband->svcnum, $self->option('protocol'),
85       user         => $r{admin_user},
86       password     => $r{admin_password},
87       host         => $r{admin_address},
88       Timeout      => $self->option('Timeout'),
89       Prompt       => $self->option('Prompt'),
90       command      => eval(qq("$command")),
91     );
92   } else {
93     return '';
94   }
95 }
96
97 sub _export_replace {
98
99   # We don't handle the case of a svc_broadband moving between routers.
100   # If you want to do that, reprovision the service.
101
102   my($self, $new, $old ) = (shift, shift, shift);
103   my $command = $self->option('replace');
104   no strict 'vars';
105   {
106     no strict 'refs';
107     ${"old_$_"} = $old->getfield($_) foreach $old->fields;
108     ${"new_$_"} = $new->getfield($_) foreach $new->fields;
109   }
110
111   my $router = $new->addr_block->router;
112   my %r;
113   $r{$_} = $router->getfield($_) foreach $router->virtual_fields;
114
115   if ($r{admin_address} ne '') {
116     $self->router_queue( $new->svcnum, $self->option('protocol'),
117       user         => $r{admin_user},
118       password     => $r{admin_password},
119       host         => $r{admin_address},
120       Timeout      => $self->option('Timeout'),
121       Prompt       => $self->option('Prompt'),
122       command      => eval(qq("$command")),
123     );
124   } else {
125     return '';
126   }
127 }
128
129 #a good idea to queue anything that could fail or take any time
130 sub router_queue {
131   #warn join ':', @_;
132   my( $self, $svcnum, $protocol ) = (shift, shift, shift);
133   my $queue = new FS::queue {
134     'svcnum' => $svcnum,
135   };
136   $queue->job ("FS::part_export::router::".$protocol."_cmd");
137   $queue->insert( @_ );
138 }
139
140 sub ssh_cmd { #subroutine, not method
141   use Net::SSH '0.08';
142   &Net::SSH::ssh_cmd( { @_ } );
143 }
144
145 sub telnet_cmd {
146   use Net::Telnet;
147
148   warn join(', ', @_);
149
150   my %arg = @_;
151
152   my $t = new Net::Telnet (Timeout => $arg{Timeout},
153                            Prompt  => $arg{Prompt});
154   $t->open($arg{host});
155   $t->login($arg{user}, $arg{password});
156   my @error = $t->cmd($arg{command});
157   die @error if (grep /^ERROR/, @error);
158 }
159
160 #sub router_insert { #subroutine, not method
161 #}
162 #sub router_replace { #subroutine, not method
163 #}
164 #sub router_delete { #subroutine, not method
165 #}
166