Skip to content

Commit

Permalink
Add veewee parallels export command.
Browse files Browse the repository at this point in the history
  • Loading branch information
wizonesolutions authored and ngiger committed Aug 3, 2014
1 parent f377d5d commit 87e67d4
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 2 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Veewee isn't only for Vagrant. It currently supports exporting VM images for th
* [VirtualBox](http://www.virtualbox.org/) - exports to `OVF` filetype
* [VMware (Fusion)](http://www.vmware.com/products/fusion/) - exports to `OVA` filetype
* [KVM](http://www.linux-kvm.org/) - exports to `IMG` filetype
* [Parallels](http://www.parallels.com/)
* [Parallels](http://www.parallels.com/) - none yet, but can export to `parallels` format (provided by [vagrant-parallels](https://github.com/yshahin/vagrant-parallels))


## Getting Started
Expand All @@ -42,7 +42,7 @@ Then depending on how you want to use Veewee, we suggest to read through one of
* [Guide for VirtualBox](doc/vbox.md)
* [Guide for VMware Fusion](doc/fusion.md)
* [Guide for KVM](doc/kvm.md)
* [Guide for Parallels](doc/parallels.md)
* [Guide for Parallels Desktop](doc/parallels.md)

Major noteworthy changes between versions can be found here:

Expand Down
9 changes: 9 additions & 0 deletions lib/veewee/command/parallels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ def build(box_name)
env.get_box(box_name).build(options)
end

desc "export [BOX_NAME]", "Exports the basebox to the vagrant-parallels format"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
method_option :force,:type => :boolean , :default => false, :aliases => "-f", :desc => "overwrite existing file"
# TODO: Is there a way to reduce Parallels image size?
method_option :vagrantfile,:type => :string , :default => "", :desc => "specify Vagrantfile"
def export(box_name)
env.get_box(box_name).export_vagrant(options)
end

desc "validate [BOX_NAME]", "Validates a box against parallels compliancy rules"
method_option :tags,:type => :array, :default => %w{parallels puppet chef}, :aliases => "-t", :desc => "tags to validate"
def validate(box_name)
Expand Down
1 change: 1 addition & 0 deletions lib/veewee/provider/parallels/box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require 'veewee/provider/parallels/box/helper/buildinfo'

require 'veewee/provider/parallels/box/build'
require 'veewee/provider/parallels/box/export'
require 'veewee/provider/parallels/box/up'
require 'veewee/provider/parallels/box/create'
require 'veewee/provider/parallels/box/poweroff'
Expand Down
176 changes: 176 additions & 0 deletions lib/veewee/provider/parallels/box/export.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
require 'pathname'
require 'erb'
module Veewee
module Provider
module Parallels
module BoxCommand

class ErbBinding < OpenStruct
def get_binding
return binding()
end
end

def export_vagrant(options)
# For now, we just assume prlctl is in the path. If not...it'll fail.
@prlcmd = "prlctl"

# Check if box already exists
unless self.exists?
ui.info "#{name} is not found, maybe you need to build it first?"
exit
end

if File.exists?("#{name}.box")
if options["force"]
env.logger.debug("#{name}.box exists, but --force was provided")
env.logger.debug("removing #{name}.box first")
FileUtils.rm("#{name}.box")
env.logger.debug("#{name}.box removed")
else
raise Veewee::Error, "export file #{name}.box already exists. Use --force option to overwrite."
end
end


# We need to shutdown first
if self.running?
ui.info "Vagrant requires the box to be shutdown, before it can export"
ui.info "Sudo also needs to work for user #{definition.ssh_user}"
ui.info "Performing a clean shutdown now."

self.halt

#Wait for state poweroff
while (self.running?) do
ui.info ".",{:new_line => false}
sleep 1
end
ui.info ""
ui.info "Machine #{name} is powered off cleanly"
end

#Vagrant requires a relative path for output of boxes

#4.0.x. not using boxes as a subdir
boxdir=Pathname.new(Dir.pwd)

full_path=File.join(boxdir,name+".box")
path1=Pathname.new(full_path)
path2=Pathname.new(Dir.pwd)
box_path=File.expand_path(path1.relative_path_from(path2).to_s)

if File.exists?("#{box_path}")
raise Veewee::Error, "box #{name}.box already exists"
end

# Create temp directory
current_dir = FileUtils.pwd
ui.info "Creating a temporary directory for export"
tmp_dir = Dir.mktmpdir
env.logger.debug("Create temporary directory for export #{tmp_dir}")

begin

ui.info "Adding additional files"

# Handling the Vagrantfile
if options["vagrantfile"].to_s == ""

# Fetching mac address

data = {
:macaddress => get_mac_address
}

# Prepare the vagrant erb
vars = ErbBinding.new(data)
template_path = File.join(File.dirname(__FILE__),'..','..','..','templates',"Vagrantfile.erb")
template = File.open(template_path).readlines.join
erb = ERB.new(template)
vars_binding = vars.send(:get_binding)
result = erb.result(vars_binding)
ui.info("Creating Vagrantfile")
vagrant_path = File.join(tmp_dir,'Vagrantfile')
env.logger.debug("Path: #{vagrant_path}")
env.logger.debug(result)
File.open(vagrant_path,'w') {|f| f.write(result) }
else
f = options["vagrantfile"]
env.logger.debug("Including vagrantfile: #{f}")
FileUtils.cp(f,File.join(tmp_dir,"Vagrantfile"))
end

#Inject a metadata.json file
ui.info("Adding metadata.json file for Parallels Desktop provider")
File.open(File.join(tmp_dir, 'metadata.json'), 'w') {|f| f.write(template_metadatafile()) }

ui.info "Exporting the box"
# TODO: Copy directory from path to the tmp dir
vm_path = get_vm_path
tmp_dest = File.join(tmp_dir, "box.pvm")
FileUtils.cp_r(vm_path, tmp_dest)
env.logger.debug("Copy #{vm_path} to #{tmp_dest}")

ui.info "Packaging the box"
FileUtils.cd(tmp_dir)
command_box_path = box_path
# Gzip, for extra smallness
command = "tar -cvzf '#{command_box_path}' ."
env.logger.debug(command)
shell_exec (command)

rescue Errno::ENOENT => ex
raise Veewee::Error, "#{ex}"
rescue Error => ex
raise Veewee::Error, "Packaging of the box failed:\n+#{ex}"
ensure
# Remove temporary directory
ui.info "Cleaning up temporary directory"
env.logger.debug("Removing temporary dir #{tmp_dir}")
FileUtils.rm_rf(tmp_dir)

FileUtils.cd(current_dir)
end
ui.info ""

ui.info "To import it into vagrant type:"
ui.info "vagrant box add '#{name}' '#{box_path}'"
ui.info ""
ui.info "To use it:"
ui.info "vagrant init '#{name}'"
ui.info "vagrant up --provider=parallels"
ui.info "vagrant ssh"
end

def get_mac_address
command = "#{@prlcmd} list --info --json \"#{self.name}\""
shell_results = shell_exec("#{command}")
# Parsing trick borrowed from vagrant-parallels, lib/vagrant-parallels/driver/
json = JSON.parse(shell_results.stdout.gsub("/\s+/", "").gsub(/^(INFO)?\[/, '').gsub(/\]$/, ''))
# TODO: Not sure if all Parallels guests use net0, but it seems good enough for now.
mac = json.fetch("Hardware").fetch("net0").fetch("mac")
env.logger.debug("mac address: #{mac}")
return mac
end

def get_vm_path
# TODO: Remove this duplication. Maybe make a function higher up somewhere.
command = "#{@prlcmd} list --info --json \"#{self.name}\""
shell_results = shell_exec("#{command}")
# Parsing trick borrowed from vagrant-parallels, lib/vagrant-parallels/driver/
json = JSON.parse(shell_results.stdout.gsub("/\s+/", "").gsub(/^(INFO)?\[/, '').gsub(/\]$/, ''))
# TODO: Do we need to catch errors here? I mean if they got this far it should exist...
vm_path = File.realpath(json.fetch("Home"))
env.logger.debug("path to VM: #{vm_path}")
return vm_path
end

def template_metadatafile
%Q({"provider": "parallels"}\n)
end

end #Module
end #Module
end #Module
end #Module

0 comments on commit 87e67d4

Please sign in to comment.