bosh-director 1.3160.0 → 1.3163.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bosh-director-worker +2 -14
  3. data/db/migrations/director/20150513225143_ip_addresses.rb +11 -0
  4. data/db/migrations/director/20150702004608_add_links.rb +8 -0
  5. data/db/migrations/director/20150708231924_add_link_spec.rb +7 -0
  6. data/db/migrations/director/20150724183256_add_debugging_to_ip_addresses.rb +8 -0
  7. data/db/migrations/director/20150730225029_add_uuid_to_instances.rb +16 -0
  8. data/db/migrations/director/20150803215805_add_availabililty_zone_and_cloud_properties_to_instances.rb +8 -0
  9. data/db/migrations/director/20150804211419_add_compilation_flag_to_instance.rb +7 -0
  10. data/db/migrations/director/20150918003455_add_bootstrap_node_to_instance.rb +7 -0
  11. data/db/migrations/director/20151008232214_add_dns_records.rb +7 -0
  12. data/db/migrations/director/20151015172551_add_orphan_disks_and_snapshots.rb +29 -0
  13. data/db/migrations/director/20151030222853_add_templates_to_instance.rb +10 -0
  14. data/db/migrations/director/20151031001039_add_spec_to_instance.rb +19 -0
  15. data/db/migrations/director/20151109190602_rename_orphan_columns.rb +13 -0
  16. data/lib/bosh/director.rb +19 -9
  17. data/lib/bosh/director/agent_client.rb +0 -17
  18. data/lib/bosh/director/api/cloud_config_manager.rb +7 -5
  19. data/lib/bosh/director/api/controllers/base_controller.rb +3 -2
  20. data/lib/bosh/director/api/controllers/cleanup_controller.rb +15 -0
  21. data/lib/bosh/director/api/controllers/deployments_controller.rb +38 -26
  22. data/lib/bosh/director/api/controllers/disks_controller.rb +20 -0
  23. data/lib/bosh/director/api/controllers/info_controller.rb +2 -2
  24. data/lib/bosh/director/api/controllers/releases_controller.rb +1 -16
  25. data/lib/bosh/director/api/controllers/stemcells_controller.rb +1 -9
  26. data/lib/bosh/director/api/deployment_manager.rb +2 -1
  27. data/lib/bosh/director/api/instance_lookup.rb +17 -0
  28. data/lib/bosh/director/api/instance_manager.rb +20 -10
  29. data/lib/bosh/director/api/release_manager.rb +28 -8
  30. data/lib/bosh/director/api/resurrector_manager.rb +9 -2
  31. data/lib/bosh/director/api/route_configuration.rb +2 -0
  32. data/lib/bosh/director/api/snapshot_manager.rb +9 -5
  33. data/lib/bosh/director/api/stemcell_manager.rb +50 -0
  34. data/lib/bosh/director/app.rb +1 -1
  35. data/lib/bosh/director/cloudcheck_helper.rb +119 -132
  36. data/lib/bosh/director/compile_task.rb +1 -1
  37. data/lib/bosh/director/compile_task_generator.rb +2 -2
  38. data/lib/bosh/director/config.rb +21 -12
  39. data/lib/bosh/director/deployment_deleter.rb +69 -0
  40. data/lib/bosh/director/deployment_plan.rb +35 -4
  41. data/lib/bosh/director/deployment_plan/agent_state_migrator.rb +47 -0
  42. data/lib/bosh/director/deployment_plan/assembler.rb +115 -241
  43. data/lib/bosh/director/deployment_plan/availability_zone.rb +27 -0
  44. data/lib/bosh/director/deployment_plan/cloud_manifest_parser.rb +144 -35
  45. data/lib/bosh/director/deployment_plan/compilation_config.rb +21 -19
  46. data/lib/bosh/director/deployment_plan/compilation_instance_pool.rb +169 -0
  47. data/lib/bosh/director/deployment_plan/deployment_repo.rb +4 -8
  48. data/lib/bosh/director/deployment_plan/deployment_spec_parser.rb +13 -1
  49. data/lib/bosh/director/deployment_plan/deployment_validator.rb +17 -0
  50. data/lib/bosh/director/deployment_plan/desired_instance.rb +15 -0
  51. data/lib/bosh/director/deployment_plan/{disk_pool.rb → disk_type.rb} +14 -19
  52. data/lib/bosh/director/deployment_plan/dynamic_network.rb +105 -53
  53. data/lib/bosh/director/deployment_plan/dynamic_network_subnet.rb +13 -0
  54. data/lib/bosh/director/deployment_plan/env.rb +18 -0
  55. data/lib/bosh/director/deployment_plan/global_network_resolver.rb +77 -0
  56. data/lib/bosh/director/deployment_plan/instance.rb +222 -390
  57. data/lib/bosh/director/deployment_plan/instance_network_reservations.rb +71 -0
  58. data/lib/bosh/director/deployment_plan/instance_plan.rb +336 -0
  59. data/lib/bosh/director/deployment_plan/instance_plan_factory.rb +54 -0
  60. data/lib/bosh/director/deployment_plan/instance_plan_sorter.rb +61 -0
  61. data/lib/bosh/director/deployment_plan/instance_planner.rb +101 -0
  62. data/lib/bosh/director/deployment_plan/instance_repository.rb +36 -0
  63. data/lib/bosh/director/deployment_plan/instance_spec.rb +154 -0
  64. data/lib/bosh/director/deployment_plan/ip_provider/database_ip_repo.rb +136 -0
  65. data/lib/bosh/director/deployment_plan/ip_provider/in_memory_ip_repo.rb +81 -0
  66. data/lib/bosh/director/deployment_plan/ip_provider/ip_provider.rb +153 -0
  67. data/lib/bosh/director/deployment_plan/ip_provider/ip_provider_factory.rb +22 -0
  68. data/lib/bosh/director/deployment_plan/job.rb +116 -53
  69. data/lib/bosh/director/deployment_plan/job_availability_zone_parser.rb +49 -0
  70. data/lib/bosh/director/deployment_plan/job_migrator.rb +90 -0
  71. data/lib/bosh/director/deployment_plan/job_network.rb +42 -0
  72. data/lib/bosh/director/deployment_plan/job_network_parser.rb +118 -0
  73. data/lib/bosh/director/deployment_plan/job_spec_parser.rb +123 -126
  74. data/lib/bosh/director/deployment_plan/links/link.rb +30 -0
  75. data/lib/bosh/director/deployment_plan/links/link_lookup.rb +66 -0
  76. data/lib/bosh/director/deployment_plan/links/link_path.rb +27 -0
  77. data/lib/bosh/director/deployment_plan/links/links_resolver.rb +70 -0
  78. data/lib/bosh/director/deployment_plan/links/template_link.rb +21 -0
  79. data/lib/bosh/director/deployment_plan/manifest_migrator.rb +3 -17
  80. data/lib/bosh/director/deployment_plan/manifest_validator.rb +46 -0
  81. data/lib/bosh/director/deployment_plan/manual_network.rb +70 -97
  82. data/lib/bosh/director/deployment_plan/manual_network_subnet.rb +148 -0
  83. data/lib/bosh/director/deployment_plan/network.rb +50 -39
  84. data/lib/bosh/director/deployment_plan/network_planner.rb +4 -0
  85. data/lib/bosh/director/deployment_plan/network_planner/plan.rb +26 -0
  86. data/lib/bosh/director/deployment_plan/network_planner/planner.rb +21 -0
  87. data/lib/bosh/director/deployment_plan/network_planner/reservation_reconciler.rb +81 -0
  88. data/lib/bosh/director/deployment_plan/network_planner/vip_static_ips_planner.rb +50 -0
  89. data/lib/bosh/director/deployment_plan/network_settings.rb +65 -0
  90. data/lib/bosh/director/deployment_plan/options/skip_drain.rb +7 -0
  91. data/lib/bosh/director/deployment_plan/package_validator.rb +79 -0
  92. data/lib/bosh/director/deployment_plan/placement_planner.rb +8 -0
  93. data/lib/bosh/director/deployment_plan/placement_planner/availability_zone_picker.rb +90 -0
  94. data/lib/bosh/director/deployment_plan/placement_planner/bruteforce_ip_allocation.rb +124 -0
  95. data/lib/bosh/director/deployment_plan/placement_planner/index_assigner.rb +32 -0
  96. data/lib/bosh/director/deployment_plan/placement_planner/networks_to_static_ips.rb +125 -0
  97. data/lib/bosh/director/deployment_plan/placement_planner/placed_desired_instances.rb +40 -0
  98. data/lib/bosh/director/deployment_plan/placement_planner/plan.rb +42 -0
  99. data/lib/bosh/director/deployment_plan/placement_planner/static_ips_availability_zone_picker.rb +237 -0
  100. data/lib/bosh/director/deployment_plan/placement_planner/unplaced_existing_instances.rb +53 -0
  101. data/lib/bosh/director/deployment_plan/planner.rb +186 -74
  102. data/lib/bosh/director/deployment_plan/planner_factory.rb +30 -147
  103. data/lib/bosh/director/deployment_plan/release_version.rb +3 -3
  104. data/lib/bosh/director/deployment_plan/resource_pool.rb +2 -174
  105. data/lib/bosh/director/deployment_plan/stemcell.rb +57 -14
  106. data/lib/bosh/director/deployment_plan/steps/package_compile_step.rb +28 -135
  107. data/lib/bosh/director/deployment_plan/steps/update_step.rb +23 -44
  108. data/lib/bosh/director/deployment_plan/template.rb +15 -4
  109. data/lib/bosh/director/deployment_plan/vip_network.rb +14 -42
  110. data/lib/bosh/director/deployment_plan/vm.rb +1 -99
  111. data/lib/bosh/director/deployment_plan/vm_type.rb +27 -0
  112. data/lib/bosh/director/disk_manager.rb +268 -0
  113. data/lib/bosh/director/dns/canonicalizer.rb +28 -0
  114. data/lib/bosh/director/dns/dns_manager.rb +163 -0
  115. data/lib/bosh/director/dns/local_dns_repo.rb +20 -0
  116. data/lib/bosh/director/dns/powerdns.rb +170 -0
  117. data/lib/bosh/director/errand/job_manager.rb +18 -29
  118. data/lib/bosh/director/error_ignorer.rb +16 -0
  119. data/lib/bosh/director/errors.rb +51 -20
  120. data/lib/bosh/director/event_log.rb +6 -0
  121. data/lib/bosh/director/instance_deleter.rb +53 -81
  122. data/lib/bosh/director/instance_reuser.rb +89 -0
  123. data/lib/bosh/director/instance_updater.rb +139 -281
  124. data/lib/bosh/director/instance_updater/preparer.rb +8 -5
  125. data/lib/bosh/director/instance_updater/state_applier.rb +21 -0
  126. data/lib/bosh/director/ip_util.rb +46 -26
  127. data/lib/bosh/director/job_renderer.rb +22 -10
  128. data/lib/bosh/director/job_runner.rb +1 -4
  129. data/lib/bosh/director/job_updater.rb +47 -35
  130. data/lib/bosh/director/job_updater_factory.rb +5 -4
  131. data/lib/bosh/director/jobs/base_job.rb +8 -0
  132. data/lib/bosh/director/jobs/cleanup_artifacts.rb +93 -0
  133. data/lib/bosh/director/jobs/delete_deployment.rb +10 -154
  134. data/lib/bosh/director/jobs/delete_deployment_snapshots.rb +1 -1
  135. data/lib/bosh/director/jobs/delete_orphan_disks.rb +44 -0
  136. data/lib/bosh/director/jobs/delete_release.rb +19 -196
  137. data/lib/bosh/director/jobs/delete_stemcell.rb +10 -76
  138. data/lib/bosh/director/jobs/export_release.rb +41 -121
  139. data/lib/bosh/director/jobs/fetch_logs.rb +0 -6
  140. data/lib/bosh/director/jobs/helpers.rb +10 -0
  141. data/lib/bosh/director/jobs/helpers/blob_deleter.rb +24 -0
  142. data/lib/bosh/director/jobs/helpers/compiled_package_deleter.rb +24 -0
  143. data/lib/bosh/director/jobs/helpers/name_version_release_deleter.rb +48 -0
  144. data/lib/bosh/director/jobs/helpers/package_deleter.rb +33 -0
  145. data/lib/bosh/director/jobs/helpers/release_deleter.rb +52 -0
  146. data/lib/bosh/director/jobs/helpers/release_version_deleter.rb +115 -0
  147. data/lib/bosh/director/jobs/helpers/releases_to_delete_picker.rb +31 -0
  148. data/lib/bosh/director/jobs/helpers/stemcell_deleter.rb +61 -0
  149. data/lib/bosh/director/jobs/helpers/stemcells_to_delete_picker.rb +30 -0
  150. data/lib/bosh/director/jobs/helpers/template_deleter.rb +20 -0
  151. data/lib/bosh/director/jobs/release/release_job.rb +18 -7
  152. data/lib/bosh/director/jobs/run_errand.rb +57 -36
  153. data/lib/bosh/director/jobs/scheduled_orphan_cleanup.rb +46 -0
  154. data/lib/bosh/director/jobs/ssh.rb +50 -17
  155. data/lib/bosh/director/jobs/update_deployment.rb +29 -11
  156. data/lib/bosh/director/jobs/update_release.rb +25 -4
  157. data/lib/bosh/director/jobs/vm_state.rb +23 -32
  158. data/lib/bosh/director/lock.rb +13 -8
  159. data/lib/bosh/director/logs_fetcher.rb +1 -1
  160. data/lib/bosh/director/models.rb +3 -0
  161. data/lib/bosh/director/models/compiled_package.rb +3 -3
  162. data/lib/bosh/director/models/deployment.rb +10 -0
  163. data/lib/bosh/director/models/instance.rb +77 -1
  164. data/lib/bosh/director/models/ip_address.rb +26 -0
  165. data/lib/bosh/director/models/orphan_disk.rb +23 -0
  166. data/lib/bosh/director/models/orphan_snapshot.rb +14 -0
  167. data/lib/bosh/director/models/template.rb +32 -9
  168. data/lib/bosh/director/models/vm.rb +5 -8
  169. data/lib/bosh/director/network_reservation.rb +69 -99
  170. data/lib/bosh/director/problem_handlers/inactive_disk.rb +5 -20
  171. data/lib/bosh/director/problem_handlers/missing_disk.rb +2 -13
  172. data/lib/bosh/director/problem_resolver.rb +2 -2
  173. data/lib/bosh/director/problem_scanner/vm_scan_stage.rb +2 -21
  174. data/lib/bosh/director/scheduler.rb +23 -6
  175. data/lib/bosh/director/{instance_updater/stopper.rb → stopper.rb} +24 -18
  176. data/lib/bosh/director/tagged_logger.rb +30 -0
  177. data/lib/bosh/director/transactor.rb +9 -0
  178. data/lib/bosh/director/version.rb +1 -1
  179. data/lib/bosh/director/vm_creator.rb +91 -19
  180. data/lib/bosh/director/vm_deleter.rb +25 -0
  181. data/lib/bosh/director/vm_recreator.rb +15 -0
  182. data/lib/cloud/dummy.rb +381 -94
  183. metadata +110 -30
  184. data/lib/bosh/director/deployment_plan/dns_binder.rb +0 -45
  185. data/lib/bosh/director/deployment_plan/instance_vm_binder.rb +0 -37
  186. data/lib/bosh/director/deployment_plan/network_subnet.rb +0 -166
  187. data/lib/bosh/director/deployment_plan/resource_pools.rb +0 -68
  188. data/lib/bosh/director/dns_helper.rb +0 -223
  189. data/lib/bosh/director/instance_updater/network_updater.rb +0 -110
  190. data/lib/bosh/director/instance_updater/vm_updater.rb +0 -189
  191. data/lib/bosh/director/problem_handlers/out_of_sync_vm.rb +0 -64
  192. data/lib/bosh/director/problem_handlers/unbound_instance_vm.rb +0 -85
  193. data/lib/bosh/director/resource_pool_updater.rb +0 -174
  194. data/lib/bosh/director/vm_data.rb +0 -63
  195. data/lib/bosh/director/vm_reuser.rb +0 -63
