Merge pull request #7867 from bbrala/hyperv-package

Package Hyper-V boxes
This commit is contained in:
Chris Roberts 2016-12-07 13:37:47 -08:00 committed by GitHub
commit 23c41f4461
14 changed files with 338 additions and 68 deletions

View File

@ -0,0 +1,51 @@
module Vagrant
module Action
module General
class PackageSetupFiles
def initialize(app, env)
@app = app
env["package.include"] ||= []
env["package.vagrantfile"] ||= nil
end
def call(env)
files = {}
env["package.include"].each do |file|
source = Pathname.new(file)
dest = nil
# If the source is relative then we add the file as-is to the include
# directory. Otherwise, we copy only the file into the root of the
# include directory. Kind of strange, but seems to match what people
# expect based on history.
if source.relative?
dest = source
else
dest = source.basename
end
# Assign the mapping
files[file] = dest
end
if env["package.vagrantfile"]
# Vagrantfiles are treated special and mapped to a specific file
files[env["package.vagrantfile"]] = "_Vagrantfile"
end
# Verify the mapping
files.each do |from, _|
raise Vagrant::Errors::PackageIncludeMissing,
file: from if !File.exist?(from)
end
# Save the mapping
env["package.files"] = files
@app.call(env)
end
end
end
end
end

View File

@ -0,0 +1,37 @@
require "fileutils"
require_relative "package"
module Vagrant
module Action
module General
class PackageSetupFolders
include Vagrant::Util::Presence
def initialize(app, env)
@app = app
end
def call(env)
env["package.output"] ||= "package.box"
env["package.directory"] ||= Dir.mktmpdir("vagrant-package-", env[:tmp_path])
# Match up a couple environmental variables so that the other parts of
# Vagrant will do the right thing.
env["export.temp_dir"] = env["package.directory"]
Vagrant::Action::General::Package.validate!(
env["package.output"], env["package.directory"])
@app.call(env)
end
def recover(env)
dir = env["package.directory"]
if File.exist?(dir)
FileUtils.rm_rf(dir)
end
end
end
end
end
end

View File

@ -64,6 +64,28 @@ module VagrantPlugins
end end
end end
# This action packages the virtual machine into a single box file.
def self.action_package
Vagrant::Action::Builder.new.tap do |b|
b.use CheckEnabled
b.use Call, IsState, :not_created do |env1, b2|
if env1[:result]
b2.use Message, I18n.t("vagrant_hyperv.message_not_created")
next
end
b2.use PackageSetupFolders
b2.use PackageSetupFiles
b2.use action_halt
b2.use SyncedFolderCleanup
b2.use Package
b2.use PackageVagrantfile
b2.use PackageMetadataJson
b2.use Export
end
end
end
def self.action_provision def self.action_provision
Vagrant::Action::Builder.new.tap do |b| Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate b.use ConfigValidate
@ -261,9 +283,16 @@ module VagrantPlugins
# The autoload farm # The autoload farm
action_root = Pathname.new(File.expand_path("../action", __FILE__)) action_root = Pathname.new(File.expand_path("../action", __FILE__))
autoload :PackageSetupFolders, action_root.join("package_setup_folders")
autoload :PackageSetupFiles, action_root.join("package_setup_files")
autoload :PackageVagrantfile, action_root.join("package_vagrantfile")
autoload :PackageMetadataJson, action_root.join("package_metadata_json")
autoload :Export, action_root.join("export")
autoload :CheckEnabled, action_root.join("check_enabled") autoload :CheckEnabled, action_root.join("check_enabled")
autoload :DeleteVM, action_root.join("delete_vm") autoload :DeleteVM, action_root.join("delete_vm")
autoload :Import, action_root.join("import") autoload :Import, action_root.join("import")
autoload :Package, action_root.join("package")
autoload :IsWindows, action_root.join("is_windows") autoload :IsWindows, action_root.join("is_windows")
autoload :ReadState, action_root.join("read_state") autoload :ReadState, action_root.join("read_state")
autoload :ResumeVM, action_root.join("resume_vm") autoload :ResumeVM, action_root.join("resume_vm")

