pauper 0.1.17 → 0.2.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.
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