openstack_activeresource 0.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 (47) hide show
  1. data/.document +5 -0
  2. data/Gemfile +15 -0
  3. data/Gemfile.lock +49 -0
  4. data/LICENSE.txt +14 -0
  5. data/README.rdoc +427 -0
  6. data/Rakefile +44 -0
  7. data/VERSION +1 -0
  8. data/lib/hot_fixes.rb +55 -0
  9. data/lib/locales/en.yml +5 -0
  10. data/lib/open_stack/base.rb +88 -0
  11. data/lib/open_stack/common.rb +56 -0
  12. data/lib/open_stack/glance/base.rb +39 -0
  13. data/lib/open_stack/glance/image.rb +33 -0
  14. data/lib/open_stack/glance.rb +27 -0
  15. data/lib/open_stack/keystone/admin/base.rb +41 -0
  16. data/lib/open_stack/keystone/admin/role.rb +35 -0
  17. data/lib/open_stack/keystone/admin/tenant.rb +104 -0
  18. data/lib/open_stack/keystone/admin/user.rb +98 -0
  19. data/lib/open_stack/keystone/admin/user_role.rb +34 -0
  20. data/lib/open_stack/keystone/admin.rb +32 -0
  21. data/lib/open_stack/keystone/public/auth.rb +141 -0
  22. data/lib/open_stack/keystone/public/base.rb +41 -0
  23. data/lib/open_stack/keystone/public/tenant.rb +64 -0
  24. data/lib/open_stack/keystone/public.rb +31 -0
  25. data/lib/open_stack/keystone.rb +27 -0
  26. data/lib/open_stack/nova/compute/base.rb +41 -0
  27. data/lib/open_stack/nova/compute/base_detail.rb +59 -0
  28. data/lib/open_stack/nova/compute/flavor.rb +56 -0
  29. data/lib/open_stack/nova/compute/floating_ip.rb +61 -0
  30. data/lib/open_stack/nova/compute/floating_ip_pool.rb +36 -0
  31. data/lib/open_stack/nova/compute/image.rb +76 -0
  32. data/lib/open_stack/nova/compute/key_pair.rb +47 -0
  33. data/lib/open_stack/nova/compute/network.rb +45 -0
  34. data/lib/open_stack/nova/compute/security_group.rb +117 -0
  35. data/lib/open_stack/nova/compute/server.rb +313 -0
  36. data/lib/open_stack/nova/compute/simple_tenant_usage.rb +128 -0
  37. data/lib/open_stack/nova/compute/volume_attachment.rb +98 -0
  38. data/lib/open_stack/nova/compute.rb +39 -0
  39. data/lib/open_stack/nova/volume/base.rb +41 -0
  40. data/lib/open_stack/nova/volume/volume.rb +77 -0
  41. data/lib/open_stack/nova/volume.rb +29 -0
  42. data/lib/open_stack/nova.rb +27 -0
  43. data/lib/open_stack.rb +40 -0
  44. data/lib/openstack_activeresource.rb +1 -0
  45. data/test/helper.rb +18 -0
  46. data/test/test_openstack-activeresource.rb +7 -0
  47. metadata +190 -0
