bosh-director 1.2941.0 → 1.2949.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6ff8d3b656ced1dac2993e1b47a0bcbe7a7b6e12
4
- data.tar.gz: bc540bdd773816f632624ac090e9ea93fb2b014a
3
+ metadata.gz: f2d58f426d00ab34e503324a356f32419e8ec589
4
+ data.tar.gz: 50c33061600de723b450e53aee4234e0f84f98b3
5
5
  SHA512:
6
- metadata.gz: 404b30865cad8f8bb7bbc1e2bc355a6ed24e30194c0c7ca09f953281bd82fa6204cb0621f669de762aeac5cf27a948ad89cf0b74f3a2b81de81afe9d2c948a5b
7
- data.tar.gz: 2193105304206a1203c9c31d432e61d7acc33c56cc44bdae9b8eacd33979e96cc868e0212e3b90ace51c127ebe0b7f2cb0afae2e2a9b74aad71e79a4a0710628
6
+ metadata.gz: 3b4108244748cffa5cd372f29faddc1504f6859904bb95b6eb6ce1a86531f832e6dab33dfa2070e3df9455a3be29716512dce4b4c3b68ea2bdbaa2ded74ac8bd
7
+ data.tar.gz: aa4dddfd9fd057e4e62f5835e85487bbb4ae18ab4f926acc852cd5eb1cee6ac9f591cfd027067265ef7fef948b3e03e765a482edcf472ea897a87449c841c9c0
@@ -81,7 +81,6 @@ require 'bosh/director/job_queue'
81
81
  require 'bosh/director/lock'
82
82
  require 'bosh/director/nats_rpc'
83
83
  require 'bosh/director/network_reservation'
84
- require 'bosh/director/package_compiler'
85
84
  require 'bosh/director/problem_scanner/scanner'
86
85
  require 'bosh/director/problem_resolver'
87
86
  require 'bosh/director/resource_pool_updater'
@@ -294,7 +294,7 @@ module Bosh::Director
294
294
  deployment = @deployment_manager.find_by_name(params[:deployment_name])
295
295
 
296
296
  manifest = Psych.load(deployment.manifest)
297
- deployment_plan = DeploymentPlan::Planner.parse(manifest, {}, Config.event_log, Config.logger)
297
+ deployment_plan = DeploymentPlan::Planner.parse(manifest, deployment.cloud_config, {}, Config.event_log, Config.logger)
298
298
 
299
299
  errands = deployment_plan.jobs.select(&:can_run_as_errand?)
300
300
 
@@ -10,7 +10,7 @@ module Bosh::Director
10
10
  class << self
11
11
  include DnsHelper
12
12
 
13
- CONFIG_OPTIONS = [
13
+ attr_accessor(
14
14
  :base_dir,
15
15
  :cloud_options,
16
16
  :db,
@@ -33,17 +33,13 @@ module Bosh::Director
33
33
  :enable_snapshots,
34
34
  :max_vm_create_tries,
35
35
  :nats_uri,
36
- ]
37
-
38
- CONFIG_OPTIONS.each do |option|
39
- attr_accessor option
40
- end
36
+ )
41
37
 
42
38
  attr_reader :db_config, :redis_logger_level
43
39
 
44
40
  def clear
45
- CONFIG_OPTIONS.each do |option|
46
- self.instance_variable_set("@#{option}".to_sym, nil)
41
+ self.instance_variables.each do |ivar|
42
+ self.instance_variable_set(ivar, nil)
47
43
  end
48
44
 
49
45
  Thread.list.each do |thr|
@@ -10,11 +10,9 @@ require 'bosh/director/deployment_plan/job'
10
10
  require 'bosh/director/deployment_plan/network'
11
11
  require 'bosh/director/deployment_plan/network_subnet'
12
12
  require 'bosh/director/deployment_plan/compiled_package'
13
- require 'bosh/director/deployment_plan/preparer'
14
13
  require 'bosh/director/deployment_plan/resource_pools'
15
14
  require 'bosh/director/deployment_plan/instance_vm_binder'
16
15
  require 'bosh/director/deployment_plan/multi_job_updater'
17
- require 'bosh/director/deployment_plan/updater'
18
16
  require 'bosh/director/deployment_plan/release_version'
19
17
  require 'bosh/director/deployment_plan/resource_pool'
