deltacloud-core 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. data/Rakefile +10 -12
  2. data/config/drivers/{aruba.yaml → arubacloud.yaml} +2 -2
  3. data/deltacloud-core.gemspec +4 -4
  4. data/lib/cimi/collections/address_templates.rb +1 -1
  5. data/lib/cimi/collections/addresses.rb +2 -2
  6. data/lib/cimi/collections/base.rb +1 -0
  7. data/lib/cimi/collections/credentials.rb +1 -1
  8. data/lib/cimi/collections/forwarding_group_templates.rb +1 -1
  9. data/lib/cimi/collections/forwarding_groups.rb +1 -1
  10. data/lib/cimi/collections/machine_configurations.rb +1 -1
  11. data/lib/cimi/collections/machine_images.rb +1 -1
  12. data/lib/cimi/collections/machine_templates.rb +1 -1
  13. data/lib/cimi/collections/machines.rb +2 -2
  14. data/lib/cimi/collections/network_configurations.rb +1 -1
  15. data/lib/cimi/collections/network_port_configurations.rb +1 -1
  16. data/lib/cimi/collections/network_port_templates.rb +1 -1
  17. data/lib/cimi/collections/network_ports.rb +1 -1
  18. data/lib/cimi/collections/network_templates.rb +1 -1
  19. data/lib/cimi/collections/networks.rb +1 -1
  20. data/lib/cimi/collections/volume_configurations.rb +1 -1
  21. data/lib/cimi/collections/volume_images.rb +1 -1
  22. data/lib/cimi/collections/volume_templates.rb +1 -1
  23. data/lib/cimi/collections/volumes.rb +1 -1
  24. data/lib/cimi/helpers/database_helper.rb +16 -61
  25. data/lib/cimi/helpers/filter_helper.rb +41 -0
  26. data/lib/cimi/helpers/select_helper.rb +62 -0
  27. data/lib/cimi/models.rb +21 -2
  28. data/lib/cimi/models/address.rb +9 -7
  29. data/lib/cimi/models/address_template.rb +4 -4
  30. data/lib/cimi/models/base.rb +62 -197
  31. data/lib/cimi/models/collection.rb +78 -70
  32. data/lib/cimi/models/disk.rb +15 -8
  33. data/lib/cimi/models/machine.rb +27 -29
  34. data/lib/cimi/models/machine_image.rb +10 -13
  35. data/lib/cimi/models/machine_template.rb +3 -3
  36. data/lib/cimi/models/resource.rb +190 -0
  37. data/lib/cimi/models/schema.rb +9 -0
  38. data/lib/cimi/models/volume.rb +10 -13
  39. data/lib/cimi/models/volume_configuration.rb +3 -3
  40. data/lib/cimi/models/volume_template.rb +2 -2
  41. data/lib/cimi/server.rb +1 -0
  42. data/lib/db.rb +15 -0
  43. data/lib/db/address_template.rb +15 -0
  44. data/lib/db/entity.rb +55 -0
  45. data/lib/db/machine_template.rb +15 -0
  46. data/lib/db/provider.rb +40 -0
  47. data/lib/db/volume_configuration.rb +15 -0
  48. data/lib/db/volume_template.rb +15 -0
  49. data/lib/deltacloud/collections/base.rb +1 -0
  50. data/lib/deltacloud/collections/instances.rb +2 -1
  51. data/lib/deltacloud/collections/storage_snapshots.rb +3 -1
  52. data/lib/deltacloud/core_ext/array.rb +5 -0
  53. data/lib/deltacloud/drivers/{aruba/aruba_driver.rb → arubacloud/arubacloud_driver.rb} +3 -6
  54. data/lib/deltacloud/drivers/digitalocean/digitalocean_driver.rb +48 -75
  55. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +18 -3
  56. data/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb +25 -0
  57. data/lib/deltacloud/drivers/fgcp/fgcp_client.rb +1 -1
  58. data/lib/deltacloud/drivers/fgcp/fgcp_driver.rb +34 -14
  59. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +2 -0
  60. data/lib/deltacloud/drivers/mock/mock_driver.rb +3 -3
  61. data/lib/deltacloud/drivers/openstack/openstack_driver.rb +93 -33
  62. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +2 -0
  63. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +2 -2
  64. data/lib/deltacloud/helpers/deltacloud_helper.rb +3 -1
  65. data/lib/deltacloud/helpers/rabbit_helper.rb +1 -2
  66. data/lib/deltacloud/models/address.rb +1 -0
  67. data/lib/deltacloud/models/blob.rb +1 -0
  68. data/lib/deltacloud/models/bucket.rb +1 -0
  69. data/lib/deltacloud/models/firewall.rb +1 -0
  70. data/lib/deltacloud/models/hardware_profile.rb +3 -1
  71. data/lib/deltacloud/models/image.rb +7 -0
  72. data/lib/deltacloud/models/instance.rb +12 -3
  73. data/lib/deltacloud/models/key.rb +1 -0
  74. data/lib/deltacloud/models/load_balancer.rb +1 -0
  75. data/lib/deltacloud/models/metric.rb +1 -0
  76. data/lib/deltacloud/models/realm.rb +10 -1
  77. data/lib/deltacloud/models/storage_snapshot.rb +1 -0
  78. data/lib/deltacloud/models/storage_volume.rb +2 -1
  79. data/lib/deltacloud/runner.rb +2 -1
  80. data/lib/deltacloud/server.rb +1 -0
  81. data/lib/deltacloud/version.rb +1 -1
  82. data/lib/deltacloud_rack.rb +4 -0
  83. data/tests/cimi/collections/machines_test.rb +80 -0
  84. data/tests/cimi/db/database_helper_test.rb +54 -99
  85. data/tests/cimi/db/db_helper.rb +2 -0
  86. data/tests/cimi/db/entity_test.rb +29 -0
  87. data/tests/cimi/model/collection_spec.rb +2 -2
  88. data/tests/cimi/model/credential_spec.rb +2 -2
  89. data/tests/cimi/model/machine_configuration_spec.rb +2 -2
  90. data/tests/cimi/model/machine_image_spec.rb +2 -2
  91. data/tests/cimi/model/machine_spec.rb +4 -4
  92. data/tests/cimi/model/machine_template_spec.rb +2 -2
  93. data/tests/cimi/model/volume_configuration_spec.rb +2 -2
  94. data/tests/cimi/model/volume_image_spec.rb +2 -2
  95. data/tests/cimi/model/volume_spec.rb +2 -2
  96. data/tests/cimi/model/volume_template_spec.rb +2 -2
  97. data/tests/cimi/spec_helper.rb +4 -0
  98. data/tests/deltacloud/collections/instances_collection_test.rb +3 -1
  99. data/tests/deltacloud/launcher_test.rb +3 -5
  100. data/tests/drivers/ec2/images_test.rb +7 -0
  101. data/tests/drivers/gogrid/images_test.rb +7 -0
  102. data/tests/drivers/openstack/instances_test.rb +8 -8
  103. data/tests/drivers/openstack/realms_test.rb +13 -13
  104. data/views/errors/403.xml.haml +2 -1
  105. data/views/images/show.html.haml +4 -1
  106. data/views/images/show.xml.haml +1 -0
  107. data/views/instances/new.html.haml +1 -1
  108. data/views/instances/show.html.haml +3 -0
  109. data/views/realms/index.html.haml +2 -0
  110. data/views/realms/show.html.haml +4 -0
  111. data/views/realms/show.xml.haml +3 -0
  112. metadata +19 -14
