bosh-director 1.5.0.pre.1113

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. data/CHANGELOG +34 -0
  2. data/bin/bosh-director +36 -0
  3. data/bin/bosh-director-console +84 -0
  4. data/bin/bosh-director-drain-workers +42 -0
  5. data/bin/bosh-director-migrate +58 -0
  6. data/bin/bosh-director-scheduler +27 -0
  7. data/bin/bosh-director-worker +76 -0
  8. data/db/migrations/README +1 -0
  9. data/db/migrations/director/20110209010747_initial.rb +118 -0
  10. data/db/migrations/director/20110406055800_add_task_user.rb +9 -0
  11. data/db/migrations/director/20110518225809_remove_cid_constrain.rb +13 -0
  12. data/db/migrations/director/20110617211923_add_deployments_release_versions.rb +32 -0
  13. data/db/migrations/director/20110622212607_add_task_checkpoint_timestamp.rb +9 -0
  14. data/db/migrations/director/20110628023039_add_state_to_instances.rb +21 -0
  15. data/db/migrations/director/20110709012332_add_disk_size_to_instances.rb +9 -0
  16. data/db/migrations/director/20110906183441_add_log_bundles.rb +11 -0
  17. data/db/migrations/director/20110907194830_add_logs_json_to_templates.rb +9 -0
  18. data/db/migrations/director/20110915205610_add_persistent_disks.rb +51 -0
  19. data/db/migrations/director/20111005180929_add_properties.rb +14 -0
  20. data/db/migrations/director/20111110024617_add_deployment_problems.rb +24 -0
  21. data/db/migrations/director/20111216214145_recreate_support_for_vms.rb +9 -0
  22. data/db/migrations/director/20120102084027_add_credentials_to_vms.rb +7 -0
  23. data/db/migrations/director/20120427235217_allow_multiple_releases_per_deployment.rb +36 -0
  24. data/db/migrations/director/20120524175805_add_task_type.rb +44 -0
  25. data/db/migrations/director/20120614001930_delete_redundant_deployment_release_relation.rb +34 -0
  26. data/db/migrations/director/20120822004528_add_fingerprint_to_templates_and_packages.rb +17 -0
  27. data/db/migrations/director/20120830191244_add_properties_to_templates.rb +9 -0
  28. data/db/migrations/director/20121106190739_persist_vm_env.rb +9 -0
  29. data/db/migrations/director/20130222232131_add_sha1_to_stemcells.rb +9 -0
  30. data/db/migrations/director/20130312211407_add_commit_hash_to_release_versions.rb +19 -0
  31. data/db/migrations/director/20130409235338_snapshot.rb +15 -0
  32. data/db/migrations/director/20130530164918_add_paused_flag_to_instance.rb +14 -0
  33. data/db/migrations/director/20130531172604_add_director_attributes.rb +13 -0
  34. data/db/migrations/dns/20120123234908_initial.rb +27 -0
  35. data/lib/bosh/director.rb +133 -0
  36. data/lib/bosh/director/agent_client.rb +78 -0
  37. data/lib/bosh/director/api.rb +29 -0
  38. data/lib/bosh/director/api/api_helper.rb +81 -0
  39. data/lib/bosh/director/api/backup_manager.rb +15 -0
  40. data/lib/bosh/director/api/controller.rb +639 -0
  41. data/lib/bosh/director/api/controller_helpers.rb +34 -0
  42. data/lib/bosh/director/api/deployment_lookup.rb +13 -0
  43. data/lib/bosh/director/api/deployment_manager.rb +60 -0
  44. data/lib/bosh/director/api/http_constants.rb +16 -0
  45. data/lib/bosh/director/api/instance_lookup.rb +44 -0
  46. data/lib/bosh/director/api/instance_manager.rb +63 -0
  47. data/lib/bosh/director/api/problem_manager.rb +40 -0
  48. data/lib/bosh/director/api/property_manager.rb +69 -0
  49. data/lib/bosh/director/api/release_manager.rb +59 -0
  50. data/lib/bosh/director/api/resource_manager.rb +69 -0
  51. data/lib/bosh/director/api/resurrector_manager.rb +15 -0
  52. data/lib/bosh/director/api/snapshot_manager.rb +94 -0
  53. data/lib/bosh/director/api/stemcell_manager.rb +50 -0
  54. data/lib/bosh/director/api/task_helper.rb +46 -0
  55. data/lib/bosh/director/api/task_manager.rb +64 -0
  56. data/lib/bosh/director/api/user_manager.rb +72 -0
  57. data/lib/bosh/director/api/vm_state_manager.rb +11 -0
  58. data/lib/bosh/director/app.rb +35 -0
  59. data/lib/bosh/director/blob_util.rb +87 -0
  60. data/lib/bosh/director/blobstores.rb +29 -0
  61. data/lib/bosh/director/client.rb +156 -0
  62. data/lib/bosh/director/cloudcheck_helper.rb +204 -0
  63. data/lib/bosh/director/compile_task.rb +157 -0
  64. data/lib/bosh/director/config.rb +370 -0
  65. data/lib/bosh/director/configuration_hasher.rb +114 -0
  66. data/lib/bosh/director/cycle_helper.rb +36 -0
  67. data/lib/bosh/director/db_backup.rb +22 -0
  68. data/lib/bosh/director/db_backup/adapter.rb +3 -0
  69. data/lib/bosh/director/db_backup/adapter/mysql2.rb +27 -0
  70. data/lib/bosh/director/db_backup/adapter/postgres.rb +36 -0
  71. data/lib/bosh/director/db_backup/adapter/sqlite.rb +17 -0
  72. data/lib/bosh/director/db_backup/error.rb +10 -0
  73. data/lib/bosh/director/deployment_plan.rb +26 -0
  74. data/lib/bosh/director/deployment_plan/assembler.rb +430 -0
  75. data/lib/bosh/director/deployment_plan/compilation_config.rb +54 -0
  76. data/lib/bosh/director/deployment_plan/compiled_package.rb +35 -0
  77. data/lib/bosh/director/deployment_plan/dynamic_network.rb +91 -0
  78. data/lib/bosh/director/deployment_plan/idle_vm.rb +109 -0
  79. data/lib/bosh/director/deployment_plan/instance.rb +413 -0
  80. data/lib/bosh/director/deployment_plan/job.rb +470 -0
  81. data/lib/bosh/director/deployment_plan/manual_network.rb +137 -0
  82. data/lib/bosh/director/deployment_plan/network.rb +74 -0
  83. data/lib/bosh/director/deployment_plan/network_subnet.rb +167 -0
  84. data/lib/bosh/director/deployment_plan/planner.rb +288 -0
  85. data/lib/bosh/director/deployment_plan/preparer.rb +52 -0
  86. data/lib/bosh/director/deployment_plan/release.rb +126 -0
  87. data/lib/bosh/director/deployment_plan/resource_pool.rb +143 -0
  88. data/lib/bosh/director/deployment_plan/resource_pools.rb +68 -0
  89. data/lib/bosh/director/deployment_plan/stemcell.rb +56 -0
  90. data/lib/bosh/director/deployment_plan/template.rb +94 -0
  91. data/lib/bosh/director/deployment_plan/update_config.rb +80 -0
  92. data/lib/bosh/director/deployment_plan/updater.rb +55 -0
  93. data/lib/bosh/director/deployment_plan/vip_network.rb +79 -0
  94. data/lib/bosh/director/dns_helper.rb +204 -0
  95. data/lib/bosh/director/download_helper.rb +44 -0
  96. data/lib/bosh/director/duration.rb +36 -0
  97. data/lib/bosh/director/encryption_helper.rb +10 -0
  98. data/lib/bosh/director/errors.rb +198 -0
  99. data/lib/bosh/director/event_log.rb +136 -0
  100. data/lib/bosh/director/ext.rb +64 -0
  101. data/lib/bosh/director/hash_string_vals.rb +13 -0
  102. data/lib/bosh/director/instance_deleter.rb +109 -0
  103. data/lib/bosh/director/instance_updater.rb +506 -0
  104. data/lib/bosh/director/ip_util.rb +67 -0
  105. data/lib/bosh/director/job_queue.rb +16 -0
  106. data/lib/bosh/director/job_runner.rb +162 -0
  107. data/lib/bosh/director/job_updater.rb +121 -0
  108. data/lib/bosh/director/jobs/backup.rb +86 -0
  109. data/lib/bosh/director/jobs/base_job.rb +66 -0
  110. data/lib/bosh/director/jobs/cloud_check/apply_resolutions.rb +46 -0
  111. data/lib/bosh/director/jobs/cloud_check/scan.rb +38 -0
  112. data/lib/bosh/director/jobs/cloud_check/scan_and_fix.rb +73 -0
  113. data/lib/bosh/director/jobs/create_snapshot.rb +23 -0
  114. data/lib/bosh/director/jobs/delete_deployment.rb +183 -0
  115. data/lib/bosh/director/jobs/delete_deployment_snapshots.rb +34 -0
  116. data/lib/bosh/director/jobs/delete_release.rb +219 -0
  117. data/lib/bosh/director/jobs/delete_snapshots.rb +23 -0
  118. data/lib/bosh/director/jobs/delete_stemcell.rb +102 -0
  119. data/lib/bosh/director/jobs/fetch_logs.rb +99 -0
  120. data/lib/bosh/director/jobs/scheduled_backup.rb +38 -0
  121. data/lib/bosh/director/jobs/snapshot_deployment.rb +61 -0
  122. data/lib/bosh/director/jobs/snapshot_deployments.rb +23 -0
  123. data/lib/bosh/director/jobs/snapshot_self.rb +43 -0
  124. data/lib/bosh/director/jobs/ssh.rb +59 -0
  125. data/lib/bosh/director/jobs/update_deployment.rb +110 -0
  126. data/lib/bosh/director/jobs/update_release.rb +672 -0
  127. data/lib/bosh/director/jobs/update_stemcell.rb +109 -0
  128. data/lib/bosh/director/jobs/vm_state.rb +89 -0
  129. data/lib/bosh/director/lock.rb +133 -0
  130. data/lib/bosh/director/lock_helper.rb +92 -0
  131. data/lib/bosh/director/models.rb +29 -0
  132. data/lib/bosh/director/models/compiled_package.rb +33 -0
  133. data/lib/bosh/director/models/deployment.rb +22 -0
  134. data/lib/bosh/director/models/deployment_problem.rb +49 -0
  135. data/lib/bosh/director/models/deployment_property.rb +21 -0
  136. data/lib/bosh/director/models/director_attribute.rb +9 -0
  137. data/lib/bosh/director/models/dns.rb +9 -0
  138. data/lib/bosh/director/models/dns/domain.rb +9 -0
  139. data/lib/bosh/director/models/dns/record.rb +7 -0
  140. data/lib/bosh/director/models/helpers/model_helper.rb +7 -0
  141. data/lib/bosh/director/models/instance.rb +28 -0
  142. data/lib/bosh/director/models/log_bundle.rb +10 -0
  143. data/lib/bosh/director/models/package.rb +30 -0
  144. data/lib/bosh/director/models/persistent_disk.rb +13 -0
  145. data/lib/bosh/director/models/release.rb +17 -0
  146. data/lib/bosh/director/models/release_version.rb +16 -0
  147. data/lib/bosh/director/models/snapshot.rb +13 -0
  148. data/lib/bosh/director/models/stemcell.rb +18 -0
  149. data/lib/bosh/director/models/task.rb +10 -0
  150. data/lib/bosh/director/models/template.rb +44 -0
  151. data/lib/bosh/director/models/user.rb +11 -0
  152. data/lib/bosh/director/models/vm.rb +42 -0
  153. data/lib/bosh/director/nats_rpc.rb +54 -0
  154. data/lib/bosh/director/network_reservation.rb +121 -0
  155. data/lib/bosh/director/next_rebase_version.rb +20 -0
  156. data/lib/bosh/director/package_compiler.rb +423 -0
  157. data/lib/bosh/director/problem_handlers/base.rb +153 -0
  158. data/lib/bosh/director/problem_handlers/inactive_disk.rb +112 -0
  159. data/lib/bosh/director/problem_handlers/invalid_problem.rb +28 -0
  160. data/lib/bosh/director/problem_handlers/missing_vm.rb +34 -0
  161. data/lib/bosh/director/problem_handlers/mount_info_mismatch.rb +62 -0
  162. data/lib/bosh/director/problem_handlers/out_of_sync_vm.rb +64 -0
  163. data/lib/bosh/director/problem_handlers/unbound_instance_vm.rb +85 -0
  164. data/lib/bosh/director/problem_handlers/unresponsive_agent.rb +78 -0
  165. data/lib/bosh/director/problem_resolver.rb +103 -0
  166. data/lib/bosh/director/problem_scanner.rb +268 -0
  167. data/lib/bosh/director/resource_pool_updater.rb +216 -0
  168. data/lib/bosh/director/scheduler.rb +57 -0
  169. data/lib/bosh/director/sequel.rb +13 -0
  170. data/lib/bosh/director/tar_gzipper.rb +47 -0
  171. data/lib/bosh/director/task_result_file.rb +19 -0
  172. data/lib/bosh/director/thread_pool.rb +8 -0
  173. data/lib/bosh/director/validation_helper.rb +55 -0
  174. data/lib/bosh/director/version.rb +7 -0
  175. data/lib/bosh/director/vm_creator.rb +80 -0
  176. data/lib/bosh/director/vm_data.rb +63 -0
  177. data/lib/bosh/director/vm_metadata_updater.rb +29 -0
  178. data/lib/bosh/director/vm_reuser.rb +63 -0
  179. data/lib/cloud/dummy.rb +149 -0
  180. metadata +664 -0