@@ -0,0 +1,65 @@
1
+ module Bosh::Director::DeploymentPlan
2
+ class NetworkSettings
3
+ def initialize(job_name, deployment_name, default_network, desired_reservations, state, availability_zone, instance_index, instance_id, dns_manager)
4
+ @job_name = job_name
5
+ @desired_reservations = desired_reservations
6
+ @default_network = default_network
7
+ @deployment_name = deployment_name
8
+ @state = state
9
+ @availability_zone = availability_zone
10
+ @instance_index = instance_index
11
+ @instance_id = instance_id
12
+ @dns_manager = dns_manager
13
+ end
14
+
15
+ def to_hash
16
+ default_properties = {}
17
+ @default_network.each do |key, value|
18
+ (default_properties[value] ||= []) << key
19
+ end
20
+
21
+ network_settings = {}
22
+ @desired_reservations.each do |reservation|
23
+ network_name = reservation.network.name
24
+ network_settings[network_name] = reservation.network.network_settings(reservation, default_properties[network_name], @availability_zone)
25
+
26
+ # Somewhat of a hack: for dynamic networks we might know IP address, Netmask & Gateway
27
+ # if they're featured in agent state, in that case we put them into network spec to satisfy
28
+ # ConfigurationHasher in both agent and director.
29
+ if @state.is_a?(Hash) &&
30
+ @state['networks'].is_a?(Hash) &&
31
+ @state['networks'][network_name].is_a?(Hash) &&
32
+ network_settings[network_name]['type'] == 'dynamic'
33
+ %w(ip netmask gateway).each do |key|
34
+ network_settings[network_name][key] = @state['networks'][network_name][key]
35
+ end
36
+ end
37
+ end
38
+
39
+ network_settings
40
+ end
41
+
42
+ def dns_record_info
43
+ dns_record_info = {}
44
+ to_hash.each do |network_name, network|
45
+ index_dns_name = @dns_manager.dns_record_name(@instance_index, @job_name, network_name, @deployment_name)
46
+ dns_record_info[index_dns_name] = network['ip']
47
+ id_dns_name = @dns_manager.dns_record_name(@instance_id, @job_name, network_name, @deployment_name)
48
+ dns_record_info[id_dns_name] = network['ip']
49
+ end
50
+ dns_record_info
51
+ end
52
+
53
+ def network_addresses
54
+ network_addresses = {}
55
+ to_hash.each do |network_name, network|
56
+ network_addresses[network_name] = {
57
+ 'address' => network['type'] == 'dynamic' ?
58
+ @dns_manager.dns_record_name(@instance_id, @job_name, network_name, @deployment_name) :
59
+ network['ip']
60
+ }
61
+ end
62
+ network_addresses
63
+ end
64
+ end
65
+ end
@@ -16,5 +16,12 @@ module Bosh::Director
16
16
  @all || @jobs.include?(job_name)
