Statistics
| Branch: | Tag: | Revision:

one / src / sunstone / sunstone-server.rb @ b9b272f0

History | View | Annotate | Download (25.7 KB)

1
#!/usr/bin/env ruby
2
# -*- coding: utf-8 -*-
3

    
4
# -------------------------------------------------------------------------- #
5
# Copyright 2002-2017, OpenNebula Project, OpenNebula Systems                #
6
#                                                                            #
7
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
8
# not use this file except in compliance with the License. You may obtain    #
9
# a copy of the License at                                                   #
10
#                                                                            #
11
# http://www.apache.org/licenses/LICENSE-2.0                                 #
12
#                                                                            #
13
# Unless required by applicable law or agreed to in writing, software        #
14
# distributed under the License is distributed on an "AS IS" BASIS,          #
15
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
16
# See the License for the specific language governing permissions and        #
17
# limitations under the License.                                             #
18
#--------------------------------------------------------------------------- #
19

    
20
ONE_LOCATION = ENV["ONE_LOCATION"]
21

    
22
if !ONE_LOCATION
23
    LOG_LOCATION = "/var/log/one"
24
    VAR_LOCATION = "/var/lib/one"
25
    ETC_LOCATION = "/etc/one"
26
    SHARE_LOCATION = "/usr/share/one"
27
    RUBY_LIB_LOCATION = "/usr/lib/one/ruby"
28
else
29
    VAR_LOCATION = ONE_LOCATION + "/var"
30
    LOG_LOCATION = ONE_LOCATION + "/var"
31
    ETC_LOCATION = ONE_LOCATION + "/etc"
32
    SHARE_LOCATION = ONE_LOCATION + "/share"
33
    RUBY_LIB_LOCATION = ONE_LOCATION+"/lib/ruby"
34
end
35

    
36
SUNSTONE_AUTH             = VAR_LOCATION + "/.one/sunstone_auth"
37
SUNSTONE_LOG              = LOG_LOCATION + "/sunstone.log"
38
CONFIGURATION_FILE        = ETC_LOCATION + "/sunstone-server.conf"
39

    
40
PLUGIN_CONFIGURATION_FILE = ETC_LOCATION + "/sunstone-plugins.yaml"
41
LOGOS_CONFIGURATION_FILE = ETC_LOCATION + "/sunstone-logos.yaml"
42

    
43
SUNSTONE_ROOT_DIR = File.dirname(__FILE__)
44

    
45
$: << RUBY_LIB_LOCATION
46
$: << RUBY_LIB_LOCATION+'/cloud'
47
$: << SUNSTONE_ROOT_DIR
48
$: << SUNSTONE_ROOT_DIR+'/models'
49

    
50
DISPLAY_NAME_XPATH = 'TEMPLATE/SUNSTONE/DISPLAY_NAME'
51
TABLE_ORDER_XPATH = 'TEMPLATE/SUNSTONE/TABLE_ORDER'
52
DEFAULT_VIEW_XPATH = 'TEMPLATE/SUNSTONE/DEFAULT_VIEW'
53
GROUP_ADMIN_DEFAULT_VIEW_XPATH = 'TEMPLATE/SUNSTONE/GROUP_ADMIN_DEFAULT_VIEW'
54
TABLE_DEFAULT_PAGE_LENGTH_XPATH = 'TEMPLATE/SUNSTONE/TABLE_DEFAULT_PAGE_LENGTH'
55
LANG_XPATH = 'TEMPLATE/SUNSTONE/LANG'
56

    
57
ONED_CONF_OPTS = {
58
    # If no costs are defined in oned.conf these values will be used
59
    'DEFAULT_COST' => {
60
        'CPU_COST' => 0,
61
        'MEMORY_COST' => 0,
62
        'DISK_COST' => 0
63
    },
64
    # Only these values will be shown when retrieving oned.conf from the browser
65
    'ALLOWED_KEYS' => [
66
        'DEFAULT_COST',
67
        'DS_MAD_CONF',
68
        'MARKET_MAD_CONF',
69
        'VM_MAD',
70
        'IM_MAD',
71
        'AUTH_MAD'
72
    ],
73
    # Generate an array if there is only 1 element
74
    'ARRAY_KEYS' => [
75
        'DS_MAD_CONF',
76
        'MARKET_MAD_CONF',
77
        'VM_MAD',
78
        'IM_MAD'
79
    ]
80
}
81

    
82
##############################################################################
83
# Required libraries
84
##############################################################################
85
require 'rubygems'
86
require 'sinatra'
87
require 'erb'
88
require 'yaml'
89
require 'securerandom'
90
require 'tmpdir'
91
require 'fileutils'
92
require 'base64'
93
require 'rexml/document'
94
require 'uri'
95
require 'open3'
96

    
97
require 'CloudAuth'
98
require 'SunstoneServer'
99
require 'SunstoneViews'
100

    
101
##############################################################################
102
# Configuration
103
##############################################################################
104

    
105
begin
106
    $conf = YAML.load_file(CONFIGURATION_FILE)