20
18
  require 'bosh/director/deployment_plan/stemcell'
@@ -26,3 +24,7 @@ require 'bosh/director/deployment_plan/vip_network'
26
24
  require 'bosh/director/deployment_plan/planner'
27
25
  require 'bosh/director/deployment_plan/dns_binder'
28
26
  require 'bosh/director/deployment_plan/notifier'
27
+ require 'bosh/director/deployment_plan/steps/prepare_step'
28
+ require 'bosh/director/deployment_plan/steps/update_step'
29
+ require 'bosh/director/deployment_plan/steps/package_compile_step'
30
+
@@ -23,8 +23,9 @@ module Bosh::Director
23
23
  # Binds release DB record(s) to a plan
24
24
  # @return [void]
25
25
  def bind_releases
26
- with_release_locks(@deployment_plan) do
27
- @deployment_plan.releases.each do |release|
26
+ releases = @deployment_plan.releases
27
+ with_release_locks(releases.map(&:name)) do
28
+ releases.each do |release|
28
29
  release.bind_model
29
30
  end
30
31
  end
@@ -13,13 +13,17 @@ module Bosh::Director
13
13
 
14
14
  # @param [Hash] manifest Raw deployment manifest
15
15
  # @return [DeploymentPlan::Planner] Deployment as build from deployment_spec
16
- def parse(manifest, options = {})
17
- @manifest = manifest
16
+ def parse(manifest, cloud_config, options = {})
17
+ @deployment_manifest = manifest
18
+ if cloud_config.nil?
19
+ @cloud_manifest = cloud_manifest_from_deployment_manifest @deployment_manifest
20
+ else
21
+ @cloud_manifest = cloud_config.manifest
22
+ end
18
23
 
19
- @job_states = safe_property(options, 'job_states',
20
- :class => Hash, :default => {})
24
+ @job_states = safe_property(options, 'job_states', :class => Hash, :default => {})
21
25
 
22
- @deployment = Planner.new(parse_name, options)
26
+ @deployment = Planner.new(parse_name, manifest, cloud_config, options)
23
27
 
24
28
  parse_properties
25
29
  parse_releases
@@ -35,27 +39,36 @@ module Bosh::Director
35
39
 
36
40
  private
37
41
 
42
+ CLOUD_MANIFEST_KEYS = ['resource_pools','compilation','disk_pools','networks']
43
+ def cloud_manifest_from_deployment_manifest(deployment_manifest)
44
+ cloud_manifest = {}
45
+ CLOUD_MANIFEST_KEYS.each do |key|
46
+ cloud_manifest[key] = deployment_manifest[key] if deployment_manifest.has_key? key
47
+ end
48
+ cloud_manifest
49
+ end
50
+
38
51
  def parse_name
39
- safe_property(@manifest, 'name', :class => String)
52
+ safe_property(@deployment_manifest, 'name', :class => String)
40
53
  end
41
54
 
42
55
  def parse_properties
