Class: Bosh::Director::DeploymentPlan::Assembler
- Includes:
- IpUtil, LockHelper
- Defined in:
- lib/bosh/director/deployment_plan/assembler.rb
Overview
DeploymentPlan::Assembler is used to populate deployment plan with information about existing deployment and information from director DB
Instance Method Summary collapse
-
#bind_configuration ⇒ void
Calculates configuration checksums for all jobs in this deployment plan.
-
#bind_deployment ⇒ void
Binds deployment DB record to a plan.
- #bind_dns ⇒ Object
-
#bind_existing_deployment ⇒ void
Binds information about existing deployment to a plan.
-
#bind_existing_vm(vm, lock) ⇒ Object
Queries agent for VM state and updates deployment plan accordingly.
-
#bind_idle_vm(vm, resource_pool, state, reservations) ⇒ Object
Binds idle VM to a resource pool with a proper network reservation.
- #bind_instance(instance_model, state, reservations) ⇒ Object
- #bind_instance_networks ⇒ Object
- #bind_instance_vms ⇒ Object
-
#bind_properties ⇒ void
Binds properties for all templates in the deployment.
-
#bind_releases ⇒ void
Binds release DB record(s) to a plan.
-
#bind_resource_pools ⇒ void
Takes a look at the current state of all resource pools in the deployment and schedules adding any new VMs if needed.
-
#bind_stemcells ⇒ void
Binds stemcell model for each stemcell spec in each resource pool in the deployment plan.
-
#bind_templates ⇒ void
Binds template models for each release spec in the deployment plan.
-
#bind_unallocated_vms ⇒ void
Looks at every job instance in the deployment plan and binds it to the instance database model (idle VM is also created in the appropriate resource pool if necessary).
- #delete_unneeded_instances ⇒ Object
- #delete_unneeded_vms ⇒ Object
- #get_network_reservations(state) ⇒ Object
- #get_state(vm) ⇒ Object
-
#initialize(deployment_plan) ⇒ Assembler
constructor
A new instance of Assembler.
- #migrate_legacy_state(vm, state) ⇒ Object
- #verify_state(vm, state) ⇒ Object
Methods included from IpUtil
#each_ip, #format_ip, #ip_to_i, #ip_to_netaddr, #process_range
Methods included from LockHelper
#with_compile_lock, #with_deployment_lock, #with_release_lock, #with_release_locks, #with_stemcell_lock
Constructor Details
#initialize(deployment_plan) ⇒ Assembler
Returns a new instance of Assembler.
8 9 10 11 12 13 14 15 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 8 def initialize(deployment_plan) @deployment_plan = deployment_plan @cloud = Config.cloud @logger = Config.logger @event_log = Config.event_log @stemcell_manager = Api::StemcellManager.new @blobstore = App.instance.blobstores.blobstore end |
Instance Method Details
#bind_configuration ⇒ void
This method returns an undefined value.
Calculates configuration checksums for all jobs in this deployment plan
320 321 322 323 324 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 320 def bind_configuration @deployment_plan.jobs_starting_on_deploy.each do |job| JobRenderer.new(job, @blobstore).render_job_instances end end |
#bind_deployment ⇒ void
This method returns an undefined value.
Binds deployment DB record to a plan
19 20 21 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 19 def bind_deployment @deployment_plan.bind_model end |
#bind_dns ⇒ Object
326 327 328 329 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 326 def bind_dns binder = DeploymentPlan::DnsBinder.new(@deployment_plan) binder.bind_deployment end |
#bind_existing_deployment ⇒ void
This method returns an undefined value.
Binds information about existing deployment to a plan
35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 35 def bind_existing_deployment lock = Mutex.new ThreadPool.new(:max_threads => Config.max_threads).wrap do |pool| @deployment_plan.vms.each do |vm| pool.process do with_thread_name("bind_existing_deployment(#{vm.agent_id})") do bind_existing_vm(vm, lock) end end end end end |
#bind_existing_vm(vm, lock) ⇒ Object
Queries agent for VM state and updates deployment plan accordingly
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 51 def bind_existing_vm(vm, lock) state = get_state(vm) lock.synchronize do @logger.debug('Processing network reservations') reservations = get_network_reservations(state) instance = vm.instance if instance bind_instance(instance, state, reservations) else @logger.debug('Binding resource pool VM') resource_pool = @deployment_plan.resource_pool( state['resource_pool']['name']) if resource_pool bind_idle_vm(vm, resource_pool, state, reservations) else @logger.debug("Resource pool doesn't exist, marking for deletion") @deployment_plan.delete_vm(vm) end end @logger.debug('Finished binding VM') end end |
#bind_idle_vm(vm, resource_pool, state, reservations) ⇒ Object
Binds idle VM to a resource pool with a proper network reservation
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 80 def bind_idle_vm(vm, resource_pool, state, reservations) @logger.debug('Adding to resource pool') idle_vm = resource_pool.add_idle_vm idle_vm.vm = vm idle_vm.current_state = state reservation = reservations[resource_pool.network.name] if reservation if reservation.static? @logger.debug('Releasing static network reservation for ' + "resource pool VM `#{vm.cid}'") resource_pool.network.release(reservation) else idle_vm.use_reservation(reservation) end else @logger.debug("No network reservation for VM `#{vm.cid}'") end end |
#bind_instance(instance_model, state, reservations) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 103 def bind_instance(instance_model, state, reservations) @logger.debug('Binding instance VM') # Update instance, if we are renaming a job. if @deployment_plan.rename_in_progress? old_name = @deployment_plan.job_rename['old_name'] new_name = @deployment_plan.job_rename['new_name'] if instance_model.job == old_name @logger.info("Renaming `#{old_name}' to `#{new_name}'") instance_model.update(:job => new_name) end end instance_name = "#{instance_model.job}/#{instance_model.index}" job = @deployment_plan.job(instance_model.job) unless job @logger.debug("Job '#{instance_model.job}' not found, marking for deletion") @deployment_plan.delete_instance(instance_model) return end instance = job.instance(instance_model.index) unless instance @logger.debug("Job instance #{instance_name} not found, marking for deletion") @deployment_plan.delete_instance(instance_model) return end @logger.debug("Found job instance #{instance_name}") instance.use_model(instance_model) instance.current_state = state @logger.debug("Copying job instance #{instance_name} network reservations") instance.take_network_reservations(reservations) @logger.debug("Copying job instance #{instance_name} resource pool reservation") job.resource_pool.mark_active_vm end |
#bind_instance_networks ⇒ Object
278 279 280 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 278 def bind_instance_networks @deployment_plan.jobs_starting_on_deploy.each(&:bind_instance_networks) end |
#bind_instance_vms ⇒ Object
331 332 333 334 335 336 337 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 331 def bind_instance_vms jobs = @deployment_plan.jobs_starting_on_deploy instances = jobs.map(&:instances).flatten binder = DeploymentPlan::InstanceVmBinder.new(@event_log) binder.bind_instance_vms(instances) end |
#bind_properties ⇒ void
This method returns an undefined value.
Binds properties for all templates in the deployment
296 297 298 299 300 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 296 def bind_properties @deployment_plan.jobs.each do |job| job.bind_properties end end |
#bind_releases ⇒ void
This method returns an undefined value.
Binds release DB record(s) to a plan
25 26 27 28 29 30 31 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 25 def bind_releases with_release_locks(@deployment_plan) do @deployment_plan.releases.each do |release| release.bind_model end end end |
#bind_resource_pools ⇒ void
This method returns an undefined value.
Takes a look at the current state of all resource pools in the deployment and schedules adding any new VMs if needed. VMs are NOT created at this stage, only data structures are being allocated. ResourcePoolUpdater will later perform actual changes based on this data.
264 265 266 267 268 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 264 def bind_resource_pools @deployment_plan.resource_pools.each do |resource_pool| resource_pool.process_idle_vms end end |
#bind_stemcells ⇒ void
This method returns an undefined value.
Binds stemcell model for each stemcell spec in each resource pool in the deployment plan
305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 305 def bind_stemcells @deployment_plan.resource_pools.each do |resource_pool| stemcell = resource_pool.stemcell if stemcell.nil? raise DirectorError, "Stemcell not bound for resource pool `#{resource_pool.name}'" end stemcell.bind_model end end |
#bind_templates ⇒ void
This method returns an undefined value.
Binds template models for each release spec in the deployment plan
284 285 286 287 288 289 290 291 292 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 284 def bind_templates @deployment_plan.releases.each do |release| release.bind_templates end @deployment_plan.jobs.each do |job| job.validate_package_names_do_not_collide! end end |
#bind_unallocated_vms ⇒ void
This method returns an undefined value.
Looks at every job instance in the deployment plan and binds it to the instance database model (idle VM is also created in the appropriate resource pool if necessary)
274 275 276 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 274 def bind_unallocated_vms @deployment_plan.jobs_starting_on_deploy.each(&:bind_unallocated_vms) end |
#delete_unneeded_instances ⇒ Object
360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 360 def delete_unneeded_instances unneeded_instances = @deployment_plan.unneeded_instances if unneeded_instances.empty? @logger.info('No unneeded instances to delete') return end event_log_stage = @event_log.begin_stage('Deleting unneeded instances', unneeded_instances.size) instance_deleter = InstanceDeleter.new(@deployment_plan) instance_deleter.delete_instances(unneeded_instances, event_log_stage) @logger.info('Deleted no longer needed instances') end |
#delete_unneeded_vms ⇒ Object
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 339 def delete_unneeded_vms unneeded_vms = @deployment_plan.unneeded_vms if unneeded_vms.empty? @logger.info('No unneeded vms to delete') return end @event_log.begin_stage('Deleting unneeded VMs', unneeded_vms.size) ThreadPool.new(:max_threads => Config.max_threads).wrap do |pool| unneeded_vms.each do |vm| pool.process do @event_log.track(vm.cid) do @logger.info("Delete unneeded VM #{vm.cid}") @cloud.delete_vm(vm.cid) vm.destroy end end end end end |
#get_network_reservations(state) ⇒ Object
144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 144 def get_network_reservations(state) reservations = {} state['networks'].each do |name, network_config| network = @deployment_plan.network(name) if network reservation = NetworkReservation.new(:ip => network_config['ip']) network.reserve(reservation) reservations[name] = reservation if reservation.reserved? end end reservations end |
#get_state(vm) ⇒ Object
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 157 def get_state(vm) @logger.debug("Requesting current VM state for: #{vm.agent_id}") agent = AgentClient.with_defaults(vm.agent_id) state = agent.get_state @logger.debug("Received VM state: #{state.pretty_inspect}") verify_state(vm, state) @logger.debug('Verified VM state') migrate_legacy_state(vm, state) state.delete('release') if state.include?('job') state['job'].delete('release') end state end |
#migrate_legacy_state(vm, state) ⇒ Object
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 238 def migrate_legacy_state(vm, state) # Persisting apply spec for VMs that were introduced before we started # persisting it on apply itself (this is for cloudcheck purposes only) if vm.apply_spec.nil? # The assumption is that apply_spec <=> VM state vm.update(:apply_spec => state) end instance = vm.instance if instance disk_size = state['persistent_disk'].to_i persistent_disk = instance.persistent_disk # This is to support legacy deployments where we did not have # the disk_size specified. if disk_size != 0 && persistent_disk && persistent_disk.size == 0 persistent_disk.update(:size => disk_size) end end end |
#verify_state(vm, state) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/bosh/director/deployment_plan/assembler.rb', line 174 def verify_state(vm, state) instance = vm.instance if instance && instance.deployment_id != vm.deployment_id # Both VM and instance should reference same deployment raise VmInstanceOutOfSync, "VM `#{vm.cid}' and instance " + "`#{instance.job}/#{instance.index}' " + "don't belong to the same deployment" end unless state.kind_of?(Hash) @logger.error("Invalid state for `#{vm.cid}': #{state.pretty_inspect}") raise AgentInvalidStateFormat, "VM `#{vm.cid}' returns invalid state: " + "expected Hash, got #{state.class}" end actual_deployment_name = state['deployment'] expected_deployment_name = @deployment_plan.name if actual_deployment_name != expected_deployment_name raise AgentWrongDeployment, "VM `#{vm.cid}' is out of sync: " + 'expected to be a part of deployment ' + "`#{expected_deployment_name}' " + 'but is actually a part of deployment ' + "`#{actual_deployment_name}'" end actual_job = state['job'].is_a?(Hash) ? state['job']['name'] : nil actual_index = state['index'] if instance.nil? && !actual_job.nil? raise AgentUnexpectedJob, "VM `#{vm.cid}' is out of sync: " + "it reports itself as `#{actual_job}/#{actual_index}' but " + 'there is no instance reference in DB' end if instance && (instance.job != actual_job || instance.index != actual_index) # Check if we are resuming a previously unfinished rename if actual_job == @deployment_plan.job_rename['old_name'] && instance.job == @deployment_plan.job_rename['new_name'] && instance.index == actual_index # Rename already happened in the DB but then something happened # and agent has never been updated. unless @deployment_plan.job_rename['force'] raise AgentRenameInProgress, "Found a job `#{actual_job}' that seems to be " + "in the middle of a rename to `#{instance.job}'. " + "Run 'rename' again with '--force' to proceed." end else raise AgentJobMismatch, "VM `#{vm.cid}' is out of sync: " + "it reports itself as `#{actual_job}/#{actual_index}' but " + "according to DB it is `#{instance.job}/#{instance.index}'" end end end |