Files
ubicloud/spec/prog/learn_storage_spec.rb
mohi-kalantari 3c4ae99614 Store device id instead of device name for StorageDevices
Right now StorageDevices store the name of devices which on reboot can
change. In order to fix that, we will use udev rules and find the device id
in /dev/disk/by-id.

For SSD disks, there is a unique identifier with wwn- prefix
and for NVMe disks, there is a unique Identifier with the nvme-eui prefix.

We will store these values in the database and on healthcheck queries
find the right device name and proceed with the normal health check routine.
2025-02-28 13:58:51 +01:00

169 lines
9.0 KiB
Ruby

# frozen_string_literal: true
require_relative "../model/spec_helper"
RSpec.describe Prog::LearnStorage do
describe "#start" do
it "exits, saving StorageDevice model instances" do
vmh = Prog::Vm::HostNexus.assemble("::1").subject
ls = described_class.new(Strand.new(stack: [{"subject_id" => vmh.id}]))
expect(ls.sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail ").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
/dev/sda / 205520896 99571712
/dev/sdb /var/storage/devices/stor1 205520896 99571712
/dev/sdc /var/storage/devices/stor2 3331416064 3331276800
EOS
expect(ls.sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail /var/storage").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
/dev/sda /var/storage/devices/stor1 205520896 99571712
/dev/sdb /var/storage/devices/stor2 3331416064 3331276800
EOS
expect(ls.sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'sda$' | grep 'wwn-' | sed -E 's/.*(wwn[^ ]*).*/\\1/'").and_return("wwn-some-random-id1")
expect(ls.sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'sdb$' | grep 'wwn-' | sed -E 's/.*(wwn[^ ]*).*/\\1/'").and_return("wwn-some-random-id2")
expect(ls.sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'sdc$' | grep 'wwn-' | sed -E 's/.*(wwn[^ ]*).*/\\1/'").and_return("wwn-some-random-id3")
expect { ls.start }.to exit({"msg" => "created StorageDevice records"}).and change {
StorageDevice.all.map { |d| [d.name, d.unix_device_list.sort] }.sort
}.from([]).to([
["DEFAULT", ["wwn-some-random-id1"]],
["stor1", ["wwn-some-random-id2"]],
["stor2", ["wwn-some-random-id3"]]
])
end
it "exits, updating existing StorageDevice model instances" do
vmh = Prog::Vm::HostNexus.assemble("::1").subject
ls = described_class.new(Strand.new(stack: [{"subject_id" => vmh.id}]))
expect(ls.sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail ").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
/dev/nvme0n1 / 6205520896 99571712
/dev/nvme0n2 /var/storage/devices/stor1 6205520896 3099571712
/dev/nvme0n3 /var/storage/devices/stor2 3331416064 1531276800
EOS
expect(ls.sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail /var/storage").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
/dev/nvme0n2 /var/storage/devices/stor1 6205520896 3099571712
/dev/nvme0n3 /var/storage/devices/stor2 3331416064 1531276800
EOS
allow(ls.sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'nvme0n2$' | grep 'nvme-eui' | sed -E 's/.*(nvme-eui[^ ]*).*/\\1/'").and_return("nvme-eui.some-random-id1")
expect(ls.sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'nvme0n3$' | grep 'nvme-eui' | sed -E 's/.*(nvme-eui[^ ]*).*/\\1/'").and_return("nvme-eui.some-random-id2")
StorageDevice.create_with_id(vm_host_id: vmh.id, name: "stor1", available_storage_gib: 100, total_storage_gib: 100, unix_device_list: ["nvme0n1"])
expect { ls.start }.to exit({"msg" => "created StorageDevice records"}).and change {
StorageDevice.map { |sd|
sd.values.slice(
:name, :available_storage_gib, :total_storage_gib
)
}.sort_by { _1[:name] }
}.from(
[{name: "stor1", total_storage_gib: 100, available_storage_gib: 100}]
).to(
[
{name: "DEFAULT", total_storage_gib: 5, available_storage_gib: 0},
{name: "stor1", total_storage_gib: 5, available_storage_gib: 2},
{name: "stor2", total_storage_gib: 3, available_storage_gib: 1}
]
)
expect(vmh.reload.available_storage_gib).to eq(3)
expect(vmh.reload.total_storage_gib).to eq(13)
end
end
describe Prog::LearnStorage, "#make_model_instances" do
subject(:ls) { described_class.new(Strand.new) }
let(:sshable) { instance_double(Sshable) }
let(:vmh) { instance_double(VmHost, id: "746976d6-315b-8f71-95e6-367c4ac068d7") }
before do
expect(ls).to receive(:sshable).and_return(sshable).at_least(:once)
expect(ls).to receive(:vm_host).and_return(vmh).at_least(:once)
end
it "can parse multiple file systems in /var/storage/NAME" do
expect(sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail ").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
tmpfs /run 3331420160 3328692224
/dev/sda / 452564664320 381456842752
tmpfs /dev/shm 16657084416 16605118464
tmpfs /run/lock 5242880 5234688
tmpfs /sys/firmware/efi/efivars 448412 74256
/dev/aaa /boot 2024529920 1641877504
/dev/sdb /var/storage/devices/stor1 205520896 99571712
/dev/sdc /var/storage/devices/stor2 3331416064 3331276800
EOS
expect(sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail /var/storage").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
/dev/sdb /var/storage/devices/stor1 205520896 99571712
/dev/sdc /var/storage/devices/stor2 3331416064 3331276800
EOS
allow(ls.sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'sdb$' | grep 'wwn-' | sed -E 's/.*(wwn[^ ]*).*/\\1/'").and_return("wwn-some-random-id1")
expect(ls.sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'sdc$' | grep 'wwn-' | sed -E 's/.*(wwn[^ ]*).*/\\1/'").and_return("wwn-some-random-id2")
expect(ls.make_model_instances.map(&:name)).to eq(%w[DEFAULT stor1 stor2])
end
it "can use any file system that is present at '/var/storage'" do
# First, the sshable is scanned for any file systems in
# /var/storage/devices. Iff there are none, a second command is
# sent to fill in the "DEFAULT" storage device.
expect(sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail ").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
tmpfs /run 3331420160 3328692224
/dev/sda / 452564664320 381456842752
tmpfs /dev/shm 16657084416 16605118464
tmpfs /run/lock 5242880 5234688
tmpfs /sys/firmware/efi/efivars 448412 74256
/dev/aaa /boot 2024529920 1641877504
EOS
expect(sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail /var/storage").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
/dev/sda / 452564664320 381456842752
EOS
allow(ls.sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'sda$' | grep 'wwn-' | sed -E 's/.*(wwn[^ ]*).*/\\1/'").and_return("wwn-some-random-id1")
expect(ls.make_model_instances.map(&:name)).to eq(%w[DEFAULT])
end
it "can find underlying unix devices for raided disks" do
expect(sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail ").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
tmpfs /run 3331420160 3328692224
/dev/sda / 452564664320 381456842752
tmpfs /dev/shm 16657084416 16605118464
tmpfs /run/lock 5242880 5234688
tmpfs /sys/firmware/efi/efivars 448412 74256
/dev/aaa /boot 2024529920 1641877504
EOS
expect(sshable).to receive(:cmd).with("cat /proc/mdstat").and_return(<<EOS)
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10]
md2 : active raid1 nvme1n1p3[1] nvme0n1p3[0]
465370432 blocks super 1.2 [2/2] [UU]
bitmap: 3/4 pages [12KB], 65536KB chunk
md0 : active raid1 nvme1n1p1[1] nvme0n1p1[0]
33520640 blocks super 1.2 [2/2] [UU]
md1 : active raid1 nvme1n1p2[1] nvme0n1p2[0]
1046528 blocks super 1.2 [2/2] [UU]
unused devices: <none>
EOS
expect(sshable).to receive(:cmd).with("df -B1 --output=source,target,size,avail /var/storage").and_return(<<EOS)
Filesystem Mounted on 1B-blocks Avail
/dev/md2 / 452564664320 381456842752
EOS
expect(sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'nvme1n1$' | grep 'nvme-eui' | sed -E 's/.*(nvme-eui[^ ]*).*/\\1/'").and_return("nvme-eui.random-id1")
expect(sshable).to receive(:cmd).with("ls -l /dev/disk/by-id/ | grep 'nvme0n1$' | grep 'nvme-eui' | sed -E 's/.*(nvme-eui[^ ]*).*/\\1/'").and_return("nvme-eui.random-id2")
expect(ls.make_model_instances.map(&:unix_device_list)).to eq([["nvme-eui.random-id1", "nvme-eui.random-id2"]])
end
end
end