107
rescue Exception => e
108
    STDERR.puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
109
    exit 1
110
end
111

    
112
$conf[:debug_level] ||= 3
113

    
114
# Set Sunstone Session Timeout
115
$conf[:session_expire_time] ||= 3600
116

    
117
# Set the TMPDIR environment variable for uploaded images
118
ENV['TMPDIR']=$conf[:tmpdir] if $conf[:tmpdir]
119

    
120
CloudServer.print_configuration($conf)
121

    
122
#Sinatra configuration
123

    
124
set :config, $conf
125
set :bind, $conf[:host]
126
set :port, $conf[:port]
127

    
128
if (proxy = $conf[:proxy])
129
    ENV['http_proxy'] = proxy
130
    ENV['HTTP_PROXY'] = proxy
131
end
132

    
133
case $conf[:sessions]
134
when 'memory', nil
135
    use Rack::Session::Pool, :key => 'sunstone'
136
when 'memcache'
137
    memcache_server=$conf[:memcache_host]+':'<<
138
        $conf[:memcache_port].to_s
139

    
140
    STDERR.puts memcache_server
141

    
142
    use Rack::Session::Memcache,
143
        :memcache_server => memcache_server,
144
        :namespace => $conf[:memcache_namespace]
145
when 'memcache-dalli'
146
    require 'rack/session/dalli'
147
    memcache_server=$conf[:memcache_host]+':'<<
148
        $conf[:memcache_port].to_s
149

    
150
    STDERR.puts memcache_server
151

    
152
    use Rack::Session::Dalli,
153
      :memcache_server => memcache_server,
154
      :namespace => $conf[:memcache_namespace],
155
      :cache => Dalli::Client.new
156
else
157
    STDERR.puts "Wrong value for :sessions in configuration file"
158
    exit(-1)
159
end
160

    
161
use Rack::Deflater
162

    
163
# Enable logger
164

    
165
include CloudLogger
166
logger=enable_logging(SUNSTONE_LOG, $conf[:debug_level].to_i)
167

    
168
begin
169
    ENV["ONE_CIPHER_AUTH"] = SUNSTONE_AUTH
170
    $cloud_auth = CloudAuth.new($conf, logger)
171
rescue => e
172
    logger.error {
173
        "Error initializing authentication system" }
174
    logger.error { e.message }
175
    exit -1
176
end
177

    
178
set :cloud_auth, $cloud_auth
179

    
180
$views_config = SunstoneViews.new
181

    
182
#start VNC proxy
183

    
184
$vnc = OpenNebulaVNC.new($conf, logger)
185

    
186
configure do
187
    set :run, false
188
    set :vnc, $vnc
189
    set :erb, :trim => '-'
190
end
191

    
192
DEFAULT_TABLE_ORDER = "desc"
193
DEFAULT_PAGE_LENGTH = 10
194

    
195
SUPPORT = {
196
    :zendesk_url => "https://opennebula.zendesk.com/api/v2",
197
    :custom_field_version => 391130,
198
    :custom_field_severity => 391197,
199
    :author_id => 21231023,
200
    :author_name => "OpenNebula Support Team",
201
    :support_subscription => "http://opennebula.systems/support/",
202
    :account => "http://opennebula.systems/buy/",
203
    :docs => "http://docs.opennebula.org/5.4/",
204
    :community => "http://opennebula.org/support/community/",
205
    :project => "OpenNebula"
206
}
207

    
208
UPGRADE = {
209
    :upgrade => "<span style='color: #0098c3'>Upgrade Available</span>&nbsp;<span style='color:#DC7D24'><i class='fa fa-exclamation-circle'></i></span>",
210
    :no_upgrade => "",
211
    :url => "http://opennebula.org/software/"
212
}
213

    
214
##############################################################################
215
# Helpers
216
##############################################################################
217
helpers do
218
    def valid_csrftoken?