@@ -24,11 +24,20 @@ class CIMI::Model::Disk < CIMI::Model::Base
24
24
 
25
25
  def self.find(instance, machine_config, context, id=:all)
26
26
  if id == :all
27
- return machine_config.disks if machine_config
27
+ name = instance.id+"_disk_" #assuming one disk for now...
28
28
 
29
- capacity = false
30
-
31
- if instance
29
+ if machine_config
30
+ mach_config_disks = machine_config.disks
31
+ return mach_config_disks.map do |d|
32
+ self.new(
33
+ :id => context.machine_url(instance.id) + "/disks/#{name}#{d[:capacity]}",
34
+ :name => "#{name}#{d[:capacity]}",
35
+ :description => "Disk for Machine #{instance.id}",
36
+ :created => instance.launch_time.nil? ? Time.now.xmlschema : Time.parse(instance.launch_time).xmlschema,
37
+ :capacity => d[:capacity]
38
+ )
39
+ end
40
+ else
32
41
  if instance.instance_profile.override? :storage
33
42
  capacity = context.to_kibibyte(instance.instance_profile.storage, 'MB')
34
43
  else
@@ -40,11 +49,9 @@ class CIMI::Model::Disk < CIMI::Model::Base
40
49
 
41
50
  return [] unless capacity
