Add ability to create security groups from puppet

Change-Id: I1835f121726e2b99bd6cc62e67849a0bc248ea4f
This commit is contained in:
Alexey Deryugin 2016-03-17 15:37:57 +03:00
parent 40a62aba41
commit 215103f788
11 changed files with 606 additions and 1 deletions

View File

@ -241,5 +241,4 @@ class Puppet::Provider::Nova < Puppet::Provider
end
return list
end
end

View File

@ -0,0 +1,37 @@
require File.join(File.dirname(__FILE__), '..','..','..',
'puppet/provider/nova')
Puppet::Type.type(:nova_security_group).provide(
:nova,
:parent => Puppet::Provider::Nova
) do
desc "Manage nova security groups"
commands :nova => 'nova'
mk_resource_methods
def exists?
sec_groups = self.class.cliout2list(auth_nova('secgroup-list'))
return sec_groups.detect do |n|
n['Name'] == resource['name']
end
end
def destroy
auth_nova("secgroup-delete", name)
@property_hash[:ensure] = :absent
end
def create
result = self.class.cliout2list(auth_nova("secgroup-create", resource[:name], resource[:description]))
@property_hash = {
:ensure => :present,
:name => resource[:name],
:id => result[0]['Id'],
:description => resource[:description]
}
end
end

View File

@ -0,0 +1,51 @@
require File.join(File.dirname(__FILE__), '..','..','..',
'puppet/provider/nova')
Puppet::Type.type(:nova_security_rule).provide(
:nova,
:parent => Puppet::Provider::Nova
) do
desc "Manage nova security rules"
commands :nova => 'nova'
mk_resource_methods
def exists?
@property_hash[:ensure] == :present
end
def destroy
args = Array.new
args << "#{@resource[:security_group]}"
args << "#{@resource[:ip_protocol]}"
args << "#{@resource[:from_port]}"
args << "#{@resource[:to_port]}"
if not @resource[:ip_range].nil?
args << "#{@resource[:ip_range]}"
else
args << "#{@resource[:source_group]}"
end
auth_nova("secgroup-delete-rule", args)
@property_hash[:ensure] = :absent
end
def create
args = Array.new
args << "#{@resource[:security_group]}"
args << "#{@resource[:ip_protocol]}"
args << "#{@resource[:from_port]}"
args << "#{@resource[:to_port]}"
if not @resource[:ip_range].nil?
args << "#{@resource[:ip_range]}"
else
args << "#{@resource[:source_group]}"
end
result = auth_nova("secgroup-add-rule", args)
end
end

View File

@ -0,0 +1,66 @@
# Copyright (C) 2016 Mirantis Inc.
#
# Author: Alexey Deryugin <aderyugin@mirantis.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# nova_security_group type
#
# == Parameters
# [*name*]
# Name for the new security group
# Required
#
# [*description*]
# Description for the new security group
# Optional
#
require 'puppet'
Puppet::Type.newtype(:nova_security_group) do
@doc = "Manage creation of nova security groups."
ensurable
newparam(:name, :namevar => true) do
desc 'Name for the new security group'
validate do |value|
if not value.is_a? String
raise ArgumentError, "name parameter must be a String"
end
unless value =~ /^[a-zA-Z0-9\-_]+$/
raise ArgumentError, "#{value} is not a valid name"
end
end
end
newproperty(:id) do
desc 'The unique Id of the security group'
validate do |v|
raise ArgumentError, 'This is a read only property'
end
end
newproperty(:description) do
desc "Description of the security group"
defaultto ''
end
validate do
raise ArgumentError, 'Name type must be set' unless self[:name]
end
end

View File

@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 Mirantis Inc.
#
# Author: Alexey Deryugin <aderyugin@mirantis.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# nova_security_group type
#
# == Parameters
# [*ip_protocol*]
# IP protocol from new security rule
# Required
#
# [*from_port*]
# Port range begin for security rule
# Required
#
# [*to_port*]
# Port range end for security rule
# Required
#
# [*ip_range*]
# IP range for security rule
# Optional
#
# [*source_group*]
# Source group for security rule
# Optional
#
# [*security_group*]
# Target security group for security rule
# Required
#
require 'puppet'
Puppet::Type.newtype(:nova_security_rule) do
desc "Manage nova security rules"
ensurable
newparam(:name) do
isnamevar
end
newparam(:ip_protocol) do
defaultto do
raise Puppet::Error, 'You should give protocol!'
end
newvalues 'tcp', 'udp', 'icmp'
end
newparam(:from_port) do
defaultto do
raise Puppet::Error, 'You should give the source port!'
end
validate do |value|
if value !~ /\d+/ or value.to_i <= 0 or value.to_i >= 65536
raise Puppet::Error, 'Incorrect from port!'
end
end
end
newparam(:to_port) do
defaultto do
raise Puppet::Error, 'You should give the destination port!'
end
validate do |value|
if value !~ /\d+/ or value.to_i <= 0 or value.to_i >= 65536
raise Puppet::Error, 'Incorrect to port!'
end
end
end
newparam(:ip_range) do
validate do |value|
def is_cidr_net?(value)
begin
address, mask = value.split('/')
return false unless address and mask
octets = address.split('.')
return false unless octets.length == 4
cidr = true
octets.each do |octet|
n = octet.to_i
cidr = false unless n <= 255
cidr = false unless n >= 0
break unless cidr
end
cidr = false unless mask.to_i <= 32
cidr = false unless mask.to_i >= 0
cidr
rescue
false
end
end
raise Puppet::Error, 'Incorrect ip_range!' unless is_cidr_net? value
end
end
newparam(:source_group) do
end
newparam(:security_group) do
defaultto do
raise Puppet::Error, 'You should provide the security group to add this rule to!'
end
end
validate do
unless !!self[:ip_range] ^ !!self[:source_group]
raise Puppet::Error, 'You should give either ip_range or source_group. Not none or both!'
end
unless self[:from_port].to_i <= self[:to_port].to_i
raise Puppet::Error, 'From_port should be lesser or equal to to_port!'
end
end
end

