Skip to content

Commit a48be3b

Browse files
nofaralfasistejskalleos
authored andcommitted
Fixes #38742 - Add fixed IP address support for OpenStack
1 parent d61cf5a commit a48be3b

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed

app/models/compute_resources/foreman/model/openstack.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ def create_vm(args = {})
162162
# fix internal network format for fog.
163163
args[:nics].delete_if(&:blank?)
164164
args[:nics].map! { |nic| nic.is_a?(String) ? { 'net_id' => nic } : nic }
165+
166+
# Process interface attributes to add fixed IP addresses
167+
process_fixed_ips(args)
168+
165169
args[:security_groups].delete_if(&:blank?) if args[:security_groups].present?
166170
format_scheduler_hint_filter(args) if args[:scheduler_hint_filter].present?
167171
vm = super(args)
@@ -336,5 +340,34 @@ def allocate_address(network)
336340
logger.warn "failed to allocate ip address for network #{network}: #{e}"
337341
raise e
338342
end
343+
344+
def process_fixed_ips(args)
345+
interfaces_attrs = args.delete(:interfaces_attributes)
346+
return unless interfaces_attrs&.any?
347+
348+
# Filter nics that have net_id
349+
nics = args[:nics].select { |nic| nic.is_a?(Hash) && nic['net_id'] }
350+
return if nics.empty?
351+
352+
# Update nics with fixed IPs based on position
353+
nics.each_with_index do |nic, index|
354+
interface_attrs = interfaces_attrs[index.to_s]
355+
next unless interface_attrs.is_a?(Hash)
356+
357+
# Handle IPv4
358+
ipv4 = interface_attrs[:ip] || interface_attrs['ip']
359+
if ipv4.present?
360+
logger.debug("Fixed IPv4 mapping: network #{nic['net_id']} -> IP #{ipv4}")
361+
nic['v4_fixed_ip'] = ipv4
362+
end
363+
364+
# Handle IPv6
365+
ipv6 = interface_attrs[:ip6] || interface_attrs['ip6']
366+
if ipv6.present?
367+
logger.debug("Fixed IPv6 mapping: network #{nic['net_id']} -> IP #{ipv6}")
368+
nic['v6_fixed_ip'] = ipv6
369+
end
370+
end
371+
end
339372
end
340373
end

test/models/compute_resources/openstack_test.rb

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,166 @@ class OpenstackTest < ActiveSupport::TestCase
5555
:flavor_ref => 'foo_flavor', :image_ref => 'foo_image')
5656
end
5757

