Statistics
| Branch: | Tag: | Revision:

one / src / cloud / ec2 / lib / EC2QueryServer.rb @ 591e21a0

History | View | Annotate | Download (10.8 KB)

1
# -------------------------------------------------------------------------- #
2
# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org)             #
3
#                                                                            #
4
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
5
# not use this file except in compliance with the License. You may obtain    #
6
# 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,          #
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
13
# See the License for the specific language governing permissions and        #
14
# limitations under the License.                                             #
15
#--------------------------------------------------------------------------- #
16

    
17
require 'rubygems'
18
require 'sinatra'
19
require 'erb'
20
require 'time'
21
require 'AWS'
22
require 'base64'
23
require 'CloudServer'
24

    
25
require 'ImageEC2'
26

    
27
###############################################################################
28
# The EC2Query Server implements a EC2 compatible server based on the
29
# OpenNebula Engine
30
###############################################################################
31
class EC2QueryServer < CloudServer
32

    
33
    ###########################################################################
34
    # Class Constants. Defined the EC2 and OpenNebula State mapping
35
    ###########################################################################
36
    EC2_STATES={
37
        :pending    => {:code => 0, :name => 'pending'},
38
        :running    => {:code => 16,:name => 'running'},
39
        :shutdown   => {:code => 32,:name => 'shutting-down'},
40
        :terminated => {:code => 48,:name => 'terminated'}
41
    }
42

    
43
    ONE_STATES={
44
        'init' => :pending,
45
        'pend' => :pending,
46
        'hold' => :pending,
47
        'stop' => :pending,
48
        'susp' => :pending,
49
        'done' => :terminated,
50
        'fail' => :terminated,
51
        'prol' => :pending,
52
        'boot' => :running,
53
        'runn' => :running,
54
        'migr' => :running,
55
        'save' => :pending,
56
        'epil' => :shutdown,
57
        'shut' => :shutdown,
58
        'fail' => :terminated,
59
        'dele' => :terminated,
60
        'unkn' => :terminated
61
    }
62

    
63
    ###########################################################################
64

    
65
    def initialize(config_file,template,views)
66
        super(config_file)
67
        @config.add_configuration_value("TEMPLATE_LOCATION",template)
68
        @config.add_configuration_value("VIEWS",views)
69

    
70
        if @config[:ssl_server]
71
            @server_host=@config[:ssl_server]
72
        else
73
            @server_host=@config[:server]
74
        end
75

    
76
        @server_port=@config[:port]
77

    
78
        print_configuration
79
    end
80

    
81
    ###########################################################################
82
    # Authentication functions
83
    ###########################################################################
84

    
85
    # EC2 protocol authentication function
86
    # params:: of the request
87
    # [return] true if authenticated
88
    def authenticate(params,env)
89
        password = get_user_password(params['AWSAccessKeyId'])
90
        return nil if !password
91

    
92
        signature = case params['SignatureVersion']
93
            when "1" then signature_version_1(params.clone, password)
94
            when "2" then signature_version_2(params,
95
                              password,
96
                              env,
97
                              true,
98
                              false)
99
        end
100

    
101
        if params['Signature']==signature
102
            return one_client_user(params['AWSAccessKeyId'], password)
103
        else
104
            if params['SignatureVersion']=="2"
105
                signature = signature_version_2(params,
106
                                  password,
107
                                  env,
108
                                  false,
109
                                  false)
110
                if params['Signature']==signature
111
                    return one_client_user(params['AWSAccessKeyId'], password)
112
                end
113
            end
114
        end
115

    
116
        return nil
117
    end
118

    
119

    
120
    ###########################################################################
121
    # Repository Interface
122
    ###########################################################################
123

    
124
    def upload_image(params, one_client)
125
        image = ImageEC2.new(Image.build_xml, one_client, params['file'])
126

    
127
        template = image.to_one_template
128
        if OpenNebula.is_error?(template)
129
            return OpenNebula::Error.new('Unsupported'), 400
130
        end
131

    
132
        rc = image.allocate(template)
133
        if OpenNebula.is_error?(rc)
134
            return OpenNebula::Error.new('Unsupported'), 400
135
        end
136

    
137
        erb_version = params['Version']
138

    
139
        response = ERB.new(File.read(@config[:views]+"/register_image.erb"))
140
        return response.result(binding), 200
141
    end
142

    
143
    def register_image(params, one_client)
144
        # Get the Image ID
145
        tmp, img=params['ImageLocation'].split('-')
146

    
147
        image = Image.new(Image.build_xml(img.to_i), one_client)
148

    
149
        # Enable the new Image
150
        rc = image.info
151
        if OpenNebula.is_error?(rc)
152
            return OpenNebula::Error.new('InvalidAMIID.NotFound'), 400
153
        end
154

    
155
        image.enable
156

    
157
        erb_version = params['Version']
158

    
159
        response = ERB.new(File.read(@config[:views]+"/register_image.erb"))
160
        return response.result(binding), 200
161
    end
162

    
163
    def describe_images(params, one_client)
164
        user_flag=-1
165
        impool = ImagePool.new(one_client, user_flag)
166
        impool.info
167

    
168
        erb_user_name = params['AWSAccessKeyId']
169
        erb_version = params['Version']
170

    
171
        response = ERB.new(File.read(@config[:views]+"/describe_images.erb"))
