pauper 0.1.17 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{README → README.md} +0 -0
- data/Rakefile +9 -0
- data/bin/pauper +173 -103
- data/lib/dhcpd.rb +0 -4
- data/lib/fusion.rb +112 -0
- data/lib/libvirt.rb +4 -4
- data/lib/lxc.rb +4 -4
- data/lib/pauper.rb +177 -17
- data/lib/pauper/version.rb +3 -0
- data/test/pauper_test.rb +7 -0
- data/test/test_helper.rb +5 -0
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94d280ad4009853e8492e658aaea2a837ec85942
|
4
|
+
data.tar.gz: f7d798e8947f7bd3bd5b6815b935d3e7e71564d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 956b22f89d47919396c52c5458cab8af7017da569be4460d6974d90771fb95c7dda58c0b737a1b8e5a4b40000deb599c52a34c5600461f407acc895d03a091b1
|
7
|
+
data.tar.gz: 34de6a41b2922bc528593b86e207532f42869dfc43facbad61f1db6f449876305c19d3b7c715c0370aa349ab1624d323ec30bbbe86da912bc75f3cdd251bae82
|
data/{README → README.md}
RENAMED
File without changes
|
data/Rakefile
ADDED
data/bin/pauper
CHANGED
@@ -3,148 +3,218 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'thor'
|
5
5
|
require 'pauper'
|
6
|
+
require 'fusion'
|
7
|
+
|
8
|
+
# Check for new versions on startup
|
9
|
+
SKIP_VERSION_CHECK_FLAG = '--skip-version-check'
|
10
|
+
unless ARGV.include?(SKIP_VERSION_CHECK_FLAG)
|
11
|
+
current_version = Gem.loaded_specs['pauper'].version
|
12
|
+
latest_version = Gem.latest_version_for('pauper')
|
13
|
+
if current_version < latest_version
|
14
|
+
puts "It's your lucky day! There's a new version of Pauper available. You're on #{current_version}. Latest is #{latest_version}."
|
15
|
+
puts "You can skip this check with the #{SKIP_VERSION_CHECK_FLAG} flag, if you really must."
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
end
|
19
|
+
ARGV.delete(SKIP_VERSION_CHECK_FLAG)
|
20
|
+
|
6
21
|
require 'vmx'
|
7
|
-
require 'dhcpd'
|
8
22
|
|
9
|
-
|
23
|
+
module Pauperism
|
24
|
+
class Vm < Thor
|
25
|
+
class_option :vmpath, :type => :string
|
26
|
+
class_option :vmrunpath, :type => :string # TODO: implement
|
10
27
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
desc "destroy [NODENAME]", "Completely destroy a VM"
|
18
|
-
def destroy(node_name=nil)
|
19
|
-
pauper = Pauper.new
|
20
|
-
if node_name
|
21
|
-
pauper.destroy(node_name)
|
22
|
-
else
|
23
|
-
pauper.destroy_all
|
28
|
+
no_commands do
|
29
|
+
def fusion(vmpath=Fusion::DEFAULT_VMPATH)
|
30
|
+
vmpath = File.expand_path(options[:vmpath]) if options[:vmpath]
|
31
|
+
@fusion ||= Fusion.new(vmpath)
|
32
|
+
end
|
24
33
|
end
|
25
|
-
end
|
26
34
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
35
|
+
desc 'start', 'Starts your fusion VM'
|
36
|
+
def start
|
37
|
+
say "Starting your VM, this may take a while..."
|
38
|
+
fusion.start
|
39
|
+
fusion.list
|
40
|
+
end
|
32
41
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
pauper.start(node_name)
|
38
|
-
else
|
39
|
-
pauper.start_all
|
42
|
+
desc 'stop', 'Stop given fusion VM '
|
43
|
+
def stop
|
44
|
+
fusion.stop
|
45
|
+
fusion.list
|
40
46
|
end
|
41
|
-
end
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
if node_name
|
47
|
-
pauper.stop(node_name)
|
48
|
-
else
|
49
|
-
pauper.stop_all
|
48
|
+
desc 'reset', 'Reset given fusion VM'
|
49
|
+
def reset
|
50
|
+
fusion.reset
|
50
51
|
end
|
51
|
-
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
if node_name
|
57
|
-
pauper.resume(node_name)
|
58
|
-
else
|
59
|
-
pauper.resume_all
|
53
|
+
desc 'suspend', 'Suspend given fusion VM'
|
54
|
+
def suspend
|
55
|
+
fusion.suspend
|
60
56
|
end
|
61
|
-
end
|
62
57
|
|
58
|
+
desc 'pause', 'Pause given fusion VM'
|
59
|
+
def pause
|
60
|
+
fusion.pause
|
61
|
+
end
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
if node_name
|
68
|
-
pauper.suspend(node_name)
|
69
|
-
else
|
70
|
-
pauper.suspend_all
|
63
|
+
desc 'unpause', 'Unpause given fusion VM'
|
64
|
+
def unpause
|
65
|
+
fusion.unpause
|
71
66
|
end
|
72
|
-
end
|
73
67
|
|
68
|
+
desc 'list', 'List running fusion VMs'
|
69
|
+
def list
|
70
|
+
fusion.list
|
71
|
+
end
|
74
72
|
|
73
|
+
desc 'vmruner', 'A tube straight to the vmrun command...'
|
74
|
+
def vmruner(cmd)
|
75
|
+
fusion.vmrun('list')
|
76
|
+
end
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
else
|
82
|
-
pauper.setup_all
|
78
|
+
desc 'setup', 'Setup your pauper vm experience'
|
79
|
+
def setup
|
80
|
+
say "Adding some lines to your vmx to try and help you be headless"
|
81
|
+
fusion.setup
|
82
|
+
# TODO: do more...
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
+
class CLI < Thor
|
87
|
+
class_option :pauperfile, :type => :string
|
86
88
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
89
|
+
desc "bootstrap", "Initialize the base image"
|
90
|
+
def bootstrap
|
91
|
+
pauper.bootstrap
|
92
|
+
end
|
92
93
|
|
94
|
+
desc "destroy [NODENAME]", "Completely destroy a VM"
|
95
|
+
def destroy(node_name=nil)
|
96
|
+
if node_name
|
97
|
+
pauper.destroy(node_name)
|
98
|
+
else
|
99
|
+
pauper.destroy_all
|
100
|
+
end
|
101
|
+
end
|
93
102
|
|
94
|
-
|
95
|
-
|
96
|
-
|
103
|
+
desc "create [NODENAME]", "Create a VM"
|
104
|
+
def create(node_name=nil)
|
105
|
+
pauper.create(node_name)
|
106
|
+
end
|
97
107
|
|
98
|
-
|
108
|
+
desc "start [NODENAME]", "Start a VM"
|
109
|
+
def start(node_name=nil)
|
110
|
+
if node_name
|
111
|
+
pauper.start(node_name)
|
112
|
+
else
|
113
|
+
pauper.start_all
|
114
|
+
end
|
115
|
+
end
|
99
116
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
vms.each do |vm|
|
107
|
-
puts "\t#{File.basename(vm)}"
|
117
|
+
desc "stop [NODENAME]", "Stop a VM"
|
118
|
+
def stop(node_name=nil)
|
119
|
+
if node_name
|
120
|
+
pauper.stop(node_name)
|
121
|
+
else
|
122
|
+
pauper.stop_all
|
108
123
|
end
|
109
|
-
exit
|
110
124
|
end
|
111
125
|
|
112
|
-
|
113
|
-
|
126
|
+
desc "resume [NODENAME]", "Resume a VM"
|
127
|
+
def resume(node_name=nil)
|
128
|
+
if node_name
|
129
|
+
pauper.resume(node_name)
|
130
|
+
else
|
131
|
+
pauper.resume_all
|
132
|
+
end
|
133
|
+
end
|
114
134
|
|
115
|
-
|
116
|
-
|
135
|
+
desc "suspend [NODENAME]", "Suspends a VM"
|
136
|
+
def suspend(node_name=nil)
|
137
|
+
if node_name
|
138
|
+
pauper.suspend(node_name)
|
139
|
+
else
|
140
|
+
pauper.suspend_all
|
141
|
+
end
|
142
|
+
end
|
117
143
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
144
|
+
desc "setup [NODENAME]", "Refresh configs and run Chef on a VM"
|
145
|
+
def setup(node_name=nil)
|
146
|
+
if node_name
|
147
|
+
pauper.setup(node_name)
|
148
|
+
else
|
149
|
+
pauper.setup_all
|
150
|
+
end
|
122
151
|
end
|
123
152
|
|
124
|
-
|
153
|
+
desc 'write_hosts', 'Write out a new /etc/hosts file'
|
154
|
+
def write_hosts
|
155
|
+
pauper.write_hosts
|
156
|
+
end
|
125
157
|
|
126
|
-
# Set up DHCPD
|
127
158
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
159
|
+
desc 'setup_osx [VMNAME]', 'Set up things for OS X'
|
160
|
+
def setup_osx(vm_path=Fusion::DEFAULT_VMPATH)
|
161
|
+
unless Pauper.osx?
|
162
|
+
puts 'Not OSX! I refuse!'
|
163
|
+
exit
|
164
|
+
end
|
165
|
+
pauper(true)
|
166
|
+
|
167
|
+
vmx_file = Fusion.new(vm_path).vmx
|
168
|
+
vmx = VMX.new(vmx_file)
|
169
|
+
|
170
|
+
mac = vmx.data['ethernet0.generatedAddress']
|
171
|
+
arpmac = mac.gsub(/0([0-9a-fA-F])/, '\1') # switch it to the format that arp likes
|
172
|
+
|
173
|
+
match = `arp -a | grep -i #{arpmac}`.match(/\(([0-9.]+)\)/)
|
174
|
+
unless match
|
175
|
+
puts "Could not find any running VM with MAC addr: #{arpmac}"
|
176
|
+
puts " >> Manually SSH into the VM then try again <<"
|
177
|
+
exit
|
178
|
+
end
|
132
179
|
|
133
|
-
|
134
|
-
|
135
|
-
|
180
|
+
ip = match.captures[0]
|
181
|
+
|
182
|
+
# Set up DHCPD
|
183
|
+
pauper.setup_osx_dhcpd(mac, ip)
|
184
|
+
|
185
|
+
# Set up route
|
186
|
+
pauper.setup_osx_routes(ip)
|
187
|
+
|
188
|
+
# Write hosts
|
189
|
+
pauper.write_osx_hosts(ip)
|
190
|
+
end
|
191
|
+
|
192
|
+
desc "up", "Start all nodes on your vm from local!"
|
193
|
+
def up
|
194
|
+
# TODO: check if VM is running
|
195
|
+
pauper(true).up
|
136
196
|
end
|
137
197
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
198
|
+
desc "halt", "Stop all nodes on your vm from local!"
|
199
|
+
def halt
|
200
|
+
pauper(true).halt
|
201
|
+
end
|
202
|
+
|
203
|
+
desc "reload", "Basically an up then a halt..."
|
204
|
+
def reload
|
205
|
+
pauper(true).reload
|
206
|
+
end
|
207
|
+
|
208
|
+
no_commands do
|
209
|
+
def pauper(ignore_network=false, pauperfile=Pauper::DEFAULT_PAUPERFILE)
|
210
|
+
pauperfile = File.expand_path(options[:pauperfile]) if options[:pauperfile]
|
211
|
+
@pauper ||= Pauper.new(ignore_network, pauperfile)
|
212
|
+
end
|
143
213
|
end
|
144
214
|
|
145
|
-
|
146
|
-
|
215
|
+
desc 'vm SUBCOMMAND', 'Operations dealing with VMware Fusion'
|
216
|
+
subcommand 'vm', Vm
|
147
217
|
end
|
148
218
|
end
|
149
219
|
|
150
|
-
CLI.start
|
220
|
+
Pauperism::CLI.start
|
data/lib/dhcpd.rb
CHANGED
data/lib/fusion.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'open3'
|
3
|
+
require 'vmx'
|
4
|
+
|
5
|
+
class Fusion
|
6
|
+
DEFAULT_VMRUN = "/Applications/VMware\\ Fusion.app/Contents/Library/vmrun"
|
7
|
+
DEFAULT_VMPATH = "#{ENV['HOME']}/Documents/Virtual\\ Machines*/*.vmwarevm"
|
8
|
+
|
9
|
+
def initialize(vmpath=DEFAULT_VMPATH, nogui='nogui', force='hard')
|
10
|
+
@vmpath = vmpath
|
11
|
+
@vmrun = DEFAULT_VMRUN
|
12
|
+
@nogui = nogui
|
13
|
+
@force = force
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
vmrun('start')
|
18
|
+
end
|
19
|
+
|
20
|
+
def stop
|
21
|
+
vmrun('stop')
|
22
|
+
end
|
23
|
+
|
24
|
+
def reset
|
25
|
+
vmrun('reset')
|
26
|
+
end
|
27
|
+
|
28
|
+
def suspend
|
29
|
+
vmrun('suspend')
|
30
|
+
end
|
31
|
+
|
32
|
+
def pause
|
33
|
+
vmrun('pause')
|
34
|
+
end
|
35
|
+
|
36
|
+
def unpause
|
37
|
+
vmrun('unpause')
|
38
|
+
end
|
39
|
+
|
40
|
+
def list
|
41
|
+
vmrun('list')
|
42
|
+
end
|
43
|
+
|
44
|
+
def vmrun(cmd)
|
45
|
+
case cmd
|
46
|
+
when 'start'
|
47
|
+
cmd = "#{@vmrun} -T fusion #{cmd} #{vmx.shellescape} #{@gui}"
|
48
|
+
when 'stop', 'reset', 'suspend'
|
49
|
+
cmd = "#{@vmrun} -T fusion #{cmd} #{vmx.shellescape} #{@force}"
|
50
|
+
when 'pause', 'unpause'
|
51
|
+
cmd = "#{@vmrun} -T fusion #{cmd} #{vmx.shellescape}"
|
52
|
+
else
|
53
|
+
cmd = "#{@vmrun} #{cmd}"
|
54
|
+
end
|
55
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
56
|
+
while line = stdout.gets
|
57
|
+
puts "> #{line}"
|
58
|
+
end
|
59
|
+
while line = stderr.gets
|
60
|
+
puts "err> #{line}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def setup
|
66
|
+
# this really just adds a few things to the vmx file for better headless
|
67
|
+
# operations.
|
68
|
+
v = VMX.new vmx
|
69
|
+
v.data['msg.autoAnswer'] = "TRUE" # Attempt to answer gui boxes you no longer get
|
70
|
+
puts " msg.autoAnswer=TRUE : auto answer gui boxes for you"
|
71
|
+
v.data['usb.generic.pluginAction'] = "host" # Always plug usb devices into host
|
72
|
+
puts " usb.generic.pluginAction='host' : never ask to plug usb things into vm"
|
73
|
+
v.data['mks.enable3d'] = "FALSE" # Turn of accelerated 3d... we dont need it
|
74
|
+
puts " mks.enable3d=FALSE : there is no need for 3d when headless..."
|
75
|
+
v.save vmx
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_vmrun_to(vmrun)
|
79
|
+
@vmrun = vmrun
|
80
|
+
end
|
81
|
+
|
82
|
+
def link_vmrun(to='/user/local/bin/vmrun')
|
83
|
+
cmd = "ln -s #{@vmrun} #{to}"
|
84
|
+
if File.exists? to
|
85
|
+
puts "Something already exists at #{to}"
|
86
|
+
else
|
87
|
+
system(cmd)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def generate_startup_script
|
92
|
+
# TODO: Generate a startup script that calls pauper vm start/suspend at the
|
93
|
+
# right times.
|
94
|
+
end
|
95
|
+
|
96
|
+
def vmx
|
97
|
+
vms = Dir[@vmpath]
|
98
|
+
if vms.size == 0
|
99
|
+
puts "I can't find your VM. Try passing in a path to the <name>.vmwarevm dir."
|
100
|
+
exit
|
101
|
+
elsif vms.size > 1
|
102
|
+
puts "I'm not sure which VM I should use! Try passing one of these options:"
|
103
|
+
vms.each do |vm|
|
104
|
+
puts "\t--vmpath=#{vm}"
|
105
|
+
end
|
106
|
+
exit
|
107
|
+
end
|
108
|
+
|
109
|
+
# Return .vmx file
|
110
|
+
Dir[vms[0] + '/*.vmx'][0]
|
111
|
+
end
|
112
|
+
end
|
data/lib/libvirt.rb
CHANGED
@@ -43,10 +43,10 @@ EOF
|
|
43
43
|
File.open(".tmp.pauper.xml",'w') do |f|
|
44
44
|
f.puts config_data
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
system("sudo mv .tmp.pauper.xml #{CONF_FILE}")
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
def load_config
|
51
51
|
system("sudo virsh net-define #{CONF_FILE}")
|
52
52
|
end
|
@@ -70,9 +70,9 @@ EOF
|
|
70
70
|
return true
|
71
71
|
end
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
def loaded?
|
75
|
-
virsh = `sudo virsh net-info pauper | grep '^Name.*pauper$'`
|
75
|
+
virsh = `sudo virsh net-info pauper | grep '^Name.*pauper$'`
|
76
76
|
unless virsh == ""
|
77
77
|
return true
|
78
78
|
end
|
data/lib/lxc.rb
CHANGED
@@ -12,11 +12,11 @@ class LXC
|
|
12
12
|
@interface = ''
|
13
13
|
@template = ERB.new <<EOF
|
14
14
|
lxc.utsname = <%= node_name %>
|
15
|
-
lxc.pts = 1024
|
15
|
+
lxc.pts = 1024
|
16
16
|
lxc.tty = 4
|
17
17
|
lxc.cgroup.cpu.shares = 512
|
18
|
-
lxc.rootfs = /var/lib/lxc/<%= node_name %>/rootfs
|
19
|
-
lxc.mount = /var/lib/lxc/<%= node_name %>/fstab
|
18
|
+
lxc.rootfs = /var/lib/lxc/<%= node_name %>/rootfs
|
19
|
+
lxc.mount = /var/lib/lxc/<%= node_name %>/fstab
|
20
20
|
lxc.network.type = veth
|
21
21
|
lxc.network.name = eth0
|
22
22
|
lxc.network.link = <%= interface %>
|
@@ -43,7 +43,7 @@ EOF
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def save(filename=@filename)
|
46
|
-
config_data = @template.result(OpenStruct.new(:node_name => @node_name,
|
46
|
+
config_data = @template.result(OpenStruct.new(:node_name => @node_name,
|
47
47
|
:mac => @mac, :interface => @interface ).send(:binding))
|
48
48
|
File.open(".tmp.lxc.conf",'w') do |f|
|
49
49
|
f.puts config_data
|
data/lib/pauper.rb
CHANGED
@@ -11,21 +11,24 @@ require 'ostruct'
|
|
11
11
|
require 'lxc'
|
12
12
|
require 'libvirt'
|
13
13
|
require 'hosts'
|
14
|
+
require 'dhcpd'
|
15
|
+
|
16
|
+
require 'io/console' # for 'noecho'
|
17
|
+
|
18
|
+
require 'pauper/version'
|
14
19
|
|
15
20
|
class Pauper
|
16
21
|
DEFAULT_PAUPERFILE = './Pauperfile'
|
17
22
|
DEFAULT_VM_PATH = File.expand_path('/var/lib/lxc/')
|
18
23
|
|
19
|
-
def initialize(pauperfile=DEFAULT_PAUPERFILE)
|
24
|
+
def initialize(ignore_network = false, pauperfile = DEFAULT_PAUPERFILE)
|
20
25
|
if root?
|
21
26
|
puts "Don't run as root!"
|
22
27
|
exit
|
23
28
|
end
|
24
29
|
|
25
|
-
@pauper_config = Pauper::Config.new(
|
26
|
-
|
27
|
-
network.enable
|
28
|
-
@pauper_config.config[:bridge] = network.interface
|
30
|
+
@pauper_config = Pauper::Config.new(pauperfile)
|
31
|
+
setup_network unless ignore_network
|
29
32
|
end
|
30
33
|
|
31
34
|
def root?
|
@@ -35,16 +38,19 @@ class Pauper
|
|
35
38
|
def config
|
36
39
|
@pauper_config
|
37
40
|
end
|
38
|
-
|
41
|
+
|
39
42
|
def bootstrap
|
40
43
|
raise "Base already exists!" if vm_exists?("base")
|
41
44
|
|
42
|
-
|
45
|
+
ip = "#{@pauper_config.config[:subnet]}.2"
|
46
|
+
ssh_clean_known_hosts('base', ip)
|
47
|
+
@pauper_config.config[:nodes].each { |n| ssh_clean_known_hosts(n.name, "#{@pauper_config.config[:subnet]}.#{n.config[:last_octet]}") }
|
48
|
+
|
49
|
+
lxc_pauper_template
|
43
50
|
system("sudo touch /var/lib/lxc/lxc.conf")
|
44
51
|
system("sudo lxc-create -n base -t pauper -f /var/lib/lxc/lxc.conf -- -a amd64 --auth-key #{public_ssh_key} -r lucid")
|
45
52
|
mac = generate_mac
|
46
|
-
|
47
|
-
|
53
|
+
|
48
54
|
lxc = LXC.new('base')
|
49
55
|
lxc.interface = @pauper_config.config[:bridge]
|
50
56
|
lxc.mac = mac
|
@@ -82,7 +88,7 @@ class Pauper
|
|
82
88
|
def public_ssh_key
|
83
89
|
ssh_key + ".pub"
|
84
90
|
end
|
85
|
-
|
91
|
+
|
86
92
|
def prepare_fstab(node_name)
|
87
93
|
File.open('.tmp.fstab','w') do |f|
|
88
94
|
@pauper_config.config[:shares].each do |share_name, guest_path, host_path, options|
|
@@ -110,7 +116,7 @@ class Pauper
|
|
110
116
|
system "sudo mv .tmp.interfaces #{DEFAULT_VM_PATH}/#{node_name}/rootfs/etc/network/interfaces"
|
111
117
|
system "sudo mv .tmp.resolv.conf #{DEFAULT_VM_PATH}/#{node_name}/rootfs/etc/resolv.conf"
|
112
118
|
end
|
113
|
-
|
119
|
+
|
114
120
|
def prepare_base_cache
|
115
121
|
@pauper_config.config[:shares].each do |share_name, guest_path, host_path, options|
|
116
122
|
cmd "sudo mkdir -p #{host_path}"
|
@@ -122,7 +128,7 @@ class Pauper
|
|
122
128
|
def reconfigure_ssh(node_name)
|
123
129
|
cmd "sudo chroot /var/lib/lxc/#{node_name}/rootfs/ /usr/sbin/dpkg-reconfigure openssh-server"
|
124
130
|
end
|
125
|
-
|
131
|
+
|
126
132
|
def create(node_name)
|
127
133
|
node_config = get_node_config(node_name)
|
128
134
|
|
@@ -132,7 +138,7 @@ class Pauper
|
|
132
138
|
clone_base(node_name)
|
133
139
|
mac = generate_mac
|
134
140
|
ip = "#{@pauper_config.config[:subnet]}.#{node_config.config[:last_octet]}"
|
135
|
-
|
141
|
+
|
136
142
|
lxc = LXC.new(node_name)
|
137
143
|
lxc.interface = @pauper_config.config[:bridge]
|
138
144
|
lxc.mac = mac
|
@@ -206,7 +212,7 @@ EOF
|
|
206
212
|
else
|
207
213
|
node_config = get_node_config(node_name)
|
208
214
|
ip = node_ip(node_config)
|
209
|
-
end
|
215
|
+
end
|
210
216
|
|
211
217
|
if vm_exists?(node_name)
|
212
218
|
stop_node(node_name)
|
@@ -218,8 +224,7 @@ EOF
|
|
218
224
|
|
219
225
|
puts "Destroying #{node_name}..."
|
220
226
|
cmd "sudo lxc-destroy -n '#{node_name}' >> pauper.log 2>&1"
|
221
|
-
|
222
|
-
cmd "ssh-keygen -R '#{ip}' >> pauper.log 2>&1"
|
227
|
+
ssh_clean_known_hosts(node_name, ip)
|
223
228
|
else
|
224
229
|
puts "#{node_name} hasn't even been created yet, thusly you can't destroy it!"
|
225
230
|
end
|
@@ -284,6 +289,26 @@ EOF
|
|
284
289
|
hosts.save(@pauper_config.config[:dev_domain])
|
285
290
|
end
|
286
291
|
|
292
|
+
def setup_osx_routes(dev_ip)
|
293
|
+
gateway = `route -n get #{@pauper_config.config[:subnet]}.0`.match(/gateway: (.*)$/).captures[0]
|
294
|
+
unless gateway == dev_ip
|
295
|
+
puts "Adding route to LXC hosts..."
|
296
|
+
system 'sudo', 'route', 'add', "#{@pauper_config.config[:subnet]}.0/24", dev_ip
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def setup_osx_dhcpd(mac, dev_ip)
|
301
|
+
dhcpd = DHCPD.new('/Library/Preferences/VMware Fusion/vmnet8/dhcpd.conf')
|
302
|
+
unless dhcpd.config['dev'] &&
|
303
|
+
dhcpd.config['dev']['hardware ethernet'] == mac &&
|
304
|
+
dhcpd.config['dev']['fixed-address'] == dev_ip
|
305
|
+
|
306
|
+
puts "Writing correct IP/MAC combo into dhcpd.conf file..."
|
307
|
+
dhcpd.config['dev'] = { 'hardware ethernet' => mac, 'fixed-address' => dev_ip }
|
308
|
+
dhcpd.save
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
287
312
|
def start_all
|
288
313
|
puts "Starting all nodes..."
|
289
314
|
@pauper_config.config[:nodes].each { |n| start(n.name) }
|
@@ -314,8 +339,133 @@ EOF
|
|
314
339
|
@pauper_config.config[:nodes].each { |n| setup(n.name) }
|
315
340
|
end
|
316
341
|
|
342
|
+
# Should call pauper stop on your vm and then do the things needed for the
|
343
|
+
# hosts to get setup and made visible
|
344
|
+
# Assumes that your local /etc/hosts already knows of your dev ip
|
345
|
+
def up
|
346
|
+
puts "Let me get you setup..."
|
347
|
+
ip = hosts_dev_ip
|
348
|
+
cmds = ["cd #{code_dir}/pauper-env",
|
349
|
+
"pauper start",
|
350
|
+
"pauper write_hosts",
|
351
|
+
"sudo iptables -F"]
|
352
|
+
ssh_with_sudo ip, cmds
|
353
|
+
if Pauper.osx?
|
354
|
+
setup_osx_routes(ip)
|
355
|
+
write_osx_hosts(ip)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
# Should call pauper stop on your vm
|
360
|
+
# Assumes that your local /etc/hosts already knows of your dev ip
|
361
|
+
def halt
|
362
|
+
puts "Let me blow things up..."
|
363
|
+
cmds = ["cd #{code_dir}/pauper-env",
|
364
|
+
"pauper stop"]
|
365
|
+
ssh_with_sudo hosts_dev_ip, cmds
|
366
|
+
end
|
367
|
+
|
368
|
+
# runs the same commands as doing a halt + up but it does it in
|
369
|
+
# one SSH session rather than two... also takes a nap in the middle
|
370
|
+
# Assumes that your local /etc/hosts already knows of your dev ip
|
371
|
+
def reload
|
372
|
+
puts "Let me blow things up..."
|
373
|
+
ip = hosts_dev_ip
|
374
|
+
cmds = ["cd #{code_dir}/pauper-env",
|
375
|
+
"pauper stop",
|
376
|
+
"sleep 2", #napping
|
377
|
+
"echo '*-*-*>KABOOM<*-*-*'",
|
378
|
+
"pauper start",
|
379
|
+
"pauper write_hosts",
|
380
|
+
"sudo iptables -F"]
|
381
|
+
ssh_with_sudo ip, cmds
|
382
|
+
if Pauper.osx?
|
383
|
+
setup_osx_routes(ip)
|
384
|
+
write_osx_hosts(ip)
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def self.osx?
|
389
|
+
RUBY_PLATFORM.include? 'darwin'
|
390
|
+
end
|
391
|
+
|
317
392
|
private
|
318
393
|
|
394
|
+
# This does SSH using some crazy channel things and lots of call backs
|
395
|
+
# requesting a pty all so that we can respond to sudo asking for a password.
|
396
|
+
#
|
397
|
+
# This needs an ssh agent. It should error without one...
|
398
|
+
#
|
399
|
+
# if cmds are passed as a array of strings it will be joined with ';'
|
400
|
+
# this is prefereable to many calls as each new call with require a new
|
401
|
+
# ssh connection.
|
402
|
+
# else cmds is assumed to be a string and used as is.
|
403
|
+
#
|
404
|
+
# this has some logic to ignore the string "sudo: unable to reslove host"
|
405
|
+
# because my vm likes to spout that 20 times per call...
|
406
|
+
def ssh_with_sudo(ip, cmds)
|
407
|
+
cmds = cmds.join(';') if cmds.kind_of?(Array)
|
408
|
+
# arrays that define what to match on when parsing responses
|
409
|
+
ignore_exact = ['', 'sudo', ':'] # exect string matchs
|
410
|
+
ignore_partial = ['unable to resolve host'] # partial string matches
|
411
|
+
pass_phrases = ['[sudo] password', # sudo waiting for password
|
412
|
+
'Enter passphrase for '] # key waiting for password...
|
413
|
+
puts "\e[32m====> SSH >===="
|
414
|
+
lines = 0
|
415
|
+
Net::SSH.start ip, ENV['USER'], {:forward_agent => true} do |ssh|
|
416
|
+
ssh.open_channel do |channel|
|
417
|
+
channel.request_pty do |ch, success| # Needed for password responses
|
418
|
+
abort('Failed to get pty...') unless success
|
419
|
+
end
|
420
|
+
channel.exec cmds do |ch, success|
|
421
|
+
abort('Failed to exec command...') unless success
|
422
|
+
channel.on_data do |ch, data|
|
423
|
+
data.rstrip! # Screw newlines
|
424
|
+
if pass_phrases.any? {|s| data.include? s}
|
425
|
+
print "#{lines}> #{data}"
|
426
|
+
lines += 1
|
427
|
+
channel.send_data STDIN.noecho(&:gets)
|
428
|
+
print "\n"
|
429
|
+
elsif ignore_exact.index data
|
430
|
+
elsif ignore_partial.any? {|s| data.include? s}
|
431
|
+
else
|
432
|
+
puts "#{lines}> #{data}"
|
433
|
+
lines += 1
|
434
|
+
end
|
435
|
+
end
|
436
|
+
channel.on_extended_data do |ch, type, data|
|
437
|
+
# in case of stderr
|
438
|
+
print "\e[31m#{lines}-err> #{data} -- #{type}\e[32m"
|
439
|
+
end
|
440
|
+
end
|
441
|
+
channel.wait # until it closes...
|
442
|
+
end
|
443
|
+
ssh.loop
|
444
|
+
end
|
445
|
+
puts "====< SSH <====\e[0m"
|
446
|
+
end
|
447
|
+
|
448
|
+
def code_dir
|
449
|
+
@pauper_config.config[:shares].each do |a|
|
450
|
+
if a[0] == 'fastly'
|
451
|
+
return a[2]
|
452
|
+
end
|
453
|
+
end
|
454
|
+
return nil
|
455
|
+
end
|
456
|
+
|
457
|
+
def hosts_dev_ip
|
458
|
+
ip = Hosts.new.config.key 'dev'
|
459
|
+
abort 'could not find dev ip in /etc/hosts' unless ip
|
460
|
+
ip
|
461
|
+
end
|
462
|
+
|
463
|
+
def setup_network
|
464
|
+
network = LIBVIRT.new(@pauper_config.config[:subnet])
|
465
|
+
network.enable
|
466
|
+
@pauper_config.config[:bridge] = network.interface
|
467
|
+
end
|
468
|
+
|
319
469
|
def node_ip(node_config)
|
320
470
|
return node_config.config[:ip] if node_config.config[:ip]
|
321
471
|
@subnet = @pauper_config.config[:subnet]
|
@@ -327,6 +477,11 @@ EOF
|
|
327
477
|
system *args
|
328
478
|
end
|
329
479
|
|
480
|
+
def ssh_clean_known_hosts(hostname, ip)
|
481
|
+
cmd "ssh-keygen -R '#{hostname}' >> pauper.log 2>&1"
|
482
|
+
cmd "ssh-keygen -R '#{ip}' >> pauper.log 2>&1"
|
483
|
+
end
|
484
|
+
|
330
485
|
def ssh_exec(ssh, cmd)
|
331
486
|
puts "ssh> #{cmd}"
|
332
487
|
print ssh.exec!(cmd)
|
@@ -1044,7 +1199,12 @@ end
|
|
1044
1199
|
:chef_options => {},
|
1045
1200
|
:dev_domain => nil
|
1046
1201
|
}
|
1047
|
-
|
1202
|
+
if File.exists? pauperfile
|
1203
|
+
pauperpath = File.dirname(pauperfile)
|
1204
|
+
instance_eval File.read(pauperfile)
|
1205
|
+
else
|
1206
|
+
abort "!! Could not find Pauperfile at #{pauperfile} !!"
|
1207
|
+
end
|
1048
1208
|
end
|
1049
1209
|
|
1050
1210
|
def get_node(node_name)
|
data/test/pauper_test.rb
ADDED
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pauper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tyler McMullen
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-02-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -78,12 +78,17 @@ extra_rdoc_files: []
|
|
78
78
|
files:
|
79
79
|
- bin/pauper
|
80
80
|
- lib/dhcpd.rb
|
81
|
+
- lib/fusion.rb
|
81
82
|
- lib/hosts.rb
|
82
83
|
- lib/libvirt.rb
|
83
84
|
- lib/lxc.rb
|
85
|
+
- lib/pauper/version.rb
|
84
86
|
- lib/pauper.rb
|
85
87
|
- lib/vmx.rb
|
86
|
-
- README
|
88
|
+
- README.md
|
89
|
+
- Rakefile
|
90
|
+
- test/pauper_test.rb
|
91
|
+
- test/test_helper.rb
|
87
92
|
homepage: http://github.com/fastly/Pauper
|
88
93
|
licenses: []
|
89
94
|
metadata: {}
|
@@ -107,4 +112,6 @@ rubygems_version: 2.0.3
|
|
107
112
|
signing_key:
|
108
113
|
specification_version: 4
|
109
114
|
summary: A semi-sane way to manage a multi-vm dev environment
|
110
|
-
test_files:
|
115
|
+
test_files:
|
116
|
+
- test/pauper_test.rb
|
117
|
+
- test/test_helper.rb
|