View File

@ -0,0 +1,38 @@
# run with: rspec spec/type/nova_security_group_spec.rb
require 'spec_helper'
describe Puppet::Type.type(:nova_security_group) do
before :each do
@provider_class = described_class.provide(:simple) do
mk_resource_methods
def create; end
def delete; end
def exists?; get(:ensure) != :absent; end
def flush; end
end
end
it "should be able to create an instance" do
expect(described_class.new(:name => 'scg0')).not_to be_nil
end
it "should be able to create a more complex instance" do
expect(described_class.new(:name => 'scg0',
:description => "Security Group")).to_not be_nil
end
it "should be able to create a instance and have the default values" do
c = described_class.new(:name => 'scg0')
expect(c[:name]).to eq("scg0")
expect(c[:description]).to eq('')
end
it "should return the given values" do
c = described_class.new(:name => 'scg0',
:description => 'Security Group')
expect(c[:name]).to eq("agg0")
expect(c[:description]).to eq('Security Group')
end
end

View File

@ -0,0 +1,35 @@
# run with: rspec spec/type/nova_security_rule_spec.rb
require 'spec_helper'
describe Puppet::Type.type(:nova_security_rule) do
before :each do
@provider_class = described_class.provide(:simple) do
mk_resource_methods
def create; end
def delete; end
def exists?; get(:ensure) != :absent; end
def flush; end
end
end
it "should be able to create an instance with ip range" do
expect(described_class.new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => 22,
:to_port => 23,
:ip_range => "0.0.0.0/0",
:security_group => "scg0")).not_to be_nil
end
it "should be able to create an instance with source group" do
expect(described_class.new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => 22,
:to_port => 23,
:source_group => "tenant",
:security_group => "scg0")).not_to be_nil
end
end

View File

@ -0,0 +1,91 @@
require 'puppet'
require 'puppet/provider/nova_security_group/nova'
require 'tempfile'
provider_class = Puppet::Type.type(:nova_security_group).provider(:nova)
describe provider_class do
let :secgroup_attrs do
{
:name => "scg0",
:description => "Security Group",
}
end
let :resource do
Puppet::Type::Nova_security_group.new(secgroup_attrs)
end
let :provider do
provider_class.new(resource)
end
shared_examples "nova_security_group" do
describe "#exists?" do
it 'should check non-existing security group' do
output = <<-EOT
+--------------------------------------+---------+------------------------+
| Id | Name | Description |
+--------------------------------------+---------+------------------------+
| f630dd92-3ff7-49bc-b012-b211451aa418 | default | Default security group |
+--------------------------------------+---------+------------------------+
EOT
provider.expects(:auth_nova).with('secgroup-list').returns(output)
expect(provider.exists?).to be_falsey
end
it 'should check existing security group' do
output = <<-EOT
+--------------------------------------+------+----------------+
| Id | Name | Description |
+--------------------------------------+------+----------------+
| f630dd92-3ff7-49bc-b012-b211451aa419 | scg0 | Security Group |
+--------------------------------------+------+----------------+
EOT
provider.expects(:auth_nova).with('secgroup-list').returns(output)
expect(provider.exists?).to be_truthy
end
end
describe "#create" do
it 'should create security group' do
output = <<-EOT
+--------------------------------------+------+----------------+
| Id | Name | Description |
+--------------------------------------+------+----------------+
| f630dd92-3ff7-49bc-b012-b211451aa419 | scg0 | Security Group |
+--------------------------------------+------+----------------+
EOT
provider.expects(:auth_nova).with('secgroup-create', 'scg0', 'Security Group').returns(output)
expect(provider.create).to be_truthy
end
end
describe "#destroy" do
it 'should destroy security group' do
output = <<-EOT
+--------------------------------------+------+----------------+
| Id | Name | Description |
+--------------------------------------+------+----------------+
| f630dd92-3ff7-49bc-b012-b211451aa419 | scg0 | Security Group |
+--------------------------------------+------+----------------+
EOT
provider.expects(:auth_nova).with('secgroup-delete', 'scg0').returns(output)
expect(provider.destroy).to be_truthy
end
end
end
it_behaves_like('nova_security_group')
end

