From 24634268c3c814109673e6ef0c67873466036bea Mon Sep 17 00:00:00 2001 From: Andres Montalban Date: Thu, 27 Apr 2017 10:56:14 -0300 Subject: [PATCH] Add basic FreeBSD support using ZFS --- .../action/manage_storage.rb | 21 ++- .../cap/freebsd/manage_storage.rb | 83 ++++++++++ .../cap/linux/manage_storage.rb | 100 ++++++++++++ .../cap/windows/manage_storage.rb | 49 ++++++ .../manage_storage.rb | 145 ------------------ locales/en.yml | 5 + 6 files changed, 254 insertions(+), 149 deletions(-) create mode 100644 lib/vagrant-persistent-storage/cap/freebsd/manage_storage.rb create mode 100644 lib/vagrant-persistent-storage/cap/linux/manage_storage.rb create mode 100644 lib/vagrant-persistent-storage/cap/windows/manage_storage.rb delete mode 100644 lib/vagrant-persistent-storage/manage_storage.rb diff --git a/lib/vagrant-persistent-storage/action/manage_storage.rb b/lib/vagrant-persistent-storage/action/manage_storage.rb index 88ed356..251ca3f 100644 --- a/lib/vagrant-persistent-storage/action/manage_storage.rb +++ b/lib/vagrant-persistent-storage/action/manage_storage.rb @@ -1,13 +1,10 @@ require "log4r" -require 'vagrant-persistent-storage/manage_storage' module VagrantPlugins module PersistentStorage module Action class ManageAll - include ManageStorage - def initialize(app, env) @app = app @machine = env[:machine] @@ -25,9 +22,25 @@ def call(env) return @app.call(env) if @machine.state.id == :poweroff && env[:machine_action] == :resume # skip if machine is powered off and the action is resume return @app.call(env) if @machine.state.id == :saved && env[:machine_action] == :resume - + return @app.call(env) unless env[:machine].config.persistent_storage.enabled? return @app.call(env) unless env[:machine].config.persistent_storage.manage? + + guest_name = @machine.guest.name if @machine.guest.respond_to?(:name) + guest_name ||= @machine.guest.to_s.downcase + + case guest_name + when /freebsd/ + env[:ui].info I18n.t('vagrant_persistent_storage.guest.freebsd') + require 'vagrant-persistent-storage/cap/freebsd/manage_storage' + when /windows/ + env[:ui].info I18n.t('vagrant_persistent_storage.guest.windows') + require 'vagrant-persistent-storage/cap/windows/manage_storage' + else + env[:ui].info I18n.t('vagrant_persistent_storage.guest.linux') + require 'vagrant-persistent-storage/cap/linux/manage_storage' + end + @logger.info '** Managing Persistent Storage **' env[:ui].info I18n.t('vagrant_persistent_storage.action.manage_storage') diff --git a/lib/vagrant-persistent-storage/cap/freebsd/manage_storage.rb b/lib/vagrant-persistent-storage/cap/freebsd/manage_storage.rb new file mode 100644 index 0000000..e940c4e --- /dev/null +++ b/lib/vagrant-persistent-storage/cap/freebsd/manage_storage.rb @@ -0,0 +1,83 @@ +require 'tempfile' +require 'fileutils' +require 'erb' + +def populate_template(machine) + mnt_name = machine.config.persistent_storage.mountname + mnt_point = machine.config.persistent_storage.mountpoint + mnt_options = machine.config.persistent_storage.mountoptions + disk_dev = machine.config.persistent_storage.diskdevice + fs_type = machine.config.persistent_storage.filesystem + manage = machine.config.persistent_storage.manage + mount = machine.config.persistent_storage.mount + format = machine.config.persistent_storage.format + + disk_dev = '/dev/ada1' unless disk_dev != 0 + + # In case disk_dev is /dev/ada1 the device is ada1 + device = disk_dev.split("/").last + + mnt_options = ['defaults'] unless mnt_options != 0 + fs_type = 'ufs' unless fs_type != 0 + + ## shell script to format disk, create/manage LVM, mount disk + disk_operations_template = ERB.new <<-EOF +#!/bin/sh + +[ -c #{disk_dev} ] || exit 1 + +OLD_POOL=$(zdb -l /dev/ada1 | grep '^[[:space:]]*name' | cut -f 2 -d"\'" | head -1) + +# We check if there is a prior zpool in device +if [ -z "${OLD_POOL}" ]; then + # Device is empty, then we create a zpool in it + zpool create -f #{mnt_name} #{disk_dev} || exit 1 +else + if [ "${OLD_POOL}" == "#{mnt_name}" ]; then + # There is a prior zpool in device that matches our setting + zpool list | grep -iq #{mnt_name} + + if [ $? -gt 0 ]; then + # The zpool is not imported + zpool import -f #{mnt_name} || exit 1 + fi + else + # The prior zpool in device differs from our setting + # so we import it with requested name + zpool import -f ${OLD_POOL} #{mnt_name} + fi +fi + +<% if mount == true %> +zfs set mountpoint=#{mnt_point} #{mnt_name} || exit 1 +<% end %> +exit $? +EOF + + buffer = disk_operations_template.result(binding) + tmp_script = Tempfile.new("disk_operations_#{mnt_name}.sh") + + target_script = "/tmp/disk_operations_#{mnt_name}.sh" + + File.open("#{tmp_script.path}", 'wb') do |f| + f.write buffer + end + + machine.communicate.upload(tmp_script.path, target_script) + machine.communicate.sudo("chmod 755 #{target_script}") +end + +def run_disk_operations(machine) + return unless machine.communicate.ready? + mnt_name = machine.config.persistent_storage.mountname + mnt_name = 'vps' unless mnt_name != 0 + target_script = "/tmp/disk_operations_#{mnt_name}.sh" + machine.communicate.sudo("#{target_script} && rm -f #{target_script}") +end + +def manage_volumes(machine) + populate_template(machine) + if machine.config.persistent_storage.manage? + run_disk_operations(machine) + end +end \ No newline at end of file diff --git a/lib/vagrant-persistent-storage/cap/linux/manage_storage.rb b/lib/vagrant-persistent-storage/cap/linux/manage_storage.rb new file mode 100644 index 0000000..3f2dece --- /dev/null +++ b/lib/vagrant-persistent-storage/cap/linux/manage_storage.rb @@ -0,0 +1,100 @@ +require 'tempfile' +require 'fileutils' +require 'erb' + +def populate_template(machine) + mnt_name = machine.config.persistent_storage.mountname + mnt_point = machine.config.persistent_storage.mountpoint + mnt_options = machine.config.persistent_storage.mountoptions + vg_name = machine.config.persistent_storage.volgroupname + disk_dev = machine.config.persistent_storage.diskdevice + fs_type = machine.config.persistent_storage.filesystem + manage = machine.config.persistent_storage.manage + use_lvm = machine.config.persistent_storage.use_lvm + mount = machine.config.persistent_storage.mount + format = machine.config.persistent_storage.format + + vg_name = 'vps' unless vg_name != 0 + disk_dev = '/dev/sdb' unless disk_dev != 0 + mnt_name = 'vps' unless mnt_name != 0 + mnt_options = ['defaults'] unless mnt_options != 0 + fs_type = 'ext3' unless fs_type != 0 + if use_lvm + device = "/dev/#{vg_name}/#{mnt_name}" + else + device = "#{disk_dev}1" + end + + ## shell script to format disk, create/manage LVM, mount disk + disk_operations_template = ERB.new <<-EOF +#!/bin/bash +# fdisk the disk if it's not a block device already: +re='[0-9][.][0-9.]*[0-9.]*'; [[ $(sfdisk --version) =~ $re ]] && version="${BASH_REMATCH}" +if ! awk -v ver="$version" 'BEGIN { if (ver < 2.26 ) exit 1; }'; then + [ -b #{disk_dev}1 ] || echo 0,,8e | sfdisk #{disk_dev} +else + [ -b #{disk_dev}1 ] || echo ,,8e | sfdisk #{disk_dev} +fi +echo "fdisk returned: $?" >> disk_operation_log.txt + +<% if use_lvm == true %> +# Create the physical volume if it doesn't already exist: +[[ `pvs #{disk_dev}1` ]] || pvcreate #{disk_dev}1 +echo "pvcreate returned: $?" >> disk_operation_log.txt +# Create the volume group if it doesn't already exist: +[[ `vgs #{vg_name}` ]] || vgcreate #{vg_name} #{disk_dev}1 +echo "vgcreate returned: $?" >> disk_operation_log.txt +# Create the logical volume if it doesn't already exist: +[[ `lvs #{vg_name} | grep #{mnt_name}` ]] || lvcreate -l 100%FREE -n #{mnt_name} #{vg_name} +echo "lvcreate returned: $?" >> disk_operation_log.txt +# Activate the volume group if it's inactive: +[[ `lvs #{vg_name} --noheadings --nameprefixes | grep LVM2_LV_ATTR | grep "wi\-a"` ]] || vgchange #{vg_name} -a y +echo "vg activation returned: $?" >> disk_operation_log.txt +<% end %> + +<% if format == true %> +# Create the filesytem if it doesn't already exist +MNT_NAME=#{mnt_name} +[[ `blkid | grep ${MNT_NAME:0:16} | grep #{fs_type}` ]] || mkfs.#{fs_type} -L #{mnt_name} #{device} +echo "#{fs_type} creation return: $?" >> disk_operation_log.txt +<% if mount == true %> +# Create mountpoint #{mnt_point} +[ -d #{mnt_point} ] || mkdir -p #{mnt_point} +# Update fstab with new mountpoint name +[[ `grep -i #{device} /etc/fstab` ]] || echo #{device} #{mnt_point} #{fs_type} #{mnt_options.join(',')} 0 0 >> /etc/fstab +echo "fstab update returned: $?" >> disk_operation_log.txt +# Finally, mount the partition +[[ `mount | grep #{mnt_point}` ]] || mount #{mnt_point} +echo "#{mnt_point} mounting returned: $?" >> disk_operation_log.txt +<% end %> +<% end %> +exit $? +EOF + + buffer = disk_operations_template.result(binding) + tmp_script = Tempfile.new("disk_operations_#{mnt_name}.sh") + + target_script = "/tmp/disk_operations_#{mnt_name}.sh" + + File.open("#{tmp_script.path}", 'wb') do |f| + f.write buffer + end + + machine.communicate.upload(tmp_script.path, target_script) + machine.communicate.sudo("chmod 755 #{target_script}") +end + +def run_disk_operations(machine) + return unless machine.communicate.ready? + mnt_name = machine.config.persistent_storage.mountname + mnt_name = 'vps' unless mnt_name != 0 + target_script = "/tmp/disk_operations_#{mnt_name}.sh" + machine.communicate.sudo("#{target_script} && rm -f #{target_script}") +end + +def manage_volumes(machine) + populate_template(machine) + if machine.config.persistent_storage.manage? + run_disk_operations(machine) + end +end \ No newline at end of file diff --git a/lib/vagrant-persistent-storage/cap/windows/manage_storage.rb b/lib/vagrant-persistent-storage/cap/windows/manage_storage.rb new file mode 100644 index 0000000..c908a90 --- /dev/null +++ b/lib/vagrant-persistent-storage/cap/windows/manage_storage.rb @@ -0,0 +1,49 @@ +require 'tempfile' +require 'fileutils' +require 'erb' + +def populate_template(machine) + + ## windows filesystem options + drive_letter = machine.config.persistent_storage.drive_letter + + if drive_letter == 0 + drive_letter = "" + else + drive_letter = "letter=#{drive_letter}" + end + + ## shell script for windows to create NTFS partition and assign drive letter + disk_operations_template = ERB.new <<-EOF +<% if format == true %> +foreach ($disk in get-wmiobject Win32_DiskDrive -Filter "Partitions = 0"){ + $disk.DeviceID + $disk.Index + "select disk "+$disk.Index+"`r clean`r create partition primary`r format fs=ntfs unit=65536 quick`r active`r assign #{drive_letter}" | diskpart >> disk_operation_log.txt +} +<% end %> +EOF + + buffer = disk_operations_template.result(binding) + tmp_script = Tempfile.new("disk_operations_#{mnt_name}.ps1") + target_script = "disk_operations_#{mnt_name}.ps1" + + File.open("#{tmp_script.path}", 'wb') do |f| + f.write buffer + end + + machine.communicate.upload(tmp_script.path, target_script) +end + +def run_disk_operations(machine) + return unless machine.communicate.ready? + target_script = "disk_operations_#{mnt_name}.ps1" + machine.communicate.sudo("powershell -executionpolicy bypass -file #{target_script}") +end + +def manage_volumes(machine) + populate_template(machine) + if machine.config.persistent_storage.manage? + run_disk_operations(machine) + end +end diff --git a/lib/vagrant-persistent-storage/manage_storage.rb b/lib/vagrant-persistent-storage/manage_storage.rb deleted file mode 100644 index ccd3599..0000000 --- a/lib/vagrant-persistent-storage/manage_storage.rb +++ /dev/null @@ -1,145 +0,0 @@ -require 'tempfile' -require 'fileutils' -require 'erb' - -module VagrantPlugins - module PersistentStorage - module ManageStorage - def populate_template(m) - mnt_name = m.config.persistent_storage.mountname - mnt_point = m.config.persistent_storage.mountpoint - mnt_options = m.config.persistent_storage.mountoptions - vg_name = m.config.persistent_storage.volgroupname - disk_dev = m.config.persistent_storage.diskdevice - fs_type = m.config.persistent_storage.filesystem - manage = m.config.persistent_storage.manage - use_lvm = m.config.persistent_storage.use_lvm - mount = m.config.persistent_storage.mount - format = m.config.persistent_storage.format - - ## windows filesystem options - drive_letter = m.config.persistent_storage.drive_letter - - if m.config.vm.communicator == :winrm - os = "windows" - else - os = "linux" - end - - vg_name = 'vps' unless vg_name != 0 - disk_dev = '/dev/sdb' unless disk_dev != 0 - mnt_name = 'vps' unless mnt_name != 0 - mnt_options = ['defaults'] unless mnt_options != 0 - fs_type = 'ext3' unless fs_type != 0 - if use_lvm - device = "/dev/#{vg_name}/#{mnt_name}" - else - device = "#{disk_dev}1" - end - if drive_letter == 0 - drive_letter = "" - else - drive_letter = "letter=#{drive_letter}" - end - - if os == "windows" - ## shell script for windows to create NTFS partition and assign drive letter - disk_operations_template = ERB.new <<-EOF - <% if format == true %> - foreach ($disk in get-wmiobject Win32_DiskDrive -Filter "Partitions = 0"){ - $disk.DeviceID - $disk.Index - "select disk "+$disk.Index+"`r clean`r create partition primary`r format fs=ntfs unit=65536 quick`r active`r assign #{drive_letter}" | diskpart >> disk_operation_log.txt - } - <% end %> - EOF - else - ## shell script to format disk, create/manage LVM, mount disk - disk_operations_template = ERB.new <<-EOF -#!/bin/bash -# fdisk the disk if it's not a block device already: -re='[0-9][.][0-9.]*[0-9.]*'; [[ $(sfdisk --version) =~ $re ]] && version="${BASH_REMATCH}" -if ! awk -v ver="$version" 'BEGIN { if (ver < 2.26 ) exit 1; }'; then - [ -b #{disk_dev}1 ] || echo 0,,8e | sfdisk #{disk_dev} -else - [ -b #{disk_dev}1 ] || echo ,,8e | sfdisk #{disk_dev} -fi -echo "fdisk returned: $?" >> disk_operation_log.txt - -<% if use_lvm == true %> -# Create the physical volume if it doesn't already exist: -[[ `pvs #{disk_dev}1` ]] || pvcreate #{disk_dev}1 -echo "pvcreate returned: $?" >> disk_operation_log.txt -# Create the volume group if it doesn't already exist: -[[ `vgs #{vg_name}` ]] || vgcreate #{vg_name} #{disk_dev}1 -echo "vgcreate returned: $?" >> disk_operation_log.txt -# Create the logical volume if it doesn't already exist: -[[ `lvs #{vg_name} | grep #{mnt_name}` ]] || lvcreate -l 100%FREE -n #{mnt_name} #{vg_name} -echo "lvcreate returned: $?" >> disk_operation_log.txt -# Activate the volume group if it's inactive: -[[ `lvs #{vg_name} --noheadings --nameprefixes | grep LVM2_LV_ATTR | grep "wi\-a"` ]] || vgchange #{vg_name} -a y -echo "vg activation returned: $?" >> disk_operation_log.txt -<% end %> - -<% if format == true %> -# Create the filesytem if it doesn't already exist -MNT_NAME=#{mnt_name} -[[ `blkid | grep ${MNT_NAME:0:16} | grep #{fs_type}` ]] || mkfs.#{fs_type} -L #{mnt_name} #{device} -echo "#{fs_type} creation return: $?" >> disk_operation_log.txt -<% if mount == true %> -# Create mountpoint #{mnt_point} -[ -d #{mnt_point} ] || mkdir -p #{mnt_point} -# Update fstab with new mountpoint name -[[ `grep -i #{device} /etc/fstab` ]] || echo #{device} #{mnt_point} #{fs_type} #{mnt_options.join(',')} 0 0 >> /etc/fstab -echo "fstab update returned: $?" >> disk_operation_log.txt -# Finally, mount the partition -[[ `mount | grep #{mnt_point}` ]] || mount #{mnt_point} -echo "#{mnt_point} mounting returned: $?" >> disk_operation_log.txt -<% end %> -<% end %> -exit $? - EOF - end - - buffer = disk_operations_template.result(binding) - tmp_script = Tempfile.new("disk_operations_#{mnt_name}.sh") - - if os == 'windows' - target_script = "disk_operations_#{mnt_name}.ps1" - else - target_script = "/tmp/disk_operations_#{mnt_name}.sh" - end - - File.open("#{tmp_script.path}", 'wb') do |f| - f.write buffer - end - m.communicate.upload(tmp_script.path, target_script) - unless os == 'windows' - m.communicate.sudo("chmod 755 #{target_script}") - end - end - - def run_disk_operations(m) - return unless m.communicate.ready? - mnt_name = m.config.persistent_storage.mountname - mnt_name = 'vps' unless mnt_name != 0 - if m.config.vm.communicator == :winrm - target_script = "disk_operations_#{mnt_name}.ps1" - m.communicate.sudo("powershell -executionpolicy bypass -file #{target_script}") - else - target_script = "/tmp/disk_operations_#{mnt_name}.sh" - m.communicate.sudo("#{target_script}") - end - - end - - def manage_volumes(m) - populate_template(m) - if m.config.persistent_storage.manage? - run_disk_operations(m) - end - end - - end - end -end diff --git a/locales/en.yml b/locales/en.yml index b64bd7f..0b771f5 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -10,3 +10,8 @@ en: config: not_a_bool: "A value for %{config_key} can only be true or false, not type '%{value}'" not_an_array_or_string: "A value for %{config_key} must be an Array or String, not type '%{is_class}'" + guest: + windows: "** Loading Windows guest capabilities **" + freebsd: "** Loading FreeBSD guest capabilities **" + linux: "** Loading Linux guest capabilities **" +