deltacloud-core 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/Rakefile +10 -2
  2. data/bin/deltacloudd +10 -10
  3. data/config.ru +2 -1
  4. data/config/drivers/digitalocean.yaml +3 -0
  5. data/deltacloud-core.gemspec +13 -6
  6. data/lib/cimi/collections.rb +1 -1
  7. data/lib/cimi/collections/address_templates.rb +27 -3
  8. data/lib/cimi/collections/addresses.rb +1 -1
  9. data/lib/cimi/collections/base.rb +1 -0
  10. data/lib/cimi/collections/cloud_entry_point.rb +4 -0
  11. data/lib/cimi/collections/credentials.rb +2 -2
  12. data/lib/cimi/collections/machine_images.rb +20 -0
  13. data/lib/cimi/collections/machine_templates.rb +72 -0
  14. data/lib/cimi/collections/machines.rb +50 -41
  15. data/lib/cimi/collections/network_ports.rb +3 -3
  16. data/lib/cimi/collections/networks.rb +4 -4
  17. data/lib/cimi/collections/resource_metadata.rb +1 -1
  18. data/lib/cimi/collections/volume_configurations.rb +25 -0
  19. data/lib/cimi/collections/volume_images.rb +21 -1
  20. data/lib/cimi/collections/volume_templates.rb +69 -0
  21. data/lib/cimi/collections/volumes.rb +5 -10
  22. data/lib/cimi/dependencies.rb +0 -1
  23. data/lib/cimi/helpers.rb +4 -1
  24. data/lib/cimi/helpers/cimi_helper.rb +62 -0
  25. data/lib/cimi/helpers/database_helper.rb +95 -0
  26. data/lib/cimi/models.rb +15 -1
  27. data/lib/cimi/models/address.rb +10 -5
  28. data/lib/cimi/models/address_template.rb +67 -3
  29. data/lib/cimi/models/base.rb +8 -5
  30. data/lib/cimi/models/cloud_entry_point.rb +6 -1
  31. data/lib/cimi/models/collection.rb +9 -4
  32. data/lib/cimi/models/disk.rb +6 -1
  33. data/lib/cimi/models/errors.rb +8 -0
  34. data/lib/cimi/models/machine.rb +68 -42
  35. data/lib/cimi/models/machine_configuration.rb +2 -2
  36. data/lib/cimi/models/machine_image.rb +41 -6
  37. data/lib/cimi/models/machine_template.rb +58 -0
  38. data/lib/cimi/models/machine_volume.rb +51 -3
  39. data/lib/cimi/models/resource_metadata.rb +88 -25
  40. data/lib/cimi/models/schema.rb +19 -5
  41. data/lib/cimi/models/volume.rb +66 -30
  42. data/lib/cimi/models/volume_configuration.rb +53 -21
  43. data/lib/cimi/models/volume_image.rb +24 -9
  44. data/lib/cimi/models/volume_template.rb +61 -0
  45. data/lib/cimi/server.rb +0 -6
  46. data/lib/db.rb +82 -0
  47. data/lib/db/address_template.rb +15 -0
  48. data/lib/db/entity.rb +22 -0
  49. data/lib/db/machine_template.rb +10 -0
  50. data/lib/db/provider.rb +13 -0
  51. data/lib/db/volume_configuration.rb +10 -0
  52. data/lib/db/volume_template.rb +10 -0
  53. data/lib/deltacloud/collections/addresses.rb +1 -1
  54. data/lib/deltacloud/collections/base.rb +12 -1
  55. data/lib/deltacloud/collections/buckets.rb +41 -8
  56. data/lib/deltacloud/collections/drivers.rb +1 -1
  57. data/lib/deltacloud/collections/firewalls.rb +2 -2
  58. data/lib/deltacloud/collections/images.rb +1 -1
  59. data/lib/deltacloud/collections/instances.rb +7 -1
  60. data/lib/deltacloud/collections/keys.rb +1 -1
  61. data/lib/deltacloud/collections/load_balancers.rb +4 -4
  62. data/lib/deltacloud/collections/storage_snapshots.rb +5 -1
  63. data/lib/deltacloud/collections/storage_volumes.rb +7 -3
  64. data/lib/deltacloud/drivers/base_driver.rb +10 -9
  65. data/lib/deltacloud/drivers/cimi_features.rb +42 -0
  66. data/lib/deltacloud/drivers/digitalocean/digitalocean_driver.rb +307 -0
  67. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +40 -14
  68. data/lib/deltacloud/drivers/exceptions.rb +8 -0
  69. data/lib/deltacloud/drivers/features.rb +19 -2
  70. data/lib/deltacloud/drivers/fgcp/fgcp_client.rb +11 -0
  71. data/lib/deltacloud/drivers/fgcp/fgcp_driver.rb +83 -11
  72. data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +1 -1
  73. data/lib/deltacloud/drivers/mock/mock_client.rb +2 -4
  74. data/lib/deltacloud/drivers/mock/mock_driver.rb +29 -0
  75. data/lib/deltacloud/drivers/openstack/openstack_driver.rb +153 -36
  76. data/lib/deltacloud/drivers/rackspace/anti_cache_monkey_patch.rb +20 -0
  77. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +1 -0
  78. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +30 -12
  79. data/lib/deltacloud/drivers/sbc/sbc_client.rb +0 -1
  80. data/lib/deltacloud/drivers/sbc/sbc_driver.rb +5 -1
  81. data/lib/deltacloud/drivers/vsphere/vsphere_client.rb +1 -1
  82. data/lib/deltacloud/drivers/vsphere/vsphere_driver.rb +19 -9
  83. data/lib/deltacloud/helpers/blob_stream_helper.rb +42 -3
  84. data/lib/deltacloud/helpers/deltacloud_helper.rb +31 -14
  85. data/lib/deltacloud/models/address.rb +9 -0
  86. data/lib/deltacloud/models/base_model.rb +4 -0
  87. data/lib/deltacloud/models/blob.rb +12 -0
  88. data/lib/deltacloud/models/bucket.rb +9 -0
  89. data/lib/deltacloud/models/firewall.rb +13 -1
  90. data/lib/deltacloud/models/firewall_rule.rb +14 -0
  91. data/lib/deltacloud/models/hardware_profile.rb +14 -0
  92. data/lib/deltacloud/models/image.rb +15 -0
  93. data/lib/deltacloud/models/instance.rb +40 -1
  94. data/lib/deltacloud/models/instance_address.rb +9 -0
  95. data/lib/deltacloud/models/key.rb +15 -0
  96. data/lib/deltacloud/models/load_balancer.rb +20 -0
  97. data/lib/deltacloud/models/metric.rb +15 -0
  98. data/lib/deltacloud/models/provider.rb +8 -0
  99. data/lib/deltacloud/models/realm.rb +9 -0
  100. data/lib/deltacloud/models/state_machine.rb +8 -0
  101. data/lib/deltacloud/models/storage_snapshot.rb +11 -0
  102. data/lib/deltacloud/models/storage_volume.rb +24 -0
  103. data/lib/deltacloud/server.rb +1 -3
  104. data/lib/deltacloud/version.rb +2 -1
  105. data/lib/deltacloud_rack.rb +1 -1
  106. data/tests/cimi/db/database_helper_test.rb +190 -0
  107. data/tests/cimi/db/db_helper.rb +30 -0
  108. data/tests/cimi/db/schema_test.rb +94 -0
  109. data/tests/cimi/model/collection_spec.rb +0 -1
  110. data/tests/cimi/model/machine_spec.rb +17 -0
  111. data/tests/cimi/spec_helper.rb +3 -2
  112. data/tests/deltacloud/collections/buckets_collection_test.rb +3 -0
  113. data/tests/deltacloud/collections/drivers_collection_test.rb +10 -0
  114. data/tests/deltacloud/collections/hardware_profiles_collection_test.rb +4 -0
  115. data/tests/deltacloud/collections/images_collection_test.rb +4 -0
  116. data/tests/deltacloud/collections/instances_collection_test.rb +14 -1
  117. data/tests/deltacloud/collections/keys_collection_test.rb +4 -0
  118. data/tests/deltacloud/collections/realms_collection_test.rb +47 -0
  119. data/tests/deltacloud/collections/storage_snapshots_collection_test.rb +47 -0
  120. data/tests/deltacloud/collections/storage_volumes_collection_test.rb +47 -0
  121. data/tests/deltacloud/common.rb +15 -0
  122. data/tests/deltacloud/deltacloud_helper_test.rb +0 -4
  123. data/tests/deltacloud/launcher_test.rb +108 -0
  124. data/tests/drivers/ec2/buckets_test.rb +2 -1
  125. data/tests/drivers/mock/buckets_test.rb +27 -0
  126. data/tests/drivers/mock/instances_test.rb +6 -0
  127. data/tests/drivers/openstack/common.rb +1 -1
  128. data/tests/drivers/openstack/hardware_profiles_test.rb +2 -1
  129. data/tests/drivers/openstack/instances_test.rb +18 -17
  130. data/tests/drivers/rhevm/common.rb +1 -0
  131. data/tests/drivers/rhevm/images_test.rb +1 -1
  132. data/tests/drivers/rhevm/instance_test.rb +7 -7
  133. data/tests/helpers/rack/rack_matrix_params_test.rb +0 -2
  134. data/tests/test_helper.rb +2 -1
  135. data/views/errors/403.xml.haml +6 -0
  136. data/views/errors/409.html.haml +47 -0
  137. data/views/errors/409.xml.haml +11 -0
  138. data/views/errors/502.html.haml +6 -5
  139. data/views/images/show.xml.haml +3 -2
  140. data/views/instances/new.html.haml +1 -1
  141. metadata +77 -30
