testlab 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/testlab/container/actions.rb +2 -0
- data/lib/testlab/container/class_methods.rb +16 -0
- data/lib/testlab/container/interface.rb +43 -0
- data/lib/testlab/container/lxc.rb +34 -0
- data/lib/testlab/container/method_missing.rb +20 -0
- data/lib/testlab/container/status.rb +7 -1
- data/lib/testlab/container.rb +22 -87
- data/lib/testlab/network/bind.rb +19 -0
- data/lib/testlab/network/class_methods.rb +13 -0
- data/lib/testlab/network/status.rb +19 -1
- data/lib/testlab/network.rb +16 -10
- data/lib/testlab/node/bind.rb +16 -14
- data/lib/testlab/node/class_methods.rb +14 -0
- data/lib/testlab/node/lifecycle.rb +21 -0
- data/lib/testlab/node/method_missing.rb +29 -0
- data/lib/testlab/node/resolv.rb +2 -3
- data/lib/testlab/node/status.rb +1 -0
- data/lib/testlab/node/templates/bind-db.erb +1 -1
- data/lib/testlab/node/templates/bind.erb +4 -3
- data/lib/testlab/node.rb +21 -63
- data/lib/testlab/{network → utility}/cidr.rb +56 -30
- data/lib/testlab/utility.rb +16 -0
- data/lib/testlab/version.rb +1 -1
- data/lib/testlab.rb +11 -11
- data/spec/support/Labfile +1 -1
- metadata +12 -6
- data/lib/testlab/container/network.rb +0 -31
- data/lib/testlab/node/templates/bind-setup.erb +0 -6
@@ -0,0 +1,43 @@
|
|
1
|
+
class TestLab
|
2
|
+
class Container
|
3
|
+
|
4
|
+
module Interface
|
5
|
+
|
6
|
+
def ip
|
7
|
+
TestLab::Utility.ip(self.primary_interface.last[:ip])
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns the CIDR of the container
|
11
|
+
def cidr
|
12
|
+
TestLab::Utility.cidr(self.primary_interface.last[:ip]).to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def ptr
|
16
|
+
octets = self.ip.split('.')
|
17
|
+
|
18
|
+
result = case self.cidr
|
19
|
+
when 0..7 then
|
20
|
+
octets[-4,4]
|
21
|
+
when 8..15 then
|
22
|
+
octets[-3,3]
|
23
|
+
when 16..23 then
|
24
|
+
octets[-2,2]
|
25
|
+
when 24..31 then
|
26
|
+
octets[-1,1]
|
27
|
+
end
|
28
|
+
|
29
|
+
result.reverse.join('.')
|
30
|
+
end
|
31
|
+
|
32
|
+
def primary_interface
|
33
|
+
if self.interfaces.any?{ |i,c| c[:primary] == true }
|
34
|
+
self.interfaces.find{ |i,c| c[:primary] == true }
|
35
|
+
else
|
36
|
+
self.interfaces.first
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -11,6 +11,18 @@ class TestLab
|
|
11
11
|
@lxc ||= self.node.lxc.container(self.id)
|
12
12
|
end
|
13
13
|
|
14
|
+
# SSH to the container
|
15
|
+
def ssh(options={})
|
16
|
+
self.node.container_ssh(self, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Does the container exist?
|
20
|
+
def exists?
|
21
|
+
@ui.logger.debug { "Container Exists?: #{self.id} " }
|
22
|
+
|
23
|
+
self.lxc.exists?
|
24
|
+
end
|
25
|
+
|
14
26
|
# Returns arguments for lxc-create based on our distro
|
15
27
|
#
|
16
28
|
# @return [Array] An array of arguments for lxc-create
|
@@ -37,6 +49,28 @@ class TestLab
|
|
37
49
|
end
|
38
50
|
end
|
39
51
|
|
52
|
+
# Builds an array of hashes containing the lxc configuration options for
|
53
|
+
# our networks
|
54
|
+
def build_lxc_network_conf(interfaces)
|
55
|
+
networks = Array.new
|
56
|
+
|
57
|
+
interfaces.each do |network, network_config|
|
58
|
+
networks << Hash[
|
59
|
+
'lxc.network.type' => :veth,
|
60
|
+
'lxc.network.flags' => :up,
|
61
|
+
'lxc.network.link' => TestLab::Network.first(network).bridge,
|
62
|
+
'lxc.network.name' => network_config[:name],
|
63
|
+
'lxc.network.hwaddr' => network_config[:mac],
|
64
|
+
'lxc.network.ipv4' => network_config[:ip]
|
65
|
+
]
|
66
|
+
if (network_config[:primary] == true) || (interfaces.count == 1)
|
67
|
+
networks.last.merge!('lxc.network.ipv4.gateway' => :auto)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
networks
|
72
|
+
end
|
73
|
+
|
40
74
|
end
|
41
75
|
|
42
76
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class TestLab
|
2
|
+
class Container
|
3
|
+
|
4
|
+
module MethodMissing
|
5
|
+
|
6
|
+
# Method missing handler
|
7
|
+
def method_missing(method_name, *method_args)
|
8
|
+
@ui.logger.debug { "CONTAINER METHOD MISSING: #{method_name.inspect}(#{method_args.inspect})" }
|
9
|
+
|
10
|
+
if (defined?(@provisioner) && @provisioner.respond_to?(method_name))
|
11
|
+
@provisioner.send(method_name, [self, *method_args].flatten)
|
12
|
+
else
|
13
|
+
super(method_name, *method_args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -3,11 +3,17 @@ class TestLab
|
|
3
3
|
|
4
4
|
module Status
|
5
5
|
|
6
|
+
def fqdn
|
7
|
+
self.domain ||= self.node.labfile.config[:domain]
|
8
|
+
|
9
|
+
[self.id, self.domain].join('.')
|
10
|
+
end
|
11
|
+
|
6
12
|
def status
|
7
13
|
interfaces = self.interfaces.collect{ |network, network_config| "#{network}:#{network_config[:name]}:#{network_config[:ip]}" }.join(', ')
|
8
|
-
|
9
14
|
{
|
10
15
|
:id => self.id,
|
16
|
+
:fqdn => self.fqdn,
|
11
17
|
:state => self.state,
|
12
18
|
:distro => self.distro,
|
13
19
|
:release => self.release,
|
data/lib/testlab/container.rb
CHANGED
@@ -7,8 +7,29 @@ class TestLab
|
|
7
7
|
#
|
8
8
|
# @author Zachary Patten <zachary@jovelabs.net>
|
9
9
|
class Container < ZTK::DSL::Base
|
10
|
-
STATUS_KEYS = %w(node_id id state distro release interfaces provisioner).map(&:to_sym)
|
10
|
+
STATUS_KEYS = %w(node_id id fqdn state distro release interfaces provisioner).map(&:to_sym)
|
11
|
+
|
12
|
+
# Sub-Modules
|
13
|
+
autoload :Actions, 'testlab/container/actions'
|
14
|
+
autoload :ClassMethods, 'testlab/container/class_methods'
|
15
|
+
autoload :Generators, 'testlab/container/generators'
|
16
|
+
autoload :Interface, 'testlab/container/interface'
|
17
|
+
autoload :Lifecycle, 'testlab/container/lifecycle'
|
18
|
+
autoload :LXC, 'testlab/container/lxc'
|
19
|
+
autoload :MethodMissing, 'testlab/container/method_missing'
|
20
|
+
autoload :Status, 'testlab/container/status'
|
11
21
|
|
22
|
+
include TestLab::Container::Actions
|
23
|
+
include TestLab::Container::Generators
|
24
|
+
include TestLab::Container::Interface
|
25
|
+
include TestLab::Container::Lifecycle
|
26
|
+
include TestLab::Container::LXC
|
27
|
+
include TestLab::Container::MethodMissing
|
28
|
+
include TestLab::Container::Status
|
29
|
+
|
30
|
+
extend TestLab::Container::ClassMethods
|
31
|
+
|
32
|
+
# Associations and Attributes
|
12
33
|
belongs_to :node, :class_name => 'TestLab::Node'
|
13
34
|
|
14
35
|
attribute :provisioner
|
@@ -28,21 +49,6 @@ class TestLab
|
|
28
49
|
attribute :persist
|
29
50
|
|
30
51
|
|
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'
|
37
|
-
|
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
|
44
|
-
|
45
|
-
|
46
52
|
def initialize(*args)
|
47
53
|
super(*args)
|
48
54
|
|
@@ -50,77 +56,6 @@ class TestLab
|
|
50
56
|
@provisioner = self.provisioner.new(self.config) if !self.provisioner.nil?
|
51
57
|
end
|
52
58
|
|
53
|
-
################################################################################
|
54
|
-
|
55
|
-
# Does the container exist?
|
56
|
-
def exists?
|
57
|
-
@ui.logger.debug { "Container Exists?: #{self.id} " }
|
58
|
-
|
59
|
-
self.lxc.exists?
|
60
|
-
end
|
61
|
-
|
62
|
-
################################################################################
|
63
|
-
|
64
|
-
# SSH to the container
|
65
|
-
def ssh(options={})
|
66
|
-
self.node.container_ssh(self, options)
|
67
|
-
end
|
68
|
-
|
69
|
-
def ip
|
70
|
-
self.primary_interface.last[:ip].split('/').first
|
71
|
-
end
|
72
|
-
|
73
|
-
# Returns the CIDR of the container
|
74
|
-
def cidr
|
75
|
-
self.primary_interface.last[:ip].split('/').last.to_i
|
76
|
-
end
|
77
|
-
|
78
|
-
def ptr
|
79
|
-
octets = self.ip.split('.')
|
80
|
-
|
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
|
91
|
-
|
92
|
-
result.reverse.join('.')
|
93
|
-
end
|
94
|
-
|
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
|
101
|
-
end
|
102
|
-
|
103
|
-
class << self
|
104
|
-
|
105
|
-
def domains
|
106
|
-
self.all.map(&:domain).compact
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
################################################################################
|
112
|
-
|
113
|
-
# Method missing handler
|
114
|
-
def method_missing(method_name, *method_args)
|
115
|
-
@ui.logger.debug { "CONTAINER METHOD MISSING: #{method_name.inspect}(#{method_args.inspect})" }
|
116
|
-
|
117
|
-
if (defined?(@provisioner) && @provisioner.respond_to?(method_name))
|
118
|
-
@provisioner.send(method_name, [self, *method_args].flatten)
|
119
|
-
else
|
120
|
-
super(method_name, *method_args)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
59
|
end
|
125
60
|
|
126
61
|
end
|
@@ -10,10 +10,28 @@ class TestLab
|
|
10
10
|
:id => self.id,
|
11
11
|
:node_id => self.node.id,
|
12
12
|
:state => self.state,
|
13
|
-
:interface => interface
|
13
|
+
:interface => interface,
|
14
|
+
:broadcast => self.broadcast,
|
15
|
+
:network => self.network,
|
16
|
+
:netmask => self.netmask
|
14
17
|
}
|
15
18
|
end
|
16
19
|
|
20
|
+
# Returns the network mask
|
21
|
+
def netmask
|
22
|
+
TestLab::Utility.netmask(self.ip)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the network address
|
26
|
+
def network
|
27
|
+
TestLab::Utility.network(self.ip)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the broadcast address
|
31
|
+
def broadcast
|
32
|
+
TestLab::Utility.broadcast(self.ip)
|
33
|
+
end
|
34
|
+
|
17
35
|
# Network Bridge State
|
18
36
|
def state
|
19
37
|
output = self.node.ssh.exec(%(sudo ifconfig #{self.bridge} | grep 'MTU'), :silence => true, :ignore_exit_status => true).output.strip
|
data/lib/testlab/network.rb
CHANGED
@@ -7,8 +7,23 @@ class TestLab
|
|
7
7
|
#
|
8
8
|
# @author Zachary Patten <zachary@jovelabs.net>
|
9
9
|
class Network < ZTK::DSL::Base
|
10
|
-
STATUS_KEYS = %w(node_id id state interface).map(&:to_sym)
|
10
|
+
STATUS_KEYS = %w(node_id id state interface network netmask broadcast).map(&:to_sym)
|
11
11
|
|
12
|
+
# Sub-Modules
|
13
|
+
autoload :Actions, 'testlab/network/actions'
|
14
|
+
autoload :Bind, 'testlab/network/bind'
|
15
|
+
autoload :ClassMethods, 'testlab/network/class_methods'
|
16
|
+
autoload :Lifecycle, 'testlab/network/lifecycle'
|
17
|
+
autoload :Status, 'testlab/network/status'
|
18
|
+
|
19
|
+
include TestLab::Network::Actions
|
20
|
+
include TestLab::Network::Bind
|
21
|
+
include TestLab::Network::Lifecycle
|
22
|
+
include TestLab::Network::Status
|
23
|
+
|
24
|
+
extend TestLab::Network::ClassMethods
|
25
|
+
|
26
|
+
# Associations and Attributes
|
12
27
|
belongs_to :node, :class_name => 'TestLab::Node'
|
13
28
|
|
14
29
|
attribute :bridge
|
@@ -16,15 +31,6 @@ class TestLab
|
|
16
31
|
attribute :ip
|
17
32
|
attribute :config
|
18
33
|
|
19
|
-
autoload :Actions, 'testlab/network/actions'
|
20
|
-
autoload :CIDR, 'testlab/network/cidr'
|
21
|
-
autoload :Lifecycle, 'testlab/network/lifecycle'
|
22
|
-
autoload :Status, 'testlab/network/status'
|
23
|
-
|
24
|
-
include TestLab::Network::Actions
|
25
|
-
include TestLab::Network::CIDR
|
26
|
-
include TestLab::Network::Lifecycle
|
27
|
-
include TestLab::Network::Status
|
28
34
|
|
29
35
|
def initialize(*args)
|
30
36
|
super(*args)
|
data/lib/testlab/node/bind.rb
CHANGED
@@ -17,14 +17,14 @@ class TestLab
|
|
17
17
|
reverse_records = Hash.new
|
18
18
|
|
19
19
|
TestLab::Container.all.each do |container|
|
20
|
-
interface
|
21
|
-
|
20
|
+
interface = container.primary_interface
|
21
|
+
container.domain ||= container.node.labfile.config[:domain]
|
22
22
|
|
23
|
-
forward_records[domain] ||= Array.new
|
24
|
-
forward_records[domain] << %(#{container.id} IN A #{container.ip})
|
23
|
+
forward_records[container.domain] ||= Array.new
|
24
|
+
forward_records[container.domain] << %(#{container.id} IN A #{container.ip})
|
25
25
|
|
26
26
|
reverse_records[interface.first] ||= Array.new
|
27
|
-
reverse_records[interface.first] << %(#{container.ptr} IN PTR #{container.id}.#{domain}.)
|
27
|
+
reverse_records[interface.first] << %(#{container.ptr} IN PTR #{container.id}.#{container.domain}.)
|
28
28
|
end
|
29
29
|
{ :forward => forward_records, :reverse => reverse_records }
|
30
30
|
end
|
@@ -48,8 +48,7 @@ class TestLab
|
|
48
48
|
build_bind_db(network.arpa, reverse_records[network.id])
|
49
49
|
end
|
50
50
|
|
51
|
-
|
52
|
-
domains.each do |domain|
|
51
|
+
TestLab::Container.domains.each do |domain|
|
53
52
|
context = {
|
54
53
|
:zone => domain
|
55
54
|
}
|
@@ -68,8 +67,6 @@ class TestLab
|
|
68
67
|
file.puts(ZTK::Template.do_not_edit_notice(:message => "TestLab v#{TestLab::VERSION} BIND DB: #{zone}", :char => ';'))
|
69
68
|
file.puts(ZTK::Template.render(bind_db_template, { :zone => zone, :records => records }))
|
70
69
|
end
|
71
|
-
|
72
|
-
# self.ssh.exec(%(sudo rm -fv /etc/bind/db.#{zone}.jnl))
|
73
70
|
end
|
74
71
|
|
75
72
|
# Builds the BIND configuration
|
@@ -80,13 +77,18 @@ class TestLab
|
|
80
77
|
end
|
81
78
|
end
|
82
79
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
80
|
+
def bind_install
|
81
|
+
self.ssh.exec(%(sudo apt-get -y install bind9))
|
82
|
+
end
|
86
83
|
|
87
|
-
|
84
|
+
def bind_reload
|
85
|
+
self.ssh.exec(%(sudo rndc reload))
|
86
|
+
end
|
88
87
|
|
89
|
-
|
88
|
+
def bind_setup
|
89
|
+
bind_install
|
90
|
+
build_bind_conf
|
91
|
+
bind_reload
|
90
92
|
end
|
91
93
|
|
92
94
|
end
|
@@ -3,6 +3,23 @@ class TestLab
|
|
3
3
|
|
4
4
|
module Lifecycle
|
5
5
|
|
6
|
+
# Iterates an array of arrays calling the specified method on all the
|
7
|
+
# collections of objects
|
8
|
+
def call_collections(collections, method_name)
|
9
|
+
collections.each do |collection|
|
10
|
+
call_methods(collection, method_name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Calls the specified method on all the objects supplied
|
15
|
+
def call_methods(objects, method_name)
|
16
|
+
objects.each do |object|
|
17
|
+
if object.respond_to?(method_name)
|
18
|
+
object.send(method_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
6
23
|
# Bootstrap the node
|
7
24
|
def node_setup
|
8
25
|
node_setup_template = File.join(self.class.template_dir, 'node-setup.erb')
|
@@ -25,6 +42,10 @@ class TestLab
|
|
25
42
|
|
26
43
|
call_collections([self.networks, self.routers, self.containers], :setup)
|
27
44
|
|
45
|
+
if self.components.include?('bind')
|
46
|
+
bind_reload
|
47
|
+
end
|
48
|
+
|
28
49
|
true
|
29
50
|
end
|
30
51
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class TestLab
|
2
|
+
class Node
|
3
|
+
|
4
|
+
module MethodMissing
|
5
|
+
|
6
|
+
# Node Method Missing Handler
|
7
|
+
def method_missing(method_name, *method_args)
|
8
|
+
@ui.logger.debug { "NODE METHOD MISSING: #{method_name.inspect}(#{method_args.inspect})" }
|
9
|
+
|
10
|
+
if TestLab::Provider::PROXY_METHODS.include?(method_name)
|
11
|
+
result = nil
|
12
|
+
|
13
|
+
if @provider.respond_to?(method_name)
|
14
|
+
@ui.logger.debug { "@provider.send(#{method_name.inspect}, #{method_args.inspect})" }
|
15
|
+
result = @provider.send(method_name, *method_args)
|
16
|
+
else
|
17
|
+
raise TestLab::ProviderError, "Your provider does not respond to the method '#{method_name}'!"
|
18
|
+
end
|
19
|
+
|
20
|
+
result
|
21
|
+
else
|
22
|
+
super(method_name, *method_args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/testlab/node/resolv.rb
CHANGED
@@ -8,10 +8,9 @@ class TestLab
|
|
8
8
|
def build_resolv_main_conf(file)
|
9
9
|
resolv_conf_template = File.join(self.class.template_dir, "resolv.erb")
|
10
10
|
|
11
|
-
domains = ([self.labfile.config[:domain]] + TestLab::Container.domains).flatten
|
12
11
|
context = {
|
13
|
-
:servers => [TestLab::Network.
|
14
|
-
:search => domains.join(' ')
|
12
|
+
:servers => [TestLab::Network.ips, "8.8.8.8", "8.8.4.4" ].flatten,
|
13
|
+
:search => TestLab::Container.domains.join(' ')
|
15
14
|
}
|
16
15
|
|
17
16
|
file.puts(ZTK::Template.do_not_edit_notice(:message => "TestLab v#{TestLab::VERSION} RESOLVER Configuration"))
|
data/lib/testlab/node/status.rb
CHANGED
@@ -10,9 +10,10 @@ options {
|
|
10
10
|
// Uncomment the following block, and insert the addresses replacing
|
11
11
|
// the all-0's placeholder.
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
forwarders {
|
14
|
+
8.8.8.8;
|
15
|
+
8.8.4.4;
|
16
|
+
};
|
16
17
|
|
17
18
|
//========================================================================
|
18
19
|
// If BIND logs error messages about the root key being expired,
|
data/lib/testlab/node.rb
CHANGED
@@ -9,6 +9,27 @@ class TestLab
|
|
9
9
|
class Node < ZTK::DSL::Base
|
10
10
|
STATUS_KEYS = %w(id instance_id state user ip port provider con net rtr).map(&:to_sym)
|
11
11
|
|
12
|
+
# Sub-Modules
|
13
|
+
autoload :Bind, 'testlab/node/bind'
|
14
|
+
autoload :ClassMethods, 'testlab/node/class_methods'
|
15
|
+
autoload :Lifecycle, 'testlab/node/lifecycle'
|
16
|
+
autoload :LXC, 'testlab/node/lxc'
|
17
|
+
autoload :MethodMissing, 'testlab/node/method_missing'
|
18
|
+
autoload :Resolv, 'testlab/node/resolv'
|
19
|
+
autoload :SSH, 'testlab/node/ssh'
|
20
|
+
autoload :Status, 'testlab/node/status'
|
21
|
+
|
22
|
+
include TestLab::Node::Bind
|
23
|
+
include TestLab::Node::Lifecycle
|
24
|
+
include TestLab::Node::LXC
|
25
|
+
include TestLab::Node::MethodMissing
|
26
|
+
include TestLab::Node::Resolv
|
27
|
+
include TestLab::Node::SSH
|
28
|
+
include TestLab::Node::Status
|
29
|
+
|
30
|
+
extend TestLab::Node::ClassMethods
|
31
|
+
|
32
|
+
# Associations and Attributes
|
12
33
|
belongs_to :labfile, :class_name => 'TestLab::Lab'
|
13
34
|
|
14
35
|
has_many :routers, :class_name => 'TestLab::Router'
|
@@ -20,21 +41,6 @@ class TestLab
|
|
20
41
|
attribute :components
|
21
42
|
|
22
43
|
|
23
|
-
autoload :Bind, 'testlab/node/bind'
|
24
|
-
autoload :Lifecycle, 'testlab/node/lifecycle'
|
25
|
-
autoload :LXC, 'testlab/node/lxc'
|
26
|
-
autoload :Resolv, 'testlab/node/resolv'
|
27
|
-
autoload :SSH, 'testlab/node/ssh'
|
28
|
-
autoload :Status, 'testlab/node/status'
|
29
|
-
|
30
|
-
include TestLab::Node::Bind
|
31
|
-
include TestLab::Node::Lifecycle
|
32
|
-
include TestLab::Node::LXC
|
33
|
-
include TestLab::Node::Resolv
|
34
|
-
include TestLab::Node::SSH
|
35
|
-
include TestLab::Node::Status
|
36
|
-
|
37
|
-
|
38
44
|
def initialize(*args)
|
39
45
|
super(*args)
|
40
46
|
|
@@ -42,54 +48,6 @@ class TestLab
|
|
42
48
|
@provider = self.provider.new(self.config)
|
43
49
|
end
|
44
50
|
|
45
|
-
################################################################################
|
46
|
-
|
47
|
-
# Iterates an array of arrays calling the specified method on all the
|
48
|
-
# collections of objects
|
49
|
-
def call_collections(collections, method_name)
|
50
|
-
collections.each do |collection|
|
51
|
-
call_methods(collection, method_name)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# Calls the specified method on all the objects supplied
|
56
|
-
def call_methods(objects, method_name)
|
57
|
-
objects.each do |object|
|
58
|
-
if object.respond_to?(method_name)
|
59
|
-
object.send(method_name)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Method missing handler
|
65
|
-
def method_missing(method_name, *method_args)
|
66
|
-
@ui.logger.debug { "NODE METHOD MISSING: #{method_name.inspect}(#{method_args.inspect})" }
|
67
|
-
|
68
|
-
if TestLab::Provider::PROXY_METHODS.include?(method_name)
|
69
|
-
result = nil
|
70
|
-
|
71
|
-
if @provider.respond_to?(method_name)
|
72
|
-
@ui.logger.debug { "@provider.send(#{method_name.inspect}, #{method_args.inspect})" }
|
73
|
-
result = @provider.send(method_name, *method_args)
|
74
|
-
else
|
75
|
-
raise TestLab::ProviderError, "Your provider does not respond to the method '#{method_name}'!"
|
76
|
-
end
|
77
|
-
|
78
|
-
result
|
79
|
-
else
|
80
|
-
super(method_name, *method_args)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
class << self
|
85
|
-
|
86
|
-
# Returns the path to the gems provider templates
|
87
|
-
def template_dir
|
88
|
-
File.join(TestLab.gem_dir, "lib", "testlab", "node", "templates")
|
89
|
-
end
|
90
|
-
|
91
|
-
end
|
92
|
-
|
93
51
|
end
|
94
52
|
|
95
53
|
end
|
@@ -1,6 +1,12 @@
|
|
1
1
|
class TestLab
|
2
|
-
|
2
|
+
module Utility
|
3
3
|
|
4
|
+
# CIDR Error Class
|
5
|
+
class CIDRError < UtilityError; end
|
6
|
+
|
7
|
+
# CIDR Module
|
8
|
+
#
|
9
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
4
10
|
module CIDR
|
5
11
|
|
6
12
|
CIDR_MATRIX = {
|
@@ -39,61 +45,81 @@ class TestLab
|
|
39
45
|
0 => { :netmask => '0.0.0.0', :broadcast => '255.255.255.255', :network => '0.0.0.0' }
|
40
46
|
}
|
41
47
|
|
42
|
-
|
43
|
-
|
44
|
-
|
48
|
+
def ip(ip)
|
49
|
+
ip.split('/').first
|
50
|
+
end
|
51
|
+
|
52
|
+
def cidr(ip)
|
53
|
+
ip.split('/').last.to_i
|
45
54
|
end
|
46
55
|
|
47
|
-
|
48
|
-
|
49
|
-
self.ip.split('/').first
|
56
|
+
def octets(ip)
|
57
|
+
ip.split('.')
|
50
58
|
end
|
51
59
|
|
52
|
-
|
53
|
-
|
54
|
-
CIDR_MATRIX[self.cidr]
|
60
|
+
def cidr_matrix(cidr)
|
61
|
+
CIDR_MATRIX[cidr.to_i]
|
55
62
|
end
|
56
63
|
|
57
|
-
|
58
|
-
|
59
|
-
cidr_matrix[:netmask]
|
64
|
+
def netmask(ip)
|
65
|
+
ip, cidr = ip.split('/')
|
66
|
+
cidr_matrix(cidr)[:netmask]
|
60
67
|
end
|
61
68
|
|
62
69
|
# Returns the network address
|
63
|
-
def network
|
64
|
-
|
70
|
+
def network(ip)
|
71
|
+
ip, cidr = ip.split('/')
|
72
|
+
cidr_matrix(cidr)[:network] % ip.split('.')
|
65
73
|
end
|
66
74
|
|
67
75
|
# Returns the broadcast address
|
68
|
-
def broadcast
|
69
|
-
|
76
|
+
def broadcast(ip)
|
77
|
+
ip, cidr = ip.split('/')
|
78
|
+
cidr_matrix(cidr)[:broadcast] % ip.split('.')
|
70
79
|
end
|
71
80
|
|
72
|
-
def cidr_octets(fill=nil)
|
73
|
-
|
81
|
+
def cidr_octets(ip, fill=nil)
|
82
|
+
ip, cidr = ip.split('/')
|
83
|
+
oct = octets(ip)
|
74
84
|
|
75
|
-
result = case
|
85
|
+
result = case cidr.to_i
|
76
86
|
when 0..7 then
|
77
|
-
|
87
|
+
oct[-4,4]
|
78
88
|
when 8..15 then
|
79
|
-
[
|
89
|
+
[fill, oct[-3,3]]
|
80
90
|
when 16..23 then
|
81
|
-
[
|
91
|
+
[fill, fill, oct[-2,2]]
|
82
92
|
when 24..31 then
|
83
|
-
[
|
93
|
+
[fill, fill, fill, oct[-1,1]]
|
84
94
|
end
|
85
95
|
|
86
96
|
result.flatten.compact
|
87
97
|
end
|
88
98
|
|
89
|
-
def
|
90
|
-
|
99
|
+
def arpa_octets(ip, fill=nil)
|
100
|
+
ip, cidr = ip.split('/')
|
101
|
+
oct = octets(ip)
|
102
|
+
|
103
|
+
result = case cidr.to_i
|
104
|
+
when 0..7 then
|
105
|
+
[fill, fill, fill, fill]
|
106
|
+
when 8..15 then
|
107
|
+
[oct[0,1], fill, fill, fill]
|
108
|
+
when 16..23 then
|
109
|
+
[oct[0,2], fill, fill]
|
110
|
+
when 24..31 then
|
111
|
+
[oct[0,3], fill]
|
112
|
+
end
|
113
|
+
|
114
|
+
result.flatten.compact.reverse
|
115
|
+
end
|
116
|
+
|
117
|
+
def ptr(ip)
|
118
|
+
cidr_octets(ip).reverse.join('.')
|
91
119
|
end
|
92
120
|
|
93
|
-
|
94
|
-
|
95
|
-
result = self.network.split('.').delete_if{ |ip| ip == '0' }.reverse.join('.')
|
96
|
-
"#{result}.in-addr.arpa"
|
121
|
+
def arpa(ip)
|
122
|
+
[arpa_octets(ip), 'in-addr', 'arpa'].flatten.join('.')
|
97
123
|
end
|
98
124
|
|
99
125
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class TestLab
|
2
|
+
|
3
|
+
# Utility Error Class
|
4
|
+
class UtilityError < TestLabError; end
|
5
|
+
|
6
|
+
# Utility Module
|
7
|
+
#
|
8
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
9
|
+
module Utility
|
10
|
+
autoload :CIDR, 'testlab/utility/cidr'
|
11
|
+
|
12
|
+
extend TestLab::Utility::CIDR
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/lib/testlab/version.rb
CHANGED
data/lib/testlab.rb
CHANGED
@@ -10,15 +10,15 @@ class TestLab
|
|
10
10
|
# Top-Level Error Class
|
11
11
|
class TestLabError < StandardError; end
|
12
12
|
|
13
|
-
|
13
|
+
# Main Classes
|
14
|
+
autoload :Container, 'testlab/container'
|
15
|
+
autoload :Labfile, 'testlab/labfile'
|
16
|
+
autoload :Network, 'testlab/network'
|
17
|
+
autoload :Node, 'testlab/node'
|
18
|
+
autoload :Provider, 'testlab/provider'
|
14
19
|
autoload :Provisioner, 'testlab/provisioner'
|
15
|
-
|
16
|
-
autoload :
|
17
|
-
autoload :Node, 'testlab/node'
|
18
|
-
autoload :Router, 'testlab/router'
|
19
|
-
autoload :Container, 'testlab/container'
|
20
|
-
autoload :Network, 'testlab/network'
|
21
|
-
autoload :Link, 'testlab/link'
|
20
|
+
autoload :Router, 'testlab/router'
|
21
|
+
autoload :Utility, 'testlab/utility'
|
22
22
|
|
23
23
|
@@ui ||= nil
|
24
24
|
|
@@ -63,17 +63,17 @@ class TestLab
|
|
63
63
|
if alive?
|
64
64
|
@@ui.stdout.puts("NODES:")
|
65
65
|
ZTK::Report.new(:ui => @@ui).spreadsheet(TestLab::Node.all, TestLab::Node::STATUS_KEYS) do |node|
|
66
|
-
OpenStruct.new(node.status
|
66
|
+
OpenStruct.new(node.status)
|
67
67
|
end
|
68
68
|
@@ui.stdout.puts
|
69
69
|
@@ui.stdout.puts("NETWORKS:")
|
70
70
|
ZTK::Report.new(:ui => @@ui).spreadsheet(TestLab::Network.all, TestLab::Network::STATUS_KEYS) do |network|
|
71
|
-
OpenStruct.new(network.status
|
71
|
+
OpenStruct.new(network.status)
|
72
72
|
end
|
73
73
|
@@ui.stdout.puts
|
74
74
|
@@ui.stdout.puts("CONTAINERS:")
|
75
75
|
ZTK::Report.new(:ui => @@ui).spreadsheet(TestLab::Container.all, TestLab::Container::STATUS_KEYS) do |container|
|
76
|
-
OpenStruct.new(container.status
|
76
|
+
OpenStruct.new(container.status)
|
77
77
|
end
|
78
78
|
|
79
79
|
true
|
data/spec/support/Labfile
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: testlab
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -161,26 +161,30 @@ files:
|
|
161
161
|
- lib/testlab.rb
|
162
162
|
- lib/testlab/container.rb
|
163
163
|
- lib/testlab/container/actions.rb
|
164
|
+
- lib/testlab/container/class_methods.rb
|
164
165
|
- lib/testlab/container/generators.rb
|
166
|
+
- lib/testlab/container/interface.rb
|
165
167
|
- lib/testlab/container/lifecycle.rb
|
166
168
|
- lib/testlab/container/lxc.rb
|
167
|
-
- lib/testlab/container/
|
169
|
+
- lib/testlab/container/method_missing.rb
|
168
170
|
- lib/testlab/container/status.rb
|
169
171
|
- lib/testlab/labfile.rb
|
170
172
|
- lib/testlab/network.rb
|
171
173
|
- lib/testlab/network/actions.rb
|
172
|
-
- lib/testlab/network/
|
174
|
+
- lib/testlab/network/bind.rb
|
175
|
+
- lib/testlab/network/class_methods.rb
|
173
176
|
- lib/testlab/network/lifecycle.rb
|
174
177
|
- lib/testlab/network/status.rb
|
175
178
|
- lib/testlab/node.rb
|
176
179
|
- lib/testlab/node/bind.rb
|
180
|
+
- lib/testlab/node/class_methods.rb
|
177
181
|
- lib/testlab/node/lifecycle.rb
|
178
182
|
- lib/testlab/node/lxc.rb
|
183
|
+
- lib/testlab/node/method_missing.rb
|
179
184
|
- lib/testlab/node/resolv.rb
|
180
185
|
- lib/testlab/node/ssh.rb
|
181
186
|
- lib/testlab/node/status.rb
|
182
187
|
- lib/testlab/node/templates/bind-db.erb
|
183
|
-
- lib/testlab/node/templates/bind-setup.erb
|
184
188
|
- lib/testlab/node/templates/bind-zone.erb
|
185
189
|
- lib/testlab/node/templates/bind.erb
|
186
190
|
- lib/testlab/node/templates/node-setup.erb
|
@@ -195,6 +199,8 @@ files:
|
|
195
199
|
- lib/testlab/provisioners/shell.rb
|
196
200
|
- lib/testlab/provisioners/templates/chef/bootstrap.erb
|
197
201
|
- lib/testlab/router.rb
|
202
|
+
- lib/testlab/utility.rb
|
203
|
+
- lib/testlab/utility/cidr.rb
|
198
204
|
- lib/testlab/version.rb
|
199
205
|
- spec/provider_spec.rb
|
200
206
|
- spec/provisioner_spec.rb
|
@@ -217,7 +223,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
217
223
|
version: '0'
|
218
224
|
segments:
|
219
225
|
- 0
|
220
|
-
hash:
|
226
|
+
hash: 4469350422868766283
|
221
227
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
222
228
|
none: false
|
223
229
|
requirements:
|
@@ -226,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
226
232
|
version: '0'
|
227
233
|
segments:
|
228
234
|
- 0
|
229
|
-
hash:
|
235
|
+
hash: 4469350422868766283
|
230
236
|
requirements: []
|
231
237
|
rubyforge_project:
|
232
238
|
rubygems_version: 1.8.25
|
@@ -1,31 +0,0 @@
|
|
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
|