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,14 @@
1
+ Sequel.migration do
2
+ up do
3
+ alter_table :instances do
4
+ add_column :resurrection_paused, TrueClass
5
+ set_column_default :resurrection_paused, false
6
+ end
7
+ end
8
+
9
+ down do
10
+ alter_table :instances do
11
+ drop_column :resurrection_paused
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ Sequel.migration do
2
+ up do
3
+ create_table(:director_attributes) do
4
+ primary_key :uuid
5
+ String :uuid, unique: true, null: false
6
+ end
7
+ end
8
+
9
+ down do
10
+ drop_table(:director_attributes)
11
+ end
12
+
13
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table :domains do
6
+ primary_key :id
7
+ String :name, :size => 255, :null => false, :unique => true
8
+ String :master, :size => 128, :null => true, :default => nil
9
+ Integer :last_check, :null => true, :default => nil
10
+ String :type, :size => 6, :null => false
11
+ Integer :notified_serial, :null => true, :default => nil
12
+ String :account, :size=> 40, :null => true, :default => nil
13
+ end
14
+
15
+ create_table :records do
16
+ primary_key :id
17
+ String :name, :size => 255, :null => true, :default => nil, :index => true
18
+ String :type, :size => 10, :null => true, :default => nil
19
+ String :content, :size => 4098, :null => true, :default => nil
20
+ Integer :ttl, :null => true, :default => nil
21
+ Integer :prio, :null => true, :default => nil
22
+ Integer :change_date, :null => true, :default => nil
23
+ foreign_key :domain_id, :domains, :on_delete => :cascade, :null => true, :default => nil, :index => true
24
+ index [:name, :type]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,133 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh
4
+ module Director
5
+ end
6
+ end
7
+
8
+ require 'digest/sha1'
9
+ require 'erb'
10
+ require 'fileutils'
11
+ require 'forwardable'
12
+ require 'logger'
13
+ require 'monitor'
14
+ require 'optparse'
15
+ require 'ostruct'
16
+ require 'pathname'
17
+ require 'pp'
18
+ require 'thread'
19
+ require 'tmpdir'
20
+ require 'yaml'
21
+ require 'time'
22
+ require 'zlib'
23
+
24
+ require 'common/runs_commands'
25
+ require 'common/exec'
26
+ require 'common/properties'
27
+
28
+ require 'bcrypt'
29
+ require 'blobstore_client'
30
+ require 'eventmachine'
31
+ require 'netaddr'
32
+ require 'resque'
33
+ require 'sequel'
34
+ require 'sinatra/base'
35
+ require 'securerandom'
36
+ require 'yajl'
37
+ require 'nats/client'
38
+ require 'securerandom'
39
+
40
+ require 'common/thread_formatter'
41
+ require 'bosh/core/encryption_handler'
42
+ require 'bosh/director/api'
43
+ require 'bosh/director/dns_helper'
44
+ require 'bosh/director/errors'
45
+ require 'bosh/director/ext'
46
+ require 'bosh/director/ip_util'
47
+ require 'bosh/director/lock_helper'
48
+ require 'bosh/director/validation_helper'
49
+ require 'bosh/director/download_helper'
50
+
51
+ require 'bosh/director/version'
52
+ require 'bosh/director/next_rebase_version'
53
+ require 'bosh/director/config'
54
+ require 'bosh/director/event_log'
55
+ require 'bosh/director/task_result_file'
56
+ require 'bosh/director/blob_util'
57
+
58
+ require 'bosh/director/client'
59
+ require 'bosh/director/agent_client'
60
+ require 'cloud'
61
+ require 'bosh/director/compile_task'
62
+ require 'bosh/director/configuration_hasher'
63
+ require 'bosh/director/cycle_helper'
64
+ require 'bosh/director/encryption_helper'
65
+ require 'bosh/director/vm_creator'
66
+ require 'bosh/director/vm_metadata_updater'
67
+ require 'bosh/director/vm_data'
68
+ require 'bosh/director/vm_reuser'
69
+ require 'bosh/director/deployment_plan'
70
+ require 'bosh/director/deployment_plan/assembler'
71
+ require 'bosh/director/duration'
72
+ require 'bosh/director/hash_string_vals'
73
+ require 'bosh/director/instance_deleter'
74
+ require 'bosh/director/instance_updater'
75
+ require 'bosh/director/job_runner'
76
+ require 'bosh/director/job_updater'
77
+ require 'bosh/director/job_queue'
78
+ require 'bosh/director/tar_gzipper'
79
+ require 'bosh/director/lock'
80
+ require 'bosh/director/nats_rpc'
81
+ require 'bosh/director/network_reservation'
82
+ require 'bosh/director/package_compiler'
83
+ require 'bosh/director/problem_scanner'
84
+ require 'bosh/director/problem_resolver'
85
+ require 'bosh/director/resource_pool_updater'
86
+ require 'bosh/director/sequel'
87
+ require 'common/thread_pool'
88
+
89
+ require 'bosh/director/cloudcheck_helper'
90
+ require 'bosh/director/problem_handlers/base'
91
+ require 'bosh/director/problem_handlers/invalid_problem'
92
+ require 'bosh/director/problem_handlers/inactive_disk'
93
+ require 'bosh/director/problem_handlers/out_of_sync_vm'
94
+ require 'bosh/director/problem_handlers/unresponsive_agent'
95
+ require 'bosh/director/problem_handlers/unbound_instance_vm'
96
+ require 'bosh/director/problem_handlers/mount_info_mismatch'
97
+ require 'bosh/director/problem_handlers/missing_vm'
98
+
99
+ require 'bosh/director/jobs/base_job'
100
+ require 'bosh/director/jobs/backup'
101
+ require 'bosh/director/jobs/scheduled_backup'
102
+ require 'bosh/director/jobs/create_snapshot'
103
+ require 'bosh/director/jobs/snapshot_deployment'
104
+ require 'bosh/director/jobs/snapshot_deployments'
105
+ require 'bosh/director/jobs/snapshot_self'
106
+ require 'bosh/director/jobs/delete_deployment'
107
+ require 'bosh/director/jobs/delete_deployment_snapshots'
108
+ require 'bosh/director/jobs/delete_release'
109
+ require 'bosh/director/jobs/delete_snapshots'
110
+ require 'bosh/director/jobs/delete_stemcell'
111
+ require 'bosh/director/jobs/update_deployment'
112
+ require 'bosh/director/jobs/update_release'
113
+ require 'bosh/director/jobs/update_stemcell'
114
+ require 'bosh/director/jobs/fetch_logs'
115
+ require 'bosh/director/jobs/vm_state'
116
+ require 'bosh/director/jobs/cloud_check/scan'
117
+ require 'bosh/director/jobs/cloud_check/scan_and_fix'
118
+ require 'bosh/director/jobs/cloud_check/apply_resolutions'
119
+ require 'bosh/director/jobs/ssh'
120
+
121
+ require 'bosh/director/models/helpers/model_helper'
122
+
123
+ require 'bosh/director/db_backup'
124
+ require 'bosh/director/blobstores'
125
+ require 'bosh/director/app'
126
+
127
+ module Bosh::Director
128
+ autoload :Models, 'bosh/director/models' # Defining model classes relies on a database connection
129
+ end
130
+
131
+ require 'bosh/director/thread_pool'
132
+ require 'bosh/director/api/controller_helpers'
133
+ require 'bosh/director/api/controller'
@@ -0,0 +1,78 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Director
4
+ class AgentClient < Client
5
+
6
+ DEFAULT_POLL_INTERVAL = 1.0
7
+
8
+ def initialize(id, options = {})
9
+ # Retry 'get_state' and 'get_task' in case of timeout errors
10
+ defaults = {
11
+ :retry_methods => { :get_state => 2, :get_task => 2}
12
+ }
13
+
14
+ credentials = Bosh::Director::Models::Vm.find(:agent_id => id).credentials
15
+ if credentials
16
+ defaults.merge!({
17
+ :credentials => credentials
18
+ })
19
+ end
20
+
21
+ super("agent", id, defaults.merge(options))
22
+ end
23
+
24
+ # This converts the old agent response format to the new. This can be taken
25
+ # out once the agents never reply with the old message format.
26
+ # The old message format is just to pass the return object as JSON. That
27
+ # means it could be any type -- array, hash, string, int, etc.
28
+ # The new format is:
29
+ # {"state"=>"task_state", "value"=><task_return_object>,
30
+ # "agent_task_id"=>123}
31
+ # This is a class method to make testing easier.
32
+ # @param [Object] msg The agent message to convert to the new format.
33
+ # @return [Hash] The message in the new message format.
34
+ def self.convert_old_message_to_new(msg)
35
+ if msg && msg.is_a?(Hash)
36
+ if !msg.has_key?("value")
37
+ # There's no value but there is state and agent_task_id.
38
+ # Just leave it.
39
+ if msg.has_key?("state") && msg.has_key?("agent_task_id")
40
+ return msg
41
+ end
42
+ # There's no value and either one or both of state/agent_task_id don't
43
+ # exist. Assume that the whole message response was meant to be the
44
+ # value.
45
+ return {"value" => msg,
46
+ "state" => msg["state"] || "done",
47
+ "agent_task_id" => msg["agent_task_id"] || nil}
48
+ elsif !msg.has_key?("state")
49
+ # The message has a value section but no "state". Mark state as
50
+ # done.
51
+ return {"value" => msg["value"],
52
+ "state" => "done",
53
+ "agent_task_id" => msg["agent_task_id"] || nil}
54
+ else
55
+ # The message was good from the start!
56
+ return msg
57
+ end
58
+ end
59
+ # If the message was anything other than a hash (float, int, string,
60
+ # array, etc.) then we want to just make that be the "value".
61
+ {"state" => "done", "value" => msg, "agent_task_id" => nil}
62
+ end
63
+
64
+ [:apply, :compile_package, :drain, :fetch_logs, :migrate_disk, :mount_disk,
65
+ :stop, :unmount_disk].each do |method|
66
+ define_method (method) do |*args|
67
+ task = AgentClient.convert_old_message_to_new(super(*args))
68
+ while task["state"] == "running"
69
+ sleep(DEFAULT_POLL_INTERVAL)
70
+ task = AgentClient.convert_old_message_to_new(
71
+ get_task(task["agent_task_id"]))
72
+ end
73
+ task["value"]
74
+ end
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Director
4
+ module Api
5
+ end
6
+ end
7
+
8
+ require 'bosh/director/api/http_constants'
9
+
10
+ require 'bosh/director/api/api_helper'
11
+ require 'bosh/director/api/task_helper'
12
+
13
+ require 'bosh/director/api/backup_manager'
14
+ require 'bosh/director/api/deployment_manager'
15
+ require 'bosh/director/api/instance_manager'
16
+ require 'bosh/director/api/problem_manager'
17
+ require 'bosh/director/api/property_manager'
18
+ require 'bosh/director/api/release_manager'
19
+ require 'bosh/director/api/resource_manager'
20
+ require 'bosh/director/api/snapshot_manager'
21
+ require 'bosh/director/api/stemcell_manager'
22
+ require 'bosh/director/api/task_manager'
23
+ require 'bosh/director/api/user_manager'
24
+ require 'bosh/director/api/vm_state_manager'
25
+ require 'bosh/director/api/backup_manager'
26
+ require 'bosh/director/api/resurrector_manager'
27
+
28
+ require 'bosh/director/api/instance_lookup'
29
+ require 'bosh/director/api/deployment_lookup'
@@ -0,0 +1,81 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+ require 'sys/filesystem'
3
+ include Sys
4
+
5
+ module Bosh::Director
6
+ module Api
7
+ module ApiHelper
8
+ READ_CHUNK_SIZE = 16384
9
+
10
+ class DisposableFile < ::Rack::File
11
+ def close
12
+ FileUtils.rm_rf(self.path) if File.exists?(self.path)
13
+ end
14
+ end
15
+
16
+ # Adapted from Sinatra::Base#send_file. There is one difference:
17
+ # it uses DisposableFile instead of Rack::File.
18
+ # DisposableFile gets removed on "close" call. This is primarily
19
+ # meant to serve temporary files fetched from the blobstore.
20
+ # We CANNOT use a Sinatra after filter, as the filter is called before
21
+ # the contents of the file is sent to the client.
22
+ def send_disposable_file(path, opts = {})
23
+ if opts[:type] || !response['Content-Type']
24
+ content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
25
+ end
26
+
27
+ disposition = opts[:disposition]
28
+ filename = opts[:filename]
29
+ disposition = 'attachment' if disposition.nil? && filename
30
+ filename = path if filename.nil?
31
+ attachment(filename, disposition) if disposition
32
+
33
+ last_modified opts[:last_modified] if opts[:last_modified]
34
+
35
+ file = DisposableFile.new nil
36
+ file.path = path
37
+ result = file.serving env
38
+ result[1].each { |k,v| headers[k] ||= v }
39
+ headers['Content-Length'] = result[1]['Content-Length']
40
+ halt opts[:status] || result[0], result[2]
41
+ rescue Errno::ENOENT
42
+ not_found
43
+ end
44
+
45
+ def json_encode(payload)
46
+ Yajl::Encoder.encode(payload)
47
+ end
48
+
49
+ def json_decode(payload)
50
+ Yajl::Parser.parse(payload)
51
+ end
52
+
53
+ def start_task
54
+ task = yield
55
+ unless task.kind_of?(Models::Task)
56
+ raise "Block didn't return Task object"
57
+ end
58
+ redirect "/tasks/#{task.id}"
59
+ end
60
+
61
+ def check_available_disk_space(dir, size)
62
+ begin
63
+ stat = Sys::Filesystem.stat(dir)
64
+ available_space = stat.block_size * stat.blocks_available
65
+ available_space > size ? true : false
66
+ rescue
67
+ false
68
+ end
69
+ end
70
+
71
+ def write_file(path, stream, chunk_size = READ_CHUNK_SIZE)
72
+ buffer = ""
73
+ File.open(path, "w") do |file|
74
+ file.write(buffer) until stream.read(chunk_size, buffer).nil?
75
+ end
76
+ rescue SystemCallError => e
77
+ raise SystemError, e.message
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,15 @@
1
+ module Bosh::Director
2
+ module Api
3
+ class BackupManager
4
+ attr_accessor :destination_path
5
+
6
+ def initialize
7
+ @destination_path = '/var/vcap/store/director/backup.tgz'
8
+ end
9
+
10
+ def create_backup(user)
11
+ JobQueue.new.enqueue(user, Jobs::Backup, 'bosh backup', [destination_path])
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,639 @@
1
+ module Bosh::Director
2
+ module Api
3
+ class Controller < Sinatra::Base
4
+ PUBLIC_URLS = %w(/info)
5
+
6
+ include ApiHelper
7
+ include Http
8
+ include DnsHelper
9
+
10
+ def initialize
11
+ super
12
+ @deployment_manager = DeploymentManager.new
13
+ @backup_manager = BackupManager.new
14
+ @instance_manager = InstanceManager.new
15
+ @resurrector_manager = ResurrectorManager.new
16
+ @problem_manager = ProblemManager.new
17
+ @property_manager = PropertyManager.new
18
+ @resource_manager = ResourceManager.new
19
+ @release_manager = ReleaseManager.new
20
+ @snapshot_manager = SnapshotManager.new
21
+ @stemcell_manager = StemcellManager.new
22
+ @task_manager = TaskManager.new
23
+ @user_manager = UserManager.new
24
+ @vm_state_manager = VmStateManager.new
25
+ @logger = Config.logger
26
+ end
27
+
28
+ mime_type :tgz, 'application/x-compressed'
29
+
30
+ def self.consumes(*types)
31
+ types = Set.new(types)
32
+ types.map! { |t| mime_type(t) }
33
+
34
+ condition do
35
+ types.include?(request.content_type)
36
+ end
37
+ end
38
+
39
+ def authenticate(user, password)
40
+ if @user_manager.authenticate(user, password)
41
+ @user = user
42
+ true
43
+ else
44
+ false
45
+ end
46
+ end
47
+
48
+ helpers ControllerHelpers
49
+
50
+ before do
51
+ auth_provided = %w(HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION).detect do |key|
52
+ request.env.has_key?(key)
53
+ end
54
+
55
+ protected! if auth_provided || !PUBLIC_URLS.include?(request.path_info)
56
+ end
57
+
58
+ after { headers('Date' => Time.now.rfc822) } # As thin doesn't inject date
59
+
60
+ configure do
61
+ set(:show_exceptions, false)
62
+ set(:raise_errors, false)
63
+ set(:dump_errors, false)
64
+ end
65
+
66
+ error do
67
+ exception = request.env['sinatra.error']
68
+ if exception.kind_of?(DirectorError)
69
+ @logger.debug('Request failed, ' +
70
+ "response code: #{exception.response_code}, " +
71
+ "error code: #{exception.error_code}, " +
72
+ "error message: #{exception.message}")
73
+ status(exception.response_code)
74
+ error_payload = {
75
+ 'code' => exception.error_code,
76
+ 'description' => exception.message
77
+ }
78
+ json_encode(error_payload)
79
+ else
80
+ msg = ["#{exception.class} - #{exception.message}:"]
81
+ msg.concat(exception.backtrace)
82
+ @logger.error(msg.join("\n"))
83
+ status(500)
84
+ end
85
+ end
86
+
87
+ post '/users', :consumes => [:json] do
88
+ user = @user_manager.get_user_from_request(request)
89
+ @user_manager.create_user(user)
90
+ status(204)
91
+ nil
92
+ end
93
+
94
+ put '/users/:username', :consumes => [:json] do
95
+ user = @user_manager.get_user_from_request(request)
96
+ if user.username != params[:username]
97
+ raise UserImmutableUsername, 'The username is immutable'
98
+ end
99
+ @user_manager.update_user(user)
100
+ status(204)
101
+ nil
102
+ end
103
+
104
+ delete '/users/:username' do
105
+ @user_manager.delete_user(params[:username])
106
+ status(204)
107
+ nil
108
+ end
109
+
110
+ post '/packages/matches', :consumes => :yaml do
111
+ manifest = Psych.load(request.body)
112
+ unless manifest.is_a?(Hash) && manifest['packages'].is_a?(Array)
113
+ raise BadManifest, "Manifest doesn't have a usable packages section"
114
+ end
115
+
116
+ fp_list = []
117
+ sha1_list = []
118
+
119
+ manifest['packages'].each do |package|
120
+ fp_list << package['fingerprint'] if package['fingerprint']
121
+ sha1_list << package['sha1'] if package['sha1']
122
+ end
123
+
124
+ filter = {:fingerprint => fp_list, :sha1 => sha1_list}.sql_or
125
+
126
+ result = Models::Package.where(filter).all.map { |package|
127
+ [package.sha1, package.fingerprint]
128
+ }.flatten.compact.uniq
129
+
130
+ json_encode(result)
131
+ end
132
+
133
+ post '/releases', :consumes => :tgz do
134
+ options = {}
135
+ options['remote'] = false
136
+ options['rebase'] = true if params['rebase'] == 'true'
137
+
138
+ task = @release_manager.create_release(@user, request.body, options)
139
+ redirect "/tasks/#{task.id}"
140
+ end
141
+
142
+ post '/releases', :consumes => :json do
143
+ options = {}
144
+ options['remote'] = true
145
+ options['rebase'] = true if params['rebase'] == 'true'
146
+ payload = json_decode(request.body)
147
+
148
+ task = @release_manager.create_release(@user, payload['location'], options)
149
+ redirect "/tasks/#{task.id}"
150
+ end
151
+
152
+ get '/releases' do
153
+ releases = Models::Release.order_by(:name.asc).map do |release|
154
+ release_versions = release.versions_dataset.order_by(:version.asc).map do |rv|
155
+ Hash['version', rv.version.to_s,
156
+ 'commit_hash', rv.commit_hash,
157
+ 'uncommitted_changes', rv.uncommitted_changes,
158
+ 'currently_deployed', !rv.deployments.empty?,
159
+ 'job_names', rv.templates.map(&:name)]
160
+ end
161
+
162
+ Hash['name', release.name,
163
+ 'release_versions', release_versions]
164
+ end
165
+
166
+ json_encode(releases)
167
+ end
168
+
169
+ get '/releases/:name' do
170
+ name = params[:name].to_s.strip
171
+ release = @release_manager.find_by_name(name)
172
+
173
+ result = { }
174
+
175
+ result['packages'] = release.packages.map do |package|
176
+ {
177
+ 'name' => package.name,
178
+ 'sha1' => package.sha1,
179
+ 'version' => package.version.to_s,
180
+ 'dependencies' => package.dependency_set.to_a
181
+ }
182
+ end
183
+
184
+ result['jobs'] = release.templates.map do |template|
185
+ {
186
+ 'name' => template.name,
187
+ 'sha1' => template.sha1,
188
+ 'version' => template.version.to_s,
189
+ 'packages' => template.package_names
190
+ }
191
+ end
192
+
193
+ result['versions'] = release.versions.map do |rv|
194
+ rv.version.to_s
195
+ end
196
+
197
+ content_type(:json)
198
+ json_encode(result)
199
+ end
200
+
201
+ delete '/releases/:name' do
202
+ release = @release_manager.find_by_name(params[:name])
203
+
204
+ options = {}
205
+ options['force'] = true if params['force'] == 'true'
206
+ options['version'] = params['version']
207
+
208
+ task = @release_manager.delete_release(@user, release, options)
209
+ redirect "/tasks/#{task.id}"
210
+ end
211
+
212
+ post '/stemcells', :consumes => :tgz do
213
+ task = @stemcell_manager.create_stemcell(@user, request.body, :remote => false)
214
+ redirect "/tasks/#{task.id}"
215
+ end
216
+
217
+ post '/stemcells', :consumes => :json do
218
+ payload = json_decode(request.body)
219
+ task = @stemcell_manager.create_stemcell(@user, payload['location'], :remote => true)
220
+ redirect "/tasks/#{task.id}"
221
+ end
222
+
223
+ get '/stemcells' do
224
+ stemcells = Models::Stemcell.order_by(:name.asc).map do |stemcell|
225
+ {
226
+ 'name' => stemcell.name,
227
+ 'version' => stemcell.version,
228
+ 'cid' => stemcell.cid
229
+ }
230
+ end
231
+ json_encode(stemcells)
232
+ end
233
+
234
+ delete '/stemcells/:name/:version' do
235
+ name, version = params[:name], params[:version]
236
+ options = {}
237
+ options['force'] = true if params['force'] == 'true'
238
+ stemcell = @stemcell_manager.find_by_name_and_version(name, version)
239
+ task = @stemcell_manager.delete_stemcell(@user, stemcell, options)
240
+ redirect "/tasks/#{task.id}"
241
+ end
242
+
243
+ post '/deployments', :consumes => :yaml do
244
+ options = {}
245
+ options['recreate'] = true if params['recreate'] == 'true'
246
+
247
+ task = @deployment_manager.create_deployment(@user, request.body, options)
248
+ redirect "/tasks/#{task.id}"
249
+ end
250
+
251
+ get '/deployments/:deployment/jobs/:job/:index' do
252
+ instance = @instance_manager.find_by_name(params[:deployment], params[:job], params[:index])
253
+
254
+ response = {
255
+ deployment: params[:deployment],
256
+ job: instance.job,
257
+ index: instance.index,
258
+ state: instance.state,
259
+ disks: instance.persistent_disks.map {|d| d.disk_cid}
260
+ }
261
+
262
+ json_encode(response)
263
+ end
264
+
265
+ # PUT /deployments/foo/jobs/dea?new_name=dea_new
266
+ put '/deployments/:deployment/jobs/:job', :consumes => :yaml do
267
+ if params['state']
268
+ options = {
269
+ 'job_states' => {
270
+ params[:job] => {
271
+ 'state' => params['state']
272
+ }
273
+ }
274
+ }
275
+ else
276
+ unless params['new_name']
277
+ raise DirectorError, "Missing operation on job `#{params[:job]}'"
278
+ end
279
+ options = {
280
+ 'job_rename' => {
281
+ 'old_name' => params[:job],
282
+ 'new_name' => params['new_name']
283
+ }
284
+ }
285
+ options['job_rename']['force'] = true if params['force'] == 'true'
286
+ end
287
+
288
+ # we get the deployment here even though it isn't used here, to make sure
289
+ # the call returns a 404 if the deployment doesn't exist
290
+ @deployment_manager.find_by_name(params[:deployment])
291
+ task = @deployment_manager.create_deployment(@user, request.body, options)
292
+ redirect "/tasks/#{task.id}"
293
+ end
294
+
295
+ # PUT /deployments/foo/jobs/dea/2?state={started,stopped,detached,restart,recreate}
296
+ put '/deployments/:deployment/jobs/:job/:index', :consumes => :yaml do
297
+ begin
298
+ index = Integer(params[:index])
299
+ rescue ArgumentError
300
+ raise InstanceInvalidIndex, "Invalid instance index `#{params[:index]}'"
301
+ end
302
+
303
+ options = {
304
+ 'job_states' => {
305
+ params[:job] => {
306
+ 'instance_states' => {
307
+ index => params['state']
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ deployment = @deployment_manager.find_by_name(params[:deployment])
314
+ manifest = request.content_length.nil? ? StringIO.new(deployment.manifest) : request.body
315
+ task = @deployment_manager.create_deployment(@user, manifest, options)
316
+ redirect "/tasks/#{task.id}"
317
+ end
318
+
319
+ # GET /deployments/foo/jobs/dea/2/logs
320
+ get '/deployments/:deployment/jobs/:job/:index/logs' do
321
+ deployment = params[:deployment]
322
+ job = params[:job]
323
+ index = params[:index]
324
+
325
+ options = {
326
+ 'type' => params[:type].to_s.strip,
327
+ 'filters' => params[:filters].to_s.strip.split(/[\s\,]+/)
328
+ }
329
+
330
+ task = @instance_manager.fetch_logs(@user, deployment, job, index, options)
331
+ redirect "/tasks/#{task.id}"
332
+ end
333
+
334
+ get '/deployments/:deployment/snapshots' do
335
+ deployment = @deployment_manager.find_by_name(params[:deployment])
336
+ json_encode(@snapshot_manager.snapshots(deployment))
337
+ end
338
+
339
+ get '/deployments/:deployment/jobs/:job/:index/snapshots' do
340
+ deployment = @deployment_manager.find_by_name(params[:deployment])
341
+ json_encode(@snapshot_manager.snapshots(deployment, params[:job], params[:index]))
342
+ end
343
+
344
+ post '/deployments/:deployment/snapshots' do
345
+ deployment = @deployment_manager.find_by_name(params[:deployment])
346
+ # until we can tell the agent to flush and wait, all snapshots are considered dirty
347
+ options = {clean: false}
348
+
349
+ task = @snapshot_manager.create_deployment_snapshot_task(@user, deployment, options)
350
+ redirect "/tasks/#{task.id}"
351
+ end
352
+
353
+ put '/deployments/:deployment/jobs/:job/:index/resurrection', consumes: :json do
354
+ payload = json_decode(request.body)
355
+
356
+ @resurrector_manager.set_pause_for_instance(params[:deployment], params[:job], params[:index], payload['resurrection_paused'])
357
+ end
358
+
359
+ post '/deployments/:deployment/jobs/:job/:index/snapshots' do
360
+ instance = @instance_manager.find_by_name(params[:deployment], params[:job], params[:index])
361
+ # until we can tell the agent to flush and wait, all snapshots are considered dirty
362
+ options = {clean: false}
363
+
364
+ task = @snapshot_manager.create_snapshot_task(@user, instance, options)
365
+ redirect "/tasks/#{task.id}"
366
+ end
367
+
368
+ delete '/deployments/:deployment/snapshots' do
369
+ deployment = @deployment_manager.find_by_name(params[:deployment])
370
+
371
+ task = @snapshot_manager.delete_deployment_snapshots_task(@user, deployment)
372
+ redirect "/tasks/#{task.id}"
373
+ end
374
+
375
+ delete '/deployments/:deployment/snapshots/:cid' do
376
+ deployment = @deployment_manager.find_by_name(params[:deployment])
377
+ snapshot = @snapshot_manager.find_by_cid(deployment, params[:cid])
378
+
379
+ task = @snapshot_manager.delete_snapshots_task(@user, [params[:cid]])
380
+ redirect "/tasks/#{task.id}"
381
+ end
382
+
383
+ get '/deployments' do
384
+ deployments = Models::Deployment.order_by(:name.asc).map { |deployment|
385
+ name = deployment.name
386
+
387
+ releases = deployment.release_versions.map { |rv|
388
+ Hash['name', rv.release.name, 'version', rv.version.to_s]
389
+ }
390
+
391
+ stemcells = deployment.stemcells.map { |sc|
392
+ Hash['name', sc.name, 'version', sc.version]
393
+ }
394
+
395
+ Hash['name', name, 'releases', releases, 'stemcells', stemcells]
396
+ }
397
+
398
+ json_encode(deployments)
399
+ end
400
+
401
+ get '/deployments/:name' do
402
+ deployment = @deployment_manager.find_by_name(params[:name])
403
+ @deployment_manager.deployment_to_json(deployment)
404
+ end
405
+
406
+ get '/deployments/:name/vms' do
407
+ deployment = @deployment_manager.find_by_name(params[:name])
408
+
409
+ format = params[:format]
410
+ if format == 'full'
411
+ task = @vm_state_manager.fetch_vm_state(@user, deployment, format)
412
+ redirect "/tasks/#{task.id}"
413
+ else
414
+ @deployment_manager.deployment_vms_to_json(deployment)
415
+ end
416
+ end
417
+
418
+ delete '/deployments/:name' do
419
+ deployment = @deployment_manager.find_by_name(params[:name])
420
+
421
+ options = {}
422
+ options['force'] = true if params['force'] == 'true'
423
+ options['keep_snapshots'] = true if params['keep_snapshots'] == 'true'
424
+ task = @deployment_manager.delete_deployment(@user, deployment, options)
425
+ redirect "/tasks/#{task.id}"
426
+ end
427
+
428
+ # Property management
429
+ get '/deployments/:deployment/properties' do
430
+ properties = @property_manager.get_properties(params[:deployment]).map do |property|
431
+ { 'name' => property.name, 'value' => property.value }
432
+ end
433
+ json_encode(properties)
434
+ end
435
+
436
+ get '/deployments/:deployment/properties/:property' do
437
+ property = @property_manager.get_property(params[:deployment], params[:property])
438
+ json_encode('value' => property.value)
439
+ end
440
+
441
+ post '/deployments/:deployment/properties', :consumes => [:json] do
442
+ payload = json_decode(request.body)
443
+ @property_manager.create_property(params[:deployment], payload['name'], payload['value'])
444
+ status(204)
445
+ end
446
+
447
+ post '/deployments/:deployment/ssh', :consumes => [:json] do
448
+ payload = json_decode(request.body)
449
+ task = @instance_manager.ssh(@user, payload)
450
+ redirect "/tasks/#{task.id}"
451
+ end
452
+
453
+ put '/deployments/:deployment/properties/:property', :consumes => [:json] do
454
+ payload = json_decode(request.body)
455
+ @property_manager.update_property(params[:deployment], params[:property], payload['value'])
456
+ status(204)
457
+ end
458
+
459
+ delete '/deployments/:deployment/properties/:property' do
460
+ @property_manager.delete_property(params[:deployment], params[:property])
461
+ status(204)
462
+ end
463
+
464
+ # Cloud check
465
+
466
+ # Initiate deployment scan
467
+ post '/deployments/:deployment/scans' do
468
+ start_task { @problem_manager.perform_scan(@user, params[:deployment]) }
469
+ end
470
+
471
+ # Get the list of problems for a particular deployment
472
+ get '/deployments/:deployment/problems' do
473
+ problems = @problem_manager.get_problems(params[:deployment]).map do |problem|
474
+ {
475
+ 'id' => problem.id,
476
+ 'type' => problem.type,
477
+ 'data' => problem.data,
478
+ 'description' => problem.description,
479
+ 'resolutions' => problem.resolutions
480
+ }
481
+ end
482
+
483
+ json_encode(problems)
484
+ end
485
+
486
+ # Try to resolve a set of problems
487
+ put '/deployments/:deployment/problems', :consumes => [:json] do
488
+ payload = json_decode(request.body)
489
+ start_task { @problem_manager.apply_resolutions(@user, params[:deployment], payload['resolutions']) }
490
+ end
491
+
492
+ put '/deployments/:deployment/scan_and_fix', :consumes => :json do
493
+ jobs_json = json_decode(request.body)['jobs']
494
+ # payload: [['j1', 'i1'], ['j1', 'i2'], ['j2', 'i1'], ...]
495
+ payload = convert_job_instance_hash(jobs_json)
496
+
497
+ start_task { @problem_manager.scan_and_fix(@user, params[:deployment], payload) }
498
+ end
499
+
500
+ get '/tasks' do
501
+ dataset = Models::Task.dataset
502
+ limit = params['limit']
503
+ if limit
504
+ limit = limit.to_i
505
+ limit = 1 if limit < 1
506
+ dataset = dataset.limit(limit)
507
+ end
508
+
509
+ states = params['state'].to_s.split(',')
510
+
511
+ if states.size > 0
512
+ dataset = dataset.filter(:state => states)
513
+ end
514
+
515
+ verbose = params['verbose'] || '1'
516
+ if verbose == '1'
517
+ dataset = dataset.filter(type: %w[
518
+ update_deployment
519
+ delete_deployment
520
+ update_release
521
+ delete_release
522
+ update_stemcell
523
+ delete_stemcell
524
+ create_snapshot
525
+ delete_snapshot
526
+ snapshot_deployment
527
+ ])
528
+ end
529
+
530
+ tasks = dataset.order_by(:timestamp.desc).map do |task|
531
+ if task_timeout?(task)
532
+ task.state = :timeout
533
+ task.save
534
+ end
535
+ @task_manager.task_to_hash(task)
536
+ end
537
+
538
+ content_type(:json)
539
+ json_encode(tasks)
540
+ end
541
+
542
+ get '/tasks/:id' do
543
+ task = @task_manager.find_task(params[:id])
544
+ if task_timeout?(task)
545
+ task.state = :timeout
546
+ task.save
547
+ end
548
+
549
+ content_type(:json)
550
+ json_encode(@task_manager.task_to_hash(task))
551
+ end
552
+
553
+ # Sends back output of given task id and params[:type]
554
+ # Example: `get /tasks/5/output?type=event` will send back the file
555
+ # at /var/vcap/store/director/tasks/5/event
556
+ get '/tasks/:id/output' do
557
+ log_type = params[:type] || 'debug'
558
+ task = @task_manager.find_task(params[:id])
559
+
560
+ if task.output.nil?
561
+ halt(204)
562
+ end
563
+
564
+ log_file = @task_manager.log_file(task, log_type)
565
+
566
+ if File.file?(log_file)
567
+ send_file(log_file, :type => 'text/plain')
568
+ else
569
+ status(204)
570
+ end
571
+ end
572
+
573
+ delete '/task/:id' do
574
+ task_id = params[:id]
575
+ task = @task_manager.find_task(task_id)
576
+
577
+ if task.state != 'processing' && task.state != 'queued'
578
+ status(400)
579
+ body("Cannot cancel task #{task_id}: invalid state (#{task.state})")
580
+ else
581
+ task.state = :cancelling
582
+ task.save
583
+ status(204)
584
+ body("Cancelling task #{task_id}")
585
+ end
586
+ end
587
+
588
+ # JMS and MB: We don't know why this code exists. According to JP it shouldn't. We want to remove it.
589
+ # To get comforable with that idea, we log something we can look for in production.
590
+ #
591
+ # GET /resources/deadbeef
592
+ get '/resources/:id' do
593
+ @logger.warn('Something is proxying a blob through the director. Find out why before we remove this method. ZAUGYZ')
594
+ tmp_file = @resource_manager.get_resource_path(params[:id])
595
+ send_disposable_file(tmp_file, :type => 'application/x-gzip')
596
+ end
597
+
598
+ post '/backups' do
599
+ start_task { @backup_manager.create_backup(@user) }
600
+ end
601
+
602
+ get '/backups' do
603
+ send_file @backup_manager.destination_path
604
+ end
605
+
606
+ get '/info' do
607
+ status = {
608
+ 'name' => Config.name,
609
+ 'uuid' => Config.uuid,
610
+ 'version' => "#{VERSION} (#{Config.revision})",
611
+ 'user' => @user,
612
+ 'cpi' => Config.cloud_type,
613
+ 'features' => {
614
+ 'dns' => {
615
+ 'status' => Config.dns_enabled?,
616
+ 'extras' => { 'domain_name' => dns_domain_name }
617
+ },
618
+ 'compiled_package_cache' => {
619
+ 'status' => Config.use_compiled_package_cache?,
620
+ 'extras' => { 'provider' => Config.compiled_package_cache_provider }
621
+ },
622
+ 'snapshots' => {
623
+ 'status' => Config.enable_snapshots
624
+ }
625
+ }
626
+ }
627
+ content_type(:json)
628
+ json_encode(status)
629
+ end
630
+
631
+ put '/resurrection', consumes: :json do
632
+ payload = json_decode(request.body)
633
+
634
+ @resurrector_manager.set_pause_for_all(payload['resurrection_paused'])
635
+ status(200)
636
+ end
637
+ end
638
+ end
639
+ end