42
51
 
43
- name = instance.id+"_disk_#{capacity}" #assuming one disk for now...
44
-
45
52
  [self.new(
46
- :id => context.machine_url(instance.id)+"/disks/#{name}",
47
- :name => name,
53
+ :id => context.machine_url(instance.id)+"/disks/#{name}#{capacity}",
54
+ :name => name + capacity.to_s,
48
55
  :description => "Disk for Machine #{instance.id}",
49
56
  :created => instance.launch_time.nil? ? Time.now.xmlschema : Time.parse(instance.launch_time).xmlschema,
50
57
  :capacity => capacity
@@ -52,6 +52,7 @@ class CIMI::Model::Machine < CIMI::Model::Base
52
52
 
53
53
  def self.create_from_json(body, context)
54
54
  json = JSON.parse(body)
55
+ additional_params={}
55
56
  machine_template = json['machineTemplate']
56
57
  if !machine_template['href'].nil?
57
58
  template = current_db.machine_templates.first(:id => machine_template['href'].split('/').last)
@@ -65,21 +66,28 @@ class CIMI::Model::Machine < CIMI::Model::Base
65
66
  additional_params[:keyname] = machine_template['credential']["href"].split('/').last
66
67
  end
67
68
  end
68
-
69
- additional_params = {}
69
+ if machine_template.has_key? "initialState"
70
+ additional_params[:initial_state] = machine_template["initialState"].strip
71
+ end
70
72
  additional_params[:name] = json['name'] if json['name']
73
+ additional_params[:realm_id] = json['realm'] if json['realm']
71
74
  instance = context.driver.create_instance(context.credentials, image_id, {
72
75
  :hwp_id => hardware_profile_id
73
76
  }.merge(additional_params))
74
77
 
75
78
  # Store attributes that are not supported by the backend cloud to local
76
79
  # database:
77
- store_attributes_for(instance, json)
78
- from_instance(instance, context)
80
+ machine = from_instance(instance, context)
81
+ machine.name = json['name'] || machine.name
82
+ machine.description = json['description']
83
+ machine.extract_properties!(json)
84
+ machine.save
85
+ machine
79
86
  end
80
87
 
81
88
  def self.create_from_xml(body, context)
82
89
  xml = XmlSimple.xml_in(body)
90
+ additional_params = {}
83
91
  if xml['machineTemplate'][0]['href']
84
92
  template = current_db.machine_templates_dataset.first(:id => xml['machineTemplate'][0]['href'].split('/').last)
85
93
  hardware_profile_id = template.machine_config.split('/').last
@@ -92,16 +100,23 @@ class CIMI::Model::Machine < CIMI::Model::Base
92
100
  additional_params[:keyname] = machine_template['credential'][0]["href"].split('/').last
93
101
  end
94
102
  end
95
- additional_params = {}
103
+ if xml["machineTemplate"][0].has_key? "initialState"
104
+ additional_params[:initial_state] = xml["machineTemplate"][0]["initialState"].first.strip
105
+ end
96
106
  additional_params[:name] = xml['name'][0] if xml['name']
107
+ additional_params[:realm_id] = xml['realm'][0] if xml['realm']
97
108
  instance = context.driver.create_instance(context.credentials, image_id, {
98
109
  :hwp_id => hardware_profile_id
99
110
  }.merge(additional_params))
100
111
 
101
112
  # Store attributes that are not supported by the backend cloud to local
102
113
  # database:
103
- entity = store_attributes_for(instance, xml)
104
- from_instance(instance, context, entity.to_hash)
114
+ machine = from_instance(instance, context)
115
+ machine.name = xml['name'] || machine.name
116
+ machine.description = xml['description']
117
+ machine.extract_properties!(xml)
118
+ machine.save
119
+ machine
105
120
  end
106
121
 
107
122
  def perform(action, context, &block)
@@ -117,8 +132,8 @@ class CIMI::Model::Machine < CIMI::Model::Base
117
132
  end
118
133
 
119
134
  def self.delete!(id, context)
120
- delete_attributes_for Instance.new(:id => id)
121
135
  context.driver.destroy_instance(context.credentials, id)
136
+ new(:id => id).destroy
122
137
  end
123
138
 
124
139
  #returns the newly attach machine_volume
@@ -136,15 +151,9 @@ class CIMI::Model::Machine < CIMI::Model::Base
136
151
  end
137
152
 
138
153
  private
139
- def self.from_instance(instance, context, stored_attributes=nil)
154
+ def self.from_instance(instance, context)
140
155
  cpu = memory = (instance.instance_profile.id == "opaque")? "n/a" : nil
141
156
  machine_conf = CIMI::Model::MachineConfiguration.find(instance.instance_profile.name, context)
142
- stored_attributes ||= load_attributes_for(instance)
143
- if stored_attributes[:property]
144
- stored_attributes[:property].merge!(convert_instance_properties(instance, context))
145
- else
146
- stored_attributes[:property] = convert_instance_properties(instance, context)
147
- end
148
157
  machine_spec = {
149
158
  :name => instance.name,
150
159
  :created => instance.launch_time.nil? ? Time.now.xmlschema : Time.parse(instance.launch_time.to_s).xmlschema,
@@ -155,9 +164,8 @@ class CIMI::Model::Machine < CIMI::Model::Base
155
164
  :memory => memory || convert_instance_memory(instance.instance_profile, context),
156
165
  :disks => { :href => context.machine_url(instance.id)+"/disks"},
157
166
  :volumes => { :href=>context.machine_url(instance.id)+"/volumes"},
158
- :operations => convert_instance_actions(instance, context),
159
- :property => stored_attributes
160
- }.merge(stored_attributes)
167
+ :operations => convert_instance_actions(instance, context)
168
+ }
161
169
  if context.expand? :disks
162
170
  machine_spec[:disks] = CIMI::Model::Disk.find(instance, machine_conf, context, :all)
163
171
  end
@@ -166,8 +174,7 @@ class CIMI::Model::Machine < CIMI::Model::Base
166
174
  end
167
175
  machine_spec[:realm] = instance.realm_id if instance.realm_id
168
176
  machine_spec[:machine_image] = { :href => context.machine_image_url(instance.image_id) } if instance.image_id
169
- machine = self.new(machine_spec)
170
- machine
177
+ self.new(machine_spec)
171
178
  end
172
179
 
173
180
  # FIXME: This will convert 'RUNNING' state to 'STARTED'
@@ -181,15 +188,6 @@ class CIMI::Model::Machine < CIMI::Model::Base
181
188
  end
182
189
  end
183
190
 
184
- def self.convert_instance_properties(instance, context)
185
- properties = {}
186
- properties["machine_image"] = context.machine_image_url(instance.image_id)
187
- if instance.respond_to? :keyname
188
- properties["credential"] = context.credential_url(instance.keyname)
189
- end
190
- properties
191
- end
192
-
193
191
  def self.convert_instance_cpu(profile, context)
194
192
  cpu_override = profile.overrides.find { |p, v| p == :cpu }
195
193
  if cpu_override.nil?
@@ -38,17 +38,13 @@ class CIMI::Model::MachineImage < CIMI::Model::Base
38
38
  end
39
39
 
40
40
  def self.from_image(image, context)
41
- stored_attributes = load_attributes_for(image)
42
41
  self.new(
43
42
  :id => context.machine_image_url(image.id),
44
- :name => stored_attributes[:name] || image.id,
45
- :description => stored_attributes[:description] || image.description,
43
+ :name => image.id,
44
+ :description => image.description,
46
45
  :state => image.state || 'UNKNOWN',
47
46
  :type => "IMAGE",
48
- :created => image.creation_time.nil? ? Time.now.xmlschema : Time.parse(image.creation_time.to_s).xmlschema,
49
- :image_location => (stored_attributes[:property] && stored_attributes[:property]['image_location']) ?
50
- stored_attributes[:property].delete('image_location') : 'unknown',
51
- :property => stored_attributes[:property]
47
+ :created => image.creation_time.nil? ? Time.now.xmlschema : Time.parse(image.creation_time.to_s).xmlschema
52
48
  )
53
49
  end
54
50
 
@@ -61,23 +57,24 @@ class CIMI::Model::MachineImage < CIMI::Model::Base
61
57
  input = XmlSimple.xml_in(request_body.read, {"ForceArray"=>false,"NormaliseSpace"=>2})
62
58
  raise 'imageLocation attribute is mandatory' unless input['imageLocation']
63
59
  input['property'] ||= {}
64
- input['property'].kind_of?(Array) ?
65
- input['property'] << { 'image_location' => input['imageLocation'] } : input['property'].merge!('image_location' => input['imageLocation'])
66
60
  else
67
61
  input = JSON.parse(request_body.read)
68
62
  raise 'imageLocation attribute is mandatory' unless input['imageLocation']
69
63
  input['properties'] ||= []
70
- input['properties'] << { 'image_location' => input['imageLocation'] }
71
64
  end
72
65
  params = {:id => context.href_id(input["imageLocation"], :machines), :name=>input["name"], :description=>input["description"]}
73
66
  image = context.driver.create_image(context.credentials, params)
74
- store_attributes_for(image, input)
75
- from_image(image, context)
67
+ result = from_image(image, context)
68
+ result.name = input['name'] if input['name']
69
+ result.description = input['description'] if input['description']
70
+ result.extract_properties!(input)
71
+ result.save
72
+ result
76
73
  end
77
74
 
78
75
  def self.delete!(image_id, context)
79
76
  context.driver.destroy_image(context.credentials, image_id)
80
- delete_attributes_for(::Image.new(:id => image_id))
77
+ new(:id => image_id).destroy
81
78
  end
82
79
 
83
80
  end
@@ -59,7 +59,7 @@ class CIMI::Model::MachineTemplate < CIMI::Model::Base
59
59
  :description => json['description'],
60
60
  :machine_config => json['machineConfig']['href'],
61
61
  :machine_image => json['machineImage']['href'],
62
- :ent_properties => json['properties'].to_json
62
+ :ent_properties => json['properties'] ? json['properties'].to_json : {}
63
63
  )
