Skip to content

Commit 6893ef4

Browse files
committed
F #7052: Add support for hotplug NIC devices
* Supports for regular (veth) and PCI NIC devices * Live context updates (for configuring the NIC devices) * Supports Linux bridges, OVS and OVS DPDK * Initial support for disk hotplugging (disabled) Author: Daniel Clavijo Coca <[email protected]>
1 parent d5dafa6 commit 6893ef4

File tree

22 files changed

+744
-207
lines changed

22 files changed

+744
-207
lines changed

install.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,7 @@ IM_PROBES_LXC_HOST_SYSTEM_FILES="\
11711171
src/im_mad/remotes/lxc-probes.d/host/system/name.sh \
11721172
src/im_mad/remotes/kvm-probes.d/host/system/clean_db.rb \
11731173
src/im_mad/remotes/lxc-probes.d/host/system/numa_host.rb \
1174+
src/im_mad/remotes/lxc-probes.d/host/system/pci.rb \
11741175
src/im_mad/remotes/lxc-probes.d/host/system/version.sh"
11751176

11761177
IM_PROBES_LXC_VM_MONITOR_FILES="\
@@ -1182,6 +1183,7 @@ IM_PROBES_LXC_VM_STATUS_FILES="\
11821183

11831184
IM_PROBES_ETC_LXC_PROBES_FILES="\
11841185
src/im_mad/remotes/lxc-probes.d/forecast.conf \
1186+
src/im_mad/remotes/lxc-probes.d/pci.conf \
11851187
src/im_mad/remotes/lib/probe_db.conf"
11861188

11871189
IM_PROBES_VERSION="src/im_mad/remotes/VERSION"

share/pkgs/sudoers/centos/opennebula

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Cmnd_Alias ONE_CEPH = /usr/bin/rbd
55
Cmnd_Alias ONE_HA = /usr/bin/systemctl start opennebula-flow, /usr/bin/systemctl stop opennebula-flow, /usr/bin/systemctl start opennebula-gate, /usr/bin/systemctl stop opennebula-gate, /usr/bin/systemctl start opennebula-hem, /usr/bin/systemctl stop opennebula-hem, /usr/bin/systemctl start opennebula-showback.timer, /usr/bin/systemctl stop opennebula-showback.timer, /usr/sbin/service opennebula-flow start, /usr/sbin/service opennebula-flow stop, /usr/sbin/service opennebula-gate start, /usr/sbin/service opennebula-gate stop, /usr/sbin/service opennebula-hem start, /usr/sbin/service opennebula-hem stop, /usr/sbin/arping, /usr/sbin/ip address *
66
Cmnd_Alias ONE_LVM = /usr/sbin/lvcreate, /usr/sbin/lvremove, /usr/sbin/lvs, /usr/sbin/vgdisplay, /usr/sbin/lvchange, /usr/sbin/lvscan, /usr/sbin/lvextend, /usr/sbin/dmsetup
77
Cmnd_Alias ONE_NFS = /usr/bin/mount, /usr/bin/umount, /usr/bin/sed -i -f /proc/self/fd/0 /etc/fstab
8-
Cmnd_Alias ONE_LXC = /usr/bin/mount, /usr/bin/umount, /usr/bin/bindfs, /usr/sbin/losetup, /usr/bin/qemu-nbd, /usr/bin/lxc-attach, /usr/bin/lxc-config, /usr/bin/lxc-create, /usr/bin/lxc-destroy, /usr/bin/lxc-info, /usr/bin/lxc-ls, /usr/bin/lxc-start, /usr/bin/lxc-stop, /usr/bin/lxc-console, /usr/sbin/e2fsck, /usr/sbin/resize2fs, /usr/sbin/xfs_growfs, /usr/bin/rbd-nbd
8+
Cmnd_Alias ONE_LXC = /usr/bin/mount, /usr/bin/umount, /usr/bin/bindfs, /usr/sbin/losetup, /usr/bin/qemu-nbd, /usr/bin/lxc-attach, /usr/bin/lxc-config, /usr/bin/lxc-create, /usr/bin/lxc-destroy, /usr/bin/lxc-info, /usr/bin/lxc-ls, /usr/bin/lxc-start, /usr/bin/lxc-stop, /usr/bin/lxc-console, /usr/bin/lxc-device, /usr/sbin/e2fsck, /usr/sbin/resize2fs, /usr/sbin/xfs_growfs, /usr/bin/rbd-nbd
99
Cmnd_Alias ONE_MARKET = /usr/lib/one/sh/create_container_image.sh
1010
Cmnd_Alias ONE_NET = /usr/sbin/iptables, /usr/sbin/ip6tables, /usr/sbin/ipset, /usr/sbin/ip link *, /usr/sbin/ip neighbour *, /usr/sbin/ip route *, /usr/sbin/ip rule *, /usr/sbin/ip tuntap *, /usr/sbin/nft, /var/tmp/one/vnm/tproxy, /usr/sbin/bridge vlan *
1111
Cmnd_Alias ONE_NETAPP = /usr/sbin/blockdev, /usr/sbin/multipath, /usr/sbin/multipathd, /usr/sbin/iscsiadm, /usr/bin/tee, /usr/bin/find

