bbrowning-deltacloud-core 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/COPYING +502 -0
  2. data/Rakefile +85 -0
  3. data/bin/deltacloudd +88 -0
  4. data/config.ru +5 -0
  5. data/deltacloud.rb +14 -0
  6. data/lib/deltacloud/base_driver.rb +19 -0
  7. data/lib/deltacloud/base_driver/base_driver.rb +189 -0
  8. data/lib/deltacloud/base_driver/features.rb +159 -0
  9. data/lib/deltacloud/base_driver/mock_driver.rb +37 -0
  10. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +340 -0
  11. data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +170 -0
  12. data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +45 -0
  13. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +322 -0
  14. data/lib/deltacloud/drivers/mock/mock_driver.rb +275 -0
  15. data/lib/deltacloud/drivers/opennebula/cloud_client.rb +116 -0
  16. data/lib/deltacloud/drivers/opennebula/occi_client.rb +204 -0
  17. data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +241 -0
  18. data/lib/deltacloud/drivers/rackspace/rackspace_client.rb +129 -0
  19. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +150 -0
  20. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +254 -0
  21. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb +84 -0
  22. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +144 -0
  23. data/lib/deltacloud/drivers/terremark/terremark_driver.rb +261 -0
  24. data/lib/deltacloud/hardware_profile.rb +153 -0
  25. data/lib/deltacloud/helpers.rb +5 -0
  26. data/lib/deltacloud/helpers/application_helper.rb +56 -0
  27. data/lib/deltacloud/helpers/conversion_helper.rb +38 -0
  28. data/lib/deltacloud/helpers/hardware_profiles_helper.rb +35 -0
  29. data/lib/deltacloud/method_serializer.rb +84 -0
  30. data/lib/deltacloud/models/base_model.rb +58 -0
  31. data/lib/deltacloud/models/image.rb +26 -0
  32. data/lib/deltacloud/models/instance.rb +37 -0
  33. data/lib/deltacloud/models/instance_profile.rb +47 -0
  34. data/lib/deltacloud/models/realm.rb +25 -0
  35. data/lib/deltacloud/models/storage_snapshot.rb +26 -0
  36. data/lib/deltacloud/models/storage_volume.rb +27 -0
  37. data/lib/deltacloud/state_machine.rb +84 -0
  38. data/lib/deltacloud/validation.rb +70 -0
  39. data/lib/drivers.rb +38 -0
  40. data/lib/sinatra/accept_media_types.rb +128 -0
  41. data/lib/sinatra/lazy_auth.rb +56 -0
  42. data/lib/sinatra/rabbit.rb +272 -0
  43. data/lib/sinatra/respond_to.rb +269 -0
  44. data/lib/sinatra/static_assets.rb +83 -0
  45. data/lib/sinatra/url_for.rb +51 -0
  46. data/public/favicon.ico +0 -0
  47. data/public/images/grid.png +0 -0
  48. data/public/images/logo-wide.png +0 -0
  49. data/public/images/rails.png +0 -0
  50. data/public/images/topbar-bg.png +0 -0
  51. data/public/javascripts/application.js +32 -0
  52. data/public/javascripts/jquery-1.4.2.min.js +154 -0
  53. data/public/stylesheets/compiled/application.css +613 -0
  54. data/public/stylesheets/compiled/ie.css +31 -0
  55. data/public/stylesheets/compiled/print.css +27 -0
  56. data/public/stylesheets/compiled/screen.css +456 -0
  57. data/server.rb +342 -0
  58. data/support/fedora/deltacloudd +68 -0
  59. data/support/fedora/rubygem-deltacloud-core.spec +91 -0
  60. data/tests/deltacloud_test.rb +60 -0
  61. data/tests/images_test.rb +94 -0
  62. data/tests/instances_test.rb +136 -0
  63. data/tests/realms_test.rb +56 -0
  64. data/tests/storage_snapshots_test.rb +48 -0
  65. data/tests/storage_volumes_test.rb +48 -0
  66. data/torquebox-ec2-config.ru +8 -0
  67. data/views/accounts/index.html.haml +11 -0
  68. data/views/accounts/show.html.haml +30 -0
  69. data/views/api/show.html.haml +15 -0
  70. data/views/api/show.xml.haml +5 -0
  71. data/views/docs/collection.html.haml +37 -0
  72. data/views/docs/collection.xml.haml +14 -0
  73. data/views/docs/index.html.haml +15 -0
  74. data/views/docs/index.xml.haml +5 -0
  75. data/views/docs/operation.html.haml +31 -0
  76. data/views/docs/operation.xml.haml +10 -0
  77. data/views/errors/auth_exception.html.haml +8 -0
  78. data/views/errors/auth_exception.xml.haml +2 -0
  79. data/views/errors/backend_error.html.haml +17 -0
  80. data/views/errors/backend_error.xml.haml +8 -0
  81. data/views/errors/validation_failure.html.haml +11 -0
  82. data/views/errors/validation_failure.xml.haml +7 -0
  83. data/views/hardware_profiles/index.html.haml +25 -0
  84. data/views/hardware_profiles/index.xml.haml +4 -0
  85. data/views/hardware_profiles/show.html.haml +19 -0
  86. data/views/hardware_profiles/show.xml.haml +17 -0
  87. data/views/images/index.html.haml +30 -0
  88. data/views/images/index.xml.haml +7 -0
  89. data/views/images/show.html.haml +21 -0
  90. data/views/images/show.xml.haml +5 -0
  91. data/views/instance_states/show.gv.erb +45 -0
  92. data/views/instance_states/show.html.haml +31 -0
  93. data/views/instance_states/show.xml.haml +8 -0
  94. data/views/instances/index.html.haml +30 -0
  95. data/views/instances/index.xml.haml +23 -0
  96. data/views/instances/new.html.haml +55 -0
  97. data/views/instances/show.html.haml +43 -0
  98. data/views/instances/show.xml.haml +41 -0
  99. data/views/layout.html.haml +26 -0
  100. data/views/realms/index.html.haml +29 -0
  101. data/views/realms/index.xml.haml +12 -0
  102. data/views/realms/show.html.haml +15 -0
  103. data/views/realms/show.xml.haml +10 -0
  104. data/views/root/index.html.haml +4 -0
  105. data/views/storage_snapshots/index.html.haml +20 -0
  106. data/views/storage_snapshots/index.xml.haml +11 -0
  107. data/views/storage_snapshots/show.html.haml +14 -0
  108. data/views/storage_snapshots/show.xml.haml +9 -0
  109. data/views/storage_volumes/index.html.haml +21 -0
  110. data/views/storage_volumes/index.xml.haml +13 -0
  111. data/views/storage_volumes/show.html.haml +20 -0
  112. data/views/storage_volumes/show.xml.haml +13 -0
  113. metadata +361 -0
