linecook 0.6.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|