219
        csrftoken = nil
220

    
221
        if params[:csrftoken]
222
            csrftoken = params[:csrftoken]
223
        else
224
            begin
225
                # Extract "csrftoken" and remove from @request_body if present
226
                request_body  = JSON.parse(@request_body)
227
                csrftoken     = request_body.delete("csrftoken")
228
                @request_body = request_body.to_json
229
            rescue
230
            end
231
        end
232

    
233
        session[:csrftoken] && session[:csrftoken] == csrftoken
234
    end
235

    
236
    def authorized?
237
        session[:ip] && session[:ip] == request.ip
238
    end
239

    
240
    def build_session
241
        begin
242
            result = $cloud_auth.auth(request.env, params)
243
        rescue Exception => e
244
            logger.error { e.message }
245
            return [500, ""]
246
        end
247

    
248
        if result.nil?
249
            logger.info { "Unauthorized login attempt" }
250
            return [401, ""]
251
        else
252
            client  = $cloud_auth.client(result, session[:active_zone_endpoint])
253
            user_id = OpenNebula::User::SELF
254

    
255
            user    = OpenNebula::User.new_with_id(user_id, client)
256
            rc = user.info
257
            if OpenNebula.is_error?(rc)
258
                logger.error { rc.message }
259
                return [500, ""]
260
            end
261

    
262
            session[:user]         = user['NAME']
263
            session[:user_id]      = user['ID']
264
            session[:user_gid]     = user['GID']
265
            session[:user_gname]   = user['GNAME']
266
            session[:ip]           = request.ip
267
            session[:remember]     = params[:remember]
268
            session[:display_name] = user[DISPLAY_NAME_XPATH] || user['NAME']
269

    
270
            csrftoken_plain = Time.now.to_f.to_s + SecureRandom.base64
271
            session[:csrftoken] = Digest::MD5.hexdigest(csrftoken_plain)
272

    
273
            group = OpenNebula::Group.new_with_id(user['GID'], client)
274
            rc = group.info
275
            if OpenNebula.is_error?(rc)
276
                logger.error { rc.message }
277
                return [500, ""]
278
            end
279

    
280
            #User IU options initialization
281
            #Load options either from user settings or default config.
282
            # - LANG
283
            # - WSS CONECTION
284
            # - TABLE ORDER
285

    
286
            if user[LANG_XPATH]
287
                session[:lang] = user[LANG_XPATH]
288
            else
289
                session[:lang] = $conf[:lang]
290
            end
291

    
292
            if user[TABLE_DEFAULT_PAGE_LENGTH_XPATH]
293
                session[:page_length] = user[TABLE_DEFAULT_PAGE_LENGTH_XPATH]
294
            else
295
                session[:page_length] = DEFAULT_PAGE_LENGTH
296
            end
297

    
298
            wss = $conf[:vnc_proxy_support_wss]
299
            #limit to yes,no options
300
            session[:vnc_wss] = (wss == true || wss == "yes" || wss == "only" ?
301
                             "yes" : "no")
302

    
303
            if user[TABLE_ORDER_XPATH]
304
                session[:table_order] = user[TABLE_ORDER_XPATH]
305
            else
306
                session[:table_order] = $conf[:table_order] || DEFAULT_TABLE_ORDER
307
            end
308

    
309
            if user[DEFAULT_VIEW_XPATH]
310
                session[:default_view] = user[DEFAULT_VIEW_XPATH]
311
            elsif group.contains_admin(user.id) && group[GROUP_ADMIN_DEFAULT_VIEW_XPATH]
312
                session[:default_view] = group[GROUP_ADMIN_DEFAULT_VIEW_XPATH]
313
            elsif group[DEFAULT_VIEW_XPATH]
314
                session[:default_view] = group[DEFAULT_VIEW_XPATH]
315
            else
316
                session[:default_view] = $views_config.available_views(session[:user], session[:user_gname]).first
317
            end
318

    
319
            #end user options
320

    
321
            if params[:remember] == "true"
322
                env['rack.session.options'][:expire_after] = 30*60*60*24-1
323
            end
324

    
325
            serveradmin_client = $cloud_auth.client(nil, session[:active_zone_endpoint])