View File

@ -0,0 +1,39 @@
require "fileutils"
module VagrantPlugins
module HyperV
module Action
class Export
def initialize(app, env)
@app = app
end
def call(env)
@env = env
@env[:ui].info @env[:machine].state.id.to_s
raise Vagrant::Errors::VMPowerOffToPackage if
@env[:machine].state.id != :off
export
@app.call(env)
end
def export
@env[:ui].info I18n.t("vagrant.actions.vm.export.exporting")
@env[:machine].provider.driver.export(@env["export.temp_dir"]) do |progress|
@env[:ui].clear_line
@env[:ui].report_progress(progress.percent, 100, false)
end
# Clear the line a final time so the next data can appear
# alone on the line.
@env[:ui].clear_line
end
end
end
end
end

View File

@ -0,0 +1,16 @@
require_relative "../../../../lib/vagrant/action/general/package"
module VagrantPlugins
module HyperV
module Action
class Package < Vagrant::Action::General::Package
# Doing this so that we can test that the parent is properly
# called in the unit tests.
alias_method :general_call, :call
def call(env)
general_call(env)
end
end
end
end
end

View File

@ -0,0 +1,34 @@
require "json"
#require 'vagrant/util/template_renderer'
module VagrantPlugins
module HyperV
module Action
class PackageMetadataJson
# For TemplateRenderer
include Vagrant::Util
def initialize(app, env)
@app = app
end
def call(env)
@env = env
create_metadata
@app.call(env)
end
# This method creates a metadata.json file to tell vagrant this is a
# Hyper V box
def create_metadata
File.open(File.join(@env["export.temp_dir"], "metadata.json"), "w") do |f|
f.write(JSON.generate({
provider: "hyperv"
}))
end
end
end
end
end
end

View File

@ -0,0 +1,16 @@
require_relative "../../../../lib/vagrant/action/general/package_setup_files"
module VagrantPlugins
module HyperV
module Action
class PackageSetupFiles < Vagrant::Action::General::PackageSetupFiles
# Doing this so that we can test that the parent is properly
# called in the unit tests.
alias_method :general_call, :call
def call(env)
general_call(env)
end
end
end
end
end

View File

@ -0,0 +1,18 @@
require "fileutils"
require_relative "../../../../lib/vagrant/action/general/package_setup_folders"
module VagrantPlugins
module HyperV
module Action
class PackageSetupFolders < Vagrant::Action::General::PackageSetupFolders
# Doing this so that we can test that the parent is properly
# called in the unit tests.
alias_method :general_call, :call
def call(env)
general_call(env)
end
end
end
end
end

View File

@ -0,0 +1,34 @@
require 'vagrant/util/template_renderer'
module VagrantPlugins
module HyperV
module Action
class PackageVagrantfile
# For TemplateRenderer
include Vagrant::Util
def initialize(app, env)
@app = app
end
def call(env)
@env = env
create_vagrantfile
@app.call(env)
end
# This method creates the auto-generated Vagrantfile at the root of the
# box. This Vagrantfile contains the MAC address so that the user doesn't
# have to worry about it.
def create_vagrantfile
File.open(File.join(@env["export.temp_dir"], "Vagrantfile"), "w") do |f|
mac_address = @env[:machine].provider.driver.read_mac_address
f.write(TemplateRenderer.render("package_Vagrantfile", {
base_mac: mac_address["mac"]
}))
end
end
end
end
end
end

View File

@ -53,10 +53,18 @@ module VagrantPlugins
execute('delete_vm.ps1', { VmId: vm_id }) execute('delete_vm.ps1', { VmId: vm_id })
end end
def export(path)
execute('export_vm.ps1', {VmId: vm_id, Path: path})
end
def read_guest_ip def read_guest_ip
execute('get_network_config.ps1', { VmId: vm_id }) execute('get_network_config.ps1', { VmId: vm_id })
end end
def read_mac_address
execute('get_network_mac.ps1', { VmId: vm_id })
end
def resume def resume
execute('resume_vm.ps1', { VmId: vm_id }) execute('resume_vm.ps1', { VmId: vm_id })
end end

