linecook 0.6.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History +139 -0
- data/HowTo/Control Virtual Machines +106 -0
- data/HowTo/Generate Scripts +263 -0
- data/HowTo/Run Scripts +87 -0
- data/HowTo/Setup Virtual Machines +76 -0
- data/License.txt +1 -1
- data/README +78 -59
- data/bin/linecook +12 -5
- data/bin/linecook_run +45 -0
- data/bin/linecook_scp +50 -0
- data/lib/linecook.rb +1 -3
- data/lib/linecook/attributes.rb +49 -12
- data/lib/linecook/commands.rb +9 -4
- data/lib/linecook/commands/build.rb +69 -0
- data/lib/linecook/commands/command.rb +13 -3
- data/lib/linecook/commands/command_error.rb +6 -0
- data/lib/linecook/commands/env.rb +74 -8
- data/lib/linecook/commands/helper.rb +271 -24
- data/lib/linecook/commands/init.rb +10 -6
- data/lib/linecook/commands/package.rb +36 -18
- data/lib/linecook/commands/run.rb +66 -0
- data/lib/linecook/commands/snapshot.rb +114 -0
- data/lib/linecook/commands/ssh.rb +39 -0
- data/lib/linecook/commands/start.rb +34 -0
- data/lib/linecook/commands/state.rb +32 -0
- data/lib/linecook/commands/stop.rb +22 -0
- data/lib/linecook/commands/vbox_command.rb +130 -0
- data/lib/linecook/cookbook.rb +112 -55
- data/lib/linecook/package.rb +293 -109
- data/lib/linecook/proxy.rb +19 -0
- data/lib/linecook/recipe.rb +321 -62
- data/lib/linecook/template.rb +7 -101
- data/lib/linecook/test.rb +196 -141
- data/lib/linecook/test/command_parser.rb +75 -0
- data/lib/linecook/test/file_test.rb +153 -35
- data/lib/linecook/test/shell_test.rb +176 -0
- data/lib/linecook/utils.rb +25 -7
- data/lib/linecook/version.rb +4 -4
- data/templates/Rakefile +44 -47
- data/templates/_gitignore +1 -1
- data/templates/attributes/project_name.rb +4 -4
- data/templates/config/ssh +15 -0
- data/templates/files/help.txt +1 -0
- data/templates/helpers/project_name/assert_content_equal.erb +15 -0
- data/templates/helpers/project_name/create_dir.erb +9 -0
- data/templates/helpers/project_name/create_file.erb +8 -0
- data/templates/helpers/project_name/install_file.erb +8 -0
- data/templates/packages/abox.yml +4 -0
- data/templates/recipes/abox.rb +22 -0
- data/templates/recipes/abox_test.rb +14 -0
- data/templates/templates/todo.txt.erb +3 -0
- data/templates/test/project_name_test.rb +19 -0
- data/templates/test/test_helper.rb +14 -0
- metadata +43 -41
- data/cookbook +0 -0
- data/lib/linecook/commands/helpers.rb +0 -28
- data/lib/linecook/commands/vbox.rb +0 -85
- data/lib/linecook/helper.rb +0 -117
- data/lib/linecook/shell.rb +0 -11
- data/lib/linecook/shell/posix.rb +0 -145
- data/lib/linecook/shell/test.rb +0 -254
- data/lib/linecook/shell/unix.rb +0 -117
- data/lib/linecook/shell/utils.rb +0 -138
- data/templates/README +0 -90
- data/templates/files/file.txt +0 -1
- data/templates/helpers/project_name/echo.erb +0 -5
- data/templates/recipes/project_name.rb +0 -20
- data/templates/scripts/project_name.yml +0 -7
- data/templates/templates/template.txt.erb +0 -3
- data/templates/vbox/setup/virtual_box +0 -86
- data/templates/vbox/ssh/id_rsa +0 -27
- data/templates/vbox/ssh/id_rsa.pub +0 -1
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'linecook/commands/command'
|
2
|
+
require 'linecook/utils'
|
2
3
|
require 'fileutils'
|
3
4
|
require 'erb'
|
4
5
|
require 'ostruct'
|
@@ -6,7 +7,7 @@ require 'ostruct'
|
|
6
7
|
module Linecook
|
7
8
|
module Commands
|
8
9
|
|
9
|
-
# ::desc
|
10
|
+
# :startdoc::desc create a linecook scaffold
|
10
11
|
#
|
11
12
|
# Initializes a linecook scaffold in the specified directory. This
|
12
13
|
# initializer is currently very basic; it is not a true generator.
|
@@ -42,7 +43,10 @@ module Linecook
|
|
42
43
|
|
43
44
|
def template(project_dir, project_name=nil)
|
44
45
|
project_name ||= File.basename(project_dir)
|
45
|
-
context = OpenStruct.new(
|
46
|
+
context = OpenStruct.new(
|
47
|
+
:project_name => project_name,
|
48
|
+
:const_name => Utils.camelize(project_name)
|
49
|
+
).send(:binding)
|
46
50
|
|
47
51
|
#
|
48
52
|
# Copy template files into place
|
@@ -71,10 +75,10 @@ module Linecook
|
|
71
75
|
end
|
72
76
|
end
|
73
77
|
|
74
|
-
# Link up
|
75
|
-
|
76
|
-
|
77
|
-
FileUtils.ln_s
|
78
|
+
# Link up project dir into test
|
79
|
+
target = File.join(project_dir, 'test', "#{project_name}_test", "test_#{project_name}")
|
80
|
+
FileUtils.mkdir_p File.dirname(target)
|
81
|
+
FileUtils.ln_s project_dir, target
|
78
82
|
end
|
79
83
|
end
|
80
84
|
end
|
@@ -1,38 +1,56 @@
|
|
1
1
|
require 'linecook/commands/command'
|
2
2
|
require 'linecook/cookbook'
|
3
|
-
require 'linecook/
|
3
|
+
require 'linecook/package'
|
4
|
+
require 'fileutils'
|
4
5
|
require 'yaml'
|
5
6
|
|
6
7
|
module Linecook
|
7
8
|
module Commands
|
8
9
|
|
9
|
-
# ::desc generates a package
|
10
|
+
# :startdoc::desc generates a package
|
10
11
|
#
|
11
|
-
# Generates
|
12
|
+
# Generates the package specified at
|
13
|
+
# 'project_dir/packages/package_name.yml'. The package file should be a
|
14
|
+
# YAML files that specifies a package env. The full path to package file
|
15
|
+
# can be given instead of package name using the --file option.
|
12
16
|
#
|
17
|
+
# If a cookbook file is present in the project_dir then it will be used to
|
18
|
+
# resolve resources available to the package. See the env command to
|
19
|
+
# interrogate a package env.
|
13
20
|
class Package < Command
|
14
|
-
config :
|
15
|
-
config :force, false, :short => :f, &c.flag
|
21
|
+
config :project_dir, '.', :short => :d # the project directory
|
22
|
+
config :force, false, :short => :f, &c.flag # force creation
|
23
|
+
config :quiet, false, &c.flag # silence output
|
16
24
|
|
17
|
-
def process(
|
18
|
-
|
25
|
+
def process(package_file, package_dir=nil)
|
26
|
+
package_dir ||= default_package_dir(package_file)
|
27
|
+
package_dir = File.expand_path(package_dir)
|
28
|
+
package = Linecook::Package.init(package_file, project_dir)
|
19
29
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
30
|
+
dependencies = package_dependencies(package) + [package_file]
|
31
|
+
if force || !FileUtils.uptodate?(package_dir, dependencies)
|
32
|
+
package.build
|
33
|
+
package.export(package_dir)
|
34
|
+
$stdout.puts package_dir unless quiet
|
26
35
|
end
|
27
36
|
|
28
|
-
|
37
|
+
package_dir
|
38
|
+
end
|
39
|
+
|
40
|
+
def package_dependencies(package)
|
41
|
+
dependencies = []
|
42
|
+
package.manifest.values.collect do |resources|
|
43
|
+
dependencies.concat resources.values
|
44
|
+
end
|
29
45
|
|
30
|
-
|
31
|
-
|
46
|
+
$LOAD_PATH.each do |path|
|
47
|
+
dependencies.concat Dir.glob("#{path}/**/*.rb")
|
48
|
+
end
|
49
|
+
dependencies
|
32
50
|
end
|
33
51
|
|
34
|
-
def
|
35
|
-
|
52
|
+
def default_package_dir(package_file)
|
53
|
+
package_file.chomp(File.extname(package_file))
|
36
54
|
end
|
37
55
|
end
|
38
56
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'linecook/commands/command'
|
2
|
+
require 'linecook/commands/build'
|
3
|
+
|
4
|
+
module Linecook
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
# :startdoc::desc run packages
|
8
|
+
class Run < Command
|
9
|
+
RUN_SCRIPT = File.expand_path('../../../../bin/linecook_run', __FILE__)
|
10
|
+
SCP_SCRIPT = File.expand_path('../../../../bin/linecook_scp', __FILE__)
|
11
|
+
|
12
|
+
config :project_dir, '.', :short => :d # the project directory
|
13
|
+
config :remote_dir, 'linecook', :short => :D # the remote package dir
|
14
|
+
config :ssh_config_file, 'config/ssh', :short => :F # the ssh config file
|
15
|
+
config :quiet, false, :short => :q, &c.flag # silence output
|
16
|
+
config :transfer, true, &c.switch # transfer package (or not)
|
17
|
+
config :remote_scripts, ['run'],
|
18
|
+
:short => :S,
|
19
|
+
:long => :remote_script, &c.list # the remote script(s)
|
20
|
+
|
21
|
+
def glob_package_dirs(package_names)
|
22
|
+
if package_names.empty?
|
23
|
+
pattern = File.expand_path('packages/*', project_dir)
|
24
|
+
Dir.glob(pattern).select {|path| File.directory?(path) }
|
25
|
+
else
|
26
|
+
package_names.collect do |package_name|
|
27
|
+
File.expand_path("packages/#{package_name}", project_dir)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def process(*package_names)
|
33
|
+
package_dirs = glob_package_dirs(package_names)
|
34
|
+
|
35
|
+
unless remote_dir[0] == ?/
|
36
|
+
self.remote_dir = "$(pwd)/#{remote_dir}"
|
37
|
+
self.remote_dir.chomp!('/')
|
38
|
+
end
|
39
|
+
|
40
|
+
opts = {
|
41
|
+
'D' => remote_dir,
|
42
|
+
'F' => ssh_config_file
|
43
|
+
}
|
44
|
+
|
45
|
+
if transfer
|
46
|
+
sh! "sh #{SCP_SCRIPT} #{format(opts)} #{package_dirs.join(' ')}"
|
47
|
+
end
|
48
|
+
|
49
|
+
remote_scripts.each do |remote_script|
|
50
|
+
script_opts = {'S' => remote_script}.merge(opts)
|
51
|
+
sh! "sh #{RUN_SCRIPT} #{format(script_opts)} #{package_dirs.join(' ')}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def format(opts)
|
56
|
+
options = []
|
57
|
+
|
58
|
+
opts.each_pair do |key, value|
|
59
|
+
options << "-#{key} '#{value}'"
|
60
|
+
end
|
61
|
+
|
62
|
+
options.sort.join(' ')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'linecook/commands/vbox_command'
|
2
|
+
|
3
|
+
module Linecook
|
4
|
+
module Commands
|
5
|
+
|
6
|
+
# :startdoc::desc take a vm snapshop
|
7
|
+
#
|
8
|
+
# Takes the specified snapshot of one or more VirtualBox virtual machines.
|
9
|
+
# By default all virtual machines configured in config/ssh will have a
|
10
|
+
# snapshot taken. If the snapshot name is already taken, the previous
|
11
|
+
# snapshot will be renamed.
|
12
|
+
#
|
13
|
+
# Snapshot can also reset a hierarchy of renamed snapshots using the
|
14
|
+
# --reset flag. For example, if there exists a snapshot 'CURRENT' then
|
15
|
+
# these command will leave you with snapshots CURRENT_0 (the original),
|
16
|
+
# CURRENT_1, and CURRENT (the latest):
|
17
|
+
#
|
18
|
+
# linecook snapshot CURRENT
|
19
|
+
# linecook snapshot CURRENT
|
20
|
+
#
|
21
|
+
# To reset:
|
22
|
+
#
|
23
|
+
# liencook snapshot --reset CURRENT
|
24
|
+
#
|
25
|
+
# After which there will only be a single 'CURRENT' snapshot, which
|
26
|
+
# corresponds to the original snapshot.
|
27
|
+
#
|
28
|
+
class Snapshot < VboxCommand
|
29
|
+
config :reset, false, :long => :reset, &c.flag # reset a snapshot
|
30
|
+
|
31
|
+
def process(snapshot, *hosts)
|
32
|
+
vm_names = resolve_vm_names(hosts)
|
33
|
+
each_vm_name(vm_names) do |vm_name|
|
34
|
+
if reset
|
35
|
+
reset_snapshot(vm_name, snapshot)
|
36
|
+
else
|
37
|
+
snapshot(vm_name, snapshot)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_snapshots(vm_name)
|
43
|
+
info = `VBoxManage -q showvminfo #{vm_name}`
|
44
|
+
snapshots = {}
|
45
|
+
|
46
|
+
stack = [{}]
|
47
|
+
parent = nil
|
48
|
+
|
49
|
+
info.each_line do |line|
|
50
|
+
next unless line =~ /^(\s+)Name\: (.*?) \(/
|
51
|
+
depth = $1.length / 3
|
52
|
+
name = $2
|
53
|
+
|
54
|
+
if depth > stack.length
|
55
|
+
stack.push stack.last[parent]
|
56
|
+
elsif depth < stack.length
|
57
|
+
stack.pop
|
58
|
+
end
|
59
|
+
|
60
|
+
snapshot = {}
|
61
|
+
snapshots[name] = snapshot
|
62
|
+
stack.last[name] = snapshot
|
63
|
+
parent = name
|
64
|
+
end
|
65
|
+
|
66
|
+
snapshots
|
67
|
+
end
|
68
|
+
|
69
|
+
def reset_snapshot(vm_name, snapshot)
|
70
|
+
stop(vm_name) if running?(vm_name)
|
71
|
+
|
72
|
+
snapshot = snapshot.upcase
|
73
|
+
restore(vm_name, snapshot)
|
74
|
+
|
75
|
+
snapshots = parse_snapshots(vm_name)
|
76
|
+
parent = snapshots.keys.select {|key| key =~ /\A#{snapshot}(?:_\d+)\z/ }.first
|
77
|
+
parent ||= snapshot
|
78
|
+
|
79
|
+
children = snapshots[parent]
|
80
|
+
children.each do |key, value|
|
81
|
+
inside_out_each(key, value) do |child|
|
82
|
+
sh! "VBoxManage -q snapshot #{vm_name} delete #{child}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
unless parent == snapshot
|
87
|
+
sh! "VBoxManage -q snapshot #{vm_name} edit #{parent} --name #{snapshot}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def snapshot(vm_name, snapshot)
|
92
|
+
snapshot = snapshot.upcase
|
93
|
+
snapshots = parse_snapshots(vm_name)
|
94
|
+
|
95
|
+
count = snapshots.keys.grep(/\A#{snapshot}(?:_|\z)/).length
|
96
|
+
if count > 0
|
97
|
+
sh! "VBoxManage -q snapshot #{vm_name} edit #{snapshot} --name #{snapshot}_#{count - 1}"
|
98
|
+
end
|
99
|
+
|
100
|
+
sh! "VBoxManage -q snapshot #{vm_name} take #{snapshot}"
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def inside_out_each(key, value, &block) # :nodoc:
|
106
|
+
value.each_pair do |k, v|
|
107
|
+
inside_out_each(k, v, &block)
|
108
|
+
end
|
109
|
+
|
110
|
+
yield(key)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'linecook/commands/vbox_command'
|
2
|
+
|
3
|
+
module Linecook
|
4
|
+
module Commands
|
5
|
+
|
6
|
+
# :startdoc::desc ssh to a vm
|
7
|
+
#
|
8
|
+
# Connects to a virtual machine using ssh, as configured in config/ssh.
|
9
|
+
#
|
10
|
+
class Ssh < VboxCommand
|
11
|
+
undef_config :names
|
12
|
+
|
13
|
+
def default_host
|
14
|
+
load_hosts(ssh_config_file).collect {|host, vm_name| host }.first
|
15
|
+
end
|
16
|
+
|
17
|
+
def process(host=default_host)
|
18
|
+
if host.to_s.strip.empty?
|
19
|
+
raise CommandError.new("no host specified")
|
20
|
+
end
|
21
|
+
|
22
|
+
ssh = "ssh -F '#{ssh_config_file}' '#{host}' --"
|
23
|
+
|
24
|
+
# Patterned after vagrant/ssh.rb (circa 0.6.6)
|
25
|
+
# Some hackery going on here. On Mac OS X Leopard (10.5), exec fails
|
26
|
+
# (GH-51). As a workaround, we fork and wait. On all other platforms, we
|
27
|
+
# simply exec.
|
28
|
+
|
29
|
+
platform = RUBY_PLATFORM.to_s.downcase
|
30
|
+
pid = nil
|
31
|
+
pid = fork if platform.include?("darwin9") || platform.include?("darwin8")
|
32
|
+
Kernel.exec(ssh) if pid.nil?
|
33
|
+
Process.wait(pid) if pid
|
34
|
+
|
35
|
+
exit $?.exitstatus
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'linecook/commands/vbox_command'
|
2
|
+
|
3
|
+
module Linecook
|
4
|
+
module Commands
|
5
|
+
|
6
|
+
# :startdoc::desc start a vm
|
7
|
+
#
|
8
|
+
# Starts one or more VirtualBox virtual machines, and resets to a snapshot
|
9
|
+
# if provided. By default all virtual machines configured in config/ssh
|
10
|
+
# will be reset and started in this way.
|
11
|
+
class Start < VboxCommand
|
12
|
+
config :type, 'headless' # vm type (headless|gui)
|
13
|
+
config :snapshot, '', :short => :s # start snapshot
|
14
|
+
config :socket, false, &c.flag
|
15
|
+
|
16
|
+
def process(*hosts)
|
17
|
+
vm_names = resolve_vm_names(hosts)
|
18
|
+
each_vm_name(vm_names) do |vm_name|
|
19
|
+
if running?(vm_name)
|
20
|
+
stop(vm_name)
|
21
|
+
sleep 0.5
|
22
|
+
end
|
23
|
+
|
24
|
+
unless snapshot.empty?
|
25
|
+
restore(vm_name, snapshot)
|
26
|
+
end
|
27
|
+
|
28
|
+
start(vm_name, type)
|
29
|
+
start_ssh_socket(vm_name) if socket
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'linecook/commands/vbox_command'
|
2
|
+
|
3
|
+
module Linecook
|
4
|
+
module Commands
|
5
|
+
|
6
|
+
# :startdoc::desc print the vm state
|
7
|
+
#
|
8
|
+
# Prints the state of one or more VirtualBox virtual machines. By default
|
9
|
+
# all virtual machines configured in config/ssh will print their state.
|
10
|
+
#
|
11
|
+
class State < VboxCommand
|
12
|
+
config :hosts, false, :short => :n, &c.flag # print state by host
|
13
|
+
|
14
|
+
def state(vm_name)
|
15
|
+
running?(vm_name) ? "running" : "stopped"
|
16
|
+
end
|
17
|
+
|
18
|
+
def process(*hosts)
|
19
|
+
vm_names = resolve_vm_names(hosts)
|
20
|
+
if hosts
|
21
|
+
each_host(vm_names) do |host|
|
22
|
+
puts "#{host}: #{state(host_map[host])}"
|
23
|
+
end
|
24
|
+
else
|
25
|
+
each_vm_name(vm_names) do |vm_name|
|
26
|
+
puts "#{vm_name}: #{state(vm_name)}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'linecook/commands/vbox_command'
|
2
|
+
|
3
|
+
module Linecook
|
4
|
+
module Commands
|
5
|
+
|
6
|
+
# :startdoc::desc stop a vm
|
7
|
+
#
|
8
|
+
# Stops one or more VirtualBox virtual machines using 'poweroff'. By
|
9
|
+
# default all virtual machines configured in config/ssh will be stopped.
|
10
|
+
#
|
11
|
+
class Stop < VboxCommand
|
12
|
+
def process(*hosts)
|
13
|
+
vm_names = resolve_vm_names(hosts)
|
14
|
+
each_vm_name(vm_names) do |vm_name|
|
15
|
+
if running?(vm_name)
|
16
|
+
stop(vm_name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'linecook/commands/command'
|
2
|
+
|
3
|
+
module Linecook
|
4
|
+
module Commands
|
5
|
+
class VboxCommand < Command
|
6
|
+
registry.delete_if {|key, value| value == self }
|
7
|
+
|
8
|
+
config :ssh_config_file, 'config/ssh', :writer => false # the ssh config file
|
9
|
+
config :quiet, false, &c.flag # silence output
|
10
|
+
config :names, false, &c.flag # use vm names
|
11
|
+
|
12
|
+
# Matches a host declaration in a ssh config file. After the match:
|
13
|
+
#
|
14
|
+
# $1:: The host name
|
15
|
+
# (ex: 'Host name' => 'name')
|
16
|
+
# $2:: The vm name (if present)
|
17
|
+
# (ex: 'Host name # [vm_name]' => 'vm_name')
|
18
|
+
#
|
19
|
+
HOST_REGEXP = /^\s*Host\s+([^\s#]+)(?:\s*#\s*\[(.*?)\])?/
|
20
|
+
|
21
|
+
# Returns a hash of (host, vm_name) pairs as declared in a ssh config
|
22
|
+
# file. Basically this means parsing out the name in each config like:
|
23
|
+
#
|
24
|
+
# Host name
|
25
|
+
#
|
26
|
+
# Normally the vm_name is the same as the host name, but an alternate can
|
27
|
+
# be specified as a comment in the form:
|
28
|
+
#
|
29
|
+
# Host name # [vm_name]
|
30
|
+
#
|
31
|
+
def load_hosts(ssh_config_file)
|
32
|
+
hosts = []
|
33
|
+
|
34
|
+
File.open(ssh_config_file) do |io|
|
35
|
+
io.each_line do |line|
|
36
|
+
next unless line =~ HOST_REGEXP
|
37
|
+
next if $2 && $2.strip.empty?
|
38
|
+
hosts << [$1, $2 || $1]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
hosts
|
43
|
+
end
|
44
|
+
|
45
|
+
def ssh_config_file=(ssh_config_file)
|
46
|
+
@ssh_config_file = ssh_config_file
|
47
|
+
@host_list = nil
|
48
|
+
@host_map = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def host_list
|
52
|
+
@host_list ||= load_hosts(ssh_config_file)
|
53
|
+
end
|
54
|
+
|
55
|
+
def host_map
|
56
|
+
@host_map ||= Hash[*host_list.flatten]
|
57
|
+
end
|
58
|
+
|
59
|
+
def resolve_vm_names(hosts)
|
60
|
+
names ? hosts : hosts.collect {|host| host_map[host] }
|
61
|
+
end
|
62
|
+
|
63
|
+
def each_host(hosts=[])
|
64
|
+
if hosts.empty?
|
65
|
+
hosts = host_list.collect {|host, vm_name| host }
|
66
|
+
hosts.delete('*')
|
67
|
+
end
|
68
|
+
|
69
|
+
hosts.uniq.each do |host|
|
70
|
+
yield(host)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def each_vm_name(vm_names=[])
|
75
|
+
if vm_names.empty?
|
76
|
+
vm_names = host_list.collect {|host, vm_name| vm_name }
|
77
|
+
vm_names.delete('*')
|
78
|
+
end
|
79
|
+
|
80
|
+
vm_names.uniq.each do |vm_name|
|
81
|
+
yield(vm_name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def ssh(host, cmd)
|
86
|
+
sh "ssh -q -F '#{ssh_config_file}' '#{host}' -- #{cmd}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def ssh!(host, cmd)
|
90
|
+
unless ssh(host, cmd)
|
91
|
+
raise CommandError, "non-zero exit status: #{$?.exitstatus}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def running?(vm_name)
|
96
|
+
`VBoxManage -q list runningvms`.include?(vm_name)
|
97
|
+
end
|
98
|
+
|
99
|
+
def start(vm_name, type='headless')
|
100
|
+
sh! "VBoxManage -q startvm #{vm_name} --type #{type}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def stop(vm_name)
|
104
|
+
sh! "VBoxManage -q controlvm #{vm_name} poweroff"
|
105
|
+
end
|
106
|
+
|
107
|
+
def restore(vm_name, snapshot)
|
108
|
+
sh! "VBoxManage -q snapshot #{vm_name} restore #{snapshot.upcase}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def discardstate(vm_name)
|
112
|
+
sh! "VBoxManage discardstate #{vm_name}"
|
113
|
+
end
|
114
|
+
|
115
|
+
def share(vm_name, name, local_dir, remote_dir)
|
116
|
+
share_dir = "#{local_dir}/#{vm_name}"
|
117
|
+
FileUtils.mkdir_p(share_dir) unless File.exists?(share_dir)
|
118
|
+
|
119
|
+
ssh vm_name, "sudo umount '#{remote_dir}' > /dev/null 2>&1"
|
120
|
+
sh "VBoxManage sharedfolder remove '#{vm_name}' --name '#{name}' --transient > /dev/null 2>&1"
|
121
|
+
sh! "VBoxManage sharedfolder add '#{vm_name}' --name '#{name}' --hostpath '#{share_dir}' --transient"
|
122
|
+
ssh! vm_name, "sudo mount -t vboxsf -o uid=1000,gid=100 '#{name}' '#{remote_dir}'"
|
123
|
+
end
|
124
|
+
|
125
|
+
def start_ssh_socket(vm_name)
|
126
|
+
sh "ssh -MNf -F '#{ssh_config_file}' '#{vm_name}' >/dev/null 2>&1 </dev/null"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|