@@ -0,0 +1,45 @@
1
+ # This file is part of the OpenStack-ActiveResource
2
+ #
3
+ # Copyright (C) 2012 Unidata S.p.A. (Davide Guerri - d.guerri@unidata.it)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module OpenStack
19
+ module Nova
20
+ module Compute
21
+
22
+ class Network < Base
23
+ self.collection_name = "os-networks"
24
+
25
+ schema do
26
+ attribute :bridge, :string
27
+ attribute :bridge_interface, :string
28
+ attribute :cidr, :string
29
+ attribute :label, :string
30
+ attribute :multi_host, :boolean
31
+ attribute :vlan, :integer
32
+ attribute :project_id, :string
33
+ end
34
+
35
+ validates :bridge, :format => {:with => /\A[a-z][a-z0-9]{1,8}\Z/i}
36
+ validates :bridge_interface, :format => {:with => /\A[a-z]{1,5}[a-z0-9]{1,5}(\.[0-9]{1,4})?\Z/i}
37
+ validates :cidr, :presence => true
38
+ validates :label, :presence => true, :format => {:with => /\A[\w\s\d]{1,20}\Z/i}
39
+ validates :vlan, :presence => true, :numericality => {:greater_than_or_equal_to => 2, :less_than_or_equal_to => 4096}
40
+
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,117 @@
1
+ # This file is part of the OpenStack-ActiveResource
2
+ #
3
+ # Copyright (C) 2012 Unidata S.p.A. (Davide Guerri - d.guerri@unidata.it)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module OpenStack
19
+ module Nova
20
+ module Compute
21
+
22
+ class SecurityGroup < Base
23
+ self.element_name = "os-security-group"
24
+ self.collection_name = "os-security-groups"
25
+
26
+ schema do
27
+ attribute :tenant_id, :string
28
+ attribute :name, :string
29
+ attribute :description, :string
30
+ end
31
+
32
+ validates :name, :presence => true
33
+ validates :description, :presence => true
34
+
35
+ end
36
+
37
+ class SecurityGroup::Rule < Base
38
+ self.element_name = "os-security-group-rule"
39
+ self.collection_name = "os-security-group-rules"
40
+
41
+ schema do
42
+ attribute :ip_protocol, :string
43
+ attribute :from_port, :integer
44
+ attribute :to_port, :integer
45
+ attribute :parent_group_id, :string
46
+ attribute :cidr, :string
47
+ end
48
+
49
+ validates :ip_protocol, :presence => true, :inclusion => {:in => %w(tcp udp icmp)}
50
+ validates :cidr, :presence => true, :format => {:with => OpenStack::IPV4_CIDR_REGEX}
51
+ validates :parent_group_id, :presence => true
52
+ validates :from_port, :presence => true, :numericality => {:only_integer => true}
53
+ validates :to_port, :presence => true, :numericality => {:only_integer => true}
54
+
55
+ validates_numericality_of :from_port, :less_than_or_equal_to => 255, :greater_than_or_equal_to => -1, :if => Proc.new { |rule| rule.icmp? }
56
+ validates_numericality_of :from_port, :less_than_or_equal_to => 65535, :greater_than_or_equal_to => 1, :if => Proc.new { |rule| rule.udp? or rule.tcp? }
57
+
58
+ validates_numericality_of :to_port, :less_than_or_equal_to => 255, :greater_than_or_equal_to => -1, :if => Proc.new { |rule| rule.icmp? }
59
+ validates_numericality_of :to_port, :less_than_or_equal_to => 65535, :greater_than_or_equal_to => 1, :if => Proc.new { |rule| rule.udp? or rule.tcp? }
60
+ validates_numericality_of :to_port, :greater_than_or_equal_to => :from_port, :if => Proc.new { |rule| rule.udp? or rule.tcp? }
61
+
62
+
63
+ def initialize(attributes = {}, persisted = false)
64
+ attributes = attributes.with_indifferent_access
65
+ new_attributes = {
66
+ :id => attributes[:id],
67
+ :ip_protocol => attributes[:ip_protocol],
68
+ :from_port => attributes[:from_port],
69
+ :to_port => attributes[:to_port],
70
+ :cidr => attributes[:cidr] || attributes[:ip_range][:cidr],
71
+ :parent_group_id => attributes[:parent_group].present? ? attributes[:parent_group].id : nil
72
+ }
73
+ super(new_attributes, persisted)
74
+ end
75
+
76
+ # Overload ActiveRecord::encode method
77
+ # Custom encoding to deal with openstack API
78
+ def encode(options={})
79
+ to_encode = {
80
+ :security_group_rule => {
81
+ :ip_protocol => ip_protocol,
82
+ :from_port => from_port,
83
+ :to_port => to_port,
84
+ :cidr => cidr,
85
+ :parent_group_id => parent_group_id,
86
+ }
87
+ }
88
+ to_encode.send("to_#{self.class.format.extension}", options)
89
+ end
90
+
91
+ def parent_group=(group)
92
+ parent_group_id = group.id
93
+ end
94
+
95
+ def parent_group
96
+ return nil if parent_group_id.nil?
97
+ SecurityGroup.find(parent_group_id)
98
+ end
99
+
100
+ def icmp?
101
+ ip_protocol == 'icmp'
102
+ end
103
+
104
+ def udp?
105
+ ip_protocol == 'udp'
106
+ end
107
+
108
+ def tcp?
109
+ ip_protocol == 'tcp'
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+
@@ -0,0 +1,313 @@
1
+ # This file is part of the OpenStack-ActiveResource
2
+ #
3
+ # Copyright (C) 2012 Unidata S.p.A. (Davide Guerri - d.guerri@unidata.it)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module OpenStack
19
+ module Nova
20
+ module Compute
21
+
22
+ class Server < BaseDetail
23
+
24
+ schema do
25
+ attribute :name, :string
26
+ attribute :status, :string
27
+ attribute :updated_at, :datetime
28
+ attribute :created_at, :datetime
29
+ attribute :vm_state, :string
30
+ attribute :task, :string
31
+ attribute :power_state, :integer
32
+ attribute :host_id, :string
33
+ attribute :tenant_id, :string
34
+ attribute :user_id, :string
35
+ attribute :image_id, :string
36
+ attribute :flavor_id, :string
37
+ attribute :key_pair_id, :string
38
+ end
39
+
40
+ validates :name, :format => {:with => /\A[\w\.\-\_]{2,}\Z/}, :length => {:maximum => 255}
41
+ validates :image, :presence => true
42
+ validates :flavor, :presence => true
43
+
44
+ def self.all_by_tenant(tenant)
45
+ tenant_id = tenant.is_a?(OpenStack::Keystone::Admin::Tenant) ? tenant.id : tenant
46
+
47
+ find(:all, :params => {:tenant_id => tenant_id})
48
+ end
49
+
50
+ def initialize(attributes = {}, persisted = false)
51
+ attributes = attributes.with_indifferent_access
52
+ new_attributes = {
53
+ :id => attributes[:id],
54
+ :name => attributes[:name],
55
+ :status => attributes[:status],
56
+ :updated_at => attributes[:updated].present? ? DateTime.strptime(attributes[:updated], OpenStack::DATETIME_FORMAT) : nil,
57
+ :created_at => attributes[:created].present? ? DateTime.strptime(attributes[:created], OpenStack::DATETIME_FORMAT) : nil,
58
+ :vm_state => attributes[:'OS-EXT-STS:vm_state'],
59
+ :task => attributes[:'OS-EXT-STS:task_state'],
60
+ :power_state => attributes['OS-EXT-STS:power_state'],
61
+ :host_id => attributes[:hostId],
62
+ :user_data => attributes[:user_data],
63
+ # :tenant_id => attributes[:tenant_id],
64
+ # :user_id => attributes[:user_id],
65
+ :addresses => attributes[:addresses] || []
66
+ }
67
+
68
+ if attributes[:key_pair].present?
69
+ new_attributes[:key_pair_id] = attributes[:key_pair].name
70
+ else
71
+ new_attributes[:key_pair_id] = attributes[:key_name]
72
+ end
73
+
74
+ if attributes[:image].present?
75
+ new_attributes[:image_id] = attributes[:image].is_a?(Image) ? attributes[:image].id : attributes[:image][:id]
76
+ elsif attributes[:image_id].present?
77
+ new_attributes[:image_id] = attributes[:image_id]
78
+ end
79
+
80
+ if attributes[:flavor].present?
81
+ new_attributes[:flavor_id] = attributes[:flavor].is_a?(Flavor) ? attributes[:flavor].id : attributes[:flavor][:id]
82
+ elsif attributes[:flavor_id].present?
83
+ new_attributes[:flavor_id] = attributes[:flavor_id]
84
+ end
85
+
86
+ super(new_attributes, persisted)
87
+
88
+ @attributes[:security_group_ids] = get_security_group_ids(attributes[:security_groups])
89
+
90
+ self
91
+ end
92
+
93
+ # Overload ActiveRecord::encode method
94
+ # Custom encoding to deal with openstack API
95
+ def encode(options={})
96
+ to_encode = {
97
+ :server => {
98
+ :name => name,
99
+ :imageRef => image_id,
100
+ :flavorRef => flavor_id,
101
+ }
102
+ }
103
+
104
+ # Optional attributes (openstack will not accept empty attribute for update/create)
105
+ to_encode[:server][:user_data] = Base64.encode64(user_data) if user_data.present?
106
+ to_encode[:server][:key_name] = key_pair_id if key_pair_id.present?
107
+ to_encode[:server][:security_groups] = get_security_groups.map { |sg| {:name => sg.name} } || []
108
+
109
+ to_encode.send("to_#{self.class.format.extension}", options)
110
+ end
111
+
112
+ # Accessors
113
+
114
+ def image
115
+ Image.find(image_id) if image_id.present?
116
+ end
117
+
118
+ def image=(image)
119
+ self.image_id = image.id
120
+ end
121
+
122
+ def flavor
123
+ Flavor.find(flavor_id) if flavor_id.present?
124
+ end
125
+
126
+ def flavor=(flavor)
127
+ self.flavor_id = flavor.id
128
+ end
129
+
130
+ def key_pair
131
+ KeyPair.find(key_pair_id) if key_pair_id.present?
132
+ end
133
+
134
+ def key_pair=(key_pair)
135
+ self.key_pair_id = key_pair.name
136
+ end
137
+
138
+ def security_groups
139
+ get_security_groups
140
+ end
141
+
142
+ def security_groups=(security_groups)
143
+ return if persisted? # Do Nothing (it's a read-only attribute for OpenStack)
144
+
145
+ @attributes[:security_group_ids] = get_security_group_ids(security_groups)
146
+ end
147
+
148
+ def addresses
149
+ addresses = {}
150
+ if persisted?
151
+ response = get('ips')
152
+ response.each { |net, address|
153
+ addresses[net] = address
154
+ }
155
+ else
156
+ attributes[:addresses]
157
+ end
158
+ end
159
+
160
+ def addresses=(something)
161
+ # Do Nothing (it's a read-only attribute for OpenStack)
162
+ end
163
+
164
+ def volume_attachments(scope = :all)
165
+ VolumeAttachment.find(scope, :params => {:server_id => self.id})
166
+ end
167
+
168
+ # Misc...
169
+
170
+ # Return the list of attached volumes
171
+ def attached_volumes
172
+ volume_attachments.present? ? volume_attachments.map { |va| va.volume } : []
173
+ end
174
+
175
+ # Attach a volume
176
+ def attach_volume!(volume, device_name)
177
+ VolumeAttachment.create(:volume => volume, :device => device_name, :server => self)
178
+ end
179
+
180
+ # Refresh server status
181
+ def refresh_status!
182
+ updated = Server.find(self.id)
183
+ self.progress = updated.progress
184
+ self.status = updated.status
185
+ self.task = updated.task
186
+
187
+ self
188
+ end
189
+
190
+ SERVER_STATUSES = {
191
+ :ACTIVE => I18n.t(:active),
192
+ :BUILD => I18n.t(:builing),
193
+ :DELETED => I18n.t(:deleted),
194
+ :ERROR => I18n.t(:in_error),
195
+ :HARD_REBOOT => I18n.t(:hard_rebooting),
196
+ :PASSWORD => I18n.t(:resetting_password),
197
+ :REBOOT => I18n.t(:soft_rebooting),
198
+ :REBUILD => I18n.t(:rebuilding_from_image),
199
+ :RESCUE => I18n.t(:in_rescue_mode),
200
+ :RESIZE => I18n.t(:resizing),
201
+ :REVERT_RESIZE => I18n.t(:revert_resizing),
202
+ :SHUTOFF => I18n.t(:user_powered_down),
203
+ :SUSPENDED => I18n.t(:suspended),
204
+ :PAUSED => I18n.t(:paused),
205
+ :UNKNOWN => I18n.t(:unknown),
206
+ :VERIFY_RESIZE => I18n.t(:awaiting_verification)
207
+ }.with_indifferent_access
208
+
209
+ # Returns an extended description for the server status
210
+ def status_description
211
+ SERVER_STATUSES[status]
212
+ end
213
+
214
+ ## Actions
215
+
216
+ # Assign a floating IP to the server.
217
+ # Params:
218
+ # +floating_ip+:: a FloatingIP to be attached to the server.
219
+ def add_floating_ip(floating_ip)
220
+ post(:action, {}, {:addFloatingIp => {:address => floating_ip.ip}}.to_json)
221
+ end
222
+
223
+ # Reboot the server.
224
+ # Params:
225
+ # +type+:: type of reboot. Should be 'hard' or 'soft' (defaults to 'hard', may be nil)
226
+ def reboot(type=:hard)
227
+ post(:action, {}, {:reboot => {:type => type}}.to_json)
228
+ end
229
+
230
+ # Creates a new snapshot of server.
231
+ # Params:
232
+ # +name+:: name of the new snapshot image
233
+ # +metadata+:: hash of metadata (may be nil)
234
+ def create_new_image(name, metadata={})
235
+ post(:action, {}, {:createImage => {:name => name, :metadata => metadata}}.to_json)
236
+ end
237
+
238
+ # Gets the output from the console log for a server.
239
+ # Params:
240
+ # +length+:: numbers of lines to get (defaults to 50, may be nil)
241
+ def console_output(length=50)
242
+ response = post(:action, {}, {:'os-getConsoleOutput' => {:length => length}}.to_json)
243
+
244
+ ActiveSupport::JSON.decode(response.body)['output']
245
+ end
246
+
247
+ # Accesses a VNC console for a specific server.
248
+ # Params:
249
+ # +length+:: numbers of lines to get (defaults to 50, may be nil)
250
+ def vnc_console(type='novnc')
251
+ response = post(:action, {}, {:'os-getVNCConsole' => {:type => type}}.to_json)
252
+
253
+ ActiveSupport::JSON.decode(response.body)['console']['url']
254
+ end
255
+
256
+ # Halts a running server. Changes status to STOPPED.
257
+ def stop
258
+ post(:action, {}, {:'os-stop' => nil}.to_json)
259
+ end
260
+
261
+ # Returns a STOPPED server to ACTIVE status.
262
+ def start
263
+ post(:action, {}, {:'os-start' => nil}.to_json)
264
+ end
265
+
266
+ # PAUSE a server.
267
+ def pause
268
+ post(:action, {}, {:'pause' => nil}.to_json)
269
+ end
270
+
271
+ # Returns a PAUSED server to ACTIVE status.
272
+ def unpause
273
+ post(:action, {}, {:'unpause' => nil}.to_json)
274
+ end
275
+
276
+ # Suspend a running server. Changes status to SUSPENDED.
277
+ def suspend
278
+ post(:action, {}, {:'suspend' => nil}.to_json)
279
+ end
280
+
281
+ # Resume a SUSPENDED server.
282
+ def resume
283
+ post(:action, {}, {:'resume' => nil}.to_json)
284
+ end
285
+
286
+ private
287
+
288
+ def get_security_groups
289
+ security_group_ids.map { |sg_id|
290
+ SecurityGroup.find sg_id
291
+ }
292
+ end
293
+
294
+ def get_security_group_ids(security_groups=nil)
295
+ if security_groups.nil?
296
+ if persisted?
297
+
298
+ get('os-security-groups').map { |security_group| security_group.with_indifferent_access[:id] }
299
+ else
300
+
301
+ []
302
+ end
303
+ else
304
+
305
+ security_groups.map { |security_group| security_group.id }
306
+ end
307
+ end
308
+
309
+ end
310
+
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,128 @@
1
+ # This file is part of the OpenStack-ActiveResource
2
+ #
3
+ # Copyright (C) 2012 Unidata S.p.A. (Davide Guerri - d.guerri@unidata.it)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module OpenStack
19
+ module Nova
20
+ module Compute
21
+
22
+ class SimpleTenantUsage < Base
23
+ self.element_name = "os-simple-tenant-usage"
24
+ self.collection_name = "os-simple-tenant-usage"
25
+
26
+ schema do
27
+ attribute :total_hours, :float
28
+ attribute :total_vcpus_usage, :float
29
+ attribute :total_memory_mb_usage, :float
30
+ attribute :total_local_gb_usage, :float
31
+ attribute :stop, :datetime
32
+ attribute :start, :datetime
33
+ end
34
+
35
+ def self.find(*arguments)
36
+ scope = arguments.slice!(0)
37
+ options = arguments.slice!(0) || {}
38
+
39
+ # Add "detailed => 1" to the query_options because of broken RESTful
40
+ # (see: http://api.openstack.org @"Simple Usage") - DG - 09/07/2012
41
+ if options[:params].nil?
42
+ options[:params] = {:detailed => 1}
43
+ else
44
+ options[:params].merge! :detailed => 1
45
+ end
46
+
47
+ # If there is no data for the given date intervals, an empty usage will be returned.
48
+ # we "filter out" these entries
49
+ case scope
50
+ when :all then
51
+ find_every(options).select { |su| su.attributes[:tenant_id].present? }
52
+ when :first then
53
+ su = find_every(options).first
54
+ su && su.attributes[:tenant_id].present? ? su : nil
55
+ when :last then
56
+ su = find_every(options).last
57
+ su && su.attributes[:tenant_id].present? ? su : nil
58
+ when :one then
59
+ su = find_one(options)
60
+ su && su.attributes[:tenant_id].present? ? su : nil
61
+ else
62
+ su = find_single(scope, options)
63
+ su && su.attributes[:tenant_id].present? ? su : nil
64
+ end
65
+ end
66
+
67
+ def server_usages
68
+ @attributes[:server_usages].present? ? @attributes[:server_usages] : []
69
+ end
70
+
71
+ def start
72
+ DateTime.parse(@attributes[:start] + ' UTC')
73
+ end
74
+
75
+ def stop
76
+ DateTime.parse(@attributes[:stop] + ' UTC')
77
+ end
78
+
79
+ def self.find_from_date(scope, from_date)
80
+ now = Time.now.utc
81
+
82
+ find(scope, :params => {
83
+ :start => from_date.utc.strftime(OpenStack::DATETIME_FORMAT),
84
+ :end => now.strftime(OpenStack::DATETIME_FORMAT)
85
+ })
86
+
87
+ end
88
+
89
+ def self.find_between_dates(scope, from_date, to_date)
90
+ find(scope, :params => {
91
+ :start => from_date.utc.strftime(OpenStack::DATETIME_FORMAT),
92
+ :end => to_date.utc.strftime(OpenStack::DATETIME_FORMAT)
93
+ })
94
+
95
+ end
96
+
97
+ end
98
+
99
+ class ServerUsage < Base
100
+
101
+ schema do
102
+ attribute :name, :string
103
+ attribute :vcpus, :integer
104
+ attribute :uptime, :integer
105
+ attribute :hours, :float
106
+ attribute :local_gb, :integer
107
+ attribute :tenant_id, :string
108
+ attribute :flavor, :string
109
+ attribute :state, :string
110
+ attribute :memory_mb, :integer
111
+ attribute :started_at, :datetime
112
+ attribute :ended_at, :datetime
113
+ end
114
+
115
+ def started_at
116
+ DateTime.parse(@attributes[:started_at] + ' UTC')
117
+ end
118
+
119
+ def ended_at
120
+ return nil if @attributes[:ended_at].blank?
121
+ DateTime.parse(@attributes[:ended_at] + ' UTC')
122
+ end
123
+
124
+ end
125
+
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,98 @@
1
+ # This file is part of the OpenStack-ActiveResource
2
+ #
3
+ # Copyright (C) 2012 Unidata S.p.A. (Davide Guerri - d.guerri@unidata.it)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module OpenStack
19
+ module Nova
20
+ module Compute
21
+
22
+ class VolumeAttachment < Base
23
+ self.element_name = "volumeAttachment"
24
+ self.collection_name = "os-volume_attachments"
25
+
26
+ def self.site
27
+ superclass.site + "servers/:server_id"
28
+ end
29
+
30
+ schema do
31
+ attribute :device, :string
32
+ attribute :volume_id, :string
33
+ attribute :server_id, :string
34
+ end
35
+
36
+ validates :device, :presence => true, :format => {:with => /\A[\/\\a-zA-Z0-9]+\Z/}
37
+ validates :volume, :presence => true
38
+ validates :server, :presence => true
39
+
40
+ def initialize(attributes = {}, persisted = false)
41
+ attributes = attributes.with_indifferent_access
42
+ new_attributes = {
43
+ :device => attributes[:device],
44
+ }
45
+
46
+ new_attachment = super(new_attributes, persisted)
47
+
48
+ if attributes[:volume].present?
49
+ new_attachment.volume_id = attributes[:volume].id
50
+ else
51
+ new_attachment.volume_id = attributes[:volumeId]
52
+ end
53
+
54
+ if attributes[:server].present?
55
+ new_attachment.server_id = attributes[:server].id
56
+ else
57
+ new_attachment.server_id = attributes[:serverId]
58
+ end
59
+
60
+ new_attachment.prefix_options[:server_id] = new_attachment.server_id
61
+
62
+ new_attachment
63
+ end
64
+
65
+ # Overload ActiveRecord::encode method
66
+ # Custom encoding to deal with openstack API
67
+ def encode(options={})
68
+ to_encode = {
69
+ VolumeAttachment.element_name => {
70
+ :device => device,
71
+ :volumeId => volume_id,
72
+ :serverId => server_id
73
+ }
74
+ }
75
+ to_encode.send("to_#{self.class.format.extension}", options)
76
+ end
77
+
78
+ def server
79
+ Server.find(server_id) if server_id.present?
80
+ end
81
+
82
+ def server=(server)
83
+ @attributes[:server_id] = server.id unless !persisted?
84
+ end
85
+
86
+ def volume
87
+ Volume::Volume.find(volume_id) if volume_id.present?
88
+ end
89
+
90
+ def volume=(volume)
91
+ @attributes[:volume_id] = volume.id unless !persisted?
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+ end
98
+ end