326
            rc = OpenNebula::System.new(serveradmin_client).get_configuration
327
            return [500, rc.message] if OpenNebula.is_error?(rc)
328
            return [500, "Couldn't find out zone identifier"] if !rc['FEDERATION/ZONE_ID']
329

    
330
            zone = OpenNebula::Zone.new_with_id(rc['FEDERATION/ZONE_ID'].to_i, client)
331
            zone.info
332
            session[:zone_name] = zone.name
333
            session[:zone_id]   = zone.id
334

    
335
            session[:federation_mode] = rc['FEDERATION/MODE'].upcase
336

    
337
            return [204, ""]
338
        end
339
    end
340

    
341
    def destroy_session
342
        session.clear
343
        return [204, ""]
344
    end
345
end
346

    
347
before do
348
    cache_control :no_store
349
    content_type 'application/json', :charset => 'utf-8'
350

    
351
    @request_body = request.body.read
352
    request.body.rewind
353

    
354
    unless %w(/ /login /vnc /spice /version).include?(request.path)
355
        halt [401, "csrftoken"] unless authorized? && valid_csrftoken?
356
    end
357

    
358
    request_vars = {}
359

    
360
    request.env.each do |k, v|
361
        if v && String === v && !v.empty?
362
            request_vars[k] = v
363
        end
364
    end
365

    
366
    hpref        = "HTTP-"
367
    head_zone    = "ZONE-NAME"
368
    reqenv       = request_vars
369

    
370
    zone_name_header = reqenv[head_zone] ? reqenv[head_zone] : reqenv[hpref+head_zone]
371

    
372
    # Try with underscores
373
    if zone_name_header.nil?
374
        hpref        = "HTTP_"
375
        head_zone    = "ZONE_NAME"
376

    
377
        zone_name_header = reqenv[head_zone] ? reqenv[head_zone] : reqenv[hpref+head_zone]
378
    end
379

    
380
    if zone_name_header && !zone_name_header.empty?
381
        client = $cloud_auth.client(session[:user], session[:active_zone_endpoint])
382
        zpool = ZonePoolJSON.new(client)
383

    
384
        rc = zpool.info
385

    
386
        halt [500, rc.to_json] if OpenNebula.is_error?(rc)
387

    
388
        found = false
389
        zpool.each{|z|
390
            if z.name == zone_name_header
391
                found = true
392
                serveradmin_client = $cloud_auth.client(nil, z['TEMPLATE/ENDPOINT'])
393
                rc = OpenNebula::System.new(serveradmin_client).get_configuration
394

    
395
                if OpenNebula.is_error?(rc)
396
                    msg = "Zone #{zone_name_header} not available " + rc.message
397
                    logger.error { msg }
398
                    halt [410, OpenNebula::Error.new(msg).to_json]
399
                end
400

    
401
                if !rc['FEDERATION/ZONE_ID']
402
                    msg = "Couldn't find out zone identifier"
403
                    logger.error { msg }
404
                    halt [500, OpenNebula::Error.new(msg).to_json]
405
                end
406

    
407
                session[:active_zone_endpoint] = z['TEMPLATE/ENDPOINT']
408
                session[:zone_name] = zone_name_header
409
                session[:zone_id]   = z.id
410
            end
411
         }
412

    
413
         if !found
414
            msg = "Zone #{zone_name_header} does not exist"
415
            logger.error { msg }
416
            halt [404, OpenNebula::Error.new(msg).to_json]
417
        end
418
    end
419

    
420
    client = $cloud_auth.client(session[:user], session[:active_zone_endpoint])
421

    
422
    @SunstoneServer = SunstoneServer.new(client, $conf, logger)
423
end
424

    
425
after do
426
    unless request.path=='/login' || request.path=='/' || request.path=='/'
427
        unless session[:remember] == "true"
428
            if params[:timeout] == "true"
429
                env['rack.session.options'][:defer] = true
430
            else
431
                env['rack.session.options'][:expire_after] = $conf[:session_expire_time]
432
            end
433
        end
434
    end
435
end
436

    
437
##############################################################################
438
# Custom routes
439
##############################################################################
440
if $conf[:routes]
441
    $conf[:routes].each { |route|
442
        require "routes/#{route}"
443
    }
444
end
445

    
446
##############################################################################
447
# HTML Requests
448
##############################################################################
449
get '/' do
450
    content_type 'text/html', :charset => 'utf-8'