share/pkgs/sudoers/debian/opennebula

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Cmnd_Alias ONE_CEPH = /usr/bin/rbd
55
Cmnd_Alias ONE_HA = /usr/bin/systemctl start opennebula-flow, /usr/bin/systemctl stop opennebula-flow, /usr/bin/systemctl start opennebula-gate, /usr/bin/systemctl stop opennebula-gate, /usr/bin/systemctl start opennebula-hem, /usr/bin/systemctl stop opennebula-hem, /usr/bin/systemctl start opennebula-showback.timer, /usr/bin/systemctl stop opennebula-showback.timer, /usr/sbin/service opennebula-flow start, /usr/sbin/service opennebula-flow stop, /usr/sbin/service opennebula-gate start, /usr/sbin/service opennebula-gate stop, /usr/sbin/service opennebula-hem start, /usr/sbin/service opennebula-hem stop, /usr/bin/arping, /usr/sbin/ip address *
66
Cmnd_Alias ONE_LVM = /usr/sbin/lvcreate, /usr/sbin/lvremove, /usr/sbin/lvs, /usr/sbin/vgdisplay, /usr/sbin/lvchange, /usr/sbin/lvscan, /usr/sbin/lvextend, /usr/sbin/dmsetup
77
Cmnd_Alias ONE_NFS = /usr/bin/mount, /usr/bin/umount, /usr/bin/sed -i -f /proc/self/fd/0 /etc/fstab
8-
Cmnd_Alias ONE_LXC = /usr/bin/mount, /usr/bin/umount, /usr/bin/bindfs, /usr/sbin/losetup, /usr/bin/qemu-nbd, /usr/bin/lxc-attach, /usr/bin/lxc-config, /usr/bin/lxc-create, /usr/bin/lxc-destroy, /usr/bin/lxc-info, /usr/bin/lxc-ls, /usr/bin/lxc-start, /usr/bin/lxc-stop, /usr/bin/lxc-console, /usr/sbin/e2fsck, /usr/sbin/resize2fs, /usr/sbin/xfs_growfs, /usr/bin/rbd-nbd
8+
Cmnd_Alias ONE_LXC = /usr/bin/mount, /usr/bin/umount, /usr/bin/bindfs, /usr/sbin/losetup, /usr/bin/qemu-nbd, /usr/bin/lxc-attach, /usr/bin/lxc-config, /usr/bin/lxc-create, /usr/bin/lxc-destroy, /usr/bin/lxc-info, /usr/bin/lxc-ls, /usr/bin/lxc-start, /usr/bin/lxc-stop, /usr/bin/lxc-console, /usr/bin/lxc-device, /usr/sbin/e2fsck, /usr/sbin/resize2fs, /usr/sbin/xfs_growfs, /usr/bin/rbd-nbd
99
Cmnd_Alias ONE_MARKET = /usr/lib/one/sh/create_container_image.sh
1010
Cmnd_Alias ONE_NET = /usr/sbin/iptables, /usr/sbin/ip6tables, /usr/sbin/ipset, /usr/sbin/ip link *, /usr/sbin/ip neighbour *, /usr/sbin/ip route *, /usr/sbin/ip rule *, /usr/sbin/ip tuntap *, /usr/sbin/nft, /var/tmp/one/vnm/tproxy, /usr/sbin/bridge vlan *
1111
Cmnd_Alias ONE_NETAPP = /usr/sbin/blockdev, /usr/sbin/multipath, /usr/sbin/multipathd, /usr/sbin/iscsiadm, /usr/bin/tee, /usr/bin/find

