From ff89b8348c815040c05d15028df471fd8a278979 Mon Sep 17 00:00:00 2001 From: John Noss Date: Fri, 24 Jul 2015 16:50:30 -0400 Subject: [PATCH 1/2] add onesecgroup provider add type and provider for onesecgroup, for the Security Groups new in OpenNebula 4.12 --- README.md | 20 +++++ lib/puppet/provider/onesecgroup/cli.rb | 115 +++++++++++++++++++++++++ lib/puppet/type/onesecgroup.rb | 61 +++++++++++++ spec/acceptance/onesecgroup_spec.rb | 54 ++++++++++++ spec/provider/onesecgroup_spec.rb | 43 +++++++++ spec/type/onesecgroup_spec.rb | 70 +++++++++++++++ 6 files changed, 363 insertions(+) create mode 100644 lib/puppet/provider/onesecgroup/cli.rb create mode 100644 lib/puppet/type/onesecgroup.rb create mode 100644 spec/acceptance/onesecgroup_spec.rb create mode 100755 spec/provider/onesecgroup_spec.rb create mode 100755 spec/type/onesecgroup_spec.rb diff --git a/README.md b/README.md index f1842238..baa66986 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,26 @@ onevm { '': } ``` +New in 4.12, security groups: +``` +onesecgroup {'securitygroup1': + description => 'Optional description', + rules => [ { protocol => 'TCP|UDP|ICMP|IPSEC|ALL', + rule_type => 'INBOUND|OUTBOUND', + ip => '192.168.0.0', + size => '255', + range => '22,53,80:90,110,1024:65535', + icmp_type => 'optional, only applies for icmp', + }, + { protocol => 'ALL', + rule_type => 'OUTBOUND', + }, + ... + ] + } +``` + + ##Support For questions or bugs [create an issue on Github](https://github.com/epost-dev/opennebula-puppet-module/issues/new). diff --git a/lib/puppet/provider/onesecgroup/cli.rb b/lib/puppet/provider/onesecgroup/cli.rb new file mode 100644 index 00000000..b2c3bba9 --- /dev/null +++ b/lib/puppet/provider/onesecgroup/cli.rb @@ -0,0 +1,115 @@ +# Opennebula onesecgroup provider for Security Groups +# +# License: APLv2 +# +# Authors: +# Based upon initial work from Ken Barber +# Modified by Martin Alfke +# +# Copyright +# initial provider had no copyright +# Deutsche Post E-POST Development GmbH - 2014, 2015 +# + +require 'rubygems' +require 'nokogiri' + +Puppet::Type.type(:onesecgroup).provide(:cli) do + desc "onesecgroup provider" + + has_command(:onesecgroup, "onesecgroup") do + environment :HOME => '/root', :ONE_AUTH => '/var/lib/one/.one/one_auth' + end + + mk_resource_methods + + # Create a security group with onesecgroup by passing in a temporary secgroup definition file. + def create + file = Tempfile.new("onesecgroup-#{resource[:name]}-create.xml") + builder = Nokogiri::XML::Builder.new do |xml| + xml.TEMPLATE do + xml.NAME resource[:name] + xml.DESCRIPTION resource[:description] + resource[:rules].each do |rule| + xml.RULE do + rule.each do |k, v| + xml.send(k.upcase, v) + end + end + end if resource[:rules] + end + end + tempfile = builder.to_xml + file.write(tempfile) + file.close + self.debug "Creating secgroup using #{tempfile}" + onesecgroup('create', file.path) + file.delete + @property_hash[:ensure] = :present + end + + # Delete a secgroup using onesecgroup delete + def destroy + onesecgroup('delete', resource[:name]) + @property_hash.clear + end + + # Check if a secgroup exists by scanning the onesecgroup list + def exists? + @property_hash[:ensure] == :present + end + + # Return the full hash of all existing secgroups + def self.instances + secgroups = Nokogiri::XML(onesecgroup('list', '-x')).root.xpath('/SECURITY_GROUP_POOL/SECURITY_GROUP') + secgroups.collect do |secgroup| + rules=[] + secgroup.xpath('./TEMPLATE/RULE').collect do |rule| + ruleitems={} + rule.xpath('*').collect do |item| + ruleitems[item.name.downcase] = item.text.upcase + end + rules << ruleitems + end + new( + :name => secgroup.xpath('./NAME').text, + :ensure => :present, + :description => secgroup.xpath('./TEMPLATE/DESCRIPTION').text, + :rules => rules + ) + end + end + + def self.prefetch(resources) + secgroups = instances + resources.keys.each do |name| + if provider = secgroups.find{ |secgroup| secgroup.name == name } + resources[name].provider = provider + end + end + end + + # Write out changes to a security group with onesecgroup update + def flush + file = Tempfile.new("onesecgroup-#{resource[:name]}-update.xml") + builder = Nokogiri::XML::Builder.new do |xml| + xml.TEMPLATE do + xml.DESCRIPTION resource[:description] + resource[:rules].each do |rule| + xml.RULE do + rule.each do |k, v| + xml.send(k.upcase, v) + end + end + end if resource[:rules] + end + end + tempfile = builder.to_xml + file.write(tempfile) + file.close + self.debug "Updating secgroup using #{tempfile}" + onesecgroup('update', resource[:name], file.path) unless @property_hash.empty? + file.delete + end + +end \ No newline at end of file diff --git a/lib/puppet/type/onesecgroup.rb b/lib/puppet/type/onesecgroup.rb new file mode 100644 index 00000000..f57818bf --- /dev/null +++ b/lib/puppet/type/onesecgroup.rb @@ -0,0 +1,61 @@ +# Opennebula onesecgroup type for Security Groups +# +# License: APLv2 +# +# Authors: +# Based upon initial work from Ken Barber +# Modified by Martin Alfke +# +# Copyright +# initial provider had no copyright +# Deutsche Post E-POST Development GmbH - 2014, 2015 +# + +Puppet::Type.newtype(:onesecgroup) do + @doc = "Type for managing security groups in OpenNebula using the" + + "onesecgroup wrapper command." + + ensurable + + # Capacity Section + newparam(:name, :namevar => true) do + desc "Name of security group." + validate do |value| + fail("Invalid name: #{value}") unless value =~ /^([A-Za-z]).*/ + end + end + + newproperty(:description) do + desc "Description of the security group." + validate do |value| + fail("Invalid description: #{value}") unless value =~ /^([A-Za-z]).*/ + end + end + + newproperty(:rules, :array_matching => :all) do + desc "An array of hashes, each defining a rule for the security group." + defaultto [] + validate do |value| + if value.is_a?( Hash) + # TODO: validate each key + valid_keys = [ + 'protocol', + 'rule_type', + 'ip', + 'size', + 'range', + 'icmp_type', + ] + fail "#{(value.keys - valid_keys).join(' and ')} is not one of #{valid_keys.join(' or ')}" unless (value.keys - valid_keys).empty? + end + end + munge do |value| + if ! value.is_a?(Hash) + fail 'each rule should be a hash' + else + value + end + end + end + +end diff --git a/spec/acceptance/onesecgroup_spec.rb b/spec/acceptance/onesecgroup_spec.rb new file mode 100644 index 00000000..7d071e26 --- /dev/null +++ b/spec/acceptance/onesecgroup_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper_acceptance' + +describe 'onesecgroup type' do + before :all do + pp =<<-EOS + class { 'one': + oned => true, + one_version => '4.12', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe 'when creating secgroup' do + it 'should idempotently run' do + pp =<<-EOS + onesecgroup { 'secgroup1': + ensure => present, + description => 'Description.', + rules => [{'protocol' => 'ALL', 'rule_type' => 'OUTBOUND'}], + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + end + + describe 'when updating a fixed secgroup' do + it 'should idempotently run' do + pp =<<-EOS + onesecgroup { 'secgroup1': + ensure => present, + description => 'Description.', + rules => [{'protocol' => 'ALL', 'rule_type' => 'OUTBOUND'}], + } + EOS + + apply_manifest(pp, :catch_changes => true) + end + end + + describe 'when deleting a Security Group' do + it 'should idempotently run' do + pp =<<-EOS + onesecgroup { 'secgroup1': + ensure => absent, + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + end +end diff --git a/spec/provider/onesecgroup_spec.rb b/spec/provider/onesecgroup_spec.rb new file mode 100755 index 00000000..34b80e10 --- /dev/null +++ b/spec/provider/onesecgroup_spec.rb @@ -0,0 +1,43 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +provider_class = Puppet::Type.type(:onesecgroup).provider(:onesecgroup) +describe provider_class do + let(:resource ) { + Puppet::Type::onesecgroup.new({ + :name => 'new_secgroup', + }) + } + + let(:provider) { + @provider = provider_class.new(@resource) + } + + it 'should exist' do + @provider + end + + context 'when checking if resource exists' do + it 'should return true if resource exists' do + skip('needs test to verify existance') + end + it 'should return false if reosurce does not exists' do + skip('needs test to verify absence') + end + end + context 'when creating' do + it 'should create tempfile with proper values' do + skip('needs tests to verify creation') + end + end + context 'when deleting' do + it 'should run onesecgroup delete ' do + skip('needs test to verify removal') + end + end + context 'when updating' do + skip('update needs all tests') + end + +end diff --git a/spec/type/onesecgroup_spec.rb b/spec/type/onesecgroup_spec.rb new file mode 100755 index 00000000..5a7dbc4d --- /dev/null +++ b/spec/type/onesecgroup_spec.rb @@ -0,0 +1,70 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +res_type_name = :onesecgroup +res_type = Puppet::Type.type(res_type_name) + +describe res_type do + let(:provider) { + prov = stub 'provider' + prov.stubs(:name).returns(res_type_name) + prov + } + let(:res_type) { + val = res_type + val.stubs(:defaultprovider).returns provider + val + } +# let(:resource) { +# res_type.new({:name => 'test'}) +# } + + before :each do + @secgroup = res_type.new(:name => 'test') + end + + it 'should have :name be its namevar' do + res_type.key_attributes.should == [:name] + end + + parameters = [] + + parameters.each do |params| + it "should have a #{params} parameter" do + expect(described_class.attrtype(params)).to eq :param + end + end + + properties = [:description, :rules] + + properties.each do |property| + it "should have a #{property} property" do + described_class.attrclass(property).ancestors.should be_include(Puppet::Property) + end + + it "should have documentation for its #{property} property" do + described_class.attrclass(property).doc.should be_instance_of(String) + end + end + + it 'should have property :description' do + @secgroup[:description] = 'This is a description.' + @secgroup[:description].should == 'This is a description.' + end + + it 'should have property :rules' do + @secgroup[:rules] = [{'protocol' => 'ALL', 'rule_type' => 'OUTBOUND'}, {'protocol' => 'ALL', 'rule_type' => 'INBOUND'}] + @secgroup[:rules].should == [{'protocol' => 'ALL', 'rule_type' => 'OUTBOUND'}, {'protocol' => 'ALL', 'rule_type' => 'INBOUND'}] + end + + parameter_tests = { + :name => { + :valid => ["test", "foo"], + :default => "test", + :invalid => ["0./fouzb&$", "&fr5"], + }, + } + it_should_behave_like "a puppet type", parameter_tests, res_type_name + +end From ae686747e3a48c9e17217ca9dad7d53aaf45efaa Mon Sep 17 00:00:00 2001 From: John Noss Date: Tue, 28 Jul 2015 10:31:53 -0400 Subject: [PATCH 2/2] update author on onesecgroup --- lib/puppet/provider/onesecgroup/cli.rb | 2 +- lib/puppet/type/onesecgroup.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/provider/onesecgroup/cli.rb b/lib/puppet/provider/onesecgroup/cli.rb index b2c3bba9..0a28f4d8 100644 --- a/lib/puppet/provider/onesecgroup/cli.rb +++ b/lib/puppet/provider/onesecgroup/cli.rb @@ -4,7 +4,7 @@ # # Authors: # Based upon initial work from Ken Barber -# Modified by Martin Alfke +# Modified by John Noss, Harvard University FAS Research Computing, 2015 # # Copyright # initial provider had no copyright diff --git a/lib/puppet/type/onesecgroup.rb b/lib/puppet/type/onesecgroup.rb index f57818bf..6a4ff82e 100644 --- a/lib/puppet/type/onesecgroup.rb +++ b/lib/puppet/type/onesecgroup.rb @@ -4,7 +4,7 @@ # # Authors: # Based upon initial work from Ken Barber -# Modified by Martin Alfke +# Modified by John Noss, Harvard University FAS Research Computing, 2015 # # Copyright # initial provider had no copyright