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,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