Class: Bosh::Director::DeploymentPlan::Instance
- Includes:
- Bosh::Director::DnsHelper
- Defined in:
- lib/bosh/director/deployment_plan/instance.rb
Overview
Represents a single job instance.
Constant Summary
Constants included from Bosh::Director::DnsHelper
Bosh::Director::DnsHelper::SOA, Bosh::Director::DnsHelper::TTL_4H, Bosh::Director::DnsHelper::TTL_5M
Instance Attribute Summary collapse
-
#configuration_hash ⇒ String
Checksum all of the configuration templates.
-
#current_state ⇒ Hash
readonly
Current state as provided by the BOSH Agent.
-
#index ⇒ Integer
readonly
Instance index.
-
#job ⇒ DeploymentPlan::Job
readonly
Associated job.
-
#model ⇒ Models::Instance
readonly
Instance model.
-
#network_reservations ⇒ Hash<String, NetworkReservation>
Network reservations.
-
#recreate ⇒ Boolean
True if this instance needs to be recreated.
- #rendered_templates_archive ⇒ Bosh::Director::Core::Templates::RenderedTemplatesArchive
-
#restart ⇒ Boolean
True if this instance needs to be restarted.
-
#state ⇒ String
Job state.
-
#template_hashes ⇒ Hash
A hash of template SHA1 hashes.
-
#vm ⇒ DeploymentPlan::Vm
readonly
Associated resource pool VM.
Instance Method Summary collapse
-
#add_network_reservation(name, reservation) ⇒ Object
Adds a new network to this instance.
-
#allocate_vm ⇒ void
Allocates an VM in this job resource pool and binds current instance to that VM.
- #apply_partial_vm_state ⇒ Object
- #apply_vm_state ⇒ Object
-
#bind_existing_instance(instance_model, state, reservations) ⇒ Object
Updates this domain object to reflect an existing instance running on an existing vm.
-
#bind_model ⇒ void
Looks up a DB model for this instance, creates one if doesn’t exist yet.
-
#bind_unallocated_vm ⇒ void
Looks up instance model in DB and binds it to this instance spec.
-
#changed? ⇒ Boolean
Returns true if the any of the expected specifications differ from the ones provided by the VM.
-
#changes ⇒ Set<Symbol>
Returns a set of all of the specification differences.
-
#configuration_changed? ⇒ Boolean
Returns true if the expected configuration hash differs from the one provided by the VM.
-
#disk_cloud_properties ⇒ Hash
Persistent disk cloud properties.
-
#disk_currently_attached? ⇒ Boolean
Returns true if the persistent disk is attached to the VM.
-
#disk_size ⇒ Integer
Persistent disk size.
-
#dns_changed? ⇒ Boolean
Returns true if the DNS records configured for the instance differ from the ones configured on the DNS server.
-
#dns_record_info ⇒ Hash<String, String>
Dns record hash of dns name and IP.
-
#dns_record_name(network_name) ⇒ String
Dns record name.
-
#find_or_create_model ⇒ Models::Instance
Looks up instance model in DB.
-
#initialize(job, index, logger) ⇒ Instance
constructor
Creates a new instance specification based on the job and index.
-
#job_changed? ⇒ Boolean
Returns true if the expected job configuration differs from the one provided by the VM.
-
#network_settings ⇒ Hash
BOSH network settings used for Agent apply call.
-
#networks_changed? ⇒ Boolean
Returns true if the network configuration changed.
-
#packages_changed? ⇒ Boolean
Returns true if the expected packaged of the running instance differ from the ones provided by the VM.
-
#persistent_disk_changed? ⇒ Boolean
Returns true if the expected persistent disk or cloud_properties differs from the state currently configured on the VM.
-
#resource_pool_changed? ⇒ Boolean
Returns true if the expected resource pool differs from the one provided by the VM.
-
#spec ⇒ Hash<String, Object>
Instance spec that’s passed to the VM during the BOSH Agent apply call.
-
#state_changed? ⇒ Boolean
Checks if agent view of the instance state is consistent with target instance state.
-
#sync_state_with_db ⇒ void
Syncs instance state with instance model in DB.
- #to_s ⇒ Object
Methods included from Bosh::Director::DnsHelper
#add_default_dns_server, #canonical, #default_dns_server, #delete_dns_records, #delete_empty_domain, #dns_domain_name, #dns_ns_record, #dns_servers, #invalid_dns, #reverse_domain, #reverse_host, #update_dns_a_record, #update_dns_ptr_record
Constructor Details
#initialize(job, index, logger) ⇒ Instance
Creates a new instance specification based on the job and index.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 46 def initialize(job, index, logger) @job = job @index = index @logger = logger @model = nil @configuration_hash = nil @template_hashes = nil @vm = nil @current_state = nil @network_reservations = {} @state = job.instance_state(@index) # Expanding virtual states case @state when 'recreate' @recreate = true @state = 'started' when 'restart' @restart = true @state = 'started' end end |
Instance Attribute Details
#configuration_hash ⇒ String
Returns Checksum all of the configuration templates.
17 18 19 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 17 def configuration_hash @configuration_hash end |
#current_state ⇒ Hash (readonly)
Returns current state as provided by the BOSH Agent.
32 33 34 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 32 def current_state @current_state end |
#index ⇒ Integer (readonly)
Returns Instance index.
11 12 13 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 11 def index @index end |
#job ⇒ DeploymentPlan::Job (readonly)
Returns Associated job.
8 9 10 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 8 def job @job end |
#model ⇒ Models::Instance (readonly)
Returns Instance model.
14 15 16 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 14 def model @model end |
#network_reservations ⇒ Hash<String, NetworkReservation>
Returns network reservations.
26 27 28 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 26 def network_reservations @network_reservations end |
#recreate ⇒ Boolean
Returns true if this instance needs to be recreated.
38 39 40 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 38 def recreate @recreate end |
#rendered_templates_archive ⇒ Bosh::Director::Core::Templates::RenderedTemplatesArchive
23 24 25 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 23 def rendered_templates_archive @rendered_templates_archive end |
#restart ⇒ Boolean
Returns true if this instance needs to be restarted.
41 42 43 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 41 def restart @restart end |
#state ⇒ String
Returns job state.
29 30 31 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 29 def state @state end |
#template_hashes ⇒ Hash
Returns A hash of template SHA1 hashes.
20 21 22 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 20 def template_hashes @template_hashes end |
#vm ⇒ DeploymentPlan::Vm (readonly)
Returns Associated resource pool VM.
35 36 37 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 35 def vm @vm end |
Instance Method Details
#add_network_reservation(name, reservation) ⇒ Object
Adds a new network to this instance
181 182 183 184 185 186 187 188 189 190 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 181 def add_network_reservation(name, reservation) old_reservation = @network_reservations[name] if old_reservation raise NetworkReservationAlreadyExists, "`#{self}' already has reservation " + "for network `#{name}', IP #{old_reservation.ip}" end @network_reservations[name] = reservation end |
#allocate_vm ⇒ void
This method returns an undefined value.
Allocates an VM in this job resource pool and binds current instance to that VM.
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 466 def allocate_vm resource_pool = @job.resource_pool vm = resource_pool.allocate_vm network = resource_pool.network if vm.model # There's already a resource pool VM that can become our instance, # so we can try to reuse its reservation instance_reservation = @network_reservations[network.name] if instance_reservation instance_reservation.take(vm.network_reservation) end else # VM is not created yet: let's just make it reference this instance # so later it knows what it needs to become vm.bound_instance = self # this also means we no longer need previous VM network reservation # (instance has its own) vm.release_reservation end @vm = vm end |
#apply_partial_vm_state ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 103 def apply_partial_vm_state @logger.info('Applying partial VM state') state = @vm.current_state state['job'] = job.spec state['index'] = index # Apply the assignment to the VM agent = AgentClient.with_defaults(@vm.model.agent_id) agent.apply(state) # Our assumption here is that director database access # is much less likely to fail than VM agent communication # so we only update database after we see a successful agent apply. # If database update fails subsequent deploy will try to # assign a new VM to this instance which is ok. @vm.model.db.transaction do @vm.model.update(:apply_spec => state) @model.update(:vm => @vm.model) end @current_state = state end |
#apply_vm_state ⇒ Object
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 127 def apply_vm_state @logger.info('Applying VM state') state = { 'deployment' => @job.deployment.name, 'networks' => network_settings, 'resource_pool' => @job.resource_pool.spec, 'job' => @job.spec, 'index' => @index, } if disk_size > 0 state['persistent_disk'] = disk_size end @model.vm.update(:apply_spec => state) agent = AgentClient.with_defaults(@model.vm.agent_id) agent.apply(state) # Agent will potentially return modified version of state # with resolved dynamic networks information @current_state = agent.get_state end |
#bind_existing_instance(instance_model, state, reservations) ⇒ Object
Updates this domain object to reflect an existing instance running on an existing vm
94 95 96 97 98 99 100 101 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 94 def bind_existing_instance(instance_model, state, reservations) raise DirectorError, "Instance `#{self}' model is already bound" if @model @model = instance_model @current_state = state take_network_reservations(reservations) add_allocated_vm(instance_model.vm, state) end |
#bind_model ⇒ void
This method returns an undefined value.
Looks up a DB model for this instance, creates one if doesn’t exist yet.
77 78 79 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 77 def bind_model @model ||= find_or_create_model end |
#bind_unallocated_vm ⇒ void
This method returns an undefined value.
Looks up instance model in DB and binds it to this instance spec. Instance model is created if it’s not found in DB. New VM is allocated if instance DB record doesn’t reference one.
85 86 87 88 89 90 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 85 def bind_unallocated_vm bind_model if @model.vm.nil? allocate_vm end end |
#changed? ⇒ Boolean
Returns true if the any of the expected specifications differ from the ones provided by the VM
384 385 386 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 384 def changed? !changes.empty? end |
#changes ⇒ Set<Symbol>
Returns a set of all of the specification differences
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 390 def changes changes = Set.new unless @state == 'detached' && @current_state.nil? changes << :restart if @restart changes << :resource_pool if resource_pool_changed? changes << :network if networks_changed? changes << :packages if packages_changed? changes << :persistent_disk if persistent_disk_changed? changes << :configuration if configuration_changed? changes << :job if job_changed? changes << :state if state_changed? changes << :dns if dns_changed? end changes end |
#configuration_changed? ⇒ Boolean
Returns true if the expected configuration hash differs from the one provided by the VM
317 318 319 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 317 def configuration_changed? configuration_hash != @current_state['configuration_hash'] end |
#disk_cloud_properties ⇒ Hash
Returns persistent disk cloud properties.
251 252 253 254 255 256 257 258 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 251 def disk_cloud_properties disk_pool = current_state['persistent_disk_pool'] if disk_pool disk_pool['cloud_properties'] else {} end end |
#disk_currently_attached? ⇒ Boolean
Returns true if the persistent disk is attached to the VM
279 280 281 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 279 def disk_currently_attached? current_state['persistent_disk'].to_i > 0 end |
#disk_size ⇒ Integer
Returns persistent disk size.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 234 def disk_size if @model.nil? disk_pool = current_state['persistent_disk_pool'] if disk_pool disk_pool['disk_size'].to_i else current_state['persistent_disk'].to_i end elsif @model.persistent_disk @model.persistent_disk.size else 0 end end |
#dns_changed? ⇒ Boolean
Returns true if the DNS records configured for the instance differ from the ones configured on the DNS server
356 357 358 359 360 361 362 363 364 365 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 356 def dns_changed? if Config.dns_enabled? dns_record_info.any? do |name, ip| Models::Dns::Record.find(:name => name, :type => 'A', :content => ip).nil? end else false end end |
#dns_record_info ⇒ Hash<String, String>
Returns dns record hash of dns name and IP.
262 263 264 265 266 267 268 269 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 262 def dns_record_info dns_record_info = {} network_settings.each do |network_name, network| name = dns_record_name(network_name) dns_record_info[name] = network['ip'] end dns_record_info end |
#dns_record_name(network_name) ⇒ String
Returns dns record name.
273 274 275 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 273 def dns_record_name(network_name) [index, job.canonical_name, canonical(network_name), job.deployment.canonical_name, dns_domain_name].join('.') end |
#find_or_create_model ⇒ Models::Instance
Looks up instance model in DB
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 448 def find_or_create_model if @job.deployment.model.nil? raise DirectorError, 'Deployment model is not bound' end conditions = { deployment_id: @job.deployment.model.id, job: @job.name, index: @index } Models::Instance.find_or_create(conditions) do |model| model.state = 'started' end end |
#job_changed? ⇒ Boolean
Returns true if the expected job configuration differs from the one provided by the VM
324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 324 def job_changed? return true if @current_state.nil? job_spec = @job.spec if job_spec != @current_state['job'] # The agent job spec could be in legacy form. job_spec cannot be, # though, because we got it from the spec function in job.rb which # automatically makes it non-legacy. return job_spec != Job.convert_from_legacy_spec(@current_state['job']) end return false end |
#network_settings ⇒ Hash
Returns BOSH network settings used for Agent apply call.
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 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 194 def network_settings default_properties = {} @job.default_network.each do |key, value| (default_properties[value] ||= []) << key end network_settings = {} @network_reservations.each do |name, reservation| network = @job.deployment.network(name) network_settings[name] = network.network_settings(reservation, default_properties[name]) # Temporary hack for running errands. # We need to avoid RunErrand task thinking that # network configuration for errand VM differs # from network configuration for its Instance. # # Obviously this does not account for other changes # in network configuration that errand job might need. # (e.g. errand job desires static ip) if @job.starts_on_deploy? network_settings[name]['dns_record_name'] = dns_record_name(name) end # Somewhat of a hack: for dynamic networks we might know IP address, Netmask & Gateway # if they're featured in agent state, in that case we put them into network spec to satisfy # ConfigurationHasher in both agent and director. if @current_state.is_a?(Hash) && @current_state['networks'].is_a?(Hash) && @current_state['networks'][name].is_a?(Hash) && network_settings[name]['type'] == 'dynamic' %w(ip netmask gateway).each do |key| network_settings[name][key] = @current_state['networks'][name][key] end end end network_settings end |
#networks_changed? ⇒ Boolean
Returns true if the network configuration changed
285 286 287 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 285 def networks_changed? network_settings != @current_state['networks'] end |
#packages_changed? ⇒ Boolean
Returns true if the expected packaged of the running instance differ from the ones provided by the VM
340 341 342 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 340 def packages_changed? @job.package_spec != @current_state['packages'] end |
#persistent_disk_changed? ⇒ Boolean
Returns true if the expected persistent disk or cloud_properties differs from the state currently configured on the VM
347 348 349 350 351 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 347 def persistent_disk_changed? new_disk_size = @job.persistent_disk_pool ? @job.persistent_disk_pool.disk_size : 0 new_disk_cloud_properties = @job.persistent_disk_pool ? @job.persistent_disk_pool.cloud_properties : {} new_disk_size != disk_size || new_disk_cloud_properties != disk_cloud_properties end |
#resource_pool_changed? ⇒ Boolean
Returns true if the expected resource pool differs from the one provided by the VM
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 291 def resource_pool_changed? if @recreate || @job.deployment.recreate return true end if @job.resource_pool.spec != @current_state['resource_pool'] return true end # env is not a part of a resource pool spec but rather gets persisted # in director DB, hence the check below # NOTE: we only update VMs that have env persisted to avoid recreating # everything, so if the director gets updated from the version that # doesn't persist VM env to the version that does, there needs to # be at least one deployment that recreates all VMs before the following # code path gets exercised. if @model && @model.vm && @model.vm.env && @job.resource_pool.env != @model.vm.env return true end false end |
#spec ⇒ Hash<String, Object>
Instance spec that’s passed to the VM during the BOSH Agent apply call. It’s what’s used for comparing the expected vs the actual state.
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 410 def spec spec = { 'deployment' => @job.deployment.name, 'job' => job.spec, 'index' => index, 'networks' => network_settings, 'resource_pool' => job.resource_pool.spec, 'packages' => job.package_spec, 'configuration_hash' => configuration_hash, 'properties' => job.properties, 'dns_domain_name' => dns_domain_name } if job.persistent_disk_pool # supply both for reverse compatibility with old agent spec['persistent_disk'] = job.persistent_disk_pool.disk_size # old agents will ignore this pool spec['persistent_disk_pool'] = job.persistent_disk_pool.spec else spec['persistent_disk'] = 0 end if template_hashes spec['template_hashes'] = template_hashes end # Ruby BOSH Agent does not look at 'rendered_templates_archive' # since it renders job templates and then compares template hashes. # Go BOSH Agent has no ability to render ERB so pre-rendered templates are provided. if rendered_templates_archive spec['rendered_templates_archive'] = rendered_templates_archive.spec end spec end |
#state_changed? ⇒ Boolean
Checks if agent view of the instance state is consistent with target instance state.
In case the instance current state is ‘detached’ we should never get to this method call.
375 376 377 378 379 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 375 def state_changed? @state == 'detached' || @state == 'started' && @current_state['job_state'] != 'running' || @state == 'stopped' && @current_state['job_state'] == 'running' end |
#sync_state_with_db ⇒ void
This method returns an undefined value.
Syncs instance state with instance model in DB. This is needed because not all instance states are available in the deployment manifest and we we cannot really persist this data in the agent state (as VM might be stopped or detached).
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 158 def sync_state_with_db if @model.nil? raise DirectorError, "Instance `#{self}' model is not bound" end if @state # Deployment plan explicitly sets state for this instance @model.update(:state => @state) elsif @model.state # Instance has its state persisted from the previous deployment @state = @model.state else # Target instance state should either be persisted in DB or provided # via deployment plan, otherwise something is really wrong raise InstanceTargetStateUndefined, "Instance `#{self}' target state cannot be determined" end end |
#to_s ⇒ Object
71 72 73 |
# File 'lib/bosh/director/deployment_plan/instance.rb', line 71 def to_s "#{@job.name}/#{@index}" end |