summaryrefslogtreecommitdiff
path: root/lib/Vyatta/Quagga/Config.pm
diff options
context:
space:
mode:
authorRobert Bays <rbays@roatan.(none)>2010-04-04 23:59:31 -0700
committerRobert Bays <rbays@roatan.(none)>2010-04-04 23:59:31 -0700
commit491d6af23d38f91dc87d310457db7d40f3226e16 (patch)
tree5cc9fb4cf579bd035e39edce52a6c0d33408cb9f /lib/Vyatta/Quagga/Config.pm
parent66a55c460bfccd9ce99d2babb7366886a5af5469 (diff)
downloadvyatta-cfg-quagga-491d6af23d38f91dc87d310457db7d40f3226e16.tar.gz
vyatta-cfg-quagga-491d6af23d38f91dc87d310457db7d40f3226e16.zip
initial update to migrate bgp to transaction library
Diffstat (limited to 'lib/Vyatta/Quagga/Config.pm')
-rw-r--r--lib/Vyatta/Quagga/Config.pm378
1 files changed, 378 insertions, 0 deletions
diff --git a/lib/Vyatta/Quagga/Config.pm b/lib/Vyatta/Quagga/Config.pm
new file mode 100644
index 00000000..107731fe
--- /dev/null
+++ b/lib/Vyatta/Quagga/Config.pm
@@ -0,0 +1,378 @@
+# Author: Robert Bays <robert@vyatta.com>
+# Date: 2010
+# Description: interface between Vyatta templates and Quagga vtysh
+
+# **** License ****
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# This code was originally developed by Vyatta, Inc.
+# Portions created by Vyatta are Copyright (C) 2010 Vyatta, Inc.
+# All Rights Reserved.
+# **** End License ****
+
+package Vyatta::Quagga::Config;
+
+use strict;
+use warnings;
+
+use lib "/opt/vyatta/share/perl5/";
+use Vyatta::Config;
+
+my $_DEBUG = 0;
+my %_vtysh;
+my %_vtyshdel;
+my $_qcomref = '';
+my $_qcomdelref = '';
+
+### Public methods -
+# Create the class.
+# input: $1 - level of the Vyatta config tree to start at
+# $2 - hashref to Quagga add/change command templates
+# $3 - hashref to Quagga delete command templates
+sub new {
+ my $that = shift;
+ my $class = ref ($that) || $that;
+ my $self = {
+ _level => shift,
+ _qcref => shift,
+ _qcdref => shift,
+ };
+
+ $_qcomref = $self->{_qcref};
+ $_qcomdelref = $self->{_qcdref};
+
+ if (! _qtree($self->{_level}, 'delete')) { return 0; }
+ if (! _qtree($self->{_level}, 'set')) { return 0; }
+
+ bless $self, $class;
+ return $self;
+}
+
+# Set/check debug level
+# input: $1 - debug level
+sub setDebugLevel {
+ my ($self, $level) = @_;
+ if ($level > 0) {
+ $_DEBUG = $level;
+ return $level;
+ }
+ return 0;
+}
+
+# reinitialize the vtysh hashes for troublshooting tree
+# walk post object creation
+sub _reInitialize {
+ my ($self) = @_;
+
+ %_vtysh = ();
+ %_vtyshdel = ();
+ _qtree($self->{_level}, 'delete');
+ _qtree($self->{_level}, 'set');
+}
+
+# populate an array reference with Quagga commands
+sub returnQuaggaCommands {
+ my ($self, $arrayref) = @_;
+ my $key;
+ my $string;
+
+ foreach $key (sort { $b cmp $a } keys %_vtyshdel) {
+ foreach $string (@{$_vtyshdel{$key}}) {
+ push @{$arrayref}, "$string";
+ }
+ }
+
+ foreach $key (sort keys %_vtysh) {
+ foreach $string (@{$_vtysh{$key}}) {
+ push @{$arrayref}, "$string";
+ }
+ }
+
+ return 1;
+}
+
+# methods to send the commands to Quagga
+sub setConfigTree {
+ my ($self, $level) = @_;
+ if (_setConfigTree($level, 0, 0)) { return 1; }
+ return 0;
+}
+
+sub setConfigTreeRecursive {
+ my ($self, $level) = @_;
+ if (_setConfigTree($level, 0, 1)) { return 1; }
+ return 0;
+}
+
+sub deleteConfigTree {
+ my ($self, $level) = @_;
+ if (_setConfigTree($level, 1, 0)) { return 1; }
+ return 0;
+}
+
+sub deleteConfigTreeRecursive {
+ my ($self, $level) = @_;
+ if (_setConfigTree($level, 1, 1)) { return 1; }
+ return 0;
+}
+
+### End Public methods -
+### Private methods
+# traverse the set/delete trees and send commands to quagga
+# set traverses from $level in tree top down.
+# delete traverses from bottom up in tree to $level.
+# execute commands in tree one at a time. If there is an error in vtysh,
+# fail. otherwise, remove command from tree on success as we may traverse
+# this portion of the tree again otherwise.
+# input: $1 - level of the tree to start at
+# $2 - delete bool
+# $3 - recursive bool
+# output: none, return failure if needed
+sub _setConfigTree {
+ my ($level, $delete, $recurse) = @_;
+
+ if ((! defined $level) ||
+ (! defined $delete) ||
+ (! defined $recurse)) { return 0; }
+
+ # default tree is the set vtysh hash
+ my $vtyshref = \%_vtysh;
+ # default tree walk order is top down
+ my $sortfunc = \&cmpf;
+
+ # if this is delete, use delete vtysh hash and walk the tree bottom up
+ if ($delete) {
+ $vtyshref = \%_vtyshdel;
+ $sortfunc = \&cmpb;
+ }
+
+ if ($_DEBUG >= 3) { print "DEBUG: _setConfigTree - enter - level: $level\tdelete: $delete\trecurse: $recurse\n"; }
+
+ my $key;
+ my @keys;
+ foreach $key (sort $sortfunc keys %$vtyshref) {
+ if ($_DEBUG >= 3) { print "DEBUG: _setConfigTree - key $key\n"; }
+
+ if ((($recurse) && ($key =~ /^$level/)) || ((! $recurse) && ($key =~ /^$level$/))) {
+ my ($index, $cmd);
+ $index = 0;
+ foreach $cmd (@{$vtyshref->{$key}}) {
+ if ($_DEBUG >= 2) { print "DEBUG: _setConfigTree - key: $key \t cmd: $cmd\n"; }
+
+ if (! _sendQuaggaCommand("$cmd")) { return 0; }
+ # remove this command so we don't hit it again in another Recurse call
+ delete ${$vtyshref->{$key}}[$index];
+ $index++;
+ }
+ }
+ }
+
+ return 1;
+}
+
+# sort subs for _setConfigTree
+sub cmpf { $a cmp $b }
+sub cmpb { $b cmp $a }
+
+# properly format a Quagga command for vtysh and send to Quagga
+# input: $1 - qVarReplaced Quagga Command string
+# output: none, return failure if needed
+sub _sendQuaggaCommand {
+ my ($command) = @_;
+ my $section;
+ my $args = "/usr/sbin/vtysh --noerr -c 'configure terminal' ";
+
+ my @commands = split / ; /, $command;
+ foreach $section (@commands) {
+ $args .= "-c '$section' ";
+ }
+
+ if ($_DEBUG >= 2) { print "DEBUG: _sendQuaggaCommand - args prior to system call - $args\n"; }
+ system("$args");
+ if ($? != 0) {
+ # TODO: note that DEBUG will never happen here with --noerr as an argument.
+ # need to fix --noerr. Also probably need to code a way to conditionally use --noerr.
+ if ($_DEBUG) {
+ print "DEBUG: _sendQuaggaCommand - vtysh failure $? - $args\n";
+ print "\n";
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+# translate a Vyatta config tree into a Quagga command using %qcom as a template.
+# input: $1 - Vyatta config tree string
+# $2 - Quagga command template string
+# output: Quagga command suitable for vtysh as defined by %qcom.
+sub _qVarReplace {
+ my $node = shift;
+ my $qcommand = shift;
+
+ if ($_DEBUG >= 2) {
+ print "DEBUG: _qVarReplace entry: node - $node\n";
+ print "DEBUG: _qVarReplace entry: qcommand - $qcommand\n";
+ }
+ my @nodes = split /\s/, $node;
+ my @qcommands = split /\s/, $qcommand;
+
+ my $result = '';
+ my $token;
+ # try to replace (#num, ?var) references foreach item in Quagga command template array
+ # with their corresponding value in Vyatta command array at (#num) index
+ foreach $token (@qcommands) {
+ # is this a #var reference? if so translate and append to result
+ if ($token =~ s/\#(\d+);*/$1/) {
+ $token--;
+ $result="$result $nodes[$token]";
+ }
+ # is this a ?var reference? if so check for existance of the var in Vyatta Config
+ # tree and conditionally append. append token + value. These conditional vars
+ # will only work at EOL in template string.
+ elsif ($token =~ s/\?(\w+);*/$1/) {
+ # TODO: Vyatta::Config needs to be fixed to accept level in constructor
+ my $config = new Vyatta::Config;
+ $config->setLevel($node);
+ my $value = $config->returnValue($token);
+ if ($value) { $result = "$result $token $value"; }
+ elsif ($config->exists($token)) { $result = "$result $token"; }
+ }
+ # is this a @var reference? if so, append just the value instead of token + value
+ elsif ($token =~ s/\@(\w+);*/$1/) {
+ my $config = new Vyatta::Config;
+ $config->setLevel($node);
+ my $value = $config->returnValue($token);
+ if ($value) { $result = "$result $value"; }
+ }
+ # if not, just append string to result
+ else {
+ $result = "$result $token";
+ }
+ }
+
+ # remove leading space characters
+ $result =~ s/^\s(.+)/$1/;
+ if ($_DEBUG >= 2) {
+ print "DEBUG: _qVarReplace exit: result - $result\n";
+ }
+
+ return $result;
+}
+
+# For given Vyatta config tree string, find a corresponding Quagga command template
+# string as defined in correctly referenced %qcom. i.e. add or delete %qcom.
+# input: $1 - Vyatta config tree string
+# $2 - Quagga command template hash
+# output: %qcom hash key to corresponding Quagga command template string
+sub _qCommandFind {
+ my $vyattaconfig = shift;
+ my $qcom = shift;
+ my $token = '';
+ my $command = '';
+
+ my @nodes = split /\s+/, $vyattaconfig;
+
+ # append each token in the Vyatta config tree. sequentially
+ # check if there is a corresponding hash in %qcom. if not,
+ # do same check again replacing the end param with var to see
+ # if this is a var replacement
+ foreach $token (@nodes) {
+ if (exists $qcom->{$token}) { $command = $token; }
+ elsif (exists $qcom->{"$command $token"}) { $command = "$command $token"; }
+ elsif (exists $qcom->{"$command var"}) { $command = "$command var"; }
+ else { return undef; }
+ }
+
+ # return hash key if Quagga command template string is found
+ if (defined $qcom->{$command}) { return $command; }
+ else { return undef; }
+}
+
+# translate the adds/changes in a Vyatta config tree into Quagga vtysh commands.
+# recursively walks the tree.
+# input: $1 - the level of the Vyatta config tree to start at
+# $2 - the action (set|delete)
+# output: none - creates the %vtysh that contains the Quagga add commands
+sub _qtree {
+ my ($level, $action) = @_;
+ my @nodes;
+ my ($qcom, $vtysh);
+
+
+ # It's ugly that I have to create a new Vyatta config object every time,
+ # but something gets messed up on the stack if I don't. not sure
+ # what yet. would love to reference a global config and just reset Level.
+ my $config = new Vyatta::Config;
+ $config->setLevel($level);
+
+ # setup references for set or delete action
+ if ($action eq 'set') {
+ $qcom = $_qcomref;
+ $vtysh = \%_vtysh;
+
+ @nodes = $config->listNodes();
+ }
+ else {
+ $qcom = $_qcomdelref;
+ $vtysh = \%_vtyshdel;
+
+ @nodes = $config->listDeleted();
+ }
+
+ if ($_DEBUG) { print "DEBUG: _qtree - action: $action\tlevel: $level\n"; }
+
+ # traverse the Vyatta config tree and translate to Quagga commands where apropos
+ if (@nodes > 0) {
+ my $node;
+ foreach $node (@nodes) {
+ if ($_DEBUG >= 2) { print "DEBUG: _qtree - foreach node loop - node $node\n"; }
+
+ # for set action, need to check that the node was actually changed. Otherwise
+ # we end up re-writing every node to Quagga every commit, which is bad. Mmm' ok?
+ if (($action eq 'delete') || ($config->isChanged("$node"))) {
+ # is there a Quagga command template?
+ # TODO: need to add function reference support to qcom hash for complicated nodes
+ my $qcommand = _qCommandFind("$level $node", $qcom);
+
+ # if I found a Quagga command template, then replace any vars
+ if ($qcommand) {
+ # get the apropos config value so we can use it in the Quagga command template
+ my $val = undef;
+ if ($action eq 'set') { $val = $config->returnValue($node); }
+ else { $val = $config->returnOrigValue($node); }
+
+ # is this a leaf node?
+ if ($val) {
+ my $var = _qVarReplace("$level $node $val", $qcom->{$qcommand});
+ push @{$vtysh->{"$qcommand"}}, $var;
+ if ($_DEBUG) {
+ print "DEBUG: _qtree leaf node command: set $level $action $node $val \n\t\t\t\t\t$var\n";
+ }
+ }
+ else {
+ my $var = _qVarReplace("$level $node", $qcom->{$qcommand});
+ push @{$vtysh->{"$qcommand"}}, $var;
+ if ($_DEBUG) {
+ print "DEBUG: _qtree node command: set $level $action $node \n\t\t\t\t$var\n";
+ }
+ }
+ }
+ }
+ # recurse to next level in tree
+ _qtree("$level $node", 'delete');
+ _qtree("$level $node", 'set');
+ }
+ }
+
+ return 1;
+}
+
+return 1;