451
    if !authorized?
452
        return erb :login
453
    end
454

    
455
    logos_conf = nil
456

    
457
    begin
458
        logos_conf = YAML.load_file(LOGOS_CONFIGURATION_FILE)
459
    rescue Exception => e
460
        logger.error { "Error parsing config file #{LOGOS_CONFIGURATION_FILE}: #{e.message}" }
461
        error 500, ""
462
    end
463

    
464
    serveradmin_client = $cloud_auth.client(nil, session[:active_zone_endpoint])
465

    
466
    rc = OpenNebula::System.new(serveradmin_client).get_configuration
467

    
468
    if OpenNebula.is_error?(rc)
469
        logger.error { rc.message }
470
        error 500, ""
471
    end
472

    
473
    oned_conf_template = rc.to_hash()['TEMPLATE']
474

    
475
    oned_conf = {}
476
    ONED_CONF_OPTS['ALLOWED_KEYS'].each do |key|
477
        value = oned_conf_template[key]
478
        if key == 'DEFAULT_COST'
479
            if value
480
                oned_conf[key] = value
481
            else
482
                oned_conf[key] = ONED_CONF_OPTS['DEFAULT_COST']
483
            end
484
        else
485
            if ONED_CONF_OPTS['ARRAY_KEYS'].include?(key) && !value.is_a?(Array)
486
                oned_conf[key] = [value]
487
            else
488
                oned_conf[key] = value
489
            end
490
        end
491
    end
492

    
493
    response.set_cookie("one-user", :value=>"#{session[:user]}")
494

    
495
    erb :index, :locals => {
496
        :logos_conf => logos_conf,
497
        :oned_conf  => oned_conf,
498
        :support    => SUPPORT,
499
        :upgrade    => UPGRADE
500
    }
501
end
502

    
503
get '/login' do
504
    content_type 'text/html', :charset => 'utf-8'
505
    if !authorized?
506
        erb :login
507
    else
508
        redirect to('/')
509
    end
510
end
511

    
512
get '/vnc' do
513
    content_type 'text/html', :charset => 'utf-8'
514
    if !authorized?
515
        erb :login
516
    else
517
        erb :vnc
518
    end
519
end
520

    
521
get '/spice' do
522
    content_type 'text/html', :charset => 'utf-8'
523
    if !authorized?
524
        erb :login
525
    else
526
        params[:title] = CGI::escape(params[:title])
527
        erb :spice
528
    end
529
end
530

    
531
get '/version' do
532
    version = {}
533

    
534
    if (remote_version_url = $conf[:remote_version])
535
        begin
536
            version = JSON.parse(Net::HTTP.get(URI(remote_version_url)))
537
        rescue Exception
538
        end
539
    end
540

    
541
    if !version["version"] || version["version"].empty?
542
        version["version"] = OpenNebula::VERSION
543
    end
544

    
545
    [200, version.to_json]
546
end
547

    
548
##############################################################################
549
# Login
550
##############################################################################
551
post '/login' do
552
    build_session
553
end
554

    
555
post '/logout' do
556
    destroy_session
557
end
558

    
559
##############################################################################
560
# User configuration and VM logs
561
##############################################################################
562

    
563
get '/config' do
564
    uconf = {
565
        :user_config => {
566
            :lang => session[:lang],
567
            :vnc_wss  => session[:vnc_wss],
568
        },
569
        :system_config => {
570
            :marketplace_url => $conf[:marketplace_url],
571
            :vnc_proxy_port => $vnc.proxy_port
572
        }
573
    }
574

    
575
    [200, uconf.to_json]
576
end
577

    
578
post '/config' do
579
    @SunstoneServer.perform_action('user',
580
                               OpenNebula::User::SELF,
581
                               @request_body)
582

    
583
    user = OpenNebula::User.new_with_id(
584
                OpenNebula::User::SELF,
585
                $cloud_auth.client(session[:user], session[:active_zone_endpoint]))
586

    
587
    rc = user.info
588
    if OpenNebula.is_error?(rc)
589
        logger.error { rc.message }
590
        error 500, ""
591
    end
592

    
593
    session[:lang]         = user[LANG_XPATH] if user[LANG_XPATH]
594
    session[:default_view] = user[DEFAULT_VIEW_XPATH] if user[DEFAULT_VIEW_XPATH]
