From 7d1ff4325e7df735f0108860b846fdd08cdbfd6b Mon Sep 17 00:00:00 2001 From: Babu Shanmugam Date: Mon, 29 Feb 2016 11:40:12 +0000 Subject: [PATCH] Added custom type vs_config and OVS provider for it vs_config checks and updates the columns in Open_vSwitch table. Records are updated only when the value differs from the one set in 'value' parameter. This will be used by projects like OVN which depends on the global configs to even boot up Change-Id: I87f870463147b41a319f1fc6c4225c35d244e3b9 --- README.md | 33 ++++++ lib/puppet/provider/vs_config/ovs.rb | 107 ++++++++++++++++++ lib/puppet/type/vs_config.rb | 36 ++++++ spec/acceptance/basic_vswitch_spec.rb | 12 ++ .../puppet/lib/provider/vs_config_ovs_spec.rb | 98 ++++++++++++++++ spec/unit/puppet/lib/type/vs_config_spec.rb | 31 +++++ 6 files changed, 317 insertions(+) create mode 100644 lib/puppet/provider/vs_config/ovs.rb create mode 100644 lib/puppet/type/vs_config.rb create mode 100644 spec/unit/puppet/lib/provider/vs_config_ovs_spec.rb create mode 100644 spec/unit/puppet/lib/type/vs_config_spec.rb diff --git a/README.md b/README.md index 24ac3be3..709862dc 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ The current layout is: * bridges - A "Bridge" is basically the thing you plug ports / interfaces into. * ports - A Port is a interface you plug into the bridge (switch). +* configs - Configuration settings, if any ## USAGE: To create a new bridge, use the `vs_bridge` type: @@ -30,6 +31,38 @@ vs_port { 'eth2': } ``` +You can change the vswitch configuration settings using. +``` +vs_config { 'parameter_name': + ensure => present, + value => "some_value" +} +``` +For configuration parameters that are 'hash' data type, the resource name +should be of the following format + +``` +parameter-name:key-name + +Ex. +vs_config { 'external_ids:ovn-remote': + ensure => present, + value => 'tcp:127.0.0.1:6640' +} +``` + +For 'set/array' data types, value should be in the following format + +``` +'[]' + +Ex. +vs_config { 'array_key': + ensure => present, + value => '[2, 1, 6, 4]' +} +``` + ## Beaker-Rspec This module has beaker-rspec tests diff --git a/lib/puppet/provider/vs_config/ovs.rb b/lib/puppet/provider/vs_config/ovs.rb new file mode 100644 index 00000000..91a2f671 --- /dev/null +++ b/lib/puppet/provider/vs_config/ovs.rb @@ -0,0 +1,107 @@ +require 'puppet' + +Puppet::Type.type(:vs_config).provide(:ovs) do + + commands :vsctl => 'ovs-vsctl' + + mk_resource_methods + + def self.munge_array_value(value) + "[#{value[1..-2].split(',').map(&:strip).sort.join(",")}]" + end + + def self.parse_column_value(value) + value = value.chomp + if value[0] == '{' + # hash case, like {system-id=\"some-id\", name=\"some-name\"} + type = 'hash' + res = {} + value[1..-2].gsub('"','').split(', ').map(&:strip).each do |v| + k,val = v.split("=") + res[k] = val + end + elsif value[0] == '[' + # set case, like ['id1', 'id2', 'id3'] + type = 'set' + res = munge_array_value(value) + else + # simple string + type = 'string' + res = value + end + + { + :type => type, + :value => res + } + end + + def self.list_config_entries + open_vs = vsctl("list", "Open_vSwitch", ".").split("\n") + configs = [] + open_vs.each do |line| + key, value = line.split(' : ').map(&:strip) + parsed_value = parse_column_value(value) + if parsed_value[:type] == "hash" + parsed_value[:value].each do |k, v| + configs.push({ + :name => "#{key}:#{k}", + :value => v, + :ensure => :present + }) + end + else + configs.push({ + :name => key, + :ensure => :present, + :value => parsed_value[:value], + }) + end + end + configs + end + + def self.instances() + configs = list_config_entries + configs.collect do |config| + new(config) + end + end + + def self.prefetch(resources) + instances.each do |prov| + if resource = resources[prov.name] + resource.provider = prov + end + end + end + + def initialize(value) + super(value) + end + + def exists? + @property_hash[:ensure] == :present + end + + def destroy + if @resource[:name].include?(':') + name, key = @resource[:name].split(':') + vsctl("remove", "Open_vSwitch", ".", name, key) + else + vsctl("clear", "Open_vSwitch", ".", @resource[:name]) + end + end + + def _set + vsctl("set", "Open_vSwitch", ".", "#{@resource[:name]}=#{@resource[:value]}") + end + + def create + _set + end + + def value=(value) + _set + end +end diff --git a/lib/puppet/type/vs_config.rb b/lib/puppet/type/vs_config.rb new file mode 100644 index 00000000..2660869f --- /dev/null +++ b/lib/puppet/type/vs_config.rb @@ -0,0 +1,36 @@ +require 'puppet' + +Puppet::Type.newtype(:vs_config) do + desc 'Switch configurations' + + ensurable + + newparam(:name, :namevar => true) do + desc 'Configuration parameter whose value need to be set' + + validate do |value| + if !value.is_a?(String) + raise ArgumentError, "Invalid name #{value}. Requires a String, not a #{value.class}" + end + end + end + + newproperty(:value) do + desc 'Configuration value for the paramter' + + validate do |value| + if !value.is_a?(String) + raise ArgumentError, "Invalid external_ids #{value}. Requires a String, not a #{value.class}" + end + end + + munge do |value| + if value[0] == '[' && value[-1] == ']' + "[#{value[1..-2].split(',').map(&:strip).sort.join(",")}]" + else + super(value) + end + end + end +end + diff --git a/spec/acceptance/basic_vswitch_spec.rb b/spec/acceptance/basic_vswitch_spec.rb index 9d1f2886..f0aef05c 100644 --- a/spec/acceptance/basic_vswitch_spec.rb +++ b/spec/acceptance/basic_vswitch_spec.rb @@ -14,6 +14,11 @@ describe 'basic vswitch' do vs_bridge { 'br-beaker': ensure => present, } + + vs_config { 'external_ids:ovn-remote': + ensure => present, + value => 'tcp:127.0.0.1:2300', + } EOS @@ -28,5 +33,12 @@ describe 'basic vswitch' do it { is_expected.to match /br-beaker/ } end end + + describe command('ovs-vsctl get Open_vSwitch . external_ids:ovn-remote') do + describe '#stdout' do + subject { super().stdout } + it { is_expected.to match /\"tcp:127.0.0.1:2300\"/ } + end + end end end diff --git a/spec/unit/puppet/lib/provider/vs_config_ovs_spec.rb b/spec/unit/puppet/lib/provider/vs_config_ovs_spec.rb new file mode 100644 index 00000000..6616eab3 --- /dev/null +++ b/spec/unit/puppet/lib/provider/vs_config_ovs_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' + +describe Puppet::Type.type(:vs_config).provider(:ovs) do + it 'should have an instance method' do + expect(described_class).to respond_to :instances + end + + it 'should have a prefetch method' do + expect(described_class).to respond_to :prefetch + end + + context "Testing string values" do + before :each do + described_class.expects(:vsctl).with( + "list", "Open_vSwitch", ".").returns 'key1 : value1 +key2 : value2 +key3 : value3' + end + + it "should return three resources" do + expect(described_class.instances.size).to eq(3) + end + + it "should return the appropriate property hash" do + described_class.instances.each do |inst| + _inst = inst.instance_variable_get("@property_hash") + expect(_inst.key?(:name)).to eq true + expect(_inst.key?(:value)).to eq true + expect(_inst.key?(:ensure)).to eq true + end + end + + it "should contain proper values" do + described_class.instances.each do |inst| + _inst = inst.instance_variable_get("@property_hash") + expect(_inst[:name][0..2]).to eq "key" + expect(_inst[:value][0..4]).to eq "value" + expect(_inst[:value][5]).to eq _inst[:name][3] + expect(_inst[:ensure]).to eq :present + end + end + end + + context "Testing array values" do + before :each do + described_class.expects(:vsctl).with( + "list", "Open_vSwitch", ".").returns 'key1 : [abc, def, ghi] +key2 : [def, abc, ghi] +key3 : [1001, 399, 240, 1200]' + end + + it "should return three resources" do + expect(described_class.instances.size).to eq(3) + end + + it "should contain proper values" do + expected_values = { + "key1" => "[abc,def,ghi]", + "key2" => "[abc,def,ghi]", + "key3" => "[1001,1200,240,399]" + } + described_class.instances.each do |inst| + _inst = inst.instance_variable_get("@property_hash") + expect(expected_values.key?(_inst[:name])).to eq true + expect(_inst[:value]).to eq expected_values[_inst[:name]] + expect(_inst[:ensure]).to eq :present + end + end + end + + context "Testing hash values" do + before :each do + described_class.expects(:vsctl).with( + "list", "Open_vSwitch", ".").returns 'key1 : {} +key2 : {"hash21"="value21"} +key3 : {"hash31"="value31", "hash32"="value32", "hash33"=33}' + end + + it "should return three resources" do + expect(described_class.instances.size).to eq(4) + end + + it "should contain valid names and values" do + expected_values = { + "key2:hash21" => "value21", + "key3:hash31" => "value31", + "key3:hash32" => "value32", + "key3:hash33" => "33"} + described_class.instances.each do |inst| + _inst = inst.instance_variable_get("@property_hash") + expect(expected_values.key?(_inst[:name])).to eq true + expect(_inst[:value]).to eq expected_values[_inst[:name]] + expect(_inst[:ensure]).to eq :present + end + end + end + +end diff --git a/spec/unit/puppet/lib/type/vs_config_spec.rb b/spec/unit/puppet/lib/type/vs_config_spec.rb new file mode 100644 index 00000000..0ca37d76 --- /dev/null +++ b/spec/unit/puppet/lib/type/vs_config_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Puppet::Type.type(:vs_config) do + it "should support present as a value for ensure" do + expect do + described_class.new(:name => 'foo', :ensure => :present) + end.to_not raise_error + end + + it "should support absent as a value for ensure" do + expect do + described_class.new(:name => 'foo', :ensure => :absent) + end.to_not raise_error + end + + it "should have a :value parameter" do + expect(described_class.attrtype(:value)).to eq(:property) + end + + it "should accept only string values" do + expect do + described_class.new({:name => "foo", :value => 123, :ensure => :present}) + end.to raise_error(Puppet::Error) + end + + it "should munge array values" do + expect( + described_class.new({:name => "foo", :value => "[2, 1, 3, 0]", :ensure => :present})[:value] + ).to eq "[0,1,2,3]" + end +end