You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by mf...@redhat.com on 2013/03/22 13:36:13 UTC

[PATCH core] CIMI: Added more descriptive CIMI schema errors

From: Michal Fojtik <mf...@redhat.com>

* Wrong attribute values are not reported correctly

Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/lib/cimi/models/resource.rb     |   5 ++
 server/lib/cimi/models/schema.rb       |  88 +++++++++++++++++---
 server/tests/cimi/model/errors_spec.rb | 141 +++++++++++++++++++++++++++++++++
 3 files changed, 225 insertions(+), 9 deletions(-)
 create mode 100644 server/tests/cimi/model/errors_spec.rb

diff --git a/server/lib/cimi/models/resource.rb b/server/lib/cimi/models/resource.rb
index 9f5bb66..2b3de42 100644
--- a/server/lib/cimi/models/resource.rb
+++ b/server/lib/cimi/models/resource.rb
@@ -36,6 +36,11 @@ module CIMI
       #
       def initialize(values = {})
         names = self.class.schema.attribute_names
+        unless values.is_a?(::Hash) or values.kind_of?(CIMI::Model::Resource)
+          raise CIMI::Model::Schema::InvalidAttributeValue.new(
+            "The #{self.class} must be initialized using Hash or CIMI::Resource (is #{values.inspect})"
+          )
+        end
         @select_attrs = values[:select_attr_list] || []
         # Make sure we always have the :id of the entity even
         # the $select parameter is used and :id is filtered out
diff --git a/server/lib/cimi/models/schema.rb b/server/lib/cimi/models/schema.rb
index 5a2049b..3a9d54c 100644
--- a/server/lib/cimi/models/schema.rb
+++ b/server/lib/cimi/models/schema.rb
@@ -19,6 +19,8 @@ require_relative "../../deltacloud/core_ext"
 # The smarts of converting from XML and JSON into internal objects
 class CIMI::Model::Schema
 
+  class InvalidAttributeValue < StandardError; end
+
   #
   # Attributes describe how we extract values from XML/JSON
   #
@@ -60,6 +62,12 @@ class CIMI::Model::Schema
     def valid?(value)
       !value.nil? and !value.to_s.empty?
     end
+
+    def report_error(message)
+      message = "The `#{@name}` attribute #{message}"
+      raise CIMI::Model::Schema::InvalidAttributeValue.new(message)
+    end
+
   end
 
   class Scalar < Attribute
@@ -190,6 +198,7 @@ class CIMI::Model::Schema
         @klass = CIMI::Model::const_get(refname)
       else
         @klass = Class.new(opts[:class]) do |m|
+          def initialize(values={}); super(values); end
           scalar :href
         end
         CIMI::Model::const_set(refname, @klass)
@@ -207,6 +216,39 @@ class CIMI::Model::Schema
         a.valid?(value.send(a.name))
       }
     end
+
+    def to_xml(model, xml)
+      super if valid_ref?(model[name])
+    end
+
+    def to_json(model, json)
+      super if valid_ref?(model[name])
+    end
+
+    private
+
+    def valid_ref?(value)
+      return true if value.is_a?(::Hash) or value.kind_of?(CIMI::Model::Resource) or value.nil?
+      report_error "must be a Hash or CIMI::Resource (is #{value})"
+    end
+  end
+
+  class Href < CIMI::Model::Schema::Struct
+
+    def to_xml(model, xml)
+      super if valid_href?(model[name])
+    end
+
+    def to_json(model, json)
+      super if valid_href?(model[name])
+    end
+
+    private
+
+    def valid_href?(value)
+      return true if value.is_a?(::Hash) or value.is_a?(struct) or value.nil?
+      report_error "must be a Hash{:href} or Struct#href (is #{value})"
+    end
   end
 
   class Array < Attribute
@@ -239,13 +281,25 @@ class CIMI::Model::Schema
     end
 
     def to_xml(model, xml)
-      ary = (model[name] || []).map { |elt| @struct.convert_to_xml(elt) }
-      xml[xml_name] = ary unless ary.empty?
+      return unless model[name]
+      if is_valid_array? model[name]
+        ary = model[name].map { |elt| @struct.convert_to_xml(elt) }
+        xml[xml_name] = ary unless ary.empty?
+      end
     end
 
     def to_json(model, json)
-      ary = (model[name] || []).map { |elt| @struct.convert_to_json(elt) }
-      json[json_name] = ary unless ary.empty?
+      return unless model[name]
+      if is_valid_array? model[name]
+        ary = model[name].map { |elt| @struct.convert_to_json(elt) }
+        json[json_name] = ary unless ary.empty?
+      end
+    end
+
+    private
+
+    def is_valid_array?(value)
+      value.is_a?(::Array) ? true : report_error('must be a valid Array')
     end
   end
 
@@ -268,15 +322,26 @@ class CIMI::Model::Schema
     end
 
     def to_xml(model, xml)
-      ary = (model[name] || {}).map { |k, v| { "key" => k, "content" => v } }
-      xml[xml_name] = ary unless ary.empty?
+      return unless model[name]
+      if is_valid_hash? model[name]
+        ary = (model[name]).map { |k, v| { "key" => k, "content" => v } }
+        xml[xml_name] = ary unless ary.empty?
+      end
     end
 
     def to_json(model, json)
-      if model[name] && ! model[name].empty?
-        json[json_name] = model[name]
+      return unless model[name]
+      if is_valid_hash? model[name]
+        json[json_name] = model[name] unless model[name].empty?
       end
     end
+
+    private
+
+    def is_valid_hash?(value)
+      value.is_a?(::Hash) ? true : report_error('must be a valid Hash')
+    end
+
   end
 
   class Collection < Attribute
@@ -413,7 +478,12 @@ class CIMI::Model::Schema
 
     def href(*args)
       opts = args.extract_opts!
-      args.each { |arg| struct(arg, opts) { scalar :href, :required => opts[:required] } }
+      #args.each { |arg| struct(arg, opts) { scalar :href, :required => opts[:required] } }
+      args.each { |arg|
+        add_attributes!([arg, opts], Href) {
+          scalar :href, :required => opts[:required]
+        }
+      }
     end
 
     def text(*args)
