1# -*- mode: ruby -*- 2# vi: set ft=ruby : 3 4require 'open3' 5def get_box_type(distro, force_distro) 6 spdk_distro = 'spdk/' + distro 7 localboxes, stderr, status = Open3.capture3("vagrant box list") 8 return spdk_distro if localboxes.include?(spdk_distro) 9 10 distro_to_type = { 11 'centos7' => 'centos/7', 12 'ubuntu2004' => 'peru/ubuntu-20.04-server-amd64', 13 'ubuntu2204' => 'generic/ubuntu2204', 14 'fedora37' => 'generic/fedora37', 15 'fedora38' => 'generic/fedora38', 16 'arch' => 'generic/arch', 17 'freebsd12' => 'generic/freebsd12', 18 'freebsd13' => 'generic/freebsd13', 19 'rocky8' => 'rockylinux/8', 20 'rocky9' => 'rockylinux/9' 21 } 22 abort("Invalid argument! #{distro}") unless distro_to_type.key?(distro) || force_distro 23 24 return distro_to_type[distro] ? distro_to_type[distro] : distro 25end 26 27def setup_proxy(config,distro) 28 return unless ENV['http_proxy'] 29 30 if Vagrant.has_plugin?("vagrant-proxyconf") 31 config.proxy.http = ENV['http_proxy'] 32 config.proxy.https = ENV['https_proxy'] 33 config.proxy.no_proxy = "localhost,127.0.0.1" 34 end 35 36 # Proxyconf does not seem to support FreeBSD boxes or at least it's 37 # docs do not mention that. Set up proxy configuration manually. 38 if distro.include?("freebsd") 39 $freebsd_proxy = <<-SCRIPT 40 sudo -s 41 echo "export http_proxy=#{ENV['http_proxy']}" >> /etc/profile 42 echo "export https_proxy=#{ENV['http_proxy']}" >> /etc/profile 43 echo "pkg_env: {http_proxy: #{ENV['http_proxy']}}" > /usr/local/etc/pkg.conf 44 chown root:wheel /usr/local/etc/pkg.conf 45 chmod 644 /usr/local/etc/pkg.conf 46 SCRIPT 47 config.vm.provision "shell", inline: $freebsd_proxy 48 end 49end 50 51def copy_gitconfig(config) 52 src_path = '~/.gitconfig' 53 return unless File.file?(File.expand_path(src_path)) 54 55 config.vm.provision "file", source: src_path, destination: ".gitconfig" 56end 57 58def copy_tsocks(config) 59 tsocks_file = 'tsocks.conf' 60 tsocks_file_path = '/etc/' + tsocks_file 61 62 return unless File.file?(tsocks_file_path) 63 64 $tsocks_copy_cmd = <<-SCRIPT 65 sudo -s 66 mv -f "#{tsocks_file}" "#{tsocks_file_path}" 67 chown root "#{tsocks_file_path}" 68 chmod 644 "#{tsocks_file_path}" 69 SCRIPT 70 71 config.vm.provision "file", source: tsocks_file_path, destination: tsocks_file 72 config.vm.provision "shell", inline: $tsocks_copy_cmd 73end 74 75def copy_vagrant_tools(config,files_sync_backend) 76 src_path = '~/vagrant_tools' 77 return unless File.directory?(File.expand_path(src_path)) 78 79 config.vm.synced_folder src_path, "/home/vagrant/tools", files_sync_backend 80end 81 82def copy_sources_dirs(config, files_sync_backend) 83 return unless ENV['COPY_SPDK_DIR'] == "1" 84 return unless ENV['SPDK_DIR'] 85 86 repo_prefix = '/home/vagrant/spdk_repo' 87 config.vm.synced_folder ENV['SPDK_DIR'], "#{repo_prefix}/spdk", files_sync_backend 88 89 # Optional directories 90 for dir in ['spdk-abi', 'dpdk'] 91 src_path = "#{ENV['SPDK_DIR']}/../#{dir}" 92 next unless File.directory?(File.expand_path(src_path)) 93 94 config.vm.synced_folder src_path, "#{repo_prefix}/#{dir}", files_sync_backend 95 end 96end 97 98def copy_spdk_artifacts(config, plugins_sync_backend) 99 return unless ENV['COPY_SPDK_ARTIFACTS'] == "1" 100 101 vagrantfile_dir=(ENV['VAGRANTFILE_DIR'] || "none") 102 config.vm.synced_folder "#{vagrantfile_dir}/output", "/home/vagrant/spdk_repo/output", plugins_sync_backend 103end 104 105def make_spdk_local_copy_of_nfs(config,distro) 106 user_group = 'vagrant:vagrant' 107 108 spdk_path = '/home/vagrant/spdk_repo/spdk' 109 spdk_tmp_path = '/tmp/spdk' 110 $spdk_repo_cmd = <<-SCRIPT 111 sudo -s 112 cp -R '#{spdk_path}' '#{spdk_tmp_path}' 113 umount '#{spdk_path}' && rm -rf '#{spdk_path}' 114 mv '#{spdk_tmp_path}' '#{spdk_path}' 115 chown -R #{user_group} '#{spdk_path}' 116 SCRIPT 117 118 config.vm.provision "shell", inline: $spdk_repo_cmd 119end 120 121def get_nvme_disk(disk, index) 122 if ENV['NVME_FILE'] 123 nvme_file = ENV['NVME_FILE'].split(',') 124 nvme_disk = nvme_file[index] 125 else 126 nvme_disk = '/var/lib/libvirt/images/nvme_disk.img' 127 end 128 129 unless nvme_disk == "none" || File.exist?(nvme_disk) 130 puts 'If run with libvirt provider please execute create_nvme_img.sh' 131 end 132 133 return nvme_disk 134end 135 136def setup_nvme_disk(libvirt, disk, index) 137 nvme_disk_id = disk + '-' + index.to_s 138 nvme_disk = get_nvme_disk(disk, index) 139 140 nvme_namespaces=(ENV['NVME_DISKS_NAMESPACES'] || "").split(',') 141 nvme_cmbs=(ENV['NVME_CMB'] || "").split(',') 142 nvme_pmrs=(ENV['NVME_PMR'] || "").split(',') 143 nvme_zns=(ENV['NVME_ZNS'] || "").split(',') 144 nvme_ms=(ENV['NVME_MS'] || "").split(',') 145 nvme_fdp=(ENV['NVME_FDP'] || "").split(',') 146 147 namespace_disks = [] 148 pmr_cmdline = "" 149 nvme_controller = "" 150 fdp_subsys = "" 151 fdp_subsys_id = "" 152 ms = "" 153 154 # Define controller 155 nvme_controller = "nvme,id=#{nvme_disk_id},serial=1234#{index}" 156 157 # For the FDP, we need to hook our nvme into a dedicated subsystem 158 if !nvme_fdp[index].nil? && nvme_fdp[index] != "" 159 fdp_subsys_id = "fdp-subsys#{index}" 160 fdp = nvme_fdp[index].split(':')[0..3] 161 fdp_ruhs = (nvme_fdp[index].split(':')[4..]) # fdp.ruhs per ns 162 163 # Put some defaults in place if needed 164 fdp_enable = "#{fdp[0] != nil && fdp[0] != '' ? fdp[0] : 'off'}" 165 fdp_runs = "#{fdp[1] != nil && fdp[1] != '' ? fdp[1] : '96M'}" 166 fdp_nrg = "#{fdp[2] != nil && fdp[2] != '' ? fdp[2] : 2}" 167 fdp_nruh = "#{fdp[3] != nil && fdp[3] != '' ? fdp[3] : 8}" 168 169 fdp_subsys = "nvme-subsys,id=#{fdp_subsys_id},fdp=#{fdp_enable}" 170 fdp_subsys << ",fdp.runs=#{fdp_runs},fdp.nrg=#{fdp_nrg},fdp.nruh=#{fdp_nruh}" 171 172 nvme_controller << ",subsys=#{fdp_subsys_id}" 173 174 libvirt.qemuargs :value => "-device" 175 libvirt.qemuargs :value => fdp_subsys 176 end 177 # Gather all drives - each namespace requires separate drive 178 if nvme_namespaces[index].nil? 179 namespace_disks = namespace_disks + nvme_disk.split() 180 elsif !nvme_namespaces[index].nil? && !nvme_namespaces[index].match(/^[0-9]+$/) 181 namespace_disks = namespace_disks + nvme_disk.split() + nvme_namespaces[index].split(':') 182 elsif !nvme_namespaces[index].nil? && nvme_namespaces[index] == "1" 183 libvirt.qemuargs :value => "-drive" 184 libvirt.qemuargs :value => "format=raw,file=#{nvme_disk},if=none,id=#{nvme_disk_id}" 185 nvme_controller <<",drive=#{nvme_disk_id}" 186 end 187 188 if !nvme_cmbs[index].nil? && nvme_cmbs[index] != "" 189 # Fix the size of the buffer to 128M 190 nvme_controller << ",cmb_size_mb=128" 191 end 192 193 if !nvme_pmrs[index].nil? && nvme_pmrs[index] != "" 194 pmr_path, pmr_size = nvme_pmrs[index].split(':') 195 if pmr_size.nil? 196 pmr_size = "16M" 197 end 198 nvme_controller << ",pmrdev=pmr#{index}" 199 pmr_cmdline = "memory-backend-file,id=pmr#{index},share=on,mem-path=#{pmr_path},size=#{pmr_size}" 200 end 201 202 libvirt.qemuargs :value => "-device" 203 libvirt.qemuargs :value => nvme_controller 204 205 if pmr_cmdline != "" 206 libvirt.qemuargs :value => "-object" 207 libvirt.qemuargs :value => pmr_cmdline 208 end 209 210 # Define all namespaces 211 namespace_disks.each_with_index { |disk, nsid| 212 if disk == "none" 213 next 214 end 215 zoned = nvme_zns[index].nil? ? "false" : "true" 216 if ! nvme_ms[index].nil? && nvme_ms[index] == "true" 217 ms = ",ms=64" 218 end 219 ns = "nvme-ns,drive=#{nvme_disk_id}-drive#{nsid},bus=#{nvme_disk_id},nsid=#{nsid + 1},zoned=#{zoned},logical_block_size=4096,physical_block_size=4096#{ms}" 220 if !fdp_ruhs.nil? && !fdp_ruhs[nsid].nil? && fdp_ruhs[nsid] != "" 221 ns << ",fdp.ruhs=#{fdp_ruhs[nsid]}" 222 end 223 libvirt.qemuargs :value => "-drive" 224 libvirt.qemuargs :value => "format=raw,file=#{disk},if=none,id=#{nvme_disk_id}-drive#{nsid}" 225 libvirt.qemuargs :value => "-device" 226 libvirt.qemuargs :value => ns 227 } 228 229end 230 231def setup_ssh(config) 232 config.ssh.forward_agent = true 233 config.ssh.forward_x11 = true 234 if ENV['VAGRANT_PASSWORD_AUTH'] == "1" 235 config.ssh.username = "vagrant" 236 config.ssh.password = "vagrant" 237 config.ssh.private_key_path = nil 238 end 239end 240 241def deploy_test_vm(config, distro, plugins_sync_backend) 242 return unless ENV['DEPLOY_TEST_VM'] == "1" 243 return unless ENV['COPY_SPDK_DIR'] == "1" 244 return unless ENV['SPDK_DIR'] 245 246 # use http proxy if available 247 setup_proxy(config, distro) 248 249 # Copy the tsocks configuration file for use when installing some spdk test pool dependencies 250 copy_tsocks(config) 251 252 # freebsd boxes in order to have spdk sources synced from 253 # host properly will use NFS with "ro" option enabled to prevent changes 254 # on host filesystem. 255 # To make sources usable in the guest VM we need to unmount them and use 256 # local copy. 257 make_spdk_local_copy_of_nfs(config,distro) if plugins_sync_backend[:type] == :nfs 258 259 config.vm.provision "shell" do |setup| 260 setup.inline = "/home/vagrant/spdk_repo/spdk/test/common/config/vm_setup.sh" 261 setup.privileged = false 262 setup.args = ["-u", "-i"] 263 end 264end 265 266def setup_virtualbox(config, vmcpu, vmram) 267 config.vm.provider "virtualbox" do |vb| 268 vb.customize ["modifyvm", :id, "--ioapic", "on"] 269 vb.memory = vmram 270 vb.cpus = vmcpu 271 272 nvme_disk=(ENV['NVME_FILE'] || "nvme_disk.img") 273 unless File.exist? (nvme_disk) 274 vb.customize ["createhd", "--filename", nvme_disk, "--variant", "Fixed", "--size", "1024"] 275 vb.customize ["storagectl", :id, "--name", "nvme", "--add", "pcie", "--controller", "NVMe", "--portcount", "1", "--bootable", "off"] 276 vb.customize ["storageattach", :id, "--storagectl", "nvme", "--type", "hdd", "--medium", nvme_disk, "--port", "0"] 277 end 278 279 #support for the SSE4.x instruction is required in some versions of VB. 280 vb.customize ["setextradata", :id, "VBoxInternal/CPUM/SSE4.1", "1"] 281 vb.customize ["setextradata", :id, "VBoxInternal/CPUM/SSE4.2", "1"] 282 end 283end 284 285def setup_libvirt(config, vmcpu, vmram, distro) 286 emulated_nvme_types=(ENV['NVME_DISKS_TYPE'] || "nvme").split(',') 287 288 config.vm.provider "libvirt" do |libvirt, override| 289 libvirt.random_hostname = "1" 290 libvirt.driver = "kvm" 291 libvirt.graphics_type = "vnc" 292 libvirt.memory = vmram 293 libvirt.cpus = vmcpu 294 libvirt.video_type = "cirrus" 295 296 if (distro.include?("freebsd")) 297 # generic/freebsd boxes need to be explicitly run with SCSI bus, 298 # otherwise boot process fails on mounting the disk 299 libvirt.disk_bus = "scsi" 300 elsif (distro.include?("arch")) 301 # Run generic/arch boxes explicitly with IDE bus, 302 # otherwise boot process fails on mounting the disk 303 libvirt.disk_bus = "ide" 304 else 305 libvirt.disk_bus = "virtio" 306 end 307 308 if ENV['SPDK_QEMU_EMULATOR'] 309 libvirt.emulator_path = ENV['SPDK_QEMU_EMULATOR'] 310 libvirt.machine_type = "pc" 311 end 312 313 # we put nvme_disk inside default pool to eliminate libvirt/SELinux Permissions Problems 314 # and to be able to run vagrant from user $HOME directory 315 316 # Loop to create all emulated disks set 317 emulated_nvme_types.each_with_index { |disk, index| 318 setup_nvme_disk(libvirt, disk, index) 319 } 320 321 # Add network interface for openstack tests 322 if ENV['SPDK_OPENSTACK_NETWORK'] == "1" 323 libvirt.qemuargs :value => "-device" 324 libvirt.qemuargs :value => "virtio-net,netdev=openstack.0" 325 libvirt.qemuargs :value => "-netdev" 326 libvirt.qemuargs :value => "user,id=openstack.0" 327 end 328 329 if ENV['VAGRANT_HUGE_MEM'] == "1" 330 libvirt.memorybacking :hugepages 331 end 332 333 # Optional field if we want use other storage pools than default 334 # libvirt.storage_pool_name = "vm" 335 end 336end 337 338################################################################################################# 339# Pick the right distro and bootstrap, default is fedora37 340distro = (ENV['SPDK_VAGRANT_DISTRO'] || "fedora37") 341provider = (ENV['SPDK_VAGRANT_PROVIDER'] || "virtualbox") 342 343# Get all variables for creating vm 344vmcpu = (ENV['SPDK_VAGRANT_VMCPU'] || 2) 345vmram = (ENV['SPDK_VAGRANT_VMRAM'] || 4096) 346 347force_distro = ENV['FORCE_DISTRO'] == "true" ? true : false 348 349distro_to_use = get_box_type(distro, force_distro) 350# Remove --copy-links from default rsync cmdline since we do want to sync 351# actual symlinks as well. Also, since copy is made between host and its 352# local VM we don't need to worry about saturating the local link so skip 353# the compression to speed up the whole transfer. 354files_sync_backend = {type: "rsync", rsync__auto: false, rsync__args: ["--archive", "--verbose", "--delete"]} 355 356if ENV['NFS4_BACKEND'] or not Vagrant.has_plugin?("vagrant-sshfs") 357 plugins_sync_backend = {type: :nfs, nfs_udp: false, nfs_version: 4} 358else 359 plugins_sync_backend = {type: :sshfs} 360end 361 362Vagrant.configure(2) do |config| 363 config.vm.box = distro_to_use 364 config.vm.box_check_update = false 365 config.vm.synced_folder '.', '/vagrant', disabled: true 366 if ENV['VAGRANT_BOX_VERSION'] 367 config.vm.box_version = ENV['VAGRANT_BOX_VERSION'] 368 end 369 370 # Copy in the .gitconfig if it exists 371 copy_gitconfig(config) 372 373 # Copy in the user's tools if they exists 374 copy_vagrant_tools(config,files_sync_backend) 375 376 copy_sources_dirs(config, files_sync_backend) 377 378 # rsync artifacts from build 379 copy_spdk_artifacts(config, plugins_sync_backend) 380 381 # Setup SSH 382 setup_ssh(config) 383 384 # Virtualbox configuration 385 setup_virtualbox(config,vmcpu,vmram) 386 387 setup_libvirt(config,vmcpu,vmram,distro) 388 389 # provision the vm with all of the necessary spdk dependencies for running the autorun.sh tests 390 deploy_test_vm(config, distro, plugins_sync_backend) 391end 392 393if ENV['EXTRA_VAGRANTFILES'] 394 loaders = (ENV['EXTRA_VAGRANTFILES'].split(',')) 395 loaders.each { |loader| 396 load loader if File.exists?(loader) 397 } 398end 399