testlab 0.0.3 → 0.0.4

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.
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