share/sudoers/sudoers.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def initialize(lib_location)
7373
:LXC => [
7474
'mount', 'umount', 'bindfs', 'losetup', 'qemu-nbd', 'lxc-attach', 'lxc-config',
7575
'lxc-create', 'lxc-destroy', 'lxc-info', 'lxc-ls', 'lxc-start', 'lxc-stop',
76-
'lxc-console', 'e2fsck', 'resize2fs', 'xfs_growfs', 'rbd-nbd'
76+
'lxc-console', 'e2fsck', 'resize2fs', 'xfs_growfs', 'rbd-nbd', 'lxc-device'
7777
],
7878
:MARKET => ["#{lib_location}/sh/create_container_image.sh"],
7979
:MEM => ['sysctl vm.drop_caches=3 vm.compact_memory=1'],
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../node-probes.d/pci.rb
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../node-probes.d/pci.conf

src/vmm_mad/remotes/lib/kvm/opennebula_vm.rb

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -408,11 +408,6 @@ def initialize(xml_action)
408408
@xpath_prefix = ''
409409
end
410410

411-
# @return true if the VM includes a PCI device being attached
412-
def pci_attach?
413-
@xml.exist? "TEMPLATE/PCI[ATTACH='YES']"
414-
end
415-
416411
#-----------------------------------------------------------------------
417412
# This function generates a XML document to attach a new interface
418413
# to the VM. The interface specification supports the same OpenNebula

src/vmm_mad/remotes/lib/lxc/client.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class LXCClient
3030
:create => 'lxc-create',
3131
:destroy => 'lxc-destroy',
3232
:info => 'lxc-info',
33+
:device => 'lxc-device',
3334
:ls => 'lxc-ls',
3435
:start => 'lxc-start',
3536
:stop => 'lxc-stop'
@@ -107,6 +108,47 @@ def list(options = {})
107108
stdout.split
108109
end
109110

111+
#-----------------------------------------------------------------------
112+
# Command Injection
113+
#-----------------------------------------------------------------------
114+
115+
def attach(name, cmd, options = {}, detach = false)
116+
cmd = append_options("#{COMMANDS[:attach]} -n #{name} -- #{cmd}", options)
117+
118+
if detach
119+
Command.container_cmd(name, cmd)
120+
else
121+
Command.execute_detach(cmd)
122+
end
123+
end
124+
125+
def bash(name, cmd, options = {})
126+
cmd = "/bin/bash -c \"#{cmd}\""
127+
cmd = append_options("#{COMMANDS[:attach]} -n #{name} -- #{cmd}", options)
128+
Command.execute_log(cmd)
129+
end
130+
131+
#-----------------------------------------------------------------------
132+
# Device Management
133+
#-----------------------------------------------------------------------
134+
135+
def attach_device(name, device_host, device_guest = nil, options = {})
136+
devices = '' + device_host
137+
devices << " #{device_guest}" if device_guest
138+
139+
cmd = append_options("#{COMMANDS[:device]} -n #{name} add #{devices}", options)
140+
Command.container_cmd(name, cmd)
141+
end
142+
143+
def detach_device(name, device_guest, device_host = nil, options = {})
144+
devices = ''
145+
devices << device_guest
146+
devices << " #{device_host}" if device_host
147+
148+
cmd = append_options("#{COMMANDS[:device]} -n #{name} del #{devices}", options)
149+
Command.container_cmd(name, cmd)
150+
end
151+
110152
private
111153

112154
# append options to cmd string

src/vmm_mad/remotes/lib/lxc/container.rb

Lines changed: 179 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
require 'client'
2222
require 'opennebula_vm'
23+
require 'tempfile'
2324

2425
# LXC Container abstraction. Handles container native and added
2526
# operations. Allows to gather container config and status data
@@ -39,6 +40,9 @@ def initialize(one, client)
3940
@client = client
4041

4142
@name = @one.vm_name
43+
44+
@driver_config = @one.lxcrc
45+
@driver_config.merge!(:id_map => 0) if @one.privileged?
4246
end
4347