595
    session[:table_order]  = user[TABLE_ORDER_XPATH] if user[TABLE_ORDER_XPATH]
596
    session[:page_length]  = user[TABLE_DEFAULT_PAGE_LENGTH_XPATH] if user[TABLE_DEFAULT_PAGE_LENGTH_XPATH]
597
    session[:display_name] = user[DISPLAY_NAME_XPATH] || user['NAME']
598

    
599
    [204, ""]
600
end
601

    
602
get '/infrastructure' do
603
    serveradmin_client = $cloud_auth.client(nil, session[:active_zone_endpoint])
604

    
605
    hpool = OpenNebula::HostPool.new(serveradmin_client)
606

    
607
    rc = hpool.info
608

    
609
    if OpenNebula.is_error?(rc)
610
        logger.error { rc.message }
611
        error 500, ""
612
    end
613

    
614
    infrastructure = {}
615

    
616
    set = Set.new
617

    
618
    xml = XMLElement.new
619
    xml.initialize_xml(hpool.to_xml, 'HOST_POOL')
620
    xml.each('HOST/HOST_SHARE/PCI_DEVICES/PCI') do |pci|
621
        set.add({
622
            :device => pci['DEVICE'],
623
            :class  => pci['CLASS'],
624
            :vendor => pci['VENDOR'],
625
            :device_name => pci['DEVICE_NAME']
626
        })
627
    end
628

    
629
    infrastructure[:pci_devices] = set.to_a
630

    
631
    set = Set.new
632

    
633
    xml.each('HOST/TEMPLATE/CUSTOMIZATION') do |customization|
634
        set.add(customization['NAME'])
635
    end
636

    
637
    infrastructure[:vcenter_customizations] = set.to_a
638

    
639
    [200, infrastructure.to_json]
640
end
641

    
642
get '/vm/:id/log' do
643
    @SunstoneServer.get_vm_log(params[:id])
644
end
645

    
646
##############################################################################
647
# Monitoring
648
##############################################################################
649

    
650
get '/:resource/monitor' do
651
    @SunstoneServer.get_pool_monitoring(
652
        params[:resource],
653
        params[:monitor_resources])
654
end
655

    
656
get '/user/:id/monitor' do
657
    @SunstoneServer.get_user_accounting(params)
658
end
659

    
660
get '/group/:id/monitor' do
661
    params[:gid] = params[:id]
662
    @SunstoneServer.get_user_accounting(params)
663
end
664

    
665
get '/:resource/:id/monitor' do
666
    @SunstoneServer.get_resource_monitoring(
667
        params[:id],
668
        params[:resource],
669
        params[:monitor_resources])
670
end
671

    
672
##############################################################################
673
# Accounting
674
##############################################################################
675

    
676
get '/vm/accounting' do
677
    @SunstoneServer.get_vm_accounting(params)
678
end
679

    
680
##############################################################################
681
# Showback
682
##############################################################################
683

    
684
get '/vm/showback' do
685
    @SunstoneServer.get_vm_showback(params)
686
end
687

    
688
##############################################################################
689
# GET Pool information
690
##############################################################################
691
get '/:pool' do
692
    zone_client = nil
693
    filter = params[:pool_filter]
694

    
695
    if params[:zone_id] && session[:federation_mode] != "STANDALONE"
696
        zone = OpenNebula::Zone.new_with_id(params[:zone_id].to_i,
697
                                            $cloud_auth.client(session[:user],
698
                                                session[:active_zone_endpoint]))
699

    
700
        rc   = zone.info
701
        return [500, rc.message] if OpenNebula.is_error?(rc)
702
        zone_client = $cloud_auth.client(session[:user],
703
                                         zone['TEMPLATE/ENDPOINT'])
704
    end
705

    
706
    if params[:pool_filter].nil?
707
        filter = session[:user_gid]
708
    end
709

    
710
    @SunstoneServer.get_pool(params[:pool],
711
                             filter,
712
                             zone_client)
713
end
714

    
715
##############################################################################
716
# GET Resource information
717
##############################################################################
718

    
719
get '/:resource/:id/template' do
720
    @SunstoneServer.get_template(params[:resource], params[:id])
721
end
722

    
723
get '/:resource/:id' do
724
    if params[:extended]
725
        @SunstoneServer.get_resource(params[:resource], params[:id], true)
726
    else
727
        @SunstoneServer.get_resource(params[:resource], params[:id])
728
    end
