testlab 1.21.1 → 1.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +59 -17
- data/bin/tl +10 -1
- data/lib/commands/support.rb +22 -10
- data/lib/commands/testlab.rb +23 -4
- data/lib/testlab/const.rb +2 -2
- data/lib/testlab/container/status.rb +2 -1
- data/lib/testlab/container.rb +30 -3
- data/lib/testlab/network.rb +18 -0
- data/lib/testlab/node/ssh.rb +22 -2
- data/lib/testlab/node.rb +18 -0
- data/lib/testlab/provisioners/bind.rb +1 -1
- data/lib/testlab/provisioners/route.rb +2 -0
- data/lib/testlab/support/parallel.rb +93 -0
- data/lib/testlab/support.rb +1 -0
- data/lib/testlab/version.rb +1 -1
- data/lib/testlab.rb +9 -4
- metadata +6 -45
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MTVjZGJiNGZlZjVkNDU5MzQ5OTkxMWIxZDljNDA0M2IyMWM0MTkyMA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NjkwOTZhNjNlMTljMjY3ZThkMTg2ZGNhZjM5OTMzZDk5N2E1YTA3NQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NjYxMDExOWM3YWE5NDAxMzJlMThmYjVhMDRhMDc4ZjE1NmY1NGZlMjk3NWIz
|
10
|
+
YWFjNGQ0OTA5NjFkYmQ5ODkyYjNiMGY4MTZhZmJiMDgxNTlkYmU2YTJhYjAw
|
11
|
+
MmZjMjlkNTdlZDkzNWRhZmVhMWE1NzA0YjAzMTIwY2Q0MjAzY2U=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NmYzNDJjMWE3OGIxNzc0NTVmODAxMWU4ZTkyZDgxZmQ0NTgzMzk1ZjM2ZGFj
|
14
|
+
Mjg0NjQwYmQ5ZjIwMGI2ZjA2MTE0ZDdhY2E1YjRkYjk4NzExMDU1NmFiOTY0
|
15
|
+
ZGEwODNiODEwNjhjMjc2ZTgzMmRlYjNmMzBhMDZiZjA3MzMyMjY=
|
data/README.md
CHANGED
@@ -1,14 +1,8 @@
|
|
1
1
|
[](http://badge.fury.io/rb/testlab)
|
2
|
-
[](https://gemnasium.com/lookout/testlab)
|
3
3
|
[](http://travis-ci.org/lookout/testlab)
|
4
4
|
[](https://coveralls.io/r/lookout/testlab)
|
5
|
-
[](https://codeclimate.com/github/lookout/testlab)
|
12
6
|
|
13
7
|
# What is TestLab?
|
14
8
|
|
@@ -65,6 +59,7 @@ When supplying container names you actually have three options. When you do not
|
|
65
59
|
tl container build # build all containers
|
66
60
|
tl container build -n chef-client # only build the chef-client container
|
67
61
|
tl container build -n chef-server,chef-client # build the chef-server and chef-client container (NOTE: list order dictates execution order)
|
62
|
+
tl container build -n \!chef-server # build all containers, except the chef-server container
|
68
63
|
|
69
64
|
# Installation
|
70
65
|
|
@@ -85,6 +80,10 @@ The `Labfile` defines your virtual computer lab. It defines where you want to r
|
|
85
80
|
|
86
81
|
You can override the default `Labfile` by setting the path to your alternate `Labfile` via the environment variable `LABFILE` or via a command line argument (see `tl help` for more details) directly to TestLab.
|
87
82
|
|
83
|
+
For some examples of Labfiles check out:
|
84
|
+
|
85
|
+
* https://github.com/zpatten/testlab-repo
|
86
|
+
|
88
87
|
## Building your Lab
|
89
88
|
|
90
89
|
You should build your TestLab node (i.e. VirtualBox VM) and TestLab networks first. This is the foundation which the containers run on. You should attempt to keep your TestLab node intact and only cycle your containers.
|
@@ -112,31 +111,74 @@ Importing entire labs with an embedded `Labfile` is coming soon.
|
|
112
111
|
|
113
112
|
You should import containers because it saves a lot of time. Building containers from scratch is a time consuming process. Using shipping container images and ultimately lab images greatly accelerates the speed at which you can move.
|
114
113
|
|
115
|
-
**The follow commands assume you have a functioning TestLab node.**
|
116
|
-
|
117
114
|
tl container build --force # Force all defined containers to build even if some can be imported
|
118
115
|
tl container build # Attempts to import all defined containers, building those which can not be imported
|
119
116
|
tl container build -n chef-client --force # Force the 'chef-client' container to build from scratch even if it can be imported
|
117
|
+
tl container build -n \!chef-server # Build all containers, except the 'chef-server' container
|
120
118
|
|
121
119
|
### Importing Containers
|
122
120
|
|
123
121
|
You should import containers because it saves a lot of time. Building containers from scratch is a time consuming process. Using shipping container images and ultimately lab images greatly accelerates the speed at which you can move.
|
124
122
|
|
125
|
-
|
126
|
-
|
127
|
-
tl container import
|
128
|
-
tl container import -n chef-
|
129
|
-
tl container import -n chef-server,chef-client # Attempts to only import the 'chef-server' and 'chef-client' container
|
123
|
+
tl container import # Import all defined containers
|
124
|
+
tl container import -n chef-client # Import the 'chef-client' container
|
125
|
+
tl container import -n chef-server,chef-client # Import the 'chef-server' and 'chef-client' container
|
126
|
+
tl container import -n \!chef-server # Import all containers, except the 'chef-server' container
|
130
127
|
|
131
128
|
### Demolishing Containers
|
132
129
|
|
133
130
|
You can easily remove all the containers your have defined in your `Labfile` as well as single containers. You should generally demolish your containers instead of destroying them because the demolish action involves decommissioning.
|
134
131
|
|
135
|
-
**The follow commands assume you have a functioning TestLab node with running containers.**
|
136
|
-
|
137
132
|
tl container demolish # Demolish all defined containers
|
138
133
|
tl container demolish -n chef-client # Only demolish the 'chef-client' container
|
139
134
|
tl container demolish -n chef-client,chef-server # Only demolish the 'chef-client' and 'chef-server' containers
|
135
|
+
tl container demolish -n \!chef-server # Demolish all containers, except the 'chef-server' container
|
136
|
+
|
137
|
+
### Recycle Containers
|
138
|
+
|
139
|
+
You can demolish and build containers all in one motion by recycling them. If a container can be imported, recycle will attempt to do so during the build phase unless the force options is specified.
|
140
|
+
|
141
|
+
tl container recycle # Recycle all defined containers
|
142
|
+
tl container recycle -n chef-client # Only recycle the 'chef-client' container
|
143
|
+
tl container recycle -n chef-client --force # Only recycle the 'chef-client' container and force building; do not import
|
144
|
+
tl container recycle -n chef-client,chef-server # Only recycle the 'chef-client' and 'chef-server' containers
|
145
|
+
tl container recycle -n \!chef-server # Recycle all containers, except the 'chef-server' container
|
146
|
+
|
147
|
+
### Bounce Containers
|
148
|
+
|
149
|
+
You can easily restart (i.e. bounce) your containers, for example:
|
150
|
+
|
151
|
+
tl container bounce # Bounce all defined containers
|
152
|
+
tl container bounce -n chef-client # Only bounce the 'chef-client' container
|
153
|
+
tl container bounce -n chef-client,chef-server # Only bounce the 'chef-client' and 'chef-server' containers
|
154
|
+
tl container bounce -n \!chef-server # Bounce all containers, except the 'chef-server' container
|
155
|
+
|
156
|
+
### Start Containers
|
157
|
+
|
158
|
+
You can easily start your containers, for example:
|
159
|
+
|
160
|
+
tl container up # Start all defined containers
|
161
|
+
tl container up -n chef-client # Only start the 'chef-client' container
|
162
|
+
tl container up -n chef-client,chef-server # Only start the 'chef-client' and 'chef-server' containers
|
163
|
+
tl container up -n \!chef-server # Start all containers, except the 'chef-server' container
|
164
|
+
|
165
|
+
### Stop Containers
|
166
|
+
|
167
|
+
You can easily stop your containers, for example:
|
168
|
+
|
169
|
+
tl container down # Stop all defined containers
|
170
|
+
tl container down -n chef-client # Only stop the 'chef-client' container
|
171
|
+
tl container down -n chef-client,chef-server # Only stop the 'chef-client' and 'chef-server' containers
|
172
|
+
tl container down -n \!chef-server # Stop all containers, except the 'chef-server' container
|
173
|
+
|
174
|
+
### Container Status
|
175
|
+
|
176
|
+
You can get the status of all, some or a single container, for example (-q silences all of the STDOUT log output):
|
177
|
+
|
178
|
+
tl -q container status # Get status for all defined containers
|
179
|
+
tl -q container status -n chef-client # Get status only for the 'chef-client' container
|
180
|
+
tl -q container status -n chef-client,chef-server # Get status only for the 'chef-client' and 'chef-server' containers
|
181
|
+
tl -q container status -n \!chef-server # Get the status of all containers, except the 'chef-server' container
|
140
182
|
|
141
183
|
### Connecting to Containers
|
142
184
|
|
data/bin/tl
CHANGED
@@ -106,7 +106,11 @@ pre do |global,command,options,args|
|
|
106
106
|
|
107
107
|
(@verbose == true) and (ENV['LOG_LEVEL'] = 'DEBUG')
|
108
108
|
|
109
|
-
File.exists?(global[:log]) and FileUtils.
|
109
|
+
File.exists?(global[:log]) and FileUtils.rm_f(global[:log])
|
110
|
+
Dir[DEFAULT_LOG_GLOB].each do |log_filename|
|
111
|
+
File.exists?(log_filename) and FileUtils.rm_f(log_filename)
|
112
|
+
end
|
113
|
+
|
110
114
|
logger = ZTK::Logger.new(global[:log])
|
111
115
|
if ((@verbose == true) || (@quiet == false))
|
112
116
|
logger.loggers << ::Logger.new(STDOUT)
|
@@ -185,9 +189,14 @@ on_error do |exception|
|
|
185
189
|
|
186
190
|
message = format_message("TestLab v#{TestLab::VERSION} Aborted (%0.4f seconds)".black.bold % testlab_run_time)
|
187
191
|
dump_file.puts(ZTK::ANSI.uncolor(message))
|
192
|
+
dump_file.close
|
193
|
+
|
188
194
|
@ui.stderr.puts(message)
|
189
195
|
@ui.logger.info { message }
|
190
196
|
|
197
|
+
@ui.stderr.puts
|
198
|
+
commands[:bugreport] and commands[:bugreport].execute({}, {}, [])
|
199
|
+
|
191
200
|
false
|
192
201
|
end
|
193
202
|
end
|
data/lib/commands/support.rb
CHANGED
@@ -57,12 +57,10 @@ def build_lab_commands(component, klass, &block)
|
|
57
57
|
c.command lab_action do |la|
|
58
58
|
|
59
59
|
la.action do |global_options, options, args|
|
60
|
-
iterate_objects_by_name(options[:name], klass)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
object.send(lab_action)
|
65
|
-
end
|
60
|
+
objects = iterate_objects_by_name(options[:name], klass)
|
61
|
+
|
62
|
+
@testlab.do_parallel_actions(klass, objects, lab_action) do |object, action, klass|
|
63
|
+
send_lab_action(object, options, action)
|
66
64
|
end
|
67
65
|
end
|
68
66
|
|
@@ -73,15 +71,29 @@ def build_lab_commands(component, klass, &block)
|
|
73
71
|
end
|
74
72
|
end
|
75
73
|
|
74
|
+
def send_lab_action(object, options, lab_action)
|
75
|
+
if %w( build recycle ).map(&:to_sym).include?(lab_action)
|
76
|
+
object.send(lab_action, options[:force])
|
77
|
+
else
|
78
|
+
object.send(lab_action)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
76
82
|
def iterate_objects_by_name(name, klass, &block)
|
77
83
|
objects = Array.new
|
78
84
|
klass_name = klass.to_s.split('::').last.downcase
|
79
85
|
|
80
|
-
|
81
|
-
|
82
|
-
else
|
86
|
+
objects = klass.all.select{ |o| (!o.template rescue true) }
|
87
|
+
if !name.nil?
|
83
88
|
names = name.split(',')
|
84
|
-
|
89
|
+
|
90
|
+
not_names = names.collect { |n| ((n[0] == '!') and n[1..-1]) }.delete_if {|n| n == false }.compact
|
91
|
+
names.delete_if { |n| n[0] == '!' }
|
92
|
+
|
93
|
+
if names.size > 0
|
94
|
+
objects = klass.find(names).select{ |o| (!o.template rescue true) }
|
95
|
+
end
|
96
|
+
objects.delete_if{ |o| not_names.include?(o.id.to_s.downcase) }
|
85
97
|
end
|
86
98
|
|
87
99
|
(objects.nil? || (objects.count == 0)) and raise TestLab::TestLabError, "We could not find any of the #{klass_name}s you supplied!"
|
data/lib/commands/testlab.rb
CHANGED
@@ -219,21 +219,40 @@ end
|
|
219
219
|
|
220
220
|
# LAB BUG REPORT
|
221
221
|
#################
|
222
|
-
desc 'Generate a
|
222
|
+
desc 'Generate a bug report'
|
223
223
|
command :bugreport do |bugreport|
|
224
224
|
bugreport.action do |global_options, options, args|
|
225
|
+
|
226
|
+
def build_header(message)
|
227
|
+
char = '#'
|
228
|
+
header = "#{char * 30} #{message} #{char * 30}"
|
229
|
+
content = Array.new
|
230
|
+
|
231
|
+
content << (char * header.uncolor.length)
|
232
|
+
content << header
|
233
|
+
content << (char * header.uncolor.length)
|
234
|
+
|
235
|
+
content
|
236
|
+
end
|
237
|
+
|
225
238
|
@testlab.ui.logger.level = ZTK::Logger::FATAL
|
226
239
|
|
227
240
|
report_file = File.join("", "tmp", "testlab-bug-report.#{Time.now.utc.to_i}")
|
228
241
|
|
229
242
|
content = Array.new
|
230
243
|
content << (IO.read(DEFAULT_DUMP_FILE) rescue nil)
|
231
|
-
|
232
|
-
content <<
|
244
|
+
|
245
|
+
content << build_header("TestLab Log")
|
246
|
+
content << IO.read(DEFAULT_LOG_FILE)
|
247
|
+
|
248
|
+
Dir[DEFAULT_LOG_GLOB].each do |log_filename|
|
249
|
+
content << build_header("TestLab Log: #{log_filename.inspect}")
|
250
|
+
content << IO.read(log_filename)
|
251
|
+
end
|
233
252
|
|
234
253
|
IO.write(report_file, content.flatten.compact.join("\n"))
|
235
254
|
|
236
|
-
@testlab.ui.
|
255
|
+
@testlab.ui.stderr.puts("The bug report for your most recent execution of TestLab is located at #{report_file.inspect}.")
|
237
256
|
end
|
238
257
|
end
|
239
258
|
|
data/lib/testlab/const.rb
CHANGED
@@ -6,6 +6,6 @@ unless defined?(DEFAULT_LOG_FILE)
|
|
6
6
|
DEFAULT_LOG_FILE = File.join('', 'tmp', 'testlab.log')
|
7
7
|
end
|
8
8
|
|
9
|
-
unless defined?(
|
10
|
-
|
9
|
+
unless defined?(DEFAULT_LOG_GLOB)
|
10
|
+
DEFAULT_LOG_GLOB = File.join('', 'tmp', 'testlab.log.*')
|
11
11
|
end
|
@@ -64,7 +64,8 @@ class TestLab
|
|
64
64
|
:interfaces => interfaces,
|
65
65
|
:provisioners => self.provisioners.map(&:to_s).collect{ |p| p.split('::').last }.join(','),
|
66
66
|
:node_id => self.node.id,
|
67
|
-
:inherited => (self.inherit.nil? ? 'none' : self.inherit)
|
67
|
+
:inherited => (self.inherit.nil? ? 'none' : self.inherit),
|
68
|
+
:priority => self.priority
|
68
69
|
}
|
69
70
|
end
|
70
71
|
|
data/lib/testlab/container.rb
CHANGED
@@ -72,7 +72,7 @@ class TestLab
|
|
72
72
|
# An array of symbols of the various keys in our status hash.
|
73
73
|
#
|
74
74
|
# @see TestLab::Container::Status
|
75
|
-
STATUS_KEYS = %w(id node_id cpu_time memory_usage disk_usage mode fqdn state distro release interfaces provisioners inherited).map(&:to_sym)
|
75
|
+
STATUS_KEYS = %w(id node_id priority cpu_time memory_usage disk_usage mode fqdn state distro release interfaces provisioners inherited).map(&:to_sym)
|
76
76
|
|
77
77
|
# Sub-Modules
|
78
78
|
autoload :Actions, 'testlab/container/actions'
|
@@ -123,12 +123,15 @@ class TestLab
|
|
123
123
|
|
124
124
|
attribute :mounts, :default => Array.new
|
125
125
|
|
126
|
+
# The URL to this container or container templates shipping container image.
|
126
127
|
attribute :sc_url
|
127
128
|
|
129
|
+
# The AppArmor profile LXC should use
|
128
130
|
attribute :aa_profile
|
131
|
+
# Additional capabilities LXC should drop
|
129
132
|
attribute :cap_drop
|
130
133
|
|
131
|
-
attribute :tags
|
134
|
+
attribute :tags
|
132
135
|
|
133
136
|
# Instructs ephemeral containers to persist; otherwise tmpfs will be used
|
134
137
|
# as the backend store for ephemeral containers.
|
@@ -141,6 +144,12 @@ class TestLab
|
|
141
144
|
# Should we inherit a container?
|
142
145
|
attribute :inherit
|
143
146
|
|
147
|
+
# Execution priority; set the order in which the object should execute when
|
148
|
+
# performing parallel operations; a higher value priority equates to more
|
149
|
+
# precedence. Objects with identical priority values will execute in
|
150
|
+
# parallel.
|
151
|
+
attribute :priority
|
152
|
+
|
144
153
|
|
145
154
|
def initialize(*args)
|
146
155
|
@ui = TestLab.ui
|
@@ -149,6 +158,12 @@ class TestLab
|
|
149
158
|
super(*args)
|
150
159
|
@ui.logger.debug { "Container '#{self.id}' Loaded" }
|
151
160
|
|
161
|
+
if self.priority.nil? && (self.template == false)
|
162
|
+
$priority_counter ||= 0
|
163
|
+
self.priority ||= (1000 - $priority_counter)
|
164
|
+
$priority_counter += 1
|
165
|
+
end
|
166
|
+
|
152
167
|
self.tags ||= [ self.id ]
|
153
168
|
|
154
169
|
if !self.inherit.nil?
|
@@ -160,7 +175,7 @@ class TestLab
|
|
160
175
|
end
|
161
176
|
|
162
177
|
# Inherit the containers attributes
|
163
|
-
parent.attributes.reject{ |k,v| [:id, :node_id, :inherit, :template].include?(k) }.each do |key, value|
|
178
|
+
parent.attributes.reject{ |k,v| [:id, :node_id, :inherit, :template, :priority].include?(k) }.each do |key, value|
|
164
179
|
self.send("#{key}=", (value.dup rescue value))
|
165
180
|
end
|
166
181
|
|
@@ -185,6 +200,18 @@ class TestLab
|
|
185
200
|
self.node.repo_dir
|
186
201
|
end
|
187
202
|
|
203
|
+
class << self
|
204
|
+
|
205
|
+
def priority_groups
|
206
|
+
self.all.delete_if{|c| (c.template == true) }.map(&:priority).sort.uniq.reverse
|
207
|
+
end
|
208
|
+
|
209
|
+
def by_priority(priority)
|
210
|
+
self.all.delete_if{|c| (c.template == true) }.select{ |c| c.priority == priority }
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
188
215
|
end
|
189
216
|
|
190
217
|
end
|
data/lib/testlab/network.rb
CHANGED
@@ -37,6 +37,12 @@ class TestLab
|
|
37
37
|
attribute :address
|
38
38
|
attribute :bridge
|
39
39
|
|
40
|
+
# Execution priority; set the order in which the object should execute when
|
41
|
+
# performing parallel operations; a higher value priority equates to more
|
42
|
+
# precedence. Objects with identical priority values will execute in
|
43
|
+
# parallel.
|
44
|
+
attribute :priority, :default => 0
|
45
|
+
|
40
46
|
|
41
47
|
def initialize(*args)
|
42
48
|
@ui = TestLab.ui
|
@@ -46,6 +52,18 @@ class TestLab
|
|
46
52
|
@ui.logger.debug { "Network '#{self.id}' Loaded" }
|
47
53
|
end
|
48
54
|
|
55
|
+
class << self
|
56
|
+
|
57
|
+
def priority_groups
|
58
|
+
self.all.map(&:priority).sort.uniq.reverse
|
59
|
+
end
|
60
|
+
|
61
|
+
def by_priority(priority)
|
62
|
+
self.all.select{ |n| n.priority == priority }
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
49
67
|
end
|
50
68
|
|
51
69
|
end
|
data/lib/testlab/node/ssh.rb
CHANGED
@@ -3,7 +3,9 @@ class TestLab
|
|
3
3
|
|
4
4
|
module SSH
|
5
5
|
|
6
|
-
# SSH
|
6
|
+
# Node SSH Connection
|
7
|
+
#
|
8
|
+
# @return [ZTK::SSH] Returns a new or cached ZTK::SSH object for the node.
|
7
9
|
def ssh(options={})
|
8
10
|
if (!defined?(@ssh) || @ssh.nil?)
|
9
11
|
@ssh ||= ZTK::SSH.new({:ui => @ui, :timeout => 3600, :silence => true}.merge(options))
|
@@ -17,7 +19,10 @@ class TestLab
|
|
17
19
|
@ssh
|
18
20
|
end
|
19
21
|
|
20
|
-
# SSH
|
22
|
+
# Container SSH Connection
|
23
|
+
#
|
24
|
+
# @return [ZTK::SSH] Returns a new or cached ZTK::SSH object for the
|
25
|
+
# container.
|
21
26
|
def container_ssh(container, options={})
|
22
27
|
name = container.id
|
23
28
|
@container_ssh ||= Hash.new
|
@@ -39,6 +44,21 @@ class TestLab
|
|
39
44
|
@container_ssh[name]
|
40
45
|
end
|
41
46
|
|
47
|
+
# Shutdown all SSH connections
|
48
|
+
#
|
49
|
+
# @return [Boolean] True if successful.
|
50
|
+
def ssh_shutdown!
|
51
|
+
@ssh.nil? or @ssh.close
|
52
|
+
@ssh = nil
|
53
|
+
|
54
|
+
@container_ssh.nil? or @container_ssh.each do |name, ssh|
|
55
|
+
ssh.nil? or ssh.close
|
56
|
+
end
|
57
|
+
@container_ssh = nil
|
58
|
+
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
42
62
|
end
|
43
63
|
|
44
64
|
end
|
data/lib/testlab/node.rb
CHANGED
@@ -44,6 +44,12 @@ class TestLab
|
|
44
44
|
attribute :provisioners, :default => Array.new
|
45
45
|
attribute :config, :default => Hash.new
|
46
46
|
|
47
|
+
# Execution priority; set the order in which the object should execute when
|
48
|
+
# performing parallel operations; a higher value priority equates to more
|
49
|
+
# precedence. Objects with identical priority values will execute in
|
50
|
+
# parallel.
|
51
|
+
attribute :priority, :default => 0
|
52
|
+
|
47
53
|
|
48
54
|
def initialize(*args)
|
49
55
|
@ui = TestLab.ui
|
@@ -74,6 +80,18 @@ class TestLab
|
|
74
80
|
self.config[:bind][:domain] ||= 'tld.invalid'
|
75
81
|
end
|
76
82
|
|
83
|
+
class << self
|
84
|
+
|
85
|
+
def priority_groups
|
86
|
+
self.all.map(&:priority).sort.uniq.reverse
|
87
|
+
end
|
88
|
+
|
89
|
+
def by_priority(priority)
|
90
|
+
self.all.select{ |n| n.priority == priority }
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
77
95
|
end
|
78
96
|
|
79
97
|
end
|
@@ -28,6 +28,7 @@ class TestLab
|
|
28
28
|
def on_node_provision(node)
|
29
29
|
@ui.logger.debug { "BIND Provisioner: Node #{node.id}" }
|
30
30
|
|
31
|
+
bind_install(node)
|
31
32
|
bind_provision(node)
|
32
33
|
|
33
34
|
true
|
@@ -153,7 +154,6 @@ class TestLab
|
|
153
154
|
end
|
154
155
|
|
155
156
|
def bind_provision(node)
|
156
|
-
bind_install(node)
|
157
157
|
build_bind_conf(node)
|
158
158
|
bind_reload(node)
|
159
159
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
class TestLab
|
2
|
+
module Support
|
3
|
+
|
4
|
+
module Parallel
|
5
|
+
|
6
|
+
# Perform actions against a collection of objects in parallel.
|
7
|
+
#
|
8
|
+
# @return [Boolean] True if successful.
|
9
|
+
def do_parallel_actions(klass, objects, action, reverse=false, &block)
|
10
|
+
|
11
|
+
# Our before fork hook; we should reset our SSH connections before
|
12
|
+
# forking, they will automatically re-establish after forking.
|
13
|
+
def before_fork(pid)
|
14
|
+
defined?(nodes) and nodes.each do |node|
|
15
|
+
node.ssh_shutdown!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Clear the screen and move the cursor to x:0, y:0 using ANSI escape
|
20
|
+
# codes
|
21
|
+
#
|
22
|
+
# @return [Boolean] Returns True if successful.
|
23
|
+
def reset_screen
|
24
|
+
self.ui.stdout.puts(ZTK::ANSI.reset)
|
25
|
+
self.ui.stdout.puts(ZTK::ANSI.goto(0, 0))
|
26
|
+
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
klass_name = klass.to_s.split('::').last
|
31
|
+
command = ZTK::Command.new(:silence => true, :ignore_exit_status => true)
|
32
|
+
parallel = ZTK::Parallel.new(:ui => self.ui)
|
33
|
+
parallel.config do |config|
|
34
|
+
config.before_fork = method(:before_fork)
|
35
|
+
end
|
36
|
+
|
37
|
+
priority_groups = klass.priority_groups
|
38
|
+
(reverse == true) and priority_groups.reverse!
|
39
|
+
|
40
|
+
priority_groups.each do |priority_group|
|
41
|
+
|
42
|
+
selected_objects = objects.select{ |c| c.priority == priority_group }
|
43
|
+
if selected_objects.count == 1
|
44
|
+
object = selected_objects.first
|
45
|
+
|
46
|
+
block.call(object, action, klass)
|
47
|
+
else
|
48
|
+
selected_objects.each do |object|
|
49
|
+
parallel.process do
|
50
|
+
$0 = "TestLab #{klass_name.capitalize} #{action.to_s.capitalize}: #{object.id.inspect}"
|
51
|
+
|
52
|
+
# Redirect all standard I/O to /dev/null
|
53
|
+
self.ui.stdout.reopen("/dev/null", "a")
|
54
|
+
self.ui.stderr.reopen("/dev/null", "a")
|
55
|
+
self.ui.stdin.reopen("/dev/null")
|
56
|
+
|
57
|
+
# Redirect logging to an object specific log file
|
58
|
+
log_filename = "/tmp/testlab.log.#{object.id.to_s.downcase}"
|
59
|
+
File.exists?(log_filename) && FileUtils.rm_f(log_filename)
|
60
|
+
self.ui.logger = ZTK::Logger.new(log_filename)
|
61
|
+
|
62
|
+
block.call(object, action, klass)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
while (parallel.count > 0) do
|
67
|
+
message = format_message("Parallel #{action.to_s.capitalize} Running:".yellow)
|
68
|
+
|
69
|
+
reset_screen
|
70
|
+
self.ui.stdout.puts(message)
|
71
|
+
self.ui.stdout.puts("-" * message.uncolor.length)
|
72
|
+
self.ui.stdout.print(command.exec(%(ps u --pid #{parallel.pids.join(' ')} 2>/dev/null)).output)
|
73
|
+
|
74
|
+
sleep(1)
|
75
|
+
|
76
|
+
# Attempt to reap processes faster, otherwise we'll only reap one
|
77
|
+
# per second if we're lucky.
|
78
|
+
for x in 1..(parallel.count) do
|
79
|
+
parallel.wait(Process::WNOHANG)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
reset_screen
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
data/lib/testlab/support.rb
CHANGED
data/lib/testlab/version.rb
CHANGED
data/lib/testlab.rb
CHANGED
@@ -137,6 +137,7 @@ class TestLab
|
|
137
137
|
autoload :User, 'testlab/user'
|
138
138
|
autoload :Utility, 'testlab/utility'
|
139
139
|
|
140
|
+
include TestLab::Support::Parallel
|
140
141
|
include TestLab::Utility::Misc
|
141
142
|
|
142
143
|
attr_accessor :config_dir
|
@@ -426,11 +427,13 @@ class TestLab
|
|
426
427
|
def method_proxy(method_name, *method_args)
|
427
428
|
nodes.each do |node|
|
428
429
|
node.send(method_name, *method_args)
|
430
|
+
|
429
431
|
node.networks.each do |network|
|
430
432
|
network.send(method_name, *method_args)
|
431
433
|
end
|
432
|
-
|
433
|
-
|
434
|
+
|
435
|
+
do_parallel_actions(TestLab::Container, node.containers, method_name) do |object, action, klass|
|
436
|
+
object.send(method_name, *method_args)
|
434
437
|
end
|
435
438
|
end
|
436
439
|
end
|
@@ -443,12 +446,14 @@ class TestLab
|
|
443
446
|
# @return [Boolean] True if successful.
|
444
447
|
def reverse_method_proxy(method_name, *method_args)
|
445
448
|
nodes.reverse.each do |node|
|
446
|
-
node.containers.reverse
|
447
|
-
|
449
|
+
do_parallel_actions(TestLab::Container, node.containers.reverse, method_name, true) do |object, action, klass|
|
450
|
+
object.send(method_name, *method_args)
|
448
451
|
end
|
452
|
+
|
449
453
|
node.networks.reverse.each do |network|
|
450
454
|
network.send(method_name, *method_args)
|
451
455
|
end
|
456
|
+
|
452
457
|
node.send(method_name, *method_args)
|
453
458
|
end
|
454
459
|
end
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: testlab
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
5
|
-
prerelease:
|
4
|
+
version: 1.22.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Zachary Patten
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2014-
|
11
|
+
date: 2014-03-13 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: gli
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ! '>='
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ! '>='
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,7 +27,6 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: lxc
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ! '>='
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,7 +34,6 @@ dependencies:
|
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ! '>='
|
44
39
|
- !ruby/object:Gem::Version
|
@@ -46,7 +41,6 @@ dependencies:
|
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: ztk
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ! '>='
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :runtime
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - ! '>='
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,7 +55,6 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: activesupport
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
59
|
- - ! '>='
|
68
60
|
- !ruby/object:Gem::Version
|
@@ -70,7 +62,6 @@ dependencies:
|
|
70
62
|
type: :runtime
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
66
|
- - ! '>='
|
76
67
|
- !ruby/object:Gem::Version
|
@@ -78,7 +69,6 @@ dependencies:
|
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: bundler
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
73
|
- - ! '>='
|
84
74
|
- !ruby/object:Gem::Version
|
@@ -86,7 +76,6 @@ dependencies:
|
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
80
|
- - ! '>='
|
92
81
|
- !ruby/object:Gem::Version
|
@@ -94,7 +83,6 @@ dependencies:
|
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
84
|
name: pry
|
96
85
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
86
|
requirements:
|
99
87
|
- - ! '>='
|
100
88
|
- !ruby/object:Gem::Version
|
@@ -102,7 +90,6 @@ dependencies:
|
|
102
90
|
type: :development
|
103
91
|
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
93
|
requirements:
|
107
94
|
- - ! '>='
|
108
95
|
- !ruby/object:Gem::Version
|
@@ -110,7 +97,6 @@ dependencies:
|
|
110
97
|
- !ruby/object:Gem::Dependency
|
111
98
|
name: rake
|
112
99
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
100
|
requirements:
|
115
101
|
- - ! '>='
|
116
102
|
- !ruby/object:Gem::Version
|
@@ -118,7 +104,6 @@ dependencies:
|
|
118
104
|
type: :development
|
119
105
|
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
107
|
requirements:
|
123
108
|
- - ! '>='
|
124
109
|
- !ruby/object:Gem::Version
|
@@ -126,7 +111,6 @@ dependencies:
|
|
126
111
|
- !ruby/object:Gem::Dependency
|
127
112
|
name: redcarpet
|
128
113
|
requirement: !ruby/object:Gem::Requirement
|
129
|
-
none: false
|
130
114
|
requirements:
|
131
115
|
- - ! '>='
|
132
116
|
- !ruby/object:Gem::Version
|
@@ -134,7 +118,6 @@ dependencies:
|
|
134
118
|
type: :development
|
135
119
|
prerelease: false
|
136
120
|
version_requirements: !ruby/object:Gem::Requirement
|
137
|
-
none: false
|
138
121
|
requirements:
|
139
122
|
- - ! '>='
|
140
123
|
- !ruby/object:Gem::Version
|
@@ -142,7 +125,6 @@ dependencies:
|
|
142
125
|
- !ruby/object:Gem::Dependency
|
143
126
|
name: aruba
|
144
127
|
requirement: !ruby/object:Gem::Requirement
|
145
|
-
none: false
|
146
128
|
requirements:
|
147
129
|
- - ! '>='
|
148
130
|
- !ruby/object:Gem::Version
|
@@ -150,7 +132,6 @@ dependencies:
|
|
150
132
|
type: :development
|
151
133
|
prerelease: false
|
152
134
|
version_requirements: !ruby/object:Gem::Requirement
|
153
|
-
none: false
|
154
135
|
requirements:
|
155
136
|
- - ! '>='
|
156
137
|
- !ruby/object:Gem::Version
|
@@ -158,7 +139,6 @@ dependencies:
|
|
158
139
|
- !ruby/object:Gem::Dependency
|
159
140
|
name: rspec
|
160
141
|
requirement: !ruby/object:Gem::Requirement
|
161
|
-
none: false
|
162
142
|
requirements:
|
163
143
|
- - ! '>='
|
164
144
|
- !ruby/object:Gem::Version
|
@@ -166,7 +146,6 @@ dependencies:
|
|
166
146
|
type: :development
|
167
147
|
prerelease: false
|
168
148
|
version_requirements: !ruby/object:Gem::Requirement
|
169
|
-
none: false
|
170
149
|
requirements:
|
171
150
|
- - ! '>='
|
172
151
|
- !ruby/object:Gem::Version
|
@@ -174,7 +153,6 @@ dependencies:
|
|
174
153
|
- !ruby/object:Gem::Dependency
|
175
154
|
name: yard
|
176
155
|
requirement: !ruby/object:Gem::Requirement
|
177
|
-
none: false
|
178
156
|
requirements:
|
179
157
|
- - ! '>='
|
180
158
|
- !ruby/object:Gem::Version
|
@@ -182,7 +160,6 @@ dependencies:
|
|
182
160
|
type: :development
|
183
161
|
prerelease: false
|
184
162
|
version_requirements: !ruby/object:Gem::Requirement
|
185
|
-
none: false
|
186
163
|
requirements:
|
187
164
|
- - ! '>='
|
188
165
|
- !ruby/object:Gem::Version
|
@@ -190,7 +167,6 @@ dependencies:
|
|
190
167
|
- !ruby/object:Gem::Dependency
|
191
168
|
name: coveralls
|
192
169
|
requirement: !ruby/object:Gem::Requirement
|
193
|
-
none: false
|
194
170
|
requirements:
|
195
171
|
- - ! '>='
|
196
172
|
- !ruby/object:Gem::Version
|
@@ -198,7 +174,6 @@ dependencies:
|
|
198
174
|
type: :development
|
199
175
|
prerelease: false
|
200
176
|
version_requirements: !ruby/object:Gem::Requirement
|
201
|
-
none: false
|
202
177
|
requirements:
|
203
178
|
- - ! '>='
|
204
179
|
- !ruby/object:Gem::Version
|
@@ -206,7 +181,6 @@ dependencies:
|
|
206
181
|
- !ruby/object:Gem::Dependency
|
207
182
|
name: travis
|
208
183
|
requirement: !ruby/object:Gem::Requirement
|
209
|
-
none: false
|
210
184
|
requirements:
|
211
185
|
- - ! '>='
|
212
186
|
- !ruby/object:Gem::Version
|
@@ -214,7 +188,6 @@ dependencies:
|
|
214
188
|
type: :development
|
215
189
|
prerelease: false
|
216
190
|
version_requirements: !ruby/object:Gem::Requirement
|
217
|
-
none: false
|
218
191
|
requirements:
|
219
192
|
- - ! '>='
|
220
193
|
- !ruby/object:Gem::Version
|
@@ -222,7 +195,6 @@ dependencies:
|
|
222
195
|
- !ruby/object:Gem::Dependency
|
223
196
|
name: travis-artifacts
|
224
197
|
requirement: !ruby/object:Gem::Requirement
|
225
|
-
none: false
|
226
198
|
requirements:
|
227
199
|
- - ! '>='
|
228
200
|
- !ruby/object:Gem::Version
|
@@ -230,7 +202,6 @@ dependencies:
|
|
230
202
|
type: :development
|
231
203
|
prerelease: false
|
232
204
|
version_requirements: !ruby/object:Gem::Requirement
|
233
|
-
none: false
|
234
205
|
requirements:
|
235
206
|
- - ! '>='
|
236
207
|
- !ruby/object:Gem::Version
|
@@ -238,7 +209,6 @@ dependencies:
|
|
238
209
|
- !ruby/object:Gem::Dependency
|
239
210
|
name: typhoeus
|
240
211
|
requirement: !ruby/object:Gem::Requirement
|
241
|
-
none: false
|
242
212
|
requirements:
|
243
213
|
- - ~>
|
244
214
|
- !ruby/object:Gem::Version
|
@@ -246,7 +216,6 @@ dependencies:
|
|
246
216
|
type: :development
|
247
217
|
prerelease: false
|
248
218
|
version_requirements: !ruby/object:Gem::Requirement
|
249
|
-
none: false
|
250
219
|
requirements:
|
251
220
|
- - ~>
|
252
221
|
- !ruby/object:Gem::Version
|
@@ -254,7 +223,6 @@ dependencies:
|
|
254
223
|
- !ruby/object:Gem::Dependency
|
255
224
|
name: websocket-native
|
256
225
|
requirement: !ruby/object:Gem::Requirement
|
257
|
-
none: false
|
258
226
|
requirements:
|
259
227
|
- - ! '>='
|
260
228
|
- !ruby/object:Gem::Version
|
@@ -262,7 +230,6 @@ dependencies:
|
|
262
230
|
type: :development
|
263
231
|
prerelease: false
|
264
232
|
version_requirements: !ruby/object:Gem::Requirement
|
265
|
-
none: false
|
266
233
|
requirements:
|
267
234
|
- - ! '>='
|
268
235
|
- !ruby/object:Gem::Version
|
@@ -376,6 +343,7 @@ files:
|
|
376
343
|
- lib/testlab/support.rb
|
377
344
|
- lib/testlab/support/execution.rb
|
378
345
|
- lib/testlab/support/lifecycle.rb
|
346
|
+
- lib/testlab/support/parallel.rb
|
379
347
|
- lib/testlab/user.rb
|
380
348
|
- lib/testlab/user/lifecycle.rb
|
381
349
|
- lib/testlab/utility.rb
|
@@ -405,33 +373,26 @@ files:
|
|
405
373
|
homepage: http://hackers.lookout.com/testlab/
|
406
374
|
licenses:
|
407
375
|
- Apache 2.0
|
376
|
+
metadata: {}
|
408
377
|
post_install_message:
|
409
378
|
rdoc_options: []
|
410
379
|
require_paths:
|
411
380
|
- lib
|
412
381
|
required_ruby_version: !ruby/object:Gem::Requirement
|
413
|
-
none: false
|
414
382
|
requirements:
|
415
383
|
- - ! '>='
|
416
384
|
- !ruby/object:Gem::Version
|
417
385
|
version: '0'
|
418
|
-
segments:
|
419
|
-
- 0
|
420
|
-
hash: -360706151585908837
|
421
386
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
422
|
-
none: false
|
423
387
|
requirements:
|
424
388
|
- - ! '>='
|
425
389
|
- !ruby/object:Gem::Version
|
426
390
|
version: '0'
|
427
|
-
segments:
|
428
|
-
- 0
|
429
|
-
hash: -360706151585908837
|
430
391
|
requirements: []
|
431
392
|
rubyforge_project:
|
432
|
-
rubygems_version:
|
393
|
+
rubygems_version: 2.2.1
|
433
394
|
signing_key:
|
434
|
-
specification_version:
|
395
|
+
specification_version: 4
|
435
396
|
summary: A toolkit for building virtual computer labs
|
436
397
|
test_files:
|
437
398
|
- features/step_definitions/container_steps.rb
|