xref: /spdk/scripts/vagrant/Vagrantfile (revision 5d1fa2993239a43d6d5c924d0555b3987aae6b8e)
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