summaryrefslogtreecommitdiff
path: root/FS/FS/part_export/router.pm
blob: 07b5b9edb1c4eb9bbf9af108ad19da8bc43504f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package FS::part_export::router;

=head1 FS::part_export::router

This export connects to a router and transmits commands via telnet or SSH.
It requires the following custom router fields:

=over 4

=item admin_address - IP address (or hostname) to connect

=item admin_user - username for admin access

=item admin_password - password for admin access

=back

The export itself needs the following options:

=over 4

=item insert, replace, delete - command strings (to be interpolated)

=item Prompt - prompt string to expect from router after successful login

=item Timeout - time to wait for prompt string

=back

(Prompt and Timeout are required only for telnet connections.)

=cut

use vars qw(@ISA @saltset);
use String::ShellQuote;
use FS::part_export;

@ISA = qw(FS::part_export);

@saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );

sub rebless { shift; }

sub _export_insert {
  my($self) = shift;
  $self->_export_command('insert', @_);
}

sub _export_delete {
  my($self) = shift;
  $self->_export_command('delete', @_);
}

sub _export_suspend {
  my($self) = shift;
  $self->_export_command('suspend', @_);
}

sub _export_unsuspend {
  my($self) = shift;
  $self->_export_command('unsuspend', @_);
}

sub _export_command {
  my ( $self, $action, $svc_broadband) = (shift, shift, shift);
  my $command = $self->option($action);
  return '' if $command =~ /^\s*$/;

  no strict 'vars';
  {
    no strict 'refs';
    ${$_} = $svc_broadband->getfield($_) foreach $svc_broadband->fields;
  }
  # fetch router info
  my $router = $svc_broadband->addr_block->router;
  my %r;
  $r{$_} = $router->getfield($_) foreach $router->virtual_fields;
  #warn qq("$command");
  #warn eval(qq("$command"));

  warn "admin_address: '$r{admin_address}'";

  if ($r{admin_address} ne '') {
    $self->router_queue( $svc_broadband->svcnum, $self->option('protocol'),
      user         => $r{admin_user},
      password     => $r{admin_password},
      host         => $r{admin_address},
      Timeout      => $self->option('Timeout'),
      Prompt       => $self->option('Prompt'),
      command      => eval(qq("$command")),
    );
  } else {
    return '';
  }
}

sub _export_replace {

  # We don't handle the case of a svc_broadband moving between routers.
  # If you want to do that, reprovision the service.

  my($self, $new, $old ) = (shift, shift, shift);
  my $command = $self->option('replace');
  no strict 'vars';
  {
    no strict 'refs';
    ${"old_$_"} = $old->getfield($_) foreach $old->fields;
    ${"new_$_"} = $new->getfield($_) foreach $new->fields;
  }

  my $router = $new->addr_block->router;
  my %r;
  $r{$_} = $router->getfield($_) foreach $router->virtual_fields;

  if ($r{admin_address} ne '') {
    $self->router_queue( $new->svcnum, $self->option('protocol'),
      user         => $r{admin_user},
      password     => $r{admin_password},
      host         => $r{admin_address},
      Timeout      => $self->option('Timeout'),
      Prompt       => $self->option('Prompt'),
      command      => eval(qq("$command")),
    );
  } else {
    return '';
  }
}

#a good idea to queue anything that could fail or take any time
sub router_queue {
  #warn join ':', @_;
  my( $self, $svcnum, $protocol ) = (shift, shift, shift);
  my $queue = new FS::queue {
    'svcnum' => $svcnum,
  };
  $queue->job ("FS::part_export::router::".$protocol."_cmd");
  $queue->insert( @_ );
}

sub ssh_cmd { #subroutine, not method
  use Net::SSH '0.08';
  &Net::SSH::ssh_cmd( { @_ } );
}

sub telnet_cmd {
  use Net::Telnet;

  warn join(', ', @_);

  my %arg = @_;

  my $t = new Net::Telnet (Timeout => $arg{Timeout},
                           Prompt  => $arg{Prompt});
  $t->open($arg{host});
  $t->login($arg{user}, $arg{password});
  my @error = $t->cmd($arg{command});
  die @error if (grep /^ERROR/, @error);
}

#sub router_insert { #subroutine, not method
#}
#sub router_replace { #subroutine, not method
#}
#sub router_delete { #subroutine, not method
#}