netvbox 0.0.1
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/bin/netvbox +64 -0
- data/lib/netvbox/config_manager.rb +59 -0
- data/lib/netvbox/version.rb +3 -0
- data/lib/netvbox/vm.rb +110 -0
- data/lib/netvbox/vm_manager.rb +70 -0
- data/lib/netvbox.rb +5 -0
- metadata +63 -0
data/bin/netvbox
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'netvbox/vm_manager'
|
4
|
+
require 'netvbox/config_manager'
|
5
|
+
require 'netvbox/version'
|
6
|
+
|
7
|
+
NETVBOX_CONFIG = "#{ENV['HOME']}/.netvbox"
|
8
|
+
|
9
|
+
def print_help
|
10
|
+
puts "NetVbox version #{NetVbox::VERSION}"
|
11
|
+
puts "Current config file: #{NETVBOX_CONFIG}"
|
12
|
+
puts 'sub-commands:'
|
13
|
+
puts ' add [ssh host] [username] [password] [vm name] [snapshot name]'
|
14
|
+
puts ' adds a vm to manage'
|
15
|
+
puts ' list'
|
16
|
+
puts ' lists vms under management'
|
17
|
+
puts ' load'
|
18
|
+
puts ' loads vm snapshots'
|
19
|
+
puts ' poweroff'
|
20
|
+
puts ' powers off all vms'
|
21
|
+
puts ' remove [ssh host] [username] [vm name] [snapshot name]'
|
22
|
+
puts ' removes a vm from management'
|
23
|
+
puts ' status'
|
24
|
+
puts ' shows status of vms'
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_args
|
28
|
+
command = ARGV[0]
|
29
|
+
config_manager = NetVbox::ConfigManager.new(NETVBOX_CONFIG)
|
30
|
+
vm_manager = NetVbox::VmManager.new(config_manager)
|
31
|
+
case command
|
32
|
+
when 'add'
|
33
|
+
if ARGV.length >= 6
|
34
|
+
hostname, username, password, vm_name, snapshot_name = ARGV[1..5]
|
35
|
+
vm_manager.add_vm(hostname, username, password, vm_name, snapshot_name)
|
36
|
+
else
|
37
|
+
print_help
|
38
|
+
end
|
39
|
+
when 'list'
|
40
|
+
vm_manager.list_vms
|
41
|
+
when 'load'
|
42
|
+
vm_manager.load_snapshots
|
43
|
+
when 'poweroff'
|
44
|
+
vm_manager.poweroff_all
|
45
|
+
when 'remove'
|
46
|
+
if ARGV.length >= 5
|
47
|
+
hostname, username, vm_name, snapshot_name = ARGV[1..4]
|
48
|
+
vm_manager.remove_vm(hostname, username, vm_name, snapshot_name)
|
49
|
+
else
|
50
|
+
print_help
|
51
|
+
end
|
52
|
+
when 'status'
|
53
|
+
vm_manager.print_status
|
54
|
+
else
|
55
|
+
puts "Unknown command: #{command}"
|
56
|
+
print_help
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if ARGV.empty?
|
61
|
+
print_help
|
62
|
+
else
|
63
|
+
parse_args
|
64
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'netvbox/vm'
|
3
|
+
|
4
|
+
module NetVbox
|
5
|
+
class ConfigManager
|
6
|
+
def initialize(config_file_path)
|
7
|
+
@config_file_path = config_file_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_all_vm_info
|
11
|
+
all_vm_info = []
|
12
|
+
if File.readable?(@config_file_path)
|
13
|
+
CSV.foreach(@config_file_path) do |row|
|
14
|
+
all_vm_info << a_to_vm_info(row)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
all_vm_info
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_vm(vm_info)
|
21
|
+
all_vm_info = get_all_vm_info
|
22
|
+
all_vm_info.index(vm_info).nil? ? write_vm_info(all_vm_info << vm_info) : false
|
23
|
+
end
|
24
|
+
|
25
|
+
def remove_vm(vm_info)
|
26
|
+
all_vm_info = get_all_vm_info
|
27
|
+
updated_vm_info = all_vm_info.select {|i| !(i === vm_info)}
|
28
|
+
all_vm_info != updated_vm_info ? write_vm_info(updated_vm_info) : false
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def vm_info_to_a(vm_info)
|
34
|
+
a = []
|
35
|
+
a << vm_info.ssh_connection_info.hostname
|
36
|
+
a << vm_info.ssh_connection_info.username
|
37
|
+
a << vm_info.ssh_connection_info.password
|
38
|
+
a << vm_info.vm_name
|
39
|
+
a << vm_info.snapshot_name
|
40
|
+
a
|
41
|
+
end
|
42
|
+
|
43
|
+
def a_to_vm_info(a)
|
44
|
+
VmInfo.new(SshConnectionInfo.new(a[0], a[1], a[2]), a[3], a[4])
|
45
|
+
end
|
46
|
+
|
47
|
+
def write_vm_info(all_vm_info)
|
48
|
+
begin
|
49
|
+
CSV.open(@config_file_path, "w") do |csv|
|
50
|
+
all_vm_info.each {|vm_info| csv << vm_info_to_a(vm_info)}
|
51
|
+
end
|
52
|
+
true
|
53
|
+
rescue IOError
|
54
|
+
puts "ERROR: Could not write vm info to #{@config_file_path}"
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/netvbox/vm.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
|
3
|
+
module NetVbox
|
4
|
+
|
5
|
+
class SshConnectionInfo
|
6
|
+
attr_reader :hostname, :username, :password
|
7
|
+
|
8
|
+
def initialize(hostname, username, password)
|
9
|
+
@hostname = hostname
|
10
|
+
@username = username
|
11
|
+
@password = password
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
self === other &&
|
16
|
+
self.password == other.password
|
17
|
+
end
|
18
|
+
|
19
|
+
def ===(other)
|
20
|
+
self.hostname == other.hostname &&
|
21
|
+
self.username == other.username
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class VmInfo
|
26
|
+
attr_reader :ssh_connection_info, :vm_name, :snapshot_name
|
27
|
+
|
28
|
+
def initialize(ssh_connection_info, vm_name, snapshot_name)
|
29
|
+
@ssh_connection_info = ssh_connection_info
|
30
|
+
@vm_name = vm_name
|
31
|
+
@snapshot_name = snapshot_name
|
32
|
+
end
|
33
|
+
|
34
|
+
def ==(other)
|
35
|
+
self.ssh_connection_info == other.ssh_connection_info &&
|
36
|
+
self.vm_name == other.vm_name &&
|
37
|
+
self.snapshot_name == other.snapshot_name
|
38
|
+
end
|
39
|
+
|
40
|
+
def ===(other)
|
41
|
+
self.ssh_connection_info === other.ssh_connection_info &&
|
42
|
+
self.vm_name == other.vm_name &&
|
43
|
+
self.snapshot_name == other.snapshot_name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Vm
|
48
|
+
attr_reader :vm_info
|
49
|
+
|
50
|
+
def initialize(vm_info)
|
51
|
+
@vm_info = vm_info
|
52
|
+
end
|
53
|
+
|
54
|
+
def status
|
55
|
+
vm_state = showvminfo('VMState')
|
56
|
+
case vm_state
|
57
|
+
when 'running'
|
58
|
+
'running'
|
59
|
+
when 'saved'
|
60
|
+
'not running (saved)'
|
61
|
+
when 'poweroff'
|
62
|
+
'not running (power off)'
|
63
|
+
when 'aborted'
|
64
|
+
'not running (aborted)'
|
65
|
+
else
|
66
|
+
vm_state
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def poweroff
|
71
|
+
if showvminfo('VMState') == 'running'
|
72
|
+
my_ssh do |ssh|
|
73
|
+
ssh.exec!("VBoxManage controlvm \"#{@vm_info.vm_name}\" poweroff").strip
|
74
|
+
end
|
75
|
+
else
|
76
|
+
"#{@vm_info.vm_name} is not running"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def load_snapshot
|
81
|
+
return "ERROR: you must disable 3d acceleration for #{@vm_info.vm_name}" if showvminfo('accelerate3d') == 'on'
|
82
|
+
poweroff
|
83
|
+
my_ssh do |ssh|
|
84
|
+
ssh.exec!("VBoxManage snapshot \"#{@vm_info.vm_name}\" restore \"#{@vm_info.snapshot_name}\" && VBoxManage startvm \"#{@vm_info.vm_name}\" --type headless")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def showvminfo(var_name)
|
91
|
+
my_ssh do |ssh|
|
92
|
+
ssh.exec!("VBoxManage showvminfo \"#{@vm_info.vm_name}\" --machinereadable | grep ^#{var_name}= | cut -d '\"' -f 2").strip
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def my_ssh
|
97
|
+
ssh_info = @vm_info.ssh_connection_info
|
98
|
+
begin
|
99
|
+
# can raise SocketError or Net::SSH::AuthenticationFailed
|
100
|
+
Net::SSH.start(ssh_info.hostname, ssh_info.username, :password => ssh_info.password) do |ssh|
|
101
|
+
return yield ssh
|
102
|
+
end
|
103
|
+
rescue SocketError
|
104
|
+
return 'Connection Error'
|
105
|
+
rescue Net::SSH::AuthenticationFailed
|
106
|
+
return 'Authentication Error'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'netvbox/vm'
|
2
|
+
require 'netvbox/config_manager'
|
3
|
+
|
4
|
+
module NetVbox
|
5
|
+
class VmManager
|
6
|
+
def initialize(config_manager)
|
7
|
+
@config_manager = config_manager
|
8
|
+
end
|
9
|
+
|
10
|
+
def print_status
|
11
|
+
puts 'Getting status info...'
|
12
|
+
threads = []
|
13
|
+
get_vms.each do |vm|
|
14
|
+
threads << Thread.new(vm) {|vm| puts "#{vm.vm_info.vm_name} on #{vm.vm_info.ssh_connection_info.hostname}... #{vm.status}"}
|
15
|
+
end
|
16
|
+
threads.each(&:join)
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_snapshots
|
20
|
+
puts 'Loading snapshots...'
|
21
|
+
threads = []
|
22
|
+
get_vms.each do |vm|
|
23
|
+
threads << Thread.new(vm) {|vm| vm.load_snapshot}
|
24
|
+
end
|
25
|
+
threads.each(&:join)
|
26
|
+
print_status
|
27
|
+
end
|
28
|
+
|
29
|
+
def poweroff_all
|
30
|
+
puts 'Powering off vms...'
|
31
|
+
threads = []
|
32
|
+
get_vms.each do |vm|
|
33
|
+
threads << Thread.new(vm) {|vm| vm.poweroff}
|
34
|
+
end
|
35
|
+
threads.each(&:join)
|
36
|
+
print_status
|
37
|
+
end
|
38
|
+
|
39
|
+
def list_vms
|
40
|
+
@config_manager.get_all_vm_info.each do |vm_info|
|
41
|
+
ssh_info = vm_info.ssh_connection_info
|
42
|
+
puts "#{ssh_info.username}@#{ssh_info.hostname} - vm: #{vm_info.vm_name}, snapshot: #{vm_info.snapshot_name}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_vm(hostname, username, password, vm_name, snapshot_name)
|
47
|
+
vm_info = VmInfo.new(SshConnectionInfo.new(hostname, username, password), vm_name, snapshot_name)
|
48
|
+
if @config_manager.add_vm(vm_info)
|
49
|
+
puts "Successfully added vm (#{vm_name}, #{snapshot_name})"
|
50
|
+
else
|
51
|
+
puts "Failed to add vm (#{vm_name}, #{snapshot_name})"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove_vm(hostname, username, vm_name, snapshot_name)
|
56
|
+
vm_info = VmInfo.new(SshConnectionInfo.new(hostname, username, nil), vm_name, snapshot_name)
|
57
|
+
if @config_manager.remove_vm(vm_info)
|
58
|
+
puts "Successfully removed vm (#{vm_name}, #{snapshot_name})"
|
59
|
+
else
|
60
|
+
puts "Failed to remove vm (#{vm_name}, #{snapshot_name})"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def get_vms
|
67
|
+
@config_manager.get_all_vm_info.collect {|vm_info| Vm.new(vm_info)}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/netvbox.rb
ADDED
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: netvbox
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dennis Hsu
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: net-ssh
|
16
|
+
requirement: &12295500 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *12295500
|
25
|
+
description: Remote administration of VirtualBox VMs through ssh
|
26
|
+
email:
|
27
|
+
- hsu.dennis@gmail.com
|
28
|
+
executables:
|
29
|
+
- netvbox
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/netvbox.rb
|
34
|
+
- lib/netvbox/version.rb
|
35
|
+
- lib/netvbox/vm_manager.rb
|
36
|
+
- lib/netvbox/vm.rb
|
37
|
+
- lib/netvbox/config_manager.rb
|
38
|
+
- bin/netvbox
|
39
|
+
homepage:
|
40
|
+
licenses: []
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.8.10
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: Remote administration of VirtualBox VMs through ssh
|
63
|
+
test_files: []
|