43
- @deployment.properties = safe_property(@manifest, 'properties',
56
+ @deployment.properties = safe_property(@deployment_manifest, 'properties',
44
57
  :class => Hash, :default => {})
45
58
  end
46
59
 
47
60
  def parse_releases
48
61
  release_specs = []
49
62
 
50
- if @manifest.has_key?('release')
51
- if @manifest.has_key?('releases')
63
+ if @deployment_manifest.has_key?('release')
64
+ if @deployment_manifest.has_key?('releases')
52
65
  raise DeploymentAmbiguousReleaseSpec,
53
66
  "Deployment manifest contains both 'release' and 'releases' " +
54
67
  'sections, please use one of the two.'
55
68
  end
56
- release_specs << @manifest['release']
69
+ release_specs << @deployment_manifest['release']
57
70
  else
58
- safe_property(@manifest, 'releases', :class => Array).each do |release|
71
+ safe_property(@deployment_manifest, 'releases', :class => Array).each do |release|
59
72
  release_specs << release
60
73
  end
61
74
  end
@@ -66,7 +79,7 @@ module Bosh::Director
66
79
  end
67
80
 
68
81
  def parse_networks
69
- networks = safe_property(@manifest, 'networks', :class => Array)
82
+ networks = safe_property(@cloud_manifest, 'networks', :class => Array)
70
83
  networks.each do |network_spec|
71
84
  type = safe_property(network_spec, 'type', :class => String,
72
85
  :default => 'manual')
@@ -92,17 +105,17 @@ module Bosh::Director
92
105
  end
93
106
 
94
107
  def parse_compilation
95
- compilation_spec = safe_property(@manifest, 'compilation', :class => Hash)
108
+ compilation_spec = safe_property(@cloud_manifest, 'compilation', :class => Hash)
96
109
  @deployment.compilation = CompilationConfig.new(@deployment, compilation_spec)
97
110
  end
98
111
 
99
112
  def parse_update
100
- update_spec = safe_property(@manifest, 'update', :class => Hash)
113
+ update_spec = safe_property(@deployment_manifest, 'update', :class => Hash)
101
114
  @deployment.update = UpdateConfig.new(update_spec)
102
115
  end
103
116
 
104
117
  def parse_resource_pools
105
- resource_pools = safe_property(@manifest, 'resource_pools', :class => Array)
118
+ resource_pools = safe_property(@cloud_manifest, 'resource_pools', :class => Array)
106
119
  resource_pools.each do |rp_spec|
107
120
  @deployment.add_resource_pool(ResourcePool.new(@deployment, rp_spec, @logger))
108
121
  end
@@ -112,7 +125,7 @@ module Bosh::Director
112
125
  end
113
126
 
114
127
  def parse_disk_pools
115
- disk_pools = safe_property(@manifest, 'disk_pools', :class => Array, :optional => true)
128
+ disk_pools = safe_property(@cloud_manifest, 'disk_pools', :class => Array, :optional => true)
116
129
  return if disk_pools.nil?
117
130
  disk_pools.each do |dp_spec|
118
131
  @deployment.add_disk_pool(DiskPool.parse(dp_spec))
@@ -120,7 +133,7 @@ module Bosh::Director
120
133
  end
121
134
 
122
135
  def parse_jobs
123
- jobs = safe_property(@manifest, 'jobs', :class => Array, :default => [])
136
+ jobs = safe_property(@deployment_manifest, 'jobs', :class => Array, :default => [])
124
137
  jobs.each do |job_spec|
125
138
  state_overrides = @job_states[job_spec['name']]
126
139
  if state_overrides
@@ -6,6 +6,7 @@ module Bosh::Director
6
6
  # from the deployment manifest and the running environment.
7
7
  module DeploymentPlan
8
8
  class Planner
9
+ include LockHelper
9
10
  include DnsHelper
10
11
  include ValidationHelper
11
12
 
@@ -53,16 +54,18 @@ module Bosh::Director
53
54
  # @param [Logger]
54
55
  # logger Log for director logging
55
56
  # @return [Bosh::Director::DeploymentPlan::Planner]
56
- def self.parse(manifest, options, event_log, logger)
57
+ def self.parse(manifest, cloud_config, options, event_log, logger)
57
58
  parser = DeploymentSpecParser.new(event_log, logger)
58
- parser.parse(manifest, options)
59
+ parser.parse(manifest, cloud_config, options)
59
60
  end
60
61
 
61
- def initialize(name, options = {})
62
+ def initialize(name, manifest_text, cloud_config, options = {})
62
63
  raise ArgumentError, 'name must not be nil' unless name
63
64
  @name = name
64
- @model = nil
65
+ @manifest_text = manifest_text
66
+ @cloud_config = cloud_config
65
67
 
68
+ @model = nil
66
69
  @properties = {}
67
70
  @releases = {}
68
71
  @networks = {}
@@ -260,6 +263,29 @@ module Bosh::Director
260
263
  def rename_in_progress?
261
264
  @job_rename['old_name'] && @job_rename['new_name']
262
265
  end
266
+
267
+ def persist_updates!
268
+ #prior updates may have had release versions that we no longer use.
269
+ #remove the references to these stale releases.
270
+ stale_release_versions = (model.release_versions - releases.map(&:model))
271
+ stale_release_names = stale_release_versions.map {|version_model| version_model.release.name}
272
+ with_release_locks(stale_release_names) do
273
+ stale_release_versions.each do |release_version|
274
+ model.remove_release_version(release_version)
275
+ end
276
+ end
277
+
278
+ model.manifest = Psych.dump(@manifest_text)
279
+ model.cloud_config = @cloud_config
280
+ model.save
281
+ end
282
+
283
+ def update_stemcell_references!
284
+ current_stemcell_models = resource_pools.map { |pool| pool.stemcell.model }
285
+ model.stemcells.each do |deployment_stemcell|
286
+ deployment_stemcell.remove_deployment(model) unless current_stemcell_models.include?(deployment_stemcell)
287
+ end
288
+ end
263
289
  end
264
290
  end
265
291
  end
@@ -0,0 +1,340 @@
1
+ require 'bosh/director/compile_task_generator'
2
+
3
+ module Bosh::Director
4
+ module DeploymentPlan
5
+ module Steps
6
+ class PackageCompileStep
7
+ include LockHelper
8
+
9
+ attr_reader :compilations_performed
10
+
11
+ # @param [DeploymentPlan] deployment_plan Deployment plan
12
+ def initialize(deployment_plan)
13
+ @deployment_plan = deployment_plan
14
+
15
+ @cloud = Config.cloud
16
+ @event_log = Config.event_log
17
+ @logger = Config.logger
18
+ @director_job = Config.current_job
19
+
20
+ @tasks_mutex = Mutex.new
21
+ @network_mutex = Mutex.new
22
+ @counter_mutex = Mutex.new
23
+
24
+ compilation_config = @deployment_plan.compilation
25
+
26
+ @network = compilation_config.network
27
+ @compilation_resources = compilation_config.cloud_properties
28
+ @compilation_env = compilation_config.env
29
+
30
+ @vm_reuser = VmReuser.new
31
+
32
+ @compile_task_generator = CompileTaskGenerator.new(@logger, @event_log)
33
+
34
+ @compile_tasks = {}
35
+ @ready_tasks = []
36
+ @compilations_performed = 0
37
+ end
38
+
39
+ def perform
40
+ @logger.info('Generating a list of compile tasks')
41
+ prepare_tasks
42
+
43
+ @compile_tasks.each_value do |task|
44
+ if task.ready_to_compile?
45
+ @logger.info("Package `#{task.package.desc}' is ready to be compiled for stemcell `#{task.stemcell.desc}'")
46
+ @ready_tasks << task
47
+ end
48
+ end
49
+
50
+ if @ready_tasks.empty?
51
+ @logger.info('All packages are already compiled')
52
+ else
53
+ compile_packages
54
+ director_job_checkpoint
55
+ end
56
+ end
57
+
58
+ def compile_tasks_count
59
+ @compile_tasks.size
60
+ end
61
+
62
+ def ready_tasks_count
63
+ @tasks_mutex.synchronize { @ready_tasks.size }
64
+ end
65
+
66
+
67
+
68
+ def compile_package(task)
69
+ package = task.package
70
+ stemcell = task.stemcell
71
+
72
+ with_compile_lock(package.id, stemcell.id) do
73
+ # Check if the package was compiled in a parallel deployment
74
+ compiled_package = task.find_compiled_package(@logger, @event_log)
75
+ if compiled_package.nil?
76
+ build = Models::CompiledPackage.generate_build_number(package, stemcell)
77
+ task_result = nil
78
+
79
+ prepare_vm(stemcell) do |vm_data|
80
+ vm_metadata_updater.update(vm_data.vm, :compiling => package.name)
81
+ agent_task =
82
+ vm_data.agent.compile_package(package.blobstore_id,
83
+ package.sha1, package.name,
84
+ "#{package.version}.#{build}",
85
+ task.dependency_spec)
86
+ task_result = agent_task['result']
87
+ end
88
+
89
+ compiled_package = Models::CompiledPackage.create do |p|
90
+ p.package = package
91
+ p.stemcell = stemcell
92
+ p.sha1 = task_result['sha1']
93
+ p.build = build
94
+ p.blobstore_id = task_result['blobstore_id']
95
+ p.dependency_key = task.dependency_key
96
+ end
97
+
98
+ if Config.use_compiled_package_cache?
99
+ if BlobUtil.exists_in_global_cache?(package, task.cache_key)
100
+ @logger.info('Already exists in global package cache, skipping upload')
101
+ else
102
+ @logger.info('Uploading to global package cache')
103
+ BlobUtil.save_to_global_cache(compiled_package, task.cache_key)
104
+ end
105
+ else
106
+ @logger.info('Global blobstore not configured, skipping upload')
107
+ end
108
+
109
+ @counter_mutex.synchronize { @compilations_performed += 1 }
110
+ end
111
+
112
+ task.use_compiled_package(compiled_package)
113
+ end
114
+ end
115
+
116
+ # This method will create a VM for each stemcell in the stemcells array
117
+ # passed in. The VMs are yielded and their destruction is ensured.
118
+ # @param [Models::Stemcell] stemcell The stemcells that need to have
119
+ # compilation VMs created.
120
+ # @yield [VmData] Yields a VmData object that contains all the data for the
121
+ # VM that should be used for compilation. This may be a reused VM or a
122
+ # freshly created VM.
123
+ def prepare_vm(stemcell)
124
+ # If we're reusing VMs, try to just return an already-created VM.
125
+ if @deployment_plan.compilation.reuse_compilation_vms
126
+ vm_data = @vm_reuser.get_vm(stemcell)
127
+ if vm_data
128
+ @logger.info("Reusing compilation VM `#{vm_data.vm.cid}' for stemcell `#{stemcell.desc}'")
129
+ begin
130
+ yield vm_data
131
+ ensure
132
+ vm_data.release
133
+ end
134
+ return
135
+ end
136
+ # This shouldn't happen. If it does there's a bug.
137
+ if @vm_reuser.get_num_vms(stemcell) >=
138
+ @deployment_plan.compilation.workers
139
+ raise PackageCompilationNotEnoughWorkersForReuse,
140
+ 'There should never be more VMs for a stemcell than the number of workers in reuse_compilation_vms mode'
141
+ end
142
+ end
143
+
144
+ @logger.info("Creating compilation VM for stemcell `#{stemcell.desc}'")
145
+
146
+ reservation = reserve_network
147
+
148
+ network_settings = {
149
+ @network.name => @network.network_settings(reservation)
150
+ }
151
+
152
+ vm = VmCreator.create(@deployment_plan.model, stemcell,
153
+ @compilation_resources, network_settings,
154
+ nil, @compilation_env)
155
+ vm_data = @vm_reuser.add_vm(reservation, vm, stemcell, network_settings)
156
+
157
+ @logger.info("Configuring compilation VM: #{vm.cid}")
158
+
159
+ begin
160
+ agent = AgentClient.with_defaults(vm.agent_id)
161
+ agent.wait_until_ready
162
+
163
+ configure_vm(vm, agent, network_settings)
164
+ vm_data.agent = agent
165
+ yield vm_data
166
+ rescue RpcTimeout => e
167
+ # if we time out waiting for the agent, we should clean up the the VM
168
+ # as it will leave us in an unrecoverable state otherwise
169
+ @vm_reuser.remove_vm(vm_data)
170
+ tear_down_vm(vm_data)
171
+ raise e
172
+ ensure
173
+ vm_data.release
174
+ unless @deployment_plan.compilation.reuse_compilation_vms
175
+ tear_down_vm(vm_data)
176
+ end
177
+ end
178
+ end
179
+
180
+ private
181
+
182
+ def prepare_tasks
183
+ @event_log.begin_stage('Preparing package compilation', 1)
184
+
185
+ @event_log.track('Finding packages to compile') do
186
+ @deployment_plan.jobs.each do |job|
187
+ stemcell = job.resource_pool.stemcell
188
+
189
+ template_descs = job.templates.map do |t|
190
+ # we purposefully did NOT inline those because
191
+ # when instance_double blows up,
192
+ # it's obscure which double is at fault
193
+ release_name = t.release.name
194
+ template_name = t.name
195
+ "`#{release_name}/#{template_name}'"
196
+ end
197
+ @logger.info("Job templates #{template_descs.join(', ')} need to run on stemcell `#{stemcell.model.desc}'")
198
+
199
+ job.templates.each do |template|
200
+ template.package_models.each do |package|
201
+ @compile_task_generator.generate!(@compile_tasks, job, template, package, stemcell.model)
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ def tear_down_vm(vm_data)
209
+ vm = vm_data.vm
210
+ if vm.exists?
211
+ reservation = vm_data.reservation
212
+ @logger.info("Deleting compilation VM: #{vm.cid}")
213
+ @cloud.delete_vm(vm.cid)
214
+ vm.destroy
215
+ release_network(reservation)
216
+ end
217
+ end
218
+
219
+ def reserve_network
220
+ reservation = NetworkReservation.new_dynamic
221
+
222
+ @network_mutex.synchronize do
223
+ @network.reserve(reservation)
224
+ end
225
+
226
+ unless reservation.reserved?
227
+ raise PackageCompilationNetworkNotReserved,
228
+ "Could not reserve network for package compilation: #{reservation.error}"
229
+ end
230
+
231
+ reservation
232
+ end
233
+
234
+ def release_network(reservation)
235
+ @network_mutex.synchronize do
236
+ @network.release(reservation)
237
+ end
238
+ end
239
+
240
+ def compile_packages
241
+ @event_log.begin_stage('Compiling packages', compilation_count)
242
+ number_of_workers = @deployment_plan.compilation.workers
243
+
244
+ begin
245
+ ThreadPool.new(:max_threads => number_of_workers).wrap do |pool|
246
+ loop do
247
+ # process as many tasks without waiting
248
+ loop do
249
+ break if director_job_cancelled?
250
+ task = @tasks_mutex.synchronize { @ready_tasks.pop }
251
+ break if task.nil?
252
+
253
+ pool.process { process_task(task) }
254
+ end
255
+
256
+ break if !pool.working? && (director_job_cancelled? || @ready_tasks.empty?)
257
+ sleep(0.1)
258
+ end
259
+ end
260
+ ensure
261
+ # Delete all of the VMs if we were reusing compilation VMs. This can't
262
+ # happen until everything was done compiling.
263
+ if @deployment_plan.compilation.reuse_compilation_vms
264
+ # Using a new ThreadPool instead of reusing the previous one,
265
+ # as if there's a failed compilation, the thread pool will stop
266
+ # processing any new thread.
267
+ ThreadPool.new(:max_threads => number_of_workers).wrap do |pool|
268
+ @vm_reuser.each do |vm_data|
269
+ pool.process { tear_down_vm(vm_data) }
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ def enqueue_unblocked_tasks(task)
277
+ @tasks_mutex.synchronize do
278
+ @logger.info("Unblocking dependents of `#{task.package.desc}` for `#{task.stemcell.desc}`")
279
+ task.dependent_tasks.each do |dep_task|
280
+ if dep_task.ready_to_compile?
281
+ @logger.info("Package `#{dep_task.package.desc}' now ready to be compiled for `#{dep_task.stemcell.desc}'")
282
+ @ready_tasks << dep_task
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ def process_task(task)
289
+ package_desc = task.package.desc
290
+ stemcell_desc = task.stemcell.desc
291
+ task_desc = "package `#{package_desc}' for stemcell `#{stemcell_desc}'"
292
+
293
+ with_thread_name("compile_package(#{package_desc}, #{stemcell_desc})") do
294
+ if director_job_cancelled?
295
+ @logger.info("Cancelled compiling #{task_desc}")
296
+ else
297
+ @event_log.track(package_desc) do
298
+ @logger.info("Compiling #{task_desc}")
299
+ compile_package(task)
300
+ @logger.info("Finished compiling #{task_desc}")
301
+ enqueue_unblocked_tasks(task)
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ def director_job_cancelled?
308
+ @director_job && @director_job.task_cancelled?
309
+ end
310
+
311
+ def director_job_checkpoint
312
+ @director_job.task_checkpoint if @director_job
313
+ end
314
+
315
+ def configure_vm(vm, agent, network_settings)
316
+ state = {
317
+ 'deployment' => @deployment_plan.name,
318
+ 'resource_pool' => 'package_compiler',
319
+ 'networks' => network_settings
320
+ }
321
+
322
+ vm.update(:apply_spec => state)
323
+ agent.apply(state)
324
+ end
325
+
326
+ def compilation_count
327
+ counter = 0
328
+ @compile_tasks.each_value do |task|
329
+ counter += 1 unless task.compiled?
330
+ end
331
+ counter
332
+ end
333
+
334
+ def vm_metadata_updater
335
+ @vm_metadata_updater ||= VmMetadataUpdater.build
336
+ end
337
+ end
338
+ end
339
+ end
340
+ end