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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ff61589708789bacee3de7073cd4c2f796a91e06
4
- data.tar.gz: 28071b91f41a77ca52cdcff1fb5a35ca957c8bdb
3
+ metadata.gz: 94d280ad4009853e8492e658aaea2a837ec85942
4
+ data.tar.gz: f7d798e8947f7bd3bd5b6815b935d3e7e71564d2
5
5
  SHA512:
6
- metadata.gz: 443bdec50d5c697e5b4e0a6fce3dda6b3435dabd614c095829939f9c384e7f52622f7188e9180e550c0af8ce13a39483a8764d559243fe707d242050ae05096c
7
- data.tar.gz: a7718fe5d5b834900e515623865e15a1b9f2e0f9bee0ab0804351a10052ba7cfa93ad46554cd608a4a1b0a3ad7a97ec4d0ec505232aa38c6980b3f1517095226
6
+ metadata.gz: 956b22f89d47919396c52c5458cab8af7017da569be4460d6974d90771fb95c7dda58c0b737a1b8e5a4b40000deb599c52a34c5600461f407acc895d03a091b1
7
+ data.tar.gz: 34de6a41b2922bc528593b86e207532f42869dfc43facbad61f1db6f449876305c19d3b7c715c0370aa349ab1624d323ec30bbbe86da912bc75f3cdd251bae82
File without changes
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/**/*.rb']
7
+ end
8
+
9
+ task default: [:test]
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
- class CLI < Thor
23
+ module Pauperism
24
+ class Vm < Thor
25
+ class_option :vmpath, :type => :string
26
+ class_option :vmrunpath, :type => :string # TODO: implement
10
27
 
11
- desc "bootstrap", "Initialize the base image"
12
- def bootstrap
13
- pauper = Pauper.new
14
- pauper.bootstrap
15
- end
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
- desc "create [NODENAME]", "Create a VM"
28
- def create(node_name=nil)
29
- pauper = Pauper.new
30
- pauper.create(node_name)
31
- end
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
- desc "start [NODENAME]", "Start a VM"
34
- def start(node_name=nil)
35
- pauper = Pauper.new
36
- if node_name
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
- desc "stop [NODENAME]", "Stop a VM"
44
- def stop(node_name=nil)
45
- pauper = Pauper.new
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
- desc "resume [NODENAME]", "Resume a VM"
54
- def resume(node_name=nil)
55
- pauper = Pauper.new
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
- desc "suspend [NODENAME]", "Suspends a VM"
65
- def suspend(node_name=nil)
66
- pauper = Pauper.new
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
- desc "setup [NODENAME]", "Refresh configs and run Chef on a VM"
77
- def setup(node_name=nil)
78
- pauper = Pauper.new
79
- if node_name
80
- pauper.setup(node_name)
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
- desc 'write_hosts', 'Write out a new /etc/hosts file'
88
- def write_hosts
89
- pauper = Pauper.new
90
- pauper.write_hosts
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
- desc 'setup_osx [VMNAME]', 'Set up things for OS X'
95
- def setup_osx(vm_path=nil)
96
- pauper = Pauper.new
103
+ desc "create [NODENAME]", "Create a VM"
104
+ def create(node_name=nil)
105
+ pauper.create(node_name)
106
+ end
97
107
 
98
- vm_path ||= "#{ENV['HOME']}/Documents/Virtual Machines*/*.vmwarevm"
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
- vms = Dir[vm_path]
101
- if vms.size == 0
102
- puts "I can't find your VM. Try passing in a path to the <name>.vmwarevm dir."
103
- exit
104
- elsif vms.size > 1
105
- puts "I'm not sure which VM I should use!"
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
- vmx_file = Dir[vms[0] + '/*.vmx'][0]
113
- vmx = VMX.new(vmx_file)
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
- mac = vmx.data['ethernet0.generatedAddress']
116
- arpmac = mac.gsub(/0([0-9a-fA-F])/, '\1') # switch it to the format that arp likes
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
- match = `arp -a | grep -i #{arpmac}`.match(/\(([0-9.]+)\)/)
119
- unless match
120
- puts "Could not find any running VM with MAC addr: #{arpmac}"
121
- exit
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
- ip = match.captures[0]
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
- dhcpd = DHCPD.new('/Library/Preferences/VMware Fusion/vmnet8/dhcpd.conf')
129
- unless dhcpd.config['dev'] &&
130
- dhcpd.config['dev']['hardware ethernet'] == mac &&
131
- dhcpd.config['dev']['fixed-address'] == ip
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
- puts "Writing correct IP/MAC combo into dhcpd.conf file..."
134
- dhcpd.config['dev'] = { 'hardware ethernet' => mac, 'fixed-address' => ip }
135
- dhcpd.save
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
- # Set up route
139
- gateway = `route -n get #{pauper.config.config[:subnet]}.0`.match(/gateway: (.*)$/).captures[0]
140
- unless gateway == ip
141
- puts "Adding route to LXC hosts..."
142
- system 'sudo', 'route', 'add', "#{pauper.config.config[:subnet]}.0/24", ip
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
- # Write hosts
146
- pauper.write_osx_hosts(ip)
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
@@ -41,10 +41,6 @@ class DHCPD
41
41
  system 'sudo', 'mv', tmp_path, @conf_path
42
42
  end
43
43
 
44
- def restart
45
- system 'sudo "/Library/Application Support/VMware Fusion/boot.sh" --restart >>vmware.log 2>&1'
46
- end
47
-
48
44
  private
49
45
 
50
46
  BEGIN_PAUPER = "#### BEGIN PAUPER ####"
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(DEFAULT_PAUPERFILE)
26
- network = LIBVIRT.new(@pauper_config.config[:subnet])
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
- lxc_pauper_template
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
- ip = "#{@pauper_config.config[:subnet]}.2"
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
- cmd "ssh-keygen -R '#{node_name}' >> pauper.log 2>&1"
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
- instance_eval File.read(pauperfile)
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)
@@ -0,0 +1,3 @@
1
+ class Pauper
2
+ VERSION = '0.2.0'
3
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ describe Pauper do
4
+ it 'should be tested ...' do
5
+ skip "... but it isn't!"
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'minitest/autorun'
5
+ require 'pauper'
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.1.17
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: 2013-12-16 00:00:00.000000000 Z
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