This series of patches adds support for hardware profiles to Deltacloud core. Hardware profiles describe how instances can be sized, and are more general than the old flavors, in that each property can either be a fixed value, a predefined enumeration of values, or a range of values.
To try these patches with the mock driver, you need to stop your server, rm -rf the mock storage root (usually /var/tmp/deltacloud-core-$USER), and start the server again once you have the patches applied.
Even if you don't try these patches, I'd highly appreciate feedback on all this, especially the XML representation of hardware profiles.
XML Representation ==================
The XML representation for hardware profiles looks like
<hardware-profile href="http://localhost:3001/api/hardware_profiles/m1-large"> <id>m1-large</id> <property kind="range" name="memory" unit="MB" value="10240"> <param href="http://localhost:3001/api/instances" method="post" name="hwp_memory" operation="create"/> <range first="7680.0" last="15360"/> </property> <property kind="enum" name="storage" unit="GB" value="850"> <param href="http://localhost:3001/api/instances" method="post" name="hwp_storage" operation="create"/> <enum> <entry value="850"/> <entry value="1024"/> </enum> </property> <property kind="fixed" name="architecture" unit="label" value="x86_64"/> <property kind="fixed" name="cpu" unit="count" value="2"/> </hardware-profile>
That is: a hardware profile has a URL (the href attribute), an <id> child giving its name, and a list of <property> children. A property can be one of three kinds; all properties have a name, a (default) value, and a unit. We will try and not gratuitously change units, i.e. for now memory is always reported in MB. The three kinds of properties are
* fixed: there's only one possible value for the property, the one in the value attribute * range: the property can take on any number between the first and last number from the <range> child. The range is inclusive of the first and last value. * enum: the property can take on one of a fixed set of values. The values are described inside an <enum> child, as the value attribute of the <entry> tags
Properties that can have more than one value, i.e. range and enum properties, also describe what operations are affected by them. Right now, this is only the create instance operation. The <param> child of the <property> element explains what parameter to pass to instance creation to change the value of that property.
Instance creation =================
When craeting an instance, you now have to pass the parameter hwp_id rather than flavor_id. The hwp_id has to be the <id> of an existing hardware profile. It is not necessary to pass any other hardware profile related parameters, since all proeprties have default values, and those are used for each property if only a hwp_id is given.
Optionally, additional parameters can be passed to instance creation to change some properties from their default value. With the above hardware profile, you could pass hwp_memory=15000 in addition to hwp_id=m1-large to get an instance with almost 15GB of memory.
Instance representation =======================
The XML for an instance now lists which properties had explicit values set when the instance was created; this is indicated in the XML as
<instance href="http://localhost:3001/api/instances/inst0"> <id>inst0</id> ... <hardware-profile href="http://localhost:3001/api/hardware_profiles/m1-large"> <id>m1-large</id> <property kind="fixed" name="memory" value="15000" unit="MB"/> </hardware-profile> ... </instance>
The 'instance profile' refers to an existing hardware profile by giving its URL and name, and uses <property> tags to show which properties have values differing from the hardware profile. The kind attribute on those tags is always 'fixed'.
Drivers =======
I've tested this pretty extensively, and at least for the EC2, Rackspace, and GoGrid drivers everything seems to work. I don't have a way to test the drivers for RimuHosting, RHEV-M, or OpenNebula. I know that these drivers need work, and would appreciate it if others could help out.
Patch series ============
The first 3 patches are minor cleanups, not directly related to hardware profiles. Patches 4-7 mutate hardware profiles to what we need, and add XML output for them. Patches 8-24 are concerned with integrating hardware profiles into the existing server and the drivers. Patch 25 removes any support with flavors - if you need to use flavors for a transitional period, that should be possible solely by not applying patch 25.
From: David Lutterkort lutter@redhat.com
--- server/lib/deltacloud/base_driver/base_driver.rb | 1 - server/lib/deltacloud/drivers/mock/mock_driver.rb | 2 -- 2 files changed, 0 insertions(+), 3 deletions(-)
diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb index 04256a4..de12326 100644 --- a/server/lib/deltacloud/base_driver/base_driver.rb +++ b/server/lib/deltacloud/base_driver/base_driver.rb @@ -37,7 +37,6 @@ module Deltacloud hw_profile = @hardware_profiles.find{|e| e.name == name} return if hw_profile hw_profile = ::Deltacloud::HardwareProfile.new( name, &block ) - puts hw_profile.inspect @hardware_profiles << hw_profile end
diff --git a/server/lib/deltacloud/drivers/mock/mock_driver.rb b/server/lib/deltacloud/drivers/mock/mock_driver.rb index 832609e..907520b 100644 --- a/server/lib/deltacloud/drivers/mock/mock_driver.rb +++ b/server/lib/deltacloud/drivers/mock/mock_driver.rb @@ -230,14 +230,12 @@ class MockDriver < Deltacloud::BaseDriver end
def stop_instance(credentials, id) - puts "STOP INSTANCE #{id}" instance_file = "#{@storage_root}/instances/#{id}.yml" instance_yml = YAML.load( File.read( instance_file ) ) instance_yml[:state] = 'STOPPED' File.open( instance_file, 'w' ) do |f| f << YAML.dump( instance_yml ) end - puts "returning #{instance_yml.inspect}" Instance.new( instance_yml ) end
From: David Lutterkort lutter@redhat.com
--- client/specs/spec_helper.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/client/specs/spec_helper.rb b/client/specs/spec_helper.rb index b9f9992..078b7e2 100644 --- a/client/specs/spec_helper.rb +++ b/client/specs/spec_helper.rb @@ -24,8 +24,8 @@ api_host = ENV['API_HOST'] ( api_host = 'localhost' ) if api_host == ''
api_port = ENV['API_PORT'] -( api_port = 3000 ) if api_port.nil? -( api_port = 3000 ) if api_port == '' +( api_port = 3001 ) if api_port.nil? +( api_port = 3001 ) if api_port == ''
API_HOST = api_host API_PORT = api_port
From: David Lutterkort lutter@redhat.com
--- server/drivers.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/server/drivers.rb b/server/drivers.rb index 633d855..72df672 100644 --- a/server/drivers.rb +++ b/server/drivers.rb @@ -22,7 +22,7 @@ def driver_source_name end
def driver_mock_source_name - return File.join('deltacloud', 'drivers', DRIVER, "#{DRIVER}_driver.rb") if driver_name.eql? 'Mock' + return File.join('deltacloud', 'drivers', DRIVER.to_s, "#{DRIVER}_driver.rb") if driver_name.eql? 'Mock' File.join('deltacloud', 'drivers', DRIVER, "#{DRIVER}_mock_driver.rb") end
From: David Lutterkort lutter@redhat.com
--- server/lib/deltacloud/hardware_profile.rb | 74 ++++++++++++-------- .../deltacloud/helpers/hardware_profiles_helper.rb | 17 ++--- server/views/hardware_profiles/index.html.haml | 6 +- server/views/hardware_profiles/show.html.haml | 6 +- 4 files changed, 60 insertions(+), 43 deletions(-)
diff --git a/server/lib/deltacloud/hardware_profile.rb b/server/lib/deltacloud/hardware_profile.rb index bf7bc40..c9dc35f 100644 --- a/server/lib/deltacloud/hardware_profile.rb +++ b/server/lib/deltacloud/hardware_profile.rb @@ -2,40 +2,55 @@ module Deltacloud class HardwareProfile
- attr_reader :name - attr_reader :cpu - attr_reader :architecture - attr_reader :memory - attr_reader :storage - - def initialize(name,&block) - @name = name - @cpu = 1 - @memory = 0 - @storage = 0 - @architecture = 1 - @mutable = false - instance_eval &block - end + class Property + attr_reader :name, :kind + # kind == :range + attr_reader :first, :last + # kind == :enum + attr_reader :values + # kind == :fixed + attr_reader :value
- def cpu(values=nil) - ( @cpu = values ) unless values.nil? - @cpu + def initialize(name, values) + @name = name + if values.is_a?(Range) + @kind = :range + @first = values.first + @last = values.last + elsif values.is_a?(Array) + @kind = :enum + @values = values + else + @kind = :fixed + @value = values + end + end end
- def architecture(values=nil) - ( @architecture = values ) unless values.nil? - @architecture + class << self + def property(prop) + define_method(prop) do |*args| + values, *ignored = *args + instvar = :"@#{prop}" + unless values.nil? + @properties[prop] = Property.new(prop, values) + end + @properties[prop] + end + end end
- def memory(values=nil) - ( @memory = values ) unless values.nil? - @memory - end + attr_reader :name + property :cpu + property :architecture + property :memory + property :storage
- def storage(values=nil) - ( @storage = values ) unless values.nil? - @storage + def initialize(name,&block) + @properties = {} + @name = name + @mutable = false + instance_eval &block if block_given? end
def mutable @@ -50,5 +65,8 @@ module Deltacloud @mutable end
+ def each_property(&block) + @properties.each_value { |prop| yield prop } + end end end diff --git a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb b/server/lib/deltacloud/helpers/hardware_profiles_helper.rb index eb1aa53..89d84e0 100644 --- a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb +++ b/server/lib/deltacloud/helpers/hardware_profiles_helper.rb @@ -1,16 +1,15 @@ module HardwareProfilesHelper
- def format_hardware_aspect(values) - f = '' - case ( values ) - when Range - f = "#{values.begin} - #{values.end}" - when Array - f = values.join( ', ' ) + def format_hardware_property(prop) + return "∅" unless prop + case prop.kind + when :range + "#{prop.first} - #{prop.last}" + when :enum + prop.values.join(', ') else - f = values.to_s + prop.value.to_s end - f end
end diff --git a/server/views/hardware_profiles/index.html.haml b/server/views/hardware_profiles/index.html.haml index ae7dcc2..26d5985 100644 --- a/server/views/hardware_profiles/index.html.haml +++ b/server/views/hardware_profiles/index.html.haml @@ -20,10 +20,10 @@ %td = link_to profile.name, hardware_profile_url( profile.name ) %td - = profile.architecture + = format_hardware_property profile.architecture %td - = format_hardware_aspect profile.memory + = format_hardware_property profile.memory %td - = format_hardware_aspect profile.storage + = format_hardware_property profile.storage %td = profile.mutable? diff --git a/server/views/hardware_profiles/show.html.haml b/server/views/hardware_profiles/show.html.haml index e76a0d8..f16d199 100644 --- a/server/views/hardware_profiles/show.html.haml +++ b/server/views/hardware_profiles/show.html.haml @@ -6,17 +6,17 @@ %dt Architecture %dd - = @profile.architecture + = format_hardware_property @profile.architecture %di %dt Memory %dd - = format_hardware_aspect @profile.memory + = format_hardware_property @profile.memory %di %dt Storage %dd - = format_hardware_aspect @profile.storage + = format_hardware_property @profile.storage %di %dt Mutable
From: David Lutterkort lutter@redhat.com
We need a more specific indication for when a HWP can be changed and altered --- server/lib/deltacloud/hardware_profile.rb | 13 ------------- server/views/hardware_profiles/index.html.haml | 4 ---- server/views/hardware_profiles/show.html.haml | 5 ----- 3 files changed, 0 insertions(+), 22 deletions(-)
diff --git a/server/lib/deltacloud/hardware_profile.rb b/server/lib/deltacloud/hardware_profile.rb index c9dc35f..613a9d5 100644 --- a/server/lib/deltacloud/hardware_profile.rb +++ b/server/lib/deltacloud/hardware_profile.rb @@ -49,22 +49,9 @@ module Deltacloud def initialize(name,&block) @properties = {} @name = name - @mutable = false instance_eval &block if block_given? end
- def mutable - @mutable = true - end - - def immutable - @mutable = false - end - - def mutable? - @mutable - end - def each_property(&block) @properties.each_value { |prop| yield prop } end diff --git a/server/views/hardware_profiles/index.html.haml b/server/views/hardware_profiles/index.html.haml index 26d5985..9d9794d 100644 --- a/server/views/hardware_profiles/index.html.haml +++ b/server/views/hardware_profiles/index.html.haml @@ -12,8 +12,6 @@ Memory %th Storage - %th - Mutable %tbody - for profile in @profiles %tr @@ -25,5 +23,3 @@ = format_hardware_property profile.memory %td = format_hardware_property profile.storage - %td - = profile.mutable? diff --git a/server/views/hardware_profiles/show.html.haml b/server/views/hardware_profiles/show.html.haml index f16d199..80fdd02 100644 --- a/server/views/hardware_profiles/show.html.haml +++ b/server/views/hardware_profiles/show.html.haml @@ -17,8 +17,3 @@ Storage %dd = format_hardware_property @profile.storage - %di - %dt - Mutable - %dd - = @profile.mutable?
From: David Lutterkort lutter@redhat.com
--- server/lib/deltacloud/hardware_profile.rb | 48 ++++++++++++++++++-- .../deltacloud/helpers/hardware_profiles_helper.rb | 4 +- 2 files changed, 46 insertions(+), 6 deletions(-)
diff --git a/server/lib/deltacloud/hardware_profile.rb b/server/lib/deltacloud/hardware_profile.rb index 613a9d5..6c0605b 100644 --- a/server/lib/deltacloud/hardware_profile.rb +++ b/server/lib/deltacloud/hardware_profile.rb @@ -2,8 +2,19 @@ module Deltacloud class HardwareProfile
+ UNITS = { + :memory => "MB", + :storage => "GB", + :architecture => "label", + :cpu => "count" + } + + def self.unit(name) + UNITS[name] + end + class Property - attr_reader :name, :kind + attr_reader :name, :kind, :default # kind == :range attr_reader :first, :last # kind == :enum @@ -11,29 +22,45 @@ module Deltacloud # kind == :fixed attr_reader :value
- def initialize(name, values) + def initialize(name, values, opts = {}) @name = name if values.is_a?(Range) @kind = :range @first = values.first @last = values.last + @default = values.first elsif values.is_a?(Array) @kind = :enum @values = values + @default = values.first else @kind = :fixed @value = values + @default = @value end + @default = opts[:default] if opts[:default] + end + + def unit + HardwareProfile.unit(name) + end + + def param + "hwp_#{name}" + end + + def fixed? + kind == :fixed end end
class << self def property(prop) define_method(prop) do |*args| - values, *ignored = *args + values, opts, *ignored = *args instvar = :"@#{prop}" unless values.nil? - @properties[prop] = Property.new(prop, values) + @properties[prop] = Property.new(prop, values, opts || {}) end @properties[prop] end @@ -55,5 +82,18 @@ module Deltacloud def each_property(&block) @properties.each_value { |prop| yield prop } end + + def properties + @properties.values + end + + def property(name) + @properties[name.to_sym] + end + + def default?(prop, v) + p = @properties[prop.to_sym] + p && p.default.to_s == v + end end end diff --git a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb b/server/lib/deltacloud/helpers/hardware_profiles_helper.rb index 89d84e0..979f818 100644 --- a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb +++ b/server/lib/deltacloud/helpers/hardware_profiles_helper.rb @@ -4,9 +4,9 @@ module HardwareProfilesHelper return "∅" unless prop case prop.kind when :range - "#{prop.first} - #{prop.last}" + "#{prop.first} - #{prop.last} (default: #{prop.default})" when :enum - prop.values.join(', ') + prop.values.join(', ') + " (default: #{prop.default})" else prop.value.to_s end
From: David Lutterkort lutter@redhat.com
--- server/server.rb | 12 +++++++++--- server/views/hardware_profiles/index.xml.haml | 4 ++++ server/views/hardware_profiles/show.xml.haml | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 server/views/hardware_profiles/index.xml.haml create mode 100644 server/views/hardware_profiles/show.xml.haml
diff --git a/server/server.rb b/server/server.rb index 67590d0..187aa07 100644 --- a/server/server.rb +++ b/server/server.rb @@ -278,15 +278,21 @@ collection :instances do end
collection :hardware_profiles do - description "Hardware profiles" + description <<END + A hardware profile represents a configuration of resources upon which a + machine may be deployed. It defines aspects such as local disk storage, + available RAM, and architecture. Each provider is free to define as many + (or as few) hardware profiles as desired. +END
operation :index do description "List of available hardware profiles" param :id, :string + param :architecture, :string, :optional, [ 'i386', 'x86_64' ] control do - @profiles = driver.hardware_profiles + @profiles = driver.hardware_profiles(credentials, params) respond_to do |format| - format.xml { convert_to_xml(:hardware_profiles, @profiles) } + format.xml { haml :'hardware_profiles/index' } format.html { haml :'hardware_profiles/index' } end end diff --git a/server/views/hardware_profiles/index.xml.haml b/server/views/hardware_profiles/index.xml.haml new file mode 100644 index 0000000..52f91a4 --- /dev/null +++ b/server/views/hardware_profiles/index.xml.haml @@ -0,0 +1,4 @@ +!!! XML +%hardware-profiles + - @profiles.each do |prof| + = haml :'hardware_profiles/show', :locals => { :@profile => prof, :partial => true } diff --git a/server/views/hardware_profiles/show.xml.haml b/server/views/hardware_profiles/show.xml.haml new file mode 100644 index 0000000..4a289a1 --- /dev/null +++ b/server/views/hardware_profiles/show.xml.haml @@ -0,0 +1,17 @@ +- unless defined?(partial) + !!! XML +%hardware-profile{ :href => hardware_profile_url(@profile.name) } + %id= @profile.name + - @profile.each_property do |prop| + - attr = { :name => prop.name, :kind => prop.kind, :unit => prop.unit } + - if prop.kind == :fixed + %property{ attr, :value => prop.value }/ + - else + %property{ attr, :value => prop.default } + %param{ :operation => "create", :href => instances_url, :method => "post", :name => prop.param } + - if prop.kind == :range + %range{ :first => prop.first, :last => prop.last }/ + - elsif prop.kind == :enum + %enum + - prop.values.each do |v| + %entry{ :value => v }/
From: David Lutterkort lutter@redhat.com
--- server/lib/deltacloud/drivers/mock/mock_driver.rb | 24 +++++++++++++++++++++ 1 files changed, 24 insertions(+), 0 deletions(-)
diff --git a/server/lib/deltacloud/drivers/mock/mock_driver.rb b/server/lib/deltacloud/drivers/mock/mock_driver.rb index 907520b..6d99c36 100644 --- a/server/lib/deltacloud/drivers/mock/mock_driver.rb +++ b/server/lib/deltacloud/drivers/mock/mock_driver.rb @@ -75,6 +75,30 @@ class MockDriver < Deltacloud::BaseDriver }), ] ) unless defined?( REALMS )
+ define_hardware_profile('m1-small') do + cpu 1 + memory 1.7 * 1024 + storage 160 + architecture 'i386' + end + + define_hardware_profile('m1-large') do + cpu 2 + memory (7.5*1024 .. 15*1024), :default => 10 * 1024 + storage [ 850, 1024 ] + architecture 'x86_64' + end + + define_hardware_profile('m1-xlarge') do + cpu 4 + memory (12*1024 .. 32*1024) + storage [ 1024, 2048, 4096 ] + architecture 'x86_64' + end + + # Some clouds tell us nothing about hardware profiles (e.g., OpenNebula) + define_hardware_profile 'opaque' + define_instance_states do start.to( :pending ) .on( :create )
lutter@redhat.com wrote:
From: David Lutterkort lutter@redhat.com
server/lib/deltacloud/drivers/mock/mock_driver.rb | 24 +++++++++++++++++++++ 1 files changed, 24 insertions(+), 0 deletions(-)
diff --git a/server/lib/deltacloud/drivers/mock/mock_driver.rb b/server/lib/deltacloud/drivers/mock/mock_driver.rb index 907520b..6d99c36 100644 --- a/server/lib/deltacloud/drivers/mock/mock_driver.rb +++ b/server/lib/deltacloud/drivers/mock/mock_driver.rb @@ -75,6 +75,30 @@ class MockDriver < Deltacloud::BaseDriver }), ] ) unless defined?( REALMS )
- define_hardware_profile('m1-small') do
- cpu 1
- memory 1.7 * 1024
- storage 160
- architecture 'i386'
- end
- define_hardware_profile('m1-large') do
- cpu 2
- memory (7.5*1024 .. 15*1024), :default => 10 * 1024
- storage [ 850, 1024 ]
- architecture 'x86_64'
- end
- define_hardware_profile('m1-xlarge') do
- cpu 4
- memory (12*1024 .. 32*1024)
- storage [ 1024, 2048, 4096 ]
- architecture 'x86_64'
- end
- # Some clouds tell us nothing about hardware profiles (e.g., OpenNebula)
- define_hardware_profile 'opaque'
- define_instance_states do start.to( :pending ) .on( :create )
When we don't define anything about a HW profile, does this mean the instance must define every HWP parameter -- requiring RAM, cpu, storage, arch,etc? Is it valid to leave a single attribute blank in a HWP -- thus making that one param required for any instance that uses it?
Or, instead, does this just mean that we pass along the HWP by name to the provider and we don't know or care what the params are? In this case I'm assuming we can't define any instance-specific overrides either.
Scott
On Mon, 2010-04-12 at 10:40 -0400, Scott Seago wrote:
When we don't define anything about a HW profile, does this mean the instance must define every HWP parameter -- requiring RAM, cpu, storage, arch,etc? Is it valid to leave a single attribute blank in a HWP -- thus making that one param required for any instance that uses it?
Or, instead, does this just mean that we pass along the HWP by name to the provider and we don't know or care what the params are? In this case I'm assuming we can't define any instance-specific overrides either.
When a property is not reported in the HWP at all, that means 'we know nothing about that', and you can not modify it per-instance either. Only properties that are mentioned explicitly in the HWP, and have kind range or enum, can be modified per-instance.
One place where I used that is in the GoGrid driver, which does not report architecture, since the arch of the resulting VM depends on the image you use.
David
From: David Lutterkort lutter@redhat.com
--- server/lib/deltacloud/base_driver/features.rb | 18 ++++++++++++++---- 1 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb index c6c9126..abe78e3 100644 --- a/server/lib/deltacloud/base_driver/features.rb +++ b/server/lib/deltacloud/base_driver/features.rb @@ -5,7 +5,6 @@ module Deltacloud
class FeatureError < StandardError; end class DuplicateFeatureDeclError < FeatureError; end - class DuplicateFeatureError < FeatureError; end class UndeclaredFeatureError < FeatureError; end
class BaseDriver @@ -40,8 +39,15 @@ module Deltacloud @description end
+ # Add a new operation or modify an existing one through BLOCK def operation(name, &block) - @operations << Operation.new(name, &block) + unless op = @operations.find { |op| op.name == name } + op = Operation.new(name, &block) + @operations << op + else + op.instance_eval(&block) if block_given? + end + op end end
@@ -92,10 +98,14 @@ module Deltacloud end
# Declare in a driver that it supports a specific feature + # + # The same feature can be declared multiple times in a driver, so that + # it can be changed successively by passing in different blocks. def self.feature(collection, name, &block) features[collection] ||= [] - if features[collection].find { |f| f.name == name } - raise DuplicateFeatureError + if f = features[collection].find { |f| f.name == name } + f.instance_eval(&block) if block_given? + return f end unless decl = feature_decl_for(collection, name) raise UndeclaredFeatureError, "No feature #{name} for #{collection}"
From: David Lutterkort lutter@redhat.com
For any dimension that is not fixed in a HWP, produce a parameter describing what's acceptable for this dimension, e.g. in a create instance operation. --- server/lib/deltacloud/hardware_profile.rb | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/server/lib/deltacloud/hardware_profile.rb b/server/lib/deltacloud/hardware_profile.rb index 6c0605b..e7eaf8b 100644 --- a/server/lib/deltacloud/hardware_profile.rb +++ b/server/lib/deltacloud/hardware_profile.rb @@ -52,6 +52,17 @@ module Deltacloud def fixed? kind == :fixed end + + def to_param + return nil if kind == :fixed + if kind == :range + # FIXME: We can't validate ranges currently + args = [param, :string, :optional] + else + args = [param, :string, :optional, values.collect { |v| v.to_s} ] + end + Validation::Param.new(args) + end end
class << self @@ -95,5 +106,11 @@ module Deltacloud p = @properties[prop.to_sym] p && p.default.to_s == v end + + def params + @properties.values.inject([]) { |m, prop| + m << prop.to_param + }.compact + end end end
From: David Lutterkort lutter@redhat.com
--- server/lib/deltacloud/validation.rb | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/server/lib/deltacloud/validation.rb b/server/lib/deltacloud/validation.rb index df1e66c..b4eb3ae 100644 --- a/server/lib/deltacloud/validation.rb +++ b/server/lib/deltacloud/validation.rb @@ -43,6 +43,15 @@ module Deltacloud::Validation @params end
+ # Add the parameters in hash +new+ to already existing parameters. If + # +new+ contains a parameter with an already existing name, the old + # definition is clobbered. + def add_params(new) + # We do not check for duplication on purpose: multiple calls + # to add_params should be cumulative + new.each { |p| @params[p.name] = p } + end + def each_param(&block) params.each_value { |p| yield p } end
From: David Lutterkort lutter@redhat.com
--- server/lib/deltacloud/base_driver/base_driver.rb | 6 ++++++ server/lib/deltacloud/base_driver/features.rb | 4 ++++ 2 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb index de12326..fc8c835 100644 --- a/server/lib/deltacloud/base_driver/base_driver.rb +++ b/server/lib/deltacloud/base_driver/base_driver.rb @@ -38,6 +38,12 @@ module Deltacloud return if hw_profile hw_profile = ::Deltacloud::HardwareProfile.new( name, &block ) @hardware_profiles << hw_profile + hw_params = hw_profile.params + unless hw_params.empty? + feature :instances, :hardware_profiles do + decl.operation(:create) { add_params(hw_params) } + end + end end
def self.hardware_profiles diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb index abe78e3..43ec9c3 100644 --- a/server/lib/deltacloud/base_driver/features.rb +++ b/server/lib/deltacloud/base_driver/features.rb @@ -136,5 +136,9 @@ module Deltacloud end end
+ declare_feature :instances, :hardware_profiles do + description "Size instances according to changes to a hardware profile" + # The parameters are filled in from the hardware profiles + end end end
From: David Lutterkort lutter@redhat.com
--- .../deltacloud/helpers/hardware_profiles_helper.rb | 13 ++++++++++--- 1 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb b/server/lib/deltacloud/helpers/hardware_profiles_helper.rb index 979f818..6269c23 100644 --- a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb +++ b/server/lib/deltacloud/helpers/hardware_profiles_helper.rb @@ -2,14 +2,21 @@ module HardwareProfilesHelper
def format_hardware_property(prop) return "∅" unless prop + u = hardware_property_unit(prop) case prop.kind when :range - "#{prop.first} - #{prop.last} (default: #{prop.default})" + "#{prop.first} #{u} - #{prop.last} #{u} (default: #{prop.default} #{u})" when :enum - prop.values.join(', ') + " (default: #{prop.default})" + prop.values.collect{ |v| "#{v} #{u}"}.join(', ') + " (default: #{prop.default} #{u})" else - prop.value.to_s + "#{prop.value} #{u}" end end
+ def hardware_property_unit(prop) + u = prop.unit + u = "" if ["label", "count"].include?(u) + u = "vcpus" if prop.name == :cpu + u + end end
From: David Lutterkort lutter@redhat.com
--- server/deltacloud.rb | 1 + server/lib/deltacloud/models/instance.rb | 1 + server/lib/deltacloud/models/instance_profile.rb | 47 ++++++++++++++++++++++ 3 files changed, 49 insertions(+), 0 deletions(-) create mode 100644 server/lib/deltacloud/models/instance_profile.rb
diff --git a/server/deltacloud.rb b/server/deltacloud.rb index c879316..2a60679 100644 --- a/server/deltacloud.rb +++ b/server/deltacloud.rb @@ -10,5 +10,6 @@ require 'deltacloud/models/flavor' require 'deltacloud/models/realm' require 'deltacloud/models/image' require 'deltacloud/models/instance' +require 'deltacloud/models/instance_profile' require 'deltacloud/models/storage_snapshot' require 'deltacloud/models/storage_volume' diff --git a/server/lib/deltacloud/models/instance.rb b/server/lib/deltacloud/models/instance.rb index 485ffb2..e5c1f9c 100644 --- a/server/lib/deltacloud/models/instance.rb +++ b/server/lib/deltacloud/models/instance.rb @@ -27,6 +27,7 @@ class Instance < BaseModel attr_accessor :actions attr_accessor :public_addresses attr_accessor :private_addresses + attr_accessor :instance_profile
def initialize(init=nil) super(init) diff --git a/server/lib/deltacloud/models/instance_profile.rb b/server/lib/deltacloud/models/instance_profile.rb new file mode 100644 index 0000000..a9e3854 --- /dev/null +++ b/server/lib/deltacloud/models/instance_profile.rb @@ -0,0 +1,47 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Model to store the hardware profile applied to an instance together with +# any instance-specific overrides +class InstanceProfile < BaseModel + attr_accessor :memory + attr_accessor :storage + attr_accessor :architecture + attr_accessor :cpu + + def initialize(hwp_name, args = {}) + opts = args.inject({ :id => hwp_name.to_s }) do |m, e| + k, v = e + m[$1] = v if k.to_s =~ /^hwp_(.*)$/ + m + end + super(opts) + end + + def name + id + end + + def overrides + [:memory, :storage, :architecture, :cpu].inject({}) do |h, p| + if v = instance_variable_get("@#{p}") + h[p] = v + end + h + end + end +end
From: David Lutterkort lutter@redhat.com
--- server/lib/deltacloud/base_driver/base_driver.rb | 21 +++++++++++++++++---- server/lib/deltacloud/hardware_profile.rb | 15 +++++++++++++++ server/server.rb | 2 +- 3 files changed, 33 insertions(+), 5 deletions(-)
diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb index fc8c835..373d64e 100644 --- a/server/lib/deltacloud/base_driver/base_driver.rb +++ b/server/lib/deltacloud/base_driver/base_driver.rb @@ -51,12 +51,25 @@ module Deltacloud @hardware_profiles end
- def hardware_profiles - self.class.hardware_profiles + def hardware_profiles(credentials, opts = nil) + results = self.class.hardware_profiles + filter_hardware_profiles(results, opts) end
- def hardware_profile(name) - self.class.hardware_profiles.find{|e| e.name == name } + def hardware_profile(credentials, name) + hardware_profiles(credentials, :name => name).first + end + + def filter_hardware_profiles(profiles, opts) + if opts + if v = opts[:architecture] + profiles = profiles.select { |hwp| hwp.include?(:architecture, v) } + end + if v = opts[:name] + profiles = profiles.select { |hwp| hwp.name == v } + end + end + profiles end
def self.define_instance_states(&block) diff --git a/server/lib/deltacloud/hardware_profile.rb b/server/lib/deltacloud/hardware_profile.rb index e7eaf8b..2d791bc 100644 --- a/server/lib/deltacloud/hardware_profile.rb +++ b/server/lib/deltacloud/hardware_profile.rb @@ -63,6 +63,16 @@ module Deltacloud end Validation::Param.new(args) end + + def include?(v) + if kind == :fixed + return v == value + elsif kind == :range + return v >= first && v <= last + else + return values.include?(v) + end + end end
class << self @@ -107,6 +117,11 @@ module Deltacloud p && p.default.to_s == v end
+ def include?(prop, v) + p = @properties[prop] + p.nil? || p.include?(v) + end + def params @properties.values.inject([]) { |m, prop| m << prop.to_param diff --git a/server/server.rb b/server/server.rb index 187aa07..7151f47 100644 --- a/server/server.rb +++ b/server/server.rb @@ -302,7 +302,7 @@ END description "Show specific hardware profile" param :id, :string, :required control do - @profile = driver.hardware_profile(params[:id]) + @profile = driver.hardware_profile(credentials, params[:id]) respond_to do |format| format.xml { haml :'hardware_profiles/show', :layout => false } format.html { haml :'hardware_profiles/show' }
From: David Lutterkort lutter@redhat.com
Adapt the initial data and add a sample instance that overrides a HWP property --- server/lib/deltacloud/base_driver/base_driver.rb | 18 ++++++++++++++++++ .../drivers/mock/data/instances/inst0.yml | 17 +++++++++++++++++ .../drivers/mock/data/instances/inst1.yml | 2 ++ .../drivers/mock/data/instances/inst2.yml | 2 ++ server/lib/deltacloud/drivers/mock/mock_driver.rb | 10 +++------- 5 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 server/lib/deltacloud/drivers/mock/data/instances/inst0.yml
diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb index 373d64e..a3069e9 100644 --- a/server/lib/deltacloud/base_driver/base_driver.rb +++ b/server/lib/deltacloud/base_driver/base_driver.rb @@ -72,6 +72,24 @@ module Deltacloud profiles end
+ def find_hardware_profile(credentials, name, image_id) + hwp = nil + if name + unless hwp = hardware_profiles(credentials, :name => name).first + raise BackendError.new(400, "bad-hardware-profile-name", + "Hardware profile '#{name}' does not exist", nil) + end + else + unless image = image(credentials, :id=>image_id) + raise BackendError.new(400, "bad-image-id", + "Image with ID '#{image_id}' does not exist", nil) + end + hwp = hardware_profiles(credentials, + :architecture=>image.architecture).first + end + return hwp + end + def self.define_instance_states(&block) machine = ::Deltacloud::StateMachine.new(&block) @instance_state_machine = machine diff --git a/server/lib/deltacloud/drivers/mock/data/instances/inst0.yml b/server/lib/deltacloud/drivers/mock/data/instances/inst0.yml new file mode 100644 index 0000000..c1880b7 --- /dev/null +++ b/server/lib/deltacloud/drivers/mock/data/instances/inst0.yml @@ -0,0 +1,17 @@ +--- +:realm_id: us +:public_addresses: +- img1.inst0.public.com +:state: RUNNING +:name: "Mock Instance With Profile Change" +:private_addresses: +- img1.inst0.private.com +:image_id: img1 +:flavor_id: m1-large +:instance_profile: !ruby/object:InstanceProfile + id: m1-large + memory: "12288" +:owner_id: mockuser +:actions: +- :reboot +- :stop diff --git a/server/lib/deltacloud/drivers/mock/data/instances/inst1.yml b/server/lib/deltacloud/drivers/mock/data/instances/inst1.yml index 26a6e3b..aa3f86e 100644 --- a/server/lib/deltacloud/drivers/mock/data/instances/inst1.yml +++ b/server/lib/deltacloud/drivers/mock/data/instances/inst1.yml @@ -6,3 +6,5 @@ :private_addresses: [ img3.inst1.private.com ] :flavor_id: m1-small :realm_id: us +:instance_profile: !ruby/object:InstanceProfile + id: m1-small diff --git a/server/lib/deltacloud/drivers/mock/data/instances/inst2.yml b/server/lib/deltacloud/drivers/mock/data/instances/inst2.yml index ee1de37..69d4720 100644 --- a/server/lib/deltacloud/drivers/mock/data/instances/inst2.yml +++ b/server/lib/deltacloud/drivers/mock/data/instances/inst2.yml @@ -6,3 +6,5 @@ :private_addresses: [ img1.inst2.private.com ] :flavor_id: m1-small :realm_id: us +:instance_profile: !ruby/object:InstanceProfile + id: m1-large diff --git a/server/lib/deltacloud/drivers/mock/mock_driver.rb b/server/lib/deltacloud/drivers/mock/mock_driver.rb index 6d99c36..8841e3f 100644 --- a/server/lib/deltacloud/drivers/mock/mock_driver.rb +++ b/server/lib/deltacloud/drivers/mock/mock_driver.rb @@ -206,12 +206,7 @@ class MockDriver < Deltacloud::BaseDriver ( realm_id = realm.id ) if realm end
- flavor_id = opts[:flavor_id] - if ( flavor_id.nil? ) - image = image(credentials, :id=>image_id ) - flavor = flavors(credentials, :architecture=>image.architecture).first - (flavor_id = flavor.id ) if flavor - end + hwp = find_hardware_profile(credentials, opts[:hwp_id], image_id)
name = opts[:name] || "i-#{Time.now.to_i}"
@@ -222,7 +217,8 @@ class MockDriver < Deltacloud::BaseDriver :owner_id=>credentials.user, :public_addresses=>["#{image_id}.#{next_id}.public.com"], :private_addresses=>["#{image_id}.#{next_id}.private.com"], - :flavor_id=>flavor_id, + :flavor_id=>hwp.name, + :instance_profile => InstanceProfile.new(hwp.name, opts), :realm_id=>realm_id, :actions=>instance_actions_for( 'RUNNING' ) }
From: David Lutterkort lutter@redhat.com
* Define hardware profiles equivalent to the flavors * Expect parameter hwp_id to be passed in for create_instance * Store InstanceProfile with instance --- server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 50 ++++++++++++++--------- 1 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb index 7f73666..11cd41b 100644 --- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb +++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb @@ -64,39 +64,53 @@ class EC2Driver < Deltacloud::BaseDriver
define_hardware_profile('m1-small') do cpu 1 - memory 1.7 + memory 1.7 * 1024 storage 160 architecture 'i386' end
define_hardware_profile('m1-large') do - cpu 2 - memory (7.5..15) - storage [ 850, 1024 ] + cpu 4 + memory 7.5 * 1024 + storage 850 architecture 'x86_64' end
define_hardware_profile('m1-xlarge') do - cpu 2 - memory 15 + cpu 8 + memory 15 * 1024 storage 1690 architecture 'x86_64' end
define_hardware_profile('c1-medium') do - cpu 2 - memory 1.7 + cpu 5 + memory 1.7 * 1024 storage 350 - architecture 'x86_64' + architecture 'i386' end
define_hardware_profile('c1-xlarge') do - cpu 2 - memory 7 + cpu 20 + memory 7 * 1024 storage 1690 architecture 'x86_64' end
+ define_hardware_profile('m2-xlarge') do + cpu 6.5 + memory 17.1 * 1024 + storage 420 + architecture 'x86_64' + end + + define_hardware_profile('m2-2xlarge') do + cpu 13 + memory 34.2 * 1024 + storage 850 + architecture 'x86_64' + end + define_instance_states do start.to( :pending ) .automatically pending.to( :running ) .automatically @@ -188,13 +202,7 @@ class EC2Driver < Deltacloud::BaseDriver def create_instance(credentials, image_id, opts) ec2 = new_client( credentials ) realm_id = opts[:realm_id] - flavor_id = opts[:flavor_id] - unless ( flavor_id ) - image = image(credentials, :id=>image_id ) - flavor = flavor( credentials, :architecture=>image.architecture ) - ( flavor_id = flavor.id ) if ( flavor ) - end - flavor_id.gsub!( /-/, '.' ) if flavor_id + hwp = find_hardware_profile(credentials, opts[:hwp_id], image_id) ec2_instances = ec2.run_instances( image_id, 1,1, @@ -202,7 +210,7 @@ class EC2Driver < Deltacloud::BaseDriver nil, opts[:user_data], 'public', - flavor_id, + hwp.name.gsub(/-/, '.'), nil, nil, realm_id ) @@ -302,6 +310,9 @@ class EC2Driver < Deltacloud::BaseDriver
realm_id = ec2_instance[:aws_availability_zone] (realm_id = nil ) if ( realm_id == '' ) + + hwp_name = ec2_instance[:aws_instance_type].gsub( /./, '-') + Instance.new( { :id=>ec2_instance[:aws_instance_id], :name => ec2_instance[:aws_image_id], @@ -312,6 +323,7 @@ class EC2Driver < Deltacloud::BaseDriver :public_addresses=>( ec2_instance[:dns_name] == '' ? [] : [ec2_instance[:dns_name]] ), :private_addresses=>( ec2_instance[:private_dns_name] == '' ? [] : [ec2_instance[:private_dns_name]] ), :flavor_id=>ec2_instance[:aws_instance_type].gsub( /./, '-'), + :instance_profile => InstanceProfile.new(hwp_name), :actions=>instance_actions_for( ec2_instance[:aws_state].upcase ), } ) end
lutter@redhat.com wrote:
From: David Lutterkort lutter@redhat.com
- Define hardware profiles equivalent to the flavors
- Expect parameter hwp_id to be passed in for create_instance
- Store InstanceProfile with instance
server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 50 ++++++++++++++--------- 1 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb index 7f73666..11cd41b 100644 --- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb +++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb @@ -64,39 +64,53 @@ class EC2Driver < Deltacloud::BaseDriver
define_hardware_profile('m1-small') do cpu 1
- memory 1.7
- memory 1.7 * 1024 storage 160 architecture 'i386' end
I guess there's no way to query ec2 for these like we do for the other object types?
Scott
On Mon, 2010-04-12 at 10:40 -0400, Scott Seago wrote:
lutter@redhat.com wrote:
From: David Lutterkort lutter@redhat.com
- Define hardware profiles equivalent to the flavors
- Expect parameter hwp_id to be passed in for create_instance
- Store InstanceProfile with instance
server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 50 ++++++++++++++--------- 1 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb index 7f73666..11cd41b 100644 --- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb +++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb @@ -64,39 +64,53 @@ class EC2Driver < Deltacloud::BaseDriver
define_hardware_profile('m1-small') do cpu 1
- memory 1.7
- memory 1.7 * 1024 storage 160 architecture 'i386' end
I guess there's no way to query ec2 for these like we do for the other object types?
No, there's no API for them.
David
From: David Lutterkort lutter@redhat.com
- Query list of hardware profiles form Rackspace - Expect parameter hwp_id for create_instance - Store InstanceProfile with instances --- .../drivers/rackspace/rackspace_driver.rb | 18 +++++++++++++++--- 1 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb index 2dd34b8..2b7aa44 100644 --- a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +++ b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb @@ -41,6 +41,18 @@ class RackspaceDriver < Deltacloud::BaseDriver results end
+ def hardware_profiles(credentials, opts = nil) + racks = new_client( credentials ) + results = racks.list_flavors.map do |flav| + HardwareProfile.new(flav["id"].to_s) do + architecture 'x86_64' + memory flav["ram"].to_i + storage flav["disk"].to_i + end + end + filter_hardware_profiles(results, opts) + end + def images(credentials, opts=nil) racks = new_client( credentials ) results = racks.list_images.map do |img| @@ -87,11 +99,10 @@ class RackspaceDriver < Deltacloud::BaseDriver # def create_instance(credentials, image_id, opts) racks = new_client( credentials ) - flavor_id = 1 - if (opts[:flavor_id]) then flavor_id = opts[:flavor_id] end + hwp_id = opts[:hwp_id] || 1 name = Time.now.to_s if (opts[:name]) then name = opts[:name] end - convert_srv_to_instance(racks.start_server(image_id, flavor_id, name)) + convert_srv_to_instance(racks.start_server(image_id, hwp_id, name)) end
# @@ -123,6 +134,7 @@ class RackspaceDriver < Deltacloud::BaseDriver inst.actions = instance_actions_for(inst.state) inst.image_id = srv["imageId"].to_s inst.flavor_id = srv["flavorId"].to_s + inst.instance_profile = InstanceProfile.new(srv["flavorId"].to_s) if srv["addresses"] inst.public_addresses = srv["addresses"]["public"] inst.private_addresses = srv["addresses"]["private"]
From: David Lutterkort lutter@redhat.com
This is not entirely right, there's a few FIXME's left; I do not have a way to test this driver. --- .../deltacloud/drivers/rimu/rimu_hosting_client.rb | 4 +- .../deltacloud/drivers/rimu/rimu_hosting_driver.rb | 22 +++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/server/lib/deltacloud/drivers/rimu/rimu_hosting_client.rb b/server/lib/deltacloud/drivers/rimu/rimu_hosting_client.rb index 55eea1b..f145caa 100755 --- a/server/lib/deltacloud/drivers/rimu/rimu_hosting_client.rb +++ b/server/lib/deltacloud/drivers/rimu/rimu_hosting_client.rb @@ -75,9 +75,9 @@ class RimuHostingClient request("/orders/order-#{id}-a/vps",'', 'DELETE') end
- def create_server(image_id, flavor_id, name) + def create_server(image_id, plan_code, name) json = {:new_vps => {:instantiation_options => {:domain_name => name, :distro => image_id}, - :pricing_plan_code => flavor_id}}.to_json + :pricing_plan_code => plan_code}}.to_json request('/orders/new-vps',json, 'POST')[:about_order] end end diff --git a/server/lib/deltacloud/drivers/rimu/rimu_hosting_driver.rb b/server/lib/deltacloud/drivers/rimu/rimu_hosting_driver.rb index 33b5ea7..eb00232 100755 --- a/server/lib/deltacloud/drivers/rimu/rimu_hosting_driver.rb +++ b/server/lib/deltacloud/drivers/rimu/rimu_hosting_driver.rb @@ -57,6 +57,21 @@ class RimuHostingDriver < Deltacloud::BaseDriver flavors end
+ def hardware_profiles(credentials, opts = nil) + rh = RimuHostingClient.new(credentials) + results = rh.list_plans.map do |plan| + # FIXME: x86 is not a valid architecture; what is Rimu offering ? + # FIXME: VPS plans offer a range of memory/storage, but that's + # not contained in hte pricing_plan_infos + HardwareProfile.new(plan["pricing_plan_code"]) do + memory plan["minimum_memory_mb"].to_f + storage => plan["minimum_disk_gb"].to_i + architecture => "x86" + end + end + filter_hardware_profiles(results, opts) + end + def realms(credentials, opts=nil) [Realm.new( { :id=>"rimu", @@ -97,16 +112,13 @@ class RimuHostingDriver < Deltacloud::BaseDriver def create_instance(credentials, image_id, opts) rh = RimuHostingClient.new(credentials) # really need to raise an exception here. - flavor_id = 1 - if (opts[:flavor_id]) then - flavor_id = opts[:flavor_id] - end + hwp_id = opts[:hwp_id] || 1 # really bad, but at least its a fqdn name = Time.now.to_s + '.com' if (opts[:name]) then name = opts[:name] end - convert_srv_to_instance(rh.create_server(image_id, flavor_id, name)) + convert_srv_to_instance(rh.create_server(image_id, hwp_id, name))
end
From: David Lutterkort lutter@redhat.com
* Define hardware profiles equivalent to the flavors * Expect parameter hwp_id to be passed in for create_instance * Store InstanceProfile with instance
This is a little hokey, since OpenNebula doesn't tell us anything what the different size profiles mean --- .../drivers/opennebula/opennebula_driver.rb | 12 +++++++++--- 1 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb index e49c662..85efeec 100644 --- a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +++ b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb @@ -58,6 +58,11 @@ class OpennebulaDriver < Deltacloud::BaseDriver } ), ] ) unless defined?( FLAVORS )
+ define_hardware_profile 'small' + + define_hardware_profile 'medium' + + define_hardware_profile 'large'
def flavors(credentials, opts=nil) return FLAVORS if ( opts.nil? ) @@ -150,7 +155,7 @@ class OpennebulaDriver < Deltacloud::BaseDriver def create_instance(credentials, image_id, opts=nil) occi_client = new_client(credentials)
- flavor_id = opts[:flavor_id].nil? ? 'small' : opts[:flavor_id] + hwp_id = opts[:hwp_id] || 'small'
instancexml = ERB.new(OCCI_VM).result(binding) instancefile = "|echo '#{instancexml}'" @@ -206,7 +211,7 @@ class OpennebulaDriver < Deltacloud::BaseDriver
state = (computehash['STATE'].text == 'ACTIVE') ? 'RUNNING' : computehash['STATE'].text
- flavor = computehash['INSTANCE_TYPE'].nil? ? 'small' : computehash['INSTANCE_TYPE'].text + hwp_name = computehash['INSTANCE_TYPE'] || 'small'
networks = [] (computehash['NETWORK'].each do |n| @@ -219,6 +224,7 @@ class OpennebulaDriver < Deltacloud::BaseDriver :name=>computehash['NAME'].text, :image_id=>imageid, :flavor_id=>flavor, + :instance_profile=>InstanceProfile.new(hwp_name), :realm_id=>'Any realm', :state=>state, :public_addreses=>networks, @@ -243,7 +249,7 @@ class OpennebulaDriver < Deltacloud::BaseDriver (OCCI_VM = %q{ <COMPUTE> <NAME><%=opts[:name]%></NAME> - <INSTANCE_TYPE><%= flavor_id %></INSTANCE_TYPE> + <INSTANCE_TYPE><%= hwp_id %></INSTANCE_TYPE> <STORAGE> <DISK image="<%= image_id %>" dev="sda1" /> </STORAGE>
From: David Lutterkort lutter@redhat.com
* Define a hardware profile for memory options. We do not report architecture, since for GoGrid that is determined with the image * Expect parameter hwp_id to be passed in for create_instance * Store InstanceProfile with instance
We should eventually generate the hardware profile from the list of RAM sizes from the server. --- .../lib/deltacloud/drivers/gogrid/gogrid_driver.rb | 66 ++++++++------------ 1 files changed, 26 insertions(+), 40 deletions(-)
diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb index 3269cef..875b876 100644 --- a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +++ b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb @@ -24,40 +24,10 @@ module Deltacloud
class GogridDriver < Deltacloud::BaseDriver
- # Storage capacity is same on all machines (10gb), it could be extended using 'Cloud Storage' - define_hardware_profile('server-with-512mb-ram') do - cpu 2 - memory 0.5 - storage 10 - architecture 'i386' - end - - define_hardware_profile('server-with-1gb-ram') do - cpu 2 - memory 1 - storage 10 - architecture 'i386' - end - - define_hardware_profile('server-with-2gb-ram') do - cpu 2 - memory 2 - storage 10 - architecture 'i386' - end - - define_hardware_profile('server-with-4gb-ram') do + define_hardware_profile 'server' do cpu 2 - memory 4 + memory [512, 1024, 2048, 4096, 8192] storage 10 - architecture 'i386' - end - - define_hardware_profile('server-with-8gb-ram') do - cpu 2 - memory 8 - storage 10 - architecture 'i386' end
# The only valid option for flavors is server RAM for now @@ -99,13 +69,19 @@ class GogridDriver < Deltacloud::BaseDriver end
def create_instance(credentials, image_id, opts=nil) - flavor_id = opts[:flavor_id] || '1' + server_ram = nil + if opts[:hwp_memory] + mem = opts[:hwp_memory].to_i + server_ram = (mem == 512) ? "512MB" : "#{mem / 1024}GB" + else + server_ram = "512MB" + end name = (opts[:name] && opts[:name]!='') ? opts[:name] : get_random_instance_name safely do - convert_instance(new_client(credentials).request('grid/server/add', { + convert_instance(new_client(credentials).request('grid/server/add', { 'name' => name, 'image' => image_id, - 'server.ram' => flavor_id, + 'server.ram' => server_ram, 'ip' => get_next_free_ip(credentials) })['list'].first, credentials.user) end @@ -122,7 +98,7 @@ class GogridDriver < Deltacloud::BaseDriver safely do instances = new_client(credentials).request('grid/server/list')['list'].collect do |instance| convert_instance(instance, credentials.user) - end + end end end instances = filter_on( instances, :state, opts ) @@ -202,11 +178,23 @@ class GogridDriver < Deltacloud::BaseDriver end
def convert_instance(instance, owner_id) + opts = {} + unless instance['ram']['id'] == "1" + mem = instance['ram']['name'] + if mem == "512MB" + opts[:hwp_memory] = "512" + else + opts[:hwp_memory] = (mem.to_i * 1024).to_s + end + end + prof = InstanceProfile.new("server", opts) + Instance.new( - :id => instance['id'], + :id => instance['name'], :owner_id => owner_id, :image_id => instance['image']['id'], :flavor_id => instance['ram']['id'], + :instance_profile => prof, :name => instance['name'], :realm_id => instance['type']['id'], :state => convert_server_state(instance['state']['name'], instance['id']), @@ -228,7 +216,7 @@ class GogridDriver < Deltacloud::BaseDriver def get_next_free_ip(credentials) ip = "" safely do - ip = new_client(credentials).request('grid/ip/list', { + ip = new_client(credentials).request('grid/ip/list', { 'ip.type' => '1', 'ip.state' => '1' })['list'].first['ip'] @@ -249,5 +237,3 @@ end end end end - -
From: David Lutterkort lutter@redhat.com
--- server/lib/converters/xml_converter.rb | 7 ++++ .../deltacloud/helpers/hardware_profiles_helper.rb | 17 +++++++++- server/server.rb | 6 ++++ server/views/instances/new.html.haml | 32 +++++++++++++++---- server/views/instances/show.html.haml | 6 ++++ 5 files changed, 59 insertions(+), 9 deletions(-)
diff --git a/server/lib/converters/xml_converter.rb b/server/lib/converters/xml_converter.rb index 0b11fd7..4b8e78e 100644 --- a/server/lib/converters/xml_converter.rb +++ b/server/lib/converters/xml_converter.rb @@ -82,6 +82,13 @@ module Converters builder.owner_id( obj.owner_id ) builder.image( :href=>@link_builder.send( :image_url, obj.image_id ) ) builder.flavor( :href=>@link_builder.send( :flavor_url, obj.flavor_id ) ) + builder.__send__( 'hardware-profile', :href=>@link_builder.send( :hardware_profile_url, obj.instance_profile.name) ) do + builder.id( obj.instance_profile.name ) + obj.instance_profile.overrides.each do |p, v| + u = ::Deltacloud::HardwareProfile::unit(p) + builder.property( :kind=>:fixed, :name=>p, :unit=>u, :value=>v ) + end + end builder.realm( :href=>@link_builder.send( :realm_url, obj.realm_id ) ) if obj.realm_id builder.state( obj.state ) builder.actions { diff --git a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb b/server/lib/deltacloud/helpers/hardware_profiles_helper.rb index 6269c23..ff7f239 100644 --- a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb +++ b/server/lib/deltacloud/helpers/hardware_profiles_helper.rb @@ -13,10 +13,23 @@ module HardwareProfilesHelper end end
+ def format_instance_profile(ip) + o = ip.overrides.collect do |p, v| + u = hardware_property_unit(p) + "#{p} = #{v} #{u}" + end + if o.empty? + "" + else + "with #{o.join(", ")}" + end + end + + private def hardware_property_unit(prop) - u = prop.unit + u = ::Deltacloud::HardwareProfile::unit(prop) u = "" if ["label", "count"].include?(u) - u = "vcpus" if prop.name == :cpu + u = "vcpus" if prop == :cpu u end end diff --git a/server/server.rb b/server/server.rb index 7151f47..5c1bd73 100644 --- a/server/server.rb +++ b/server/server.rb @@ -203,6 +203,7 @@ get "/api/instances/new" do @instance = Instance.new( { :id=>params[:id], :image_id=>params[:image_id] } ) @image = driver.image( credentials, :id => params[:image_id] ) @flavors = driver.flavors( credentials, { :architecture=>@image.architecture } ) + @hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture ) @realms = driver.realms(credentials) respond_to do |format| format.html { haml :"instances/new" } @@ -238,7 +239,12 @@ collection :instances do param :image_id, :string, :required param :realm_id, :string, :optional param :flavor_id, :string, :optional + param :hwp_id, :string, :optional control do + # FIXME: Strictly speaking, we'd need to check that only either + # hwp_id or flavor_id are set, but not both. Since flavors will go + # away shortly, we can be a little sloppy + params[:hwp_id] = params[:flavor_id] if params[:flavor_id] @image = driver.image(credentials, :id => params[:image_id]) instance = driver.create_instance(credentials, @image.id, params) respond_to do |format| diff --git a/server/views/instances/new.html.haml b/server/views/instances/new.html.haml index ded9106..c746475 100644 --- a/server/views/instances/new.html.haml +++ b/server/views/instances/new.html.haml @@ -8,16 +8,34 @@ %label Instance Name: %input{ :name => 'name', :size => 30 }/ - - if !@flavors.empty? - %h3 What flavor of machine? - - for flavor in @flavors + - if !@hardware_profiles.empty? + %h3 What size machine? + - for hwp in @hardware_profiles .radio-group - %label{ :for => "flavor_id_#{flavor.id}" } - %input{ :type => :radio, :name => 'flavor_id', :value => flavor.id }/ - = flavor.id + %label{ :for => "hwp_id_#{hwp.name}" } + %input{ :type => :radio, :name => 'hwp_id', :value => hwp.name }/ + = hwp.name %br/ %span.radio-group-details - = "#{flavor.architecture}, #{flavor.memory} GB, #{flavor.storage} GB" + - first = true + - hwp.properties.select { |prop| prop.fixed? }.each do |prop| + - unless first + , + - first = false + = format_hardware_property prop + - hwp.properties.reject { |prop| prop.fixed? }.each do |prop| + %span.radio-group-details + %label{ :for => "#{prop.param}_#{hwp.name}" } + = prop.name + - if prop.kind == :enum + %select{ :size => 1, :name => prop.param } + - for v in prop.values + %option= v + = prop.unit + - else + %input{ :name => prop.param, :size => 10, :value => "#{prop.default}" } + = prop.unit + (value must be between #{prop.first} #{prop.unit} and #{prop.last} #{prop.unit}) - if !@realms.empty? %h3 Where do you want it? - for realm in @realms diff --git a/server/views/instances/show.html.haml b/server/views/instances/show.html.haml index aa285e8..2a81c12 100644 --- a/server/views/instances/show.html.haml +++ b/server/views/instances/show.html.haml @@ -19,6 +19,12 @@ %dd = @instance.flavor_id ? link_to(@instance.flavor_id, flavor_url(@instance.flavor_id)) : 'default' %di + %dt Hardware Profile + %dd + - prof = @instance.instance_profile + = link_to(prof.name, hardware_profile_url(prof.name)) + = format_instance_profile(prof) + %di %dt Realm %dd = @instance.realm_id ? link_to(@instance.realm_id, realm_url(@instance.realm_id)) : 'default'
From: David Lutterkort lutter@redhat.com
--- client/bin/deltacloudc | 1 - client/lib/dcloud/hardware_profile.rb | 85 +++++++++++++++++++++++++++++ client/lib/dcloud/instance.rb | 30 ++++++++++ client/lib/deltacloud.rb | 49 ++++++++++++++++- client/specs/fixtures/instances/inst0.yml | 17 ++++++ client/specs/fixtures/instances/inst1.yml | 2 + client/specs/fixtures/instances/inst2.yml | 2 + client/specs/hardware_profiles_spec.rb | 71 ++++++++++++++++++++++++ client/specs/instances_spec.rb | 46 +++++++++++++--- 9 files changed, 291 insertions(+), 12 deletions(-) create mode 100644 client/lib/dcloud/hardware_profile.rb create mode 100644 client/specs/fixtures/instances/inst0.yml create mode 100644 client/specs/hardware_profiles_spec.rb
diff --git a/client/bin/deltacloudc b/client/bin/deltacloudc index d2a7936..246c5b1 100755 --- a/client/bin/deltacloudc +++ b/client/bin/deltacloudc @@ -70,7 +70,6 @@ client = DeltaCloud.new(url.user, url.password, api_url, { :verbose => options[: collections = client.entry_points.keys
# Exclude collection which don't have methods in client library yet -collections.delete(:hardware_profiles) collections.delete(:instance_states)
# If list parameter passed print out available collection diff --git a/client/lib/dcloud/hardware_profile.rb b/client/lib/dcloud/hardware_profile.rb new file mode 100644 index 0000000..d6c4aa0 --- /dev/null +++ b/client/lib/dcloud/hardware_profile.rb @@ -0,0 +1,85 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +require 'dcloud/base_model' + +module DCloud + class HardwareProfile < BaseModel + + class Property + attr_reader :name, :unit, :value + + def initialize(xml, name) + @name = name + p = REXML::XPath.first(xml, "property[@name = '#{name}']") + if p + @value = p.attributes['value'] + @unit = p.attributes['unit'] + end + end + + def present? + ! @value.nil? + end + + def to_s + v = @value || "---" + u = @unit || "" + u = "" if ["label", "count"].include?(u) + "#{v} #{u}" + end + end + + class FloatProperty < Property + def initialize(xml, name) + super(xml, name) + @value = @value.to_f if @value + end + end + + xml_tag_name :hardware_profile + + attribute :memory + attribute :storage + attribute :architecture + + def initialize(client, uri, xml=nil) + super( client, uri, xml ) + end + + def load_payload(xml=nil) + super(xml) + unless xml.nil? + @memory = FloatProperty.new(xml, 'memory') + @storage = FloatProperty.new(xml, 'storage') + @architecture = Property.new(xml, 'architecture') + end + end + + def to_plain + sprintf("%-15s | %-6s | %10s | %10s ", id[0, 15], + architecture.to_s[0,6], memory.to_s[0,10], storage.to_s[0,10]) + end + + private + def property_value(xml, name) + p = REXML::XPath.first(xml, "property[@name = '#{name}']") + p ? p.attributes['value'] : "" + end + end +end diff --git a/client/lib/dcloud/instance.rb b/client/lib/dcloud/instance.rb index e3455aa..00636c6 100644 --- a/client/lib/dcloud/instance.rb +++ b/client/lib/dcloud/instance.rb @@ -19,6 +19,33 @@ require 'dcloud/base_model'
module DCloud + + class InstanceProfile + attr_reader :hardware_profile, :id + + def initialize(client, xml) + @hardware_profile = HardwareProfile.new(client, xml.attributes['href']) + @properties = {} + @id = xml.text("id") + xml.get_elements('property').each do |prop| + @properties[prop.attributes['name'].to_sym] = { + :value => prop.attributes['value'], + :unit => prop.attributes['unit'], + :kind => prop.attributes['kind'].to_sym + } + end + end + + def [](prop) + p = @properties[prop] + p ? p[:value] : nil + end + + def property(prop) + @properties[prop] + end + end + class Instance < BaseModel
xml_tag_name :instance @@ -33,6 +60,7 @@ module DCloud attribute :flavor attribute :realm attribute :action_urls + attribute :instance_profile
def initialize(client, uri, xml=nil) @action_urls = {} @@ -100,6 +128,8 @@ module DCloud realm_uri = xml.get_elements( 'realm' )[0].attributes['href'] @realm = Realm.new( @client, realm_uri ) end + instance_profile = xml.get_elements( 'hardware-profile' ).first + @instance_profile = InstanceProfile.new( @client, instance_profile ) @state = xml.text( 'state' ) @actions = [] xml.get_elements( 'actions/link' ).each do |link| diff --git a/client/lib/deltacloud.rb b/client/lib/deltacloud.rb index 87c60fa..dbbd89b 100644 --- a/client/lib/deltacloud.rb +++ b/client/lib/deltacloud.rb @@ -19,6 +19,7 @@ require 'rest_client' require 'rexml/document' require 'logger' require 'dcloud/flavor' +require 'dcloud/hardware_profile' require 'dcloud/realm' require 'dcloud/image' require 'dcloud/instance' @@ -108,6 +109,34 @@ class DeltaCloud nil end
+ def hardware_profiles(opts={}) + hardware_profiles = [] + request(entry_points[:hardware_profiles], :get, opts) do |response| + doc = REXML::Document.new( response ) + doc.get_elements( 'hardware-profiles/hardware-profile' ).each do |hwp| + uri = hwp.attributes['href'] + hardware_profiles << DCloud::HardwareProfile.new( self, uri, hwp ) + end + end + hardware_profiles + end + + def hardware_profile(id) + request( entry_points[:hardware_profiles], :get, {:id=>id } ) do |response| + doc = REXML::Document.new( response ) + doc.get_elements( '/hardware-profile' ).each do |hwp| + uri = hwp.attributes['href'] + return DCloud::HardwareProfile.new( self, uri, hwp ) + end + end + end + + def fetch_hardware_profile(uri) + xml = fetch_resource( :hardware_profile, uri ) + return DCloud::HardwareProfile.new( self, uri, xml ) if xml + nil + end + def fetch_resource(type, uri) request( uri ) do |response| doc = REXML::Document.new( response ) @@ -262,16 +291,32 @@ class DeltaCloud nil end
+ # Create a new instance, using image +image_id+. Possible optiosn are + # + # name - a user-defined name for the instance + # realm - a specific realm for placement of the instance + # hardware_profile - either a string giving the name of the + # hardware profile or a hash. The hash must have an + # entry +id+, giving the id of the hardware profile, + # and may contain additional names of properties, + # e.g. 'storage', to override entries in the + # hardware profile def create_instance(image_id, opts={}) name = opts[:name] realm_id = opts[:realm] - flavor_id = opts[:flavor]
params = {} ( params[:realm_id] = realm_id ) if realm_id - ( params[:flavor_id] = flavor_id ) if flavor_id ( params[:name] = name ) if name
+ if opts[:hardware_profile].is_a?(String) + params[:hwp_id] = opts[:hardware_profile] + elsif opts[:hardware_profile].is_a?(Hash) + opts[:hardware_profile].each do |k,v| + params[:"hwp_#{k}"] = v + end + end + params[:image_id] = image_id request( entry_points[:instances], :post, {}, params ) do |response| doc = REXML::Document.new( response ) diff --git a/client/specs/fixtures/instances/inst0.yml b/client/specs/fixtures/instances/inst0.yml new file mode 100644 index 0000000..c1880b7 --- /dev/null +++ b/client/specs/fixtures/instances/inst0.yml @@ -0,0 +1,17 @@ +--- +:realm_id: us +:public_addresses: +- img1.inst0.public.com +:state: RUNNING +:name: "Mock Instance With Profile Change" +:private_addresses: +- img1.inst0.private.com +:image_id: img1 +:flavor_id: m1-large +:instance_profile: !ruby/object:InstanceProfile + id: m1-large + memory: "12288" +:owner_id: mockuser +:actions: +- :reboot +- :stop diff --git a/client/specs/fixtures/instances/inst1.yml b/client/specs/fixtures/instances/inst1.yml index 26a6e3b..aa3f86e 100644 --- a/client/specs/fixtures/instances/inst1.yml +++ b/client/specs/fixtures/instances/inst1.yml @@ -6,3 +6,5 @@ :private_addresses: [ img3.inst1.private.com ] :flavor_id: m1-small :realm_id: us +:instance_profile: !ruby/object:InstanceProfile + id: m1-small diff --git a/client/specs/fixtures/instances/inst2.yml b/client/specs/fixtures/instances/inst2.yml index ee1de37..69d4720 100644 --- a/client/specs/fixtures/instances/inst2.yml +++ b/client/specs/fixtures/instances/inst2.yml @@ -6,3 +6,5 @@ :private_addresses: [ img1.inst2.private.com ] :flavor_id: m1-small :realm_id: us +:instance_profile: !ruby/object:InstanceProfile + id: m1-large diff --git a/client/specs/hardware_profiles_spec.rb b/client/specs/hardware_profiles_spec.rb new file mode 100644 index 0000000..5a59aa1 --- /dev/null +++ b/client/specs/hardware_profiles_spec.rb @@ -0,0 +1,71 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +require 'specs/spec_helper' + +def prop_check(prop, value_class) + if prop.present? + prop.value.should_not be_nil + prop.value.should be_a(value_class) + end +end + +describe "hardware_profiles" do + + it_should_behave_like "all resources" + + it "should allow retrieval of all hardware profiles" do + DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| + hardware_profiles = client.hardware_profiles + hardware_profiles.should_not be_empty + hardware_profiles.each do |hwp| + hwp.uri.should_not be_nil + hwp.uri.should be_a(String) + prop_check(hwp.architecture, String) + prop_check(hwp.storage, Float) + prop_check(hwp.memory, Float) + end + end + end + + it "should allow filtering of hardware_profiles by architecture" do + DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| + hardware_profiles = client.hardware_profiles( :architecture=>'i386' ) + hardware_profiles.should_not be_empty + hardware_profiles.size.should eql( 2 ) + hardware_profiles.first.architecture.value.should eql( 'i386' ) + end + end + + it "should allow fetching a hardware_profile by id" do + DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| + hwp = client.hardware_profile( 'm1-small' ) + hwp.should_not be_nil + hwp.id.should eql( 'm1-small' ) + end + end + + it "should allow fetching a hardware_profile by URI" do + DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| + hwp = client.fetch_hardware_profile( API_URL + '/hardware_profiles/m1-small' ) + hwp.should_not be_nil + hwp.id.should eql( 'm1-small' ) + end + end + +end diff --git a/client/specs/instances_spec.rb b/client/specs/instances_spec.rb index d5a7816..8aaf79f 100644 --- a/client/specs/instances_spec.rb +++ b/client/specs/instances_spec.rb @@ -35,6 +35,8 @@ describe "instances" do instance.image.should be_a( DCloud::Image ) instance.flavor.should_not be_nil instance.flavor.should be_a( DCloud::Flavor ) + instance.instance_profile.should_not be_nil + instance.instance_profile.should be_a( DCloud::InstanceProfile ) instance.state.should_not be_nil instance.state.should be_a( String ) instance.public_addresses.should_not be_nil @@ -60,18 +62,23 @@ describe "instances" do
it "should allow retrieval of a single instance" do DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| - instance = client.instance( "inst1" ) + instance = client.instance( "inst0" ) instance.should_not be_nil instance.name.should_not be_nil - instance.name.should eql( 'MockUserInstance' ) + instance.name.should eql( 'Mock Instance With Profile Change' ) instance.uri.should_not be_nil instance.uri.should be_a( String ) instance.owner_id.should eql( "mockuser" ) - instance.public_addresses.first.should eql( "img3.inst1.public.com" ) + instance.public_addresses.first.should eql( "img1.inst0.public.com" ) instance.image.should_not be_nil - instance.image.uri.should eql( API_URL + "/images/img3" ) + instance.image.uri.should eql( API_URL + "/images/img1" ) instance.flavor.should_not be_nil - instance.flavor.uri.should eql( API_URL + "/flavors/m1-small" ) + instance.flavor.uri.should eql( API_URL + "/flavors/m1-large" ) + instance.instance_profile.should_not be_nil + instance.instance_profile.hardware_profile.should_not be_nil + instance.instance_profile.hardware_profile.uri.should eql( API_URL + "/hardware_profiles/m1-large" ) + instance.instance_profile[:memory].should eql( "12288" ) + instance.instance_profile[:storage].should be_nil instance.state.should eql( "RUNNING" ) instance.actions.should_not be_nil end @@ -86,6 +93,7 @@ describe "instances" do instance.name.should eql( 'TestInstance' ) instance.image.id.should eql( 'img1' ) instance.flavor.id.should eql( 'm1-large' ) + instance.instance_profile.id.should eql( 'm1-large' ) instance.realm.id.should eql( 'us' ) end end @@ -98,30 +106,50 @@ describe "instances" do instance.id.should match( /inst[0-9]+/ ) instance.image.id.should eql( 'img1' ) instance.flavor.id.should eql( 'm1-large' ) + instance.instance_profile.id.should eql( 'm1-large' ) instance.realm.id.should eql( 'eu' ) end end
- it "should allow creation of new instances with specific flavor" do + it "should allow creation of new instances with specific hardware profile" do DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| - instance = client.create_instance( 'img1', :flavor=>'m1-xlarge' ) + instance = client.create_instance( 'img1', + :hardware_profile=>'m1-xlarge' ) instance.should_not be_nil instance.uri.should match( %r{#{API_URL}/instances/inst[0-9]+} ) instance.id.should match( /inst[0-9]+/ ) instance.image.id.should eql( 'img1' ) instance.flavor.id.should eql( 'm1-xlarge' ) + instance.instance_profile.id.should eql( 'm1-xlarge' ) instance.realm.id.should eql( 'us' ) end end
- it "should allow creation of new instances with specific realm and flavor" do + it "should allow creation of new instances with specific hardware profile overriding memory" do DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| - instance = client.create_instance( 'img1', :realm=>'eu', :flavor=>'m1-xlarge' ) + hwp = { :id => 'm1-xlarge', :memory => 32768 } + instance = client.create_instance( 'img1', :hardware_profile=> hwp ) instance.should_not be_nil instance.uri.should match( %r{#{API_URL}/instances/inst[0-9]+} ) instance.id.should match( /inst[0-9]+/ ) instance.image.id.should eql( 'img1' ) instance.flavor.id.should eql( 'm1-xlarge' ) + instance.instance_profile.id.should eql( 'm1-xlarge' ) + instance.instance_profile[:memory].should eql( "32768" ) + instance.realm.id.should eql( 'us' ) + end + end + + it "should allow creation of new instances with specific realm and hardware profile" do + DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| + instance = client.create_instance( 'img1', :realm=>'eu', + :hardware_profile=>'m1-xlarge' ) + instance.should_not be_nil + instance.uri.should match( %r{#{API_URL}/instances/inst[0-9]+} ) + instance.id.should match( /inst[0-9]+/ ) + instance.image.id.should eql( 'img1' ) + instance.flavor.id.should eql( 'm1-xlarge' ) + instance.instance_profile.id.should eql( 'm1-xlarge' ) instance.realm.id.should eql( 'eu' ) end end
From: David Lutterkort lutter@redhat.com
--- server/features/hardware_profiles.feature | 23 +++++++++++ .../step_definitions/hardware_profiles_steps.rb | 41 ++++++++++++++++++++ server/features/support/mock/config.yaml | 5 ++- 3 files changed, 68 insertions(+), 1 deletions(-) create mode 100644 server/features/hardware_profiles.feature create mode 100644 server/features/step_definitions/hardware_profiles_steps.rb
diff --git a/server/features/hardware_profiles.feature b/server/features/hardware_profiles.feature new file mode 100644 index 0000000..9c1de72 --- /dev/null +++ b/server/features/hardware_profiles.feature @@ -0,0 +1,23 @@ +Feature: Working with hardware profiles + In order to work with hardware profiles + + Background: + Given I want to get XML + + Scenario: I want to get list of all hardware profiles + When I follow hardware profiles link in entry points + Then I should see <HARDWARE_PROFILE_COUNT> hardware profile inside hardware profiles + And each link in hardware profiles should point me to valid hardware profile + + Scenario: I want to show hardware profile details + When I request for '<HARDWARE_PROFILE_ID>' hardware profile + Then I should get this hardware profile + And it should have a href attribute + And hardware profile should include id parameter + And it should have a fixed property 'cpu' + And it should have a range property 'memory' + And it should have a enum property 'storage' + + Scenario: I want filter hardware profiles by architecture + When I want hardware profiles with '<HARDWARE_PROFILE_ARCH>' architecture + Then the returned hardware profiles should have architecture '<HARDWARE_PROFILE_ARCH>' diff --git a/server/features/step_definitions/hardware_profiles_steps.rb b/server/features/step_definitions/hardware_profiles_steps.rb new file mode 100644 index 0000000..b2e64bf --- /dev/null +++ b/server/features/step_definitions/hardware_profiles_steps.rb @@ -0,0 +1,41 @@ +Then /^it should have a (\w+) attribute$/ do |name| + attr = Nokogiri::XML(last_response.body).xpath('/hardware-profile').first[name] + attr.should_not be_nil + if (name == 'href') + attr.should == "http://example.org/api/hardware_profiles/#%7BCONFIG%5B:hardware_profile_id%5..." + end +end + +Then /^it should have a (\w+) property '(.+)'$/ do |kind, name| + props = Nokogiri::XML(last_response.body).xpath("/hardware-profile/property[@name = '#{name}']") + props.size.should == 1 + prop = props.first + prop['kind'].should == kind + prop['unit'].should_not be_nil + if kind == 'range' + ranges = prop.xpath('range') + ranges.size.should == 1 + range = ranges.first + range['first'].should_not be_nil + range['last'].should_not be_nil + end + if kind == 'enum' + enums = prop.xpath('enum') + enums.size.should == 1 + enums.first.xpath('entry').size.should_not == 0 + end +end + +Then /^the returned hardware profiles should have (.+) '(.+)'$/ do |parameter, value| + params = {} + value = replace_variables(value) + @tested_params.collect { |param| params[:"#{param[0]}"] = param[1] } + get '/api/hardware_profiles', params, {} + last_response.status.should == 200 + parameters = [] + Nokogiri::XML(last_response.body).xpath("/hardware-profiles/hardware-profile/property[@name = '#{parameter}']").each do |elt| + parameters << elt['value'] + end + parameters.uniq.size.should == 1 + parameters.uniq.first.should == value +end diff --git a/server/features/support/mock/config.yaml b/server/features/support/mock/config.yaml index 6b76f08..4ec0d03 100644 --- a/server/features/support/mock/config.yaml +++ b/server/features/support/mock/config.yaml @@ -1,4 +1,4 @@ ---- +--- :username: mockuser :password: mockpassword :storage_snapshot_id: snap2 @@ -19,7 +19,10 @@ :image_id: img2 :storage_volume_state: AVAILABLE :flavor_id: m1-small +:hardware_profile_id: m1-large :instance_realm: us :image_count: 3 :storage_volume_count: 2 :flavor_count: 5 +:hardware_profile_count: 4 +:hardware_profile_arch: x86_64
From: David Lutterkort lutter@redhat.com
From now on, we solely use hardware profiles
--- client/bin/deltacloudc | 8 +- client/lib/dcloud/flavor.rb | 49 -------------- client/lib/dcloud/instance.rb | 3 - client/lib/deltacloud.rb | 29 --------- client/specs/fixtures/instances/inst0.yml | 1 - client/specs/fixtures/instances/inst1.yml | 1 - client/specs/fixtures/instances/inst2.yml | 1 - client/specs/flavors_spec.rb | 67 -------------------- client/specs/initialization_spec.rb | 2 +- client/specs/instances_spec.rb | 9 --- client/specs/storage_volume_spec.rb | 6 +- server/Rakefile | 1 - server/deltacloud.rb | 1 - server/features/api.feature | 2 - server/features/flavors.feature | 23 ------- server/features/instances.feature | 8 +- server/features/step_definitions/flavors_steps.rb | 5 -- .../features/step_definitions/instances_steps.rb | 11 ++- server/features/support/ec2/config.yaml | 8 +- server/features/support/mock/config.yaml | 3 - server/lib/converters/xml_converter.rb | 8 --- server/lib/deltacloud/base_driver/base_driver.rb | 14 ---- server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 45 ------------- .../drivers/mock/data/instances/inst0.yml | 1 - .../drivers/mock/data/instances/inst1.yml | 1 - .../drivers/mock/data/instances/inst2.yml | 1 - server/lib/deltacloud/drivers/mock/mock_driver.rb | 46 ------------- .../drivers/opennebula/opennebula_driver.rb | 34 +--------- .../drivers/rackspace/rackspace_driver.rb | 16 ----- .../lib/deltacloud/drivers/rhevm/rhevm_driver.rb | 20 +----- .../deltacloud/drivers/rimu/rimu_hosting_driver.rb | 16 +----- server/lib/deltacloud/helpers/conversion_helper.rb | 2 +- server/lib/deltacloud/helpers/driver_helper.rb | 2 +- server/lib/deltacloud/models/flavor.rb | 25 ------- server/lib/deltacloud/models/instance.rb | 1 - server/lib/sinatra/rabbit.rb | 2 +- server/server.rb | 24 ------- server/tests/deltacloud_test.rb | 2 +- server/tests/flavors_test.rb | 65 ------------------- server/tests/images_test.rb | 8 +- server/tests/instances_test.rb | 14 ++-- server/tests/realms_test.rb | 8 +- server/views/flavors/index.html.haml | 21 ------ server/views/flavors/show.html.haml | 16 ----- server/views/instances/index.html.haml | 3 - server/views/instances/show.html.haml | 4 - 46 files changed, 48 insertions(+), 589 deletions(-) delete mode 100644 client/lib/dcloud/flavor.rb delete mode 100644 client/specs/flavors_spec.rb delete mode 100644 server/features/flavors.feature delete mode 100644 server/features/step_definitions/flavors_steps.rb delete mode 100644 server/lib/deltacloud/models/flavor.rb delete mode 100644 server/tests/flavors_test.rb delete mode 100644 server/views/flavors/index.html.haml delete mode 100644 server/views/flavors/show.html.haml
diff --git a/client/bin/deltacloudc b/client/bin/deltacloudc index 246c5b1..1ba0bf5 100755 --- a/client/bin/deltacloudc +++ b/client/bin/deltacloudc @@ -39,7 +39,7 @@ BANNER opts.on( '-i', '--id ID', 'ID for operation') { |id| options[:id] = id } opts.on( '-d', '--image-id ID', 'Image ID') { |id| options[:image_id] = id } opts.on( '-a', '--arch ARCH', 'Architecture (x86, x86_64)') { |id| options[:architecture] = id } - opts.on( '-f', '--flavor_id FLAVOR', 'Flavor') { |id| options[:flavor_id] = id } + opts.on( '-p', '--hardware-profile HARDWARE_PROFILE', 'Hardware Profile') { |id| options[:hwp_id] = id } opts.on( '-n', '--name NAME', 'Name (for instance eg.)') { |name| options[:name] = name } opts.on( '-s', '--state STATE', 'Instance state (RUNNING, STOPPED)') { |state| options[:state] = state } opts.on( '-u', '--url URL', 'API url ($API_URL variable)') { |url| options[:api_url] = url } @@ -97,7 +97,7 @@ if options[:version] exit(0) end
-# List items from collection (typically /flavors) +# List items from collection (typically /instances) # Do same if 'index' operation is set if options[:collection] and ( options[:operation].nil? or options[:operation].eql?('index') ) invalid_usage("Unknown collection: #{options[:collection]}") unless collections.include?(options[:collection].to_sym) @@ -126,7 +126,7 @@ if options[:collection] and options[:operation] end
# If collection is set and requested operation is create new instance, - # --image-id, --flavor-id and --name parameters are used + # --image-id, --hardware-profile and --name parameters are used # Returns created instance in plain form if options[:collection].eql?('instances') and options[:operation].eql?('create') invalid_usage("Missing image-id") unless options[:image_id] @@ -135,7 +135,7 @@ if options[:collection] and options[:operation] end params.merge!(:name => options[:name]) if options[:name] params.merge!(:image_id => options[:image_id]) if options[:image_id] - params.merge!(:flavor_id => options[:flavor_id]) if options[:flavor_id] + params.merge!(:hwp_id => options[:hwp_id]) if options[:hwp_id] instance = client.create_instance(options[:image_id], params) puts instance.to_plain exit(0) diff --git a/client/lib/dcloud/flavor.rb b/client/lib/dcloud/flavor.rb deleted file mode 100644 index dfa3b44..0000000 --- a/client/lib/dcloud/flavor.rb +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -require 'dcloud/base_model' - -module DCloud - class Flavor < BaseModel - - xml_tag_name :flavor - - attribute :memory - attribute :storage - attribute :architecture - - def initialize(client, uri, xml=nil) - super( client, uri, xml ) - end - - def load_payload(xml=nil) - super(xml) - unless xml.nil? - @memory = xml.text( 'memory' ).to_f - @storage = xml.text( 'storage' ).to_f - @architecture = xml.text( 'architecture' ) - end - end - - def to_plain - sprintf("%-15s | %-6s | %10s GB | %10s GB", self.id[0, 15], self.architecture[0,6], - self.memory.to_s[0,10], self.storage.to_s[0,10]) - end - - end -end diff --git a/client/lib/dcloud/instance.rb b/client/lib/dcloud/instance.rb index 00636c6..4898083 100644 --- a/client/lib/dcloud/instance.rb +++ b/client/lib/dcloud/instance.rb @@ -57,7 +57,6 @@ module DCloud attribute :state attribute :actions attribute :image - attribute :flavor attribute :realm attribute :action_urls attribute :instance_profile @@ -121,8 +120,6 @@ module DCloud end image_uri = xml.get_elements( 'image' )[0].attributes['href'] @image = Image.new( @client, image_uri ) - flavor_uri = xml.get_elements( 'flavor' )[0].attributes['href'] - @flavor = Flavor.new( @client, flavor_uri ) # Only use realms if they are there if (!xml.get_elements( 'realm' ).empty?) realm_uri = xml.get_elements( 'realm' )[0].attributes['href'] diff --git a/client/lib/deltacloud.rb b/client/lib/deltacloud.rb index dbbd89b..7593a56 100644 --- a/client/lib/deltacloud.rb +++ b/client/lib/deltacloud.rb @@ -18,7 +18,6 @@ require 'rest_client' require 'rexml/document' require 'logger' -require 'dcloud/flavor' require 'dcloud/hardware_profile' require 'dcloud/realm' require 'dcloud/image' @@ -81,34 +80,6 @@ class DeltaCloud @features.has_key?(collection) && @features[collection].include?(name) end
- def flavors(opts={}) - flavors = [] - request(entry_points[:flavors], :get, opts) do |response| - doc = REXML::Document.new( response ) - doc.get_elements( 'flavors/flavor' ).each do |flavor| - uri = flavor.attributes['href'] - flavors << DCloud::Flavor.new( self, uri, flavor ) - end - end - flavors - end - - def flavor(id) - request( entry_points[:flavors], :get, {:id=>id } ) do |response| - doc = REXML::Document.new( response ) - doc.get_elements( '/flavor' ).each do |flavor| - uri = flavor.attributes['href'] - return DCloud::Flavor.new( self, uri, flavor ) - end - end - end - - def fetch_flavor(uri) - xml = fetch_resource( :flavor, uri ) - return DCloud::Flavor.new( self, uri, xml ) if xml - nil - end - def hardware_profiles(opts={}) hardware_profiles = [] request(entry_points[:hardware_profiles], :get, opts) do |response| diff --git a/client/specs/fixtures/instances/inst0.yml b/client/specs/fixtures/instances/inst0.yml index c1880b7..a5b73be 100644 --- a/client/specs/fixtures/instances/inst0.yml +++ b/client/specs/fixtures/instances/inst0.yml @@ -7,7 +7,6 @@ :private_addresses: - img1.inst0.private.com :image_id: img1 -:flavor_id: m1-large :instance_profile: !ruby/object:InstanceProfile id: m1-large memory: "12288" diff --git a/client/specs/fixtures/instances/inst1.yml b/client/specs/fixtures/instances/inst1.yml index aa3f86e..3544d40 100644 --- a/client/specs/fixtures/instances/inst1.yml +++ b/client/specs/fixtures/instances/inst1.yml @@ -4,7 +4,6 @@ :owner_id: mockuser :public_addresses: [ img3.inst1.public.com ] :private_addresses: [ img3.inst1.private.com ] -:flavor_id: m1-small :realm_id: us :instance_profile: !ruby/object:InstanceProfile id: m1-small diff --git a/client/specs/fixtures/instances/inst2.yml b/client/specs/fixtures/instances/inst2.yml index 69d4720..9a70cb8 100644 --- a/client/specs/fixtures/instances/inst2.yml +++ b/client/specs/fixtures/instances/inst2.yml @@ -4,7 +4,6 @@ :owner_id: anotheruser :public_addresses: [ img1.inst2.public.com ] :private_addresses: [ img1.inst2.private.com ] -:flavor_id: m1-small :realm_id: us :instance_profile: !ruby/object:InstanceProfile id: m1-large diff --git a/client/specs/flavors_spec.rb b/client/specs/flavors_spec.rb deleted file mode 100644 index 91a063e..0000000 --- a/client/specs/flavors_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -require 'specs/spec_helper' - -describe "flavors" do - - it_should_behave_like "all resources" - - it "should allow retrieval of all flavors" do - DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| - flavors = client.flavors - flavors.should_not be_empty - flavors.each do |flavor| - flavor.uri.should_not be_nil - flavor.uri.should be_a(String) - flavor.architecture.should_not be_nil - flavor.architecture.should be_a(String) - flavor.storage.should_not be_nil - flavor.storage.should be_a(Float) - flavor.memory.should_not be_nil - flavor.memory.should be_a(Float) - end - end - end - - it "should allow filtering of flavors by architecture" do - DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| - flavors = client.flavors( :architecture=>'i386' ) - flavors.should_not be_empty - flavors.size.should eql( 1 ) - flavors.first.architecture.should eql( 'i386' ) - end - end - - it "should allow fetching a flavor by id" do - DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| - flavor = client.flavor( 'm1-small' ) - flavor.should_not be_nil - flavor.id.should eql( 'm1-small' ) - end - end - - it "should allow fetching a flavor by URI" do - DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client| - flavor = client.fetch_flavor( API_URL + '/flavors/m1-small' ) - flavor.should_not be_nil - flavor.id.should eql( 'm1-small' ) - end - end - -end diff --git a/client/specs/initialization_spec.rb b/client/specs/initialization_spec.rb index 6fcf1a4..98e740b 100644 --- a/client/specs/initialization_spec.rb +++ b/client/specs/initialization_spec.rb @@ -28,7 +28,7 @@ describe "initializing the client" do
it "should discover entry points upon connection" do DeltaCloud.new( "name", "password", API_URL ) do |client| - client.entry_points[:flavors].should eql( "#{API_URL}/flavors" ) + client.entry_points[:hardware_profiles].should eql( "#{API_URL}/hardware_profiles" ) client.entry_points[:images].should eql( "#{API_URL}/images" ) client.entry_points[:instances].should eql( "#{API_URL}/instances" ) client.entry_points[:storage_volumes].should eql( "#{API_URL}/storage_volumes" ) diff --git a/client/specs/instances_spec.rb b/client/specs/instances_spec.rb index 8aaf79f..2e90e99 100644 --- a/client/specs/instances_spec.rb +++ b/client/specs/instances_spec.rb @@ -33,8 +33,6 @@ describe "instances" do instance.owner_id.should be_a( String ) instance.image.should_not be_nil instance.image.should be_a( DCloud::Image ) - instance.flavor.should_not be_nil - instance.flavor.should be_a( DCloud::Flavor ) instance.instance_profile.should_not be_nil instance.instance_profile.should be_a( DCloud::InstanceProfile ) instance.state.should_not be_nil @@ -72,8 +70,6 @@ describe "instances" do instance.public_addresses.first.should eql( "img1.inst0.public.com" ) instance.image.should_not be_nil instance.image.uri.should eql( API_URL + "/images/img1" ) - instance.flavor.should_not be_nil - instance.flavor.uri.should eql( API_URL + "/flavors/m1-large" ) instance.instance_profile.should_not be_nil instance.instance_profile.hardware_profile.should_not be_nil instance.instance_profile.hardware_profile.uri.should eql( API_URL + "/hardware_profiles/m1-large" ) @@ -92,7 +88,6 @@ describe "instances" do instance.id.should match( /inst[0-9]+/ ) instance.name.should eql( 'TestInstance' ) instance.image.id.should eql( 'img1' ) - instance.flavor.id.should eql( 'm1-large' ) instance.instance_profile.id.should eql( 'm1-large' ) instance.realm.id.should eql( 'us' ) end @@ -105,7 +100,6 @@ describe "instances" do instance.uri.should match( %r{#{API_URL}/instances/inst[0-9]+} ) instance.id.should match( /inst[0-9]+/ ) instance.image.id.should eql( 'img1' ) - instance.flavor.id.should eql( 'm1-large' ) instance.instance_profile.id.should eql( 'm1-large' ) instance.realm.id.should eql( 'eu' ) end @@ -119,7 +113,6 @@ describe "instances" do instance.uri.should match( %r{#{API_URL}/instances/inst[0-9]+} ) instance.id.should match( /inst[0-9]+/ ) instance.image.id.should eql( 'img1' ) - instance.flavor.id.should eql( 'm1-xlarge' ) instance.instance_profile.id.should eql( 'm1-xlarge' ) instance.realm.id.should eql( 'us' ) end @@ -133,7 +126,6 @@ describe "instances" do instance.uri.should match( %r{#{API_URL}/instances/inst[0-9]+} ) instance.id.should match( /inst[0-9]+/ ) instance.image.id.should eql( 'img1' ) - instance.flavor.id.should eql( 'm1-xlarge' ) instance.instance_profile.id.should eql( 'm1-xlarge' ) instance.instance_profile[:memory].should eql( "32768" ) instance.realm.id.should eql( 'us' ) @@ -148,7 +140,6 @@ describe "instances" do instance.uri.should match( %r{#{API_URL}/instances/inst[0-9]+} ) instance.id.should match( /inst[0-9]+/ ) instance.image.id.should eql( 'img1' ) - instance.flavor.id.should eql( 'm1-xlarge' ) instance.instance_profile.id.should eql( 'm1-xlarge' ) instance.realm.id.should eql( 'eu' ) end diff --git a/client/specs/storage_volume_spec.rb b/client/specs/storage_volume_spec.rb index 204e2b7..ff461d7 100644 --- a/client/specs/storage_volume_spec.rb +++ b/client/specs/storage_volume_spec.rb @@ -46,7 +46,8 @@ describe "storage volumes" do storage_volume.device.should eql( '/dev/sda1' ) storage_volume.instance.should_not be_nil storage_volume.instance.id.should eql( 'inst1' ) - storage_volume.instance.flavor.architecture.should eql( 'i386' ) + ip = storage_volume.instance.instance_profile + ip.hardware_profile.architecture.value.should eql( 'i386' ) end end
@@ -62,7 +63,8 @@ describe "storage volumes" do storage_volume.device.should eql( '/dev/sda1' ) storage_volume.instance.should_not be_nil storage_volume.instance.id.should eql( 'inst1' ) - storage_volume.instance.flavor.architecture.should eql( 'i386' ) + ip = storage_volume.instance.instance_profile + ip.hardware_profile.architecture.value.should eql( 'i386' ) end end
diff --git a/server/Rakefile b/server/Rakefile index 72b7af6..c8b54c4 100644 --- a/server/Rakefile +++ b/server/Rakefile @@ -27,7 +27,6 @@ require 'cucumber/rake/task' desc "Run basic unit tests" Rake::TestTask.new("test") { |t| t.test_files = FileList[ - 'tests/flavors_test.rb', 'tests/realms_test.rb', 'tests/images_test.rb', 'tests/instances_test.rb', diff --git a/server/deltacloud.rb b/server/deltacloud.rb index 2a60679..1c19a5d 100644 --- a/server/deltacloud.rb +++ b/server/deltacloud.rb @@ -6,7 +6,6 @@ require 'deltacloud/hardware_profile' require 'deltacloud/state_machine'
require 'deltacloud/models/base_model' -require 'deltacloud/models/flavor' require 'deltacloud/models/realm' require 'deltacloud/models/image' require 'deltacloud/models/instance' diff --git a/server/features/api.feature b/server/features/api.feature index 25b109a..49dabcb 100644 --- a/server/features/api.feature +++ b/server/features/api.feature @@ -5,7 +5,6 @@ Feature: Working with API Given I want to get XML When I request for entry points Then I should see these entry points: - | flavors | | realms | | instances | | images | @@ -19,7 +18,6 @@ Feature: Working with API When I request for entry points Then I should get valid HTML response And I should see these entry points in page: - | flavors | | realms | | instances | | images | diff --git a/server/features/flavors.feature b/server/features/flavors.feature deleted file mode 100644 index 6dd0934..0000000 --- a/server/features/flavors.feature +++ /dev/null @@ -1,23 +0,0 @@ -Feature: Working with flavors - In order to work with flavors - - Background: - Given I want to get XML - - Scenario: I want to get list of all flavors - When I follow flavors link in entry points - Then I should see <FLAVOR_COUNT> flavor inside flavors - And each link in flavors should point me to valid flavor - - Scenario: I want to show flavor details - When I request for '<FLAVOR_ID>' flavor - Then I should get this flavor - And flavor should have valid href parameter - And flavor should include id parameter - And flavor should include architecture parameter - And flavor should include memory parameter - And flavor should include storage parameter - - Scenario: I want filter flavors by architecture - When I want flavors with '<FLAVOR_ARCH>' architecture - Then I should get only flavors with architecture '<FLAVOR_ARCH>' diff --git a/server/features/instances.feature b/server/features/instances.feature index 3bd26e3..f11aa21 100644 --- a/server/features/instances.feature +++ b/server/features/instances.feature @@ -55,12 +55,12 @@ Feature: Managing instances Then I could follow image href attribute And this attribute should point me to valid image
- Scenario: I want to get instance flavor + Scenario: I want to get instance hardware profile Given I am authorized to show instance '<INSTANCE_1_ID>' Given I request for '<INSTANCE_1_ID>' instance - When I want to get details about instance flavor - Then I could follow flavor href attribute - And this attribute should point me to valid flavor + When I want to get details about instance hardware profile + Then I could follow hardware profile href attribute + And this attribute should point me to valid hardware profile
Scenario: I want to get instance realm Given I am authorized to show instance '<INSTANCE_1_ID>' diff --git a/server/features/step_definitions/flavors_steps.rb b/server/features/step_definitions/flavors_steps.rb deleted file mode 100644 index d806aaf..0000000 --- a/server/features/step_definitions/flavors_steps.rb +++ /dev/null @@ -1,5 +0,0 @@ -Then /^flavor should have valid href parameter$/ do - href=Nokogiri::XML(last_response.body).xpath('/flavor').first[:href] - href.should == "http://example.org/api/flavors/#%7BCONFIG%5B:flavor_id%5D%7D" -end - diff --git a/server/features/step_definitions/instances_steps.rb b/server/features/step_definitions/instances_steps.rb index fbffd06..c31682b 100644 --- a/server/features/step_definitions/instances_steps.rb +++ b/server/features/step_definitions/instances_steps.rb @@ -24,17 +24,20 @@ end When /^I want to get details about instance (.+)$/ do |model| end
-Then /^I could follow (image|realm|flavor) href attribute$/ do |model| +Then /^I could follow (image|realm|hardware profile) href attribute$/ do |model| + model.tr!(' ', '-') m = Nokogiri::XML(last_response.body).xpath("/instance/#{model}").first model_url = URI.parse(m[:href]).path get model_url, {} last_response.status.should == 200 end
-Then /^this attribute should point me to valid (image|realm|flavor)$/ do |model| - attribute = Nokogiri::XML(last_response.body).xpath("/#{model}").first +Then /^this attribute should point me to valid (image|realm|hardware profile)$/ do |model| + model_tag = model.tr(' ', '-') + model.tr!(' ', '_') + attribute = Nokogiri::XML(last_response.body).xpath("/#{model_tag}").first attribute.should_not == nil - attribute.name.should == model + attribute.name.should == model_tag end
When /^I want to (.+) this instance$/ do |action| diff --git a/server/features/support/ec2/config.yaml b/server/features/support/ec2/config.yaml index f2e9fe0..6295e42 100644 --- a/server/features/support/ec2/config.yaml +++ b/server/features/support/ec2/config.yaml @@ -1,10 +1,10 @@ ---- +--- :username: mockuser :password: mockpassword :storage_snapshot_id: snap-72a5401b :driver_name: ec2 :instances_count: 2 -:flavor_arch: x86_64 +:hardware_profile_arch: x86_64 :storage_snapshot_state: AVAILABLE :realm_id: us-east-1a :instance_1_name: ami-e4b6538d @@ -19,8 +19,8 @@ :instance_image_id: ami-e4b6538d :image_id: ami-e4b6538d :storage_volume_state: AVAILABLE -:flavor_id: m1-small +:hardware_profile_id: m1-small :instance_realm: us-east-1a :image_count: 2 :storage_volume_count: 2 -:flavor_count: 5 +:hardware_profile_count: 5 diff --git a/server/features/support/mock/config.yaml b/server/features/support/mock/config.yaml index 4ec0d03..6f282ff 100644 --- a/server/features/support/mock/config.yaml +++ b/server/features/support/mock/config.yaml @@ -4,7 +4,6 @@ :storage_snapshot_id: snap2 :driver_name: mock :instances_count: 180 -:flavor_arch: x86_64 :storage_snapshot_state: AVAILABLE :realm_id: us :instance_1_name: 1268827277 testing instance @@ -18,11 +17,9 @@ :instance_image_id: img2 :image_id: img2 :storage_volume_state: AVAILABLE -:flavor_id: m1-small :hardware_profile_id: m1-large :instance_realm: us :image_count: 3 :storage_volume_count: 2 -:flavor_count: 5 :hardware_profile_count: 4 :hardware_profile_arch: x86_64 diff --git a/server/lib/converters/xml_converter.rb b/server/lib/converters/xml_converter.rb index 4b8e78e..82a499d 100644 --- a/server/lib/converters/xml_converter.rb +++ b/server/lib/converters/xml_converter.rb @@ -49,13 +49,6 @@ module Converters end else case ( obj ) - when Flavor - builder.flavor( :href=>@link_builder.send( :flavor_url, obj.id ) ) { - builder.id( obj.id ) - builder.architecture( obj.architecture ) - builder.memory( obj.memory ) - builder.storage( obj.storage ) - } when Image builder.image( :href=>@link_builder.send( :image_url, obj.id ) ) { builder.id( obj.id ) @@ -81,7 +74,6 @@ module Converters builder.name( obj.name ) builder.owner_id( obj.owner_id ) builder.image( :href=>@link_builder.send( :image_url, obj.image_id ) ) - builder.flavor( :href=>@link_builder.send( :flavor_url, obj.flavor_id ) ) builder.__send__( 'hardware-profile', :href=>@link_builder.send( :hardware_profile_url, obj.instance_profile.name) ) do builder.id( obj.instance_profile.name ) obj.instance_profile.overrides.each do |p, v| diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb index a3069e9..a2b3bbe 100644 --- a/server/lib/deltacloud/base_driver/base_driver.rb +++ b/server/lib/deltacloud/base_driver/base_driver.rb @@ -115,20 +115,6 @@ module Deltacloud actions end
- def flavor(credentials, opts) - flavors = flavors(credentials, opts) - return flavors.first unless flavors.empty? - nil - end - - def flavors(credentials, ops) - [] - end - - def flavors_by_architecture(credentials, architecture) - flavors(credentials, :architecture => architecture) - end - def realm(credentials, opts) realms = realms(credentials, opts) return realms.first unless realms.empty? diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb index 11cd41b..155bc8a 100644 --- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb +++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb @@ -24,42 +24,6 @@ module Deltacloud module EC2 class EC2Driver < Deltacloud::BaseDriver
- # - # Flavors - # - FLAVORS = [ - Flavor.new( { - :id=>'m1-small', - :memory=>1.7, - :storage=>160, - :architecture=>'i386', - } ), - Flavor.new( { - :id=>'m1-large', - :memory=>7.5, - :storage=>850, - :architecture=>'x86_64', - } ), - Flavor.new( { - :id=>'m1-xlarge', - :memory=>15, - :storage=>1690, - :architecture=>'x86_64', - } ), - Flavor.new( { - :id=>'c1-medium', - :memory=>1.7, - :storage=>350, - :architecture=>'x86_64', - } ), - Flavor.new( { - :id=>'c1-xlarge', - :memory=>7, - :storage=>1690, - :architecture=>'x86_64', - } ), - ] - feature :instances, :user_data
define_hardware_profile('m1-small') do @@ -123,14 +87,6 @@ class EC2Driver < Deltacloud::BaseDriver stopped.to( :finish ) .automatically end
- def flavors(credentials, opts=nil) - return FLAVORS if ( opts.nil? ) - results = FLAVORS - results = filter_on( results, :id, opts ) - results = filter_on( results, :architecture, opts ) - results - end - # # Images # @@ -322,7 +278,6 @@ class EC2Driver < Deltacloud::BaseDriver :realm_id=>realm_id, :public_addresses=>( ec2_instance[:dns_name] == '' ? [] : [ec2_instance[:dns_name]] ), :private_addresses=>( ec2_instance[:private_dns_name] == '' ? [] : [ec2_instance[:private_dns_name]] ), - :flavor_id=>ec2_instance[:aws_instance_type].gsub( /./, '-'), :instance_profile => InstanceProfile.new(hwp_name), :actions=>instance_actions_for( ec2_instance[:aws_state].upcase ), } ) diff --git a/server/lib/deltacloud/drivers/mock/data/instances/inst0.yml b/server/lib/deltacloud/drivers/mock/data/instances/inst0.yml index c1880b7..a5b73be 100644 --- a/server/lib/deltacloud/drivers/mock/data/instances/inst0.yml +++ b/server/lib/deltacloud/drivers/mock/data/instances/inst0.yml @@ -7,7 +7,6 @@ :private_addresses: - img1.inst0.private.com :image_id: img1 -:flavor_id: m1-large :instance_profile: !ruby/object:InstanceProfile id: m1-large memory: "12288" diff --git a/server/lib/deltacloud/drivers/mock/data/instances/inst1.yml b/server/lib/deltacloud/drivers/mock/data/instances/inst1.yml index aa3f86e..3544d40 100644 --- a/server/lib/deltacloud/drivers/mock/data/instances/inst1.yml +++ b/server/lib/deltacloud/drivers/mock/data/instances/inst1.yml @@ -4,7 +4,6 @@ :owner_id: mockuser :public_addresses: [ img3.inst1.public.com ] :private_addresses: [ img3.inst1.private.com ] -:flavor_id: m1-small :realm_id: us :instance_profile: !ruby/object:InstanceProfile id: m1-small diff --git a/server/lib/deltacloud/drivers/mock/data/instances/inst2.yml b/server/lib/deltacloud/drivers/mock/data/instances/inst2.yml index 69d4720..9a70cb8 100644 --- a/server/lib/deltacloud/drivers/mock/data/instances/inst2.yml +++ b/server/lib/deltacloud/drivers/mock/data/instances/inst2.yml @@ -4,7 +4,6 @@ :owner_id: anotheruser :public_addresses: [ img1.inst2.public.com ] :private_addresses: [ img1.inst2.private.com ] -:flavor_id: m1-small :realm_id: us :instance_profile: !ruby/object:InstanceProfile id: m1-large diff --git a/server/lib/deltacloud/drivers/mock/mock_driver.rb b/server/lib/deltacloud/drivers/mock/mock_driver.rb index 8841e3f..9671f02 100644 --- a/server/lib/deltacloud/drivers/mock/mock_driver.rb +++ b/server/lib/deltacloud/drivers/mock/mock_driver.rb @@ -23,43 +23,6 @@ module Deltacloud module Mock class MockDriver < Deltacloud::BaseDriver
- # - # Flavors - # - - ( FLAVORS = [ - Flavor.new({ - :id=>'m1-small', - :memory=>1.7, - :storage=>160, - :architecture=>'i386', - }), - Flavor.new({ - :id=>'m1-large', - :memory=>7.5, - :storage=>850, - :architecture=>'x86_64', - }), - Flavor.new({ - :id=>'m1-xlarge', - :memory=>15, - :storage=>1690, - :architecture=>'x86_64', - }), - Flavor.new({ - :id=>'c1-medium', - :memory=>1.7, - :storage=>350, - :architecture=>'x86_64', - }), - Flavor.new({ - :id=>'c1-xlarge', - :memory=>7, - :storage=>1690, - :architecture=>'x86_64', - }), - ] ) unless defined?( FLAVORS ) - ( REALMS = [ Realm.new({ :id=>'us', @@ -129,14 +92,6 @@ class MockDriver < Deltacloud::BaseDriver end end
- def flavors(credentials, opts=nil) - return FLAVORS if ( opts.nil? ) - results = FLAVORS - results = filter_on( results, :id, opts ) - results = filter_on( results, :architecture, opts ) - results - end - def realms(credentials, opts=nil) return REALMS if ( opts.nil? ) results = REALMS @@ -217,7 +172,6 @@ class MockDriver < Deltacloud::BaseDriver :owner_id=>credentials.user, :public_addresses=>["#{image_id}.#{next_id}.public.com"], :private_addresses=>["#{image_id}.#{next_id}.private.com"], - :flavor_id=>hwp.name, :instance_profile => InstanceProfile.new(hwp.name, opts), :realm_id=>realm_id, :actions=>instance_actions_for( 'RUNNING' ) diff --git a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb index 85efeec..c5e1408 100644 --- a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +++ b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb @@ -34,44 +34,15 @@ module Deltacloud class OpennebulaDriver < Deltacloud::BaseDriver
###################################################################### - # Flavors + # Hardware profiles ######################################################################
- (FLAVORS = [ - Flavor.new( { - :id=>'small', - :memory=>'small-memory', - :storage=>'small-storage', - :architecture=>'Any architecture', - } ), - Flavor.new( { - :id=>'medium', - :memory=>'medium-memory', - :storage=>'medium-storage', - :architecture=>'Any architecture', - } ), - Flavor.new( { - :id=>'large', - :memory=>'large-memory', - :storage=>'large-storage', - :architecture=>'Any architecture', - } ), - ] ) unless defined?( FLAVORS ) - define_hardware_profile 'small'
define_hardware_profile 'medium'
define_hardware_profile 'large'
- def flavors(credentials, opts=nil) - return FLAVORS if ( opts.nil? ) - results = FLAVORS - results = filter_on( results, :id, opts ) - results - end - - ###################################################################### # Realms ###################################################################### @@ -223,8 +194,7 @@ class OpennebulaDriver < Deltacloud::BaseDriver :owner_id=>credentials.user, :name=>computehash['NAME'].text, :image_id=>imageid, - :flavor_id=>flavor, - :instance_profile=>InstanceProfile.new(hwp_name), + :instance_profile=>InstanceProfile.new(hwp_name), :realm_id=>'Any realm', :state=>state, :public_addreses=>networks, diff --git a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb index 2b7aa44..3ad5452 100644 --- a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +++ b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb @@ -26,21 +26,6 @@ class RackspaceDriver < Deltacloud::BaseDriver
feature :instances, :user_name
- def flavors(credentials, opts=nil) - racks = new_client( credentials ) - results = racks.list_flavors.map do |flav| - Flavor.new( { - :id=> flav["id"].to_s, - :memory=>flav["ram"].to_f/1024, - :storage=>flav["disk"].to_i, - :architecture=>'x86_64' - } ) - end - results = filter_on( results, :id, opts ) - results = filter_on( results, :architecture, opts ) - results - end - def hardware_profiles(credentials, opts = nil) racks = new_client( credentials ) results = racks.list_flavors.map do |flav| @@ -133,7 +118,6 @@ class RackspaceDriver < Deltacloud::BaseDriver inst.state = srv["status"] == "ACTIVE" ? "RUNNING" : "PENDING" inst.actions = instance_actions_for(inst.state) inst.image_id = srv["imageId"].to_s - inst.flavor_id = srv["flavorId"].to_s inst.instance_profile = InstanceProfile.new(srv["flavorId"].to_s) if srv["addresses"] inst.public_addresses = srv["addresses"]["public"] diff --git a/server/lib/deltacloud/drivers/rhevm/rhevm_driver.rb b/server/lib/deltacloud/drivers/rhevm/rhevm_driver.rb index fb6aee8..f22ce34 100644 --- a/server/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +++ b/server/lib/deltacloud/drivers/rhevm/rhevm_driver.rb @@ -83,23 +83,7 @@ class RHEVMDriver < Deltacloud::BaseDriver st end
- # - # Flavors - # - FLAVORS = [ - Flavor.new({ - :id=>"rhevm", - :memory=>"Any Memory", - :storage=>"Any Storage", - :architecture=>"Any Architecture", - }) - ] - - def flavors(credentials, opts=nil) - return FLAVORS if ( opts.nil? || (! opts[:id])) - FLAVORS.select{|f| opts[:id] == f.id} - end - + define_hardware_profile 'rhevm'
# # Realms @@ -207,7 +191,7 @@ class RHEVMDriver < Deltacloud::BaseDriver :owner_id => NO_OWNER, :image_id => vm["TemplateId"], :state => statify(vm["Status"]), - :flavor_id => "rhevm", + :instance_profile => InstanceProfile.new("rhevm"), :actions => instance_actions_for(statify(vm["Status"])), }) end diff --git a/server/lib/deltacloud/drivers/rimu/rimu_hosting_driver.rb b/server/lib/deltacloud/drivers/rimu/rimu_hosting_driver.rb index eb00232..d0ffac9 100755 --- a/server/lib/deltacloud/drivers/rimu/rimu_hosting_driver.rb +++ b/server/lib/deltacloud/drivers/rimu/rimu_hosting_driver.rb @@ -43,20 +43,6 @@ class RimuHostingDriver < Deltacloud::BaseDriver images end
- def flavors(credentials, opts=nil) - rh = RimuHostingClient.new(credentials) - flavors = rh.list_plans.map do | flavor | - Flavor.new({ - :id => flavor["pricing_plan_code"], - :memory => flavor["minimum_memory_mb"].to_f/1024, - :storage => flavor["minimum_disk_gb"].to_i, - :architecture => "x86" - }) - end - flavors = filter_on( flavors, :id, opts) - flavors - end - def hardware_profiles(credentials, opts = nil) rh = RimuHostingClient.new(credentials) results = rh.list_plans.map do |plan| @@ -131,7 +117,7 @@ class RimuHostingDriver < Deltacloud::BaseDriver :name => inst["domain_name"], :realm_id => "RH", :owner_id => "root", - :flavor_id => "none", + :instance_profile => InstanceProfile.new("none"), :actions => instance_actions_for("RUNNING") }) end diff --git a/server/lib/deltacloud/helpers/conversion_helper.rb b/server/lib/deltacloud/helpers/conversion_helper.rb index 6ba85d5..d50f1e6 100644 --- a/server/lib/deltacloud/helpers/conversion_helper.rb +++ b/server/lib/deltacloud/helpers/conversion_helper.rb @@ -21,7 +21,7 @@ load 'converters/xml_converter.rb' module ConversionHelper
def convert_to_xml(type, obj) - if ( [ :flavor, :account, :image, :realm, :instance, :storage_volume, :storage_snapshot ].include?( type ) ) + if ( [ :account, :image, :realm, :instance, :storage_volume, :storage_snapshot ].include?( type ) ) Converters::XMLConverter.new( self, type ).convert(obj) end end diff --git a/server/lib/deltacloud/helpers/driver_helper.rb b/server/lib/deltacloud/helpers/driver_helper.rb index 352253f..9e36468 100644 --- a/server/lib/deltacloud/helpers/driver_helper.rb +++ b/server/lib/deltacloud/helpers/driver_helper.rb @@ -22,7 +22,7 @@ require 'converters/xml_converter' module DriverHelper
def convert_to_xml(type, obj) - if ( [ :flavor, :account, :image, :realm, :instance, :storage_volume, :storage_snapshot ].include?( type ) ) + if ( [ :account, :image, :realm, :instance, :storage_volume, :storage_snapshot ].include?( type ) ) Converters::XMLConverter.new( self, type ).convert(obj) end end diff --git a/server/lib/deltacloud/models/flavor.rb b/server/lib/deltacloud/models/flavor.rb deleted file mode 100644 index a096978..0000000 --- a/server/lib/deltacloud/models/flavor.rb +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -class Flavor < BaseModel - - attr_accessor :architecture - attr_accessor :memory - attr_accessor :storage - -end diff --git a/server/lib/deltacloud/models/instance.rb b/server/lib/deltacloud/models/instance.rb index e5c1f9c..d313bc7 100644 --- a/server/lib/deltacloud/models/instance.rb +++ b/server/lib/deltacloud/models/instance.rb @@ -20,7 +20,6 @@ class Instance < BaseModel
attr_accessor :owner_id attr_accessor :image_id - attr_accessor :flavor_id attr_accessor :name attr_accessor :realm_id attr_accessor :state diff --git a/server/lib/sinatra/rabbit.rb b/server/lib/sinatra/rabbit.rb index 7091937..6018810 100644 --- a/server/lib/sinatra/rabbit.rb +++ b/server/lib/sinatra/rabbit.rb @@ -153,7 +153,7 @@ module Sinatra # operation is the element URL with the name of the operation # appended # - # This also defines a helper method like show_flavor_url that returns + # This also defines a helper method like show_instance_url that returns # the URL to this operation (in request context) def operation(name, opts = {}, &block) raise DuplicateOperationException if @operations[name] diff --git a/server/server.rb b/server/server.rb index 5c1bd73..43008c8 100644 --- a/server/server.rb +++ b/server/server.rb @@ -99,24 +99,6 @@ end
# Rabbit DSL
-collection :flavors do - description "Within a cloud provider a flavor represents a configuration of resources upon which a machine may be deployed. A flavor defines aspects such as local disk storage, available RAM, and architecture. A future revision of the Deltacloud API will include more aspects, including number and speed of CPUs available. Each provider is free to define as many (or as few) flavors as desired." - - operation :index do - description 'Operation will list all available flavors. For specific architecture use "architecture" parameter.' - param :architecture, :string, :optional, [ 'i386', 'x86_64' ] - param :id, :string, :optional - control { filter_all(:flavors) } - end - - operation :show do - description 'Show an flavor identified by "id" parameter.' - param :id, :string, :required - control { show(:flavor) } - end - -end - collection :realms do description "Within a cloud provider a realm represents a boundary containing resources. The exact definition of a realm is left to the cloud provider. In some cases, a realm may represent different datacenters, different continents, or different pools of resources within a single datacenter. A cloud provider may insist that resources must all exist within a single realm in order to cooperate. For instance, storage volumes may only be allowed to be mounted to instances within the same realm."
@@ -202,7 +184,6 @@ end get "/api/instances/new" do @instance = Instance.new( { :id=>params[:id], :image_id=>params[:image_id] } ) @image = driver.image( credentials, :id => params[:image_id] ) - @flavors = driver.flavors( credentials, { :architecture=>@image.architecture } ) @hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture ) @realms = driver.realms(credentials) respond_to do |format| @@ -238,13 +219,8 @@ collection :instances do description "Create a new instance" param :image_id, :string, :required param :realm_id, :string, :optional - param :flavor_id, :string, :optional param :hwp_id, :string, :optional control do - # FIXME: Strictly speaking, we'd need to check that only either - # hwp_id or flavor_id are set, but not both. Since flavors will go - # away shortly, we can be a little sloppy - params[:hwp_id] = params[:flavor_id] if params[:flavor_id] @image = driver.image(credentials, :id => params[:image_id]) instance = driver.create_instance(credentials, @image.id, params) respond_to do |format| diff --git a/server/tests/deltacloud_test.rb b/server/tests/deltacloud_test.rb index 2b5cf6c..bf046bf 100644 --- a/server/tests/deltacloud_test.rb +++ b/server/tests/deltacloud_test.rb @@ -26,7 +26,7 @@ module DeltacloudTest end
def test_if_http_status_is_correct_with_wrong_credentials - return if ['flavors', 'realms'].include?(@collection) + return if ['realms'].include?(@collection) wrong_header = rack_headers wrong_header['HTTP_AUTHORIZATION'] = authorization('wronguser', 'wrongpassword') get '/api/'+@collection+'.xml', @params, wrong_header diff --git a/server/tests/flavors_test.rb b/server/tests/flavors_test.rb deleted file mode 100644 index c920b63..0000000 --- a/server/tests/flavors_test.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'tests/deltacloud_test' - -class FlavorsTest < Test::Unit::TestCase - - def initialize(*args) - @collection = 'flavors' - @operations = [:index, :show] - @options = [:id, :architecture, :memory, :storage] - @params = {} - super(*args) - end - - def test_if_flavors_are_not_empty - get '/api/flavors.xml', @params, rack_headers - doc = Nokogiri::XML.parse(last_response.body) - assert_not_equal 0, doc.xpath('/flavors/flavor').size - end - - [:id, :architecture, :memory, :storage].each do |option| - method_name = :"test_if_flavors_index_contain_#{option}" - send :define_method, method_name do - get '/api/flavors.xml', @params, rack_headers - doc = Nokogiri::XML.parse(last_response.body) - flavor = doc.xpath('/flavors/flavor[1]').first - assert_not_nil flavor.xpath(option.to_s).first - end - end - - [:id, :architecture, :memory, :storage].each do |option| - method_name = :"test_if_flavor_show_contain_#{option}" - send :define_method, method_name do - get '/api/flavors/m1-small.xml', @params, rack_headers - doc = Nokogiri::XML.parse(last_response.body) - flavor = doc.xpath('/flavor').first - assert_not_nil flavor.xpath(option.to_s).first - end - end - - def test_flavors_filtering_by_architecture - @params[:architecture] = 'i386' - get '/api/flavors.xml', @params, rack_headers - doc = Nokogiri::XML.parse(last_response.body) - assert_equal 1, doc.xpath('/flavors/flavor').size - assert_equal @params[:architecture], doc.xpath('/flavors/flavor/architecture').first.text - end - - def test_flavors_filtering_by_id - get '/api/flavors.xml', { :id => 'm1-small'}, rack_headers - doc = Nokogiri::XML.parse(last_response.body) - assert_equal 1, doc.xpath('/flavors/flavor').size - assert_equal 'm1-small', doc.xpath('/flavors/flavor/id').first.text - end - - def test_flavors_filtering_by_id_and_architecture - get '/api/flavors.xml', { :architecture => 'x86_64', :id => 'c1-xlarge'}, rack_headers - doc = Nokogiri::XML.parse(last_response.body) - assert_equal 1, doc.xpath('/flavors/flavor').size - assert_equal 'c1-xlarge', doc.xpath('/flavors/flavor/id').first.text - assert_equal 'x86_64', doc.xpath('/flavors/flavor/architecture').first.text - end - - include DeltacloudTest - -end - diff --git a/server/tests/images_test.rb b/server/tests/images_test.rb index 507c81b..177852d 100644 --- a/server/tests/images_test.rb +++ b/server/tests/images_test.rb @@ -20,8 +20,8 @@ class ImagesTest < Test::Unit::TestCase send :define_method, method_name do get '/api/images.xml', @params, rack_headers doc = Nokogiri::XML.parse(last_response.body) - flavor = doc.xpath('/images/image[1]').first - assert_not_nil flavor.xpath(option.to_s).first + elt = doc.xpath('/images/image[1]').first + assert_not_nil elt.xpath(option.to_s).first end end
@@ -30,8 +30,8 @@ class ImagesTest < Test::Unit::TestCase send :define_method, method_name do get '/api/images/img1.xml', @params, rack_headers doc = Nokogiri::XML.parse(last_response.body) - flavor = doc.xpath('/image').first - assert_not_nil flavor.xpath(option.to_s).first + elt = doc.xpath('/image').first + assert_not_nil elt.xpath(option.to_s).first end end
diff --git a/server/tests/instances_test.rb b/server/tests/instances_test.rb index 9504cc0..8dcf8c9 100644 --- a/server/tests/instances_test.rb +++ b/server/tests/instances_test.rb @@ -19,7 +19,7 @@ class InstancesTest < Test::Unit::TestCase assert_not_equal 0, doc.xpath('/instances/instance').size end
- [:id, :name, :owner_id, :image, :flavor, :realm, :state, :actions, :'public-addresses', :'private-addresses'].each do |option| + [:id, :name, :owner_id, :image, :realm, :state, :actions, :'public-addresses', :'private-addresses'].each do |option| method_name = :"test_if_instances_index_contain_#{option}" send :define_method, method_name do get '/api/instances.xml', @params, rack_headers @@ -29,7 +29,7 @@ class InstancesTest < Test::Unit::TestCase end end
- [:id, :name, :owner_id, :image, :flavor, :realm, :state, :actions, :'public-addresses', :'private-addresses'].each do |option| + [:id, :name, :owner_id, :image, :realm, :state, :actions, :'public-addresses', :'private-addresses'].each do |option| method_name = :"test_if_instance_show_contain_#{option}" send :define_method, method_name do get '/api/instances/inst1.xml', @params, rack_headers @@ -77,19 +77,19 @@ class InstancesTest < Test::Unit::TestCase assert_equal @params[:image_id], image_id end
- def test_create_instance_with_flavor_id + def test_create_instance_with_hwp_id
@params = { :name => '_test-instance', :image_id => 'img1', - :flavor_id => 'm1-xlarge' + :hwp_id => 'm1-xlarge' }
post '/api/instances.xml', @params, rack_headers doc = Nokogiri::XML.parse(last_response.body) - flavor_href = doc.xpath('/instance/flavor').first[:href].to_s - flavor_id = flavor_href.gsub(/.*/([\w-]+)$/, '\1') - assert_equal @params[:flavor_id], flavor_id + hwp_href = doc.xpath('/instance/hardware_profile').first[:href].to_s + hwp_id = hwp_href.gsub(/.*/([\w-]+)$/, '\1') + assert_equal @params[:hwp_id], hwp_id end
def test_create_instance_with_realm_id diff --git a/server/tests/realms_test.rb b/server/tests/realms_test.rb index 4048ac6..b6053b3 100644 --- a/server/tests/realms_test.rb +++ b/server/tests/realms_test.rb @@ -21,8 +21,8 @@ class RealmsTest < Test::Unit::TestCase send :define_method, method_name do get '/api/realms.xml', @params, rack_headers doc = Nokogiri::XML.parse(last_response.body) - flavor = doc.xpath('/realms/realm[1]').first - assert_not_nil flavor.xpath(option.to_s).first + realm = doc.xpath('/realms/realm[1]').first + assert_not_nil realm.xpath(option.to_s).first end end
@@ -31,8 +31,8 @@ class RealmsTest < Test::Unit::TestCase send :define_method, method_name do get '/api/realms/us.xml', @params, rack_headers doc = Nokogiri::XML.parse(last_response.body) - flavor = doc.xpath('/realm').first - assert_not_nil flavor.xpath(option.to_s).first + realm = doc.xpath('/realm').first + assert_not_nil realm.xpath(option.to_s).first end end
diff --git a/server/views/flavors/index.html.haml b/server/views/flavors/index.html.haml deleted file mode 100644 index 2b44bb7..0000000 --- a/server/views/flavors/index.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -%h1 - Flavors - -%table.display - %thead - %tr - %th ID - %th Architecture - %th Memory - %th Storage - %tbody - - @flavors.each do |flavor| - %tr - %td - = link_to flavor.id, flavor_url( flavor.id ) - %td - = flavor.architecture - %td - = "#{flavor.memory} GB" - %td - = "#{flavor.storage} GB" diff --git a/server/views/flavors/show.html.haml b/server/views/flavors/show.html.haml deleted file mode 100644 index d81e75b..0000000 --- a/server/views/flavors/show.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -%h1 - = @flavor.id - -%dl - %di - %dt Architecture - %dd - = @flavor.architecture - %di - %dt Memory - %dd - = "#{@flavor.memory} GB" - %di - %dt Storage - %dd - = "#{@flavor.storage} GB" diff --git a/server/views/instances/index.html.haml b/server/views/instances/index.html.haml index 20feb7c..6f3996d 100644 --- a/server/views/instances/index.html.haml +++ b/server/views/instances/index.html.haml @@ -7,7 +7,6 @@ %th Owner %th Name %th Image - %th Flavor %th Realm %th State %th Actions @@ -23,8 +22,6 @@ %td = link_to instance.image_id, image_url( instance.image_id ) %td - = instance.flavor_id ? link_to(instance.flavor_id, flavor_url(instance.flavor_id)) : 'default' - %td = instance.realm_id ? link_to(instance.realm_id, realm_url( instance.realm_id )) : 'default' %td = instance.state diff --git a/server/views/instances/show.html.haml b/server/views/instances/show.html.haml index 2a81c12..dabcc61 100644 --- a/server/views/instances/show.html.haml +++ b/server/views/instances/show.html.haml @@ -15,10 +15,6 @@ %dd = @instance.name %di - %dt Flavor - %dd - = @instance.flavor_id ? link_to(@instance.flavor_id, flavor_url(@instance.flavor_id)) : 'default' - %di %dt Hardware Profile %dd - prof = @instance.instance_profile
On 09/04/10 16:59 -0700, lutter@redhat.com wrote:
XML Representation
The XML representation for hardware profiles looks like
<hardware-profile href="http://localhost:3001/api/hardware_profiles/m1-large">
Why we use both hardware-profile and hardware_profile ? This must be handled by extra code in client/aggregator. For me, using hardware_profile also in URL is fine (same for storage(-_)snapshots/volumes).
<id>m1-large</id>
<property kind="range" name="memory" unit="MB" value="10240"> <param href="http://localhost:3001/api/instances" method="post" name="hwp_memory" operation="create"/> <range first="7680.0" last="15360"/> </property> <property kind="enum" name="storage" unit="GB" value="850"> <param href="http://localhost:3001/api/instances" method="post" name="hwp_storage" operation="create"/> <enum> <entry value="850"/> <entry value="1024"/> </enum> </property> <property kind="fixed" name="architecture" unit="label" value="x86_64"/> <property kind="fixed" name="cpu" unit="count" value="2"/> </hardware-profile>
That is: a hardware profile has a URL (the href attribute), an <id> child giving its name, and a list of <property> children. A property can be one of three kinds; all properties have a name, a (default) value, and a unit. We will try and not gratuitously change units, i.e. for now memory is always reported in MB. The three kinds of properties are
Looks clean for me ;)
- fixed: there's only one possible value for the property, the one in the value attribute
- range: the property can take on any number between the first and last number from the <range> child. The range is inclusive of the first and last value.
- enum: the property can take on one of a fixed set of values. The values are described inside an <enum> child, as the value attribute of the <entry> tags
Properties that can have more than one value, i.e. range and enum properties, also describe what operations are affected by them. Right now, this is only the create instance operation. The <param> child of the <property> element explains what parameter to pass to instance creation to change the value of that property.
Nice. We should consider creating a DTD or XScheme for all XMLs we produce and publish it on website (+ integrate it as part of tests)
Instance creation
When craeting an instance, you now have to pass the parameter hwp_id rather than flavor_id. The hwp_id has to be the <id> of an existing hardware profile. It is not necessary to pass any other hardware profile related parameters, since all proeprties have default values, and those are used for each property if only a hwp_id is given.
Optionally, additional parameters can be passed to instance creation to change some properties from their default value. With the above hardware profile, you could pass hwp_memory=15000 in addition to hwp_id=m1-large to get an instance with almost 15GB of memory.
Perfect, this will work nicely with those providers who allows to choose custom memory/storage values.
Instance representation
The XML for an instance now lists which properties had explicit values set when the instance was created; this is indicated in the XML as
<instance href="http://localhost:3001/api/instances/inst0"> <id>inst0</id> ... <hardware-profile href="http://localhost:3001/api/hardware_profiles/m1-large"> <id>m1-large</id> <property kind="fixed" name="memory" value="15000" unit="MB"/> </hardware-profile> ... </instance>
The 'instance profile' refers to an existing hardware profile by giving its URL and name, and uses <property> tags to show which properties have values differing from the hardware profile. The kind attribute on those tags is always 'fixed'.
Nice. Why isn't 'id' part of element btw? According to this whitepaper (http://www.ibm.com/developerworks/xml/library/x-eleatt.html) 'ID' is perfect candidate for element attribute.
Btw. ACK for all patches. Applied cleanely for me.
- Michal
On Mon, 2010-04-12 at 10:28 +0200, Michal Fojtik wrote:
On 09/04/10 16:59 -0700, lutter@redhat.com wrote:
XML Representation
The XML representation for hardware profiles looks like
<hardware-profile href="http://localhost:3001/api/hardware_profiles/m1-large">
Why we use both hardware-profile and hardware_profile ? This must be handled by extra code in client/aggregator. For me, using hardware_profile also in URL is fine (same for storage(-_)snapshots/volumes).
An attempt at foolish consistency ;) I thought that we use '-' to separate words in most tag names, rather than '_'. But that's not true, and we do use '_' e.g. in <owner_id/>
Based of the discussions with the RHEV-M guys, it might be best if we take another look at all our XML, and replace '-' with '_' for consistency.
I think though we should just do that with another cleanup patch.
Nice. We should consider creating a DTD or XScheme for all XMLs we produce and publish it on website (+ integrate it as part of tests)
Agreed; I am a little hesitant to do that, since writing good schemas is a lot of work, and only worth it if anybody pays attention to them (and in any event, it should be Relax-NG, DTD aren't all that expressive, and XML Schema is inscrutable)
Nice. Why isn't 'id' part of element btw? According to this whitepaper (http://www.ibm.com/developerworks/xml/library/x-eleatt.html) 'ID' is perfect candidate for element attribute.
Another thing that I'd like to see cleaned up, though there's some discussion around using a consistent style over all REST API's we publish (e.g., for RHEV-M, too) But you are right: <id/> should really be an attribute.
David
lutter@redhat.com wrote:
This series of patches adds support for hardware profiles to Deltacloud core. Hardware profiles describe how instances can be sized, and are more general than the old flavors, in that each property can either be a fixed value, a predefined enumeration of values, or a range of values.
To try these patches with the mock driver, you need to stop your server, rm -rf the mock storage root (usually /var/tmp/deltacloud-core-$USER), and start the server again once you have the patches applied.
Even if you don't try these patches, I'd highly appreciate feedback on all this, especially the XML representation of hardware profiles.
Although I haven't tested any of this, I looked over all the patches. I had a couple questions on specific patches, and some inline questions/comments below, but overall this looks good to me. I think I have most of what I need to start implementing this on the aggregator side now.
XML Representation
The XML representation for hardware profiles looks like
<hardware-profile href="http://localhost:3001/api/hardware_profiles/m1-large"> <id>m1-large</id> <property kind="range" name="memory" unit="MB" value="10240"> <param href="http://localhost:3001/api/instances" method="post" name="hwp_memory" operation="create"/> <range first="7680.0" last="15360"/> </property> <property kind="enum" name="storage" unit="GB" value="850"> <param href="http://localhost:3001/api/instances" method="post" name="hwp_storage" operation="create"/> <enum> <entry value="850"/> <entry value="1024"/> </enum> </property> <property kind="fixed" name="architecture" unit="label" value="x86_64"/>
How do architecture values for HWP and image (and instance?) interact. With GoGrid, there is no HWP arch, but other drivers include it. I'm assuming that, if present, we've got to match up arch for HWP, image, instance, etc. Is there anything else to worry about here? Is architecture the only optional parameter? In the 'opaque' case (such as OpenNebula) all params are blank -- are the HWP parameters independently optional, or do we have distinct allowable sets of params (i.e. "specify all", "specify none", "specify all but arch")?
In the aggregator model, with the old flavor model, I created a set of mappings from "front end" (i.e. aggregator-facing) profiles to "back end" profiles, split by architecture. So you might have a front end "small" profile that maps to one HWP for back end account foo, arch x86_64, and another profile for account foo, arch i386, and a similar set for each other back end account. You might have more than one for a given arch and account, since there might not be a clear one-to-one overlap. With architecture as optional, I guess I could keep that same model but allowing arch to be empty for the back end profiles.
More generally, as we've previously discussed, the new HWP model with ranges, etc may be more difficult to handle in the aggregator-to-provider mappings. We still need a way to allow the aggregator user to choose a single hardware profile for an aggregator instance (since we're specifying instances via the deltacloud API), and we need a way for this profile to map to multiple back end accounts (since a single instance start request could run on any number of back end accounts depending on image and realm selection)
<property kind="fixed" name="cpu" unit="count" value="2"/>
</hardware-profile>
That is: a hardware profile has a URL (the href attribute), an <id> child giving its name, and a list of <property> children. A property can be one of three kinds; all properties have a name, a (default) value, and a unit. We will try and not gratuitously change units, i.e. for now memory is always reported in MB. The three kinds of properties are
- fixed: there's only one possible value for the property, the one in the value attribute
- range: the property can take on any number between the first and last number from the <range> child. The range is inclusive of the first and last value.
- enum: the property can take on one of a fixed set of values. The values are described inside an <enum> child, as the value attribute of the <entry> tags
Properties that can have more than one value, i.e. range and enum properties, also describe what operations are affected by them. Right now, this is only the create instance operation. The <param> child of the <property> element explains what parameter to pass to instance creation to change the value of that property.
Instance creation
When craeting an instance, you now have to pass the parameter hwp_id rather than flavor_id. The hwp_id has to be the <id> of an existing hardware profile. It is not necessary to pass any other hardware profile related parameters, since all proeprties have default values, and those are used for each property if only a hwp_id is given.
Optionally, additional parameters can be passed to instance creation to change some properties from their default value. With the above hardware profile, you could pass hwp_memory=15000 in addition to hwp_id=m1-large to get an instance with almost 15GB of memory.
Instance representation
The XML for an instance now lists which properties had explicit values set when the instance was created; this is indicated in the XML as
<instance href="http://localhost:3001/api/instances/inst0"> <id>inst0</id> ... <hardware-profile href="http://localhost:3001/api/hardware_profiles/m1-large"> <id>m1-large</id> <property kind="fixed" name="memory" value="15000" unit="MB"/> </hardware-profile>
From looking at patches, it sounds like the above specifies two things: 1) a reference to the existing HWP m1-large 2) the definition of an internal (non-shared) instance profile object that defines memory but leaves everything else blank.
So I had a couple questions here: - The "effective" HWP for an instance (i.e. its actual resource consumption) is determined by taking the HWP and replacing the default values with any values specified in the instance profile - This is more of an aggregator modeling question, but I've got a few options for modeling this in the aggregator DB: 1) explicit instance profile object with the overridden params and a reference to the HWP in the instance profile. replace HWP ref in instance with an instance profile ref 2) explicit instance profile object as above, but keep the HWP ref in the instance def (alongside the instance profile ref) 3) add individual cpu_override, ram_override, etc params to instance instead of creating a separate instance_profile object.
For the aggregator modeling bit, 1) seems to be the cleanest -- it also provides a convenient place to put the business logic that merges the overrides with the HWP itself, but it requires an extra join to get at the HWP (and requires instance profile objects even when we're not overriding anything).
Scott
...
</instance>
The 'instance profile' refers to an existing hardware profile by giving its URL and name, and uses <property> tags to show which properties have values differing from the hardware profile. The kind attribute on those tags is always 'fixed'.
Drivers
I've tested this pretty extensively, and at least for the EC2, Rackspace, and GoGrid drivers everything seems to work. I don't have a way to test the drivers for RimuHosting, RHEV-M, or OpenNebula. I know that these drivers need work, and would appreciate it if others could help out.
Patch series
The first 3 patches are minor cleanups, not directly related to hardware profiles. Patches 4-7 mutate hardware profiles to what we need, and add XML output for them. Patches 8-24 are concerned with integrating hardware profiles into the existing server and the drivers. Patch 25 removes any support with flavors - if you need to use flavors for a transitional period, that should be possible solely by not applying patch 25. _______________________________________________ deltacloud-devel mailing list deltacloud-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/deltacloud-devel
On Mon, 2010-04-12 at 10:40 -0400, Scott Seago wrote:
lutter@redhat.com wrote:
XML Representation
The XML representation for hardware profiles looks like
<hardware-profile href="http://localhost:3001/api/hardware_profiles/m1-large"> <id>m1-large</id> <property kind="range" name="memory" unit="MB" value="10240"> <param href="http://localhost:3001/api/instances" method="post" name="hwp_memory" operation="create"/> <range first="7680.0" last="15360"/> </property> <property kind="enum" name="storage" unit="GB" value="850"> <param href="http://localhost:3001/api/instances" method="post" name="hwp_storage" operation="create"/> <enum> <entry value="850"/> <entry value="1024"/> </enum> </property> <property kind="fixed" name="architecture" unit="label" value="x86_64"/>
How do architecture values for HWP and image (and instance?) interact. With GoGrid, there is no HWP arch, but other drivers include it. I'm assuming that, if present, we've got to match up arch for HWP, image, instance, etc.
That's a good point. I agree that the client should have to match the arch on HWP with the arch on the image. In that case, the GoGrid driver should report arch ['i386', 'x86_64'] for memory sizes <= 4GB, and arch 'x86_64' for the ones above 4GB.
Is there anything else to worry about here? Is architecture the only optional parameter? In the 'opaque' case (such as OpenNebula) all params are blank -- are the HWP parameters independently optional, or do we have distinct allowable sets of params (i.e. "specify all", "specify none", "specify all but arch")?
I look at the HWP as a best-effort attempt to report as much as possible about VM's that can be created. So, the properties are all independently optional, particularly since some backends (like OpenNebula) tell us nothing about any hardware properties.
In practice, it's maybe not too much of a stretch to assume that arch is either i386 or x86_64 (until IBM starts selling zSeries slices in a cloud ;) I could change things that rather than not reporting any arch, I'd report the set of those two.
In the aggregator model, with the old flavor model, I created a set of mappings from "front end" (i.e. aggregator-facing) profiles to "back end" profiles, split by architecture. So you might have a front end "small" profile that maps to one HWP for back end account foo, arch x86_64, and another profile for account foo, arch i386, and a similar set for each other back end account. You might have more than one for a given arch and account, since there might not be a clear one-to-one overlap. With architecture as optional, I guess I could keep that same model but allowing arch to be empty for the back end profiles.
Unfortunately, I don't think there's anything more elegant we can do automatically. If you have two backend accounts A and B, and HWP A1, A2, .., An for A and B1, B2, ..., Bm for B, a pool containing A and B should report all those HWP, plus all HWP Ai intersected with Bj for all i,j. In theory, you could allow user-defined HWP that cover more than the above, though you'll get funky placement behavior, i.e. depending on what concrete values you choose, your instance may only be able to run in A or B (or neither).
In practice, that will become pretty unwieldy, and the best way out might be to ask the user what HWP they want to use, with some guidance on how the HWP they manufacture will affect VM placement.
Instance representation
The XML for an instance now lists which properties had explicit values set when the instance was created; this is indicated in the XML as
<instance href="http://localhost:3001/api/instances/inst0"> <id>inst0</id> ... <hardware-profile href="http://localhost:3001/api/hardware_profiles/m1-large"> <id>m1-large</id> <property kind="fixed" name="memory" value="15000" unit="MB"/> </hardware-profile>
From looking at patches, it sounds like the above specifies two things:
- a reference to the existing HWP m1-large
- the definition of an internal (non-shared) instance profile object
that defines memory but leaves everything else blank.
Yes, the above means 'the instance is sized according to HWP m1-large, but has memory set to 15000MB'; if there are other non-fixed properties in m1-large, we are using the default value for them.
So I had a couple questions here: - The "effective" HWP for an instance (i.e. its actual resource consumption) is determined by taking the HWP and replacing the default values with any values specified in the instance profile
Yes.
- This is more of an aggregator modeling question, but I've got a
few options for modeling this in the aggregator DB: 1) explicit instance profile object with the overridden params and a reference to the HWP in the instance profile. replace HWP ref in instance with an instance profile ref
That's how I did it in my patches.
2) explicit instance profile object as above, but keep the HWP
ref in the instance def (alongside the instance profile ref) 3) add individual cpu_override, ram_override, etc params to instance instead of creating a separate instance_profile object.
For the aggregator modeling bit, 1) seems to be the cleanest -- it also provides a convenient place to put the business logic that merges the overrides with the HWP itself, but it requires an extra join to get at the HWP (and requires instance profile objects even when we're not overriding anything).
True, though I agree that is still the cleanest way to model this.
David
lutter@redhat.com wrote:
Patch series
The first 3 patches are minor cleanups, not directly related to hardware profiles. Patches 4-7 mutate hardware profiles to what we need, and add XML output for them. Patches 8-24 are concerned with integrating hardware profiles into the existing server and the drivers. Patch 25 removes any support with flavors - if you need to use flavors for a transitional period, that should be possible solely by not applying patch 25.
Another reply here.
After you get an ACK I think you should only push oatches 1-24. Hold off on patch 25 until after the aggregator updates to use HWP are pushed.
Scott
deltacloud-devel mailing list deltacloud-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/deltacloud-devel
On Mon, 2010-04-12 at 11:24 -0400, Scott Seago wrote:
lutter@redhat.com wrote:
Patch series
The first 3 patches are minor cleanups, not directly related to hardware profiles. Patches 4-7 mutate hardware profiles to what we need, and add XML output for them. Patches 8-24 are concerned with integrating hardware profiles into the existing server and the drivers. Patch 25 removes any support with flavors - if you need to use flavors for a transitional period, that should be possible solely by not applying patch 25.
Another reply here.
After you get an ACK I think you should only push oatches 1-24. Hold off on patch 25 until after the aggregator updates to use HWP are pushed.
Yes, that's why I structured it that way. Hopefully, switching the portal won't take all that long.
David
David Lutterkort wrote:
On Mon, 2010-04-12 at 11:24 -0400, Scott Seago wrote:
lutter@redhat.com wrote:
Patch series
The first 3 patches are minor cleanups, not directly related to hardware profiles. Patches 4-7 mutate hardware profiles to what we need, and add XML output for them. Patches 8-24 are concerned with integrating hardware profiles into the existing server and the drivers. Patch 25 removes any support with flavors - if you need to use flavors for a transitional period, that should be possible solely by not applying patch 25.
Another reply here.
After you get an ACK I think you should only push oatches 1-24. Hold off on patch 25 until after the aggregator updates to use HWP are pushed.
Yes, that's why I structured it that way. Hopefully, switching the portal won't take all that long.
David
It should be a relatively quick change -- especially since the complicated mapping questions aren't really required to be answered in the initial patch. All I really need to do is update the model itself and update the taskomatic-oriented bits that still use flavor.
Scott
On Fri, 2010-04-09 at 16:59 -0700, lutter@redhat.com wrote:
This series of patches adds support for hardware profiles to Deltacloud core. Hardware profiles describe how instances can be sized, and are more general than the old flavors, in that each property can either be a fixed value, a predefined enumeration of values, or a range of values.
To make it a little easier to test these patches, I pushed them as branch dev/hardware-profiles to git://gitorious.org/deltacloud-sinatra/deltacloud-sinatra.git
You can browse the repo at http://gitorious.org/deltacloud-sinatra/deltacloud-sinatra
David
On 12/04/10 09:44 -0700, David Lutterkort wrote:
On Fri, 2010-04-09 at 16:59 -0700, lutter@redhat.com wrote:
This series of patches adds support for hardware profiles to Deltacloud core. Hardware profiles describe how instances can be sized, and are more general than the old flavors, in that each property can either be a fixed value, a predefined enumeration of values, or a range of values.
To make it a little easier to test these patches, I pushed them as branch dev/hardware-profiles to git://gitorious.org/deltacloud-sinatra/deltacloud-sinatra.git
You can browse the repo at http://gitorious.org/deltacloud-sinatra/deltacloud-sinatra
Perfect, this is working for me, but still getting one small exception: When calling /api/instances/inst1
/usr/bin/thin:19NoMethodError: undefined method `name' for nil:NilClass /home/mfojtik/code/deltacloud-sinatra/server/views/instances/show.html.haml:21:in `render'
All other things are working as expected.
- Michal
On Tue, 2010-04-13 at 12:37 +0200, Michal Fojtik wrote:
On 12/04/10 09:44 -0700, David Lutterkort wrote:
On Fri, 2010-04-09 at 16:59 -0700, lutter@redhat.com wrote:
This series of patches adds support for hardware profiles to Deltacloud core. Hardware profiles describe how instances can be sized, and are more general than the old flavors, in that each property can either be a fixed value, a predefined enumeration of values, or a range of values.
To make it a little easier to test these patches, I pushed them as branch dev/hardware-profiles to git://gitorious.org/deltacloud-sinatra/deltacloud-sinatra.git
You can browse the repo at http://gitorious.org/deltacloud-sinatra/deltacloud-sinatra
Perfect, this is working for me, but still getting one small exception: When calling /api/instances/inst1
/usr/bin/thin:19NoMethodError: undefined method `name' for nil:NilClass /home/mfojtik/code/deltacloud-sinatra/server/views/instances/show.html.haml:21:in `render'
All other things are working as expected.
That points to an instance without a instance_profile. Is this with the mock driver ? And if so, did you 'rm -rf /var/tmp/deltacloud-core-$USER' after applying the patches and restart the server ?
David
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 04/17/2010 12:33 AM, David Lutterkort wrote:
On Tue, 2010-04-13 at 12:37 +0200, Michal Fojtik wrote:
On 12/04/10 09:44 -0700, David Lutterkort wrote:
On Fri, 2010-04-09 at 16:59 -0700, lutter@redhat.com wrote:
This series of patches adds support for hardware profiles to Deltacloud core. Hardware profiles describe how instances can be sized, and are more general than the old flavors, in that each property can either be a fixed value, a predefined enumeration of values, or a range of values.
To make it a little easier to test these patches, I pushed them as branch dev/hardware-profiles to git://gitorious.org/deltacloud-sinatra/deltacloud-sinatra.git
You can browse the repo at http://gitorious.org/deltacloud-sinatra/deltacloud-sinatra
Perfect, this is working for me, but still getting one small exception: When calling /api/instances/inst1
/usr/bin/thin:19NoMethodError: undefined method `name' for nil:NilClass /home/mfojtik/code/deltacloud-sinatra/server/views/instances/show.html.haml:21:in `render'
All other things are working as expected.
That points to an instance without a instance_profile. Is this with the mock driver ? And if so, did you 'rm -rf /var/tmp/deltacloud-core-$USER' after applying the patches and restart the server ?
Yes, that helped. It's working perfectly now.
- Michal
- -- - -------------------------------------------------------- Michal Fojtik, mfojtik@redhat.com, +420 532 294 4307 Ruby / Ruby On Rails Developer Deltacloud API: http://deltacloud.org - --------------------------------------------------------
On Fri, 2010-04-09 at 16:59 -0700, lutter@redhat.com wrote:
This series of patches adds support for hardware profiles to Deltacloud core. Hardware profiles describe how instances can be sized, and are more general than the old flavors, in that each property can either be a fixed value, a predefined enumeration of values, or a range of values.
I just pushed the patch series. To make transition easier, I also tagged the revision before patch 25/25 (removing flavors) as flavors-last. Just 'git checkout flavors-last' if you still need that.
David
deltacloud-devel@lists.fedorahosted.org