require File.join(File.dirname(__FILE__), '..','..','..', 'puppet/provider/nova') Puppet::Type.type(:nova_security_rule).provide( :openstack, :parent => Puppet::Provider::Nova ) do desc <<-EOT Manage nova security rules EOT @credentials = Puppet::Provider::Openstack::CredentialsV3.new def create opts = [@resource[:security_group]] opts << '--protocol' << @resource[:ip_protocol] if @resource[:ip_protocol].to_s == 'icmp' unless @resource[:from_port].to_i == -1 and @resource[:to_port].to_i == -1 opts << "--icmp-type" << @resource[:from_port] opts << "--icmp-code" << @resource[:to_port] end else opts << "--dst-port" << "#{@resource[:from_port]}:#{@resource[:to_port]}" end unless @resource[:ip_range].nil? opts << "--remote-ip" << @resource[:ip_range] else opts << "--remote-group" << @resource[:source_group] end @property_hash = self.class.nova_request('security group rule', 'create', nil, opts) @property_hash[:ensure] = :present end def exists? @property_hash[:ensure] == :present end def destroy self.class.request('security group rule', 'delete', @property_hash[:name]) @property_hash[:ensure] == :absent end mk_resource_methods def self.instances rules = [] secgroup_provider = Puppet::Type.type(:nova_security_group).provider(:openstack) groups = secgroup_provider.instances groups.each do |g| self.nova_request('security group rule', 'list', nil, ['--long', g.id]).each do |attrs| # NOTE(mnaser): Originally, security groups were ingress only so to maintain # backwards compatibility, we ignore all egress rules. next if attrs[:direction] == 'egress' # NOTE(mnaser): With Neutron, an empty ip_range means all networks, therefore # we replace it by '0.0.0.0/0' for backwards compatibility. attrs[:ip_range] = '0.0.0.0/0' if attrs[:ip_range].empty? and attrs[:remote_security_group].empty? # NOTE(mnaser): Another quirk, Neutron can have an empty port range which means # all ports, we adjust the field accordingly for the protocol. if attrs[:port_range].empty? if ['tcp', 'udp'].include? attrs[:ip_protocol] attrs[:from_port] = 0 attrs[:to_port] = 65536 else attrs[:from_port] = -1 attrs[:to_port] = -1 end else attrs[:from_port], attrs[:to_port] = attrs[:port_range].split(':') end rule = { :ensure => :present, :name => attrs[:id], :security_group => g.name, :from_port => attrs[:from_port], :to_port => attrs[:to_port], } # NOTE(mnaser): The puppet type does not like getting source_group even if it's not set. unless attrs[:ip_range].empty? rule[:ip_range] = attrs[:ip_range] else rule[:source_group] = attrs[:remote_security_group] end # NOTE(mnaser): With Neutron, it is possible to have the ip_protocol empty # which means all 3 protocols are allowed. We create three # resources to maintain backwards compatible. if attrs[:ip_protocol].empty? rules << new(rule.merge(:ip_protocol => 'tcp', :from_port => 0, :to_port => 65536)) rules << new(rule.merge(:ip_protocol => 'udp', :from_port => 0, :to_port => 65536)) rules << new(rule.merge(:ip_protocol => 'icmp', :from_port => -1, :to_port => -1)) else rules << new(rule.merge(:ip_protocol => attrs[:ip_protocol])) end end end rules end def self.prefetch(resources) security_group_rules = instances resources.keys.each do |name| resource = resources[name].to_hash rule = security_group_rules.find do |r| r.security_group == resource[:security_group] && \ r.ip_protocol.to_s == resource[:ip_protocol].to_s && \ r.from_port.to_s == resource[:from_port].to_s && \ r.to_port.to_s == resource[:to_port].to_s end resources[name].provider = rule if rule end end end