View File

@ -0,0 +1,48 @@
require 'puppet'
require 'puppet/provider/nova_security_rule/nova'
require 'tempfile'
provider_class = Puppet::Type.type(:nova_security_rule).provider(:nova)
describe provider_class do
let :secrule_attrs do
{
:name => "scr0",
:ip_protocol => "tcp",
:from_port => '22',
:to_port => '23',
:ip_range => '0.0.0.0/0',
:security_group => 'scg0'
}
end
let :resource do
Puppet::Type::Nova_security_rule.new(secrule_attrs)
end
let :provider do
provider_class.new(resource)
end
shared_examples "nova_security_rule" do
describe "#create" do
it 'should create security rule' do
provider.expects(:auth_nova).with('secgroup-add-rule', ['scg0', 'tcp', '22', '23', '0.0.0.0/0']).returns('+-------------+-----------+---------+-----------+--------------+\n| IP Protocol | From Port | To Port | IP Range | Source Group |\n+-------------+-----------+---------+-----------+--------------+\n| tcp | 22 | 23 | 0.0.0.0/0 | |\n+-------------+-----------+---------+-----------+--------------+')
provider.create
end
end
describe "#destroy" do
it 'should destroy security rule' do
provider.expects(:auth_nova).with('secgroup-delete-rule', ['scg0', 'tcp', '22', '23', '0.0.0.0/0']).returns('+-------------+-----------+---------+-----------+--------------+\n| IP Protocol | From Port | To Port | IP Range | Source Group |\n+-------------+-----------+---------+-----------+--------------+\n| tcp | 22 | 23 | 0.0.0.0/0 | |\n+-------------+-----------+---------+-----------+--------------+')
provider.destroy
end
end
end
it_behaves_like('nova_security_rule')
end

View File

@ -0,0 +1,20 @@
require 'puppet'
require 'puppet/type/nova_security_group'
describe 'Puppet::Type.type(:nova_security_group)' do
it 'should reject invalid name value' do
expect { Puppet::Type.type(:nova_security_group).new(:name => 65535) }.to raise_error(Puppet::Error, /name parameter must be a String/)
expect { Puppet::Type.type(:nova_security_group).new(:name => 'sc g0') }.to raise_error(Puppet::Error, /is not a valid name/)
end
it 'should accept a valid name value' do
Puppet::Type.type(:nova_security_group).new(:name => 'scg0')
end
it 'should accept description' do
Puppet::Type.type(:nova_security_group).new(:name => 'scg0',
:description => 'Security Group')
end
end

View File

@ -0,0 +1,83 @@
require 'puppet'
require 'puppet/type/nova_security_group'
describe 'Puppet::Type.type(:nova_security_group)' do
it 'should reject an invalid ipv4 CIDR value' do
expect { Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => '22',
:to_port => '22',
:ip_range => '192.168.1.0',
:security_group => 'scg0') }.to raise_error(Puppet::Error, /Incorrect ip_range!/)
expect { Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => '22',
:to_port => '22',
:ip_range => '::1/24',
:security_group => 'scg0') }.to raise_error(Puppet::Error, /Incorrect ip_range!/)
end
it 'should reject an invalid from port value' do
expect { Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => '-22',
:to_port => '22',
:ip_range => '192.168.1.0/24',
:security_group => 'scg0') }.to raise_error(Puppet::Error, /Incorrect from port!/)
expect { Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:to_port => '22',
:ip_range => '192.168.1.0/24',
:security_group => 'scg0') }.to raise_error(Puppet::Error, /You should give the source port/)
end
it 'should reject an invalid from port value' do
expect { Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => '22',
:to_port => '-22',
:ip_range => '192.168.1.0/24',
:security_group => 'scg0') }.to raise_error(Puppet::Error, /Incorrect to port!/)
expect { Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => '22',
:ip_range => '192.168.1.0/24',
:security_group => 'scg0') }.to raise_error(Puppet::Error, /You should give the destination port/)
end
it 'should fails with security group not specified' do
expect { Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => '22',
:to_port => '22',
:ip_range => '192.168.1.0/24') }.to raise_error(Puppet::Error, /You should provide the security group/)
end
it 'should fails with none of ip_range and source_group specified' do
expect { Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => '22',
:to_port => '22',
:security_group => 'scg0') }.to raise_error(Puppet::Error, /You should give either ip_range or source_group/)
end
it 'should fails with both ip_range and source group specified' do
expect { Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => '22',
:to_port => '22',
:ip_range => '192.168.1.0/24',
:source_group => 'tenant',
:security_group => 'scg0') }.to raise_error(Puppet::Error, /You should give either ip_range or source_group/)
end
it 'should accept a valid parameters' do
Puppet::Type.type(:nova_security_rule).new(:name => 'scr0',
:ip_protocol => 'tcp',
:from_port => '22',
:to_port => '22',
:ip_range => '192.168.1.0/24',
:security_group => 'scg0')
end
end