64
64
  from_db(new_template, context)
65
65
  end
@@ -71,7 +71,7 @@ class CIMI::Model::MachineTemplate < CIMI::Model::Base
71
71
  :description => xml['description'].first,
72
72
  :machine_config => xml['machineConfig'].first['href'],
73
73
  :machine_image => xml['machineImage'].first['href'],
74
- :ent_properties => JSON::dump(xml['property'].inject({}) { |r, p| r[p['key']]=p['content']; r })
74
+ :ent_properties => xml['property'] ? JSON::dump(xml['property'].inject({}) { |r, p| r[p['key']]=p['content']; r }) : {}
75
75
  )
76
76
  from_db(new_template, context)
77
77
  end
@@ -89,7 +89,7 @@ class CIMI::Model::MachineTemplate < CIMI::Model::Base
89
89
  :description => model.description,
90
90
  :machine_config => { :href => model.machine_config },
91
91
  :machine_image => { :href => model.machine_image },
92
- :property => JSON::parse(model.ent_properties),
92
+ :property => (model.ent_properties ? JSON::parse(model.ent_properties) : nil),
93
93
  :created => Time.parse(model.created_at.to_s).xmlschema,
94
94
  :operations => [
95
95
  { :href => context.destroy_machine_template_url(model.id), :rel => 'http://schemas.dmtf.org/cimi/1/action/delete' }
@@ -0,0 +1,190 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership. The
4
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations
14
+ # under the License.
15
+
16
+ require_relative '../helpers/filter_helper'
17
+ require_relative '../helpers/select_helper'
18
+
19
+ module CIMI
20
+ module Model
21
+ class Resource
22
+
23
+ extend CIMI::Model::Schema::DSL
24
+ include CIMI::Helpers::SelectResourceMethods
25
+ include CIMI::Helpers::FilterResourceMethods
26
+
27
+ #
28
+ # We keep the values of the attributes in a hash
29
+ #
30
+ attr_reader :attribute_values
31
+
32
+ CMWG_NAMESPACE = "http://schemas.dmtf.org/cimi/1"
33
+
34
+ #
35
+ # Factory methods
36
+ #
37
+ def initialize(values = {})
38
+ names = self.class.schema.attribute_names
39
+ @select_attrs = values[:select_attr_list] || []
40
+ # Make sure we always have the :id of the entity even
41
+ # the $select parameter is used and :id is filtered out
42
+ #
43
+ @base_id = values[:base_id] || values[:id]
44
+ @attribute_values = names.inject(OrderedHash.new) do |hash, name|
45
+ hash[name] = self.class.schema.convert(name, values[name])
46
+ hash
47
+ end
48
+ end
49
+
50
+ # The CIMI::Model::Resource class methods
51
+ class << self
52
+
53
+ def base_schema
54
+ @schema ||= CIMI::Model::Schema.new
55
+ end
56
+
57
+ def clone_base_schema
58
+ @schema_duped = true
59
+ @schema = superclass.base_schema.deep_clone
60
+ end
61
+
62
+ def base_schema_cloned?
63
+ @schema_duped
64
+ end
65
+
66
+ private :clone_base_schema, :base_schema_cloned?
67
+
68
+ # If the model is inherited by another model, we want to clone
69
+ # the base schema instead of using the parent model schema, which
70
+ # might be modified
71
+ #
72
+ def inherited(child)
73
+ child.instance_eval do
74
+ def schema
75
+ base_schema_cloned? ? @schema : clone_base_schema
76
+ end
77
+ end
78
+ end
79
+
80
+ def add_attributes!(names, attr_klass, &block)
81
+ if self.respond_to? :schema
82
+ schema.add_attributes!(names, attr_klass, &block)
83
+ else
84
+ base_schema.add_attributes!(names, attr_klass, &block)
85
+ end
86
+ names.each do |name|
87
+ define_method(name) { self[name] }
88
+ define_method(:"#{name}=") { |newval| self[name] = newval }
89
+ end
90
+ end
91
+
92
+ # Return Array of links to current CIMI object
93
+ #
94
+ def all_uri(context)
95
+ self.all(context).map { |e| { :href => e.id } }
96
+ end
97
+
98
+ # Construct a new object from the XML representation +xml+
99
+ def from_xml(text)
100
+ xml = XmlSimple.xml_in(text, :force_content => true)
101
+ model = self.new
102
+ @schema.from_xml(xml, model)
103
+ model
104
+ end
105
+
106
+ # Construct a new object
107
+ def from_json(text)
108
+ json = JSON::parse(text)
109
+ model = self.new
110
+ @schema.from_json(json, model)
111
+ model
112
+ end
113
+
114
+ def parse(text, content_type)
115
+ if content_type == "application/xml"
116
+ from_xml(text)
117
+ elsif content_type == "application/json"
118
+ from_json(text)
119
+ else
120
+ raise "Can not parse content type #{content_type}"
121
+ end
122
+ end
123
+
124
+ #
125
+ # Serialize
126
+ #
127
+
128
+ def xml_tag_name
129
+ self.name.split("::").last
130
+ end
131
+
132
+ def resource_uri
133
+ CMWG_NAMESPACE + "/" + self.name.split("::").last
134
+ end
135
+
136
+ def to_json(model)
137
+ json = @schema.to_json(model)
138
+ json[:resourceURI] = resource_uri
139
+ JSON::unparse(json)
140
+ end
141
+
142
+ def to_xml(model)
143
+ xml = @schema.to_xml(model)
144
+ xml["xmlns"] = CMWG_NAMESPACE
145
+ xml["resourceURI"] = resource_uri
146
+ XmlSimple.xml_out(xml, :root_name => xml_tag_name)
147
+ end
148
+ end
149
+
150
+ # END of class methods
151
+
152
+ def [](a)
153
+ @attribute_values[a]
154
+ end
155
+
156
+ def []=(a, v)
157
+ return @attribute_values.delete(a) if v.nil?
158
+ @attribute_values[a] = self.class.schema.convert(a, v)
159
+ end
160
+
161
+ # Apply the $select options to all sub-collections and prepare then
162
+ # to serialize by setting correct :href and :id attributes.
163
+ #
164
+ def prepare
165
+ self.class.schema.collections.map { |coll| coll.name }.each do |n|
166
+ if @select_attrs.empty? or @select_attrs.include?(n)
167
+ self[n].href = "#{self.base_id}/#{n}" if !self[n].href
168
+ self[n].id = "#{self.base_id}/#{n}" if !self[n].entries.empty?
169
+ else
170
+ self[n] = nil
171
+ end
172
+ end
173
+ end
174
+
175
+ def base_id
176
+ self.id || @base_id
177
+ end
178
+
179
+ def to_json
180
+ self.class.to_json(self)
181
+ end
182
+
183
+ def to_xml
184
+ self.class.to_xml(self)
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+ end
@@ -91,6 +91,9 @@ class CIMI::Model::Schema
91
91
  def initialize(name, opts, &block)
92
92
  content = opts[:content]
93
93
  super(name, opts)
94
+ if opts[:class]
95
+ opts[:schema] = opts[:class].schema
96
+ end
94
97
  if opts[:schema]
95
98
  if block_given?
96
99
  raise "Cannot provide :schema option and a block"
@@ -241,6 +244,7 @@ class CIMI::Model::Schema
241
244
  end
242
245
 
243
246
  def to_xml(model, xml)
247
+ return if model[name].nil?
244
248
  model[name].prepare
245
249
  if model[name].entries.empty?
246
250
  xml[xml_name] = { "href" => model[name].href }
@@ -250,6 +254,7 @@ class CIMI::Model::Schema
250
254
  end
251
255
 
252
256
  def to_json(model, json)
257
+ return if model[name].nil?
253
258
  model[name].prepare
254
259
  if model[name].entries.empty?
255
260
  json[json_name] = { "href" => model[name].href }
@@ -278,6 +283,10 @@ class CIMI::Model::Schema
278
283
  @attributes = []
279
284
  end
280
285
 
286
+ def deep_clone
287
+ Marshal::load(Marshal.dump(self))
288
+ end
289
+
281
290
  def collections
282
291
  @attributes.select { |a| a.is_a?(Collection) }
283
292
  end