openstack 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ module OpenStack
2
+ module Compute
3
+ class Flavor
4
+
5
+ attr_reader :id
6
+ attr_reader :name
7
+ attr_reader :ram
8
+ attr_reader :disk
9
+ attr_reader :vcpus
10
+
11
+ # This class provides an object for the "Flavor" of a server. The Flavor can generally be taken as the server specification,
12
+ # providing information on things like memory and disk space.
13
+ #
14
+ # The disk attribute is an integer representing the disk space in GB. The memory attribute is an integer representing the RAM in MB.
15
+ #
16
+ # This is called from the get_flavor method on a OpenStack::Compute::Connection object, returns a OpenStack::Compute::Flavor object, and will likely not be called directly.
17
+ #
18
+ # >> flavor = cs.get_flavor(1)
19
+ # => #<OpenStack::Compute::Flavor:0x1014f8bc8 @name="256 server", @disk=10, @id=1, @ram=256>
20
+ # >> flavor.name
21
+ # => "256 server"
22
+ def initialize(compute,id)
23
+ response = compute.connection.csreq("GET",compute.connection.service_host,"#{compute.connection.service_path}/flavors/#{URI.escape(id.to_s)}",compute.connection.service_port,compute.connection.service_scheme)
24
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
25
+ data = JSON.parse(response.body)['flavor']
26
+ @id = data['id']
27
+ @name = data['name']
28
+ @ram = data['ram']
29
+ @disk = data['disk']
30
+ @vcpus = data['vcpus']
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,73 @@
1
+ module OpenStack
2
+ module Compute
3
+ class Image
4
+
5
+ require 'openstack/compute/metadata'
6
+
7
+ attr_reader :id
8
+ attr_reader :name
9
+ attr_reader :server
10
+ attr_reader :updated
11
+ attr_reader :created
12
+ attr_reader :status
13
+ attr_reader :minDisk
14
+ attr_reader :minRam
15
+ attr_reader :progress
16
+ attr_reader :metadata
17
+
18
+ # This class provides an object for the "Image" of a server. The Image refers to the Operating System type and version.
19
+ #
20
+ # Returns the Image object identifed by the supplied ID number. Called from the get_image instance method of OpenStack::Compute::Connection,
21
+ # it will likely not be called directly from user code.
22
+ #
23
+ # >> cs = OpenStack::Compute::Connection.new(USERNAME,API_KEY)
24
+ # >> image = cs.get_image(2)
25
+ # => #<OpenStack::Compute::Image:0x1015371c0 ...>
26
+ # >> image.name
27
+ # => "CentOS 5.2"
28
+ def initialize(compute,id)
29
+ @id = id
30
+ @compute = compute
31
+ populate
32
+ end
33
+
34
+ # Makes the HTTP call to load information about the provided image. Can also be called directly on the Image object to refresh data.
35
+ # Returns true if the refresh call succeeds.
36
+ #
37
+ # >> image.populate
38
+ # => true
39
+ def populate
40
+ path = "/images/#{URI.escape(self.id.to_s)}"
41
+ response = @compute.connection.req("GET", path)
42
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
43
+ data = JSON.parse(response.body)['image']
44
+ @id = data['id']
45
+ @name = data['name']
46
+ @server = data['server']
47
+ if data['updated'] then
48
+ @updated = DateTime.parse(data['updated'])
49
+ end
50
+ @created = DateTime.parse(data['created'])
51
+ @metadata = OpenStack::Compute::Metadata.new(@compute, path, data['metadata'])
52
+ @status = data['status']
53
+ @minDisk = data['minDisk']
54
+ @minRam = data['minRam']
55
+ @progress = data['progress']
56
+ return true
57
+ end
58
+ alias :refresh :populate
59
+
60
+ # Delete an image. This should be returning invalid permissions when attempting to delete system images, but it's not.
61
+ # Returns true if the deletion succeeds.
62
+ #
63
+ # >> image.delete!
64
+ # => true
65
+ def delete!
66
+ response = @compute.connection.csreq("DELETE",@compute.connection.service_host,"#{@compute.connection.service_path}/images/#{URI.escape(self.id.to_s)}",@compute.connection.service_port,@compute.connection.service_scheme)
67
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
68
+ true
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,116 @@
1
+ module OpenStack
2
+ module Compute
3
+
4
+ class Metadata
5
+
6
+ def initialize(compute, parent_url, metadata=nil)
7
+ @compute = compute
8
+ @base_url = "#{parent_url}/metadata"
9
+ @metadata = metadata
10
+ end
11
+
12
+ def [](key)
13
+ refresh if @metadata.nil?
14
+ @metadata[key]
15
+ end
16
+
17
+ def []=(key, value)
18
+ @metadata = {} if @metadata.nil?
19
+ @metadata[key] = value
20
+ end
21
+
22
+ def store(key, value)
23
+ @metadata = {} if @metadata.nil?
24
+ @metadata[key] = value
25
+ end
26
+
27
+ def each_pair
28
+ @metadata = {} if @metadata.nil?
29
+ @metadata.each_pair do |k,v|
30
+ yield k, v
31
+ end
32
+ end
33
+
34
+ def size
35
+ @metadata = {} if @metadata.nil?
36
+ @metadata.size
37
+ end
38
+
39
+ def each
40
+ refresh if @metadata.nil?
41
+ @metadata.each
42
+ end
43
+
44
+ def save
45
+ return if @metadata.nil?
46
+ json = JSON.generate(:metadata => @metadata)
47
+ response = @compute.connection.req('PUT', @base_url, :data => json)
48
+ @metadata = JSON.parse(response.body)['metadata']
49
+ end
50
+
51
+ def update(keys=nil)
52
+ return if @metadata.nil?
53
+ if keys.nil?
54
+ json = JSON.generate(:metadata => @metadata)
55
+ response = @compute.connection.req('POST', @base_url, :data => json)
56
+ @metadata = JSON.parse(response.body)['metadata']
57
+ else
58
+ keys.each { |key|
59
+ next if not @metadata.has_key?(key)
60
+ json = JSON.generate(:meta => { key => @metadata[key] })
61
+ @compute.connection.req('PUT', "#{@base_url}/#{key}", :data => json)
62
+ }
63
+ end
64
+ end
65
+
66
+ def refresh(keys=nil)
67
+ if keys.nil?
68
+ response = @compute.connection.req('GET', @base_url)
69
+ @metadata = JSON.parse(response.body)['metadata']
70
+ else
71
+ @metadata = {} if @metadata == nil
72
+ keys.each { |key|
73
+ response = @compute.connection.req('GET', "#{@base_url}/#{key}")
74
+ next if response.code == "404"
75
+ meta = JSON.parse(response.body)['meta']
76
+ meta.each { |k, v| @metadata[k] = v }
77
+ }
78
+ end
79
+ end
80
+
81
+ def delete(keys)
82
+ return if @metadata.nil?
83
+ keys.each { |key|
84
+ @metadata.delete(key)
85
+ }
86
+ end
87
+
88
+ def delete!(keys)
89
+ keys.each { |key|
90
+ @compute.connection.req('DELETE', "#{@base_url}/#{key}")
91
+ @metadata.delete(key) if not @metadata.nil?
92
+ }
93
+ end
94
+
95
+ def clear
96
+ if @metadata.nil?
97
+ @metadata = {}
98
+ else
99
+ @metadata.clear
100
+ end
101
+ end
102
+
103
+ def clear!
104
+ clear
105
+ save
106
+ end
107
+
108
+ def has_key?(key)
109
+ return False if @metadata.nil?
110
+ return @metadata.has_key?(key)
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,23 @@
1
+ module OpenStack
2
+ module Compute
3
+ module Personalities
4
+
5
+ # Handles parsing the Personality hash to load it up with Base64-encoded data.
6
+ def self.get_personality(options)
7
+ return if options.nil?
8
+ require 'base64'
9
+ data = []
10
+ itemcount = 0
11
+ options.each do |localpath,svrpath|
12
+ raise OpenStack::Exception::TooManyPersonalityItems, "Personality files are limited to a total of #{MAX_PERSONALITY_ITEMS} items" if itemcount >= MAX_PERSONALITY_ITEMS
13
+ raise OpenStack::Exception::PersonalityFilePathTooLong, "Server-side path of #{svrpath} exceeds the maximum length of #{MAX_SERVER_PATH_LENGTH} characters" if svrpath.size > MAX_SERVER_PATH_LENGTH
14
+ raise OpenStack::Exception::PersonalityFileTooLarge, "Local file #{localpath} exceeds the maximum size of #{MAX_PERSONALITY_FILE_SIZE} bytes" if File.size(localpath) > MAX_PERSONALITY_FILE_SIZE
15
+ b64 = Base64.encode64(IO.read(localpath))
16
+ data.push({:path => svrpath, :contents => b64})
17
+ itemcount += 1
18
+ end
19
+ data
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,249 @@
1
+ module OpenStack
2
+ module Compute
3
+ class Server
4
+
5
+ require 'openstack/compute/metadata'
6
+
7
+ attr_reader :id
8
+ attr_reader :name
9
+ attr_reader :status
10
+ attr_reader :progress
11
+ attr_reader :accessipv4
12
+ attr_reader :accessipv6
13
+ attr_reader :addresses
14
+ attr_reader :hostId
15
+ attr_reader :image
16
+ attr_reader :flavor
17
+ attr_reader :metadata
18
+ attr_accessor :adminPass
19
+
20
+ # This class is the representation of a single Server object. The constructor finds the server identified by the specified
21
+ # ID number, accesses the API via the populate method to get information about that server, and returns the object.
22
+ #
23
+ # Will be called via the get_server or create_server methods on the OpenStack::Compute::Connection object, and will likely not be called directly.
24
+ #
25
+ # >> server = cs.get_server(110917)
26
+ # => #<OpenStack::Compute::Server:0x1014e5438 ....>
27
+ # >> server.name
28
+ # => "RenamedRubyTest"
29
+ def initialize(compute,id)
30
+ @compute = compute
31
+ @id = id
32
+ @svrmgmthost = @compute.connection.service_host
33
+ @svrmgmtpath = @compute.connection.service_path
34
+ @svrmgmtport = @compute.connection.service_port
35
+ @svrmgmtscheme = @compute.connection.service_scheme
36
+ populate
37
+ return self
38
+ end
39
+
40
+ # Makes the actual API call to get information about the given server object. If you are attempting to track the status or project of
41
+ # a server object (for example, when rebuilding, creating, or resizing a server), you will likely call this method within a loop until
42
+ # the status becomes "ACTIVE" or other conditions are met.
43
+ #
44
+ # Returns true if the API call succeeds.
45
+ #
46
+ # >> server.refresh
47
+ # => true
48
+ def populate(data=nil)
49
+ path = "/servers/#{URI.encode(@id.to_s)}"
50
+ if data.nil? then
51
+ response = @compute.connection.req("GET", path)
52
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
53
+ data = JSON.parse(response.body)["server"]
54
+ end
55
+ @id = data["id"]
56
+ @name = data["name"]
57
+ @status = data["status"]
58
+ @progress = data["progress"]
59
+ @addresses = get_addresses(data["addresses"])
60
+ @metadata = OpenStack::Compute::Metadata.new(@compute, path, data["metadata"])
61
+ @hostId = data["hostId"]
62
+ @image = data["image"]
63
+ @flavor = data["flavor"]
64
+ true
65
+ end
66
+ alias :refresh :populate
67
+
68
+ # Sends an API request to reboot this server. Takes an optional argument for the type of reboot, which can be "SOFT" (graceful shutdown)
69
+ # or "HARD" (power cycle). The hard reboot is also triggered by server.reboot!, so that may be a better way to call it.
70
+ #
71
+ # Returns true if the API call succeeds.
72
+ #
73
+ # >> server.reboot
74
+ # => true
75
+ def reboot(type="SOFT")
76
+ data = JSON.generate(:reboot => {:type => type})
77
+ response = @compute.connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
78
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
79
+ true
80
+ end
81
+
82
+ # Sends an API request to hard-reboot (power cycle) the server. See the reboot method for more information.
83
+ #
84
+ # Returns true if the API call succeeds.
85
+ #
86
+ # >> server.reboot!
87
+ # => true
88
+ def reboot!
89
+ self.reboot("HARD")
90
+ end
91
+
92
+ # Updates various parameters about the server. Currently, the only operations supported are changing the server name (not the actual hostname
93
+ # on the server, but simply the label in the Servers API) and the administrator password (note: changing the admin password will trigger
94
+ # a reboot of the server). Other options are ignored. One or both key/value pairs may be provided. Keys are case-sensitive.
95
+ #
96
+ # Input hash key values are :name and :adminPass. Returns true if the API call succeeds.
97
+ #
98
+ # >> server.update(:name => "MyServer", :adminPass => "12345")
99
+ # => true
100
+ # >> server.name
101
+ # => "MyServer"
102
+ def update(options)
103
+ data = JSON.generate(:server => options)
104
+ response = @compute.connection.csreq("PUT",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
105
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
106
+ # If we rename the instance, repopulate the object
107
+ self.populate if options[:name]
108
+ true
109
+ end
110
+
111
+ # Deletes the server from OpenStack Compute. The server will be shut down, data deleted, and billing stopped.
112
+ #
113
+ # Returns true if the API call succeeds.
114
+ #
115
+ # >> server.delete!
116
+ # => true
117
+ def delete!
118
+ response = @compute.connection.csreq("DELETE",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}",@svrmgmtport,@svrmgmtscheme)
119
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
120
+ true
121
+ end
122
+
123
+ # The rebuild function removes all data on the server and replaces it with
124
+ # the specified image. The serverRef and all IP addresses will remain the
125
+ # same. If name and metadata are specified, they will replace existing
126
+ # values, otherwise they will not change. A rebuild operation always
127
+ # removes data injected into the file system via server personality. You
128
+ # may reinsert data into the filesystem during the rebuild.
129
+ #
130
+ # This method expects a hash of the form:
131
+ # {
132
+ # :imageRef => "https://foo.com/v1.1/images/2",
133
+ # :name => "newName",
134
+ # :metadata => { :values => { :foo : "bar" } },
135
+ # :personality => [
136
+ # {
137
+ # :path => "/etc/banner.txt",
138
+ # :contents => : "ICAgpY2hhcmQgQmFjaA=="
139
+ # }
140
+ # ]
141
+ # }
142
+ #
143
+ # This will wipe and rebuild the server, but keep the server ID number,
144
+ # name, and IP addresses the same.
145
+ #
146
+ # Returns true if the API call succeeds.
147
+ #
148
+ # >> server.rebuild!
149
+ # => true
150
+ def rebuild!(options)
151
+ options[:personality] = Personalities.get_personality(options[:personality])
152
+ json = JSON.generate(:rebuild => options)
153
+ response = @compute.connection.req('POST', "/servers/#{@id}/action", :data => json)
154
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
155
+ data = JSON.parse(response.body)['server']
156
+ self.populate(data)
157
+ self.adminPass = data['adminPass']
158
+ true
159
+ end
160
+
161
+ # Takes a snapshot of the server and creates a server image from it. That image can then be used to build new servers. The
162
+ # snapshot is saved asynchronously. Check the image status to make sure that it is ACTIVE before attempting to perform operations
163
+ # on it.
164
+ #
165
+ # A name string for the saved image must be provided. A new OpenStack::Compute::Image object for the saved image is returned.
166
+ #
167
+ # The image is saved as a backup, of which there are only three available slots. If there are no backup slots available,
168
+ # A OpenStack::Exception::OpenStackComputeFault will be raised.
169
+ #
170
+ # >> image = server.create_image(:name => "My Rails Server")
171
+ # =>
172
+ def create_image(options)
173
+ data = JSON.generate(:createImage => options)
174
+ response = @compute.connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
175
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
176
+ image_id = response["Location"].scan(/.*\/(.*)/).flatten
177
+ OpenStack::Compute::Image.new(@compute, image_id)
178
+ end
179
+
180
+ # Resizes the server to the size contained in the server flavor found at ID flavorRef. The server name, ID number, and IP addresses
181
+ # will remain the same. After the resize is done, the server.status will be set to "VERIFY_RESIZE" until the resize is confirmed or reverted.
182
+ #
183
+ # Refreshes the OpenStack::Compute::Server object, and returns true if the API call succeeds.
184
+ #
185
+ # >> server.resize!(1)
186
+ # => true
187
+ def resize!(flavorRef)
188
+ data = JSON.generate(:resize => {:flavorRef => flavorRef})
189
+ response = @compute.connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
190
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
191
+ self.populate
192
+ true
193
+ end
194
+
195
+ # After a server resize is complete, calling this method will confirm the resize with the OpenStack API, and discard the fallback/original image.
196
+ #
197
+ # Returns true if the API call succeeds.
198
+ #
199
+ # >> server.confirm_resize!
200
+ # => true
201
+ def confirm_resize!
202
+ # If the resize bug gets figured out, should put a check here to make sure that it's in the proper state for this.
203
+ data = JSON.generate(:confirmResize => nil)
204
+ response = @compute.connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
205
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
206
+ self.populate
207
+ true
208
+ end
209
+
210
+ # After a server resize is complete, calling this method will reject the resized server with the OpenStack API, destroying
211
+ # the new image and replacing it with the pre-resize fallback image.
212
+ #
213
+ # Returns true if the API call succeeds.
214
+ #
215
+ # >> server.confirm_resize!
216
+ # => true
217
+ def revert_resize!
218
+ # If the resize bug gets figured out, should put a check here to make sure that it's in the proper state for this.
219
+ data = JSON.generate(:revertResize => nil)
220
+ response = @compute.connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
221
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
222
+ self.populate
223
+ true
224
+ end
225
+
226
+ # Changes the admin password.
227
+ # Returns the password if it succeeds.
228
+ def change_password!(password)
229
+ json = JSON.generate(:changePassword => { :adminPass => password })
230
+ @compute.connection.req('POST', "/servers/#{@id}/action", :data => json)
231
+ @adminPass = password
232
+ end
233
+
234
+ def get_addresses(address_info)
235
+ address_list = OpenStack::Compute::AddressList.new
236
+ address_info.each do |label, addr|
237
+ addr.each do |address|
238
+ address_list << OpenStack::Compute::Address.new(label,address)
239
+ if address_list.last.version == 4 && (!@accessipv4 || accessipv4 == "") then
240
+ @accessipv4 = address_list.last.address
241
+ end
242
+ end
243
+ end
244
+ address_list
245
+ end
246
+
247
+ end
248
+ end
249
+ end