@@ -39,7 +39,7 @@ module Deltacloud::Collections
39
39
  halt 404 unless @driver
40
40
  respond_to do |format|
41
41
  format.xml { haml :"drivers/show" }
42
- format.json { @driver.to_json }
42
+ format.json { { :driver => @driver.merge(:id => params[:id]) }.to_json }
43
43
  format.html { haml :"drivers/show" }
44
44
  end
45
45
  end
@@ -66,7 +66,7 @@ module Deltacloud::Collections
66
66
  respond_to do |format|
67
67
  format.xml { haml :"firewalls/show" }
68
68
  format.html { haml :"firewalls/show" }
69
- format.json { xml_to_json("firewalls/show") }
69
+ format.json { JSON::dump(:firewall => @firewall.to_hash(self)) }
70
70
  end
71
71
  end
72
72
  end
@@ -109,7 +109,7 @@ module Deltacloud::Collections
109
109
  respond_to do |format|
110
110
  format.xml { haml :"firewalls/show" }
111
111
  format.html { haml :"firewalls/show" }
112
- format.json { xml_to_json("firewalls/show") }
112
+ format.json { JSON::dump(:firewall => @firewall.to_hash(self) ) }
113
113
  end
114
114
  end
115
115
  end
@@ -50,7 +50,7 @@ module Deltacloud::Collections
50
50
  response['Location'] = image_url(@image.id)
