SPDK supported max ios per sec for read/write combined, so we added support for it. However, cgroup v2 (which we’re migrating to for I/O rate limiting) does not support combined IOPS limits — only separate `IOReadIOPSMax` and `IOWriteIOPSMax`. This patch removes the unused `max_ios_per_sec` knob. It was never used in production, and our current rate limiting relies solely on read/write bandwidth, which is fully supported by cgroup v2.
173 lines
5.1 KiB
Ruby
173 lines
5.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "json"
|
|
|
|
class Prog::Test::Vm < Prog::Test::Base
|
|
subject_is :vm, :sshable
|
|
|
|
label def start
|
|
hop_verify_dd
|
|
end
|
|
|
|
label def verify_dd
|
|
# Verifies basic block device health
|
|
# See https://github.com/ubicloud/ubicloud/issues/276
|
|
sshable.cmd("dd if=/dev/urandom of=~/1.txt bs=512 count=1000000")
|
|
sshable.cmd("sync ~/1.txt")
|
|
size_info = sshable.cmd("ls -s ~/1.txt").split
|
|
|
|
unless size_info[0].to_i.between?(500000, 500100)
|
|
fail_test "unexpected size after dd"
|
|
end
|
|
|
|
hop_install_packages
|
|
end
|
|
|
|
label def install_packages
|
|
if /ubuntu|debian/.match?(vm.boot_image)
|
|
sshable.cmd("sudo apt update")
|
|
sshable.cmd("sudo apt install -y build-essential fio")
|
|
elsif vm.boot_image.start_with?("almalinux")
|
|
sshable.cmd("sudo dnf check-update || [ $? -eq 100 ]")
|
|
sshable.cmd("sudo dnf install -y gcc gcc-c++ make fio")
|
|
else
|
|
fail_test "unexpected boot image: #{vm.boot_image}"
|
|
end
|
|
|
|
hop_verify_extra_disks
|
|
end
|
|
|
|
def umount_if_mounted(mount_path)
|
|
sshable.cmd("sudo umount #{mount_path.shellescape}")
|
|
rescue Sshable::SshError => e
|
|
raise unless e.stderr.include?("not mounted")
|
|
end
|
|
|
|
label def verify_extra_disks
|
|
vm.vm_storage_volumes[1..].each_with_index { |volume, disk_index|
|
|
mount_path = "/home/ubi/mnt#{disk_index}"
|
|
sshable.cmd("mkdir -p #{mount_path.shellescape}")
|
|
# this might be a retry, so ensure the mount point is not already mounted
|
|
umount_if_mounted(mount_path)
|
|
sshable.cmd("sudo mkfs.ext4 #{volume.device_path.shellescape}")
|
|
sshable.cmd("sudo mount #{volume.device_path.shellescape} #{mount_path.shellescape}")
|
|
sshable.cmd("sudo chown ubi #{mount_path.shellescape}")
|
|
test_file = File.join(mount_path, "1.txt")
|
|
sshable.cmd("dd if=/dev/urandom of=#{test_file.shellescape} bs=512 count=10000")
|
|
sshable.cmd("sync #{test_file.shellescape}")
|
|
}
|
|
|
|
hop_ping_google
|
|
end
|
|
|
|
label def ping_google
|
|
sshable.cmd("ping -c 2 google.com")
|
|
hop_verify_io_rates
|
|
end
|
|
|
|
def get_read_bw_bytes
|
|
fio_read_cmd = <<~CMD
|
|
sudo fio --filename=./f --size=100M --direct=1 --rw=randread --bs=1M --ioengine=libaio \\
|
|
--iodepth=256 --runtime=4 --numjobs=1 --time_based --group_reporting \\
|
|
--name=test-job --eta-newline=1 --output-format=json
|
|
CMD
|
|
|
|
fio_read_output = JSON.parse(sshable.cmd(fio_read_cmd))
|
|
|
|
fio_read_output.dig("jobs", 0, "read", "bw_bytes")
|
|
end
|
|
|
|
def get_write_bw_bytes
|
|
fio_write_cmd = <<~CMD
|
|
sudo fio --filename=./f --size=100M --direct=1 --rw=randwrite --bs=1M --ioengine=libaio \\
|
|
--iodepth=256 --runtime=4 --numjobs=1 --time_based --group_reporting \\
|
|
--name=test-job --eta-newline=1 --output-format=json
|
|
CMD
|
|
|
|
fio_write_output = JSON.parse(sshable.cmd(fio_write_cmd))
|
|
|
|
fio_write_output.dig("jobs", 0, "write", "bw_bytes")
|
|
end
|
|
|
|
label def verify_io_rates
|
|
vol = vm.vm_storage_volumes.first
|
|
hop_ping_vms_in_subnet if vol.max_read_mbytes_per_sec.nil?
|
|
|
|
# Verify that the max_read_mbytes_per_sec is working
|
|
read_bw_bytes = get_read_bw_bytes
|
|
fail_test "exceeded read bw limit: #{read_bw_bytes}" if read_bw_bytes > vol.max_read_mbytes_per_sec * 1.2 * 1024 * 1024
|
|
|
|
# Verify that the max_write_mbytes_per_sec is working
|
|
write_bw_bytes = get_write_bw_bytes
|
|
fail_test "exceeded write bw limit: #{write_bw_bytes}" if write_bw_bytes > vol.max_write_mbytes_per_sec * 1.2 * 1024 * 1024
|
|
|
|
hop_ping_vms_in_subnet
|
|
end
|
|
|
|
label def ping_vms_in_subnet
|
|
vms_with_same_subnet.each { |x|
|
|
# ping public IPs
|
|
sshable.cmd("ping -c 2 #{x.ephemeral_net4}")
|
|
sshable.cmd("ping -c 2 #{x.ephemeral_net6.nth(2)}")
|
|
|
|
# ping private IPs
|
|
nic = x.nics.first
|
|
private_ip6 = nic.private_ipv6.nth(2).to_s
|
|
private_ip4 = nic.private_ipv4.network.to_s
|
|
sshable.cmd("ping -c 2 #{private_ip6}")
|
|
sshable.cmd("ping -c 2 #{private_ip4}")
|
|
}
|
|
|
|
hop_ping_vms_not_in_subnet
|
|
end
|
|
|
|
label def ping_vms_not_in_subnet
|
|
vms_with_different_subnet.each { |x|
|
|
# ping public IPs should work
|
|
sshable.cmd("ping -c 2 #{x.ephemeral_net4}")
|
|
sshable.cmd("ping -c 2 #{x.ephemeral_net6.nth(2)}")
|
|
|
|
# ping private IPs shouldn't work
|
|
nic = x.nics.first
|
|
private_ip6 = nic.private_ipv6.nth(2).to_s
|
|
private_ip4 = nic.private_ipv4.network.to_s
|
|
|
|
begin
|
|
sshable.cmd("ping -c 2 #{private_ip6}")
|
|
rescue Sshable::SshError
|
|
else
|
|
raise "Unexpected successful ping to private ip6 of a vm in different subnet"
|
|
end
|
|
|
|
begin
|
|
sshable.cmd("ping -c 2 #{private_ip4}")
|
|
rescue Sshable::SshError
|
|
else
|
|
raise "Unexpected successful ping to private ip4 of a vm in different subnet"
|
|
end
|
|
}
|
|
|
|
hop_finish
|
|
end
|
|
|
|
label def finish
|
|
pop "Verified VM!"
|
|
end
|
|
|
|
label def failed
|
|
nap 15
|
|
end
|
|
|
|
def vms_in_same_project
|
|
vm.project.vms.filter { it.id != vm.id }
|
|
end
|
|
|
|
def vms_with_same_subnet
|
|
vms_in_same_project.filter { it.private_subnets.first.id == vm.private_subnets.first.id }
|
|
end
|
|
|
|
def vms_with_different_subnet
|
|
vms_in_same_project.filter { it.private_subnets.first.id != vm.private_subnets.first.id }
|
|
end
|
|
end
|