172
        return response.result(binding), 200
173
    end
174

    
175
    ###########################################################################
176
    # Instance Interface
177
    ###########################################################################
178

    
179
    def run_instances(params, one_client)
180
        # Get the instance type and path
181
        if params['InstanceType'] != nil
182
            instance_type_name = params['InstanceType']
183
            instance_type      = @instance_types[instance_type_name]
184

    
185
            if instance_type != nil
186
                path = @config[:template_location] + "/#{instance_type['TEMPLATE']}"
187
            end
188
        end
189

    
190
        # Get the image
191
        tmp, img=params['ImageId'].split('-')
192

    
193
        # Build the VM
194
        erb_vm_info=Hash.new
195
        erb_vm_info[:img_id]        = img.to_i
196
        erb_vm_info[:ec2_img_id]    = params['ImageId']
197
        erb_vm_info[:instance_type] = instance_type_name
198
        erb_vm_info[:template]      = path
199
        erb_vm_info[:user_data]     = params['UserData']
200

    
201
        template      = ERB.new(File.read(erb_vm_info[:template]))
202
        template_text = template.result(binding)
203

    
204
        # Start the VM.
205
        vm = VirtualMachine.new(VirtualMachine.build_xml, one_client)
206

    
207
        rc = vm.allocate(template_text)
208
        if OpenNebula::is_error?(rc)
209
            return OpenNebula::Error.new('Unsupported'),400
210
        end
211

    
212
        vm.info
213

    
214
        erb_vm_info[:vm_id]=vm.id
215
        erb_vm_info[:vm]=vm
216
        erb_user_name = params['AWSAccessKeyId']
217
        erb_version = params['Version']
218

    
219
        response = ERB.new(File.read(@config[:views]+"/run_instances.erb"))
220
        return response.result(binding), 200
221
    end
222

    
223
    def describe_instances(params, one_client)
224
        user_flag=-1
225
        vmpool = VirtualMachinePool.new(one_client, user_flag)
226
        vmpool.info
227

    
228
        erb_version = params['Version']
229
        erb_user_name = params['AWSAccessKeyId']
230
        
231
        response = ERB.new(File.read(@config[:views]+"/describe_instances.erb"))
232
        return response.result(binding), 200
233
    end
234

    
235
    def terminate_instances(params, one_client)
236
        # Get the VM ID
237
        vmid=params['InstanceId.1']
238
        vmid=params['InstanceId.01'] if !vmid
239

    
240
        tmp, vmid=vmid.split('-') if vmid[0]==?i
241

    
242
        vm = VirtualMachine.new(VirtualMachine.build_xml(vmid),one_client)
243
        rc = vm.info
244

    
245
        return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc)
246

    
247
        if vm.status == 'runn'
248
            rc = vm.shutdown
249
        else
250
            rc = vm.finalize
251
        end
252

    
253
        return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc)
254

    
255
        erb_version = params['Version']
256

    
257
        response =ERB.new(File.read(@config[:views]+"/terminate_instances.erb"))
258
        return response.result(binding), 200
259
    end
260

    
261
private
262

    
263
    # Calculates signature version 1
264
    def signature_version_1(params, secret_key, digest='sha1')
265
        params.delete('Signature')
266
        req_desc = params.sort {|x,y| x[0].downcase <=> y[0].downcase}.to_s
267

    
268
        digest_generator = OpenSSL::Digest::Digest.new(digest)
269
        digest = OpenSSL::HMAC.digest(digest_generator,
270
                                      secret_key,
271
                                      req_desc)
272
        b64sig = Base64.b64encode(digest)
273
        return b64sig.strip
274
    end
275

    
276
    # Calculates signature version 2
277
    def signature_version_2(params, secret_key, env, includeport=true, urlencode=true)
278
        signature_params = params.reject { |key,value|
279
            key=='Signature' or key=='file' }
280

    
281
        if includeport
282
            server_str = @server_host + ':' + @server_port
283
        else
284
            server_str = @server_host
285
        end
286

    
287
        canonical_str = AWS.canonical_string(signature_params,
288
                         server_str,
289
                         env['REQUEST_METHOD'])
290

    
291
        # Use the correct signature strength
292
        sha_strength = case params['SignatureMethod']
293
            when "HmacSHA1" then 'sha1'
294
            when "HmacSHA256" then 'sha256'
295
            else 'sha1'
296
        end
297

    
298
        digest = OpenSSL::Digest::Digest.new(sha_strength)
299
        b64hmac =
300
            Base64.encode64(
301
                OpenSSL::HMAC.digest(digest, secret_key, canonical_str)).gsub("\n","")
302

    
303
        if urlencode
304
            return CGI::escape(b64hmac)
305
        else
306
            return b64hmac
307
        end
308
    end
309
    
310
    ###########################################################################
311
    # Helper functions
312
    ###########################################################################
313
    def render_state(vm)
314
        ec2_state = EC2_STATES[ONE_STATES[vm.status]]
315

    
316
        return "<code>#{ec2_state[:code]}</code>
317
        <name>#{ec2_state[:name]}</name>"
318
    end
319

    
320
    def render_launch_time(vm)
321
        return "<launchTime>#{Time.at(vm["STIME"].to_i).xmlschema}</launchTime>"
322
    end
323
end
324