51
51
  respond_to do |format|
52
52
  format.xml { haml :"images/show" }
53
- format.json { xml_to_json('images/show') }
53
+ format.json { JSON::dump(:image => @image.to_hash(self)) }
54
54
  format.html { haml :"images/show" }
55
55
  end
56
56
  end
@@ -60,7 +60,13 @@ module Deltacloud::Collections
60
60
  status 201 # Created
61
61
  respond_to do |format|
62
62
  format.xml { haml :"instances/#{action_handler}" }
63
- format.json { xml_to_json("instances/#{action_handler}") }
63
+ format.json do
64
+ if @elements
65
+ JSON::dump(:instances => @elements.map { |i| i.to_hash(self) })
66
+ elsif @instance and @instance.id
67
+ JSON::dump(:instance => @instance.to_hash(self))
68
+ end
69
+ end
64
70
  format.html do
65
71
  if @elements
66
72
  haml :"instances/index"
@@ -42,7 +42,7 @@ module Deltacloud::Collections
42
42
  respond_to do |format|
43
43
  format.xml { haml :"keys/show", :ugly => true }
44
44
  format.html { haml :"keys/show" }
45
- format.json { xml_to_json("keys/show")}
45
+ format.json { JSON::dump(:key => @key.to_hash(self)) }
46
46
  end
47
47
  end
48
48
  end
@@ -41,7 +41,7 @@ module Deltacloud::Collections
41
41
  @unregistered_instances = all_instances - @registered_instances
42
42
  respond_to do |format|
43
43
  format.xml { haml :'load_balancers/show' }
