1 package FS::part_export::nas_wrapper;
3 =head1 FS::part_export::nas_wrapper
5 This is a meta-export that triggers other exports for FS::svc_broadband objects
6 based on a set of configurable conditions. These conditions are defined by the
7 following FS::router virtual fields:
11 =item nas_conf - Per-router meta-export configuration. See L</"nas_conf Syntax">.
15 =head2 nas_conf Syntax
17 export_name|routernum[,routernum]|[field,condition[,field,condition]][||...]
21 =item export_name - Name or exportnum of the export to be executed. In order to specify export options you must use the exportnum form. (ex. 'router' for FS::part_export::router).
23 =item routernum - FS::router routernum corresponding to the desired FS::router for which this export will be run.
25 =item field - FS::svc_broadband field (real or virtual). The following condition (regex) will be matched against the value of this field.
27 =item condition - A regular expression to be match against the value of the previously listed FS::svc_broadband field.
31 If multiple routernum's are specified, then the export will be triggered for each router listed. If multiple field/condition pairs are present, then the results of the matches will be and'd. Note that if a false match is found, the rest of the matches may not be checked.
33 You can specify multiple export/router/condition sets by concatenating them with '||'.
38 use vars qw(@ISA %info $me $DEBUG);
40 use FS::Record qw(qsearchs);
44 use Data::Dumper qw(Dumper);
46 @ISA = qw(FS::part_export);
47 $me = '[' . __PACKAGE__ . ']';
51 'svc' => 'svc_broadband',
52 'desc' => 'A meta-export that triggers other svc_broadband exports.',
58 sub rebless { shift; }
62 $self->_export_command('insert', @_);
67 $self->_export_command('delete', @_);
72 $self->_export_command('suspend', @_);
75 sub _export_unsuspend {
77 $self->_export_command('unsuspend', @_);
82 $self->_export_command('replace', @_);
86 my ( $self, $action, $svc_broadband) = (shift, shift, shift);
89 if ($action eq 'replace') {
90 $new = $svc_broadband;
94 my $router = $svc_broadband->addr_block->router;
96 return '' unless grep(/^nas_conf$/, $router->fields);
97 my $nas_conf = $router->nas_conf;
99 my $child_exports = &_parse_nas_conf($nas_conf);
103 my $queue_child_exports = {};
105 # Similar to FS::svc_Common::replace, calling insert, delete, and replace
106 # exports where necessary depending on which conditions match.
107 if ($action eq 'replace') {
109 my @new_child_exports = ();
110 my @old_child_exports = ();
112 # Find all the matching "new" child exports.
113 foreach my $child_export (@$child_exports) {
114 my $match = &_test_child_export_conditions(
115 $child_export->{'conditions'},
120 push @new_child_exports, $child_export;
124 # Find all the matching "old" child exports.
125 foreach my $child_export (@$child_exports) {
126 my $match = &_test_child_export_conditions(
127 $child_export->{'conditions'},
132 push @old_child_exports, $child_export;
136 # Insert exports for new.
137 push @{$queue_child_exports->{'insert'}}, (
139 my $new_child_export = $_;
140 if (! grep { $new_child_export eq $_ } @old_child_exports) {
141 $new_child_export->{'args'} = [ $new ];
149 # Replace exports for new and old.
150 push @{$queue_child_exports->{'replace'}}, (
152 my $new_child_export = $_;
153 if (grep { $new_child_export eq $_ } @old_child_exports) {
154 $new_child_export->{'args'} = [ $new, $old ];
162 # Delete exports for old.
163 push @{$queue_child_exports->{'delete'}}, (
165 my $old_child_export = $_;
166 if (! grep { $old_child_export eq $_ } @new_child_exports) {
167 $old_child_export->{'args'} = [ $old ];
177 foreach my $child_export (@$child_exports) {
178 my $match = &_test_child_export_conditions(
179 $child_export->{'conditions'},
184 $child_export->{'args'} = [ $svc_broadband ];
185 push @{$queue_child_exports->{$action}}, $child_export;
191 warn "[debug]$me Dispatching child exports... "
192 . &Dumper($queue_child_exports) if $DEBUG;
194 # Actually call the child exports now, with their preset action and arguments.
195 foreach my $_action (keys(%$queue_child_exports)) {
197 foreach my $_child_export (@{$queue_child_exports->{$_action}}) {
198 $error = &_dispatch_child_export(
201 @{$_child_export->{'args'}},
205 # Bail if there's an error queueing one of the exports.
206 # This will all get rolled-back.
207 return $error if $error;
217 sub _parse_nas_conf {
219 my $nas_conf = shift;
220 my @child_exports = ();
222 foreach my $cond_set ($nas_conf =~ m/(.*?[^\\])(?:\|\||$)/g) {
224 warn "[debug]$me cond_set is '$cond_set'" if $DEBUG;
226 my @args = $cond_set =~ m/(.*?[^\\])(?:\||$)/g;
229 'export' => $args[0],
230 'routernum' => [ split(/,\s*/, $args[1]) ],
231 'conditions' => { @args[2..$#args] },
234 warn "[debug]$me " . Dumper(\%child_export) if $DEBUG;
236 push @child_exports, { %child_export };
240 return \@child_exports;
244 sub _dispatch_child_export {
246 my ($child_export, $action, @args) = (shift, shift, @_);
248 my $child_export_name = $child_export->{'export'};
249 my @routernums = @{$child_export->{'routernum'}};
253 # And the real hack begins...
255 my $child_part_export;
256 if ($child_export_name =~ /^(\d+)$/) {
258 $child_part_export = qsearchs('part_export', { exportnum => $exportnum });
259 unless ($child_part_export) {
260 return "No such FS::part_export with exportnum '$exportnum'";
263 $child_export_name = $child_part_export->exporttype;
265 $child_part_export = new FS::part_export {
266 'exporttype' => $child_export_name,
267 'machine' => 'bogus',
271 warn "[debug]$me running export '$child_export_name' for routernum(s) '"
272 . join(',', @routernums) . "'" if $DEBUG;
274 my $cmd_method = "_export_$action";
276 foreach my $routernum (@routernums) {
277 $error ||= $child_part_export->$cmd_method(
279 'routernum' => $routernum,
284 warn "[debug]$me export '$child_export_name' returned '$error'"
291 sub _test_child_export_conditions {
293 my ($conditions, $svc_broadband) = (shift, shift);
296 foreach my $cond_field (keys %$conditions) {
297 my $cond_regex = $conditions->{$cond_field};
298 warn "[debug]$me Condition: $cond_field =~ /$cond_regex/" if $DEBUG;
299 unless ($svc_broadband->get($cond_field) =~ /$cond_regex/) {