data/Rakefile ADDED
@@ -0,0 +1,85 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
19
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
20
+
21
+ require 'rake'
22
+ require 'rake/testtask'
23
+ require 'rake/gempackagetask'
24
+
25
+ desc "Run basic unit tests"
26
+ Rake::TestTask.new("test") { |t|
27
+ t.test_files = FileList[
28
+ 'tests/realms_test.rb',
29
+ 'tests/images_test.rb',
30
+ 'tests/instances_test.rb',
31
+ 'tests/storage_volumes_test.rb',
32
+ 'tests/storage_snapshots_test.rb',
33
+ ]
34
+ t.verbose = false
35
+ t.warning = false
36
+ }
37
+
38
+ load 'deltacloud-core.gemspec'
39
+
40
+ Rake::GemPackageTask.new(@spec) do |pkg|
41
+ pkg.need_tar = true
42
+ end
43
+
44
+ desc "Install API"
45
+ task :install, [:install_dir, :bin_dir] do |t, args|
46
+
47
+ require 'fileutils'
48
+ require 'pp'
49
+
50
+ files = FileList[
51
+ Dir["config/**/**"],
52
+ Dir["features/**/**"],
53
+ Dir["lib/**/**"],
54
+ Dir["public/**/**"],
55
+ Dir["views/**/**"],
56
+ "config.ru",
57
+ "COPYING",
58
+ "README",
59
+ "*.rb"
60
+ ]
61
+
62
+ INSTALL_DIR=args.install_dir || "/usr/local/share/deltacloud-core"
63
+ BIN_DIR=args.bin_dir || "/usr/local/bin"
64
+
65
+ exit(1) unless FileUtils.mkdir_p(INSTALL_DIR)
66
+ exit(1) unless FileUtils.mkdir_p(BIN_DIR)
67
+
68
+ files.each do |f|
69
+ install_path = "#{INSTALL_DIR}/#{File.dirname(f)}"
70
+ unless File.directory?(install_path)
71
+ FileUtils.mkdir_p(install_path, :mode => 0755, :verbose => true)
72
+ end
73
+ next if File.directory?(f)
74
+ FileUtils.install(f, "#{INSTALL_DIR}/#{File.dirname(f)}", :verbose => true)
75
+ end
76
+
77
+ FileUtils.install('bin/deltacloudd', BIN_DIR, :verbose => true, :mode => 0755)
78
+ end
79
+
80
+ desc "Uninstall API"
81
+ task :uninstall do
82
+ require 'fileutils'
83
+ INSTALL_DIR="/usr/share/deltacloud-core"
84
+ FileUtils.rm_rf(INSTALL_DIR)
85
+ end
data/bin/deltacloudd ADDED
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'optparse'
5
+ require 'thin'
6
+
7
+ options = {
8
+ :env => 'development'
9
+ }
10
+ optparse = OptionParser.new do |opts|
11
+
12
+ opts.banner = <<BANNER
13
+ Usage:
14
+ deltacloudd -i <driver> [options]
15
+
16
+ Options:
17
+ BANNER
18
+ opts.on( '-i', '--driver DRIVER', 'Driver to use') do |driver|
19
+ ENV["API_DRIVER"] = driver
20
+ end
21
+ opts.on( '-r', '--hostname HOSTNAME',
22
+ 'Bind to HOST address (default: localhost)') do |host|
23
+ ENV["API_HOST"] = host
24
+ end
25
+ opts.on( '-p', '--port PORT', 'Use PORT (default: 3001)') do |port|
26
+ ENV["API_PORT"] = port
27
+ end
28
+ opts.on( '-e', '--env ENV', 'Environment (default: "development")') { |env| options[:env] = env }
29
+ opts.on( '-h', '--help', '') { options[:help] = true }
30
+ end
31
+
32
+ optparse.parse!
33
+
34
+ if options[:help]
35
+ puts optparse
36
+ exit(0)
37
+ end
38
+
39
+ unless ENV["API_DRIVER"]
40
+ puts "You need to specify a driver to use (-i <driver>)"
41
+ exit(1)
42
+ end
43
+
44
+ ENV["API_HOST"] = "localhost" unless ENV["API_HOST"]
45
+ ENV["API_PORT"] = "3001" unless ENV["API_PORT"]
46
+
47
+ dirname="#{File.dirname(__FILE__)}/.."
48
+
49
+ argv_opts = ARGV.clone
50
+ argv_opts << ['start'] unless Thin::Runner.commands.include?(options[0])
51
+ argv_opts << ['--address', ENV["API_HOST"] ]
52
+ argv_opts << ['--port', ENV["API_PORT"] ]
53
+ argv_opts << ['--rackup', 'config.ru' ]
54
+ argv_opts << ['--chdir', dirname ]
55
+ argv_opts << ['-e', options[:env] ]
56
+ argv_opts << ['--threaded', '-D', '--stats', '/stats']
57
+
58
+ argv_opts.flatten!
59
+
60
+ if options[:env] == "development"
61
+ use_rerun = false
62
+ begin
63
+ require "rerun"
64
+ use_rerun = true
65
+ rescue
66
+ # Do nothing
67
+ end
68
+ end
69
+
70
+ puts "Starting Deltacloud API :: #{ENV["API_DRIVER"]} :: http://#{ENV["API_HOST"]}:#{ENV["API_PORT"]}/api"
71
+ puts
72
+
73
+ if use_rerun
74
+ argv_opts.unshift "thin"
75
+ command = argv_opts.join(" ")
76
+ topdir = File::expand_path(File::join(File::dirname(__FILE__), ".."))
77
+ rerun = Rerun::Runner.new(command, :dir => topdir)
78
+ rerun.start
79
+ rerun.join
80
+ else
81
+ thin = Thin::Runner.new(argv_opts)
82
+
83
+ begin
84
+ thin.run!
85
+ rescue Exception => e
86
+ puts "ERROR: #{e.message}"
87
+ end
88
+ end
data/config.ru ADDED
@@ -0,0 +1,5 @@
1
+ require 'sinatra'
2
+ require 'rubygems'
3
+
4
+ require 'server.rb'
5
+ run Sinatra::Application
data/deltacloud.rb ADDED
@@ -0,0 +1,14 @@
1
+ # Add ./lib into load path
2
+ $:.unshift File.join(File.dirname(__FILE__), 'lib')
3
+
4
+ require 'deltacloud/base_driver'
5
+ require 'deltacloud/hardware_profile'
6
+ require 'deltacloud/state_machine'
7
+
8
+ require 'deltacloud/models/base_model'
9
+ require 'deltacloud/models/realm'
10
+ require 'deltacloud/models/image'
11
+ require 'deltacloud/models/instance'
12
+ require 'deltacloud/models/instance_profile'
13
+ require 'deltacloud/models/storage_snapshot'
14
+ require 'deltacloud/models/storage_volume'
@@ -0,0 +1,19 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ require 'deltacloud/base_driver/base_driver'
19
+ require 'deltacloud/base_driver/features'
@@ -0,0 +1,189 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module Deltacloud
19
+
20
+ class AuthException < Exception
21
+ end
22
+
23
+ class BackendError < StandardError
24
+ attr_reader :code, :cause, :details
25
+ def initialize(code, cause, message, details)
26
+ super(message)
27
+ @code = code
28
+ @cause = cause
29
+ @details = details
30
+ end
31
+ end
32
+
33
+ class BaseDriver
34
+
35
+ def self.define_hardware_profile(name,&block)
36
+ @hardware_profiles ||= []
37
+ hw_profile = @hardware_profiles.find{|e| e.name == name}
38
+ return if hw_profile
39
+ hw_profile = ::Deltacloud::HardwareProfile.new( name, &block )
40
+ @hardware_profiles << hw_profile
41
+ hw_params = hw_profile.params
42
+ unless hw_params.empty?
43
+ feature :instances, :hardware_profiles do
44
+ decl.operation(:create) { add_params(hw_params) }
45
+ end
46
+ end
47
+ end
48
+
49
+ def self.hardware_profiles
50
+ @hardware_profiles ||= []
51
+ @hardware_profiles
52
+ end
53
+
54
+ def hardware_profiles(credentials, opts = nil)
55
+ results = self.class.hardware_profiles
56
+ filter_hardware_profiles(results, opts)
57
+ end
58
+
59
+ def hardware_profile(credentials, name)
60
+ hardware_profiles(credentials, :name => name).first
61
+ end
62
+
63
+ def filter_hardware_profiles(profiles, opts)
64
+ if opts
65
+ if v = opts[:architecture]
66
+ profiles = profiles.select { |hwp| hwp.include?(:architecture, v) }
67
+ end
68
+ if v = opts[:name]
69
+ profiles = profiles.select { |hwp| hwp.name == v }
70
+ end
71
+ end
72
+ profiles
73
+ end
74
+
75
+ def find_hardware_profile(credentials, name, image_id)
76
+ hwp = nil
77
+ if name
78
+ unless hwp = hardware_profiles(credentials, :name => name).first
79
+ raise BackendError.new(400, "bad-hardware-profile-name",
80
+ "Hardware profile '#{name}' does not exist", nil)
81
+ end
82
+ else
83
+ unless image = image(credentials, :id=>image_id)
84
+ raise BackendError.new(400, "bad-image-id",
85
+ "Image with ID '#{image_id}' does not exist", nil)
86
+ end
87
+ hwp = hardware_profiles(credentials,
88
+ :architecture=>image.architecture).first
89
+ end
90
+ return hwp
91
+ end
92
+
93
+ def self.define_instance_states(&block)
94
+ machine = ::Deltacloud::StateMachine.new(&block)
95
+ @instance_state_machine = machine
96
+ end
97
+
98
+ def self.instance_state_machine
99
+ @instance_state_machine
100
+ end
101
+
102
+ def instance_state_machine
103
+ self.class.instance_state_machine
104
+ end
105
+
106
+ def instance_actions_for(state)
107
+ actions = []
108
+ state_key = state.downcase.to_sym
109
+ states = instance_state_machine.states()
110
+ current_state = states.find{|e| e.name == state.underscore.to_sym }
111
+ if ( current_state )
112
+ actions = current_state.transitions.collect{|e|e.action}
113
+ actions.reject!{|e| e.nil?}
114
+ end
115
+ actions
116
+ end
117
+
118
+ def realm(credentials, opts)
119
+ realms = realms(credentials, opts)
120
+ return realms.first unless realms.empty?
121
+ nil
122
+ end
123
+
124
+ def realms(credentials, opts=nil)
125
+ []
126
+ end
127
+
128
+ def image(credentials, opts)
129
+ images = images(credentials, opts)
130
+ return images.first unless images.empty?
131
+ nil
132
+ end
133
+
134
+ def images(credentials, ops)
135
+ []
136
+ end
137
+
138
+ def instance(credentials, opts)
139
+ instances = instances(credentials, opts)
140
+ return instances.first unless instances.empty?
141
+ nil
142
+ end
143
+
144
+ def instances(credentials, ops)
145
+ []
146
+ end
147
+
148
+ def create_instance(credentials, image_id, opts)
149
+ end
150
+ def start_instance(credentials, id)
151
+ end
152
+ def stop_instance(credentials, id)
153
+ end
154
+ def reboot_instance(credentials, id)
155
+ end
156
+
157
+ def storage_volume(credentials, opts)
158
+ volumes = storage_volumes(credentials, opts)
159
+ return volumes.first unless volumes.empty?
160
+ nil
161
+ end
162
+
163
+ def storage_volumes(credentials, ops)
164
+ []
165
+ end
166
+
167
+ def storage_snapshot(credentials, opts)
168
+ snapshots = storage_snapshots(credentials, opts)
169
+ return snapshots.first unless snapshots.empty?
170
+ nil
171
+ end
172
+
173
+ def storage_snapshots(credentials, ops)
174
+ []
175
+ end
176
+
177
+ def filter_on(collection, attribute, opts)
178
+ return collection if opts.nil?
179
+ return collection if opts[attribute].nil?
180
+ filter = opts[attribute]
181
+ if ( filter.is_a?( Array ) )
182
+ return collection.select{|e| filter.include?( e.send(attribute) ) }
183
+ else
184
+ return collection.select{|e| filter == e.send(attribute) }
185
+ end
186
+ end
187
+ end
188
+
189
+ end
@@ -0,0 +1,159 @@
1
+ require 'deltacloud/validation'
2
+
3
+ # Add advertising of optional features to the base driver
4
+ module Deltacloud
5
+
6
+ class FeatureError < StandardError; end
7
+ class DuplicateFeatureDeclError < FeatureError; end
8
+ class UndeclaredFeatureError < FeatureError; end
9
+
10
+ class BaseDriver
11
+
12
+ # An operation on a collection like cretae or show. Features
13
+ # can add parameters to operations
14
+ class Operation
15
+ attr_reader :name
16
+
17
+ include Deltacloud::Validation
18
+
19
+ def initialize(name, &block)
20
+ @name = name
21
+ @params = {}
22
+ instance_eval &block
23
+ end
24
+ end
25
+
26
+ # The declaration of a feature, defines what operations
27
+ # are modified by it
28
+ class FeatureDecl
29
+ attr_reader :name, :operations
30
+
31
+ def initialize(name, &block)
32
+ @name = name
33
+ @operations = []
34
+ instance_eval &block
35
+ end
36
+
37
+ def description(text=nil)
38
+ @description = text if text
39
+ @description
40
+ end
41
+
42
+ # Add a new operation or modify an existing one through BLOCK
43
+ def operation(name, &block)
44
+ unless op = @operations.find { |op| op.name == name }
45
+ op = Operation.new(name, &block)
46
+ @operations << op
47
+ else
48
+ op.instance_eval(&block) if block_given?
49
+ end
50
+ op
51
+ end
52
+ end
53
+
54
+ # A specific feature enabled by a driver (see +feature+)
55
+ class Feature
56
+ attr_reader :decl
57
+
58
+ def initialize(decl, &block)
59
+ @decl = decl
60
+ instance_eval &block if block_given?
61
+ end
62
+
63
+ def name
64
+ decl.name
65
+ end
66
+
67
+ def operations
68
+ decl.operations
69
+ end
70
+
71
+ def description
72
+ decl.description
73
+ end
74
+ end
75
+
76
+ def self.feature_decls
77
+ @@feature_decls ||= {}
78
+ end
79
+
80
+ def self.feature_decl_for(collection, name)
81
+ decls = feature_decls[collection]
82
+ if decls
83
+ decls.find { |dcl| dcl.name == name }
84
+ else
85
+ nil
86
+ end
87
+ end
88
+
89
+ # Declare a new feature
90
+ def self.declare_feature(collection, name, &block)
91
+ feature_decls[collection] ||= []
92
+ raise DuplicateFeatureDeclError if feature_decl_for(collection, name)
93
+ feature_decls[collection] << FeatureDecl.new(name, &block)
94
+ end
95
+
96
+ def self.features
97
+ @@features ||= {}
98
+ end
99
+
100
+ # Declare in a driver that it supports a specific feature
101
+ #
102
+ # The same feature can be declared multiple times in a driver, so that
103
+ # it can be changed successively by passing in different blocks.
104
+ def self.feature(collection, name, &block)
105
+ features[collection] ||= []
106
+ if f = features[collection].find { |f| f.name == name }
107
+ f.instance_eval(&block) if block_given?
108
+ return f
109
+ end
110
+ unless decl = feature_decl_for(collection, name)
111
+ raise UndeclaredFeatureError, "No feature #{name} for #{collection}"
112
+ end
113
+ features[collection] << Feature.new(decl, &block)
114
+ end
115
+
116
+ def features(collection)
117
+ self.class.features[collection] || []
118
+ end
119
+
120
+ #
121
+ # Declaration of optional features
122
+ #
123
+ declare_feature :instances, :user_name do
124
+ description "Accept a user-defined name on instance creation"
125
+ operation :create do
126
+ param :name, :string, :optional, nil,
127
+ "The user-defined name"
128
+ end
129
+ end
130
+
131
+ declare_feature :instances, :user_data do
132
+ description "Make user-defined data available on a special webserver"
133
+ operation :create do
134
+ param :user_data, :string, :optional, nil,
135
+ "Base64 encoded user data will be published to internal webserver"
136
+ end
137
+ end
138
+
139
+ declare_feature :instances, :authentication_key do
140
+ operation :create do
141
+ param :keyname, :string, :optional, nil
142
+ "EC2 key authentification method"
143
+ end
144
+ operation :show do
145
+ end
146
+ end
147
+
148
+ declare_feature :instances, :authentication_password do
149
+ operation :create do
150
+ param :password, :string, :optional
151
+ end
152
+ end
153
+
154
+ declare_feature :instances, :hardware_profiles do
155
+ description "Size instances according to changes to a hardware profile"
156
+ # The parameters are filled in from the hardware profiles
157
+ end
158
+ end
159
+ end