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 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
  [![Gem Version](https://badge.fury.io/rb/testlab.png)](http://badge.fury.io/rb/testlab)
2
- [![Dependency Status](https://gemnasium.com/zpatten/testlab.png)](https://gemnasium.com/zpatten/testlab)
2
+ [![Dependency Status](https://gemnasium.com/lookout/testlab.png)](https://gemnasium.com/lookout/testlab)
3
3
  [![Build Status](https://secure.travis-ci.org/lookout/testlab.png)](http://travis-ci.org/lookout/testlab)
4
4
  [![Coverage Status](https://coveralls.io/repos/lookout/testlab/badge.png?branch=master)](https://coveralls.io/r/lookout/testlab)
5
- [![Code Climate](https://codeclimate.com/github/zpatten/testlab.png)](https://codeclimate.com/github/zpatten/testlab)
6
-
7
- # A Quick Note on Versioning
8
-
9
- I attempt to keep TestLab in line with Semantic Versioning when incrementing version numbers. I am human so I may screw this up on, hopefully rare, occasion. Semantic Versioning is delicious and everyone should be following this schema, especially when it comes to Ruby Gems which have a habit of pushing your projects into "dependency hell".
10
-
11
- * http://semver.org/
5
+ [![Code Climate](https://codeclimate.com/github/lookout/testlab.png)](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
- **The follow commands assume you have a functioning TestLab node.**
126
-
127
- tl container import # Attempts to import all defined containers
128
- tl container import -n chef-client # Attempts to only import the 'chef-client' container
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.move(global[:log], DEFAULT_LOG_BACKUP)
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
@@ -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) do |object|
61
- if %w( build recycle ).map(&:to_sym).include?(lab_action)
62
- object.send(lab_action, options[:force])
63
- else
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
- if name.nil?
81
- objects = klass.all.select{ |object| (!object.template rescue true) }
82
- else
86
+ objects = klass.all.select{ |o| (!o.template rescue true) }
87
+ if !name.nil?
83
88
  names = name.split(',')
84
- objects = klass.find(names).select{ |object| (!object.template rescue true) }
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!"
@@ -219,21 +219,40 @@ end
219
219
 
220
220
  # LAB BUG REPORT
221
221
  #################
222
- desc 'Generate a lab bug report'
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
- content << ("#{'=' * 30} TestLab Log #{'=' * 30}")
232
- content << IO.read(DEFAULT_LOG_BACKUP)
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.stdout.puts("The bug report for your most recent execution of TestLab is located at #{report_file.inspect}.")
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?(DEFAULT_LOG_BACKUP)
10
- DEFAULT_LOG_BACKUP = File.join('', 'tmp', 'testlab.log.bak')
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
 
@@ -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, :default => Array.new
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
@@ -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
@@ -3,7 +3,9 @@ class TestLab
3
3
 
4
4
  module SSH
5
5
 
6
- # SSH to the Node
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 to a container running on the Node
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
@@ -33,6 +33,7 @@ class TestLab
33
33
 
34
34
  true
35
35
  end
36
+ alias :on_network_destroy :on_network_down
36
37
 
37
38
  # Route: Node Down
38
39
  def on_node_down(node)
@@ -42,6 +43,7 @@ class TestLab
42
43
 
43
44
  true
44
45
  end
46
+ alias :on_node_destroy :on_node_down
45
47
 
46
48
  private
47
49
 
@@ -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
@@ -13,6 +13,7 @@ class TestLab
13
13
  module Support
14
14
  autoload :Execution, 'testlab/support/execution'
15
15
  autoload :Lifecycle, 'testlab/support/lifecycle'
16
+ autoload :Parallel, 'testlab/support/parallel'
16
17
  end
17
18
 
18
19
  end
@@ -1,6 +1,6 @@
1
1
  class TestLab
2
2
  unless const_defined?(:VERSION)
3
3
  # TestLab Gem Version
4
- VERSION = "1.21.1"
4
+ VERSION = "1.22.0"
5
5
  end
6
6
  end
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
- node.containers.each do |container|
433
- container.send(method_name, *method_args)
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.each do |container|
447
- container.send(method_name, *method_args)
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.21.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-02-07 00:00:00.000000000 Z
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: 1.8.25
393
+ rubygems_version: 2.2.1
433
394
  signing_key:
434
- specification_version: 3
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