bosh-director 1.5.0.pre.1113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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