17
17
  end
18
18
  end
19
+
20
+ class AlwaysSkipDrain
21
+ def for_job(_)
22
+ true
23
+ end
24
+ end
25
+
19
26
  end
20
27
  end
@@ -0,0 +1,79 @@
1
+ module Bosh::Director
2
+ module DeploymentPlan
3
+ class PackageValidator
4
+ def initialize(logger)
5
+ @faults = {}
6
+ @logger = logger
7
+ end
8
+
9
+ def validate(release_version_model, stemcel_model)
10
+ release_desc = "#{release_version_model.release.name}/#{release_version_model.version}"
11
+
12
+ @logger.debug("Validating packages for release '#{release_desc}'")
13
+ release_version_model.packages.each do |package|
14
+ packages_list = release_version_model.transitive_dependencies(package)
15
+ packages_list << package
16
+ packages_list.each do |needed_package|
17
+ if needed_package.sha1.nil? || needed_package.blobstore_id.nil?
18
+ compiled_packages_list = Bosh::Director::Models::CompiledPackage[:package_id => needed_package.id, :stemcell_id => stemcel_model.id]
19
+ if compiled_packages_list.nil?
20
+ @faults[release_desc] ||= Set.new
21
+ @faults[release_desc] << Fault.new(needed_package, stemcel_model)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def handle_faults
29
+ return if @faults.empty?
30
+
31
+ msg = "\n"
32
+ unique_stemcells = @faults.map { |_, faults| faults.map { |fault| fault.stemcell } }.flatten.to_set
33
+
34
+ @faults.each do |release_desc, faults|
35
+ if unique_stemcells.size > 1
36
+ msg += message_for_multiple_stemcells(release_desc, faults)
37
+ else
38
+ msg += message_for_single_stemcell(release_desc, faults)
39
+ end
40
+ end
41
+
42
+ raise PackageMissingSourceCode, msg
43
+ end
44
+
45
+ private
46
+
47
+ def message_for_multiple_stemcells(release_desc, faults)
48
+ msg = "Can't use release '#{release_desc}'. It references packages without source code and are not compiled against intended stemcells:\n"
49
+ sorted_faults = faults.to_a.sort_by { |fault| fault.package.name }
50
+ sorted_faults.each do |fault|
51
+ msg += " - '#{fault.package_desc}' against stemcell '#{fault.stemcell.desc}'\n"
52
+ end
53
+
54
+ msg
55
+ end
56
+
57
+ def message_for_single_stemcell(release_desc, faults)
58
+ sorted_faults = faults.to_a.sort_by { |fault| fault.package.name }
59
+ stemcell_desc = faults.first.stemcell.desc
60
+
61
+ msg = "Can't use release '#{release_desc}'. It references packages without" +
62
+ " source code and are not compiled against stemcell '#{stemcell_desc}':\n"
63
+ sorted_faults.each do |fault|
64
+ msg += " - '#{fault.package_desc}'\n"
65
+ end
66
+
67
+ msg
68
+ end
69
+
70
+ private
71
+
72
+ class Fault < Struct.new(:package, :stemcell)
73
+ def package_desc
74
+ "#{package.name}/#{package.version}"
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,8 @@
1
+ require 'bosh/director/deployment_plan/placement_planner/plan'
2
+ require 'bosh/director/deployment_plan/placement_planner/index_assigner'
3
+ require 'bosh/director/deployment_plan/placement_planner/availability_zone_picker'
4
+ require 'bosh/director/deployment_plan/placement_planner/static_ips_availability_zone_picker'
5
+ require 'bosh/director/deployment_plan/placement_planner/networks_to_static_ips'
6
+ require 'bosh/director/deployment_plan/placement_planner/bruteforce_ip_allocation'
7
+ require 'bosh/director/deployment_plan/placement_planner/placed_desired_instances'
8
+ require 'bosh/director/deployment_plan/placement_planner/unplaced_existing_instances'
@@ -0,0 +1,90 @@
1
+ module Bosh
2
+ module Director
3
+ module DeploymentPlan
4
+ module PlacementPlanner
5
+ class AvailabilityZonePicker
6
+ def initialize(instance_plan_factory, network_planner, networks, desired_azs)
7
+ @instance_plan_factory = instance_plan_factory
8
+ @network_planner = network_planner
9
+ @networks = networks
10
+ @desired_azs = desired_azs
11
+ @logger = Config.logger
12
+ end
13
+
14
+ def place_and_match_in(desired_instances, existing_instance_models)
15
+ unplaced_existing_instances = UnplacedExistingInstances.new(existing_instance_models)
16
+ desired_azs_sorted = unplaced_existing_instances.azs_sorted_by_existing_instance_count_descending(@desired_azs)
17
+ @logger.debug("Desired azs: #{desired_azs_sorted.inspect}")
18
+ placed_instances = PlacedDesiredInstances.new(desired_azs_sorted)
19
+
20
+ remaining_desired_instances = place_instances_that_have_persistent_disk_in_existing_az(desired_azs_sorted, desired_instances, placed_instances, unplaced_existing_instances)
21
+ balance_across_desired_azs(remaining_desired_instances, placed_instances, unplaced_existing_instances)
22
+
23
+ obsolete_instance_plans(unplaced_existing_instances.unclaimed) +
24
+ desired_existing_instance_plans(placed_instances.existing) +
25
+ desired_new_instance_plans(placed_instances.absent)
26
+ end
27
+
28
+ private
29
+
30
+ def place_instances_that_have_persistent_disk_in_existing_az(desired_azs, desired_instances, placed_instances, unplaced_existing_instances)
31
+ desired_instances = desired_instances.dup
32
+ return desired_instances if desired_azs.nil?
33
+ unplaced_existing_instances.instances_with_persistent_disk.each do |existing_instance|
34
+ break if desired_instances.empty?
35
+ az = desired_azs.find { |az| az.name == existing_instance.availability_zone }
36
+ next if az.nil?
37
+ desired_instance = desired_instances.pop
38
+ unplaced_existing_instances.claim_instance(existing_instance)
39
+ placed_instances.record_placement(az, desired_instance, existing_instance)
40
+ end
41
+ desired_instances
42
+ end
43
+
44
+ def balance_across_desired_azs(desired_instances, placed_instances, unplaced_existing_instances)
45
+ desired_instances.each do |desired_instance|
46
+ azs_with_fewest_placed = placed_instances.azs_with_fewest_instances
47
+ @logger.debug("azs with fewest placed: #{azs_with_fewest_placed.inspect}")
48
+ az = unplaced_existing_instances.azs_sorted_by_existing_instance_count_descending(azs_with_fewest_placed).first
49
+ @logger.debug("az: #{az.inspect}")
50
+
51
+ existing_instance = unplaced_existing_instances.claim_instance_for_az(az)
52
+ placed_instances.record_placement(az, desired_instance, existing_instance)
53
+ end
54
+ end
55
+
56
+ def obsolete_instance_plans(obsolete_instances)
57
+ obsolete_instances.map do |existing_instance|
58
+ @instance_plan_factory.obsolete_instance_plan(existing_instance)
59
+ end
60
+ end
61
+
62
+ def desired_existing_instance_plans(desired_existing_instances)
63
+ desired_existing_instances.map do |desired_existing_instance|
64
+ instance_plan = @instance_plan_factory.desired_existing_instance_plan(
65
+ desired_existing_instance[:existing_instance_model],
66
+ desired_existing_instance[:desired_instance]
67
+ )
68
+ populate_network_plans(instance_plan)
69
+ instance_plan
70
+ end
71
+ end
72
+
73
+ def desired_new_instance_plans(new_desired_instances)
74
+ new_desired_instances.map do |desired_instance|
75
+ instance_plan = @instance_plan_factory.desired_new_instance_plan(desired_instance)
76
+ populate_network_plans(instance_plan)
77
+ instance_plan
78
+ end
79
+ end
80
+
81
+ def populate_network_plans(instance_plan)
82
+ @networks.each do |network|
83
+ instance_plan.network_plans << @network_planner.network_plan_with_dynamic_reservation(instance_plan, network)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,124 @@
1
+ require 'common/deep_copy'
2
+
3
+ module Bosh
4
+ module Director
5
+ module DeploymentPlan
6
+ module PlacementPlanner
7
+ class BruteForceIpAllocation
8
+ def initialize(networks_to_static_ips)
9
+ @networks_to_static_ips = networks_to_static_ips
10
+ end
11
+
12
+ def find_best_combination
13
+ allocated_ips = AllocatedIps.new
14
+ try_combination(@networks_to_static_ips, allocated_ips)
15
+ end
16
+
17
+ private
18
+
19
+ def try_combination(networks_to_static_ips, allocated_ips)
20
+ if all_ips_belong_to_single_az(networks_to_static_ips)
21
+ if even_distribution_of_ips?(networks_to_static_ips)
22
+ return networks_to_static_ips
23
+ else
24
+ return nil
25
+ end
26
+ end
27
+
28
+ previous_network = nil
29
+ networks_to_static_ips.each do |network, static_ips_to_azs|
30
+ if previous_network
31
+ previous_assignment = PreviousAssignment.new(networks_to_static_ips[previous_network])
32
+ unless previous_assignment.has_same_distribution?(static_ips_to_azs)
33
+ return nil
34
+ end
35
+ end
36
+ previous_network = network
37
+
38
+ static_ips_to_azs.each_with_index do |static_ip_to_azs|
39
+ if static_ip_to_azs.az_names.size == 1
40
+ allocated_ips.allocate(static_ip_to_azs.az_names.first)
41
+ next
42
+ end
43
+
44
+ # prioritize AZs based on least number of allocated IPs
45
+ sorted_az_names = allocated_ips.sort_by_least_allocated_ips(static_ip_to_azs.az_names)
46
+ sorted_az_names.each do |az_name|
47
+ static_ip_to_azs.az_names = [az_name]
48
+ allocated_ips.allocate(az_name)
49
+ candidate_networks_to_static_ips = Bosh::Common::DeepCopy.copy(networks_to_static_ips)
50
+ result = try_combination(candidate_networks_to_static_ips, AllocatedIps.new)
51
+ next if result.nil?
52
+ return result
53
+ end
54
+ end
55
+ end
56
+
57
+ return nil
58
+ end
59
+
60
+ def even_distribution_of_ips?(networks_to_static_ips)
61
+ hash = {}
62
+ networks_to_static_ips.each do |network, static_ips_to_azs|
63
+ hash[network] ||= {}
64
+ static_ips_to_azs.each do |static_ip_to_azs|
65
+ static_ip_to_azs.az_names.each do |az_name|
66
+ hash[network][az_name] ||= 0
67
+ hash[network][az_name] += 1
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ hash.values.uniq.size > 1 ? false : true
74
+ end
75
+
76
+ def all_ips_belong_to_single_az(networks_to_static_ips)
77
+ !networks_to_static_ips.values.any? do |static_ips_to_azs|
78
+ static_ips_to_azs.any? do |static_ip_to_az|
79
+ static_ip_to_az.az_names.size > 1
80
+ end
81
+ end
82
+ end
83
+
84
+ class AllocatedIps
85
+ def initialize
86
+ @allocated_ips = Hash.new {|h,k| h[k] = 0 }
87
+ end
88
+
89
+ def allocate(az_name)
90
+ @allocated_ips[az_name] += 1
91
+ end
92
+
93
+ def sort_by_least_allocated_ips(az_names)
94
+ az_names.sort_by do |az_name|
95
+ @allocated_ips[az_name]
96
+ end
97
+ end
98
+ end
99
+
100
+ class PreviousAssignment
101
+ def initialize(network_to_static_ips)
102
+ @previous_assignment = Hash.new {|h,k| h[k] = 0 }
103
+ network_to_static_ips.each do |previous_assignment_ip|
104
+ @previous_assignment[previous_assignment_ip.az_names.first] += 1
105
+ end
106
+ end
107
+
108
+ def has_same_distribution?(static_ips_to_azs)
109
+ @previous_assignment.each do |az_name, required_number_of_ips_in_az|
110
+ ips_in_az = static_ips_to_azs.select { |static_ip_to_azs| static_ip_to_azs.az_names.include?(az_name) }
111
+ if ips_in_az.size < required_number_of_ips_in_az
112
+ return false
113
+ end
114
+ end
115
+
116
+ return true
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
@@ -0,0 +1,32 @@
1
+ module Bosh
2
+ module Director
3
+ module DeploymentPlan
4
+ module PlacementPlanner
5
+ class IndexAssigner
6
+ def initialize(deployment_model)
7
+ @deployment_model = deployment_model
8
+ end
9
+
10
+ def assign_index(job_name, existing_instance_model=nil)
11
+ if existing_instance_model && existing_instance_model.job == job_name
12
+ return existing_instance_model.index
13
+ end
14
+
15
+ sorted_indexes = Models::Instance.filter(job: job_name, deployment: @deployment_model).sort_by(&:index).map(&:index)
16
+ if sorted_indexes.empty?
17
+ 0
18
+ else
19
+ find_unused_index(sorted_indexes)
20
+ end
21
+ end
22
+
23
+ def find_unused_index(sorted_indexes)
24
+ sorted_indexes.unshift(-1)
25
+ next_indexes = sorted_indexes.map { |i| i + 1 }
26
+ (next_indexes - sorted_indexes).min
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,125 @@
1
+ module Bosh
2
+ module Director
3
+ module DeploymentPlan
4
+ module PlacementPlanner
5
+ class NetworksToStaticIps
6
+ extend Bosh::Director::IpUtil
7
+ include Bosh::Director::IpUtil
8
+
9
+ def self.create(job_networks, desired_azs, job_name)
10
+ networks_to_static_ips = {}
11
+
12
+ desired_az_names = desired_azs.nil? ? [nil] : desired_azs.to_a.map(&:name)
13
+
14
+ job_networks.each do |job_network|
15
+ next unless job_network.static?
16
+ subnets = job_network.deployment_network.subnets
17
+
18
+ job_network.static_ips.each do |static_ip|
19
+ subnet_for_ip = subnets.find { |subnet| subnet.static_ips.include?(static_ip) }
20
+ if subnet_for_ip.nil?
21
+ raise JobNetworkInstanceIpMismatch,
22
+ "Job '#{job_name}' with network '#{job_network.name}' declares static ip '#{format_ip(static_ip)}', " +
23
+ "which belongs to no subnet"
24
+ end
25
+ az_names = subnet_for_ip.availability_zone_names.nil? ? [nil] : subnet_for_ip.availability_zone_names
26
+ filtered_az_names = az_names.select { |static_ip_az_name| desired_az_names.include?(static_ip_az_name) }.uniq
27
+ networks_to_static_ips[job_network.name] ||= []
28
+ networks_to_static_ips[job_network.name] << StaticIpToAzs.new(static_ip, filtered_az_names)
29
+ end
30
+ end
31
+
32
+ new(networks_to_static_ips, job_name)
33
+ end
34
+
35
+ def initialize(networks_to_static_ips, job_name)
36
+ @networks_to_static_ips = networks_to_static_ips
37
+ @job_name = job_name
38
+ end
39
+
40
+ def validate_azs_are_declared_in_job_and_subnets(desired_azs)
41
+ if desired_azs.nil? &&
42
+ @networks_to_static_ips.any? do |_, static_ips_to_az|
43
+ static_ips_to_az.any? { |static_ip_to_az| !static_ip_to_az.az_names.any?(&:nil?) }
44
+ end
45
+
46
+ raise JobInvalidAvailabilityZone,
47
+ "Job '#{@job_name}' subnets declare availability zones and the job does not"
48
+ end
49
+ end
50
+
51
+ def validate_ips_are_in_desired_azs(desired_azs)
52
+ return if desired_azs.to_a.empty?
53
+
54
+ desired_az_names = desired_azs.to_a.map(&:name)
55
+ @networks_to_static_ips.each do |_, static_ips_to_az|
56
+ non_desired_ip_to_az = static_ips_to_az.find do |static_ip_to_az|
57
+ !(desired_az_names & static_ip_to_az.az_names).any?
58
+ end
59
+
60
+ if non_desired_ip_to_az
61
+ raise JobStaticIpsFromInvalidAvailabilityZone,
62
+ "Job '#{@job_name}' declares static ip '#{format_ip(non_desired_ip_to_az.ip)}' which does not belong to any of the job's availability zones."
63
+ end
64
+ end
65
+ end
66
+
67
+ def azs_to_networks
68
+ result = {}
69
+ @networks_to_static_ips.each do |network_name, static_ips_to_azs|
70
+ static_ips_to_azs.each do |static_ip_to_azs|
71
+ static_ip_to_azs.az_names.each do |az_name|
72
+ result[az_name][network_name] ||= []
73
+ result[az_name][network_name] << static_ip_to_azs.ip
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def distribute_evenly_per_zone
80
+ best_combination = BruteForceIpAllocation.new(@networks_to_static_ips).find_best_combination
81
+ if best_combination.nil?
82
+ raise JobNetworkInstanceIpMismatch, "Failed to evenly distribute static IPs between zones for job '#{@job_name}'"
83
+ end
84
+ @networks_to_static_ips = best_combination
85
+ end
86
+
87
+ def next_ip_for_network(network)
88
+ unclaimed_networks_to_static_ips[network.name].first
89
+ end
90
+
91
+ def claim_in_az(ip, az_name)
92
+ @networks_to_static_ips.each do |_, static_ips_to_azs|
93
+ static_ips_to_azs.each do |static_ip_to_azs|
94
+ if static_ip_to_azs.ip == ip
95
+ static_ip_to_azs.claimed = true
96
+ static_ip_to_azs.az_names = [az_name]
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ def delete(ip)
103
+ @networks_to_static_ips.each do |_, static_ip_to_azs|
104
+ static_ip_to_azs.delete_if { |static_ip_to_az| static_ip_to_az.ip == ip }
105
+ end
106
+ end
107
+
108
+ def find_by_network_and_ip(network, ip)
109
+ unclaimed_networks_to_static_ips[network.name].find { |static_ip_to_azs| static_ip_to_azs.ip == ip }
110
+ end
111
+
112
+ def find_by_network_and_az(network, az_name)
113
+ unclaimed_networks_to_static_ips[network.name].find { |static_ip_to_azs| static_ip_to_azs.az_names.include?(az_name) }
114
+ end
115
+
116
+ def unclaimed_networks_to_static_ips
117
+ Hash[@networks_to_static_ips.map { |network_name, static_ips_to_azs| [network_name, static_ips_to_azs.reject(&:claimed)] }]
118
+ end
119
+
120
+ class StaticIpToAzs < Struct.new(:ip, :az_names, :claimed); end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end