View File

@ -0,0 +1,15 @@
Param(
[Parameter(Mandatory=$true)]
[string]$VmId,
[Parameter(Mandatory=$true)]
[string]$Path
)
$vm = Get-VM -Id $VmId -ErrorAction "Stop"
$vm | Export-VM -Path $Path
# Prepare directory structure for box import
$name = $vm.Name
Move-Item $Path/$name/* $Path
Remove-Item -Path $Path/Snapshots -Force -Recurse
Remove-Item -Path $Path/$name -Force

View File

@ -0,0 +1,28 @@
Param(
[Parameter(Mandatory=$true)]
[string]$VmId
)
# Include the following modules
$Dir = Split-Path $script:MyInvocation.MyCommand.Path
. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
$ip_address = ""
$vm = Get-VM -Id $VmId -ErrorAction "Stop"
$networks = Get-VMNetworkAdapter -VM $vm
foreach ($network in $networks) {
if ($network.MacAddress -gt 0) {
$mac_address = $network.MacAddress
if (-Not ([string]::IsNullOrEmpty($mac_address))) {
# We found our mac address!
break
}
}
}
$resultHash = @{
mac = "$mac_address"
}
$result = ConvertTo-Json $resultHash
Write-Output-Message $result

View File

@ -1,49 +1,14 @@
require_relative "../../../../lib/vagrant/action/general/package_setup_files"
module VagrantPlugins module VagrantPlugins
module ProviderVirtualBox module ProviderVirtualBox
module Action module Action
class PackageSetupFiles class PackageSetupFiles < Vagrant::Action::General::PackageSetupFiles
def initialize(app, env) # Doing this so that we can test that the parent is properly
@app = app # called in the unit tests.
alias_method :general_call, :call
env["package.include"] ||= []
env["package.vagrantfile"] ||= nil
end
def call(env) def call(env)
files = {} general_call(env)
env["package.include"].each do |file|
source = Pathname.new(file)
dest = nil
# If the source is relative then we add the file as-is to the include
# directory. Otherwise, we copy only the file into the root of the
# include directory. Kind of strange, but seems to match what people
# expect based on history.
if source.relative?
dest = source
else
dest = source.basename
end
# Assign the mapping
files[file] = dest
end
if env["package.vagrantfile"]
# Vagrantfiles are treated special and mapped to a specific file
files[env["package.vagrantfile"]] = "_Vagrantfile"
end
# Verify the mapping
files.each do |from, _|
raise Vagrant::Errors::PackageIncludeMissing,
file: from if !File.exist?(from)
end
# Save the mapping
env["package.files"] = files
@app.call(env)
end end
end end
end end

View File

@ -1,36 +1,16 @@
require "fileutils" require "fileutils"
require_relative "../../../../lib/vagrant/action/general/package" require_relative "../../../../lib/vagrant/action/general/package_setup_folders"
module VagrantPlugins module VagrantPlugins
module ProviderVirtualBox module ProviderVirtualBox
module Action module Action
class PackageSetupFolders class PackageSetupFolders < Vagrant::Action::General::PackageSetupFolders
include Vagrant::Util::Presence # Doing this so that we can test that the parent is properly
# called in the unit tests.
def initialize(app, env) alias_method :general_call, :call
@app = app
end
def call(env) def call(env)
env["package.output"] ||= "package.box" general_call(env)
env["package.directory"] ||= Dir.mktmpdir("vagrant-package-", env[:tmp_path])
# Match up a couple environmental variables so that the other parts of
# Vagrant will do the right thing.
env["export.temp_dir"] = env["package.directory"]
Vagrant::Action::General::Package.validate!(
env["package.output"], env["package.directory"])
@app.call(env)
end
def recover(env)
dir = env["package.directory"]
if File.exist?(dir)
FileUtils.rm_rf(dir)
end
end end
end end
end end