diff options
author | Stephen Hemminger <stephen.hemminger@vyatta.com> | 2008-11-21 17:09:30 -0800 |
---|---|---|
committer | Stephen Hemminger <stephen.hemminger@vyatta.com> | 2008-11-21 17:09:30 -0800 |
commit | 32cefc2d846f9946bf6e5b84cd914b5e2959dbfa (patch) | |
tree | 0bbb5d52e5621b84a897bd023ae51e15c9e72be9 /lib/Vyatta/Config.pm | |
parent | 6d87be16190a658ac324488024a82961774f0ece (diff) | |
download | vyatta-cfg-32cefc2d846f9946bf6e5b84cd914b5e2959dbfa.tar.gz vyatta-cfg-32cefc2d846f9946bf6e5b84cd914b5e2959dbfa.zip |
Convert VyattaConfig (et al) to Vyatta::Config
Use hierarchal directory structure
Diffstat (limited to 'lib/Vyatta/Config.pm')
-rwxr-xr-x | lib/Vyatta/Config.pm | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/lib/Vyatta/Config.pm b/lib/Vyatta/Config.pm new file mode 100755 index 0000000..7074d9c --- /dev/null +++ b/lib/Vyatta/Config.pm @@ -0,0 +1,603 @@ +#!/usr/bin/perl + +# Author: An-Cheng Huang <ancheng@vyatta.com> +# Date: 2007 +# Description: vyatta configuration parser + +# **** 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) 2006, 2007, 2008 Vyatta, Inc. +# All Rights Reserved. +# **** End License **** + +package Vyatta::Config; + +use strict; + +use Vyatta::ConfigDOMTree; + +my %fields = ( + _changes_only_dir_base => $ENV{VYATTA_CHANGES_ONLY_DIR}, + _new_config_dir_base => $ENV{VYATTA_TEMP_CONFIG_DIR}, + _active_dir_base => $ENV{VYATTA_ACTIVE_CONFIGURATION_DIR}, + _vyatta_template_dir => $ENV{VYATTA_CONFIG_TEMPLATE}, + _current_dir_level => "/", + _level => undef, +); + +sub new { + my $that = shift; + my $class = ref ($that) || $that; + my $self = { + %fields, + }; + + bless $self, $class; + return $self; +} + +sub _set_current_dir_level { + my ($self) = @_; + my $level = $self->{_level}; + + $level =~ s/\//%2F/g; + $level =~ s/\s+/\//g; + + $self->{_current_dir_level} = "/$level"; + return $self->{_current_dir_level}; +} + +## setLevel("level") +# if "level" is supplied, set the current level of the hierarchy we are working on +# return the current level +sub setLevel { + my ($self, $level) = @_; + + $self->{_level} = $level if defined($level); + $self->_set_current_dir_level(); + + return $self->{_level}; +} + +## listNodes("level") +# return array of all nodes at "level" +# level is relative +sub listNodes { + my ($self, $path) = @_; + my @nodes = (); + + if (defined $path) { + $path =~ s/\//%2F/g; + $path =~ s/\s+/\//g; + $path = $self->{_new_config_dir_base} . $self->{_current_dir_level} . "/" . $path; + } + else { + $path = $self->{_new_config_dir_base} . $self->{_current_dir_level}; + } + + #print "DEBUG Vyatta::Config->listNodes(): path = $path\n"; + opendir DIR, "$path" or return (); + @nodes = grep !/^\./, readdir DIR; + closedir DIR; + + my @nodes_modified = (); + while (@nodes) { + my $tmp = pop (@nodes); + $tmp =~ s/\n//g; + $tmp =~ s/%2F/\//g; + #print "DEBUG Vyatta::Config->listNodes(): node = $tmp\n"; + push @nodes_modified, $tmp; + } + + return @nodes_modified; +} + +## listOrigNodes("level") +# return array of all original nodes (i.e., before any current change; i.e., +# in "working") at "level" +# level is relative +sub listOrigNodes { + my ($self, $path) = @_; + my @nodes = (); + + if (defined $path) { + $path =~ s/\//%2F/g; + $path =~ s/\s+/\//g; + $path = $self->{_active_dir_base} . $self->{_current_dir_level} . "/" + . $path; + } + else { + $path = $self->{_active_dir_base} . $self->{_current_dir_level}; + } + + #print "DEBUG Vyatta::Config->listNodes(): path = $path\n"; + opendir DIR, "$path" or return (); + @nodes = grep !/^\./, readdir DIR; + closedir DIR; + + my @nodes_modified = (); + while (@nodes) { + my $tmp = pop (@nodes); + $tmp =~ s/\n//g; + $tmp =~ s/%2F/\//g; + #print "DEBUG Vyatta::Config->listNodes(): node = $tmp\n"; + push @nodes_modified, $tmp; + } + + return @nodes_modified; +} + +## listOrigNodes("level") +# return array of all original nodes (i.e., before any current change; i.e., +# in "working") at "level" +# level is relative +sub listOrigNodesNoDef { + my ($self, $path) = @_; + my @nodes = (); + + if (defined $path) { + $path =~ s/\//%2F/g; + $path =~ s/\s+/\//g; + $path = $self->{_active_dir_base} . $self->{_current_dir_level} . "/" + . $path; + } + else { + $path = $self->{_active_dir_base} . $self->{_current_dir_level}; + } + + #print "DEBUG Vyatta::Config->listNodes(): path = $path\n"; + opendir DIR, "$path" or return (); + @nodes = grep !/^\./, readdir DIR; + closedir DIR; + + my @nodes_modified = (); + while (@nodes) { + my $tmp = pop (@nodes); + $tmp =~ s/\n//g; + $tmp =~ s/%2F/\//g; + #print "DEBUG Vyatta::Config->listNodes(): node = $tmp\n"; + if ($tmp ne 'def') { + push @nodes_modified, $tmp; + } + } + + return @nodes_modified; +} + +## returnParent("level") +# return the name of parent node relative to the current hierarchy +# in this case "level" is set to the parent dir ".. .." +# for example +sub returnParent { + my ($self, $node) = @_; + my @x, my $tmp; + + # split our hierarchy into vars on a stack + my @level = split /\s+/, $self->{_level}; + + # count the number of parents we need to lose + # and then pop 1 less + @x = split /\s+/, $node; + for ($tmp = 1; $tmp < @x; $tmp++) { + pop @level; + } + + # return the parent + $tmp = pop @level; + return $tmp; +} + +## returnValue("node") +# returns the value of "node" or undef if the node doesn't exist . +# node is relative +sub returnValue { + my ( $self, $node ) = @_; + my $tmp; + + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + + if ( -f "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node/node.val" ) { + open FILE, "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node/node.val" || return undef; + read FILE, $tmp, 16384; + close FILE; + + $tmp =~ s/\n$//; + return $tmp; + } + else { + return undef; + } +} + + +## returnOrigValue("node") +# returns the original value of "node" (i.e., before the current change; i.e., +# in "working") or undef if the node doesn't exist. +# node is relative +sub returnOrigValue { + my ( $self, $node ) = @_; + my $tmp; + + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + my $filepath = "$self->{_active_dir_base}$self->{_current_dir_level}/$node"; + if ( -f "$filepath/node.val") { + open FILE, "$filepath/node.val" || return undef; + read FILE, $tmp, 16384; + close FILE; + + $tmp =~ s/\n$//; + return $tmp; + } else { + return undef; + } +} + +## returnValues("node") +# returns an array of all the values of "node", or an empty array if the values do not exist. +# node is relative +sub returnValues { + my $val = returnValue(@_); + my @values = (); + if (defined($val)) { + @values = split("\n", $val); + } + return @values; +} + +## returnOrigValues("node") +# returns an array of all the original values of "node" (i.e., before the +# current change; i.e., in "working"), or an empty array if the values do not +# exist. +# node is relative +sub returnOrigValues { + my $val = returnOrigValue(@_); + my @values = (); + if (defined($val)) { + @values = split("\n", $val); + } + return @values; +} + +## exists("node") +# Returns true if the "node" exists. +sub exists { + my ( $self, $node ) = @_; + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + + if ( -d "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node" ) { + #print "DEBUG: the dir is there\n"; + return !0; + } else { + return undef; + } +} + +## existsOrig("node") +# Returns true if the "original node" exists. +sub existsOrig { + my ( $self, $node ) = @_; + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + + if ( -d "$self->{_active_dir_base}$self->{_current_dir_level}/$node" ) { + return 1; + } else { + return undef; + } +} + +## isDeleted("node") +# is the "node" deleted. node is relative. returns true or false +sub isDeleted { + my ($self, $node) = @_; + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + + my $filepathAct + = "$self->{_active_dir_base}$self->{_current_dir_level}/$node"; + my $filepathNew + = "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node"; + + if ((-e $filepathAct) && !(-e $filepathNew)) { + return 1; + } + return 0; +} + +## listDeleted("level") +# return array of deleted nodes in the "level" +# "level" defaults to current +sub listDeleted { + my ($self, $path) = @_; + my @new_nodes = $self->listNodes("$path"); + my @orig_nodes = $self->listOrigNodes("$path"); + my %new_hash = map { $_ => 1 } @new_nodes; + my @deleted = grep { !defined($new_hash{$_}) } @orig_nodes; + return @deleted; +} + +## isChanged("node") +# will check the change_dir to see if the "node" has been changed from a previous +# value. returns true or false. +sub isChanged { + my ($self, $node) = @_; + + # let's setup the filepath for the change_dir + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + my $filepath = "$self->{_changes_only_dir_base}$self->{_current_dir_level}/$node"; + + # if the node exists in the change dir, it's modified. + if (-e "$filepath") { return 1; } + else { return 0; } +} + +## isChangedOrDeleted("node") +# is the "node" changed or deleted. node is relative. returns true or false +sub isChangedOrDeleted { + my ($self, $node) = @_; + + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + + my $filepathChg + = "$self->{_changes_only_dir_base}$self->{_current_dir_level}/$node"; + if (-e $filepathChg) { + return 1; + } + + my $filepathAct + = "$self->{_active_dir_base}$self->{_current_dir_level}/$node"; + my $filepathNew + = "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node"; + + if ((-e $filepathAct) && !(-e $filepathNew)) { + return 1; + } + + return 0; +} + +## isAdded("node") +# will compare the new_config_dir to the active_dir to see if the "node" has +# been added. returns true or false. +sub isAdded { + my ($self, $node) = @_; + + #print "DEBUG Vyatta::Config->isAdded(): node $node\n"; + # let's setup the filepath for the modify dir + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + my $filepathNewConfig = "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node"; + + #print "DEBUG Vyatta::Config->isAdded(): filepath $filepathNewConfig\n"; + + # if the node doesn't exist in the modify dir, it's not + # been added. so short circuit and return false. + if (! -e $filepathNewConfig) { return 0; } + + # now let's setup the path for the working dir + my $filepathActive = "$self->{_active_dir_base}$self->{_current_dir_level}/$node"; + + # if the node is in the active_dir it's not new + if (-e $filepathActive) { return 0; } + else { return 1; } +} + +## listNodeStatus("level") +# return a hash of the status of nodes at the current config level +# node name is the hash key. node status is the hash value. +# node status can be one of deleted, added, changed, or static +sub listNodeStatus { + my ($self, $path) = @_; + my @nodes = (); + my %nodehash = (); + + # find deleted nodes first + @nodes = $self->listDeleted("$path"); + foreach my $node (@nodes) { + if ($node =~ /.+/) { $nodehash{$node} = "deleted" }; + } + + @nodes = (); + @nodes = $self->listNodes("$path"); + foreach my $node (@nodes) { + if ($node =~ /.+/) { + #print "DEBUG Vyatta::Config->listNodeStatus(): node $node\n"; + # No deleted nodes -- added, changed, ot static only. + if ($self->isAdded("$path $node")) { $nodehash{$node} = "added"; } + elsif ($self->isChanged("$path $node")) { $nodehash{$node} = "changed"; } + else { $nodehash{$node} = "static"; } + } + } + + return %nodehash; +} + +############ DOM Tree ################ + +#Create active DOM Tree +sub createActiveDOMTree { + + my $self = shift; + + my $tree = new Vyatta::Config::DOMTree($self->{_active_dir_base} . $self->{_current_dir_level},"active"); + + return $tree; +} + +#Create changes only DOM Tree +sub createChangesOnlyDOMTree { + + my $self = shift; + + my $tree = new Vyatta::Config::DOMTree($self->{_changes_only_dir_base} . $self->{_current_dir_level}, + "changes_only"); + + return $tree; +} + +#Create new config DOM Tree +sub createNewConfigDOMTree { + + my $self = shift; + my $level = $self->{_new_config_dir_base} . $self->{_current_dir_level}; + + return new Vyatta::Config::DOMTree($level, "new_config"); +} + + +###### functions for templates ###### + +# $1: array representing the config node path. +# returns the filesystem path to the template of the specified node, +# or undef if the specified node path is not valid. +sub getTmplPath { + my $self = shift; + my @cfg_path = @{$_[0]}; + my $tpath = $self->{_vyatta_template_dir}; + for my $p (@cfg_path) { + if (-d "$tpath/$p") { + $tpath .= "/$p"; + next; + } + if (-d "$tpath/node.tag") { + $tpath .= "/node.tag"; + next; + } + # the path is not valid! + return undef; + } + return $tpath +} + +sub isTagNode { + my $self = shift; + my $cfg_path_ref = shift; + my $tpath = $self->getTmplPath($cfg_path_ref); + return undef if (!defined($tpath)); + if (-d "$tpath/node.tag") { + return 1; + } + return 0; +} + +sub hasTmplChildren { + my $self = shift; + my $cfg_path_ref = shift; + my $tpath = $self->getTmplPath($cfg_path_ref); + return undef if (!defined($tpath)); + opendir(TDIR, $tpath) or return 0; + my @tchildren = grep !/^node\.def$/, (grep !/^\./, (readdir TDIR)); + closedir TDIR; + if (scalar(@tchildren) > 0) { + return 1; + } + return 0; +} + +# $cfg_path_ref: ref to array containing the node path. +# returns ($is_multi, $is_text, $default), +# or undef if specified node is not valid. +sub parseTmpl { + my $self = shift; + my $cfg_path_ref = shift; + my ($is_multi, $is_text, $default) = (0, 0, undef); + my $tpath = $self->getTmplPath($cfg_path_ref); + return undef if (!defined($tpath)); + if (! -r "$tpath/node.def") { + return ($is_multi, $is_text); + } + open(TMPL, "<$tpath/node.def") or return ($is_multi, $is_text); + foreach (<TMPL>) { + if (/^multi:/) { + $is_multi = 1; + } + if (/^type:\s+txt\s*$/) { + $is_text = 1; + } + if (/^default:\s+(\S+)\s*$/) { + $default = $1; + } + } + close TMPL; + return ($is_multi, $is_text, $default); +} + +###### misc functions ###### + +# compare two value lists and return "deleted" and "added" lists. +# since this is for multi-value nodes, there is no "changed" (if a value's +# ordering changed, it is deleted then added). +# $0: \@orig_values +# $1: \@new_values +sub compareValueLists { + my $self = shift; + my @ovals = @{$_[0]}; + my @nvals = @{$_[1]}; + my %comp_hash = ( + 'deleted' => [], + 'added' => [], + ); + my $idx = 0; + my %ohash = map { $_ => ($idx++) } @ovals; + $idx = 0; + my %nhash = map { $_ => ($idx++) } @nvals; + my $min_changed_idx = 2**31; + my %dhash = (); + foreach (@ovals) { + if (!defined($nhash{$_})) { + push @{$comp_hash{'deleted'}}, $_; + $dhash{$_} = 1; + if ($ohash{$_} < $min_changed_idx) { + $min_changed_idx = $ohash{$_}; + } + } + } + foreach (@nvals) { + if (defined($ohash{$_})) { + if ($ohash{$_} != $nhash{$_}) { + if ($ohash{$_} < $min_changed_idx) { + $min_changed_idx = $ohash{$_}; + } + } + } + } + foreach (@nvals) { + if (defined($ohash{$_})) { + if ($ohash{$_} != $nhash{$_}) { + if (!defined($dhash{$_})) { + push @{$comp_hash{'deleted'}}, $_; + $dhash{$_} = 1; + } + push @{$comp_hash{'added'}}, $_; + } elsif ($ohash{$_} >= $min_changed_idx) { + # ordering unchanged, but something before it is changed. + if (!defined($dhash{$_})) { + push @{$comp_hash{'deleted'}}, $_; + $dhash{$_} = 1; + } + push @{$comp_hash{'added'}}, $_; + } else { + # this is before any changed value. do nothing. + } + } else { + push @{$comp_hash{'added'}}, $_; + } + } + return %comp_hash; +} + +1; |