58+
test "process_fixed_ips adds fixed IP when interface has IP specified" do
59+
args = {
60+
:nics => [{ 'net_id' => 'network-123' }],
61+
:interfaces_attributes => {
62+
'0' => { 'ip' => '192.168.1.100' },
63+
},
64+
}
65+
66+
@compute_resource.send(:process_fixed_ips, args)
67+
68+
expected_nics = [{ 'net_id' => 'network-123', 'v4_fixed_ip' => '192.168.1.100' }]
69+
assert_equal expected_nics, args[:nics]
70+
assert_nil args[:interfaces_attributes]
71+
end
72+
73+
test "process_fixed_ips handles multiple networks correctly" do
74+
args = {
75+
:nics => [
76+
{ 'net_id' => 'network-123' },
77+
{ 'net_id' => 'network-456' },
78+
],
79+
:interfaces_attributes => {
80+
'0' => { 'ip' => '192.168.1.100' },
81+
'1' => { 'ip' => '10.0.0.50' },
82+
},
83+
}
84+
85+
@compute_resource.send(:process_fixed_ips, args)
86+
87+
expected_nics = [
88+
{ 'net_id' => 'network-123', 'v4_fixed_ip' => '192.168.1.100' },
89+
{ 'net_id' => 'network-456', 'v4_fixed_ip' => '10.0.0.50' },
90+
]
91+
assert_equal expected_nics, args[:nics]
92+
end
93+
94+
test "process_fixed_ips handles out-of-order interface indices" do
95+
args = {
96+
:nics => [
97+
{ 'net_id' => 'network-123' },
98+
{ 'net_id' => 'network-456' },
99+
],
100+
:interfaces_attributes => {
101+
'1' => { 'ip' => '10.0.0.50' },
102+
'0' => { 'ip' => '192.168.1.100' },
103+
},
104+
}
105+
106+
@compute_resource.send(:process_fixed_ips, args)
107+
108+
expected_nics = [
109+
{ 'net_id' => 'network-123', 'v4_fixed_ip' => '192.168.1.100' },
110+
{ 'net_id' => 'network-456', 'v4_fixed_ip' => '10.0.0.50' },
111+
]
112+
assert_equal expected_nics, args[:nics]
113+
end
114+
115+
test "process_fixed_ips adds IPv6 address when interface has IPv6 specified" do
116+
args = {
117+
:nics => [{ 'net_id' => 'network-123' }],
118+
:interfaces_attributes => {
119+
'0' => { 'ip6' => '2001:db8::1' },
120+
},
121+
}
122+
123+
@compute_resource.send(:process_fixed_ips, args)
124+
125+
expected_nics = [{ 'net_id' => 'network-123', 'v6_fixed_ip' => '2001:db8::1' }]
126+
assert_equal expected_nics, args[:nics]
127+
assert_nil args[:interfaces_attributes]
128+
end
129+
130+
test "process_fixed_ips adds both IPv4 and IPv6 addresses" do
131+
args = {
132+
:nics => [{ 'net_id' => 'network-123' }],
133+
:interfaces_attributes => {
134+
'0' => { 'ip' => '192.168.1.100', 'ip6' => '2001:db8::1' },
135+
},
136+
}
137+
138+
@compute_resource.send(:process_fixed_ips, args)
139+
140+
expected_nics = [{ 'net_id' => 'network-123', 'v4_fixed_ip' => '192.168.1.100', 'v6_fixed_ip' => '2001:db8::1' }]
141+
assert_equal expected_nics, args[:nics]
142+
assert_nil args[:interfaces_attributes]
143+
end
144+
145+
test "process_fixed_ips handles multiple networks with mixed IPv4 and IPv6" do
146+
args = {
147+
:nics => [
148+
{ 'net_id' => 'network-123' },
149+
{ 'net_id' => 'network-456' },
150+
],
151+
:interfaces_attributes => {
152+
'0' => { 'ip' => '192.168.1.100', 'ip6' => '2001:db8::1' },
153+
'1' => { 'ip' => '10.0.0.50', 'ip6' => '2001:db8::2' },
154+
},
155+
}
156+
157+
@compute_resource.send(:process_fixed_ips, args)
158+
159+
expected_nics = [
160+
{ 'net_id' => 'network-123', 'v4_fixed_ip' => '192.168.1.100', 'v6_fixed_ip' => '2001:db8::1' },
161+
{ 'net_id' => 'network-456', 'v4_fixed_ip' => '10.0.0.50', 'v6_fixed_ip' => '2001:db8::2' },
162+
]
163+
assert_equal expected_nics, args[:nics]
164+
end
165+
166+
test "process_fixed_ips skips nics without net_id" do
167+
args = {
168+
:nics => [
169+
{ 'net_id' => 'network-123' },
170+
{ 'other_key' => 'value' },
171+
{ 'net_id' => 'network-456' },
172+
],
173+
:interfaces_attributes => {
174+
'0' => { 'ip' => '192.168.1.100' },
175+
'1' => { 'ip' => '10.0.0.50' },
176+
'2' => { 'ip' => '172.16.0.1' },
177+
},
178+
}
179+
180+
@compute_resource.send(:process_fixed_ips, args)
181+
182+
expected_nics = [
183+
{ 'net_id' => 'network-123', 'v4_fixed_ip' => '192.168.1.100' },
184+
{ 'other_key' => 'value' },
185+
{ 'net_id' => 'network-456', 'v4_fixed_ip' => '10.0.0.50' },
186+
]
187+
assert_equal expected_nics, args[:nics]
188+
end
189+
190+
test "process_fixed_ips handles empty IP values gracefully" do
191+
args = {
192+
:nics => [{ 'net_id' => 'network-123' }],
193+
:interfaces_attributes => {
194+
'0' => { 'ip' => '', 'ip6' => '' },
195+
},
196+
}
197+
198+
@compute_resource.send(:process_fixed_ips, args)
199+
200+
expected_nics = [{ 'net_id' => 'network-123' }]
201+
assert_equal expected_nics, args[:nics]
202+
assert_nil args[:interfaces_attributes]
203+
end
204+
205+
test "create_vm processes fixed IPs when interfaces_attributes present" do
206+
Fog.mock!
207+
@compute_resource.stubs(:key_pair).returns(mocked_key_pair)
208+
@compute_resource.expects(:process_fixed_ips).once
209+
210+
@compute_resource.create_vm(
211+
:nics => [{ 'net_id' => 'network-123' }],
212+
:interfaces_attributes => { '0' => { 'ip' => '192.168.1.100' } },
213+
:flavor_ref => 'foo_flavor',
214+
:image_ref => 'foo_image'
215+
)
216+
end
217+
58218
describe "formatting hints" do
59219
it "formats well when set to ServerGroupAntiAffinity" do
60220
args = {

0 commit comments

Comments
 (0)