diff --git a/server/tests/cimi/model/errors_spec.rb b/server/tests/cimi/model/errors_spec.rb
new file mode 100644
index 0000000..fac38fb
--- /dev/null
+++ b/server/tests/cimi/model/errors_spec.rb
@@ -0,0 +1,141 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+require 'rubygems'
+require 'require_relative' if RUBY_VERSION < '1.9'
+
+require_relative '../spec_helper.rb' if require 'minitest/autorun'
+
+describe 'Schema' do
+  describe 'Hash attributes' do
+
+    it 'should not report error when attribute is set properly' do
+      machine = CIMI::Model::MachineTemplate.new(:property => {})
+      machine.to_xml.must_be_kind_of String
+      machine.to_json.must_be_kind_of String
+    end
+
+    it 'should not report error when attribute is nil' do
+      machine = CIMI::Model::MachineTemplate.new(:property => nil)
+      machine.to_xml.must_be_kind_of String
+      machine.to_json.must_be_kind_of String
+    end
+
+    it 'should not report error when attribute is not set' do
+      machine = CIMI::Model::MachineTemplate.new
+      machine.to_xml.must_be_kind_of String
+      machine.to_json.must_be_kind_of String
+    end
+
+    it 'should report invalid value for Hash attribute when set to String' do
+      machine = CIMI::Model::MachineTemplate.new(:property => '')
+      lambda { machine.to_xml }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+      lambda { machine.to_json }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+    end
+
+    it 'should report invalid value for Hash attribute when set to Array' do
+      machine = CIMI::Model::MachineTemplate.new(:property => [])
+      lambda { machine.to_xml }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+      lambda { machine.to_json }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+    end
+
+  end
+
+  describe 'Array attributes' do
+
+    it 'should report invalid value when set to Hash' do
+      machine = CIMI::Model::MachineTemplate.new(:volumes => {} )
+      lambda { machine.to_xml }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+      lambda { machine.to_json }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+    end
+
+    it 'should report invalid value when set to String' do
+      machine = CIMI::Model::MachineTemplate.new(:volumes => '' )
+      lambda { machine.to_xml }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+      lambda { machine.to_json }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+    end
+
+    it 'should not report error when attribute is set properly' do
+      machine = CIMI::Model::MachineTemplate.new(:volumes => [])
+      machine.to_xml.must_be_kind_of String
+      machine.to_json.must_be_kind_of String
+    end
+
+    it 'should not report error when attribute is nil' do
+      machine = CIMI::Model::MachineTemplate.new(:volumes => nil)
+      machine.to_xml.must_be_kind_of String
+      machine.to_json.must_be_kind_of String
+
+    end
+
+    it 'should not report error when attribute is not set' do
+      machine = CIMI::Model::MachineTemplate.new
+      machine.to_xml.must_be_kind_of String
+      machine.to_json.must_be_kind_of String
+    end
+  end
+
+  describe 'Ref attributes' do
+
+    it 'should report error when initialized using Array' do
+      lambda {
+        CIMI::Model::MachineTemplate.new(:machine_config => [])
+      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+    end
+
+    it 'should report error when initialized using String' do
+      lambda {
+        CIMI::Model::MachineTemplate.new(:machine_config => '')
+      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+    end
+
+    it 'could be initialized by the Hash value' do
+      machine = CIMI::Model::MachineTemplate.new(:machine_config => { :href => 'http://localhost/1' })
+      machine.machine_config.must_be_instance_of CIMI::Model::MachineConfigurationRef
+      machine.machine_config.href.must_equal 'http://localhost/1'
+      machine.to_xml.must_be_instance_of String
+      machine.to_json.must_be_instance_of String
+    end
+
+    it 'could be initialized by the Ref value' do
+      machine = CIMI::Model::MachineTemplate.new(:machine_config => CIMI::Model::MachineConfigurationRef.new(:href => 'http://localhost/1'))
+      machine.machine_config.must_be_instance_of CIMI::Model::MachineConfigurationRef
+      machine.machine_config.href.must_equal 'http://localhost/1'
+      machine.to_xml.must_be_instance_of String
+      machine.to_json.must_be_instance_of String
+    end
+
+  end
+
+  describe 'Href attributes' do
+
+    it 'should report error when value is not a Hash' do
+      machine = CIMI::Model::Machine.new(:machine_image => '')
+      lambda {
+        machine.to_xml
+      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+      lambda {
+        machine.to_json
+      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
+    end
+
+    it 'should not report error when initialized correctly' do
+      machine = CIMI::Model::Machine.new( :machine_image => { :href => 'test' })
+      machine.to_xml.must_be_instance_of String
+      machine.to_json.must_be_instance_of String
+    end
+  end
+
+end
-- 
1.8.1.4


RE: [PATCH core] CIMI: Added more descriptive CIMI schema errors

Posted by "Koper, Dies" <di...@fast.au.fujitsu.com>.
I was able to apply it with:

git config --global apply.whitespace fix
and
git am 0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch

but the error message hasn't changed:

E, [2013-03-27T12:25:39.949808 #7780] ERROR -- 502:
[Deltacloud::Exceptions::ProviderError] The
CIMI::Model::MachineConfigurationRef must be initialized using Hash or
CIMI::Resource (is #<CIMI::Service::MachineConfiguration:0x408c420
@model=#<CIMI::Model::MachineConfiguration:0x408c2b8 @select_attrs=[],
@base_id="http:
...
followed by tens of pages of unhelpful debugging output.

And commenting out that line in fgcp_driver_cimi_methods.rb still leads
to

> > > E, [2013-03-26T22:54:23.194324 #4960] ERROR -- 500:
[NoMethodError]
> > > undefined method `[]' for nil:NilClass
> > >
d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:326:in
`to_xml'

Regards,
Dies Koper


> -----Original Message-----
> From: Koper, Dies [mailto:diesk@fast.au.fujitsu.com]
> Sent: Wednesday, 27 March 2013 12:28 AM
> To: dev@deltacloud.apache.org; mfojtik@redhat.com
> Subject: RE: [PATCH core] CIMI: Added more descriptive CIMI schema
errors
> 
> I couldn't get it to apply:
> 
> d:\sources\OSS\cloud\deltacloud\server>git apply
> 0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch
> server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:26: trailing
> whitespace.
>         unless values.is_a?(::Hash) or
> values.kind_of?(CIMI::Model::Resource)
> server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:27: trailing
> whitespace.
>           raise CIMI::Model::Schema::InvalidAttributeValue.new(
> server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:28: trailing
> whitespace.
>             "The #{self.class} must be initialized using Hash or
> CIMI::Resource (is #{values.inspect})"
> server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:29: trailing
> whitespace.
>           )
> server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:30: trailing
> whitespace.
>         end
> fatal: git apply: bad git-diff - expected /dev/null on line 187
> 
> I tried a few things, like apply.whitespace=fix and
> core.whitespace=nowarn, but no luck.
> 
> Regards,
> Dies Koper
> 
> 
> > -----Original Message-----
> > From: Michal Fojtik [mailto:mfojtik@redhat.com]
> > Sent: Tuesday, 26 March 2013 11:54 PM
> > To: dev@deltacloud.apache.org
> > Subject: Re: [PATCH core] CIMI: Added more descriptive CIMI schema
> errors
> >
> > On 03/26/2013 01:10 PM, Koper, Dies wrote:
> >
> > Can you please try: http://tracker.deltacloud.org/set/402 ?
> >
> >    -- Michal
> >
> > > Hi Michal,
> > >
> > > I tried this patch and it gave the following error when I tried to
> > > retrieve system templates from the fgcp:
> > >
> > > E, [2013-03-26T22:51:05.100517 #1940] ERROR -- 502:
> > > [Deltacloud::Exceptions::ProviderError] The
> > > CIMI::Model::MachineConfigurationRef must be initialized using
Hash
> > or
> > > CIMI::Resource (is #<CIMI::Service::MachineConfiguration:0x411ae30
> > > @model=#<CIMI::Model::MachineConfiguration:0x411ad70
> > @select_attrs=[],
> > > @base_id="http:
> > > //localhost:3001/cimi/machine_configurations/economy",
> > >
> >
> > >
> > > Is the return type of MachineConfiguration.find incorrect?
> > > I tried commenting out that line, but that led to the following
> error:
> > >
> > > E, [2013-03-26T22:54:23.194324 #4960] ERROR -- 500:
[NoMethodError]
> > > undefined method `[]' for nil:NilClass
> > >
> > >
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:325
> > :in
> > > `to_xml'
> > >
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444
> > :in
> > > `block in to_xml'
> > >
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444
> > :in
> > > `each'
> > >
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444
> > :in
> > > `to_xml'
> > >
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:158
> > :in
> > > `convert_to_xml'
> > > ...
> > >
> > > Cheers,
> > > Dies Koper
> > >
> > >
> > >> -----Original Message-----
> > >> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
> > >> Sent: Friday, 22 March 2013 11:36 PM
> > >> To: dev@deltacloud.apache.org
> > >> Subject: [PATCH core] CIMI: Added more descriptive CIMI schema
> errors
> > >>
> > >> From: Michal Fojtik <mf...@redhat.com>
> > >>
> > >> * Wrong attribute values are not reported correctly
> > >>
> > >> Signed-off-by: Michal fojtik <mf...@redhat.com>
> > >> ---
> > >>   server/lib/cimi/models/resource.rb     |   5 ++
> > >>   server/lib/cimi/models/schema.rb       |  88
> > +++++++++++++++++---
> > >>   server/tests/cimi/model/errors_spec.rb | 141
> > >> +++++++++++++++++++++++++++++++++
> > >>   3 files changed, 225 insertions(+), 9 deletions(-)
> > >>   create mode 100644 server/tests/cimi/model/errors_spec.rb
> > >>
> > >> diff --git a/server/lib/cimi/models/resource.rb
> > >> b/server/lib/cimi/models/resource.rb
> > >> index 9f5bb66..2b3de42 100644
> > >> --- a/server/lib/cimi/models/resource.rb
> > >> +++ b/server/lib/cimi/models/resource.rb
> > >> @@ -36,6 +36,11 @@ module CIMI
> > >>         #
> > >>         def initialize(values = {})
> > >>           names = self.class.schema.attribute_names
> > >> +        unless values.is_a?(::Hash) or
> > >> values.kind_of?(CIMI::Model::Resource)
> > >> +          raise CIMI::Model::Schema::InvalidAttributeValue.new(
> > >> +            "The #{self.class} must be initialized using Hash or
> > >> CIMI::Resource (is #{values.inspect})"
> > >> +          )
> > >> +        end
> > >>           @select_attrs = values[:select_attr_list] || []
> > >>           # Make sure we always have the :id of the entity even
> > >>           # the $select parameter is used and :id is filtered out
> > >> diff --git a/server/lib/cimi/models/schema.rb
> > >> b/server/lib/cimi/models/schema.rb
> > >> index 5a2049b..3a9d54c 100644
> > >> --- a/server/lib/cimi/models/schema.rb
> > >> +++ b/server/lib/cimi/models/schema.rb
> > >> @@ -19,6 +19,8 @@ require_relative "../../deltacloud/core_ext"
> > >>   # The smarts of converting from XML and JSON into internal
> objects
> > >>   class CIMI::Model::Schema
> > >>
> > >> +  class InvalidAttributeValue < StandardError; end
> > >> +
> > >>     #
> > >>     # Attributes describe how we extract values from XML/JSON
> > >>     #
> > >> @@ -60,6 +62,12 @@ class CIMI::Model::Schema
> > >>       def valid?(value)
> > >>         !value.nil? and !value.to_s.empty?
> > >>       end
> > >> +
> > >> +    def report_error(message)
> > >> +      message = "The `#{@name}` attribute #{message}"
> > >> +      raise
> > CIMI::Model::Schema::InvalidAttributeValue.new(message)
> > >> +    end
> > >> +
> > >>     end
> > >>
> > >>     class Scalar < Attribute
> > >> @@ -190,6 +198,7 @@ class CIMI::Model::Schema
> > >>           @klass = CIMI::Model::const_get(refname)
> > >>         else
> > >>           @klass = Class.new(opts[:class]) do |m|
> > >> +          def initialize(values={}); super(values); end
> > >>             scalar :href
> > >>           end
> > >>           CIMI::Model::const_set(refname, @klass)
> > >> @@ -207,6 +216,39 @@ class CIMI::Model::Schema
> > >>           a.valid?(value.send(a.name))
> > >>         }
> > >>       end
> > >> +
> > >> +    def to_xml(model, xml)
> > >> +      super if valid_ref?(model[name])
> > >> +    end
> > >> +
> > >> +    def to_json(model, json)
> > >> +      super if valid_ref?(model[name])
> > >> +    end
> > >> +
> > >> +    private
> > >> +
> > >> +    def valid_ref?(value)
> > >> +      return true if value.is_a?(::Hash) or
> > >> value.kind_of?(CIMI::Model::Resource) or value.nil?
> > >> +      report_error "must be a Hash or CIMI::Resource (is
> #{value})"
> > >> +    end
> > >> +  end
> > >> +
> > >> +  class Href < CIMI::Model::Schema::Struct
> > >> +
> > >> +    def to_xml(model, xml)
> > >> +      super if valid_href?(model[name])
> > >> +    end
> > >> +
> > >> +    def to_json(model, json)
> > >> +      super if valid_href?(model[name])
> > >> +    end
> > >> +
> > >> +    private
> > >> +
> > >> +    def valid_href?(value)
> > >> +      return true if value.is_a?(::Hash) or value.is_a?(struct)
> or
> > >> value.nil?
> > >> +      report_error "must be a Hash{:href} or Struct#href (is
> > > #{value})"
> > >> +    end
> > >>     end
> > >>
> > >>     class Array < Attribute
> > >> @@ -239,13 +281,25 @@ class CIMI::Model::Schema
> > >>       end
> > >>
> > >>       def to_xml(model, xml)
> > >> -      ary = (model[name] || []).map { |elt|
> > >> @struct.convert_to_xml(elt) }
> > >> -      xml[xml_name] = ary unless ary.empty?
> > >> +      return unless model[name]
> > >> +      if is_valid_array? model[name]
> > >> +        ary = model[name].map { |elt|
@struct.convert_to_xml(elt)
> }
> > >> +        xml[xml_name] = ary unless ary.empty?
> > >> +      end
> > >>       end
> > >>
> > >>       def to_json(model, json)
> > >> -      ary = (model[name] || []).map { |elt|
> > >> @struct.convert_to_json(elt) }
> > >> -      json[json_name] = ary unless ary.empty?
> > >> +      return unless model[name]
> > >> +      if is_valid_array? model[name]
> > >> +        ary = model[name].map { |elt|
@struct.convert_to_json(elt)
> }
> > >> +        json[json_name] = ary unless ary.empty?
> > >> +      end
> > >> +    end
> > >> +
> > >> +    private
> > >> +
> > >> +    def is_valid_array?(value)
> > >> +      value.is_a?(::Array) ? true : report_error('must be a
valid
> > >> Array')
> > >>       end
> > >>     end
> > >>
> > >> @@ -268,15 +322,26 @@ class CIMI::Model::Schema
> > >>       end
> > >>
> > >>       def to_xml(model, xml)
> > >> -      ary = (model[name] || {}).map { |k, v| { "key" => k,
> "content"
> > >> => v } }
> > >> -      xml[xml_name] = ary unless ary.empty?
> > >> +      return unless model[name]
> > >> +      if is_valid_hash? model[name]
> > >> +        ary = (model[name]).map { |k, v| { "key" => k, "content"
> =>
> > v
> > > } }
> > >> +        xml[xml_name] = ary unless ary.empty?
> > >> +      end
> > >>       end
> > >>
> > >>       def to_json(model, json)
> > >> -      if model[name] && ! model[name].empty?
> > >> -        json[json_name] = model[name]
> > >> +      return unless model[name]
> > >> +      if is_valid_hash? model[name]
> > >> +        json[json_name] = model[name] unless model[name].empty?
> > >>         end
> > >>       end
> > >> +
> > >> +    private
> > >> +
> > >> +    def is_valid_hash?(value)
> > >> +      value.is_a?(::Hash) ? true : report_error('must be a valid
> > > Hash')
> > >> +    end
> > >> +
> > >>     end
> > >>
> > >>     class Collection < Attribute
> > >> @@ -413,7 +478,12 @@ class CIMI::Model::Schema
> > >>
> > >>       def href(*args)
> > >>         opts = args.extract_opts!
> > >> -      args.each { |arg| struct(arg, opts) { scalar :href,
> :required
> > > =>
> > >> opts[:required] } }
> > >> +      #args.each { |arg| struct(arg, opts) { scalar :href,
> :required
> > >> => opts[:required] } }
> > >> +      args.each { |arg|
> > >> +        add_attributes!([arg, opts], Href) {
> > >> +          scalar :href, :required => opts[:required]
> > >> +        }
> > >> +      }
> > >>       end
> > >>
> > >>       def text(*args)
> > >> diff --git a/server/tests/cimi/model/errors_spec.rb
> > >> b/server/tests/cimi/model/errors_spec.rb
> > >> new file mode 100644
> > >> index 0000000..fac38fb
> > >> --- /dev/null
> > >> +++ b/server/tests/cimi/model/errors_spec.rb
> > >> @@ -0,0 +1,141 @@
> > >> +# Licensed to the Apache Software Foundation (ASF) under one or
> more
> > >> +# contributor license agreements.  See the NOTICE file
distributed
> > > with
> > >> +# this work for additional information regarding copyright
> ownership.
> > >> The
> > >> +# ASF licenses this file to you under the Apache License,
Version
> > 2.0
> > >> (the
> > >> +# "License"); you may not use this file except in compliance
with
> > the
> > >> +# License.  You may obtain a copy of the License at
> > >> +#
> > >> +#       http://www.apache.org/licenses/LICENSE-2.0
> > >> +#
> > >> +# Unless required by applicable law or agreed to in writing,
> software
> > >> +# distributed under the License is distributed on an "AS IS"
> BASIS,
> > >> WITHOUT
> > >> +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
> > > See
> > >> the
> > >> +# License for the specific language governing permissions and
> > >> limitations
> > >> +# under the License.
> > >> +#
> > >> +require 'rubygems'
> > >> +require 'require_relative' if RUBY_VERSION < '1.9'
> > >> +
> > >> +require_relative '../spec_helper.rb' if require
> 'minitest/autorun'
> > >> +
> > >> +describe 'Schema' do
> > >> +  describe 'Hash attributes' do
> > >> +
> > >> +    it 'should not report error when attribute is set properly'
> do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:property =>
{})
> > >> +      machine.to_xml.must_be_kind_of String
> > >> +      machine.to_json.must_be_kind_of String
> > >> +    end
> > >> +
> > >> +    it 'should not report error when attribute is nil' do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:property =>
> nil)
> > >> +      machine.to_xml.must_be_kind_of String
> > >> +      machine.to_json.must_be_kind_of String
> > >> +    end
> > >> +
> > >> +    it 'should not report error when attribute is not set' do
> > >> +      machine = CIMI::Model::MachineTemplate.new
> > >> +      machine.to_xml.must_be_kind_of String
> > >> +      machine.to_json.must_be_kind_of String
> > >> +    end
> > >> +
> > >> +    it 'should report invalid value for Hash attribute when set
> to
> > >> String' do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:property =>
'')
> > >> +      lambda { machine.to_xml }.must_raise
> > >> CIMI::Model::Schema::InvalidAttributeValue
> > >> +      lambda { machine.to_json }.must_raise
> > >> CIMI::Model::Schema::InvalidAttributeValue
> > >> +    end
> > >> +
> > >> +    it 'should report invalid value for Hash attribute when set
> to
> > > Array'
> > >> do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:property =>
[])
> > >> +      lambda { machine.to_xml }.must_raise
> > >> CIMI::Model::Schema::InvalidAttributeValue
> > >> +      lambda { machine.to_json }.must_raise
> > >> CIMI::Model::Schema::InvalidAttributeValue
> > >> +    end
> > >> +
> > >> +  end
> > >> +
> > >> +  describe 'Array attributes' do
> > >> +
> > >> +    it 'should report invalid value when set to Hash' do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:volumes => {}
)
> > >> +      lambda { machine.to_xml }.must_raise
> > >> CIMI::Model::Schema::InvalidAttributeValue
> > >> +      lambda { machine.to_json }.must_raise
> > >> CIMI::Model::Schema::InvalidAttributeValue
> > >> +    end
> > >> +
> > >> +    it 'should report invalid value when set to String' do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:volumes => ''
)
> > >> +      lambda { machine.to_xml }.must_raise
> > >> CIMI::Model::Schema::InvalidAttributeValue
> > >> +      lambda { machine.to_json }.must_raise
> > >> CIMI::Model::Schema::InvalidAttributeValue
> > >> +    end
> > >> +
> > >> +    it 'should not report error when attribute is set properly'
> do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:volumes => [])
> > >> +      machine.to_xml.must_be_kind_of String
> > >> +      machine.to_json.must_be_kind_of String
> > >> +    end
> > >> +
> > >> +    it 'should not report error when attribute is nil' do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:volumes =>
nil)
> > >> +      machine.to_xml.must_be_kind_of String
> > >> +      machine.to_json.must_be_kind_of String
> > >> +
> > >> +    end
> > >> +
> > >> +    it 'should not report error when attribute is not set' do
> > >> +      machine = CIMI::Model::MachineTemplate.new
> > >> +      machine.to_xml.must_be_kind_of String
> > >> +      machine.to_json.must_be_kind_of String
> > >> +    end
> > >> +  end
> > >> +
> > >> +  describe 'Ref attributes' do
> > >> +
> > >> +    it 'should report error when initialized using Array' do
> > >> +      lambda {
> > >> +        CIMI::Model::MachineTemplate.new(:machine_config => [])
> > >> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> > >> +    end
> > >> +
> > >> +    it 'should report error when initialized using String' do
> > >> +      lambda {
> > >> +        CIMI::Model::MachineTemplate.new(:machine_config => '')
> > >> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> > >> +    end
> > >> +
> > >> +    it 'could be initialized by the Hash value' do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:machine_config
> > =>
> > >> { :href => 'http://localhost/1' })
> > >> +      machine.machine_config.must_be_instance_of
> > >> CIMI::Model::MachineConfigurationRef
> > >> +      machine.machine_config.href.must_equal
> 'http://localhost/1'
> > >> +      machine.to_xml.must_be_instance_of String
> > >> +      machine.to_json.must_be_instance_of String
> > >> +    end
> > >> +
> > >> +    it 'could be initialized by the Ref value' do
> > >> +      machine = CIMI::Model::MachineTemplate.new(:machine_config
> > =>
> > >> CIMI::Model::MachineConfigurationRef.new(:href =>
> > >> 'http://localhost/1'))
> > >> +      machine.machine_config.must_be_instance_of
> > >> CIMI::Model::MachineConfigurationRef
> > >> +      machine.machine_config.href.must_equal
> 'http://localhost/1'
> > >> +      machine.to_xml.must_be_instance_of String
> > >> +      machine.to_json.must_be_instance_of String
> > >> +    end
> > >> +
> > >> +  end
> > >> +
> > >> +  describe 'Href attributes' do
> > >> +
> > >> +    it 'should report error when value is not a Hash' do
> > >> +      machine = CIMI::Model::Machine.new(:machine_image => '')
> > >> +      lambda {
> > >> +        machine.to_xml
> > >> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> > >> +      lambda {
> > >> +        machine.to_json
> > >> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> > >> +    end
> > >> +
> > >> +    it 'should not report error when initialized correctly' do
> > >> +      machine = CIMI::Model::Machine.new( :machine_image => {
> :href
> > > =>
> > >> 'test' })
> > >> +      machine.to_xml.must_be_instance_of String
> > >> +      machine.to_json.must_be_instance_of String
> > >> +    end
> > >> +  end
> > >> +
> > >> +end
> > >> --
> > >> 1.8.1.4
> > >>
> > >
> > >
> >
> >
> > --
> >
> > Michal Fojtik <mf...@redhat.com>
> > Deltacloud API, CloudForms
> 
> 



RE: [PATCH core] CIMI: Added more descriptive CIMI schema errors

Posted by "Koper, Dies" <di...@fast.au.fujitsu.com>.
I couldn't get it to apply:

d:\sources\OSS\cloud\deltacloud\server>git apply
0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch
server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:26: trailing
whitespace.
        unless values.is_a?(::Hash) or
values.kind_of?(CIMI::Model::Resource)
server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:27: trailing
whitespace.
          raise CIMI::Model::Schema::InvalidAttributeValue.new(
server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:28: trailing
whitespace.
            "The #{self.class} must be initialized using Hash or
CIMI::Resource (is #{values.inspect})"
server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:29: trailing
whitespace.
          )
server/0-f7fac9768e02ee3f12386b8fc311311298ee81a2.patch:30: trailing
whitespace.
        end
fatal: git apply: bad git-diff - expected /dev/null on line 187

I tried a few things, like apply.whitespace=fix and
core.whitespace=nowarn, but no luck.

Regards,
Dies Koper


> -----Original Message-----
> From: Michal Fojtik [mailto:mfojtik@redhat.com]
> Sent: Tuesday, 26 March 2013 11:54 PM
> To: dev@deltacloud.apache.org
> Subject: Re: [PATCH core] CIMI: Added more descriptive CIMI schema
errors
> 
> On 03/26/2013 01:10 PM, Koper, Dies wrote:
> 
> Can you please try: http://tracker.deltacloud.org/set/402 ?
> 
>    -- Michal
> 
> > Hi Michal,
> >
> > I tried this patch and it gave the following error when I tried to
> > retrieve system templates from the fgcp:
> >
> > E, [2013-03-26T22:51:05.100517 #1940] ERROR -- 502:
> > [Deltacloud::Exceptions::ProviderError] The
> > CIMI::Model::MachineConfigurationRef must be initialized using Hash
> or
> > CIMI::Resource (is #<CIMI::Service::MachineConfiguration:0x411ae30
> > @model=#<CIMI::Model::MachineConfiguration:0x411ad70
> @select_attrs=[],
> > @base_id="http:
> > //localhost:3001/cimi/machine_configurations/economy",
> >
> @attribute_values={:id=>"http://localhost:3001/cimi/machine_configur
> atio
> > ns/economy", :name=>"economy", :d
> > escription=>"Machine Configuration with 1782579 KiB of memory and 1
> > CPU", :created=>"2013-03-26T22:51:04+11:00", :updated=>nil,
> > :property=>nil, :cpu=>"1", :memo
> > ry=>1782579, :disks=>nil, :operations=>nil}>,
> > @context=#<CIMI::Collections::SystemTemplates:0x2de9bf8
> > @default_layout=:layout, @app=#<struct Sinatra::ExtendedRa
> > ck app=#<Rack::MethodOverride:0x4115580 @app=#<Rack::Head:0x4115598
> > @app=#<Rack::NullLogger:0x41155c8
> > @app=#<Rack::Protection::FrameOptions:0x4115670 @app=#<Rac
> > k::Protection::HttpOrigin:0x41156b8
> > @app=#<Rack::Protection::IPSpoofing:0x4115718
> > @app=#<Rack::Protection::JsonCsrf:0x4115760
> > @app=#<Rack::Protection::PathTrave
> > rsal:0x41157a8 @app=#<Rack::Protection::XSSHeader:0x4115820
> > @app=#<Rack::Accept::Context:0x4115c88 @app=#<struct
> > Sinatra::ExtendedRack app=#<Sinatra::ShowExcept
> > ions:0x2e11f90 @app=#<Rack::Head:0x2e11fc0
> > @app=#<Rack::NullLogger:0x2e11fd8
> > @app=#<Rack::Protection::FrameOptions:0x2e12140
> > @app=#<Rack::Protection::HttpOrigin
> > :0x2e12260 @app=#<Rack::Protection::IPSpoofing:0x2e122c0
> > @app=#<Rack::Protection::JsonCsrf:0x2e12350
> > @app=#<Rack::Protection::PathTraversal:0x2e123b0 @app=#<Rac
> > k::Protection::XSSHeader:0x2e12440 @app=#<Rack::MediaType:0x2e128c0
> > @default_layout=:layout, @app=#<struct Sinatra::ExtendedRack
> > app=#<Sinatra::ShowExceptions:0
> > x2dfe868 @app=#<Rack::Head:0x2dfe880
> @app=#<Rack::NullLogger:0x2dfe898
> > @app=#<Rack::Protection::FrameOptions:0x2dfe970
> > @app=#<Rack::Protection::HttpOrigin:0x2df
> > e9e8 @app=#<Rack::Protection::IPSpoofing:0x2dfea48
> > @app=#<Rack::Protection::JsonCsrf:0x2dfead8
> > @app=#<Rack::Protection::PathTraversal:0x2dfeb68 @app=#<Rack::Pro
> > tection::XSSHeader:0x2dfecd0
> > @app=#<CIMI::Rabbit::VolumesCollection:0x2df7ec0
> > @default_layout=:layout,
> @app=#<CIMI::Collections::Volumes:0x2df87f0
> > @default_layo
> > ut=:layout, @app=#<struct Sinatra::ExtendedRack
> > app=#<Rack::MethodOverride:0x2df8df0 @app=#<Rack::Head:0x2df9000
> > @app=#<Rack::NullLogger:0x2df9048 @app=#<Rack::
> > Protection::FrameOptions:0x2df90d8
> > @app=#<Rack::Protection::HttpOrigin:0x2df9150
> > @app=#<Rack::Protection::IPSpoofing:0x2df91c8
> > @app=#<Rack::Protection::JsonCsrf
> > :0x2df9228 @app=#<Rack::Protection::PathTraversal:0x2df9288
> > @app=#<Rack::Protection::XSSHeader:0x2df9300
> > @app=#<Rack::Accept::Context:0x2df9690 @app=#<struct Si
> > natra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2dd87c8
> > @app=#<Rack::Head:0x2dd87f8 @app=#<Rack::NullLogger:0x2dd8810
> > @app=#<Rack::Protection::FrameOptions:
> > 0x2dd89a8 @app=#<Rack::Protection::HttpOrigin:0x2dd8b88
> > @app=#<Rack::Protection::IPSpoofing:0x2dd8bd0
> > @app=#<Rack::Protection::JsonCsrf:0x2dd8e28 @app=#<Rack::P
> > rotection::PathTraversal:0x2dd8e88
> > @app=#<Rack::Protection::XSSHeader:0x2dd8f18
> > @app=#<Rack::MediaType:0x2dd9638 @default_layout=:layout,
> @app=#<struct
> > Sinatra:
> > :ExtendedRack app=#<Sinatra::ShowExceptions:0x2db9b50
> > @app=#<Rack::Head:0x2db9b80 @app=#<Rack::NullLogger:0x2db9bc8
> > @app=#<Rack::Protection::FrameOptions:0x2db9
> > c58 @app=#<Rack::Protection::HttpOrigin:0x2db9d00
> > @app=#<Rack::Protection::IPSpoofing:0x2db9d60
> > @app=#<Rack::Protection::JsonCsrf:0x2db9da8 @app=#<Rack::Protect
> > ion::PathTraversal:0x2db9e38
> > @app=#<Rack::Protection::XSSHeader:0x2db9ee0
> > @app=#<CIMI::Rabbit::VolumeConfigurationsCollection:0x2dba480
> > @default_layout=:layout,
> >   @app=#<CIMI::Collections::VolumeConfigurations:0x2dba8b8
> > @default_layout=:layout, @app=#<struct Sinatra::ExtendedRack
> > app=#<Rack::MethodOverride:0x2db3268 @app
> > =#<Rack::Head:0x2db3280 @app=#<Rack::NullLogger:0x2db3310
> > @app=#<Rack::Protection::FrameOptions:0x2db3448
> > @app=#<Rack::Protection::HttpOrigin:0x2db3508 @app=#<R
> > ack::Protection::IPSpoofing:0x2db3658
> > @app=#<Rack::Protection::JsonCsrf:0x2db36b8
> > @app=#<Rack::Protection::PathTraversal:0x2db3760
> > @app=#<Rack::Protection::XSSH
> > eader:0x2db3868 @app=#<Rack::Accept::Context:0x2db4240 @app=#<struct
> > Sinatra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2d231d8
> > @app=#<Rack::Head:0x2d23250 @
> > app=#<Rack::NullLogger:0x2d23268
> > @app=#<Rack::Protection::FrameOptions:0x2d234a8
> > @app=#<Rack::Protection::HttpOrigin:0x2d23610
> > @app=#<Rack::Protection::IPSpoofi
> > ng:0x2d236a0 @app=#<Rack::Protection::JsonCsrf:0x2d237c0
> > @app=#<Rack::Protection::PathTraversal:0x2d238f8
> > @app=#<Rack::Protection::XSSHeader:0x2d23970 @app=#<Ra
> > ck::MediaType:0x2d24318 @default_layout=:layout, @app=#<struct
> > Sinatra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2c75c58
> > @app=#<Rack::Head:0x2c75cb8 @app=#<
> > Rack::NullLogger:0x2c75ce8
> > @app=#<Rack::Protection::FrameOptions:0x2c75e98
> > @app=#<Rack::Protection::HttpOrigin:0x2c75fa0
> > @app=#<Rack::Protection::IPSpoofing:0x2
> > c76060 @app=#<Rack::Protection::JsonCsrf:0x2c76150
> > @app=#<Rack::Protection::PathTraversal:0x2c761e0
> > @app=#<Rack::Protection::XSSHeader:0x2c76270 @app=#<CIMI::Ra
> > bbit::VolumeImagesCollection:0x2c77020 @default_layout=:layout,
> > @app=#<CIMI::Collections::VolumeImages:0x2c77578
> > @default_layout=:layout, @app=#<struct Sinatra:
> > :ExtendedRack app=#<Rack::MethodOverride:0x2c779f8
> > @app=#<Rack::Head:0x2c77a10 @app=#<Rack::NullLogger:0x2c77a28
> > @app=#<Rack::Protection::FrameOptions:0x2c77ab8
> >   @app=#<Rack::Protection::HttpOrigin:0x2c77b18
> > @app=#<Rack::Protection::IPSpoofing:0x2c77b78
> > @app=#<Rack::Protection::JsonCsrf:0x2c77bf0 @app=#<Rack::Protection
> > ::PathTraversal:0x2c77c38
> @app=#<Rack::Protection::XSSHeader:0x2c77c98
> > @app=#<Rack::Accept::Context:0x2c6c0a0 @app=#<struct
> > Sinatra::ExtendedRack app=#<Sinatra:
> > :ShowExceptions:0x2be9900 @app=#<Rack::Head:0x2be9918
> > @app=#<Rack::NullLogger:0x2be9930
> > @app=#<Rack::Protection::FrameOptions:0x2be99d8
> @app=#<Rack::Protection:
> > :HttpOrigin:0x2be9a98 @app=#<Rack::Protection::IPSpoofing:0x2be9b28
> > @app=#<Rack::Protection::JsonCsrf:0x2be9b88
> > @app=#<Rack::Protection::PathTraversal:0x2be9bd0
> >   @app=#<Rack::Protection::XSSHeader:0x2be9c60
> > @app=#<Rack::MediaType:0x2bea920 @default_layout=:layout,
> @app=#<struct
> > Sinatra::ExtendedRack app=#<Sinatra::ShowE
> > xceptions:0x28340e8 @app=#<Rack::Head:0x2834190
> > @app=#<Rack::NullLogger:0x28341a8
> > @app=#<Rack::Protection::FrameOptions:0x2834358
> > @app=#<Rack::Protection::HttpO
> > rigin:0x2834490 @app=#<Rack::Protection::IPSpoofing:0x28345c8
> > @app=#<Rack::Protection::JsonCsrf:0x2834670
> > @app=#<Rack::Protection::PathTraversal:0x2834700 @app=
> > #<Rack::Protection::XSSHeader:0x2834820
> > @app=#<CIMI::Rabbit::VolumeTemplatesCollection:0x25f44d8
> > @default_layout=:layout, @app=#<CIMI::Collections::VolumeTempla
> > tes:0x25e41d0 @default_layout=:layout, @app=#<CIMI::API:0x25e6750
> > @default_layout=:layout, @app=nil,
> > @template_cache=#<Tilt::Cache:0x25e65d0 @cache={}>>, @templ
> > ate_cache=#<Tilt::Cache:0x25e4128 @cache={}>>,
> > @template_cache=#<Tilt::Cache:0x25f43d0 @cache={}>>,
> > @options={:reaction=>:drop_session, :logging=>true, :message
> >
> =>"Forbidden", :encryptor=>Digest::SHA1, :session_key=>"rack.session
> ",
> > :status=>403, :allow_empty_referrer=>true, :html_types=>["text/htm
> l",
> > "application/xhtml"
> > ], :xss_mode=>:block, :nosniff=>true, :except=>[:session_hijacking,
> > :remote_token]}>,
> @options={:reaction=>:drop_session, :logging=>true,
> > :message=>"Forbidden",
> >   :encryptor=>Digest::SHA1, :session_key=>"rack.session", :status=
> >403,
> > :allow_empty_referrer=>true, :html_types=>["text/html",
> > "application/xhtml"], :except=>[:
> > session_hijacking, :remote_token]}>,
> @options={:reaction=>:drop_session,
> > :logging=>true, :message=>"Forbidden", :encryptor=>Digest::SHA1,
> > :session_key=>"rack.se
> > ssion", :status=>403, :allow_empty_referrer=>true,
> > :html_types=>["text/html", "application/xhtml"],
> > :except=>[:session_hijacking, :remote_token]}>, @options={:r
> > eaction=>:drop_session, :logging=>true, :message=>"Forbidden",
> > :encryptor=>Digest::SHA1, :session_key=>"rack.session", :status=>4
> 03,
> > :allow_empty_referrer=>true
> > , :html_types=>["text/html", "application/xhtml"],
> > :except=>[:session_hijacking, :remote_token]}>,
> > @options={:reaction=>:drop_session, :logging=>true, :message=
> >>
> "Forbidden", :encryptor=>Digest::SHA1, :session_key=>"rack.session",
> > :status=>403, :allow_empty_referrer=>true, :html_types=>["text/htm
> l",
> > "application/xhtml"]
> > , :except=>[:session_hijacking, :remote_token]}>,
> > @options={:reaction=>:drop_session, :logging=>true,
> > :message=>"Forbidden", :encryptor=>Digest::SHA1, :session_
> > key=>"rack.session", :status=>403, :allow_empty_referrer=>true,
> > :html_types=>["text/html", "application/xhtml"],
> > :frame_options=>:sameorigin, :except=>[:session
> > _hijacking, :remote_token]}>>>, @template=#<ERB:0x28340d0
> > @safe_level=nil, @src="#coding:US-ASCII\n_erbout = '';
_erbout.concat
> > \"<!DOCTYPE html>\\n<html>\\n<he
> > ad>\\n  <meta http-equiv=\\\"Content-Type\\\" content=\\\"text/html;
> > charset=utf-8\\\"/>\\n  <title>\"\n\n\n\n; _erbout.concat((h
> > exception.class ).to_s); _erbo
> > ut.concat \" at \"; _erbout.concat((h path ).to_s); _erbout.concat
> > \"</title>\\n\\n  <script type=\\\"text/javascript\\\">\\n
> //<!--\\n
> > function toggle(id) {\
> > \n    var pre  = document.getElementById(\\\"pre-\\\" + id);\\n
> var
> > post = document.getElementById(\\\"post-\\\" + id);\\n    var
context
> =
> > document.getEleme
> > ntById(\\\"context-\\\" + id);\\n\\n    if (pre.style.display ==
> > 'block') {\\n      pre.style.display = 'none';\\n
> > post.style.display = 'none';\\n      con
> > text.style.background = \\\"none\\\";\\n    } else {\\n
> > pre.style.display = 'block';\\n      post.style.display =
'block';\\n
> > context.style.background
> >   = \\\"#fffed9\\\";\\n    }\\n  }\\n\\n  function
> toggleBacktrace(){\\n
> > var bt = document.getElementById(\\\"backtrace\\\");\\n    var
> toggler =
> > document.get
> > ElementById(\\\"expando\\\");\\n\\n    if (bt.className ==
> 'condensed')
> > {\\n      bt.className = 'expanded';\\n      toggler.innerHTML =
> > \\\"(condense)\\\";\\n
> >     } else {\\n      bt.className = 'condensed';\\n
> > toggler.innerHTML = \\\"(expand)\\\";\\n    }\\n  }\\n  //-->\\n
> > </script>\\n\\n<style type=\\\"text/cs
> > s\\\" media=\\\"screen\\\">\\n  *                   {margin: 0;
> padding:
> > 0; border: 0; outline: 0;}\\n  div.clear           {clear: both;}\\n
> > body
> >     {background: #EEEEEE; margin: 0; padding: 0;\\n
> > font-family: 'Lucida Grande', 'Lucida Sans Unicode',\\n
> > 'Garuda';
> > <snip>
> >
> > Plus a few more pages of that kind of output.
> > Looking at the first part, " The
CIMI::Model::MachineConfigurationRef
> > must be initialized using Hash or CIMI::Resource (is
> > #<CIMI::Service::MachineConfiguration", what does it mean?
> >
> > In my code, system templates has componentDescriptors which is
assigned
> > an array of hashes.
> > One hash contains e.g.:
> >
> >              {
> >                :name             => desc['vsysdescriptorName'][0],
> >                :description      => '',
> >                :type             =>
> > "http://schemas.dmtf.org/cimi/1/Machine",
> >                :machine_template =>
> CIMI::Model::MachineTemplate.new(
> >                  :name             => vserver['vserverName'][0],
> >                  :description      => '',
> >                  :machine_config   =>
> > CIMI::Service::MachineConfiguration.find(vserver['vserverType'][0],
> > context),
> >                  :machine_image    => { :href =>
> > context.machine_image_url(vserver['diskimageId'][0]) },
> >                  :volume_templates => volume_templates
> >                )
> >              }
> >
> > Is the return type of MachineConfiguration.find incorrect?
> > I tried commenting out that line, but that led to the following
error:
> >
> > E, [2013-03-26T22:54:23.194324 #4960] ERROR -- 500: [NoMethodError]
> > undefined method `[]' for nil:NilClass
> >
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:325
> :in
> > `to_xml'
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444
> :in
> > `block in to_xml'
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444
> :in
> > `each'
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444
> :in
> > `to_xml'
> >
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:158
> :in
> > `convert_to_xml'
> > ...
> >
> > Cheers,
> > Dies Koper
> >
> >
> >> -----Original Message-----
> >> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
> >> Sent: Friday, 22 March 2013 11:36 PM
> >> To: dev@deltacloud.apache.org
> >> Subject: [PATCH core] CIMI: Added more descriptive CIMI schema
errors
> >>
> >> From: Michal Fojtik <mf...@redhat.com>
> >>
> >> * Wrong attribute values are not reported correctly
> >>
> >> Signed-off-by: Michal fojtik <mf...@redhat.com>
> >> ---
> >>   server/lib/cimi/models/resource.rb     |   5 ++
> >>   server/lib/cimi/models/schema.rb       |  88
> +++++++++++++++++---
> >>   server/tests/cimi/model/errors_spec.rb | 141
> >> +++++++++++++++++++++++++++++++++
> >>   3 files changed, 225 insertions(+), 9 deletions(-)
> >>   create mode 100644 server/tests/cimi/model/errors_spec.rb
> >>
> >> diff --git a/server/lib/cimi/models/resource.rb
> >> b/server/lib/cimi/models/resource.rb
> >> index 9f5bb66..2b3de42 100644
> >> --- a/server/lib/cimi/models/resource.rb
> >> +++ b/server/lib/cimi/models/resource.rb
> >> @@ -36,6 +36,11 @@ module CIMI
> >>         #
> >>         def initialize(values = {})
> >>           names = self.class.schema.attribute_names
> >> +        unless values.is_a?(::Hash) or
> >> values.kind_of?(CIMI::Model::Resource)
> >> +          raise CIMI::Model::Schema::InvalidAttributeValue.new(
> >> +            "The #{self.class} must be initialized using Hash or
> >> CIMI::Resource (is #{values.inspect})"
> >> +          )
> >> +        end
> >>           @select_attrs = values[:select_attr_list] || []
> >>           # Make sure we always have the :id of the entity even
> >>           # the $select parameter is used and :id is filtered out
> >> diff --git a/server/lib/cimi/models/schema.rb
> >> b/server/lib/cimi/models/schema.rb
> >> index 5a2049b..3a9d54c 100644
> >> --- a/server/lib/cimi/models/schema.rb
> >> +++ b/server/lib/cimi/models/schema.rb
> >> @@ -19,6 +19,8 @@ require_relative "../../deltacloud/core_ext"
> >>   # The smarts of converting from XML and JSON into internal
objects
> >>   class CIMI::Model::Schema
> >>
> >> +  class InvalidAttributeValue < StandardError; end
> >> +
> >>     #
> >>     # Attributes describe how we extract values from XML/JSON
> >>     #
> >> @@ -60,6 +62,12 @@ class CIMI::Model::Schema
> >>       def valid?(value)
> >>         !value.nil? and !value.to_s.empty?
> >>       end
> >> +
> >> +    def report_error(message)
> >> +      message = "The `#{@name}` attribute #{message}"
> >> +      raise
> CIMI::Model::Schema::InvalidAttributeValue.new(message)
> >> +    end
> >> +
> >>     end
> >>
> >>     class Scalar < Attribute
> >> @@ -190,6 +198,7 @@ class CIMI::Model::Schema
> >>           @klass = CIMI::Model::const_get(refname)
> >>         else
> >>           @klass = Class.new(opts[:class]) do |m|
> >> +          def initialize(values={}); super(values); end
> >>             scalar :href
> >>           end
> >>           CIMI::Model::const_set(refname, @klass)
> >> @@ -207,6 +216,39 @@ class CIMI::Model::Schema
> >>           a.valid?(value.send(a.name))
> >>         }
> >>       end
> >> +
> >> +    def to_xml(model, xml)
> >> +      super if valid_ref?(model[name])
> >> +    end
> >> +
> >> +    def to_json(model, json)
> >> +      super if valid_ref?(model[name])
> >> +    end
> >> +
> >> +    private
> >> +
> >> +    def valid_ref?(value)
> >> +      return true if value.is_a?(::Hash) or
> >> value.kind_of?(CIMI::Model::Resource) or value.nil?
> >> +      report_error "must be a Hash or CIMI::Resource (is
#{value})"
> >> +    end
> >> +  end
> >> +
> >> +  class Href < CIMI::Model::Schema::Struct
> >> +
> >> +    def to_xml(model, xml)
> >> +      super if valid_href?(model[name])
> >> +    end
> >> +
> >> +    def to_json(model, json)
> >> +      super if valid_href?(model[name])
> >> +    end
> >> +
> >> +    private
> >> +
> >> +    def valid_href?(value)
> >> +      return true if value.is_a?(::Hash) or value.is_a?(struct) or
> >> value.nil?
> >> +      report_error "must be a Hash{:href} or Struct#href (is
> > #{value})"
> >> +    end
> >>     end
> >>
> >>     class Array < Attribute
> >> @@ -239,13 +281,25 @@ class CIMI::Model::Schema
> >>       end
> >>
> >>       def to_xml(model, xml)
> >> -      ary = (model[name] || []).map { |elt|
> >> @struct.convert_to_xml(elt) }
> >> -      xml[xml_name] = ary unless ary.empty?
> >> +      return unless model[name]
> >> +      if is_valid_array? model[name]
> >> +        ary = model[name].map { |elt| @struct.convert_to_xml(elt)
}
> >> +        xml[xml_name] = ary unless ary.empty?
> >> +      end
> >>       end
> >>
> >>       def to_json(model, json)
> >> -      ary = (model[name] || []).map { |elt|
> >> @struct.convert_to_json(elt) }
> >> -      json[json_name] = ary unless ary.empty?
> >> +      return unless model[name]
> >> +      if is_valid_array? model[name]
> >> +        ary = model[name].map { |elt| @struct.convert_to_json(elt)
}
> >> +        json[json_name] = ary unless ary.empty?
> >> +      end
> >> +    end
> >> +
> >> +    private
> >> +
> >> +    def is_valid_array?(value)
> >> +      value.is_a?(::Array) ? true : report_error('must be a valid
> >> Array')
> >>       end
> >>     end
> >>
> >> @@ -268,15 +322,26 @@ class CIMI::Model::Schema
> >>       end
> >>
> >>       def to_xml(model, xml)
> >> -      ary = (model[name] || {}).map { |k, v| { "key" => k,
"content"
> >> => v } }
> >> -      xml[xml_name] = ary unless ary.empty?
> >> +      return unless model[name]
> >> +      if is_valid_hash? model[name]
> >> +        ary = (model[name]).map { |k, v| { "key" => k, "content"
=>
> v
> > } }
> >> +        xml[xml_name] = ary unless ary.empty?
> >> +      end
> >>       end
> >>
> >>       def to_json(model, json)
> >> -      if model[name] && ! model[name].empty?
> >> -        json[json_name] = model[name]
> >> +      return unless model[name]
> >> +      if is_valid_hash? model[name]
> >> +        json[json_name] = model[name] unless model[name].empty?
> >>         end
> >>       end
> >> +
> >> +    private
> >> +
> >> +    def is_valid_hash?(value)
> >> +      value.is_a?(::Hash) ? true : report_error('must be a valid
> > Hash')
> >> +    end
> >> +
> >>     end
> >>
> >>     class Collection < Attribute
> >> @@ -413,7 +478,12 @@ class CIMI::Model::Schema
> >>
> >>       def href(*args)
> >>         opts = args.extract_opts!
> >> -      args.each { |arg| struct(arg, opts) { scalar :href,
:required
> > =>
> >> opts[:required] } }
> >> +      #args.each { |arg| struct(arg, opts) { scalar :href,
:required
> >> => opts[:required] } }
> >> +      args.each { |arg|
> >> +        add_attributes!([arg, opts], Href) {
> >> +          scalar :href, :required => opts[:required]
> >> +        }
> >> +      }
> >>       end
> >>
> >>       def text(*args)
> >> diff --git a/server/tests/cimi/model/errors_spec.rb
> >> b/server/tests/cimi/model/errors_spec.rb
> >> new file mode 100644
> >> index 0000000..fac38fb
> >> --- /dev/null
> >> +++ b/server/tests/cimi/model/errors_spec.rb
> >> @@ -0,0 +1,141 @@
> >> +# Licensed to the Apache Software Foundation (ASF) under one or
more
> >> +# contributor license agreements.  See the NOTICE file distributed
> > with
> >> +# this work for additional information regarding copyright
ownership.
> >> The
> >> +# ASF licenses this file to you under the Apache License, Version
> 2.0
> >> (the
> >> +# "License"); you may not use this file except in compliance with
> the
> >> +# License.  You may obtain a copy of the License at
> >> +#
> >> +#       http://www.apache.org/licenses/LICENSE-2.0
> >> +#
> >> +# Unless required by applicable law or agreed to in writing,
software
> >> +# distributed under the License is distributed on an "AS IS"
BASIS,
> >> WITHOUT
> >> +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> > See
> >> the
> >> +# License for the specific language governing permissions and
> >> limitations
> >> +# under the License.
> >> +#
> >> +require 'rubygems'
> >> +require 'require_relative' if RUBY_VERSION < '1.9'
> >> +
> >> +require_relative '../spec_helper.rb' if require 'minitest/autorun'
> >> +
> >> +describe 'Schema' do
> >> +  describe 'Hash attributes' do
> >> +
> >> +    it 'should not report error when attribute is set properly' do
> >> +      machine = CIMI::Model::MachineTemplate.new(:property => {})
> >> +      machine.to_xml.must_be_kind_of String
> >> +      machine.to_json.must_be_kind_of String
> >> +    end
> >> +
> >> +    it 'should not report error when attribute is nil' do
> >> +      machine = CIMI::Model::MachineTemplate.new(:property => nil)
> >> +      machine.to_xml.must_be_kind_of String
> >> +      machine.to_json.must_be_kind_of String
> >> +    end
> >> +
> >> +    it 'should not report error when attribute is not set' do
> >> +      machine = CIMI::Model::MachineTemplate.new
> >> +      machine.to_xml.must_be_kind_of String
> >> +      machine.to_json.must_be_kind_of String
> >> +    end
> >> +
> >> +    it 'should report invalid value for Hash attribute when set to
> >> String' do
> >> +      machine = CIMI::Model::MachineTemplate.new(:property => '')
> >> +      lambda { machine.to_xml }.must_raise
> >> CIMI::Model::Schema::InvalidAttributeValue
> >> +      lambda { machine.to_json }.must_raise
> >> CIMI::Model::Schema::InvalidAttributeValue
> >> +    end
> >> +
> >> +    it 'should report invalid value for Hash attribute when set to
> > Array'
> >> do
> >> +      machine = CIMI::Model::MachineTemplate.new(:property => [])
> >> +      lambda { machine.to_xml }.must_raise
> >> CIMI::Model::Schema::InvalidAttributeValue
> >> +      lambda { machine.to_json }.must_raise
> >> CIMI::Model::Schema::InvalidAttributeValue
> >> +    end
> >> +
> >> +  end
> >> +
> >> +  describe 'Array attributes' do
> >> +
> >> +    it 'should report invalid value when set to Hash' do
> >> +      machine = CIMI::Model::MachineTemplate.new(:volumes => {} )
> >> +      lambda { machine.to_xml }.must_raise
> >> CIMI::Model::Schema::InvalidAttributeValue
> >> +      lambda { machine.to_json }.must_raise
> >> CIMI::Model::Schema::InvalidAttributeValue
> >> +    end
> >> +
> >> +    it 'should report invalid value when set to String' do
> >> +      machine = CIMI::Model::MachineTemplate.new(:volumes => '' )
> >> +      lambda { machine.to_xml }.must_raise
> >> CIMI::Model::Schema::InvalidAttributeValue
> >> +      lambda { machine.to_json }.must_raise
> >> CIMI::Model::Schema::InvalidAttributeValue
> >> +    end
> >> +
> >> +    it 'should not report error when attribute is set properly' do
> >> +      machine = CIMI::Model::MachineTemplate.new(:volumes => [])
> >> +      machine.to_xml.must_be_kind_of String
> >> +      machine.to_json.must_be_kind_of String
> >> +    end
> >> +
> >> +    it 'should not report error when attribute is nil' do
> >> +      machine = CIMI::Model::MachineTemplate.new(:volumes => nil)
> >> +      machine.to_xml.must_be_kind_of String
> >> +      machine.to_json.must_be_kind_of String
> >> +
> >> +    end
> >> +
> >> +    it 'should not report error when attribute is not set' do
> >> +      machine = CIMI::Model::MachineTemplate.new
> >> +      machine.to_xml.must_be_kind_of String
> >> +      machine.to_json.must_be_kind_of String
> >> +    end
> >> +  end
> >> +
> >> +  describe 'Ref attributes' do
> >> +
> >> +    it 'should report error when initialized using Array' do
> >> +      lambda {
> >> +        CIMI::Model::MachineTemplate.new(:machine_config => [])
> >> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> >> +    end
> >> +
> >> +    it 'should report error when initialized using String' do
> >> +      lambda {
> >> +        CIMI::Model::MachineTemplate.new(:machine_config => '')
> >> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> >> +    end
> >> +
> >> +    it 'could be initialized by the Hash value' do
> >> +      machine = CIMI::Model::MachineTemplate.new(:machine_config
> =>
> >> { :href => 'http://localhost/1' })
> >> +      machine.machine_config.must_be_instance_of
> >> CIMI::Model::MachineConfigurationRef
> >> +      machine.machine_config.href.must_equal 'http://localhost/1'
> >> +      machine.to_xml.must_be_instance_of String
> >> +      machine.to_json.must_be_instance_of String
> >> +    end
> >> +
> >> +    it 'could be initialized by the Ref value' do
> >> +      machine = CIMI::Model::MachineTemplate.new(:machine_config
> =>
> >> CIMI::Model::MachineConfigurationRef.new(:href =>
> >> 'http://localhost/1'))
> >> +      machine.machine_config.must_be_instance_of
> >> CIMI::Model::MachineConfigurationRef
> >> +      machine.machine_config.href.must_equal 'http://localhost/1'
> >> +      machine.to_xml.must_be_instance_of String
> >> +      machine.to_json.must_be_instance_of String
> >> +    end
> >> +
> >> +  end
> >> +
> >> +  describe 'Href attributes' do
> >> +
> >> +    it 'should report error when value is not a Hash' do
> >> +      machine = CIMI::Model::Machine.new(:machine_image => '')
> >> +      lambda {
> >> +        machine.to_xml
> >> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> >> +      lambda {
> >> +        machine.to_json
> >> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> >> +    end
> >> +
> >> +    it 'should not report error when initialized correctly' do
> >> +      machine = CIMI::Model::Machine.new( :machine_image => {
:href
> > =>
> >> 'test' })
> >> +      machine.to_xml.must_be_instance_of String
> >> +      machine.to_json.must_be_instance_of String
> >> +    end
> >> +  end
> >> +
> >> +end
> >> --
> >> 1.8.1.4
> >>
> >
> >
> 
> 
> --
> 
> Michal Fojtik <mf...@redhat.com>
> Deltacloud API, CloudForms



Re: [PATCH core] CIMI: Added more descriptive CIMI schema errors

Posted by Michal Fojtik <mf...@redhat.com>.
On 03/26/2013 01:10 PM, Koper, Dies wrote:

Can you please try: http://tracker.deltacloud.org/set/402 ?

   -- Michal

> Hi Michal,
>
> I tried this patch and it gave the following error when I tried to
> retrieve system templates from the fgcp:
>
> E, [2013-03-26T22:51:05.100517 #1940] ERROR -- 502:
> [Deltacloud::Exceptions::ProviderError] The
> CIMI::Model::MachineConfigurationRef must be initialized using Hash or
> CIMI::Resource (is #<CIMI::Service::MachineConfiguration:0x411ae30
> @model=#<CIMI::Model::MachineConfiguration:0x411ad70 @select_attrs=[],
> @base_id="http:
> //localhost:3001/cimi/machine_configurations/economy",
> @attribute_values={:id=>"http://localhost:3001/cimi/machine_configuratio
> ns/economy", :name=>"economy", :d
> escription=>"Machine Configuration with 1782579 KiB of memory and 1
> CPU", :created=>"2013-03-26T22:51:04+11:00", :updated=>nil,
> :property=>nil, :cpu=>"1", :memo
> ry=>1782579, :disks=>nil, :operations=>nil}>,
> @context=#<CIMI::Collections::SystemTemplates:0x2de9bf8
> @default_layout=:layout, @app=#<struct Sinatra::ExtendedRa
> ck app=#<Rack::MethodOverride:0x4115580 @app=#<Rack::Head:0x4115598
> @app=#<Rack::NullLogger:0x41155c8
> @app=#<Rack::Protection::FrameOptions:0x4115670 @app=#<Rac
> k::Protection::HttpOrigin:0x41156b8
> @app=#<Rack::Protection::IPSpoofing:0x4115718
> @app=#<Rack::Protection::JsonCsrf:0x4115760
> @app=#<Rack::Protection::PathTrave
> rsal:0x41157a8 @app=#<Rack::Protection::XSSHeader:0x4115820
> @app=#<Rack::Accept::Context:0x4115c88 @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra::ShowExcept
> ions:0x2e11f90 @app=#<Rack::Head:0x2e11fc0
> @app=#<Rack::NullLogger:0x2e11fd8
> @app=#<Rack::Protection::FrameOptions:0x2e12140
> @app=#<Rack::Protection::HttpOrigin
> :0x2e12260 @app=#<Rack::Protection::IPSpoofing:0x2e122c0
> @app=#<Rack::Protection::JsonCsrf:0x2e12350
> @app=#<Rack::Protection::PathTraversal:0x2e123b0 @app=#<Rac
> k::Protection::XSSHeader:0x2e12440 @app=#<Rack::MediaType:0x2e128c0
> @default_layout=:layout, @app=#<struct Sinatra::ExtendedRack
> app=#<Sinatra::ShowExceptions:0
> x2dfe868 @app=#<Rack::Head:0x2dfe880 @app=#<Rack::NullLogger:0x2dfe898
> @app=#<Rack::Protection::FrameOptions:0x2dfe970
> @app=#<Rack::Protection::HttpOrigin:0x2df
> e9e8 @app=#<Rack::Protection::IPSpoofing:0x2dfea48
> @app=#<Rack::Protection::JsonCsrf:0x2dfead8
> @app=#<Rack::Protection::PathTraversal:0x2dfeb68 @app=#<Rack::Pro
> tection::XSSHeader:0x2dfecd0
> @app=#<CIMI::Rabbit::VolumesCollection:0x2df7ec0
> @default_layout=:layout, @app=#<CIMI::Collections::Volumes:0x2df87f0
> @default_layo
> ut=:layout, @app=#<struct Sinatra::ExtendedRack
> app=#<Rack::MethodOverride:0x2df8df0 @app=#<Rack::Head:0x2df9000
> @app=#<Rack::NullLogger:0x2df9048 @app=#<Rack::
> Protection::FrameOptions:0x2df90d8
> @app=#<Rack::Protection::HttpOrigin:0x2df9150
> @app=#<Rack::Protection::IPSpoofing:0x2df91c8
> @app=#<Rack::Protection::JsonCsrf
> :0x2df9228 @app=#<Rack::Protection::PathTraversal:0x2df9288
> @app=#<Rack::Protection::XSSHeader:0x2df9300
> @app=#<Rack::Accept::Context:0x2df9690 @app=#<struct Si
> natra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2dd87c8
> @app=#<Rack::Head:0x2dd87f8 @app=#<Rack::NullLogger:0x2dd8810
> @app=#<Rack::Protection::FrameOptions:
> 0x2dd89a8 @app=#<Rack::Protection::HttpOrigin:0x2dd8b88
> @app=#<Rack::Protection::IPSpoofing:0x2dd8bd0
> @app=#<Rack::Protection::JsonCsrf:0x2dd8e28 @app=#<Rack::P
> rotection::PathTraversal:0x2dd8e88
> @app=#<Rack::Protection::XSSHeader:0x2dd8f18
> @app=#<Rack::MediaType:0x2dd9638 @default_layout=:layout, @app=#<struct
> Sinatra:
> :ExtendedRack app=#<Sinatra::ShowExceptions:0x2db9b50
> @app=#<Rack::Head:0x2db9b80 @app=#<Rack::NullLogger:0x2db9bc8
> @app=#<Rack::Protection::FrameOptions:0x2db9
> c58 @app=#<Rack::Protection::HttpOrigin:0x2db9d00
> @app=#<Rack::Protection::IPSpoofing:0x2db9d60
> @app=#<Rack::Protection::JsonCsrf:0x2db9da8 @app=#<Rack::Protect
> ion::PathTraversal:0x2db9e38
> @app=#<Rack::Protection::XSSHeader:0x2db9ee0
> @app=#<CIMI::Rabbit::VolumeConfigurationsCollection:0x2dba480
> @default_layout=:layout,
>   @app=#<CIMI::Collections::VolumeConfigurations:0x2dba8b8
> @default_layout=:layout, @app=#<struct Sinatra::ExtendedRack
> app=#<Rack::MethodOverride:0x2db3268 @app
> =#<Rack::Head:0x2db3280 @app=#<Rack::NullLogger:0x2db3310
> @app=#<Rack::Protection::FrameOptions:0x2db3448
> @app=#<Rack::Protection::HttpOrigin:0x2db3508 @app=#<R
> ack::Protection::IPSpoofing:0x2db3658
> @app=#<Rack::Protection::JsonCsrf:0x2db36b8
> @app=#<Rack::Protection::PathTraversal:0x2db3760
> @app=#<Rack::Protection::XSSH
> eader:0x2db3868 @app=#<Rack::Accept::Context:0x2db4240 @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2d231d8
> @app=#<Rack::Head:0x2d23250 @
> app=#<Rack::NullLogger:0x2d23268
> @app=#<Rack::Protection::FrameOptions:0x2d234a8
> @app=#<Rack::Protection::HttpOrigin:0x2d23610
> @app=#<Rack::Protection::IPSpoofi
> ng:0x2d236a0 @app=#<Rack::Protection::JsonCsrf:0x2d237c0
> @app=#<Rack::Protection::PathTraversal:0x2d238f8
> @app=#<Rack::Protection::XSSHeader:0x2d23970 @app=#<Ra
> ck::MediaType:0x2d24318 @default_layout=:layout, @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2c75c58
> @app=#<Rack::Head:0x2c75cb8 @app=#<
> Rack::NullLogger:0x2c75ce8
> @app=#<Rack::Protection::FrameOptions:0x2c75e98
> @app=#<Rack::Protection::HttpOrigin:0x2c75fa0
> @app=#<Rack::Protection::IPSpoofing:0x2
> c76060 @app=#<Rack::Protection::JsonCsrf:0x2c76150
> @app=#<Rack::Protection::PathTraversal:0x2c761e0
> @app=#<Rack::Protection::XSSHeader:0x2c76270 @app=#<CIMI::Ra
> bbit::VolumeImagesCollection:0x2c77020 @default_layout=:layout,
> @app=#<CIMI::Collections::VolumeImages:0x2c77578
> @default_layout=:layout, @app=#<struct Sinatra:
> :ExtendedRack app=#<Rack::MethodOverride:0x2c779f8
> @app=#<Rack::Head:0x2c77a10 @app=#<Rack::NullLogger:0x2c77a28
> @app=#<Rack::Protection::FrameOptions:0x2c77ab8
>   @app=#<Rack::Protection::HttpOrigin:0x2c77b18
> @app=#<Rack::Protection::IPSpoofing:0x2c77b78
> @app=#<Rack::Protection::JsonCsrf:0x2c77bf0 @app=#<Rack::Protection
> ::PathTraversal:0x2c77c38 @app=#<Rack::Protection::XSSHeader:0x2c77c98
> @app=#<Rack::Accept::Context:0x2c6c0a0 @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra:
> :ShowExceptions:0x2be9900 @app=#<Rack::Head:0x2be9918
> @app=#<Rack::NullLogger:0x2be9930
> @app=#<Rack::Protection::FrameOptions:0x2be99d8 @app=#<Rack::Protection:
> :HttpOrigin:0x2be9a98 @app=#<Rack::Protection::IPSpoofing:0x2be9b28
> @app=#<Rack::Protection::JsonCsrf:0x2be9b88
> @app=#<Rack::Protection::PathTraversal:0x2be9bd0
>   @app=#<Rack::Protection::XSSHeader:0x2be9c60
> @app=#<Rack::MediaType:0x2bea920 @default_layout=:layout, @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra::ShowE
> xceptions:0x28340e8 @app=#<Rack::Head:0x2834190
> @app=#<Rack::NullLogger:0x28341a8
> @app=#<Rack::Protection::FrameOptions:0x2834358
> @app=#<Rack::Protection::HttpO
> rigin:0x2834490 @app=#<Rack::Protection::IPSpoofing:0x28345c8
> @app=#<Rack::Protection::JsonCsrf:0x2834670
> @app=#<Rack::Protection::PathTraversal:0x2834700 @app=
> #<Rack::Protection::XSSHeader:0x2834820
> @app=#<CIMI::Rabbit::VolumeTemplatesCollection:0x25f44d8
> @default_layout=:layout, @app=#<CIMI::Collections::VolumeTempla
> tes:0x25e41d0 @default_layout=:layout, @app=#<CIMI::API:0x25e6750
> @default_layout=:layout, @app=nil,
> @template_cache=#<Tilt::Cache:0x25e65d0 @cache={}>>, @templ
> ate_cache=#<Tilt::Cache:0x25e4128 @cache={}>>,
> @template_cache=#<Tilt::Cache:0x25f43d0 @cache={}>>,
> @options={:reaction=>:drop_session, :logging=>true, :message
> =>"Forbidden", :encryptor=>Digest::SHA1, :session_key=>"rack.session",
> :status=>403, :allow_empty_referrer=>true, :html_types=>["text/html",
> "application/xhtml"
> ], :xss_mode=>:block, :nosniff=>true, :except=>[:session_hijacking,
> :remote_token]}>, @options={:reaction=>:drop_session, :logging=>true,
> :message=>"Forbidden",
>   :encryptor=>Digest::SHA1, :session_key=>"rack.session", :status=>403,
> :allow_empty_referrer=>true, :html_types=>["text/html",
> "application/xhtml"], :except=>[:
> session_hijacking, :remote_token]}>, @options={:reaction=>:drop_session,
> :logging=>true, :message=>"Forbidden", :encryptor=>Digest::SHA1,
> :session_key=>"rack.se
> ssion", :status=>403, :allow_empty_referrer=>true,
> :html_types=>["text/html", "application/xhtml"],
> :except=>[:session_hijacking, :remote_token]}>, @options={:r
> eaction=>:drop_session, :logging=>true, :message=>"Forbidden",
> :encryptor=>Digest::SHA1, :session_key=>"rack.session", :status=>403,
> :allow_empty_referrer=>true
> , :html_types=>["text/html", "application/xhtml"],
> :except=>[:session_hijacking, :remote_token]}>,
> @options={:reaction=>:drop_session, :logging=>true, :message=
>> "Forbidden", :encryptor=>Digest::SHA1, :session_key=>"rack.session",
> :status=>403, :allow_empty_referrer=>true, :html_types=>["text/html",
> "application/xhtml"]
> , :except=>[:session_hijacking, :remote_token]}>,
> @options={:reaction=>:drop_session, :logging=>true,
> :message=>"Forbidden", :encryptor=>Digest::SHA1, :session_
> key=>"rack.session", :status=>403, :allow_empty_referrer=>true,
> :html_types=>["text/html", "application/xhtml"],
> :frame_options=>:sameorigin, :except=>[:session
> _hijacking, :remote_token]}>>>, @template=#<ERB:0x28340d0
> @safe_level=nil, @src="#coding:US-ASCII\n_erbout = ''; _erbout.concat
> \"<!DOCTYPE html>\\n<html>\\n<he
> ad>\\n  <meta http-equiv=\\\"Content-Type\\\" content=\\\"text/html;
> charset=utf-8\\\"/>\\n  <title>\"\n\n\n\n; _erbout.concat((h
> exception.class ).to_s); _erbo
> ut.concat \" at \"; _erbout.concat((h path ).to_s); _erbout.concat
> \"</title>\\n\\n  <script type=\\\"text/javascript\\\">\\n  //<!--\\n
> function toggle(id) {\
> \n    var pre  = document.getElementById(\\\"pre-\\\" + id);\\n    var
> post = document.getElementById(\\\"post-\\\" + id);\\n    var context =
> document.getEleme
> ntById(\\\"context-\\\" + id);\\n\\n    if (pre.style.display ==
> 'block') {\\n      pre.style.display = 'none';\\n
> post.style.display = 'none';\\n      con
> text.style.background = \\\"none\\\";\\n    } else {\\n
> pre.style.display = 'block';\\n      post.style.display = 'block';\\n
> context.style.background
>   = \\\"#fffed9\\\";\\n    }\\n  }\\n\\n  function toggleBacktrace(){\\n
> var bt = document.getElementById(\\\"backtrace\\\");\\n    var toggler =
> document.get
> ElementById(\\\"expando\\\");\\n\\n    if (bt.className == 'condensed')
> {\\n      bt.className = 'expanded';\\n      toggler.innerHTML =
> \\\"(condense)\\\";\\n
>     } else {\\n      bt.className = 'condensed';\\n
> toggler.innerHTML = \\\"(expand)\\\";\\n    }\\n  }\\n  //-->\\n
> </script>\\n\\n<style type=\\\"text/cs
> s\\\" media=\\\"screen\\\">\\n  *                   {margin: 0; padding:
> 0; border: 0; outline: 0;}\\n  div.clear           {clear: both;}\\n
> body
>     {background: #EEEEEE; margin: 0; padding: 0;\\n
> font-family: 'Lucida Grande', 'Lucida Sans Unicode',\\n
> 'Garuda';
> <snip>
>
> Plus a few more pages of that kind of output.
> Looking at the first part, " The CIMI::Model::MachineConfigurationRef
> must be initialized using Hash or CIMI::Resource (is
> #<CIMI::Service::MachineConfiguration", what does it mean?
>
> In my code, system templates has componentDescriptors which is assigned
> an array of hashes.
> One hash contains e.g.:
>
>              {
>                :name             => desc['vsysdescriptorName'][0],
>                :description      => '',
>                :type             =>
> "http://schemas.dmtf.org/cimi/1/Machine",
>                :machine_template => CIMI::Model::MachineTemplate.new(
>                  :name             => vserver['vserverName'][0],
>                  :description      => '',
>                  :machine_config   =>
> CIMI::Service::MachineConfiguration.find(vserver['vserverType'][0],
> context),
>                  :machine_image    => { :href =>
> context.machine_image_url(vserver['diskimageId'][0]) },
>                  :volume_templates => volume_templates
>                )
>              }
>
> Is the return type of MachineConfiguration.find incorrect?
> I tried commenting out that line, but that led to the following error:
>
> E, [2013-03-26T22:54:23.194324 #4960] ERROR -- 500: [NoMethodError]
> undefined method `[]' for nil:NilClass
>
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:325:in
> `to_xml'
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444:in
> `block in to_xml'
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444:in
> `each'
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444:in
> `to_xml'
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:158:in
> `convert_to_xml'
> ...
>
> Cheers,
> Dies Koper
>
>
>> -----Original Message-----
>> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
>> Sent: Friday, 22 March 2013 11:36 PM
>> To: dev@deltacloud.apache.org
>> Subject: [PATCH core] CIMI: Added more descriptive CIMI schema errors
>>
>> From: Michal Fojtik <mf...@redhat.com>
>>
>> * Wrong attribute values are not reported correctly
>>
>> Signed-off-by: Michal fojtik <mf...@redhat.com>
>> ---
>>   server/lib/cimi/models/resource.rb     |   5 ++
>>   server/lib/cimi/models/schema.rb       |  88 +++++++++++++++++---
>>   server/tests/cimi/model/errors_spec.rb | 141
>> +++++++++++++++++++++++++++++++++
>>   3 files changed, 225 insertions(+), 9 deletions(-)
>>   create mode 100644 server/tests/cimi/model/errors_spec.rb
>>
>> diff --git a/server/lib/cimi/models/resource.rb
>> b/server/lib/cimi/models/resource.rb
>> index 9f5bb66..2b3de42 100644
>> --- a/server/lib/cimi/models/resource.rb
>> +++ b/server/lib/cimi/models/resource.rb
>> @@ -36,6 +36,11 @@ module CIMI
>>         #
>>         def initialize(values = {})
>>           names = self.class.schema.attribute_names
>> +        unless values.is_a?(::Hash) or
>> values.kind_of?(CIMI::Model::Resource)
>> +          raise CIMI::Model::Schema::InvalidAttributeValue.new(
>> +            "The #{self.class} must be initialized using Hash or
>> CIMI::Resource (is #{values.inspect})"
>> +          )
>> +        end
>>           @select_attrs = values[:select_attr_list] || []
>>           # Make sure we always have the :id of the entity even
>>           # the $select parameter is used and :id is filtered out
>> diff --git a/server/lib/cimi/models/schema.rb
>> b/server/lib/cimi/models/schema.rb
>> index 5a2049b..3a9d54c 100644
>> --- a/server/lib/cimi/models/schema.rb
>> +++ b/server/lib/cimi/models/schema.rb
>> @@ -19,6 +19,8 @@ require_relative "../../deltacloud/core_ext"
>>   # The smarts of converting from XML and JSON into internal objects
>>   class CIMI::Model::Schema
>>
>> +  class InvalidAttributeValue < StandardError; end
>> +
>>     #
>>     # Attributes describe how we extract values from XML/JSON
>>     #
>> @@ -60,6 +62,12 @@ class CIMI::Model::Schema
>>       def valid?(value)
>>         !value.nil? and !value.to_s.empty?
>>       end
>> +
>> +    def report_error(message)
>> +      message = "The `#{@name}` attribute #{message}"
>> +      raise CIMI::Model::Schema::InvalidAttributeValue.new(message)
>> +    end
>> +
>>     end
>>
>>     class Scalar < Attribute
>> @@ -190,6 +198,7 @@ class CIMI::Model::Schema
>>           @klass = CIMI::Model::const_get(refname)
>>         else
>>           @klass = Class.new(opts[:class]) do |m|
>> +          def initialize(values={}); super(values); end
>>             scalar :href
>>           end
>>           CIMI::Model::const_set(refname, @klass)
>> @@ -207,6 +216,39 @@ class CIMI::Model::Schema
>>           a.valid?(value.send(a.name))
>>         }
>>       end
>> +
>> +    def to_xml(model, xml)
>> +      super if valid_ref?(model[name])
>> +    end
>> +
>> +    def to_json(model, json)
>> +      super if valid_ref?(model[name])
>> +    end
>> +
>> +    private
>> +
>> +    def valid_ref?(value)
>> +      return true if value.is_a?(::Hash) or
>> value.kind_of?(CIMI::Model::Resource) or value.nil?
>> +      report_error "must be a Hash or CIMI::Resource (is #{value})"
>> +    end
>> +  end
>> +
>> +  class Href < CIMI::Model::Schema::Struct
>> +
>> +    def to_xml(model, xml)
>> +      super if valid_href?(model[name])
>> +    end
>> +
>> +    def to_json(model, json)
>> +      super if valid_href?(model[name])
>> +    end
>> +
>> +    private
>> +
>> +    def valid_href?(value)
>> +      return true if value.is_a?(::Hash) or value.is_a?(struct) or
>> value.nil?
>> +      report_error "must be a Hash{:href} or Struct#href (is
> #{value})"
>> +    end
>>     end
>>
>>     class Array < Attribute
>> @@ -239,13 +281,25 @@ class CIMI::Model::Schema
>>       end
>>
>>       def to_xml(model, xml)
>> -      ary = (model[name] || []).map { |elt|
>> @struct.convert_to_xml(elt) }
>> -      xml[xml_name] = ary unless ary.empty?
>> +      return unless model[name]
>> +      if is_valid_array? model[name]
>> +        ary = model[name].map { |elt| @struct.convert_to_xml(elt) }
>> +        xml[xml_name] = ary unless ary.empty?
>> +      end
>>       end
>>
>>       def to_json(model, json)
>> -      ary = (model[name] || []).map { |elt|
>> @struct.convert_to_json(elt) }
>> -      json[json_name] = ary unless ary.empty?
>> +      return unless model[name]
>> +      if is_valid_array? model[name]
>> +        ary = model[name].map { |elt| @struct.convert_to_json(elt) }
>> +        json[json_name] = ary unless ary.empty?
>> +      end
>> +    end
>> +
>> +    private
>> +
>> +    def is_valid_array?(value)
>> +      value.is_a?(::Array) ? true : report_error('must be a valid
>> Array')
>>       end
>>     end
>>
>> @@ -268,15 +322,26 @@ class CIMI::Model::Schema
>>       end
>>
>>       def to_xml(model, xml)
>> -      ary = (model[name] || {}).map { |k, v| { "key" => k, "content"
>> => v } }
>> -      xml[xml_name] = ary unless ary.empty?
>> +      return unless model[name]
>> +      if is_valid_hash? model[name]
>> +        ary = (model[name]).map { |k, v| { "key" => k, "content" => v
> } }
>> +        xml[xml_name] = ary unless ary.empty?
>> +      end
>>       end
>>
>>       def to_json(model, json)
>> -      if model[name] && ! model[name].empty?
>> -        json[json_name] = model[name]
>> +      return unless model[name]
>> +      if is_valid_hash? model[name]
>> +        json[json_name] = model[name] unless model[name].empty?
>>         end
>>       end
>> +
>> +    private
>> +
>> +    def is_valid_hash?(value)
>> +      value.is_a?(::Hash) ? true : report_error('must be a valid
> Hash')
>> +    end
>> +
>>     end
>>
>>     class Collection < Attribute
>> @@ -413,7 +478,12 @@ class CIMI::Model::Schema
>>
>>       def href(*args)
>>         opts = args.extract_opts!
>> -      args.each { |arg| struct(arg, opts) { scalar :href, :required
> =>
>> opts[:required] } }
>> +      #args.each { |arg| struct(arg, opts) { scalar :href, :required
>> => opts[:required] } }
>> +      args.each { |arg|
>> +        add_attributes!([arg, opts], Href) {
>> +          scalar :href, :required => opts[:required]
>> +        }
>> +      }
>>       end
>>
>>       def text(*args)
>> diff --git a/server/tests/cimi/model/errors_spec.rb
>> b/server/tests/cimi/model/errors_spec.rb
>> new file mode 100644
>> index 0000000..fac38fb
>> --- /dev/null
>> +++ b/server/tests/cimi/model/errors_spec.rb
>> @@ -0,0 +1,141 @@
>> +# Licensed to the Apache Software Foundation (ASF) under one or more
>> +# contributor license agreements.  See the NOTICE file distributed
> with
>> +# this work for additional information regarding copyright ownership.
>> The
>> +# ASF licenses this file to you under the Apache License, Version 2.0
>> (the
>> +# "License"); you may not use this file except in compliance with the
>> +# License.  You may obtain a copy of the License at
>> +#
>> +#       http://www.apache.org/licenses/LICENSE-2.0
>> +#
>> +# Unless required by applicable law or agreed to in writing, software
>> +# distributed under the License is distributed on an "AS IS" BASIS,
>> WITHOUT
>> +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See
>> the
>> +# License for the specific language governing permissions and
>> limitations
>> +# under the License.
>> +#
>> +require 'rubygems'
>> +require 'require_relative' if RUBY_VERSION < '1.9'
>> +
>> +require_relative '../spec_helper.rb' if require 'minitest/autorun'
>> +
>> +describe 'Schema' do
>> +  describe 'Hash attributes' do
>> +
>> +    it 'should not report error when attribute is set properly' do
>> +      machine = CIMI::Model::MachineTemplate.new(:property => {})
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +
>> +    it 'should not report error when attribute is nil' do
>> +      machine = CIMI::Model::MachineTemplate.new(:property => nil)
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +
>> +    it 'should not report error when attribute is not set' do
>> +      machine = CIMI::Model::MachineTemplate.new
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +
>> +    it 'should report invalid value for Hash attribute when set to
>> String' do
>> +      machine = CIMI::Model::MachineTemplate.new(:property => '')
>> +      lambda { machine.to_xml }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda { machine.to_json }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should report invalid value for Hash attribute when set to
> Array'
>> do
>> +      machine = CIMI::Model::MachineTemplate.new(:property => [])
>> +      lambda { machine.to_xml }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda { machine.to_json }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +  end
>> +
>> +  describe 'Array attributes' do
>> +
>> +    it 'should report invalid value when set to Hash' do
>> +      machine = CIMI::Model::MachineTemplate.new(:volumes => {} )
>> +      lambda { machine.to_xml }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda { machine.to_json }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should report invalid value when set to String' do
>> +      machine = CIMI::Model::MachineTemplate.new(:volumes => '' )
>> +      lambda { machine.to_xml }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda { machine.to_json }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should not report error when attribute is set properly' do
>> +      machine = CIMI::Model::MachineTemplate.new(:volumes => [])
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +
>> +    it 'should not report error when attribute is nil' do
>> +      machine = CIMI::Model::MachineTemplate.new(:volumes => nil)
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +
>> +    end
>> +
>> +    it 'should not report error when attribute is not set' do
>> +      machine = CIMI::Model::MachineTemplate.new
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +  end
>> +
>> +  describe 'Ref attributes' do
>> +
>> +    it 'should report error when initialized using Array' do
>> +      lambda {
>> +        CIMI::Model::MachineTemplate.new(:machine_config => [])
>> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should report error when initialized using String' do
>> +      lambda {
>> +        CIMI::Model::MachineTemplate.new(:machine_config => '')
>> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'could be initialized by the Hash value' do
>> +      machine = CIMI::Model::MachineTemplate.new(:machine_config =>
>> { :href => 'http://localhost/1' })
>> +      machine.machine_config.must_be_instance_of
>> CIMI::Model::MachineConfigurationRef
>> +      machine.machine_config.href.must_equal 'http://localhost/1'
>> +      machine.to_xml.must_be_instance_of String
>> +      machine.to_json.must_be_instance_of String
>> +    end
>> +
>> +    it 'could be initialized by the Ref value' do
>> +      machine = CIMI::Model::MachineTemplate.new(:machine_config =>
>> CIMI::Model::MachineConfigurationRef.new(:href =>
>> 'http://localhost/1'))
>> +      machine.machine_config.must_be_instance_of
>> CIMI::Model::MachineConfigurationRef
>> +      machine.machine_config.href.must_equal 'http://localhost/1'
>> +      machine.to_xml.must_be_instance_of String
>> +      machine.to_json.must_be_instance_of String
>> +    end
>> +
>> +  end
>> +
>> +  describe 'Href attributes' do
>> +
>> +    it 'should report error when value is not a Hash' do
>> +      machine = CIMI::Model::Machine.new(:machine_image => '')
>> +      lambda {
>> +        machine.to_xml
>> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda {
>> +        machine.to_json
>> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should not report error when initialized correctly' do
>> +      machine = CIMI::Model::Machine.new( :machine_image => { :href
> =>
>> 'test' })
>> +      machine.to_xml.must_be_instance_of String
>> +      machine.to_json.must_be_instance_of String
>> +    end
>> +  end
>> +
>> +end
>> --
>> 1.8.1.4
>>
>
>


-- 

Michal Fojtik <mf...@redhat.com>
Deltacloud API, CloudForms

Re: [PATCH core] CIMI: Added more descriptive CIMI schema errors

Posted by Michal Fojtik <mf...@redhat.com>.
On 03/26/2013 01:10 PM, Koper, Dies wrote:
> Hi Michal,
>
> I tried this patch and it gave the following error when I tried to
> retrieve system templates from the fgcp:


Hi Dies,

Thanks, this patch should help identifying the issues with using CIMI
schema and mappings.

In this case I think it is a bug in my patch I need to fix. The 
'Service' should be inherited from the 'Model' class which should
be inherited from the 'CIMI::Resource'. Will fix it and send a new version.

   -- MIchal

>
> E, [2013-03-26T22:51:05.100517 #1940] ERROR -- 502:
> [Deltacloud::Exceptions::ProviderError] The
> CIMI::Model::MachineConfigurationRef must be initialized using Hash or
> CIMI::Resource (is #<CIMI::Service::MachineConfiguration:0x411ae30
> @model=#<CIMI::Model::MachineConfiguration:0x411ad70 @select_attrs=[],
> @base_id="http:
> //localhost:3001/cimi/machine_configurations/economy",
> @attribute_values={:id=>"http://localhost:3001/cimi/machine_configuratio
> ns/economy", :name=>"economy", :d
> escription=>"Machine Configuration with 1782579 KiB of memory and 1
> CPU", :created=>"2013-03-26T22:51:04+11:00", :updated=>nil,
> :property=>nil, :cpu=>"1", :memo
> ry=>1782579, :disks=>nil, :operations=>nil}>,
> @context=#<CIMI::Collections::SystemTemplates:0x2de9bf8
> @default_layout=:layout, @app=#<struct Sinatra::ExtendedRa
> ck app=#<Rack::MethodOverride:0x4115580 @app=#<Rack::Head:0x4115598
> @app=#<Rack::NullLogger:0x41155c8
> @app=#<Rack::Protection::FrameOptions:0x4115670 @app=#<Rac
> k::Protection::HttpOrigin:0x41156b8
> @app=#<Rack::Protection::IPSpoofing:0x4115718
> @app=#<Rack::Protection::JsonCsrf:0x4115760
> @app=#<Rack::Protection::PathTrave
> rsal:0x41157a8 @app=#<Rack::Protection::XSSHeader:0x4115820
> @app=#<Rack::Accept::Context:0x4115c88 @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra::ShowExcept
> ions:0x2e11f90 @app=#<Rack::Head:0x2e11fc0
> @app=#<Rack::NullLogger:0x2e11fd8
> @app=#<Rack::Protection::FrameOptions:0x2e12140
> @app=#<Rack::Protection::HttpOrigin
> :0x2e12260 @app=#<Rack::Protection::IPSpoofing:0x2e122c0
> @app=#<Rack::Protection::JsonCsrf:0x2e12350
> @app=#<Rack::Protection::PathTraversal:0x2e123b0 @app=#<Rac
> k::Protection::XSSHeader:0x2e12440 @app=#<Rack::MediaType:0x2e128c0
> @default_layout=:layout, @app=#<struct Sinatra::ExtendedRack
> app=#<Sinatra::ShowExceptions:0
> x2dfe868 @app=#<Rack::Head:0x2dfe880 @app=#<Rack::NullLogger:0x2dfe898
> @app=#<Rack::Protection::FrameOptions:0x2dfe970
> @app=#<Rack::Protection::HttpOrigin:0x2df
> e9e8 @app=#<Rack::Protection::IPSpoofing:0x2dfea48
> @app=#<Rack::Protection::JsonCsrf:0x2dfead8
> @app=#<Rack::Protection::PathTraversal:0x2dfeb68 @app=#<Rack::Pro
> tection::XSSHeader:0x2dfecd0
> @app=#<CIMI::Rabbit::VolumesCollection:0x2df7ec0
> @default_layout=:layout, @app=#<CIMI::Collections::Volumes:0x2df87f0
> @default_layo
> ut=:layout, @app=#<struct Sinatra::ExtendedRack
> app=#<Rack::MethodOverride:0x2df8df0 @app=#<Rack::Head:0x2df9000
> @app=#<Rack::NullLogger:0x2df9048 @app=#<Rack::
> Protection::FrameOptions:0x2df90d8
> @app=#<Rack::Protection::HttpOrigin:0x2df9150
> @app=#<Rack::Protection::IPSpoofing:0x2df91c8
> @app=#<Rack::Protection::JsonCsrf
> :0x2df9228 @app=#<Rack::Protection::PathTraversal:0x2df9288
> @app=#<Rack::Protection::XSSHeader:0x2df9300
> @app=#<Rack::Accept::Context:0x2df9690 @app=#<struct Si
> natra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2dd87c8
> @app=#<Rack::Head:0x2dd87f8 @app=#<Rack::NullLogger:0x2dd8810
> @app=#<Rack::Protection::FrameOptions:
> 0x2dd89a8 @app=#<Rack::Protection::HttpOrigin:0x2dd8b88
> @app=#<Rack::Protection::IPSpoofing:0x2dd8bd0
> @app=#<Rack::Protection::JsonCsrf:0x2dd8e28 @app=#<Rack::P
> rotection::PathTraversal:0x2dd8e88
> @app=#<Rack::Protection::XSSHeader:0x2dd8f18
> @app=#<Rack::MediaType:0x2dd9638 @default_layout=:layout, @app=#<struct
> Sinatra:
> :ExtendedRack app=#<Sinatra::ShowExceptions:0x2db9b50
> @app=#<Rack::Head:0x2db9b80 @app=#<Rack::NullLogger:0x2db9bc8
> @app=#<Rack::Protection::FrameOptions:0x2db9
> c58 @app=#<Rack::Protection::HttpOrigin:0x2db9d00
> @app=#<Rack::Protection::IPSpoofing:0x2db9d60
> @app=#<Rack::Protection::JsonCsrf:0x2db9da8 @app=#<Rack::Protect
> ion::PathTraversal:0x2db9e38
> @app=#<Rack::Protection::XSSHeader:0x2db9ee0
> @app=#<CIMI::Rabbit::VolumeConfigurationsCollection:0x2dba480
> @default_layout=:layout,
>   @app=#<CIMI::Collections::VolumeConfigurations:0x2dba8b8
> @default_layout=:layout, @app=#<struct Sinatra::ExtendedRack
> app=#<Rack::MethodOverride:0x2db3268 @app
> =#<Rack::Head:0x2db3280 @app=#<Rack::NullLogger:0x2db3310
> @app=#<Rack::Protection::FrameOptions:0x2db3448
> @app=#<Rack::Protection::HttpOrigin:0x2db3508 @app=#<R
> ack::Protection::IPSpoofing:0x2db3658
> @app=#<Rack::Protection::JsonCsrf:0x2db36b8
> @app=#<Rack::Protection::PathTraversal:0x2db3760
> @app=#<Rack::Protection::XSSH
> eader:0x2db3868 @app=#<Rack::Accept::Context:0x2db4240 @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2d231d8
> @app=#<Rack::Head:0x2d23250 @
> app=#<Rack::NullLogger:0x2d23268
> @app=#<Rack::Protection::FrameOptions:0x2d234a8
> @app=#<Rack::Protection::HttpOrigin:0x2d23610
> @app=#<Rack::Protection::IPSpoofi
> ng:0x2d236a0 @app=#<Rack::Protection::JsonCsrf:0x2d237c0
> @app=#<Rack::Protection::PathTraversal:0x2d238f8
> @app=#<Rack::Protection::XSSHeader:0x2d23970 @app=#<Ra
> ck::MediaType:0x2d24318 @default_layout=:layout, @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2c75c58
> @app=#<Rack::Head:0x2c75cb8 @app=#<
> Rack::NullLogger:0x2c75ce8
> @app=#<Rack::Protection::FrameOptions:0x2c75e98
> @app=#<Rack::Protection::HttpOrigin:0x2c75fa0
> @app=#<Rack::Protection::IPSpoofing:0x2
> c76060 @app=#<Rack::Protection::JsonCsrf:0x2c76150
> @app=#<Rack::Protection::PathTraversal:0x2c761e0
> @app=#<Rack::Protection::XSSHeader:0x2c76270 @app=#<CIMI::Ra
> bbit::VolumeImagesCollection:0x2c77020 @default_layout=:layout,
> @app=#<CIMI::Collections::VolumeImages:0x2c77578
> @default_layout=:layout, @app=#<struct Sinatra:
> :ExtendedRack app=#<Rack::MethodOverride:0x2c779f8
> @app=#<Rack::Head:0x2c77a10 @app=#<Rack::NullLogger:0x2c77a28
> @app=#<Rack::Protection::FrameOptions:0x2c77ab8
>   @app=#<Rack::Protection::HttpOrigin:0x2c77b18
> @app=#<Rack::Protection::IPSpoofing:0x2c77b78
> @app=#<Rack::Protection::JsonCsrf:0x2c77bf0 @app=#<Rack::Protection
> ::PathTraversal:0x2c77c38 @app=#<Rack::Protection::XSSHeader:0x2c77c98
> @app=#<Rack::Accept::Context:0x2c6c0a0 @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra:
> :ShowExceptions:0x2be9900 @app=#<Rack::Head:0x2be9918
> @app=#<Rack::NullLogger:0x2be9930
> @app=#<Rack::Protection::FrameOptions:0x2be99d8 @app=#<Rack::Protection:
> :HttpOrigin:0x2be9a98 @app=#<Rack::Protection::IPSpoofing:0x2be9b28
> @app=#<Rack::Protection::JsonCsrf:0x2be9b88
> @app=#<Rack::Protection::PathTraversal:0x2be9bd0
>   @app=#<Rack::Protection::XSSHeader:0x2be9c60
> @app=#<Rack::MediaType:0x2bea920 @default_layout=:layout, @app=#<struct
> Sinatra::ExtendedRack app=#<Sinatra::ShowE
> xceptions:0x28340e8 @app=#<Rack::Head:0x2834190
> @app=#<Rack::NullLogger:0x28341a8
> @app=#<Rack::Protection::FrameOptions:0x2834358
> @app=#<Rack::Protection::HttpO
> rigin:0x2834490 @app=#<Rack::Protection::IPSpoofing:0x28345c8
> @app=#<Rack::Protection::JsonCsrf:0x2834670
> @app=#<Rack::Protection::PathTraversal:0x2834700 @app=
> #<Rack::Protection::XSSHeader:0x2834820
> @app=#<CIMI::Rabbit::VolumeTemplatesCollection:0x25f44d8
> @default_layout=:layout, @app=#<CIMI::Collections::VolumeTempla
> tes:0x25e41d0 @default_layout=:layout, @app=#<CIMI::API:0x25e6750
> @default_layout=:layout, @app=nil,
> @template_cache=#<Tilt::Cache:0x25e65d0 @cache={}>>, @templ
> ate_cache=#<Tilt::Cache:0x25e4128 @cache={}>>,
> @template_cache=#<Tilt::Cache:0x25f43d0 @cache={}>>,
> @options={:reaction=>:drop_session, :logging=>true, :message
> =>"Forbidden", :encryptor=>Digest::SHA1, :session_key=>"rack.session",
> :status=>403, :allow_empty_referrer=>true, :html_types=>["text/html",
> "application/xhtml"
> ], :xss_mode=>:block, :nosniff=>true, :except=>[:session_hijacking,
> :remote_token]}>, @options={:reaction=>:drop_session, :logging=>true,
> :message=>"Forbidden",
>   :encryptor=>Digest::SHA1, :session_key=>"rack.session", :status=>403,
> :allow_empty_referrer=>true, :html_types=>["text/html",
> "application/xhtml"], :except=>[:
> session_hijacking, :remote_token]}>, @options={:reaction=>:drop_session,
> :logging=>true, :message=>"Forbidden", :encryptor=>Digest::SHA1,
> :session_key=>"rack.se
> ssion", :status=>403, :allow_empty_referrer=>true,
> :html_types=>["text/html", "application/xhtml"],
> :except=>[:session_hijacking, :remote_token]}>, @options={:r
> eaction=>:drop_session, :logging=>true, :message=>"Forbidden",
> :encryptor=>Digest::SHA1, :session_key=>"rack.session", :status=>403,
> :allow_empty_referrer=>true
> , :html_types=>["text/html", "application/xhtml"],
> :except=>[:session_hijacking, :remote_token]}>,
> @options={:reaction=>:drop_session, :logging=>true, :message=
>> "Forbidden", :encryptor=>Digest::SHA1, :session_key=>"rack.session",
> :status=>403, :allow_empty_referrer=>true, :html_types=>["text/html",
> "application/xhtml"]
> , :except=>[:session_hijacking, :remote_token]}>,
> @options={:reaction=>:drop_session, :logging=>true,
> :message=>"Forbidden", :encryptor=>Digest::SHA1, :session_
> key=>"rack.session", :status=>403, :allow_empty_referrer=>true,
> :html_types=>["text/html", "application/xhtml"],
> :frame_options=>:sameorigin, :except=>[:session
> _hijacking, :remote_token]}>>>, @template=#<ERB:0x28340d0
> @safe_level=nil, @src="#coding:US-ASCII\n_erbout = ''; _erbout.concat
> \"<!DOCTYPE html>\\n<html>\\n<he
> ad>\\n  <meta http-equiv=\\\"Content-Type\\\" content=\\\"text/html;
> charset=utf-8\\\"/>\\n  <title>\"\n\n\n\n; _erbout.concat((h
> exception.class ).to_s); _erbo
> ut.concat \" at \"; _erbout.concat((h path ).to_s); _erbout.concat
> \"</title>\\n\\n  <script type=\\\"text/javascript\\\">\\n  //<!--\\n
> function toggle(id) {\
> \n    var pre  = document.getElementById(\\\"pre-\\\" + id);\\n    var
> post = document.getElementById(\\\"post-\\\" + id);\\n    var context =
> document.getEleme
> ntById(\\\"context-\\\" + id);\\n\\n    if (pre.style.display ==
> 'block') {\\n      pre.style.display = 'none';\\n
> post.style.display = 'none';\\n      con
> text.style.background = \\\"none\\\";\\n    } else {\\n
> pre.style.display = 'block';\\n      post.style.display = 'block';\\n
> context.style.background
>   = \\\"#fffed9\\\";\\n    }\\n  }\\n\\n  function toggleBacktrace(){\\n
> var bt = document.getElementById(\\\"backtrace\\\");\\n    var toggler =
> document.get
> ElementById(\\\"expando\\\");\\n\\n    if (bt.className == 'condensed')
> {\\n      bt.className = 'expanded';\\n      toggler.innerHTML =
> \\\"(condense)\\\";\\n
>     } else {\\n      bt.className = 'condensed';\\n
> toggler.innerHTML = \\\"(expand)\\\";\\n    }\\n  }\\n  //-->\\n
> </script>\\n\\n<style type=\\\"text/cs
> s\\\" media=\\\"screen\\\">\\n  *                   {margin: 0; padding:
> 0; border: 0; outline: 0;}\\n  div.clear           {clear: both;}\\n
> body
>     {background: #EEEEEE; margin: 0; padding: 0;\\n
> font-family: 'Lucida Grande', 'Lucida Sans Unicode',\\n
> 'Garuda';
> <snip>
>
> Plus a few more pages of that kind of output.
> Looking at the first part, " The CIMI::Model::MachineConfigurationRef
> must be initialized using Hash or CIMI::Resource (is
> #<CIMI::Service::MachineConfiguration", what does it mean?
>
> In my code, system templates has componentDescriptors which is assigned
> an array of hashes.
> One hash contains e.g.:
>
>              {
>                :name             => desc['vsysdescriptorName'][0],
>                :description      => '',
>                :type             =>
> "http://schemas.dmtf.org/cimi/1/Machine",
>                :machine_template => CIMI::Model::MachineTemplate.new(
>                  :name             => vserver['vserverName'][0],
>                  :description      => '',
>                  :machine_config   =>
> CIMI::Service::MachineConfiguration.find(vserver['vserverType'][0],
> context),
>                  :machine_image    => { :href =>
> context.machine_image_url(vserver['diskimageId'][0]) },
>                  :volume_templates => volume_templates
>                )
>              }
>
> Is the return type of MachineConfiguration.find incorrect?
> I tried commenting out that line, but that led to the following error:
>
> E, [2013-03-26T22:54:23.194324 #4960] ERROR -- 500: [NoMethodError]
> undefined method `[]' for nil:NilClass
>
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:325:in
> `to_xml'
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444:in
> `block in to_xml'
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444:in
> `each'
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444:in
> `to_xml'
> d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:158:in
> `convert_to_xml'
> ...
>
> Cheers,
> Dies Koper
>
>
>> -----Original Message-----
>> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
>> Sent: Friday, 22 March 2013 11:36 PM
>> To: dev@deltacloud.apache.org
>> Subject: [PATCH core] CIMI: Added more descriptive CIMI schema errors
>>
>> From: Michal Fojtik <mf...@redhat.com>
>>
>> * Wrong attribute values are not reported correctly
>>
>> Signed-off-by: Michal fojtik <mf...@redhat.com>
>> ---
>>   server/lib/cimi/models/resource.rb     |   5 ++
>>   server/lib/cimi/models/schema.rb       |  88 +++++++++++++++++---
>>   server/tests/cimi/model/errors_spec.rb | 141
>> +++++++++++++++++++++++++++++++++
>>   3 files changed, 225 insertions(+), 9 deletions(-)
>>   create mode 100644 server/tests/cimi/model/errors_spec.rb
>>
>> diff --git a/server/lib/cimi/models/resource.rb
>> b/server/lib/cimi/models/resource.rb
>> index 9f5bb66..2b3de42 100644
>> --- a/server/lib/cimi/models/resource.rb
>> +++ b/server/lib/cimi/models/resource.rb
>> @@ -36,6 +36,11 @@ module CIMI
>>         #
>>         def initialize(values = {})
>>           names = self.class.schema.attribute_names
>> +        unless values.is_a?(::Hash) or
>> values.kind_of?(CIMI::Model::Resource)
>> +          raise CIMI::Model::Schema::InvalidAttributeValue.new(
>> +            "The #{self.class} must be initialized using Hash or
>> CIMI::Resource (is #{values.inspect})"
>> +          )
>> +        end
>>           @select_attrs = values[:select_attr_list] || []
>>           # Make sure we always have the :id of the entity even
>>           # the $select parameter is used and :id is filtered out
>> diff --git a/server/lib/cimi/models/schema.rb
>> b/server/lib/cimi/models/schema.rb
>> index 5a2049b..3a9d54c 100644
>> --- a/server/lib/cimi/models/schema.rb
>> +++ b/server/lib/cimi/models/schema.rb
>> @@ -19,6 +19,8 @@ require_relative "../../deltacloud/core_ext"
>>   # The smarts of converting from XML and JSON into internal objects
>>   class CIMI::Model::Schema
>>
>> +  class InvalidAttributeValue < StandardError; end
>> +
>>     #
>>     # Attributes describe how we extract values from XML/JSON
>>     #
>> @@ -60,6 +62,12 @@ class CIMI::Model::Schema
>>       def valid?(value)
>>         !value.nil? and !value.to_s.empty?
>>       end
>> +
>> +    def report_error(message)
>> +      message = "The `#{@name}` attribute #{message}"
>> +      raise CIMI::Model::Schema::InvalidAttributeValue.new(message)
>> +    end
>> +
>>     end
>>
>>     class Scalar < Attribute
>> @@ -190,6 +198,7 @@ class CIMI::Model::Schema
>>           @klass = CIMI::Model::const_get(refname)
>>         else
>>           @klass = Class.new(opts[:class]) do |m|
>> +          def initialize(values={}); super(values); end
>>             scalar :href
>>           end
>>           CIMI::Model::const_set(refname, @klass)
>> @@ -207,6 +216,39 @@ class CIMI::Model::Schema
>>           a.valid?(value.send(a.name))
>>         }
>>       end
>> +
>> +    def to_xml(model, xml)
>> +      super if valid_ref?(model[name])
>> +    end
>> +
>> +    def to_json(model, json)
>> +      super if valid_ref?(model[name])
>> +    end
>> +
>> +    private
>> +
>> +    def valid_ref?(value)
>> +      return true if value.is_a?(::Hash) or
>> value.kind_of?(CIMI::Model::Resource) or value.nil?
>> +      report_error "must be a Hash or CIMI::Resource (is #{value})"
>> +    end
>> +  end
>> +
>> +  class Href < CIMI::Model::Schema::Struct
>> +
>> +    def to_xml(model, xml)
>> +      super if valid_href?(model[name])
>> +    end
>> +
>> +    def to_json(model, json)
>> +      super if valid_href?(model[name])
>> +    end
>> +
>> +    private
>> +
>> +    def valid_href?(value)
>> +      return true if value.is_a?(::Hash) or value.is_a?(struct) or
>> value.nil?
>> +      report_error "must be a Hash{:href} or Struct#href (is
> #{value})"
>> +    end
>>     end
>>
>>     class Array < Attribute
>> @@ -239,13 +281,25 @@ class CIMI::Model::Schema
>>       end
>>
>>       def to_xml(model, xml)
>> -      ary = (model[name] || []).map { |elt|
>> @struct.convert_to_xml(elt) }
>> -      xml[xml_name] = ary unless ary.empty?
>> +      return unless model[name]
>> +      if is_valid_array? model[name]
>> +        ary = model[name].map { |elt| @struct.convert_to_xml(elt) }
>> +        xml[xml_name] = ary unless ary.empty?
>> +      end
>>       end
>>
>>       def to_json(model, json)
>> -      ary = (model[name] || []).map { |elt|
>> @struct.convert_to_json(elt) }
>> -      json[json_name] = ary unless ary.empty?
>> +      return unless model[name]
>> +      if is_valid_array? model[name]
>> +        ary = model[name].map { |elt| @struct.convert_to_json(elt) }
>> +        json[json_name] = ary unless ary.empty?
>> +      end
>> +    end
>> +
>> +    private
>> +
>> +    def is_valid_array?(value)
>> +      value.is_a?(::Array) ? true : report_error('must be a valid
>> Array')
>>       end
>>     end
>>
>> @@ -268,15 +322,26 @@ class CIMI::Model::Schema
>>       end
>>
>>       def to_xml(model, xml)
>> -      ary = (model[name] || {}).map { |k, v| { "key" => k, "content"
>> => v } }
>> -      xml[xml_name] = ary unless ary.empty?
>> +      return unless model[name]
>> +      if is_valid_hash? model[name]
>> +        ary = (model[name]).map { |k, v| { "key" => k, "content" => v
> } }
>> +        xml[xml_name] = ary unless ary.empty?
>> +      end
>>       end
>>
>>       def to_json(model, json)
>> -      if model[name] && ! model[name].empty?
>> -        json[json_name] = model[name]
>> +      return unless model[name]
>> +      if is_valid_hash? model[name]
>> +        json[json_name] = model[name] unless model[name].empty?
>>         end
>>       end
>> +
>> +    private
>> +
>> +    def is_valid_hash?(value)
>> +      value.is_a?(::Hash) ? true : report_error('must be a valid
> Hash')
>> +    end
>> +
>>     end
>>
>>     class Collection < Attribute
>> @@ -413,7 +478,12 @@ class CIMI::Model::Schema
>>
>>       def href(*args)
>>         opts = args.extract_opts!
>> -      args.each { |arg| struct(arg, opts) { scalar :href, :required
> =>
>> opts[:required] } }
>> +      #args.each { |arg| struct(arg, opts) { scalar :href, :required
>> => opts[:required] } }
>> +      args.each { |arg|
>> +        add_attributes!([arg, opts], Href) {
>> +          scalar :href, :required => opts[:required]
>> +        }
>> +      }
>>       end
>>
>>       def text(*args)
>> diff --git a/server/tests/cimi/model/errors_spec.rb
>> b/server/tests/cimi/model/errors_spec.rb
>> new file mode 100644
>> index 0000000..fac38fb
>> --- /dev/null
>> +++ b/server/tests/cimi/model/errors_spec.rb
>> @@ -0,0 +1,141 @@
>> +# Licensed to the Apache Software Foundation (ASF) under one or more
>> +# contributor license agreements.  See the NOTICE file distributed
> with
>> +# this work for additional information regarding copyright ownership.
>> The
>> +# ASF licenses this file to you under the Apache License, Version 2.0
>> (the
>> +# "License"); you may not use this file except in compliance with the
>> +# License.  You may obtain a copy of the License at
>> +#
>> +#       http://www.apache.org/licenses/LICENSE-2.0
>> +#
>> +# Unless required by applicable law or agreed to in writing, software
>> +# distributed under the License is distributed on an "AS IS" BASIS,
>> WITHOUT
>> +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See
>> the
>> +# License for the specific language governing permissions and
>> limitations
>> +# under the License.
>> +#
>> +require 'rubygems'
>> +require 'require_relative' if RUBY_VERSION < '1.9'
>> +
>> +require_relative '../spec_helper.rb' if require 'minitest/autorun'
>> +
>> +describe 'Schema' do
>> +  describe 'Hash attributes' do
>> +
>> +    it 'should not report error when attribute is set properly' do
>> +      machine = CIMI::Model::MachineTemplate.new(:property => {})
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +
>> +    it 'should not report error when attribute is nil' do
>> +      machine = CIMI::Model::MachineTemplate.new(:property => nil)
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +
>> +    it 'should not report error when attribute is not set' do
>> +      machine = CIMI::Model::MachineTemplate.new
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +
>> +    it 'should report invalid value for Hash attribute when set to
>> String' do
>> +      machine = CIMI::Model::MachineTemplate.new(:property => '')
>> +      lambda { machine.to_xml }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda { machine.to_json }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should report invalid value for Hash attribute when set to
> Array'
>> do
>> +      machine = CIMI::Model::MachineTemplate.new(:property => [])
>> +      lambda { machine.to_xml }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda { machine.to_json }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +  end
>> +
>> +  describe 'Array attributes' do
>> +
>> +    it 'should report invalid value when set to Hash' do
>> +      machine = CIMI::Model::MachineTemplate.new(:volumes => {} )
>> +      lambda { machine.to_xml }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda { machine.to_json }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should report invalid value when set to String' do
>> +      machine = CIMI::Model::MachineTemplate.new(:volumes => '' )
>> +      lambda { machine.to_xml }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda { machine.to_json }.must_raise
>> CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should not report error when attribute is set properly' do
>> +      machine = CIMI::Model::MachineTemplate.new(:volumes => [])
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +
>> +    it 'should not report error when attribute is nil' do
>> +      machine = CIMI::Model::MachineTemplate.new(:volumes => nil)
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +
>> +    end
>> +
>> +    it 'should not report error when attribute is not set' do
>> +      machine = CIMI::Model::MachineTemplate.new
>> +      machine.to_xml.must_be_kind_of String
>> +      machine.to_json.must_be_kind_of String
>> +    end
>> +  end
>> +
>> +  describe 'Ref attributes' do
>> +
>> +    it 'should report error when initialized using Array' do
>> +      lambda {
>> +        CIMI::Model::MachineTemplate.new(:machine_config => [])
>> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should report error when initialized using String' do
>> +      lambda {
>> +        CIMI::Model::MachineTemplate.new(:machine_config => '')
>> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'could be initialized by the Hash value' do
>> +      machine = CIMI::Model::MachineTemplate.new(:machine_config =>
>> { :href => 'http://localhost/1' })
>> +      machine.machine_config.must_be_instance_of
>> CIMI::Model::MachineConfigurationRef
>> +      machine.machine_config.href.must_equal 'http://localhost/1'
>> +      machine.to_xml.must_be_instance_of String
>> +      machine.to_json.must_be_instance_of String
>> +    end
>> +
>> +    it 'could be initialized by the Ref value' do
>> +      machine = CIMI::Model::MachineTemplate.new(:machine_config =>
>> CIMI::Model::MachineConfigurationRef.new(:href =>
>> 'http://localhost/1'))
>> +      machine.machine_config.must_be_instance_of
>> CIMI::Model::MachineConfigurationRef
>> +      machine.machine_config.href.must_equal 'http://localhost/1'
>> +      machine.to_xml.must_be_instance_of String
>> +      machine.to_json.must_be_instance_of String
>> +    end
>> +
>> +  end
>> +
>> +  describe 'Href attributes' do
>> +
>> +    it 'should report error when value is not a Hash' do
>> +      machine = CIMI::Model::Machine.new(:machine_image => '')
>> +      lambda {
>> +        machine.to_xml
>> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
>> +      lambda {
>> +        machine.to_json
>> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
>> +    end
>> +
>> +    it 'should not report error when initialized correctly' do
>> +      machine = CIMI::Model::Machine.new( :machine_image => { :href
> =>
>> 'test' })
>> +      machine.to_xml.must_be_instance_of String
>> +      machine.to_json.must_be_instance_of String
>> +    end
>> +  end
>> +
>> +end
>> --
>> 1.8.1.4
>>
>
>


-- 

Michal Fojtik <mf...@redhat.com>
Deltacloud API, CloudForms

RE: [PATCH core] CIMI: Added more descriptive CIMI schema errors

Posted by "Koper, Dies" <di...@fast.au.fujitsu.com>.
Hi Michal,

I tried this patch and it gave the following error when I tried to
retrieve system templates from the fgcp:

E, [2013-03-26T22:51:05.100517 #1940] ERROR -- 502:
[Deltacloud::Exceptions::ProviderError] The
CIMI::Model::MachineConfigurationRef must be initialized using Hash or
CIMI::Resource (is #<CIMI::Service::MachineConfiguration:0x411ae30
@model=#<CIMI::Model::MachineConfiguration:0x411ad70 @select_attrs=[],
@base_id="http:
//localhost:3001/cimi/machine_configurations/economy",
@attribute_values={:id=>"http://localhost:3001/cimi/machine_configuratio
ns/economy", :name=>"economy", :d
escription=>"Machine Configuration with 1782579 KiB of memory and 1
CPU", :created=>"2013-03-26T22:51:04+11:00", :updated=>nil,
:property=>nil, :cpu=>"1", :memo
ry=>1782579, :disks=>nil, :operations=>nil}>,
@context=#<CIMI::Collections::SystemTemplates:0x2de9bf8
@default_layout=:layout, @app=#<struct Sinatra::ExtendedRa
ck app=#<Rack::MethodOverride:0x4115580 @app=#<Rack::Head:0x4115598
@app=#<Rack::NullLogger:0x41155c8
@app=#<Rack::Protection::FrameOptions:0x4115670 @app=#<Rac
k::Protection::HttpOrigin:0x41156b8
@app=#<Rack::Protection::IPSpoofing:0x4115718
@app=#<Rack::Protection::JsonCsrf:0x4115760
@app=#<Rack::Protection::PathTrave
rsal:0x41157a8 @app=#<Rack::Protection::XSSHeader:0x4115820
@app=#<Rack::Accept::Context:0x4115c88 @app=#<struct
Sinatra::ExtendedRack app=#<Sinatra::ShowExcept
ions:0x2e11f90 @app=#<Rack::Head:0x2e11fc0
@app=#<Rack::NullLogger:0x2e11fd8
@app=#<Rack::Protection::FrameOptions:0x2e12140
@app=#<Rack::Protection::HttpOrigin
:0x2e12260 @app=#<Rack::Protection::IPSpoofing:0x2e122c0
@app=#<Rack::Protection::JsonCsrf:0x2e12350
@app=#<Rack::Protection::PathTraversal:0x2e123b0 @app=#<Rac
k::Protection::XSSHeader:0x2e12440 @app=#<Rack::MediaType:0x2e128c0
@default_layout=:layout, @app=#<struct Sinatra::ExtendedRack
app=#<Sinatra::ShowExceptions:0
x2dfe868 @app=#<Rack::Head:0x2dfe880 @app=#<Rack::NullLogger:0x2dfe898
@app=#<Rack::Protection::FrameOptions:0x2dfe970
@app=#<Rack::Protection::HttpOrigin:0x2df
e9e8 @app=#<Rack::Protection::IPSpoofing:0x2dfea48
@app=#<Rack::Protection::JsonCsrf:0x2dfead8
@app=#<Rack::Protection::PathTraversal:0x2dfeb68 @app=#<Rack::Pro
tection::XSSHeader:0x2dfecd0
@app=#<CIMI::Rabbit::VolumesCollection:0x2df7ec0
@default_layout=:layout, @app=#<CIMI::Collections::Volumes:0x2df87f0
@default_layo
ut=:layout, @app=#<struct Sinatra::ExtendedRack
app=#<Rack::MethodOverride:0x2df8df0 @app=#<Rack::Head:0x2df9000
@app=#<Rack::NullLogger:0x2df9048 @app=#<Rack::
Protection::FrameOptions:0x2df90d8
@app=#<Rack::Protection::HttpOrigin:0x2df9150
@app=#<Rack::Protection::IPSpoofing:0x2df91c8
@app=#<Rack::Protection::JsonCsrf
:0x2df9228 @app=#<Rack::Protection::PathTraversal:0x2df9288
@app=#<Rack::Protection::XSSHeader:0x2df9300
@app=#<Rack::Accept::Context:0x2df9690 @app=#<struct Si
natra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2dd87c8
@app=#<Rack::Head:0x2dd87f8 @app=#<Rack::NullLogger:0x2dd8810
@app=#<Rack::Protection::FrameOptions:
0x2dd89a8 @app=#<Rack::Protection::HttpOrigin:0x2dd8b88
@app=#<Rack::Protection::IPSpoofing:0x2dd8bd0
@app=#<Rack::Protection::JsonCsrf:0x2dd8e28 @app=#<Rack::P
rotection::PathTraversal:0x2dd8e88
@app=#<Rack::Protection::XSSHeader:0x2dd8f18
@app=#<Rack::MediaType:0x2dd9638 @default_layout=:layout, @app=#<struct
Sinatra:
:ExtendedRack app=#<Sinatra::ShowExceptions:0x2db9b50
@app=#<Rack::Head:0x2db9b80 @app=#<Rack::NullLogger:0x2db9bc8
@app=#<Rack::Protection::FrameOptions:0x2db9
c58 @app=#<Rack::Protection::HttpOrigin:0x2db9d00
@app=#<Rack::Protection::IPSpoofing:0x2db9d60
@app=#<Rack::Protection::JsonCsrf:0x2db9da8 @app=#<Rack::Protect
ion::PathTraversal:0x2db9e38
@app=#<Rack::Protection::XSSHeader:0x2db9ee0
@app=#<CIMI::Rabbit::VolumeConfigurationsCollection:0x2dba480
@default_layout=:layout,
 @app=#<CIMI::Collections::VolumeConfigurations:0x2dba8b8
@default_layout=:layout, @app=#<struct Sinatra::ExtendedRack
app=#<Rack::MethodOverride:0x2db3268 @app
=#<Rack::Head:0x2db3280 @app=#<Rack::NullLogger:0x2db3310
@app=#<Rack::Protection::FrameOptions:0x2db3448
@app=#<Rack::Protection::HttpOrigin:0x2db3508 @app=#<R
ack::Protection::IPSpoofing:0x2db3658
@app=#<Rack::Protection::JsonCsrf:0x2db36b8
@app=#<Rack::Protection::PathTraversal:0x2db3760
@app=#<Rack::Protection::XSSH
eader:0x2db3868 @app=#<Rack::Accept::Context:0x2db4240 @app=#<struct
Sinatra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2d231d8
@app=#<Rack::Head:0x2d23250 @
app=#<Rack::NullLogger:0x2d23268
@app=#<Rack::Protection::FrameOptions:0x2d234a8
@app=#<Rack::Protection::HttpOrigin:0x2d23610
@app=#<Rack::Protection::IPSpoofi
ng:0x2d236a0 @app=#<Rack::Protection::JsonCsrf:0x2d237c0
@app=#<Rack::Protection::PathTraversal:0x2d238f8
@app=#<Rack::Protection::XSSHeader:0x2d23970 @app=#<Ra
ck::MediaType:0x2d24318 @default_layout=:layout, @app=#<struct
Sinatra::ExtendedRack app=#<Sinatra::ShowExceptions:0x2c75c58
@app=#<Rack::Head:0x2c75cb8 @app=#<
Rack::NullLogger:0x2c75ce8
@app=#<Rack::Protection::FrameOptions:0x2c75e98
@app=#<Rack::Protection::HttpOrigin:0x2c75fa0
@app=#<Rack::Protection::IPSpoofing:0x2
c76060 @app=#<Rack::Protection::JsonCsrf:0x2c76150
@app=#<Rack::Protection::PathTraversal:0x2c761e0
@app=#<Rack::Protection::XSSHeader:0x2c76270 @app=#<CIMI::Ra
bbit::VolumeImagesCollection:0x2c77020 @default_layout=:layout,
@app=#<CIMI::Collections::VolumeImages:0x2c77578
@default_layout=:layout, @app=#<struct Sinatra:
:ExtendedRack app=#<Rack::MethodOverride:0x2c779f8
@app=#<Rack::Head:0x2c77a10 @app=#<Rack::NullLogger:0x2c77a28
@app=#<Rack::Protection::FrameOptions:0x2c77ab8
 @app=#<Rack::Protection::HttpOrigin:0x2c77b18
@app=#<Rack::Protection::IPSpoofing:0x2c77b78
@app=#<Rack::Protection::JsonCsrf:0x2c77bf0 @app=#<Rack::Protection
::PathTraversal:0x2c77c38 @app=#<Rack::Protection::XSSHeader:0x2c77c98
@app=#<Rack::Accept::Context:0x2c6c0a0 @app=#<struct
Sinatra::ExtendedRack app=#<Sinatra:
:ShowExceptions:0x2be9900 @app=#<Rack::Head:0x2be9918
@app=#<Rack::NullLogger:0x2be9930
@app=#<Rack::Protection::FrameOptions:0x2be99d8 @app=#<Rack::Protection:
:HttpOrigin:0x2be9a98 @app=#<Rack::Protection::IPSpoofing:0x2be9b28
@app=#<Rack::Protection::JsonCsrf:0x2be9b88
@app=#<Rack::Protection::PathTraversal:0x2be9bd0
 @app=#<Rack::Protection::XSSHeader:0x2be9c60
@app=#<Rack::MediaType:0x2bea920 @default_layout=:layout, @app=#<struct
Sinatra::ExtendedRack app=#<Sinatra::ShowE
xceptions:0x28340e8 @app=#<Rack::Head:0x2834190
@app=#<Rack::NullLogger:0x28341a8
@app=#<Rack::Protection::FrameOptions:0x2834358
@app=#<Rack::Protection::HttpO
rigin:0x2834490 @app=#<Rack::Protection::IPSpoofing:0x28345c8
@app=#<Rack::Protection::JsonCsrf:0x2834670
@app=#<Rack::Protection::PathTraversal:0x2834700 @app=
#<Rack::Protection::XSSHeader:0x2834820
@app=#<CIMI::Rabbit::VolumeTemplatesCollection:0x25f44d8
@default_layout=:layout, @app=#<CIMI::Collections::VolumeTempla
tes:0x25e41d0 @default_layout=:layout, @app=#<CIMI::API:0x25e6750
@default_layout=:layout, @app=nil,
@template_cache=#<Tilt::Cache:0x25e65d0 @cache={}>>, @templ
ate_cache=#<Tilt::Cache:0x25e4128 @cache={}>>,
@template_cache=#<Tilt::Cache:0x25f43d0 @cache={}>>,
@options={:reaction=>:drop_session, :logging=>true, :message
=>"Forbidden", :encryptor=>Digest::SHA1, :session_key=>"rack.session",
:status=>403, :allow_empty_referrer=>true, :html_types=>["text/html",
"application/xhtml"
], :xss_mode=>:block, :nosniff=>true, :except=>[:session_hijacking,
:remote_token]}>, @options={:reaction=>:drop_session, :logging=>true,
:message=>"Forbidden",
 :encryptor=>Digest::SHA1, :session_key=>"rack.session", :status=>403,
:allow_empty_referrer=>true, :html_types=>["text/html",
"application/xhtml"], :except=>[:
session_hijacking, :remote_token]}>, @options={:reaction=>:drop_session,
:logging=>true, :message=>"Forbidden", :encryptor=>Digest::SHA1,
:session_key=>"rack.se
ssion", :status=>403, :allow_empty_referrer=>true,
:html_types=>["text/html", "application/xhtml"],
:except=>[:session_hijacking, :remote_token]}>, @options={:r
eaction=>:drop_session, :logging=>true, :message=>"Forbidden",
:encryptor=>Digest::SHA1, :session_key=>"rack.session", :status=>403,
:allow_empty_referrer=>true
, :html_types=>["text/html", "application/xhtml"],
:except=>[:session_hijacking, :remote_token]}>,
@options={:reaction=>:drop_session, :logging=>true, :message=
>"Forbidden", :encryptor=>Digest::SHA1, :session_key=>"rack.session",
:status=>403, :allow_empty_referrer=>true, :html_types=>["text/html",
"application/xhtml"]
, :except=>[:session_hijacking, :remote_token]}>,
@options={:reaction=>:drop_session, :logging=>true,
:message=>"Forbidden", :encryptor=>Digest::SHA1, :session_
key=>"rack.session", :status=>403, :allow_empty_referrer=>true,
:html_types=>["text/html", "application/xhtml"],
:frame_options=>:sameorigin, :except=>[:session
_hijacking, :remote_token]}>>>, @template=#<ERB:0x28340d0
@safe_level=nil, @src="#coding:US-ASCII\n_erbout = ''; _erbout.concat
\"<!DOCTYPE html>\\n<html>\\n<he
ad>\\n  <meta http-equiv=\\\"Content-Type\\\" content=\\\"text/html;
charset=utf-8\\\"/>\\n  <title>\"\n\n\n\n; _erbout.concat((h
exception.class ).to_s); _erbo
ut.concat \" at \"; _erbout.concat((h path ).to_s); _erbout.concat
\"</title>\\n\\n  <script type=\\\"text/javascript\\\">\\n  //<!--\\n
function toggle(id) {\
\n    var pre  = document.getElementById(\\\"pre-\\\" + id);\\n    var
post = document.getElementById(\\\"post-\\\" + id);\\n    var context =
document.getEleme
ntById(\\\"context-\\\" + id);\\n\\n    if (pre.style.display ==
'block') {\\n      pre.style.display = 'none';\\n
post.style.display = 'none';\\n      con
text.style.background = \\\"none\\\";\\n    } else {\\n
pre.style.display = 'block';\\n      post.style.display = 'block';\\n
context.style.background
 = \\\"#fffed9\\\";\\n    }\\n  }\\n\\n  function toggleBacktrace(){\\n
var bt = document.getElementById(\\\"backtrace\\\");\\n    var toggler =
document.get
ElementById(\\\"expando\\\");\\n\\n    if (bt.className == 'condensed')
{\\n      bt.className = 'expanded';\\n      toggler.innerHTML =
\\\"(condense)\\\";\\n
   } else {\\n      bt.className = 'condensed';\\n
toggler.innerHTML = \\\"(expand)\\\";\\n    }\\n  }\\n  //-->\\n
</script>\\n\\n<style type=\\\"text/cs
s\\\" media=\\\"screen\\\">\\n  *                   {margin: 0; padding:
0; border: 0; outline: 0;}\\n  div.clear           {clear: both;}\\n
body
   {background: #EEEEEE; margin: 0; padding: 0;\\n
font-family: 'Lucida Grande', 'Lucida Sans Unicode',\\n
'Garuda';
<snip>

Plus a few more pages of that kind of output.
Looking at the first part, " The CIMI::Model::MachineConfigurationRef
must be initialized using Hash or CIMI::Resource (is
#<CIMI::Service::MachineConfiguration", what does it mean?

In my code, system templates has componentDescriptors which is assigned
an array of hashes.
One hash contains e.g.:

            {
              :name             => desc['vsysdescriptorName'][0],
              :description      => '',
              :type             =>
"http://schemas.dmtf.org/cimi/1/Machine",
              :machine_template => CIMI::Model::MachineTemplate.new(
                :name             => vserver['vserverName'][0],
                :description      => '',
                :machine_config   =>
CIMI::Service::MachineConfiguration.find(vserver['vserverType'][0],
context),
                :machine_image    => { :href =>
context.machine_image_url(vserver['diskimageId'][0]) },
                :volume_templates => volume_templates
              )
            }

Is the return type of MachineConfiguration.find incorrect?
I tried commenting out that line, but that led to the following error:

E, [2013-03-26T22:54:23.194324 #4960] ERROR -- 500: [NoMethodError]
undefined method `[]' for nil:NilClass

d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:325:in
`to_xml'
d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444:in
`block in to_xml'
d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444:in
`each'
d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:444:in
`to_xml'
d:/sources/OSS/cloud/deltacloud/server/lib/cimi/models/schema.rb:158:in
`convert_to_xml'
...

Cheers,
Dies Koper


> -----Original Message-----
> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
> Sent: Friday, 22 March 2013 11:36 PM
> To: dev@deltacloud.apache.org
> Subject: [PATCH core] CIMI: Added more descriptive CIMI schema errors
> 
> From: Michal Fojtik <mf...@redhat.com>
> 
> * Wrong attribute values are not reported correctly
> 
> Signed-off-by: Michal fojtik <mf...@redhat.com>
> ---
>  server/lib/cimi/models/resource.rb     |   5 ++
>  server/lib/cimi/models/schema.rb       |  88 +++++++++++++++++---
>  server/tests/cimi/model/errors_spec.rb | 141
> +++++++++++++++++++++++++++++++++
>  3 files changed, 225 insertions(+), 9 deletions(-)
>  create mode 100644 server/tests/cimi/model/errors_spec.rb
> 
> diff --git a/server/lib/cimi/models/resource.rb
> b/server/lib/cimi/models/resource.rb
> index 9f5bb66..2b3de42 100644
> --- a/server/lib/cimi/models/resource.rb
> +++ b/server/lib/cimi/models/resource.rb
> @@ -36,6 +36,11 @@ module CIMI
>        #
>        def initialize(values = {})
>          names = self.class.schema.attribute_names
> +        unless values.is_a?(::Hash) or
> values.kind_of?(CIMI::Model::Resource)
> +          raise CIMI::Model::Schema::InvalidAttributeValue.new(
> +            "The #{self.class} must be initialized using Hash or
> CIMI::Resource (is #{values.inspect})"
> +          )
> +        end
>          @select_attrs = values[:select_attr_list] || []
>          # Make sure we always have the :id of the entity even
>          # the $select parameter is used and :id is filtered out
> diff --git a/server/lib/cimi/models/schema.rb
> b/server/lib/cimi/models/schema.rb
> index 5a2049b..3a9d54c 100644
> --- a/server/lib/cimi/models/schema.rb
> +++ b/server/lib/cimi/models/schema.rb
> @@ -19,6 +19,8 @@ require_relative "../../deltacloud/core_ext"
>  # The smarts of converting from XML and JSON into internal objects
>  class CIMI::Model::Schema
> 
> +  class InvalidAttributeValue < StandardError; end
> +
>    #
>    # Attributes describe how we extract values from XML/JSON
>    #
> @@ -60,6 +62,12 @@ class CIMI::Model::Schema
>      def valid?(value)
>        !value.nil? and !value.to_s.empty?
>      end
> +
> +    def report_error(message)
> +      message = "The `#{@name}` attribute #{message}"
> +      raise CIMI::Model::Schema::InvalidAttributeValue.new(message)
> +    end
> +
>    end
> 
>    class Scalar < Attribute
> @@ -190,6 +198,7 @@ class CIMI::Model::Schema
>          @klass = CIMI::Model::const_get(refname)
>        else
>          @klass = Class.new(opts[:class]) do |m|
> +          def initialize(values={}); super(values); end
>            scalar :href
>          end
>          CIMI::Model::const_set(refname, @klass)
> @@ -207,6 +216,39 @@ class CIMI::Model::Schema
>          a.valid?(value.send(a.name))
>        }
>      end
> +
> +    def to_xml(model, xml)
> +      super if valid_ref?(model[name])
> +    end
> +
> +    def to_json(model, json)
> +      super if valid_ref?(model[name])
> +    end
> +
> +    private
> +
> +    def valid_ref?(value)
> +      return true if value.is_a?(::Hash) or
> value.kind_of?(CIMI::Model::Resource) or value.nil?
> +      report_error "must be a Hash or CIMI::Resource (is #{value})"
> +    end
> +  end
> +
> +  class Href < CIMI::Model::Schema::Struct
> +
> +    def to_xml(model, xml)
> +      super if valid_href?(model[name])
> +    end
> +
> +    def to_json(model, json)
> +      super if valid_href?(model[name])
> +    end
> +
> +    private
> +
> +    def valid_href?(value)
> +      return true if value.is_a?(::Hash) or value.is_a?(struct) or
> value.nil?
> +      report_error "must be a Hash{:href} or Struct#href (is
#{value})"
> +    end
>    end
> 
>    class Array < Attribute
> @@ -239,13 +281,25 @@ class CIMI::Model::Schema
>      end
> 
>      def to_xml(model, xml)
> -      ary = (model[name] || []).map { |elt|
> @struct.convert_to_xml(elt) }
> -      xml[xml_name] = ary unless ary.empty?
> +      return unless model[name]
> +      if is_valid_array? model[name]
> +        ary = model[name].map { |elt| @struct.convert_to_xml(elt) }
> +        xml[xml_name] = ary unless ary.empty?
> +      end
>      end
> 
>      def to_json(model, json)
> -      ary = (model[name] || []).map { |elt|
> @struct.convert_to_json(elt) }
> -      json[json_name] = ary unless ary.empty?
> +      return unless model[name]
> +      if is_valid_array? model[name]
> +        ary = model[name].map { |elt| @struct.convert_to_json(elt) }
> +        json[json_name] = ary unless ary.empty?
> +      end
> +    end
> +
> +    private
> +
> +    def is_valid_array?(value)
> +      value.is_a?(::Array) ? true : report_error('must be a valid
> Array')
>      end
>    end
> 
> @@ -268,15 +322,26 @@ class CIMI::Model::Schema
>      end
> 
>      def to_xml(model, xml)
> -      ary = (model[name] || {}).map { |k, v| { "key" => k, "content"
> => v } }
> -      xml[xml_name] = ary unless ary.empty?
> +      return unless model[name]
> +      if is_valid_hash? model[name]
> +        ary = (model[name]).map { |k, v| { "key" => k, "content" => v
} }
> +        xml[xml_name] = ary unless ary.empty?
> +      end
>      end
> 
>      def to_json(model, json)
> -      if model[name] && ! model[name].empty?
> -        json[json_name] = model[name]
> +      return unless model[name]
> +      if is_valid_hash? model[name]
> +        json[json_name] = model[name] unless model[name].empty?
>        end
>      end
> +
> +    private
> +
> +    def is_valid_hash?(value)
> +      value.is_a?(::Hash) ? true : report_error('must be a valid
Hash')
> +    end
> +
>    end
> 
>    class Collection < Attribute
> @@ -413,7 +478,12 @@ class CIMI::Model::Schema
> 
>      def href(*args)
>        opts = args.extract_opts!
> -      args.each { |arg| struct(arg, opts) { scalar :href, :required
=>
> opts[:required] } }
> +      #args.each { |arg| struct(arg, opts) { scalar :href, :required
> => opts[:required] } }
> +      args.each { |arg|
> +        add_attributes!([arg, opts], Href) {
> +          scalar :href, :required => opts[:required]
> +        }
> +      }
>      end
> 
>      def text(*args)
> diff --git a/server/tests/cimi/model/errors_spec.rb
> b/server/tests/cimi/model/errors_spec.rb
> new file mode 100644
> index 0000000..fac38fb
> --- /dev/null
> +++ b/server/tests/cimi/model/errors_spec.rb
> @@ -0,0 +1,141 @@
> +# Licensed to the Apache Software Foundation (ASF) under one or more
> +# contributor license agreements.  See the NOTICE file distributed
with
> +# this work for additional information regarding copyright ownership.
> The
> +# ASF licenses this file to you under the Apache License, Version 2.0
> (the
> +# "License"); you may not use this file except in compliance with the
> +# License.  You may obtain a copy of the License at
> +#
> +#       http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See
> the
> +# License for the specific language governing permissions and
> limitations
> +# under the License.
> +#
> +require 'rubygems'
> +require 'require_relative' if RUBY_VERSION < '1.9'
> +
> +require_relative '../spec_helper.rb' if require 'minitest/autorun'
> +
> +describe 'Schema' do
> +  describe 'Hash attributes' do
> +
> +    it 'should not report error when attribute is set properly' do
> +      machine = CIMI::Model::MachineTemplate.new(:property => {})
> +      machine.to_xml.must_be_kind_of String
> +      machine.to_json.must_be_kind_of String
> +    end
> +
> +    it 'should not report error when attribute is nil' do
> +      machine = CIMI::Model::MachineTemplate.new(:property => nil)
> +      machine.to_xml.must_be_kind_of String
> +      machine.to_json.must_be_kind_of String
> +    end
> +
> +    it 'should not report error when attribute is not set' do
> +      machine = CIMI::Model::MachineTemplate.new
> +      machine.to_xml.must_be_kind_of String
> +      machine.to_json.must_be_kind_of String
> +    end
> +
> +    it 'should report invalid value for Hash attribute when set to
> String' do
> +      machine = CIMI::Model::MachineTemplate.new(:property => '')
> +      lambda { machine.to_xml }.must_raise
> CIMI::Model::Schema::InvalidAttributeValue
> +      lambda { machine.to_json }.must_raise
> CIMI::Model::Schema::InvalidAttributeValue
> +    end
> +
> +    it 'should report invalid value for Hash attribute when set to
Array'
> do
> +      machine = CIMI::Model::MachineTemplate.new(:property => [])
> +      lambda { machine.to_xml }.must_raise
> CIMI::Model::Schema::InvalidAttributeValue
> +      lambda { machine.to_json }.must_raise
> CIMI::Model::Schema::InvalidAttributeValue
> +    end
> +
> +  end
> +
> +  describe 'Array attributes' do
> +
> +    it 'should report invalid value when set to Hash' do
> +      machine = CIMI::Model::MachineTemplate.new(:volumes => {} )
> +      lambda { machine.to_xml }.must_raise
> CIMI::Model::Schema::InvalidAttributeValue
> +      lambda { machine.to_json }.must_raise
> CIMI::Model::Schema::InvalidAttributeValue
> +    end
> +
> +    it 'should report invalid value when set to String' do
> +      machine = CIMI::Model::MachineTemplate.new(:volumes => '' )
> +      lambda { machine.to_xml }.must_raise
> CIMI::Model::Schema::InvalidAttributeValue
> +      lambda { machine.to_json }.must_raise
> CIMI::Model::Schema::InvalidAttributeValue
> +    end
> +
> +    it 'should not report error when attribute is set properly' do
> +      machine = CIMI::Model::MachineTemplate.new(:volumes => [])
> +      machine.to_xml.must_be_kind_of String
> +      machine.to_json.must_be_kind_of String
> +    end
> +
> +    it 'should not report error when attribute is nil' do
> +      machine = CIMI::Model::MachineTemplate.new(:volumes => nil)
> +      machine.to_xml.must_be_kind_of String
> +      machine.to_json.must_be_kind_of String
> +
> +    end
> +
> +    it 'should not report error when attribute is not set' do
> +      machine = CIMI::Model::MachineTemplate.new
> +      machine.to_xml.must_be_kind_of String
> +      machine.to_json.must_be_kind_of String
> +    end
> +  end
> +
> +  describe 'Ref attributes' do
> +
> +    it 'should report error when initialized using Array' do
> +      lambda {
> +        CIMI::Model::MachineTemplate.new(:machine_config => [])
> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> +    end
> +
> +    it 'should report error when initialized using String' do
> +      lambda {
> +        CIMI::Model::MachineTemplate.new(:machine_config => '')
> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> +    end
> +
> +    it 'could be initialized by the Hash value' do
> +      machine = CIMI::Model::MachineTemplate.new(:machine_config =>
> { :href => 'http://localhost/1' })
> +      machine.machine_config.must_be_instance_of
> CIMI::Model::MachineConfigurationRef
> +      machine.machine_config.href.must_equal 'http://localhost/1'
> +      machine.to_xml.must_be_instance_of String
> +      machine.to_json.must_be_instance_of String
> +    end
> +
> +    it 'could be initialized by the Ref value' do
> +      machine = CIMI::Model::MachineTemplate.new(:machine_config =>
> CIMI::Model::MachineConfigurationRef.new(:href =>
> 'http://localhost/1'))
> +      machine.machine_config.must_be_instance_of
> CIMI::Model::MachineConfigurationRef
> +      machine.machine_config.href.must_equal 'http://localhost/1'
> +      machine.to_xml.must_be_instance_of String
> +      machine.to_json.must_be_instance_of String
> +    end
> +
> +  end
> +
> +  describe 'Href attributes' do
> +
> +    it 'should report error when value is not a Hash' do
> +      machine = CIMI::Model::Machine.new(:machine_image => '')
> +      lambda {
> +        machine.to_xml
> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> +      lambda {
> +        machine.to_json
> +      }.must_raise CIMI::Model::Schema::InvalidAttributeValue
> +    end
> +
> +    it 'should not report error when initialized correctly' do
> +      machine = CIMI::Model::Machine.new( :machine_image => { :href
=>
> 'test' })
> +      machine.to_xml.must_be_instance_of String
> +      machine.to_json.must_be_instance_of String
> +    end
> +  end
> +
> +end
> --
> 1.8.1.4
>