From c7f0bdcf3b4fd882e423fa0a15c5949a4bcb79bb Mon Sep 17 00:00:00 2001 From: ytti Date: Wed, 15 Jan 2025 16:16:16 +0200 Subject: [PATCH] Allow using regexp for model_map and group_map keys (#3362) * Allow using regexp for model_map and group_map keys Use case is that the source returns string which has unrelated and variant data but the actual model is included somewhere in this data For example source could have space separated list of tags and one of these tags could be model name. Eg 'procurve switch'. Closes #3360 * stop normalising config keys into string * comment and document regexp maps --------- Co-authored-by: Robert Cheramy --- CHANGELOG.md | 1 + docs/Configuration.md | 16 ++++++++++++++-- lib/oxidized/config.rb | 2 +- lib/oxidized/source/source.rb | 34 ++++++++++++++++++++++++++++++++-- spec/source/source_spec.rb | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 spec/source/source_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 77bf35961..af1684503 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added - junos: add unit test (@systeembeheerder) - apc_aos: support for scp (@robertcheramy) +- config: allow model_map and group_map keys to be regexp. Fixes #3360 (@ytti) ### Changed - sonicos: accept policy message. Fixes #3339 (@Steve-M-C, @robertcheramy) diff --git a/docs/Configuration.md b/docs/Configuration.md index 9e162dd35..a7f09cea4 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -174,7 +174,16 @@ input: ## Advanced Configuration -Below is an advanced example configuration. You will be able to (optionally) override options per device. The router.db format used is `hostname:model:username:password:enable_password`. Hostname and model will be the only required options, all others override the global configuration sections. +Below is an advanced example configuration. + +You will be able to (optionally) override options per device. +The router.db format used is `hostname:model:username:password:enable_password`. +Hostname and model will be the only required options, all others override the +global configuration sections. + +Custom model names can be mapped to an oxidized model name with a string or +a regular expression. + ```yaml --- @@ -226,6 +235,7 @@ source: model_map: cisco: ios juniper: junos + !ruby/regexp /procurve/: procurve ``` ## Advanced Group Configuration @@ -268,7 +278,8 @@ groups: ssh_keys: "~/.ssh/id_rsa_bar_vyatta" ``` -For mapping multiple group values to a common name +For mapping multiple group values to a common name, you can use strings and +regular expressions: ```yaml group_map: @@ -276,6 +287,7 @@ group_map: alias2: groupA alias3: groupB alias4: groupB + !ruby/regexp /specialgroup/: groupS aliasN: groupZ # ... ``` diff --git a/lib/oxidized/config.rb b/lib/oxidized/config.rb index b626e1ec6..a54bf3137 100644 --- a/lib/oxidized/config.rb +++ b/lib/oxidized/config.rb @@ -19,7 +19,7 @@ def self.load(cmd_opts = {}) cfgfile = cmd_opts[:config_file] || 'config' # configuration file with full path as a class instance variable @configfile = File.join(usrdir, cfgfile) - asetus = Asetus.new(name: 'oxidized', load: false, key_to_s: true, usrdir: usrdir, cfgfile: cfgfile) + asetus = Asetus.new(name: 'oxidized', load: false, usrdir: usrdir, cfgfile: cfgfile) Oxidized.asetus = asetus asetus.default.username = 'username' diff --git a/lib/oxidized/source/source.rb b/lib/oxidized/source/source.rb index c4d59f320..bae5f656d 100644 --- a/lib/oxidized/source/source.rb +++ b/lib/oxidized/source/source.rb @@ -8,12 +8,42 @@ def initialize @group_map = Oxidized.config.group_map || {} end + # common code of #map_model and #map_group + def map_value(map_hash, original_value) + map_hash.each do |key, new_value| + mthd = key.instance_of?(Regexp) ? :match : :eql? + return new_value if original_value.send(mthd, key) + end + original_value + end + + # search a match for model in the configuration and returns it. + # If no match is found, return model + # + # model can be matched against a string or a regexp: + # + # model_map: + # cisco: ios + # juniper: junos + # !ruby/regexp /procurve/: procurve def map_model(model) - @model_map.has_key?(model) ? @model_map[model] : model + map_value(@model_map, model) end + # search a match for group in the configuration and returns it. + # If no match is found, return group + # + # group can be matched against a string or a regexp: + # + # group_map: + # alias1: groupA + # alias2: groupA + # alias3: groupB + # alias4: groupB + # !ruby/regexp /specialgroup/: groupS + # aliasN: groupZ def map_group(group) - @group_map.has_key?(group) ? @group_map[group] : group + map_value(@group_map, group) end def node_var_interpolate(var) diff --git a/spec/source/source_spec.rb b/spec/source/source_spec.rb new file mode 100644 index 000000000..80ca40919 --- /dev/null +++ b/spec/source/source_spec.rb @@ -0,0 +1,32 @@ +require_relative '../spec_helper' +require 'oxidized/source/source' + +describe Oxidized::Source do + describe '#map_model' do + before(:each) do + Asetus.any_instance.expects(:load) + Asetus.any_instance.expects(:create).returns(false) + + # Set :home_dir to make sure the OXIDIZED_HOME environment variable is not used + Oxidized::Config.load({ home_dir: '/cfg_path/' }) + yaml = %( + juniper: junos + !ruby/regexp /procurve/: procurve + ) + Oxidized.config.model_map = YAML.unsafe_load(yaml) + @source = Oxidized::Source::Source.new + end + + it 'returns map value for existing string key' do + _(@source.map_model('juniper')).must_equal 'junos' + end + + it 'returns its argument for non-existing string key' do + _(@source.map_model('ios')).must_equal 'ios' + end + + it 'returns map value for existing regexp key' do + _(@source.map_model('foo procurve1234 bar')).must_equal 'procurve' + end + end +end