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
This commit is contained in:
Babu Shanmugam 2016-02-29 11:40:12 +00:00
parent 3ebeb07188
commit 7d1ff4325e
6 changed files with 317 additions and 0 deletions

View File

@ -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
```
'[<values>]'
Ex.
vs_config { 'array_key':
ensure => present,
value => '[2, 1, 6, 4]'
}
```
## Beaker-Rspec
This module has beaker-rspec tests

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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