gepetto 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +35 -0
  3. data/README.rdoc +212 -0
  4. data/Rakefile +28 -0
  5. data/app_generators/gepetto/USAGE +0 -0
  6. data/app_generators/gepetto/gepetto_generator.rb +35 -0
  7. data/app_generators/gepetto/templates/Rakefile +24 -0
  8. data/app_generators/gepetto/templates/config/fileserver.conf +7 -0
  9. data/app_generators/gepetto/templates/config/puppet.conf +11 -0
  10. data/app_generators/gepetto/templates/manifests/classes/empty.pp +1 -0
  11. data/app_generators/gepetto/templates/manifests/defaults.pp +12 -0
  12. data/app_generators/gepetto/templates/manifests/nodes.pp +5 -0
  13. data/app_generators/gepetto/templates/manifests/site.pp +9 -0
  14. data/app_generators/gepetto/templates/manifests/templates.pp +0 -0
  15. data/app_generators/gepetto/templates/script/module +67 -0
  16. data/app_generators/gepetto/templates/script/puppetca +3 -0
  17. data/app_generators/gepetto/templates/script/puppetmasterd +13 -0
  18. data/app_generators/gepetto/templates/script/puppetrun +2 -0
  19. data/bin/gepetto +18 -0
  20. data/gepetto.gemspec +37 -0
  21. data/lib/gepetto.rb +6 -0
  22. data/lib/gepetto/tasks.rb +1 -0
  23. data/puppet_generators/module/USAGE +18 -0
  24. data/puppet_generators/module/module_generator.rb +24 -0
  25. data/puppet_generators/module/templates/README +0 -0
  26. data/puppet_generators/module/templates/manifests/init.pp +0 -0
  27. data/script/destroy +14 -0
  28. data/script/generate +14 -0
  29. data/tasks/host.pp +75 -0
  30. data/tasks/log.rake +9 -0
  31. data/tasks/puppet.rake +8 -0
  32. data/tasks/sandbox.pp +101 -0
  33. data/tasks/sandbox.rake +437 -0
  34. data/tasks/tmp.rake +24 -0
  35. data/tasks/utils.rake +17 -0
  36. metadata +104 -0
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ /usr/sbin/puppetca --ssldir $PWD/tmp/ssl $*
@@ -0,0 +1,13 @@
1
+ #!/bin/sh
2
+
3
+ # create tmp if needed, other directories are created by puppetmasterd
4
+ [ -d tmp ] || mkdir tmp
5
+
6
+ # change working directories
7
+ OPTIONS="--logdir=$PWD/log --vardir=$PWD/tmp/lib --rundir=$PWD/tmp/run --ssldir=$PWD/tmp/ssl"
8
+ # use local files
9
+ OPTIONS="$OPTIONS --templatedir=$PWD/templates --manifestdir=$PWD/manifests --modulepath=$PWD/modules --confdir=$PWD/config"
10
+
11
+ OPTIONS="$OPTIONS --certname=puppet --logdest=console"
12
+
13
+ /usr/sbin/puppetmasterd --no-daemonize $OPTIONS $*
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ /usr/sbin/puppetrun --ssldir=$PWD/tmp/ssl --confdir=$PWD/config --certname=puppet $*
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'rubigen'
5
+
6
+ if %w(-v --version).include? ARGV.first
7
+ require 'gepetto/version'
8
+ puts "#{File.basename($0)} #{Gepetto::VERSION}"
9
+ exit(0)
10
+ end
11
+
12
+ require 'rubigen/scripts/generate'
13
+ RubiGen::Base.use_application_sources!
14
+
15
+ RubiGen::Base.prepend_sources(
16
+ RubiGen::PathSource.new(:app, File.join(File.dirname(__FILE__), "..", "app_generators"))
17
+ )
18
+ RubiGen::Scripts::Generate.new.run(ARGV, :generator => 'gepetto')
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{gepetto}
5
+ s.version = "0.0.7"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Alban Peignier"]
9
+ s.date = %q{2009-08-20}
10
+ s.default_executable = %q{gepetto}
11
+ s.description = %q{A helper suite for Puppet projects to create, manage and help daily development
12
+
13
+ More information about Puppet: http://reductivelabs.com/trac/puppet/}
14
+ s.email = ["alban.peignier@free.fr"]
15
+ s.executables = ["gepetto"]
16
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
17
+ s.files = ["History.txt", "Manifest.txt", "README.rdoc", "Rakefile", "app_generators/gepetto/USAGE", "app_generators/gepetto/gepetto_generator.rb", "app_generators/gepetto/templates/Rakefile", "app_generators/gepetto/templates/config/fileserver.conf", "app_generators/gepetto/templates/config/puppet.conf", "app_generators/gepetto/templates/manifests/classes/empty.pp", "app_generators/gepetto/templates/manifests/defaults.pp", "app_generators/gepetto/templates/manifests/nodes.pp", "app_generators/gepetto/templates/manifests/site.pp", "app_generators/gepetto/templates/manifests/templates.pp", "app_generators/gepetto/templates/script/module", "app_generators/gepetto/templates/script/puppetca", "app_generators/gepetto/templates/script/puppetmasterd", "app_generators/gepetto/templates/script/puppetrun", "bin/gepetto", "gepetto.gemspec", "lib/gepetto.rb", "lib/gepetto/tasks.rb", "puppet_generators/module/USAGE", "puppet_generators/module/module_generator.rb", "puppet_generators/module/templates/README", "puppet_generators/module/templates/manifests/init.pp", "script/destroy", "script/generate", "tasks/host.pp", "tasks/log.rake", "tasks/puppet.rake", "tasks/sandbox.pp", "tasks/sandbox.rake", "tasks/tmp.rake", "tasks/utils.rake"]
18
+ s.homepage = %q{http://github.com/albanpeignier/gepetto/}
19
+ s.rdoc_options = ["--main", "README.rdoc"]
20
+ s.require_paths = ["lib"]
21
+ s.rubyforge_project = %q{gepetto}
22
+ s.rubygems_version = %q{1.3.4}
23
+ s.summary = %q{A helper suite for Puppet projects to create, manage and help daily development More information about Puppet: http://reductivelabs.com/trac/puppet/}
24
+
25
+ if s.respond_to? :specification_version then
26
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
27
+ s.specification_version = 3
28
+
29
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
30
+ s.add_development_dependency(%q<hoe>, [">= 2.3.3"])
31
+ else
32
+ s.add_dependency(%q<hoe>, [">= 2.3.3"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<hoe>, [">= 2.3.3"])
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Gepetto
5
+ VERSION = '0.0.8'
6
+ end
@@ -0,0 +1 @@
1
+ Dir[File.join(File.dirname(__FILE__), %w[.. .. tasks], '**/*.rake')].each { |rake| load rake }
@@ -0,0 +1,18 @@
1
+ Description:
2
+ Create an empty module organisation as specified by
3
+ http://reductivelabs.com/trac/puppet/wiki/ModuleOrganisation
4
+
5
+ This generates a module directories and files into modules/<modulename>.
6
+
7
+ Example:
8
+ ./script/generate module passenger
9
+
10
+ creates directories :
11
+ modules/passenger/templates
12
+ modules/passenger/files
13
+ modules/passenger/depends
14
+
15
+ creates files :
16
+ modules/passenger/README
17
+ modules/passenger/manifests/init.pp
18
+ modules/passenger/manifests/defaults.pp
@@ -0,0 +1,24 @@
1
+ require 'rbconfig'
2
+
3
+ class ModuleGenerator < RubiGen::Base
4
+
5
+ attr_reader :module_name
6
+
7
+ def initialize(runtime_args, runtime_options = {})
8
+ super
9
+ usage if args.empty?
10
+ @module_name = args.shift
11
+ @destination_root = "modules/#{module_name}"
12
+ end
13
+
14
+ def manifest
15
+ record do |m|
16
+ # Root directory and all subdirectories.
17
+ m.directory ''
18
+ %w{manifests files templates}.each { |path| m.directory path }
19
+ m.template_copy_each %w( README )
20
+ m.template_copy_each %w( init.pp ), 'manifests'
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,75 @@
1
+ Exec { path => "/usr/bin:/usr/sbin/:/bin:/sbin" }
2
+
3
+ # install qemu
4
+
5
+ package { qemu:
6
+ ensure => "latest"
7
+ }
8
+
9
+ # compile kqemu module
10
+
11
+ package { kqemu-source:
12
+ ensure => "latest",
13
+ require => Package[qemu]
14
+ }
15
+
16
+ exec { "modass-kqemu":
17
+ # modass returns 249 with non-inter ...
18
+ command => 'module-assistant --non-inter a-i kqemu || dpkg -l "kqemu-modules-`uname -r`" | grep ^ii',
19
+ unless => 'dpkg -l "kqemu-modules-`uname -r`" | grep ^ii',
20
+ require => Package[kqemu-source]
21
+ }
22
+
23
+ exec { "add kqemu in /etc/modules":
24
+ command => "echo kqemu >> /etc/modules",
25
+ unless => "grep kqemu /etc/modules",
26
+ require => Exec["modass-kqemu"]
27
+ }
28
+
29
+ exec { "modprobe-kqemu":
30
+ command => "modprobe kqemu",
31
+ unless => "lsmod | grep kqemu",
32
+ require => Exec["modass-kqemu"]
33
+ }
34
+
35
+ file { "/dev/kqemu":
36
+ # default permissions on debian, but not on ubuntu
37
+ mode => 666,
38
+ require => Exec["modprobe-kqemu"]
39
+ }
40
+
41
+ # install uml-utilities for tunctl
42
+
43
+ package { uml-utilities: }
44
+
45
+ exec { "add tun in /etc/modules":
46
+ command => "echo tun >> /etc/modules",
47
+ unless => "grep tun /etc/modules"
48
+ }
49
+
50
+ exec { "modprobe tun":
51
+ unless => "lsmod | grep tun"
52
+ }
53
+
54
+ file { "/dev/net/tun":
55
+ mode => 666
56
+ }
57
+
58
+ # provide a basic qemu-ifup
59
+
60
+ file { "/etc/qemu-ifup":
61
+ mode => 755,
62
+ content => '#!/bin/sh -x
63
+
64
+ if [ "$USER" != "root" -o "$1" != "sudo" ]; then
65
+ exec sudo -p "Password for $0:" $0 sudo $1
66
+ fi
67
+
68
+ [ "$1" = "sudo" ] && shift
69
+
70
+ /sbin/ifconfig $1 172.20.0.1
71
+ iptables -t nat -A POSTROUTING -s 172.20.0.1/24 -o eth0 -j MASQUERADE
72
+ sysctl -w net.ipv4.ip_forward=1
73
+ ',
74
+ require => Package[qemu]
75
+ }
@@ -0,0 +1,9 @@
1
+ namespace :log do
2
+ desc "Truncates all *.log files in log/ to zero bytes"
3
+ task :clear do
4
+ FileList["log/*.log"].each do |log_file|
5
+ f = File.open(log_file, "w")
6
+ f.close
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ namespace :puppet do
2
+ desc "Check syntax of puppet manifests"
3
+ task :check_syntax do
4
+ FileList['manifests/**/*.pp'].each do |manifest|
5
+ sh "puppet --color=false --confdir=/tmp --vardir=/tmp --parseonly --ignoreimport #{manifest}"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,101 @@
1
+ # Minimal settings to boot sandbox image with qemu
2
+
3
+ # These variables are defined by rake task
4
+ #$host_ip='172.20.0.1'
5
+ #$sandbox_ip='172.20.0.2'
6
+
7
+ Exec { path => "/usr/bin:/usr/sbin/:/bin:/sbin" }
8
+
9
+ file { "/etc/fstab":
10
+ content => "/dev/hda1 / ext3 errors=remount-ro 0 1
11
+ proc /proc proc defaults 0 0
12
+ "
13
+ }
14
+
15
+ # Console on ttyS0 not tty1
16
+ exec { "inittab-ttyS0-getty":
17
+ command => "sed -i '/getty 38400 tty1/ s/tty1/ttyS0/' /etc/inittab",
18
+ unless => "grep 'getty 38400 ttyS0' /etc/inittab"
19
+ }
20
+
21
+ # and no other gettys
22
+ exec { "inittab-no-tty-gettys":
23
+ command => "sed -i '/getty 38400 tty[23456]/ d' /etc/inittab",
24
+ onlyif => "grep 'getty 38400 tty[23456]' /etc/inittab"
25
+ }
26
+
27
+ file { "/etc/hostname":
28
+ content => "sandbox"
29
+ }
30
+
31
+ # an host object doesn't find a provider
32
+ file { "/etc/hosts":
33
+ content => "127.0.0.1 localhost
34
+ 127.0.1.1 sandbox
35
+ $host_ip puppet
36
+ "
37
+ }
38
+
39
+ # root's password is 'root'
40
+ user { root:
41
+ password => '$1$aybpiIGf$cB7iFDNZvViQtQjEZ5HFQ0'
42
+ }
43
+
44
+ package { [console-common,console-tools,console-data,base-config,man-db,manpages]:
45
+ ensure => absent
46
+ }
47
+
48
+ # if network configuration changes, eth0 is renamed by udev :-/
49
+ file { "/etc/udev/rules.d/70-persistent-net.rules":
50
+ ensure => absent
51
+ }
52
+
53
+ file { "/etc/network/interfaces":
54
+ content => "auto lo
55
+ iface lo inet loopback
56
+
57
+ auto eth0
58
+ iface eth0 inet static
59
+ address $sandbox_ip
60
+ netmask 255.255.255.0
61
+ gateway $host_ip
62
+ dns-nameservers $host_ip
63
+ "
64
+ }
65
+
66
+ # puppet configuration
67
+
68
+ file { "/etc/default/puppet":
69
+ content => 'START=no
70
+ DAEMON_OPTS="-w 5"
71
+ '
72
+ }
73
+
74
+ file { "/etc/puppet/namespaceauth.conf":
75
+ content => "[puppetrunner]
76
+ allow $host_ip
77
+ "
78
+ }
79
+
80
+ file { "/etc/puppet/puppet.conf":
81
+ content => '[main]
82
+ logdir=/var/log/puppet
83
+ vardir=/var/lib/puppet
84
+ ssldir=/var/lib/puppet/ssl
85
+ rundir=/var/run/puppet
86
+ factpath=$vardir/lib/facter
87
+ pluginsync=false
88
+ color=false
89
+
90
+ [puppetd]
91
+ report=true
92
+ # run puppetd .. every day
93
+ runinterval = 86400
94
+ listen=true
95
+ '
96
+ }
97
+
98
+ exec { "syslog-to-ttyS0":
99
+ command => "echo '*.* -/dev/ttyS0' >> /etc/rsyslog.conf",
100
+ unless => 'grep /dev/ttyS0 /etc/rsyslog.conf'
101
+ }
@@ -0,0 +1,437 @@
1
+ require 'rake/tasklib'
2
+ require 'ping'
3
+ require 'tempfile'
4
+
5
+ class Sandbox < Rake::TaskLib
6
+
7
+ def self.default_architecture
8
+ case PLATFORM
9
+ when /x86_64/
10
+ "amd64"
11
+ else
12
+ "i386"
13
+ end
14
+ end
15
+
16
+ @@images_directory = "/var/tmp"
17
+
18
+ def self.images_directory
19
+ @@images_directory
20
+ end
21
+
22
+ def self.images_directory=(directory)
23
+ @@images_directory = directory
24
+ end
25
+
26
+ def puppet_file(name)
27
+ File.dirname(__FILE__) + "/#{name}.pp"
28
+ end
29
+
30
+ attr_reader :name
31
+ attr_accessor :bootstraper, :ip_address, :host_ip_address, :tap_device
32
+ attr_accessor :disk_size, :memory_size, :mount_point
33
+ attr_accessor :architecture
34
+
35
+ def initialize(name = "sandbox")
36
+ @name = name
37
+
38
+ init
39
+ yield self if block_given?
40
+ define
41
+ end
42
+
43
+ def define
44
+ @architecture = Sandbox.default_architecture
45
+ bootstraper = DebianBoostraper.new
46
+
47
+ @ip_address ||= '172.20.0.2'
48
+ @host_ip_address ||= @ip_address.gsub(/\.[0-9]+$/,'.1')
49
+
50
+ @disk_size ||= '512M'
51
+ @memory_size ||= '128M'
52
+
53
+ @mount_point ||= "/mnt/#{name}"
54
+ @tap_device ||= 'tap0'
55
+ end
56
+
57
+ def bootstraper=(bootstraper)
58
+ @bootstraper = bootstraper
59
+ sync_architecture
60
+ end
61
+
62
+ def architecture=(architecture)
63
+ @architecture = architecture
64
+ sync_architecture
65
+ end
66
+
67
+ def sync_architecture
68
+ if self.bootstraper and self.architecture
69
+ self.bootstraper.architecture = self.architecture
70
+ end
71
+ end
72
+
73
+ def kernel_architecture
74
+ case self.architecture
75
+ when 'i386'
76
+ '686'
77
+ else
78
+ self.architecture
79
+ end
80
+ end
81
+
82
+ def init
83
+ namespace @name do
84
+
85
+ desc "Setup local machine to host sandbox"
86
+ task :setup => 'tmp:create' do
87
+ sudo "puppet #{puppet_file(:host)}"
88
+ end
89
+
90
+ # Mix between these ways :
91
+ # - http://www.mail-archive.com/qemu-devel@nongnu.org/msg01208.html
92
+ # - http://www.geocities.com/gtalon51/Articles/Minimal_Linux_system/Minimal_Linux_system.html
93
+ # - qemu-make-debian-root
94
+ namespace :create do
95
+ task :image do
96
+ sh "qemu-img create -f raw #{disk_image} #{disk_size}"
97
+ # create the partition table
98
+ sh "echo '63,' | /sbin/sfdisk --no-reread -uS -H16 -S63 #{disk_image}"
99
+ end
100
+
101
+ task :fs do
102
+ # format the filesystem
103
+ begin
104
+ sudo "losetup -o #{fs_offset} /dev/loop0 #{disk_image}"
105
+
106
+ # because '/sbin/sfdisk -s /dev/loop0' returns a wrong value :
107
+ extract_fs_block_size = "/sbin/sfdisk -l #{disk_image} 2> /dev/null | awk '/img1/ { gsub(\"\\+\", \"\", $5); print $5 }'"
108
+
109
+ sudo "/sbin/mke2fs -jqF /dev/loop0 `#{extract_fs_block_size}`"
110
+ ensure
111
+ sudo "losetup -d /dev/loop0"
112
+ end
113
+ end
114
+
115
+ task :system do
116
+ # install a debian base system
117
+ mount do
118
+ bootstraper.bootstrap mount_point
119
+ end
120
+ end
121
+
122
+ task :kernel do
123
+ mount do
124
+ chroot_sh "echo 'do_initrd = Yes' >> /etc/kernel-img.conf"
125
+
126
+ kernel_package =
127
+ case self.bootstraper.version
128
+ when 'etch', 'lenny'
129
+ "linux-image-2.6-#{kernel_architecture}"
130
+ when 'hardy'
131
+ 'linux-image-2.6.24-16-generic'
132
+ when 'intrepid'
133
+ 'linux-image-generic'
134
+ end
135
+
136
+ chroot_sh "DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes #{kernel_package}"
137
+ end
138
+ end
139
+
140
+ task :grub do
141
+ mount do
142
+ chroot_sh "DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes grub"
143
+
144
+ grub_dir = "#{mount_point}/boot/grub"
145
+ chroot_sh "mkdir /boot/grub" unless File.exists?(grub_dir)
146
+
147
+ stage_files = Dir["#{mount_point}/usr/lib/grub/**/stage?", "#{mount_point}/usr/lib/grub/**/e2fs_stage1_5"]
148
+ sudo "cp #{stage_files.join(' ')} #{grub_dir}"
149
+
150
+ Tempfile.open('menu_lst') do |f|
151
+ f.write(['default 0',
152
+ 'timeout 0',
153
+ 'title Linux',
154
+ 'root (hd0,0)',
155
+ 'kernel /vmlinuz root=/dev/hda1 ro',
156
+ 'initrd /initrd.img'].join("\n"))
157
+ f.close
158
+ sudo "cp #{f.path} #{grub_dir}/menu.lst"
159
+ end
160
+ end
161
+
162
+ Tempfile.open('grub_input') do |f|
163
+ f.write(["device (hd0) #{disk_image}",
164
+ "root (hd0,0)",
165
+ "setup (hd0)",
166
+ "quit"].join("\n"))
167
+ f.close
168
+ sudo "grub --device-map=/dev/null < #{f.path}"
169
+ end
170
+ end
171
+
172
+ task :config do
173
+ Tempfile.open('sandbox_puppet_file') do |sandbox_puppet_file|
174
+ sandbox_puppet_file.puts "$host_ip='#{host_ip_address}'"
175
+ sandbox_puppet_file.puts "$sandbox_ip='#{ip_address}'"
176
+ sandbox_puppet_file.puts IO.read(puppet_file(:sandbox))
177
+
178
+ sandbox_puppet_file.close
179
+
180
+ # finalize configuration with puppet
181
+ mount do
182
+ sudo "cp #{sandbox_puppet_file.path} #{mount_point}/etc/sandbox.pp"
183
+ sudo "chroot #{mount_point} puppet /etc/sandbox.pp"
184
+ end
185
+ end
186
+ end
187
+
188
+ task :tap_device do
189
+ unless tap_device_exists?
190
+ sudo "tunctl -u #{ENV['USER']} -t #{tap_device}"
191
+ end
192
+ end
193
+
194
+ task :snapshot do
195
+ snapshot(:initial)
196
+ end
197
+ end
198
+
199
+ desc "Create a fresh image for sandbox"
200
+ task :create => [ 'clean', 'create:image', 'create:fs', 'create:system', 'create:kernel', 'create:grub', 'create:config', 'create:snapshot' ]
201
+
202
+ desc "Destroy sandbox images"
203
+ task :destroy => 'clean' do
204
+ rm_f disk_image
205
+ rm_f disk_image(:initial)
206
+ end
207
+
208
+ desc "Start sandbox"
209
+ task :start => ['create:tap_device', 'tmp:create'] do
210
+ start
211
+ end
212
+
213
+ desc "Start sandbox from initial image in snapshot"
214
+ task :start_from_initial do
215
+ start :hda => disk_image(:initial), :snapshot => true
216
+ end
217
+
218
+ task :wait do
219
+ wait
220
+ end
221
+
222
+ desc "Stop sandbox"
223
+ task :stop do
224
+ sh "kill -9 `cat tmp/run/#{name}.pid`"
225
+ end
226
+
227
+ task :revert do
228
+ sh "qemu-img convert -O raw #{disk_image(:initial)} #{disk_image}"
229
+ end
230
+
231
+ task :mount do
232
+ mount_image
233
+ end
234
+
235
+ task :umount do
236
+ umount_image
237
+ end
238
+
239
+ task :clean => 'puppet:clean' do
240
+ # clean known_hosts
241
+ known_hosts_file="#{ENV['HOME']}/.ssh/known_hosts"
242
+ sh "sed -i '/#{hostname},#{ip_address}/ d' #{known_hosts_file}" if File.exists?(known_hosts_file)
243
+ end
244
+
245
+ task :status do
246
+ status
247
+ end
248
+
249
+ namespace :puppet do
250
+
251
+ desc "Run puppetd in sandbox"
252
+ task :run do
253
+ sh "./script/puppetrun --host #{hostname}"
254
+ end
255
+
256
+ desc "Sign a request from sandbox"
257
+ task :sign do
258
+ sh "./script/puppetca --sign #{hostname}"
259
+ end
260
+
261
+ task :clean do
262
+ # remove pending request
263
+ sh "rm -f ssl/ca/requests/#{hostname}*.pem"
264
+ # remove signed certificat
265
+ sh "./script/puppetca --clean #{hostname} || true"
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ def start(options = {})
272
+ options = {
273
+ :daemonize => true,
274
+ :snapshot => ENV['SNAPSHOT'],
275
+ :hda => disk_image,
276
+ :nographic => false,
277
+ :m => memory_size,
278
+ :net => ["nic", "tap,ifname=#{tap_device}"]
279
+ }.update(options)
280
+
281
+ if options[:daemonize]
282
+ options = {
283
+ :pidfile => File.expand_path("tmp/run/#{name}.pid"), :serial => "file:" + File.expand_path("log/#{name}.log")
284
+ }.update(options)
285
+ end
286
+
287
+ options_as_string = options.collect do |name,value|
288
+ argument = "-#{name}"
289
+
290
+ case value
291
+ when Array
292
+ value.collect { |v| "#{argument} '#{v}'" }
293
+ when true
294
+ argument
295
+ when false
296
+ when nil
297
+ when ''
298
+ nil
299
+ else
300
+ "#{argument} '#{value}'"
301
+ end
302
+ end.compact.join(' ')
303
+
304
+ qemu_command =
305
+ case PLATFORM
306
+ when /x86_64/
307
+ "qemu-system-x86_64"
308
+ else
309
+ "qemu"
310
+ end
311
+
312
+ sh "#{qemu_command} #{options_as_string}"
313
+ end
314
+
315
+ def snapshot(name)
316
+ sh "qemu-img convert -O qcow2 #{disk_image} #{disk_image(name)}"
317
+ end
318
+
319
+ def wait(timeout = 30, max_try_count = 5)
320
+ try_count = 5
321
+ try_timeout = timeout / try_count
322
+
323
+ 5.times do
324
+ if Ping.pingecho(ip_address, try_timeout)
325
+ return
326
+ else
327
+ sleep try_timeout
328
+ end
329
+ end
330
+
331
+ raise "no response from #{hostname} after #{timeout} seconds"
332
+ end
333
+
334
+ def mount(&block)
335
+ begin
336
+ mount_image
337
+ yield mount_point
338
+ ensure
339
+ umount_image
340
+ end
341
+ end
342
+
343
+ def mount_image
344
+ sudo "mkdir #{mount_point}" unless File.exists? mount_point
345
+ sudo "mount -o loop,offset=#{fs_offset} #{disk_image} #{mount_point}"
346
+
347
+ sudo "mount proc #{mount_point}/proc -t proc" if File.exists? "#{mount_point}/proc"
348
+ end
349
+
350
+ def umount_image
351
+ [ "#{mount_point}/proc", mount_point ].each do |mount|
352
+ sudo "umount #{mount} || true"
353
+ end
354
+ end
355
+
356
+ # TODO to be customizable
357
+
358
+ def disk_image(suffix = nil)
359
+ suffix = "-#{suffix}" if suffix
360
+ File.join Sandbox.images_directory, "#{name}#{suffix}.img"
361
+ end
362
+
363
+ def fs_offset
364
+ 32256
365
+ end
366
+
367
+ def hostname
368
+ if name =~ /^sandbox/
369
+ name
370
+ else
371
+ "sandbox-#{name}"
372
+ end
373
+ end
374
+
375
+ def tap_device_exists?
376
+ IO.readlines('/proc/net/dev').any? { |l| l =~ /\s+#{tap_device}/ }
377
+ end
378
+
379
+ def status
380
+ puts "#{hostname} status:"
381
+ puts self.inspect
382
+ end
383
+
384
+ def chroot_sh(cmd)
385
+ sudo "chroot #{mount_point} sh -c \"#{cmd}\""
386
+ end
387
+
388
+ end
389
+
390
+ class DebianBoostraper
391
+
392
+ attr_accessor :version, :mirror, :include, :exclude, :architecture
393
+
394
+ def initialize(&block)
395
+ default_attributes
396
+
397
+ @include = %w{puppet ssh udev resolvconf}
398
+ @exclude = %w{syslinux at exim mailx libstdc++2.10-glibc2.2 mbr setserial fdutils info ipchains iptables lilo pcmcia-cs ppp pppoe pppoeconf pppconfig telnet exim4 exim4-base exim4-config exim4-daemon-light pciutils modconf tasksel console-common console-tools console-data base-config man-db manpages}
399
+
400
+ yield self if block_given?
401
+ end
402
+
403
+ def default_attributes
404
+ @version = 'lenny'
405
+ @mirror = 'http://ftp.debian.org/debian'
406
+ @architecture = Sandbox.default_architecture
407
+ end
408
+
409
+ def bootstrap(root)
410
+ options_as_string = options.collect{|k,v| "--#{k} #{Array(v).join(',')}"}.join(' ')
411
+ sudo "debootstrap #{options_as_string} #{version} #{root} #{mirror}"
412
+ end
413
+
414
+ def options
415
+ {
416
+ :arch => architecture,
417
+ :exclude => @exclude,
418
+ :include => @include
419
+ }
420
+ end
421
+
422
+ end
423
+
424
+ class UbuntuBoostraper < DebianBoostraper
425
+
426
+ def default_attributes
427
+ super
428
+
429
+ @version = 'intrepid'
430
+ @mirror = 'http://archive.ubuntu.com/ubuntu/'
431
+ end
432
+
433
+ def options
434
+ super.update :components => 'main,universe'
435
+ end
436
+
437
+ end