Project

General

Profile

« Previous | Next » 

Revision 7b7d7d1d

Added by Timo Goebel almost 8 years ago

fixes #14665 - support IPv6 via API

View differences:

app/controllers/api/v1/hosts_controller.rb
param :location_id, :number, :required => true, :desc => "required if locations are enabled" if SETTINGS[:locations_enabled]
param :organization_id, :number, :required => true, :desc => "required if organizations are enabled" if SETTINGS[:organizations_enabled]
param :environment_id, String, :desc => "required if host is managed and value is not inherited from host group"
param :ip, String, :desc => "not required if using a subnet with DHCP proxy"
param :ip, String, :desc => "IPv4 address"
param :ip6, String, :desc => "IPv6 address"
param :mac, String, :desc => "required for managed host that is bare metal, not required if it's a virtual machine"
param :architecture_id, :number, :desc => "required if host is managed and value is not inherited from host group"
param :domain_id, :number, :desc => "required if host is managed and value is not inherited from host group"
......
param :operatingsystem_id, String, :desc => "required if host is managed and value is not inherited from host group"
param :medium_id, String, :desc => "required if not imaged based provisioning and host is managed and value is not inherited from host group"
param :ptable_id, :number, :desc => "required if host is managed and custom partition has not been defined"
param :subnet_id, :number, :desc => "required if host is managed and value is not inherited from host group"
param :subnet_id, :number, :desc => "IPv4 subnet"
param :subnet6_id, :number, :desc => "IPv6 subnet"
param :compute_resource_id, :number, :desc => "nil means host is bare metal"
param :root_pass, String, :desc => "required if host is managed and value is not inherited from host group or default password in settings"
param :model_id, :number
......
param :host, Hash, :required => true do
param :name, String
param :environment_id, String
param :ip, String, :desc => "not required if using a subnet with dhcp proxy"
param :ip, String, :desc => "IPv4 address, not required if using a subnet with dhcp proxy"
param :ip6, String, :desc => "IPv6 address"
param :mac, String, :desc => "not required if its a virtual machine"
param :architecture_id, :number
param :domain_id, :number
......
param :puppetclass_ids, Array
param :medium_id, :number
param :ptable_id, :number
param :subnet_id, :number
param :subnet_id, :number, :desc => "IPv4 subnet"
param :subnet6_id, :number, :desc => "IPv6 subnet"
param :compute_resource_id, :number
param :sp_subnet_id, :number
param :model_id, :number
app/controllers/api/v1/subnets_controller.rb
api :POST, '/subnets', 'Create a subnet'
param :subnet, Hash, :required => true do
param :name, String, :desc => 'Subnet name', :required => true
param :network_type, Subnet::SUBNET_TYPES.values, :desc => 'Type or protocol, IPv4 or IPv6, defaults to IPv4'
param :network, String, :desc => 'Subnet network', :required => true
param :mask, String, :desc => 'Netmask for this subnet', :required => true
param :gateway, String, :desc => 'Primary DNS for this subnet'
......
end
def create
@subnet = Subnet.new(params[:subnet])
@subnet = Subnet.new_network_type(params[:subnet])
process_response @subnet.save
end
app/controllers/api/v2/interfaces_controller.rb
def_param_group :interface_attributes do
#common parameters
param :mac, String, :desc => N_("MAC address of interface. Required for managed interfaces on bare metal.")
param :ip, String, :desc => N_("IP address of interface")
param :ip, String, :desc => N_("IPv4 address of interface")
param :ip6, String, :desc => N_("IPv6 address of interface")
param :type, InterfaceTypeMapper::ALLOWED_TYPE_NAMES, :desc => N_("Interface type, e.g. bmc. Default is %{default_nic_type}")
param :name, String, :desc => N_("Interface's DNS name")
param :subnet_id, Fixnum, :desc => N_("Foreman subnet ID of interface")
param :subnet_id, Fixnum, :desc => N_("Foreman subnet ID of IPv4 interface")
param :subnet6_id, Fixnum, :desc => N_("Foreman subnet ID of IPv6 interface")
param :domain_id, Fixnum, :desc => N_("Foreman domain ID of interface. Required for primary interfaces on managed hosts.")
param :identifier, String, :desc => N_("Device identifier, e.g. eth0 or eth1.1")
param :managed, :bool, :desc => N_("Should this interface be managed via DHCP and DNS smart proxy and should it be configured during provisioning?")
app/controllers/api/v2/subnets_controller.rb
def_param_group :subnet do
param :subnet, Hash, :required => true, :action_aware => true do
param :name, String, :desc => N_("Subnet name"), :required => true
param :network_type, Subnet::SUBNET_TYPES.values, :desc => N_('Type or protocol, IPv4 or IPv6, defaults to IPv4')
param :network, String, :desc => N_("Subnet network"), :required => true
param :mask, String, :desc => N_("Netmask for this subnet"), :required => true
param :gateway, String, :desc => N_("Primary DNS for this subnet")
......
param_group :subnet, :as => :create
def create
@subnet = Subnet.new(params[:subnet])
@subnet = Subnet.new_network_type(params[:subnet])
process_response @subnet.save
end
app/models/subnet.rb
attr_accessible :name, :type, :network, :mask, :gateway, :dns_primary, :dns_secondary, :ipam, :from,
:to, :vlanid, :boot_mode, :dhcp_id, :dhcp, :tftp_id, :tftp, :dns_id, :dns, :domain_ids, :domain_names,
:subnet_parameters_attributes, :cidr
:subnet_parameters_attributes, :cidr, :network_type
attr_exportable :name, :network, :mask, :gateway, :dns_primary, :dns_secondary, :from, :to, :boot_mode,
:ipam, :vlanid, :type
:ipam, :vlanid, :network_type
# This casts Subnet to Subnet::Ipv4 if no type is set
def self.new(*attributes, &block)
(h = attributes.first).is_a?(Hash) && (type = h.with_indifferent_access.delete(:type))
type = attributes.first.with_indifferent_access.delete(:type) if attributes.first.is_a?(Hash)
return Subnet::Ipv4.new_without_cast(*attributes, &block) if self == Subnet && type.nil?
super
end
......
name
end
def network_type
SUBNET_TYPES[type.to_sym]
end
def network_type=(value)
self[:type] = SUBNET_TYPES.key(value)
end
# Subnets are sorted on their priority value
# [+other+] : Subnet object with which to compare ourself
# +returns+ : Subnet object with higher precedence
......
ip = IPAddr.new(ip)
Subnet.all.detect {|s| s.family == ip.family && s.contains?(ip)}
end
# allows to create a specific subnet class based on the network_type.
# network_type is more user friendly than the class names
def new_network_type(args)
network_type = args.delete(:network_type) || 'IPv4'
SUBNET_TYPES.each do |network_type_class, network_type_name|
return network_type_class.to_s.constantize.new(args) if network_type_name.downcase == network_type.downcase
end
raise ::Foreman::Exception.new N_("unknown network_type")
end
end
private
app/views/api/v1/subnets/show.json.rabl
object @subnet
attributes :id, :name, :network, :mask, :priority, :vlanid,
attributes :id, :name, :network_type, :network, :mask, :priority, :vlanid,
:gateway, :dns_primary, :dns_secondary, :from, :to, :domain_ids,
:dns_id, :dhcp_id, :tftp_id, :cidr, :ipam
app/views/api/v2/hosts/main.json.rabl
@object.configuration_status(:last_reports => @last_reports)
@object.configuration_status_label(:last_reports => @last_reports)
attributes :ip, :environment_id, :environment_name, :last_report, :mac, :realm_id, :realm_name,
attributes :ip, :ip6, :environment_id, :environment_name, :last_report, :mac, :realm_id, :realm_name,
:sp_mac, :sp_ip, :sp_name, :domain_id, :domain_name, :architecture_id, :architecture_name, :operatingsystem_id, :operatingsystem_name,
:subnet_id, :subnet_name, :sp_subnet_id, :ptable_id, :ptable_name, :medium_id, :medium_name, :build,
:subnet_id, :subnet_name, :subnet6_id, :subnet6_name, :sp_subnet_id, :ptable_id, :ptable_name, :medium_id, :medium_name, :build,
:comment, :disk, :installed_at, :model_id, :hostgroup_id, :owner_id, :owner_type,
:enabled, :puppet_ca_proxy_id, :managed, :use_image, :image_file, :uuid, :compute_resource_id, :compute_resource_name,
:compute_profile_id, :compute_profile_name, :capabilities, :provision_method,
app/views/api/v2/interfaces/main.json.rabl
extends "api/v2/interfaces/base"
attributes :subnet_id, :subnet_name, :domain_id, :domain_name, :created_at, :updated_at,
attributes :subnet_id, :subnet_name, :subnet6_id, :subnet6_name, :domain_id, :domain_name, :created_at, :updated_at,
:managed, :identifier
node do |interface|
app/views/api/v2/subnets/main.json.rabl
extends "api/v2/subnets/base"
attributes :network, :cidr, :mask, :priority, :vlanid, :gateway, :dns_primary, :dns_secondary,
attributes :network, :network_type, :cidr, :mask, :priority, :vlanid, :gateway,
:dns_primary, :dns_secondary,
:from, :to, :created_at, :updated_at, :ipam, :boot_mode
child :dhcp => :dhcp do
test/functional/api/v1/subnets_controller_test.rb
require 'test_helper'
class Api::V1::SubnetsControllerTest < ActionController::TestCase
valid_attrs = { :name => 'QA2', :network => '10.35.2.27', :mask => '255.255.255.0' }
valid_v4_attrs = { :name => 'QA2', :network_type => 'IPv4', :network => '10.35.2.27', :mask => '255.255.255.0' }
valid_v6_attrs = { :name => 'QA2', :network_type => 'IPv6', :network => '2001:db8::', :mask => 'ffff:ffff:ffff:ffff::', :ipam => 'None' }
def test_index
get :index
......
assert !show_response.empty?
end
test "should create subnet" do
assert_difference('Subnet.count') do
post :create, { :subnet => valid_attrs }
test "should create IPv4 subnet" do
assert_difference('Subnet::Ipv4.count') do
post :create, { :subnet => valid_v4_attrs }
end
assert_response :success
assert_equal 'Subnet::Ipv4', Subnet.find_by_name('QA2').type
end
test "should create IPv6 subnet" do
assert_difference('Subnet::Ipv6.count') do
post :create, { :subnet => valid_v6_attrs }
end
assert_response :success
assert_equal 'Subnet::Ipv6', Subnet.find_by_name('QA2').type
end
test "does not create subnet with non-existent domain" do
post :create, { :subnet => valid_attrs.merge(:domain_ids => [1, 2]) }
post :create, { :subnet => valid_v4_attrs.merge(:domain_ids => [1, 2]) }
assert_response :not_found
end
test "should update subnet" do
put :update, { :id => subnets(:one).to_param, :subnet => valid_attrs }
put :update, { :id => subnets(:one).to_param, :subnet => valid_v4_attrs }
assert_response :success
end
test/functional/api/v2/interfaces_controller_test.rb
class Api::V2::InterfacesControllerTest < ActionController::TestCase
valid_attrs = { 'name' => "test.foreman.com", 'ip' => "10.0.1.1", 'mac' => "AA:AA:AA:AA:AA:AA",
'username' => "foo", 'password' => "bar", 'provider' => "IPMI",
'type' => "bmc" }
'type' => "bmc", 'ip6' => '2001:db8::1' }
def setup
@host = FactoryGirl.create(:host)
test/functional/api/v2/subnets_controller_test.rb
require 'test_helper'
class Api::V2::SubnetsControllerTest < ActionController::TestCase
valid_attrs = { :name => 'QA2', :network => '10.35.2.27', :mask => '255.255.255.0' }
valid_v4_attrs = { :name => 'QA2', :network_type => 'IPv4', :network => '10.35.2.27', :mask => '255.255.255.0' }
valid_v6_attrs = { :name => 'QA2', :network_type => 'IPv6', :network => '2001:db8::', :mask => 'ffff:ffff:ffff:ffff::', :ipam => 'None' }
test "index content is a JSON array" do
get :index
......
assert !show_response.empty?
end
test "should create subnet" do
test "should create IPv4 subnet" do
assert_difference('Subnet.count') do
post :create, { :subnet => valid_attrs }
post :create, { :subnet => valid_v4_attrs }
end
assert_response :created
end
test "should create IPv4 subnet if type is not defined" do
assert_difference('Subnet.count') do
post :create, { :subnet => valid_v4_attrs.reject {|k, v| k == :network_type} }
end
subnet = Subnet.find_by_name(valid_v4_attrs[:name])
assert_equal valid_v4_attrs[:network_type], subnet.network_type
assert_response :created
end
test "should create IPv6 subnet" do
assert_difference('Subnet.count') do
post :create, { :subnet => valid_v6_attrs }
end
assert_response :created
end
test "does not create subnet with non-existent domain" do
post :create, { :subnet => valid_attrs.merge(:domain_ids => [1, 2]) }
post :create, { :subnet => valid_v4_attrs.merge(:domain_ids => [1, 2]) }
assert_response :not_found
end
test "should update subnet" do
put :update, { :id => subnets(:one).to_param, :subnet => valid_attrs }
put :update, { :id => subnets(:one).to_param, :subnet => valid_v4_attrs }
assert_response :success
end
test "should not update subnet and change type" do
put :update, { :id => subnets(:one).to_param, :subnet => valid_v6_attrs }
assert_response :unprocessable_entity
end
test "should destroy subnets" do
assert_difference('Subnet.count', -1) do
delete :destroy, { :id => subnets(:four).to_param }
test/unit/host_test.rb
test '#info ENC YAML contains ipv4 and ipv6 subnets' do
host = FactoryGirl.build(:host, :with_subnet, :with_ipv6_subnet)
enc = host.info
assert enc['parameters']['foreman_subnets'].any? {|s| s['type'] == 'Subnet::Ipv4'}
assert enc['parameters']['foreman_subnets'].any? {|s| s['type'] == 'Subnet::Ipv6'}
assert enc['parameters']['foreman_subnets'].any? {|s| s['network_type'] == 'IPv4'}
assert enc['parameters']['foreman_subnets'].any? {|s| s['network_type'] == 'IPv6'}
end
describe 'cloning' do
test/unit/subnet_test.rb
assert_equal Subnet::Ipv4, subnet.class
end
test 'should be cast to Subnet::Ipv6 if type is set accordingly' do
subnet = Subnet.new(:type => 'Subnet::Ipv6')
assert_equal Subnet::Ipv6, subnet.class
end
test 'child class should not be cast to default sti class even if no type is set' do
class Subnet::Test < Subnet; end
subnet = Subnet::Test.new
assert_equal Subnet::Test, subnet.class
end
test '#network_type returns the subnets type in human friendly form' do
subnet = Subnet.new(:type => 'Subnet::Ipv4')
assert_equal 'IPv4', subnet.network_type
subnet6 = Subnet.new(:type => 'Subnet::Ipv6')
assert_equal 'IPv6', subnet6.network_type
end
test '#network_type= should set #type' do
subnet = Subnet.new(:type => 'Subnet::Ipv4')
subnet.network_type = 'IPv6'
assert_equal 'Subnet::Ipv6', subnet.type
end
test '.new_network_type instantiates network_type from arguments' do
assert_instance_of Subnet::Ipv6, Subnet.new_network_type(:network_type => 'IPv6')
end
test '.new_network_type raises error for unknown network type' do
e = assert_raise(Foreman::Exception) { Subnet.new_network_type({:network_type => 'Unknown'}) }
assert_match /unknown network_type/, e.message
end
test "the name should be unique in the domain scope" do
first = FactoryGirl.create(:subnet_ipv6, :with_domains)
subnet = FactoryGirl.build(:subnet_ipv6, :name => first.name, :domains => first.domains)

Also available in: Unified diff