4448
class << self
@@ -96,11 +100,8 @@ def create(options = {})
96100
error = false
97101
mounted = []
98102

99-
lxcrc = @one.lxcrc
100-
lxcrc.merge!(:id_map => 0) if @one.privileged?
101-
102103
@one.disks.each do |disk|
103-
if disk.mount(lxcrc)
104+
if disk.mount(@driver_config)
104105
mounted << disk
105106
else
106107
error = true
@@ -141,7 +142,7 @@ def start
141142
return false
142143
end
143144

144-
return true if wait_deploy(5)
145+
return true if wait_deploy(5) && configure_pci_nics
145146

146147
clean(true)
147148
return false
@@ -159,23 +160,48 @@ def shutdown
159160
def reboot
160161
rc = @client.stop(@name)
161162

162-
# Remove nic from ovs-switch if needed
163-
@one.get_nics.each do |nic|
164-
del_bridge_port(nic) # network driver matching implemented here
165-
end
163+
# # avoid bind mounts on new container restart
164+
# @one.disks.each {|d| return false unless d.clean_live }
165+
166+
# lxc-stop doesn't remove ovs host side nic
167+
@one.nics.each {|n| @one.del_bridge_port(n) }
166168

167169
return false unless rc
168170

169-
@client.start(@name)
171+
# Make sure container starts with an updated configuration file
172+
file = Tempfile.new
173+
file.write(@one.to_lxc)
174+
file.flush
175+
file.rewind
176+
177+
# starting without ovs host side nic removal results in error
178+
@client.start(@name, { :rcfile => file.path })
179+
wait_deploy(5)
170180
end
171181

182+
#---------------------------------------------------------------------------
183+
# Storage
184+
#---------------------------------------------------------------------------
185+
172186
def clean(ignore_err = false)
187+
disks = @one.disks
188+
189+
# sort disks so rootfs is last during cleanup operations
190+
disks.each do |disk|
191+
next unless disk.rootfs?
192+
193+
disks << disks.delete(disk)
194+
break
195+
end
196+
197+
# rubocop:disable Style/CombinableLoops
173198
# Unmap storage
174-
@one.disks.each do |disk|
199+
disks.each do |disk|
175200
rc = disk.umount({ :ignore_err => ignore_err })
176201

177202
return false if ignore_err != true && !rc
178203
end
204+
# rubocop:enable Style/CombinableLoops
179205

180206
# Clean bindpoint
181207
FileUtils.rm_rf(@one.bind_folder) if Dir.exist?(@one.bind_folder)
@@ -184,6 +210,92 @@ def clean(ignore_err = false)
184210
@client.destroy(@name) if @client.list.include?(@name)
185211
end
186212