@@ -0,0 +1,94 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Director
4
+ module DeploymentPlan
5
+ class Template
6
+
7
+ attr_reader :name
8
+ attr_reader :release
9
+
10
+ attr_reader :model
11
+ attr_reader :package_models
12
+
13
+ # @param [DeploymentPlan::Release] release Release
14
+ # @param [String] name Template name
15
+ def initialize(release, name)
16
+ @release = release
17
+ @name = name
18
+ @model = nil
19
+ @package_models = []
20
+ @logger = Config.logger
21
+ end
22
+
23
+ # Looks up template model and its package models in DB
24
+ # @return [void]
25
+ def bind_models
26
+ @model = @release.get_template_model_by_name(@name)
27
+
28
+ if @model.nil?
29
+ raise DeploymentUnknownTemplate, "Can't find template `#{@name}'"
30
+ end
31
+
32
+ @package_models = @model.package_names.map do |name|
33
+ @release.get_package_model_by_name(name)
34
+ end
35
+ end
36
+
37
+ # Downloads template blob to a given path
38
+ # @return [String] Path to downloaded blob
39
+ def download_blob
40
+ uuid = SecureRandom.uuid
41
+ path = File.join(Dir.tmpdir, "template-#{uuid}")
42
+
43
+ @logger.debug("Downloading template `#{@name}' (#{blobstore_id})...")
44
+ t1 = Time.now
45
+
46
+ File.open(path, "w") do |f|
47
+ App.instance.blobstores.blobstore.get(blobstore_id, f)
48
+ end
49
+
50
+ @logger.debug("Template `#{@name}' downloaded to #{path} " +
51
+ "(took #{Time.now - t1}s)")
52
+
53
+ path
54
+ end
55
+
56
+ # @return [String]
57
+ def version
58
+ present_model.version
59
+ end
60
+
61
+ # @return [String]
62
+ def sha1
63
+ present_model.sha1
64
+ end
65
+
66
+ # @return [String]
67
+ def blobstore_id
68
+ present_model.blobstore_id
69
+ end
70
+
71
+ # @return [Array]
72
+ def logs
73
+ present_model.logs
74
+ end
75
+
76
+ # @return [Hash]
77
+ def properties
78
+ present_model.properties
79
+ end
80
+
81
+ private
82
+
83
+ # Returns model only if it's present, fails otherwise
84
+ # @return [Models::Template]
85
+ def present_model
86
+ if @model.nil?
87
+ raise DirectorError, "Template `#{@name}' model is unbound"
88
+ end
89
+ @model
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,80 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Director
4
+ module DeploymentPlan
5
+ class UpdateConfig
6
+ include ValidationHelper
7
+
8
+ attr_accessor :canaries
9
+ attr_accessor :max_in_flight
10
+
11
+ attr_accessor :min_canary_watch_time
12
+ attr_accessor :max_canary_watch_time
13
+
14
+ attr_accessor :min_update_watch_time
15
+ attr_accessor :max_update_watch_time
16
+
17
+ # @param [Hash] update_config Raw update config from deployment manifest
18
+ # @param [optional, Hash] default_update_config Default update config
19
+ def initialize(update_config, default_update_config = nil)
20
+ optional = !default_update_config.nil?
21
+
22
+ @canaries = safe_property(update_config, "canaries",
23
+ :class => Integer, :optional => optional)
24
+
25
+ @max_in_flight = safe_property(update_config, "max_in_flight",
26
+ :class => Integer, :optional => optional,
27
+ :min => 1)
28
+
29
+ canary_watch_times = safe_property(update_config, "canary_watch_time",
30
+ :class => String,
31
+ :optional => optional)
32
+ update_watch_times = safe_property(update_config, "update_watch_time",
33
+ :class => String,
34
+ :optional => optional)
35
+
36
+ if canary_watch_times
37
+ @min_canary_watch_time, @max_canary_watch_time =
38
+ parse_watch_times(canary_watch_times)
39
+ end
40
+
41
+ if update_watch_times
42
+ @min_update_watch_time, @max_update_watch_time =
43
+ parse_watch_times(update_watch_times)
44
+ end
45
+
46
+ if optional
47
+ @canaries ||= default_update_config.canaries
48
+
49
+ @min_canary_watch_time ||= default_update_config.min_canary_watch_time
50
+ @max_canary_watch_time ||= default_update_config.max_canary_watch_time
51
+
52
+ @min_update_watch_time ||= default_update_config.min_update_watch_time
53
+ @max_update_watch_time ||= default_update_config.max_update_watch_time
54
+
55
+ @max_in_flight ||= default_update_config.max_in_flight
56
+ end
57
+ end
58
+
59
+ def parse_watch_times(value)
60
+ value = value.to_s
61
+
62
+ if value =~ /^\s*(\d+)\s*\-\s*(\d+)\s*$/
63
+ result = [$1.to_i, $2.to_i]
64
+ elsif value =~ /^\s*(\d+)\s*$/
65
+ result = [$1.to_i, $1.to_i]
66
+ else
67
+ raise UpdateConfigInvalidWatchTime,
68
+ "Watch time should be an integer or a range of two integers"
69
+ end
70
+
71
+ if result[0] > result[1]
72
+ raise UpdateConfigInvalidWatchTime,
73
+ "Min watch time cannot be greater than max watch time"
74
+ end
75
+
76
+ result
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,55 @@
1
+ module Bosh::Director
2
+ module DeploymentPlan
3
+ class Updater
4
+ def initialize(job, event_log, resource_pools, assembler, deployment_plan)
5
+ @job = job
6
+ @logger = job.logger
7
+ @event_log = event_log
8
+ @resource_pools = resource_pools
9
+ @assembler = assembler
10
+ @deployment_plan = deployment_plan
11
+ end
12
+
13
+ def update
14
+ event_log.begin_stage('Preparing DNS', 1)
15
+ job.track_and_log('Binding DNS') do
16
+ if Config.dns_enabled?
17
+ assembler.bind_dns
18
+ end
19
+ end
20
+
21
+ logger.info('Updating resource pools')
22
+ resource_pools.update
23
+ job.task_checkpoint
24
+
25
+ logger.info('Binding instance VMs')
26
+ assembler.bind_instance_vms
27
+
28
+ event_log.begin_stage('Preparing configuration', 1)
29
+ job.track_and_log('Binding configuration') do
30
+ assembler.bind_configuration
31
+ end
32
+
33
+ logger.info('Deleting no longer needed VMs')
34
+ assembler.delete_unneeded_vms
35
+
36
+ logger.info('Deleting no longer needed instances')
37
+ assembler.delete_unneeded_instances
38
+
39
+ logger.info('Updating jobs')
40
+ deployment_plan.jobs.each do |bosh_job|
41
+ job.task_checkpoint
42
+ logger.info("Updating job: #{bosh_job.name}")
43
+ JobUpdater.new(deployment_plan, bosh_job).update
44
+ end
45
+
46
+ logger.info('Refilling resource pools')
47
+ resource_pools.refill
48
+ end
49
+
50
+ private
51
+
52
+ attr_reader :job, :event_log, :resource_pools, :logger, :assembler, :deployment_plan
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,79 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Director
4
+ module DeploymentPlan
5
+ class VipNetwork < Network
6
+ include IpUtil
7
+
8
+ # @return [Hash] Network cloud properties
9
+ attr_reader :cloud_properties
10
+
11
+ ##
12
+ # Creates a new network.
13
+ #
14
+ # @param [DeploymentPlan] deployment associated deployment plan
15
+ # @param [Hash] network_spec parsed deployment manifest network section
16
+ def initialize(deployment, network_spec)
17
+ super
18
+ @cloud_properties = safe_property(network_spec, "cloud_properties",
19
+ :class => Hash)
20
+ @reserved_ips = Set.new
21
+ end
22
+
23
+ ##
24
+ # Reserves a network resource.
25
+ #
26
+ # This is either an already used reservation being verified or a new one
27
+ # waiting to be fulfilled.
28
+ # @param [NetworkReservation] reservation
29
+ # @return [Boolean] true if the reservation was fulfilled
30
+ def reserve(reservation)
31
+ reservation.reserved = false
32
+ if reservation.ip.nil?
33
+ raise NetworkReservationIpMissing,
34
+ "Must have IP for static reservations"
35
+ elsif reservation.dynamic?
36
+ reservation.error = NetworkReservation::WRONG_TYPE
37
+ elsif @reserved_ips.include?(reservation.ip)
38
+ reservation.error = NetworkReservation::USED
39
+ else
40
+ reservation.reserved = true
41
+ reservation.type = NetworkReservation::STATIC
42
+ @reserved_ips.add(reservation.ip)
43
+ end
44
+ reservation.reserved?
45
+ end
46
+
47
+ ##
48
+ # Releases a previous reservation that had been fulfilled.
49
+ # @param [NetworkReservation] reservation
50
+ # @return [void]
51
+ def release(reservation)
52
+ unless reservation.ip
53
+ raise NetworkReservationIpMissing,
54
+ "Can't release reservation without an IP"
55
+ end
56
+ @reserved_ips.delete(reservation.ip)
57
+ end
58
+
59
+ ##
60
+ # Returns the network settings for the specific reservation.
61
+ #
62
+ # @param [NetworkReservation] reservation
63
+ # @param [Array<String>] default_properties
64
+ # @return [Hash] network settings that will be passed to the BOSH Agent
65
+ def network_settings(reservation, default_properties = VALID_DEFAULTS)
66
+ if default_properties && !default_properties.empty?
67
+ raise NetworkReservationVipDefaultProvided,
68
+ "Can't provide any defaults since this is a VIP network"
69
+ end
70
+
71
+ {
72
+ "type" => "vip",
73
+ "ip" => ip_to_netaddr(reservation.ip).ip,
74
+ "cloud_properties" => @cloud_properties
75
+ }
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,204 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Director
4
+ module DnsHelper
5
+
6
+ # primary_ns contact serial refresh retry expire minimum
7
+ SOA = "localhost hostmaster@localhost 0 10800 604800 30"
8
+ TTL_5M = 300
9
+ TTL_4H = 3600 * 4
10
+
11
+ # @param [String] ip IP address
12
+ # @return [String] reverse dns domain name for an IP
13
+ def reverse_domain(ip)
14
+ reverse(ip, 2)
15
+ end
16
+
17
+ # @param [String] ip IP address
18
+ # @return [String] reverse dns name for an IP used for a PTR record
19
+ def reverse_host(ip)
20
+ reverse(ip, 3)
21
+ end
22
+
23
+ def canonical(string)
24
+ # a-z, 0-9, -, case insensitive, and must start with a letter
25
+ string = string.downcase.gsub(/_/, "-").gsub(/[^a-z0-9-]/, "")
26
+ if string =~ /^(\d|-)/
27
+ raise DnsInvalidCanonicalName,
28
+ "Invalid DNS canonical name `#{string}', must begin with a letter"
29
+ end
30
+ if string =~ /-$/
31
+ raise DnsInvalidCanonicalName,
32
+ "Invalid DNS canonical name `#{string}', can't end with a hyphen"
33
+ end
34
+ string
35
+ end
36
+
37
+ # build a list of dns servers to use
38
+ def dns_servers(network, spec, add_default_dns = true)
39
+ servers = nil
40
+ dns_property = safe_property(spec, "dns",
41
+ :class => Array, :optional => true)
42
+ if dns_property
43
+ servers = []
44
+ dns_property.each do |dns|
45
+ dns = NetAddr::CIDR.create(dns)
46
+ unless dns.size == 1
47
+ invalid_dns(network, "must be a single IP")
48
+ end
49
+
50
+ servers << dns.ip
51
+ end
52
+ end
53
+
54
+ return servers unless add_default_dns
55
+
56
+ add_default_dns_server(servers)
57
+ end
58
+
59
+ # returns the default DNS server
60
+ def default_dns_server
61
+ Config.dns["server"] if Config.dns
62
+ end
63
+
64
+ # add default dns server to an array of dns servers
65
+ def add_default_dns_server(servers)
66
+ return servers unless Config.dns_enabled?
67
+
68
+ default_server = default_dns_server
69
+ if default_server && default_server != "127.0.0.1"
70
+ (servers ||= []) << default_server
71
+ servers.uniq!
72
+ end
73
+
74
+ servers
75
+ end
76
+
77
+ # returns the DNS domain name
78
+ def dns_domain_name
79
+ Config.dns_domain_name
80
+ end
81
+
82
+ # returns the DNS name server record
83
+ def dns_ns_record
84
+ "ns.#{dns_domain_name}"
85
+ end
86
+
87
+ # create/update DNS A record
88
+ def update_dns_a_record(domain, name, ip_address)
89
+ record = Models::Dns::Record.find(:domain_id => domain.id,
90
+ :name => name)
91
+ if record.nil?
92
+ record = Models::Dns::Record.new(:domain_id => domain.id,
93
+ :name => name, :type => "A",
94
+ :ttl => TTL_5M)
95
+ end
96
+ record.content = ip_address
97
+ record.change_date = Time.now.to_i
98
+ record.save
99
+ end
100
+
101
+ # create/update DNS PTR records (for reverse lookups)
102
+ def update_dns_ptr_record(name, ip_address)
103
+ reverse_domain = reverse_domain(ip_address)
104
+ reverse_host = reverse_host(ip_address)
105
+
106
+ rdomain = Models::Dns::Domain.safe_find_or_create(:name => reverse_domain,
107
+ :type => "NATIVE")
108
+ Models::Dns::Record.find_or_create(:domain_id => rdomain.id,
109
+ :name => reverse_domain,
110
+ :type =>'SOA', :content => SOA,
111
+ :ttl => TTL_4H)
112
+
113
+ Models::Dns::Record.find_or_create(:domain_id => rdomain.id,
114
+ :name => reverse_domain,
115
+ :type =>'NS', :ttl => TTL_4H,
116
+ :content => dns_ns_record)
117
+
118
+ record = Models::Dns::Record.find(:content => name, :type =>'PTR')
119
+
120
+ # delete the record if the IP address changed
121
+ if record && record.name != reverse_host
122
+ id = record.domain_id
123
+ record.destroy
124
+ record = nil
125
+
126
+ # delete the domain if the domain id changed and it's empty
127
+ if id != rdomain.id
128
+ delete_empty_domain(Models::Dns::Domain[id])
129
+ end
130
+ end
131
+
132
+ unless record
133
+ record = Models::Dns::Record.new(:domain_id => rdomain.id,
134
+ :name => reverse_host,
135
+ :type =>'PTR', :ttl => TTL_5M)
136
+ end
137
+ record.content = name
138
+ record.change_date = Time.now.to_i
139
+ record.save
140
+ end
141
+
142
+ # deletes all DNS records matching the pattern
143
+ # @param [String] record_pattern SQL pattern
144
+ # @param [Integer] domain_id domain record id
145
+ def delete_dns_records(record_pattern, domain_id=nil)
146
+ records = Models::Dns::Record.filter(:name.like(record_pattern))
147
+ if domain_id
148
+ records = records.filter(:domain_id => domain_id)
149
+ end
150
+
151
+ # delete A records and collect all IPs for later
152
+ ips = []
153
+ records.each do |record|
154
+ ips << record.content
155
+ @logger.info("Deleting DNS record: #{record.name}")
156
+ record.destroy
157
+ end
158
+
159
+ # delete PTR records from IP list
160
+ ips.each do |ip|
161
+ records = Models::Dns::Record.filter(:name.like(reverse_host(ip)))
162
+ records.each do |record|
163
+ @logger.info("Deleting reverse DNS record: #{record.name}")
164
+ record.destroy
165
+ end
166
+ end
167
+
168
+ # see if any of the reverse domains are empty and should be deleted
169
+ ips.each do |ip|
170
+ reverse = reverse_domain(ip)
171
+ rdomain = Models::Dns::Domain.filter(:name => reverse,
172
+ :type => "NATIVE")
173
+ rdomain.each do |domain|
174
+ delete_empty_domain(domain)
175
+ end
176
+ end
177
+ end
178
+
179
+ # if the count is 2, it means we only have the NS & SOA record
180
+ # and the domain is "empty" and can be deleted
181
+ def delete_empty_domain(domain)
182
+ if domain.records.size == 2
183
+ @logger.info("Deleting empty reverse domain #{domain.name}")
184
+ domain.destroy # cascaded - all records are removed
185
+ end
186
+ end
187
+
188
+ # @param [String] network name
189
+ # @param [String] reason
190
+ # @raise NetworkInvalidDns
191
+ def invalid_dns(network, reason)
192
+ raise NetworkInvalidDns,
193
+ "Invalid DNS for network `#{network}': #{reason}"
194
+ end
195
+
196
+ private
197
+
198
+ def reverse(ip, n)
199
+ octets = ip.split(/\./)
200
+ "#{octets[0..n].reverse.join(".")}.in-addr.arpa"
201
+ end
202
+
203
+ end
204
+ end