testlab 0.0.4 → 0.1.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.
- 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
|