44
- format.json { xml_to_json('load_balancers/show') }
44
+ format.json { JSON::dump(:load_balancer => @load_balancer.to_hash(self)) }
45
45
  format.html { haml :'load_balancers/show' }
46
46
  end
47
47
  end
@@ -59,7 +59,7 @@ module Deltacloud::Collections
59
59
  response['Location'] = load_balancer_url(@load_balancer.id)
60
60
  respond_to do |format|
61
61
  format.xml { haml :"load_balancers/show" }
62
- format.json { xml_to_json("load_balancers/show")}
62
+ format.json { JSON::dump(:load_balancer => @load_balancer.to_hash(self)) }
63
63
  format.html { redirect load_balancer_url(@load_balancer.id)}
64
64
  end
65
65
  end
@@ -73,7 +73,7 @@ module Deltacloud::Collections
73
73
  @load_balancer = driver.load_balancer(credentials, :id => params[:id])
74
74
  respond_to do |format|
75
75
  format.xml { haml :'load_balancers/show' }
76
- format.json { xml_to_json('load_balancers/show') }
76
+ format.json { JSON::dump(:load_balancer => @load_balancer.to_hash(self) ) }
77
77
  format.html { redirect load_balancer_url(@load_balancer.id)}
78
78
  end
79
79
  end
@@ -87,7 +87,7 @@ module Deltacloud::Collections
87
87
  @load_balancer = driver.load_balancer(credentials, :id => params[:id])
88
88
  respond_to do |format|
89
89
  format.xml { haml :'load_balancers/show' }
90
- format.json { xml_to_json('load_balancers/show')}
90
+ format.json { JSON::dump(:load_balancer => @load_balancer.to_hash(self)) }
91
91
  format.html { redirect load_balancer_url(@load_balancer.id) }
92
92
  end
93
93
  end
@@ -31,7 +31,11 @@ module Deltacloud::Collections
31
31
  @storage_snapshot = driver.create_storage_snapshot(credentials, params)
32
32
  status 201 # Created
33
33
  response['Location'] = storage_snapshot_url(@storage_snapshot.id)
34
- show(:storage_snapshot)
34
+ respond_to do |format|
35
+ format.xml { haml :"storage_snapshots/show" }
36
+ format.html { haml :"storage_snapshots/show" }
37
+ format.json { xml_to_json "storage_snapshots/show" }
38
+ end
35
39
  end
36
40
  end
37
41
 
@@ -16,6 +16,8 @@
16
16
  module Deltacloud::Collections
17
17
  class StorageVolumes < Base
18
18
 
19
+ include Deltacloud::Features
20
+
19
21
  set :capability, lambda { |m| driver.respond_to? m }
20
22
  check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
21
23
 
@@ -37,6 +39,8 @@ module Deltacloud::Collections
37
39
  param :snapshot_id, :string, :optional
38
40
  param :capacity, :string, :optional
39
41
  param :realm_id, :string, :optional
42
+ param :name, :string, :optional
43
+ param :description, :string, :optional
40
44
  control do
41
45
  @storage_volume = driver.create_storage_volume(credentials, params)
42
46
  status 201
@@ -44,7 +48,7 @@ module Deltacloud::Collections
44
48
  respond_to do |format|
45
49
  format.xml { haml :"storage_volumes/show" }
46
50
  format.html { haml :"storage_volumes/show" }
47
- format.json { xml_to_json("storage_volumes/show") }
51
+ format.json { JSON::dump(:storage_volume => @storage_volume.to_hash(self)) }
48
52
  end
49
53
  end
50
54
  end
@@ -59,7 +63,7 @@ module Deltacloud::Collections
59
63
  respond_to do |format|
60
64
  format.html { redirect(storage_volume_url(params[:id]))}
61
65
  format.xml { haml :"storage_volumes/show" }
62
- format.json { xml_to_json("storage_volumes/show")}
66
+ format.json { JSON::dump(:storage_volume => @storage_volume.to_hash(self)) }
63
67
  end
64
68
  end
65
69
  end
@@ -75,7 +79,7 @@ module Deltacloud::Collections
75
79
  respond_to do |format|
76
80
  format.html { redirect(storage_volume_url(params[:id]))}
77
81
  format.xml { haml :"storage_volumes/show" }
