testlab 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,10 +6,20 @@
6
6
 
7
7
  # TestLab
8
8
 
9
- A framework for building lightweight virtual laboratories using LXC
9
+ A framework for building lightweight virtual infrastructure using LXC
10
+
11
+ # REQUIREMENTS
12
+
13
+ * Latest VirtualBox Package
14
+ * Latest Vagrant Package (non-gem version)
15
+ * Ubuntu 13.04 Server 64-bit (Raring) Base Box - https://github.com/zpatten/raring64
10
16
 
11
17
  # RESOURCES
12
18
 
19
+ Documentation:
20
+
21
+ * http://zpatten.github.io/testlab/
22
+
13
23
  Source:
14
24
 
15
25
  * https://github.com/zpatten/testlab
@@ -18,10 +28,6 @@ Issues:
18
28
 
19
29
  * https://github.com/zpatten/testlab/issues
20
30
 
21
- Documentation:
22
-
23
- * http://zpatten.github.com/testlab/
24
-
25
31
  # LICENSE
26
32
 
27
33
  TestLab - A framework for building lightweight virtual laboratories using LXC
@@ -0,0 +1,47 @@
1
+ class TestLab
2
+ class Container
3
+
4
+ module Actions
5
+
6
+ # Create the container
7
+ def create
8
+ @ui.logger.debug { "Container Create: #{self.id} " }
9
+
10
+ self.distro ||= "ubuntu"
11
+ self.release ||= "precise"
12
+ self.arch ||= detect_arch
13
+
14
+ self.lxc.config.clear
15
+ self.lxc.config['lxc.utsname'] = self.id
16
+ self.lxc.config['lxc.arch'] = self.arch
17
+ self.lxc.config.networks = build_lxc_network_conf(self.interfaces)
18
+ self.lxc.config.save
19
+
20
+ self.lxc.create(*create_args)
21
+ end
22
+
23
+ # Destroy the container
24
+ def destroy
25
+ @ui.logger.debug { "Container Destroy: #{self.id} " }
26
+
27
+ self.lxc.destroy
28
+ end
29
+
30
+ # Start the container
31
+ def up
32
+ @ui.logger.debug { "Container Up: #{self.id} " }
33
+
34
+ self.lxc.start
35
+ end
36
+
37
+ # Stop the container
38
+ def down
39
+ @ui.logger.debug { "Container Down: #{self.id} " }
40
+
41
+ self.lxc.stop
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,42 @@
1
+ class TestLab
2
+ class Container
3
+
4
+ module Generators
5
+
6
+ def generate_ip
7
+ octets = [ 192..192,
8
+ 168..168,
9
+ 0..254,
10
+ 1..254 ]
11
+ ip = Array.new
12
+ for x in 1..4 do
13
+ ip << octets[x-1].to_a[rand(octets[x-1].count)].to_s
14
+ end
15
+ ip.join(".")
16
+ end
17
+
18
+ def generate_mac
19
+ digits = [ %w(0),
20
+ %w(0),
21
+ %w(0),
22
+ %w(0),
23
+ %w(5),
24
+ %w(e),
25
+ %w(0 1 2 3 4 5 6 7 8 9 a b c d e f),
26
+ %w(0 1 2 3 4 5 6 7 8 9 a b c d e f),
27
+ %w(5 6 7 8 9 a b c d e f),
28
+ %w(3 4 5 6 7 8 9 a b c d e f),
29
+ %w(0 1 2 3 4 5 6 7 8 9 a b c d e f),
30
+ %w(0 1 2 3 4 5 6 7 8 9 a b c d e f) ]
31
+ mac = ""
32
+ for x in 1..12 do
33
+ mac += digits[x-1][rand(digits[x-1].count)]
34
+ mac += ":" if (x.modulo(2) == 0) && (x != 12)
35
+ end
36
+ mac
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ class TestLab
2
+ class Container
3
+
4
+ module Lifecycle
5
+
6
+ # Container Setup
7
+ def setup
8
+ @ui.logger.debug { "Container Setup: #{self.id} " }
9
+
10
+ self.create
11
+ self.up
12
+ end
13
+
14
+ # Container Teardown
15
+ def teardown
16
+ @ui.logger.debug { "Container Teardown: #{self.id} " }
17
+
18
+ self.down
19
+ self.destroy
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ class TestLab
2
+ class Container
3
+
4
+ module LXC
5
+
6
+ # Our LXC Container class
7
+ #
8
+ # @return [LXC] An instance of LXC::Container configured for this
9
+ # container.
10
+ def lxc
11
+ @lxc ||= self.node.lxc.container(self.id)
12
+ end
13
+
14
+ # Returns arguments for lxc-create based on our distro
15
+ #
16
+ # @return [Array] An array of arguments for lxc-create
17
+ def create_args
18
+ case self.distro.downcase
19
+ when "ubuntu" then
20
+ %W(-f /etc/lxc/#{self.id} -t #{self.distro} -- --release #{self.release} --arch #{self.arch})
21
+ when "fedora" then
22
+ %W(-f /etc/lxc/#{self.id} -t #{self.distro} -- --release #{self.release})
23
+ end
24
+ end
25
+
26
+ # Attempt to detect the architecture of the node. The value returned is
27
+ # respective to the container distro.
28
+ #
29
+ # @return [String] The arch of the node in the context of the container
30
+ # distro
31
+ def detect_arch
32
+ case self.distro.downcase
33
+ when "ubuntu" then
34
+ ((self.node.arch =~ /x86_64/) ? "amd64" : "i386")
35
+ when "fedora" then
36
+ ((self.node.arch =~ /x86_64/) ? "amd64" : "i686")
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ class TestLab
2
+ class Container
3
+
4
+ module Network
5
+
6
+ # Builds an array of hashes containing the lxc configuration options for
7
+ # our networks
8
+ def build_lxc_network_conf(interfaces)
9
+ networks = Array.new
10
+
11
+ interfaces.each do |network, network_config|
12
+ networks << Hash[
13
+ 'lxc.network.type' => :veth,
14
+ 'lxc.network.flags' => :up,
15
+ 'lxc.network.link' => TestLab::Network.first(network).bridge,
16
+ 'lxc.network.name' => network_config[:name],
17
+ 'lxc.network.hwaddr' => network_config[:mac],
18
+ 'lxc.network.ipv4' => network_config[:ip]
19
+ ]
20
+ if (network_config[:primary] == true) || (interfaces.count == 1)
21
+ networks.last.merge!('lxc.network.ipv4.gateway' => :auto)
22
+ end
23
+ end
24
+
25
+ networks
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ class TestLab
2
+ class Container
3
+
4
+ module Status
5
+
6
+ def status
7
+ interfaces = self.interfaces.collect{ |network, network_config| "#{network}:#{network_config[:name]}:#{network_config[:ip]}" }.join(', ')
8
+
9
+ {
10
+ :id => self.id,
11
+ :state => self.state,
12
+ :distro => self.distro,
13
+ :release => self.release,
14
+ :interfaces => interfaces,
15
+ :provisioner => self.provisioner,
16
+ :node_id => self.node.id
17
+ }
18
+ end
19
+
20
+ # State of the container
21
+ def state
22
+ self.lxc.state
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -14,6 +14,11 @@ class TestLab
14
14
  attribute :provisioner
15
15
  attribute :config
16
16
 
17
+ attribute :domain
18
+
19
+ attribute :user
20
+ attribute :keys
21
+
17
22
  attribute :interfaces
18
23
 
19
24
  attribute :distro
@@ -22,125 +27,85 @@ class TestLab
22
27
 
23
28
  attribute :persist
24
29
 
25
- def initialize(*args)
26
- super(*args)
27
-
28
- @ui = TestLab.ui
29
- @provisioner = self.provisioner.new(self.config) if !self.provisioner.nil?
30
- end
31
-
32
- def status
33
- interfaces = self.interfaces.collect{ |key, value| "#{key}:#{value[:name]}:#{value[:ip]}" }.join(', ')
34
-
35
- {
36
- :id => self.id,
37
- :state => self.state,
38
- :distro => self.distro,
39
- :release => self.release,
40
- :interfaces => interfaces,
41
- :provisioner => self.provisioner,
42
- :node_id => self.node.id
43
- }
44
- end
45
-
46
- # Our LXC Container class
47
- def lxc
48
- @lxc ||= self.node.lxc.container(self.id)
49
- end
50
-
51
- # Create the container
52
- def create
53
- @ui.logger.debug { "Container Create: #{self.id} " }
54
-
55
- self.arch ||= detect_arch
56
30
 
57
- self.lxc.config.clear
31
+ autoload :Actions, 'testlab/container/actions'
32
+ autoload :Generators, 'testlab/container/generators'
33
+ autoload :Lifecycle, 'testlab/container/lifecycle'
34
+ autoload :LXC, 'testlab/container/lxc'
35
+ autoload :Network, 'testlab/container/network'
36
+ autoload :Status, 'testlab/container/status'
58
37
 
59
- self.lxc.config['lxc.utsname'] = self.id
38
+ include TestLab::Container::Actions
39
+ include TestLab::Container::Generators
40
+ include TestLab::Container::Lifecycle
41
+ include TestLab::Container::LXC
42
+ include TestLab::Container::Network
43
+ include TestLab::Container::Status
60
44
 
61
- self.interfaces.each do |network, network_config|
62
- n = Hash.new
63
- n['lxc.network.type'] = :veth
64
- n['lxc.network.flags'] = :up
65
- n['lxc.network.link'] = TestLab::Network.first(network).bridge
66
- n['lxc.network.name'] = (network_config[:name] || "eth0")
67
- n['lxc.network.hwaddr'] = (network_config[:mac] || generate_mac)
68
- n['lxc.network.ipv4'] = (network_config[:ip] || generate_ip)
69
- self.lxc.config.networks << n
70
- end
71
45
 
72
- self.lxc.config.save
46
+ def initialize(*args)
47
+ super(*args)
73
48
 
74
- self.lxc.create(*create_args)
49
+ @ui = TestLab.ui
50
+ @provisioner = self.provisioner.new(self.config) if !self.provisioner.nil?
75
51
  end
76
52
 
77
- # Destroy the container
78
- def destroy
79
- @ui.logger.debug { "Container Destroy: #{self.id} " }
80
-
81
- self.lxc.destroy
82
- end
53
+ ################################################################################
83
54
 
84
- # Start the container
85
- def up
86
- @ui.logger.debug { "Container Up: #{self.id} " }
55
+ # Does the container exist?
56
+ def exists?
57
+ @ui.logger.debug { "Container Exists?: #{self.id} " }
87
58
 
88
- self.lxc.start
59
+ self.lxc.exists?
89
60
  end
90
61
 
91
- # Stop the container
92
- def down
93
- @ui.logger.debug { "Container Down: #{self.id} " }
62
+ ################################################################################
94
63
 
95
- self.lxc.stop
64
+ # SSH to the container
65
+ def ssh(options={})
66
+ self.node.container_ssh(self, options)
96
67
  end
97
68
 
98
- # Reload the container
99
- def reload
100
- @ui.logger.debug { "Container Reload: #{self.id} " }
101
-
102
- self.down
103
- self.up
69
+ def ip
70
+ self.primary_interface.last[:ip].split('/').first
104
71
  end
105
72
 
106
- # Does the container exist?
107
- def exists?
108
- @ui.logger.debug { "Container Exists?: #{self.id} " }
109
-
110
- self.lxc.exists?
73
+ # Returns the CIDR of the container
74
+ def cidr
75
+ self.primary_interface.last[:ip].split('/').last.to_i
111
76
  end
112
77
 
113
- # State of the container
114
- def state
115
- self.lxc.state
116
- end
78
+ def ptr
79
+ octets = self.ip.split('.')
117
80
 
118
- ################################################################################
81
+ result = case self.cidr
82
+ when 0..7 then
83
+ octets[-4,4]
84
+ when 8..15 then
85
+ octets[-3,3]
86
+ when 16..23 then
87
+ octets[-2,2]
88
+ when 24..31 then
89
+ octets[-1,1]
90
+ end
119
91
 
120
- # Container Callback: after_create
121
- def after_create
122
- @ui.logger.debug { "Container Callback: After Create: #{self.id} " }
92
+ result.reverse.join('.')
123
93
  end
124
94
 
125
- # Container Callback: after_up
126
- def after_up
127
- @ui.logger.debug { "Container Callback: After Up: #{self.id} " }
128
-
129
- self.create
130
- self.up
95
+ def primary_interface
96
+ if self.interfaces.any?{ |i,c| c[:primary] == true }
97
+ self.interfaces.find{ |i,c| c[:primary] == true }
98
+ else
99
+ self.interfaces.first
100
+ end
131
101
  end
132
102
 
133
- # Container Callback: before_down
134
- def before_down
135
- @ui.logger.debug { "Container Callback: Before Down: #{self.id} " }
103
+ class << self
136
104
 
137
- self.down
138
- self.destroy
139
- end
105
+ def domains
106
+ self.all.map(&:domain).compact
107
+ end
140
108
 
141
- # Container Callback: before_destroy
142
- def before_destroy
143
- @ui.logger.debug { "Container Callback: Before Destroy: #{self.id} " }
144
109
  end
145
110
 
146
111
  ################################################################################
@@ -156,63 +121,6 @@ class TestLab
156
121
  end
157
122
  end
158
123
 
159
- ################################################################################
160
- private
161
- ################################################################################
162
-
163
- # Returns arguments for lxc-create based on our distro
164
- def create_args
165
- case self.distro.downcase
166
- when "ubuntu" then
167
- %W(-f /etc/lxc/#{self.id} -t #{self.distro} -- --release #{self.release} --arch #{arch})
168
- when "fedora" then
169
- %W(-f /etc/lxc/#{self.id} -t #{self.distro} -- --release #{self.release})
170
- end
171
- end
172
-
173
- # Attempt to detect the architecture of the node our container is running on
174
- def detect_arch
175
- case self.distro.downcase
176
- when "ubuntu" then
177
- ((self.node.arch =~ /x86_64/) ? "amd64" : "i386")
178
- when "fedora" then
179
- ((self.node.arch =~ /x86_64/) ? "amd64" : "i686")
180
- end
181
- end
182
-
183
- def generate_ip
184
- octets = [ 192..192,
185
- 168..168,
186
- 0..254,
187
- 1..254 ]
188
- ip = Array.new
189
- for x in 1..4 do
190
- ip << octets[x-1].to_a[rand(octets[x-1].count)].to_s
191
- end
192
- ip.join(".")
193
- end
194
-
195
- def generate_mac
196
- digits = [ %w(0),
197
- %w(0),
198
- %w(0),
199
- %w(0),
200
- %w(5),
201
- %w(e),
202
- %w(0 1 2 3 4 5 6 7 8 9 a b c d e f),
203
- %w(0 1 2 3 4 5 6 7 8 9 a b c d e f),
204
- %w(5 6 7 8 9 a b c d e f),
205
- %w(3 4 5 6 7 8 9 a b c d e f),
206
- %w(0 1 2 3 4 5 6 7 8 9 a b c d e f),
207
- %w(0 1 2 3 4 5 6 7 8 9 a b c d e f) ]
208
- mac = ""
209
- for x in 1..12 do
210
- mac += digits[x-1][rand(digits[x-1].count)]
211
- mac += ":" if (x.modulo(2) == 0) && (x != 12)
212
- end
213
- mac
214
- end
215
-
216
124
  end
217
125
 
218
126
  end
@@ -0,0 +1,37 @@
1
+ class TestLab
2
+ class Network
3
+
4
+ module Actions
5
+
6
+ # Create the network
7
+ def create
8
+ @ui.logger.debug { "Network Create: #{self.id} " }
9
+
10
+ self.node.ssh.exec(%(sudo brctl addbr #{self.bridge}), :silence => true, :ignore_exit_status => true)
11
+ end
12
+
13
+ # Destroy the network
14
+ def destroy
15
+ @ui.logger.debug { "Network Destroy: #{self.id} " }
16
+
17
+ self.node.ssh.exec(%(sudo brctl delbr #{self.bridge}), :silence => true, :ignore_exit_status => true)
18
+ end
19
+
20
+ # Start the network
21
+ def up
22
+ @ui.logger.debug { "Network Up: #{self.id} " }
23
+
24
+ self.node.ssh.exec(%(sudo ifconfig #{self.bridge} #{self.ip} up), :silence => true, :ignore_exit_status => true)
25
+ end
26
+
27
+ # Stop the network
28
+ def down
29
+ @ui.logger.debug { "Network Down: #{self.id} " }
30
+
31
+ self.node.ssh.exec(%(sudo ifconfig #{self.bridge} down), :silence => true, :ignore_exit_status => true)
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,102 @@
1
+ class TestLab
2
+ class Network
3
+
4
+ module CIDR
5
+
6
+ CIDR_MATRIX = {
7
+ 32 => { :netmask => '255.255.255.255', :broadcast => '%s.%s.%s.%s', :network => '%s.%s.%s.%s' },
8
+ 31 => { :netmask => '255.255.255.254', :broadcast => '%s.%s.%s.1', :network => '%s.%s.%s.254' },
9
+ 30 => { :netmask => '255.255.255.252', :broadcast => '%s.%s.%s.3', :network => '%s.%s.%s.252' },
10
+ 29 => { :netmask => '255.255.255.248', :broadcast => '%s.%s.%s.7', :network => '%s.%s.%s.248' },
11
+ 28 => { :netmask => '255.255.255.240', :broadcast => '%s.%s.%s.15', :network => '%s.%s.%s.240' },
12
+ 27 => { :netmask => '255.255.255.224', :broadcast => '%s.%s.%s.31', :network => '%s.%s.%s.224' },
13
+ 26 => { :netmask => '255.255.255.192', :broadcast => '%s.%s.%s.63', :network => '%s.%s.%s.192' },
14
+ 25 => { :netmask => '255.255.255.128', :broadcast => '%s.%s.%s.127', :network => '%s.%s.%s.128' },
15
+ 24 => { :netmask => '255.255.255.0', :broadcast => '%s.%s.%s.255', :network => '%s.%s.%s.0' },
16
+ 23 => { :netmask => '255.255.254.0', :broadcast => '%s.%s.1.255', :network => '%s.%s.254.0' },
17
+ 22 => { :netmask => '255.255.252.0', :broadcast => '%s.%s.3.255', :network => '%s.%s.252.0' },
18
+ 21 => { :netmask => '255.255.248.0', :broadcast => '%s.%s.7.255', :network => '%s.%s.248.0' },
19
+ 20 => { :netmask => '255.255.240.0', :broadcast => '%s.%s.15.255', :network => '%s.%s.240.0' },
20
+ 19 => { :netmask => '255.255.224.0', :broadcast => '%s.%s.31.255', :network => '%s.%s.224.0' },
21
+ 18 => { :netmask => '255.255.192.0', :broadcast => '%s.%s.63.255', :network => '%s.%s.192.0' },
22
+ 17 => { :netmask => '255.255.128.0', :broadcast => '%s.%s.127.255', :network => '%s.%s.128.0' },
23
+ 16 => { :netmask => '255.255.0.0', :broadcast => '%s.%s.255.255', :network => '%s.%s.0.0' },
24
+ 15 => { :netmask => '255.254.0.0', :broadcast => '%s.1.255.255', :network => '%s.254.0.0' },
25
+ 14 => { :netmask => '255.252.0.0', :broadcast => '%s.3.255.255', :network => '%s.252.0.0' },
26
+ 13 => { :netmask => '255.248.0.0', :broadcast => '%s.7.255.255', :network => '%s.248.0.0' },
27
+ 12 => { :netmask => '255.240.0.0', :broadcast => '%s.15.255.255', :network => '%s.240.0.0' },
28
+ 11 => { :netmask => '255.224.0.0', :broadcast => '%s.31.255.255', :network => '%s.224.0.0' },
29
+ 10 => { :netmask => '255.192.0.0', :broadcast => '%s.63.255.255', :network => '%s.192.0.0' },
30
+ 9 => { :netmask => '255.128.0.0', :broadcast => '%s.127.255.255', :network => '%s.128.0.0' },
31
+ 8 => { :netmask => '255.0.0.0', :broadcast => '%s.255.255.255', :network => '%s.0.0.0' },
32
+ 7 => { :netmask => '254.0.0.0', :broadcast => '1.255.255.255', :network => '254.0.0.0' },
33
+ 6 => { :netmask => '252.0.0.0', :broadcast => '3.255.255.255', :network => '252.0.0.0' },
34
+ 5 => { :netmask => '248.0.0.0', :broadcast => '7.255.255.255', :network => '248.0.0.0' },
35
+ 4 => { :netmask => '240.0.0.0', :broadcast => '15.255.255.255', :network => '240.0.0.0' },
36
+ 3 => { :netmask => '224.0.0.0', :broadcast => '31.255.255.255', :network => '224.0.0.0' },
37
+ 2 => { :netmask => '192.0.0.0', :broadcast => '63.255.255.255', :network => '192.0.0.0' },
38
+ 1 => { :netmask => '128.0.0.0', :broadcast => '127.255.255.255', :network => '128.0.0.0' },
39
+ 0 => { :netmask => '0.0.0.0', :broadcast => '255.255.255.255', :network => '0.0.0.0' }
40
+ }
41
+
42
+ # Returns the CIDR of the network
43
+ def cidr
44
+ self.ip.split('/').last.to_i
45
+ end
46
+
47
+ # Returns the IP with the CIDR notation stripped away
48
+ def clean_ip
49
+ self.ip.split('/').first
50
+ end
51
+
52
+ # Returns the entry from the CIDR_MATRIX constant based on our CIDR
53
+ def cidr_matrix
54
+ CIDR_MATRIX[self.cidr]
55
+ end
56
+
57
+ # Returns the network mask
58
+ def netmask
59
+ cidr_matrix[:netmask]
60
+ end
61
+
62
+ # Returns the network address
63
+ def network
64
+ cidr_matrix[:network] % clean_ip.split('.')
65
+ end
66
+
67
+ # Returns the broadcast address
68
+ def broadcast
69
+ cidr_matrix[:broadcast] % clean_ip.split('.')
70
+ end
71
+
72
+ def cidr_octets(fill=nil)
73
+ octets = self.clean_ip.split('.')
74
+
75
+ result = case self.cidr
76
+ when 0..7 then
77
+ octets[-4,4]
78
+ when 8..15 then
79
+ [octets[-3,3], fill]
80
+ when 16..23 then
81
+ [octets[-2,2], fill, fill]
82
+ when 24..31 then
83
+ [octets[-1,1], fill, fill, fill]
84
+ end
85
+
86
+ result.flatten.compact
87
+ end
88
+
89
+ def ptr
90
+ cidr_octets.reverse.join('.')
91
+ end
92
+
93
+ # Returns the ARPA address
94
+ def arpa
95
+ result = self.network.split('.').delete_if{ |ip| ip == '0' }.reverse.join('.')
96
+ "#{result}.in-addr.arpa"
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,25 @@
1
+ class TestLab
2
+ class Network
3
+
4
+ module Lifecycle
5
+
6
+ # Network Setup
7
+ def setup
8
+ @ui.logger.debug { "Network Setup: #{self.id} " }
9
+
10
+ self.create
11
+ self.up
12
+ end
13
+
14
+ # Network Teardown
15
+ def teardown
16
+ @ui.logger.debug { "Network Teardown: #{self.id} " }
17
+
18
+ self.down
19
+ self.destroy
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end