729
end
730

    
731
##############################################################################
732
# Delete Resource
733
##############################################################################
734
delete '/:resource/:id' do
735
    @SunstoneServer.delete_resource(params[:resource], params[:id])
736
end
737

    
738
##############################################################################
739
# Upload image
740
##############################################################################
741
post '/upload' do
742
    tmpfile = nil
743

    
744
    name = params[:tempfile]
745

    
746
    if !name
747
        [500, OpenNebula::Error.new("There was a problem uploading the file, " \
748
                "please check the permissions on the file").to_json]
749
    else
750
        tmpfile = File.join(Dir.tmpdir, name)
751
        res = @SunstoneServer.upload(params[:img], tmpfile)
752
        FileUtils.rm(tmpfile)
753
        res
754
    end
755
end
756

    
757
post '/upload_chunk' do
758
    info = env['rack.request.form_hash']
759
    chunk_number = info['resumableChunkNumber'].to_i - 1
760
    chunk_size = info['resumableChunkSize'].to_i
761
    chunk_current_size = info['resumableCurrentChunkSize'].to_i
762
    chunk_start = chunk_number * chunk_size
763
    chunk_end = chunk_start + chunk_current_size - 1
764
    identifier = info['']
765
    size = info['resumableTotalSize'].to_i
766

    
767
    file_name = info['resumableIdentifier']
768
    file_path = File.join(Dir.tmpdir, file_name)
769

    
770
    tmpfile=info['file'][:tempfile]
771

    
772
    begin
773
        chunk = tmpfile.read
774
    rescue => e
775
        STDERR.puts e.backtrace
776
        return [500, OpenNebula::Error.new("Could not read the uploaded " \
777
                                           "chunk.".to_json)]
778
    end
779

    
780
    if File.exist? file_path
781
        mode = "r+"
782
    else
783
        mode = "w"
784
    end
785

    
786
    begin
787
        open(file_path, mode) do |f|
788
            f.seek(chunk_start)
789
            f.write_nonblock(chunk)
790
        end
791
        tmpfile.unlink
792
    rescue => e
793
        STDERR.puts e.backtrace
794
        return [500, OpenNebula::Error.new("Can not write to the temporary" \
795
                                           " image file").to_json]
796
    end
797

    
798
    ""
799
end
800

    
801
##############################################################################
802
# Download marketplaceapp
803
##############################################################################
804
get '/marketplaceapp/:id/download' do
805
    dl_resource = @SunstoneServer.download_marketplaceapp(params[:id])
806

    
807
    # If the first element of dl_resource is a number, it is the exit_code after
808
    # an error happend, so return it.
809
    return dl_resource if dl_resource[0].kind_of?(Fixnum)
810

    
811
    download_cmd, filename = dl_resource
812

    
813
    # Send headers
814
    headers['Cache-Control']       = "no-transform" # Do not use Rack::Deflater
815
    headers['Content-Disposition'] = "attachment; filename=\"#{filename}\""
816

    
817
    content_type :'application/octet-stream'
818

    
819
    # Start stream
820
    stream do |out|
821
        Open3.popen3(download_cmd) do |_,o,e,w|
822

    
823
            until o.eof?
824
                # Read in chunks of 16KB
825
                out << o.read(16384)
826
            end
827

    
828
            if !w.value.success?
829
                error_message = "downloader.sh: " << e.read
830
                logger.error { error_message }
831

    
832
                if request.user_agent == "OpenNebula CLI"
833
                    out << "@^_^@ #{error_message} @^_^@"
834
                end
835
            end
836
        end
837
    end
838
end
839

    
840
##############################################################################
841
# Create a new Resource
842
##############################################################################
843
post '/:pool' do
844
    @SunstoneServer.create_resource(params[:pool], @request_body)
845
end
846

    
847
##############################################################################
848
# Start VNC Session for a target VM
849
##############################################################################
850
post '/vm/:id/startvnc' do
851
    vm_id = params[:id]
852
    @SunstoneServer.startvnc(vm_id, $vnc)
853
end
854

    
855
##############################################################################
856
# Perform an action on a Resource
857
##############################################################################
858
post '/:resource/:id/action' do
859
    @SunstoneServer.perform_action(params[:resource],
860
                                   params[:id],
861
                                   @request_body)
862
end
863

    
864
Sinatra::Application.run! if(!defined?(WITH_RACKUP))