78
- format.json { xml_to_json("storage_volumes/show") }
82
+ format.json { JSON::dump(:storage_volume => @storage_volume.to_hash(self)) }
79
83
  end
80
84
  end
81
85
  end
@@ -19,6 +19,7 @@ module Deltacloud
19
19
  require_relative '../core_ext.rb'
20
20
  require_relative './exceptions.rb'
21
21
  require_relative './features.rb'
22
+ require_relative './cimi_features.rb'
22
23
  require_relative '../models/state_machine.rb'
23
24
 
24
25
  class BaseDriver
@@ -26,7 +27,7 @@ module Deltacloud
26
27
  include Exceptions
27
28
 
28
29
  STATE_MACHINE_OPTS = {
29
- :all_states => [:start, :pending, :running, :stopping, :stopped, :finish],
30
+ :all_states => [:start, :pending, :running, :stopping, :stopped, :finish, :error],
30
31
  :all_actions => [:create, :reboot, :stop, :start, :destroy]
31
32
  } unless defined?(STATE_MACHINE_OPTS)
32
33
 
@@ -38,7 +39,6 @@ module Deltacloud
38
39
  @features ||= {}
39
40
  end
40
41
 
41
-
42
42
  def self.feature(collection, feature_name)
43
43
  return if has_feature?(collection, feature_name)
44
44
  constraints[collection] ||= {}
@@ -60,6 +60,10 @@ module Deltacloud
60
60
  features.has_key?(collection) and features[collection].include?(feature_name)
61
61
  end
62
62
 
63
+ def has_feature?(collection, feature_name)
64
+ self.class.has_feature?(collection, feature_name)
65
+ end
66
+
63
67
  def name
64
68
  self.class.name.split('::').last.gsub('Driver', '').downcase
65
69
  end
@@ -102,18 +106,15 @@ module Deltacloud
102
106
 
103
107
  def find_hardware_profile(credentials, profile_id, image_id)
104
108
  hwp = nil
105
- if name
109
+ if profile_id
106
110
  unless hwp = hardware_profile(credentials, profile_id)
107
- raise BackendError.new(400, "bad-hardware-profile-name",
108
- "Hardware profile '#{name}' does not exist", nil)
111
+ raise Exceptions.exception_from_status(400, "Hardware profile '#{profile_id}' does not exists.")
109
112
  end
110
113
  else
111
114
  unless image = image(credentials, :id=>image_id)
112
- raise BackendError.new(400, "bad-image-id",
113
- "Image with ID '#{image_id}' does not exist", nil)
115
+ raise Exceptions.exception_from_status(400, "Image #{image_id} does not exists.")
114
116
  end
115
- hwp = hardware_profiles(credentials,
116
- :architecture=>image.architecture).first
117
+ hwp = hardware_profiles(credentials, :architecture => image.architecture).first
117
118
  end
118
119
  return hwp
119
120
  end