213+
def attach_disk(id = nil, guest_path = nil)
214+
id ||= @one.hotplug_disk_id
215+
guest_path ||= "/#{@driver_config[:mountopts][:mountpoint].gsub('$id', id.to_s)}"
216+
217+
disk = @one.disk(id)
218+
container_path = @one.bind_mount_path(guest_path)
219+
220+
disk.mount(@driver_config)
221+
disk.pass_mount(container_path)
222+
end
223+
224+
def detach_disk(id = nil)
225+
id ||= @one.hotplug_disk_id
226+
disk = @one.disk(id)
227+
228+
# umount the entry inside the container
229+
cmd = "umount #{disk.mountpoint}"
230+
return false unless execute(cmd)
231+
232+
disk.umount
233+
end
234+
235+
def attach_context
236+
return true unless @one.has_context?
237+
238+
id = @one.context_id
239+
guest_path = '/context'
240+
241+
attach_disk(id, guest_path)
242+
end
243+
244+
def detach_context
245+
return true unless @one.has_context?
246+
247+
detach_disk(@one.context_id)
248+
end
249+
250+
#---------------------------------------------------------------------------
251+
# Network
252+
#---------------------------------------------------------------------------
253+
254+
def attach_nic(mac)
255+
if @one.pci_attach?
256+
nic = @one.hotplug_pci
257+
pci_nic = @one.nic_name_by_address(nic['ADDRESS'])
258+
259+
@client.attach_device(@name, pci_nic)
260+
else
261+
nic = @one.nic_by_mac(mac)
262+
263+
veth_peer = @one.create_veth_pair(nic)
264+
return false unless veth_peer
265+
266+
if !@one.add_bridge_port(nic)
267+
@one.delete_nic(nic['TARGET'])
268+
return false
269+
end
270+
271+
return true if @client.attach_device(@name, LXCVM.veth_peer(nic),
272+
LXCVM.nic_guest(nic))
273+
274+
@one.del_bridge_port(nic)
275+
@one.delete_nic(nic['TARGET'])
276+
false
277+
end
278+
end
279+
280+
def detach_nic(mac)
281+
if @one.pci_attach?
282+
nic = @one.hotplug_pci
283+
pci_nic = nic_name_by_short_address(nic['SHORT_ADDRESS'])
284+
285+
@client.detach_device(@name, pci_nic)
286+
else
287+
nic = @one.nic_by_mac(mac)
288+
289+
return false unless @client.detach_device(@name, LXCVM.nic_guest(nic),
290+
LXCVM.veth_peer(nic))
291+
292+
return true if !@one.del_bridge_port(nic)
293+
294+
@one.delete_nic(nic['TARGET'])
295+
true
296+
end
297+
end
298+
187299
#---------------------------------------------------------------------------
188300
# VNC
189301
#---------------------------------------------------------------------------
@@ -192,8 +304,64 @@ def vnc(signal)
192304
@one.vnc(signal, @one.lxcrc[:vnc][:command], @one.lxcrc[:vnc])
193305
end
194306

307+
#---------------------------------------------------------------------------
308+
# Command Injection
309+
#---------------------------------------------------------------------------
310+
311+
def restart_context
312+
cmd = 'service one-context-reconfigure restart'
313+
execute(cmd, true)
314+
315+
configure_pci_nics
316+
end
317+
318+
# Trigger context pci configuration script without translated PCI device address
319+
# one-context will fail to setup device due to VM_ADDRESS
320+
def configure_pci_nics
321+
pci_nics = @one.pci_nics
322+
323+
return true if pci_nics.empty?
324+
325+
cmd = 'set -a; source /context/context.sh;'
326+
pci_nics.each do |nic|
327+
# override VM_ADDRESS
328+
id = nic['NIC_ID']
329+
address = nic['SHORT_ADDRESS']
330+
331+
cmd << " export PCI#{id}_ADDRESS=#{address};"
332+
end
333+
# find -lname fails on alpine. -lname not an option
334+
cmd << ' /etc/one-context.d/loc-10-network-pci local'
335+
336+
bash(cmd)
337+
true
338+
end
339+
340+
def execute(cmd, detach = false)
341+
@client.attach(@name, cmd, {}, detach)
342+
end
343+
344+
def bash(cmd)
345+
@client.bash(@name, cmd, {})
346+
end
347+
195348
private
196349

350+
def get_pci_nic_name_by_short_address(address)
351+
# find -lname fails on alpine. -lname not an option
352+
cmd = "find /sys/class/net/*/device -lname *#{address}" # same as loc-10-network-pci lookup
353+
354+
rc, o, _e = bash(cmd)
355+
356+
if rc != 0
357+
msg = "#{__method__} PCI Device #{address} not found inside container"
358+
OpenNebula::DriverLogger msg
359+
return false
360+
end
361+
362+
o.split('/')[4] # /sys/class/net/dev159/device
363+
end
364+
197365
# Waits for the container to be RUNNING
198366
# @param timeout[Integer] seconds to wait for the conatiner to start
199367
def wait_deploy(timeout)
@@ -207,18 +375,4 @@ def wait_deploy(timeout)
207375
running?
208376
end
209377

210-
def del_bridge_port(nic)
211-
return true unless /ovswitch/ =~ nic['VN_MAD']
212-
213-
cmd = 'sudo -n ovs-vsctl --if-exists del-port '\
214-
"#{nic['BRIDGE']} #{nic['TARGET']}"
215-
216-
rc, _o, e = Command.execute(cmd, false, 1)
217-
218-
return true if rc.zero?
219-
220-
OpenNebula::DriverLogger.log_error "#{__method__}: #{e}"
221-
false
222-
end
223-
224378
end

0 commit comments

Comments
 (0)