@@ -0,0 +1,42 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership. The
4
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations
14
+ # under the License.
15
+
16
+ module CIMI
17
+ module Features
18
+
19
+ def self.included(k)
20
+ current_features = features
21
+ k.instance_eval do
22
+ features(&current_features)
23
+ end
24
+ end
25
+
26
+ def self.features(&block)
27
+ block_given? ? @features = block : @features || Proc.new{}
28
+ end
29
+
30
+
31
+ features do
32
+ feature :default_initial_state, :for => :machines do
33
+ description "Indicates what the default initial state of a new Machine "
34
+ end
35
+
36
+ feature :initial_states, :for => :machines do
37
+ description "Indicates the list of allowable initial states"
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,307 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership. The
4
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations
14
+ # under the License.
15
+
16
+ require 'restclient'
17
+
18
+ module Deltacloud
19
+ module Drivers
20
+ module Digitalocean
21
+ class DigitaloceanDriver < Deltacloud::BaseDriver
22
+
23
+ feature :instances, :user_name
24
+ feature :images, :owner_id
25
+
26
+ define_hardware_profile '66' do
27
+ cpu 1
28
+ memory 512
29
+ storage 20 * 1024
30
+ architecture ['i386', 'x86_64']
31
+ end
32
+
33
+ define_hardware_profile '63' do
34
+ cpu 1
35
+ memory 1024
36
+ storage 30 * 1024
37
+ architecture ['i386', 'x86_64']
38
+ end
39
+
40
+ define_hardware_profile '62' do
41
+ cpu 2
42
+ memory 2 * 1024
43
+ storage 40 * 1024
44
+ architecture ['i386', 'x86_64']
45
+ end
46
+
47
+ define_hardware_profile '64' do
48
+ cpu 2
49
+ memory 4 * 1024
50
+ storage 60 * 1024
51
+ architecture ['i386', 'x86_64']
52
+ end
53
+
54
+ define_hardware_profile '65' do
55
+ cpu 4
56
+ memory 8 * 1024
57
+ storage 80 * 1024
58
+ architecture ['i386', 'x86_64']
59
+ end
60
+
61
+ define_hardware_profile '61' do
62
+ cpu 8
63
+ memory 16 * 1024
64
+ storage 160 * 1024
65
+ architecture ['i386', 'x86_64']
66
+ end
67
+
68
+ define_hardware_profile '60' do
69
+ cpu 12
70
+ memory 32 * 1024
71
+ storage 320 * 1024
72
+ architecture ['i386', 'x86_64']
73
+ end
74
+
75
+ define_hardware_profile '70' do
76
+ cpu 16
77
+ memory 48 * 1024
78
+ storage 480 * 1024
79
+ architecture ['i386', 'x86_64']
80
+ end
81
+
82
+ define_hardware_profile '69' do
83
+ cpu 16
84
+ memory 64 * 1024
85
+ storage 640 * 1024
86
+ architecture ['i386', 'x86_64']
87
+ end
88
+
89
+ define_hardware_profile '68' do
90
+ cpu 24
91
+ memory 96 * 1024
92
+ storage 960 * 1024
93
+ architecture ['i386', 'x86_64']
94
+ end
95
+
96
+ define_instance_states do
97
+ start.to( :pending ) .on( :create )
98
+ pending.to( :running ) .automatically
99
+ running.to( :running ) .on( :reboot )
100
+ running.to( :stopping ) .on( :stop )
101
+ running.to( :finish ) .on( :destroy )
102
+ stopped.to( :running ) .on( :start )
103
+ stopping.to( :stopped ) .automatically
104
+ stopped.to( :finish ) .automatically
105
+ error.from(:running, :pending, :stopping)
106
+ end
107
+
108
+ def realms(credentials, opts={})
109
+ safely do
110
+ new_client(credentials).get('regions')['regions'].map do |r|
111
+ Realm.new(
112
+ :id => r['id'],
113
+ :name => r['name'],
114
+ :state => 'AVAILABLE',
115
+ :limit => :unlimited
116
+ )
117
+ end
118
+ end
119
+ end
120
+
121
+ # By default images will return list of 'all' images available
122
+ # to launch.
123
+ # With 'owner_id' you can filter them using 'global' and 'my_images'
124
+ # values to get less images.
125
+ #
126
+ def images(credentials, opts={})
127
+ unless opts[:id]
128
+ filter = opts[:owner_id] ? { :filter => opts[:owner_id] } : {}
129
+ img_arr = safely do
130
+ new_client(credentials).get('images', filter)['images'].map do |i|
131
+ convert_image(credentials, i)
132
+ end
133
+ end
134
+ filter_on( img_arr, :architecture, opts )
135
+ else
136
+ safely do
137
+ [convert_image(
138
+ credentials,
139
+ new_client(credentials).get('images/%s' % opts[:id])['image']
140
+ )]
141
+ end
142
+ end
143
+ end
144
+
145
+ # You can only destroy images you own.
146
+ #
147
+ def destroy_image(credentials, image_id)
148
+ safely do
149
+ new_client(credentials).get('images/%s/destroy', image_id)
150
+ end
151
+ end
152
+
153
+ def instances(credentials, opts={})
154
+ inst_arr = safely do
155
+ new_client(credentials).get('droplets')['droplets'].map do |i|
156
+ convert_instance(credentials.user, i)
157
+ end
158
+ end
159
+ filter_on inst_arr, :state, opts
160
+ end
161
+
162
+ def instance(credentials, opts={})
163
+ safely do
164
+ convert_instance(
165
+ credentials.user,
166
+ new_client(credentials).get("droplets/#{opts[:id]}")["droplet"]
167
+ )
168
+ end
169
+ end
170
+
171
+ def create_instance(credentials, image_id, opts={})
172
+ safely do
173
+ client = new_client(credentials)
174
+ args = { :image_id => image_id }
175
+ args.merge!(:region_id => opts[:realm_id]) if opts[:realm_id]
176
+ args.merge!(:size_id => opts[:hwp_id]) if opts[:hwp_id]
177
+ args.merge!(:name => opts[:name] || "inst#{Time.now.to_i}")
178
+ convert_instance(
179
+ credentials.user,
180
+ client.get("droplets/new", args)['droplet']
181
+ )
182
+ end
183
+ end
184
+
185
+ def destroy_instance(credentials, instance_id)
186
+ safely do
187
+ new_client(credentials).get("droplets/#{instance_id}/destroy/")
188
+ end
189
+ end
190
+
191
+ def stop_instance(credentials, instance_id)
192
+ safely do
193
+ new_client(credentials).get("droplets/#{instance_id}/shutdown")
194
+ end
195
+ end
196
+
197
+ def start_instance(credentials, instance_id)
198
+ safely do
199
+ new_client(credentials).get("droplets/#{instance_id}/power_on/")
200
+ end
201
+ end
202
+
203
+ def reboot_instance(credentials, instance_id)
204
+ safely do
205
+ new_client(credentials).get("droplets/#{instance_id}/reboot/")
206
+ end
207
+ end
208
+
209
+ exceptions do
210
+
211
+ on(/InternalServerError/) do
212
+ status 502
213
+ end
214
+
215
+ on(/No .*Found/) do
216
+ status 404
217
+ end
218
+
219
+ on(/An invalid/) do
220
+ status 400
221
+ end
222
+
223
+ end
224
+
225
+ private
226
+
227
+ class Client
228
+
229
+ API_URL = "https://api.digitalocean.com/"
230
+ attr_reader :credentials
231
+
232
+ def initialize(credentials)
233
+ @credentials = credentials
234
+ @resource = RestClient::Resource.new(API_URL)
235
+ end
236
+
237
+ def get(uri, opts={})
238
+ opts.merge!(
239
+ :client_id => credentials.user,
240
+ :api_key => credentials.password
241
+ )
242
+ result = @resource[uri].get(:params => opts)
243
+ json_result = JSON::parse(result)
244
+ if json_result['status'] != 'OK'
245
+ p result
246
+ error_message = json_result['error_message'] || json_result['status']
247
+ raise error_message
248
+ end
249
+ json_result
250
+ end
251
+
252
+ end
253
+
254
+ def new_client(credentials)
255
+ Client.new(credentials)
256
+ end
257
+
258
+ def extract_arch_from_name(name)
259
+ return 'x86_64' if name.include? 'x64'
260
+ return 'i386' if name.include? 'x32'
261
+ end
262
+
263
+ def convert_state(status)
264
+ case status
265
+ when 'active' then 'RUNNING'
266
+ when 'archive', 'new', 'pending' then 'PENDING'
267
+ when 'off' then 'STOPPED'
268
+ else status.upcase
269
+ end
270
+ end
271
+
272
+ def convert_instance(user, i)
273
+ state = convert_state(i['status'] || 'pending')
274
+ address = i['ip_address'].nil? ?
275
+ [] : [InstanceAddress.new(i['ip_address'])]
276
+
277
+ Instance.new(
278
+ :id => i['id'].to_s,
279
+ :name => i['name'],
280
+ :image_id => i['image_id'],
281
+ :realm_id => i['region_id'],
282
+ :instance_profile => InstanceProfile.new(i['size_id']),
283
+ :state => state,
284
+ :public_addresses => address,
285
+ :private_addresses => [],
286
+ :owner_id => user,
287
+ :create_image => false,
288
+ :actions => instance_actions_for(state)
289
+ )
290
+ end
291
+
292
+ def convert_image(credentials, i)
293
+ Image.new(
294
+ :id => i['id'].to_s,
295
+ :name => i['name'],
296
+ :description => i['distribution'],
297
+ :owner_id => 'global',
298
+ :state => 'AVAILABLE',
299
+ :architecture => extract_arch_from_name(i['name']),
300
+ :hardware_profiles => hardware_profiles(credentials)
301
+ )
302
+ end
303
+
304
+ end
305
+ end
306
+ end
307
+ end