From 4ccaf9704a5d415e4979bbf53dce263d2c1e7cdb Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 13 Feb 2012 15:55:11 +0100 Subject: [PATCH] Feature #1076: Add VNC support in SelfService. When enabled in the occi server configuration file, UI users will be able to click the VNC icon that appears in the VM information. Then the websockets proxy will be set up, provided that the machine has been configured with the appropiate GRAPHICS section etc. This must be done in the OCCI templates, and cannot be done by the UI user. Wss sessions can be configured in the occi server configuration file. Unlike Sunstone, here they are transparent to the user and whenever they are enabled VNC sessions will be launched using wss:// automaticly. As such, it is not up to the user to choose the type of connection, and it fully depends on the server configuration. Additionally the install_novnc.sh script has been updated and improved. The install.sh has been updated too. --- install.sh | 3 +- share/install_novnc.sh | 31 ++++++--- src/cloud/occi/etc/occi-server.conf | 23 ++++++- src/cloud/occi/lib/OCCIServer.rb | 32 ++++++++- src/cloud/occi/lib/occi-server.rb | 69 ++++++++++++++++++- src/cloud/occi/lib/ui/public/js/locale.js | 2 +- src/cloud/occi/lib/ui/public/js/occi.js | 9 ++- .../occi/lib/ui/public/js/plugins/compute.js | 42 ++++++++--- .../lib/ui/public/js/plugins/configuration.js | 25 +++++-- 9 files changed, 205 insertions(+), 31 deletions(-) diff --git a/install.sh b/install.sh index 9c10727ba6..d5ce330bcb 100755 --- a/install.sh +++ b/install.sh @@ -1012,7 +1012,8 @@ OCCI_LIB_FILES="src/cloud/occi/lib/OCCIServer.rb \ src/cloud/occi/lib/UserOCCI.rb \ src/cloud/occi/lib/UserPoolOCCI.rb \ src/cloud/occi/lib/ImageOCCI.rb \ - src/cloud/occi/lib/ImagePoolOCCI.rb" + src/cloud/occi/lib/ImagePoolOCCI.rb \ + src/sunstone/OpenNebulaVNC.rb" OCCI_LIB_CLIENT_FILES="src/cloud/occi/lib/OCCIClient.rb" diff --git a/share/install_novnc.sh b/share/install_novnc.sh index 52dbf373ef..861fe78315 100755 --- a/share/install_novnc.sh +++ b/share/install_novnc.sh @@ -7,35 +7,50 @@ if [ -z "$ONE_LOCATION" ]; then ONE_SHARE=/usr/share/one ONE_PUBLIC_SUNSTONE=/usr/lib/one/sunstone/public SUNSTONE_CONF=/etc/one/sunstone-server.conf + ONE_PUBLIC_SELFSERVICE=/usr/lib/one/ruby/cloud/occi/ui/public + SELFSERVICE_CONF=/etc/one/occi-server.conf else ONE_SHARE=$ONE_LOCATION/share ONE_PUBLIC_SUNSTONE=$ONE_LOCATION/lib/sunstone/public SUNSTONE_CONF=$ONE_LOCATION/etc/sunstone-server.conf + ONE_PUBLIC_SELFSERVICE=$ONE_LOCATION/lib/ruby/cloud/occi/ui/public + SELFSERVICE_CONF=$ONE_LOCATION/etc/occi-server.conf fi +echo "Downloading noVNC latest version..." mkdir -p $NOVNC_TMP -wget -P $NOVNC_TMP --no-check-certificate http://github.com/kanaka/noVNC/tarball/master - +cd $NOVNC_TMP +curl -O -# -L http://github.com/kanaka/noVNC/tarball/master if [ $? -ne 0 ]; then - echo "Error downloading noVNC" + echo "\nError downloading noVNC" exit 1 fi +echo "Extracting files to temporary folder..." tar=`ls -rt $NOVNC_TMP|tail -n1` -tar -C $ONE_SHARE -mxvzf $NOVNC_TMP/$tar +tar -C $ONE_SHARE -mxzf $NOVNC_TMP/$tar if [ $? -ne 0 ]; then echo "Error untaring noVNC" exit 1 fi +echo "Moving files to OpenNebula $ONE_SHARE folder..." +rm -rf $ONE_SHARE/noVNC dir=`ls -rt $ONE_SHARE|tail -n1` mv $ONE_SHARE/$dir $ONE_SHARE/noVNC +echo "Installing Sunstone client libraries in $ONE_PUBLIC_SUNSTONE..." mkdir -p $ONE_PUBLIC_SUNSTONE/vendor/noVNC -mv $ONE_SHARE/noVNC/include/ $ONE_PUBLIC_SUNSTONE/vendor/noVNC/ +cp -r $ONE_SHARE/noVNC/include/ $ONE_PUBLIC_SUNSTONE/vendor/noVNC/ -sed -i.bck "s%^\(:vnc_proxy_path: \).*$%\1$ONE_SHARE/$PROXY_PATH%" $SUNSTONE_CONF +echo "Installing SelfService client libraries in $ONE_PUBLIC_SELFSERVICE..." +mkdir -p $ONE_PUBLIC_SELFSERVICE/vendor/noVNC +cp -r $ONE_SHARE/noVNC/include/ $ONE_PUBLIC_SELFSERVICE/vendor/noVNC/ -#Update file permissions -chmod +x $ONE_SHARE/noVNC/utils/launch.sh +echo "Backing up and updating $SUNSTONE_CONF with new VNC proxy path..." +sed -i.bck "s%^\(:vnc_proxy_path:\).*$%\1 $ONE_SHARE/$PROXY_PATH%" $SUNSTONE_CONF +echo "Backing up and updating $SELFSERVICE_CONF with new VNC proxy path..." +sed -i.bck "s%^\(:vnc_proxy_path:\).*$%\1 $ONE_SHARE/$PROXY_PATH%" $SELFSERVICE_CONF + +echo "Installation successful" \ No newline at end of file diff --git a/src/cloud/occi/etc/occi-server.conf b/src/cloud/occi/etc/occi-server.conf index 0084890209..90ea4df134 100644 --- a/src/cloud/occi/etc/occi-server.conf +++ b/src/cloud/occi/etc/occi-server.conf @@ -52,5 +52,26 @@ :cpu: 8 :memory: 8192 -# Default language setting for Self-Service UI + +############################################################# +### SelfService UI Settings +############################################################# + +# Default language setting :lang: en_US + +# VNC Configuration +# vnc_enable: yes | no. Allow users to launch vnc sessions. +# base_port: base_port + vnc_port of the VM is the port where the +# proxy will listen for VNC session connections to that VM. +# vnc_proxy_path: path to the websockets proxy (set by install_novnc.sh) +# support_wss: no | yes | only. If yes or only provide path to cert and key. +# "yes" means the proxy will accept both ws and wss connections. +# vnc_proxy_cert: Certificate to encrypt wss connections. +# vnc_proxy_key: Key for wss connections. Only necessary if not included in cert. +:vnc_enable: no +:vnc_proxy_base_port: 33876 +:vnc_proxy_path: +:vnc_proxy_support_wss: no +:vnc_proxy_cert: +:vnc_proxy_key: diff --git a/src/cloud/occi/lib/OCCIServer.rb b/src/cloud/occi/lib/OCCIServer.rb index fbe3d210a9..d6c8691b96 100755 --- a/src/cloud/occi/lib/OCCIServer.rb +++ b/src/cloud/occi/lib/OCCIServer.rb @@ -30,6 +30,8 @@ require 'ImagePoolOCCI' require 'UserOCCI' require 'UserPoolOCCI' +require 'OpenNebulaVNC' + require 'pp' @@ -236,7 +238,6 @@ class OCCIServer < CloudServer return to_occi_xml(vm, 200) end - # Deletes a COMPUTE resource # request:: _Hash_ hash containing the data of the request # [return] _String_,_Integer_ Delete confirmation msg or error, @@ -505,4 +506,33 @@ class OCCIServer < CloudServer return to_occi_xml(user, 200) end + + ############################################################################ + # VNC Methods + ############################################################################ + + def startvnc(id,config) + vm = VirtualMachineOCCI.new( + VirtualMachine.build_xml(id), + @client) + + rc = vm.info + if OpenNebula.is_error?(rc) + error = "Error starting VNC session, " + error += "could not retrieve Virtual Machine" + return [404,error] + end + + vnc_proxy = OpenNebulaVNC.new(config,{:json_errors => false}) + return vnc_proxy.start(vm) + end + + def stopvnc(pipe) + begin + OpenNebulaVNC.stop(pipe) + rescue Exception => e + return [500, e.message] + end + return [200,nil] + end end diff --git a/src/cloud/occi/lib/occi-server.rb b/src/cloud/occi/lib/occi-server.rb index 470ec9e19a..b64016525d 100755 --- a/src/cloud/occi/lib/occi-server.rb +++ b/src/cloud/occi/lib/occi-server.rb @@ -309,7 +309,19 @@ end ## UI ############################################## -post '/config' do +get '/ui/config/:opt' do + case params[:opt] + when "lang" then session[:lang] + when "wss" + wss = settings.config[:vnc_proxy_support_wss] + wss = (wss == true || wss == "yes" || wss == "only" ? "yes" : "no") + return wss + when "vnc" then settings.config[:vnc_enable] ? "yes" : "no" + else [404, "Unknown configuration option"] + end +end + +post '/ui/config' do begin body = JSON.parse(request.body.read) rescue @@ -321,6 +333,7 @@ post '/config' do when "lang" then session[:lang]=value end end + return 200 end get '/ui/login' do @@ -355,3 +368,57 @@ post '/ui/upload' do result,rc = @occi_server.post_storage(request) treat_response(result,rc) end + +post '/ui/startvnc/:id' do + if !settings.config[:vnc_enable] + return [403, "VNC sessions are disabled"] + end + + vm_id = params[:id] + + vnc_hash = session['vnc'] + + if !vnc_hash + session['vnc']= {} + elsif vnc_hash[vm_id] + #return existing information + info = vnc_hash[vm_id].clone + info.delete(:pipe) + + return [200, info.to_json] + end + + rc = @occi_server.startvnc(vm_id, settings.config) + + if rc[0] == 200 + info = rc[1] + session['vnc'][vm_id] = info.clone + info.delete(:pipe) + + [200, info.to_json] + else + rc + end +end + +post '/ui/stopvnc/:id' do + if !settings.config[:vnc_enable] + return [403, "VNC sessions are disabled"] + end + + vm_id = params[:id] + vnc_hash = session['vnc'] + + if !vnc_hash || !vnc_hash[vm_id] + msg = "It seems there is no VNC proxy running for this machine" + return [403, msg] + end + + rc = @occi_server.stopvnc(vnc_hash[vm_id][:pipe]) + + if rc[0] == 200 + session['vnc'].delete(vm_id) + end + + rc +end diff --git a/src/cloud/occi/lib/ui/public/js/locale.js b/src/cloud/occi/lib/ui/public/js/locale.js index be816c7239..792772954f 100644 --- a/src/cloud/occi/lib/ui/public/js/locale.js +++ b/src/cloud/occi/lib/ui/public/js/locale.js @@ -45,7 +45,7 @@ function setLang(lang_str){ if (('localStorage' in window) && (window['localStorage'] !== null)){ localStorage['lang']=lang_str; }; - $.post('config',JSON.stringify({lang:lang_str}),function(){window.location.href = "./ui"}); + $.post('ui/config',JSON.stringify({lang:lang_str}),function(){window.location.href = "./ui"}); }; $(document).ready(function(){ diff --git a/src/cloud/occi/lib/ui/public/js/occi.js b/src/cloud/occi/lib/ui/public/js/occi.js index 004938dc67..569a75e527 100644 --- a/src/cloud/occi/lib/ui/public/js/occi.js +++ b/src/cloud/occi/lib/ui/public/js/occi.js @@ -413,7 +413,7 @@ var OCCI = { params.data.body = ''; OCCI.Action.update(params,OCCI.VM.resource,"saveas"); }, -/* "vnc" : function(params,startstop){ + "vnc" : function(params,startstop){ var callback = params.success; var callback_error = params.error; var id = params.data.id; @@ -423,7 +423,7 @@ var OCCI = { var action = OCCI.Helper.action(method); var request = OCCI.Helper.request(resource,method, id); $.ajax({ - url: "vm/" + id + "/" + method, + url: "ui/" + method + "/" + id, type: "POST", dataType: "json", success: function(response){ @@ -440,13 +440,16 @@ var OCCI = { }, "stopvnc" : function(params){ OCCI.VM.vnc(params,"stopvnc"); + }, +/* "monitor" : function(params){ OCCI.Action.monitor(params,OCCI.VM.resource,false); }, "monitor_all" : function(params){ OCCI.Action.monitor(params,OCCI.VM.resource,true); - }*/ + } +*/ }, "Image": { diff --git a/src/cloud/occi/lib/ui/public/js/plugins/compute.js b/src/cloud/occi/lib/ui/public/js/plugins/compute.js index 993faf93ab..5613a1b3a2 100644 --- a/src/cloud/occi/lib/ui/public/js/plugins/compute.js +++ b/src/cloud/occi/lib/ui/public/js/plugins/compute.js @@ -15,16 +15,19 @@ /* -------------------------------------------------------------------------- */ /*Virtual Machines tab plugin*/ -//var INCLUDE_URI = "vendor/noVNC/include/"; +var INCLUDE_URI = "vendor/noVNC/include/"; //var VM_HISTORY_LENGTH = 40; -/* + function loadVNC(){ var script = ''; document.write(script); } loadVNC(); +var vnc_enable=false; +var use_wss=false; +/* var vm_graphs = [ { title : tr("CPU"), monitor_resources : "cpu_usage", @@ -264,7 +267,6 @@ var vm_actions = { error: onError }, - /* "VM.startvnc" : { type: "single", call: OCCI.VM.startvnc, @@ -280,6 +282,7 @@ var vm_actions = { notify: true }, +/* "VM.monitor" : { type: "monitor", call : OCCI.VM.monitor, @@ -540,6 +543,10 @@ function updateVMInfo(request,vm){ '+tr("Memory")+'\ '+vm_info.MEMORY+'\ \ + \ + '+tr("Launch VNC session")+'\ + '+vncIcon(vm_info)+'\ + \ \ \
\ @@ -940,7 +947,7 @@ function setVMAutorefresh(){ },INTERVAL+someTime()); } -/* + function updateVNCState(rfb, state, oldstate, msg) { var s, sb, cad, klass; s = $D('VNC_status'); @@ -1016,7 +1023,7 @@ function setupVNC(){ Sunstone.runAction("VM.stopvnc",id); }); - $('.vnc',main_tabs_context).live("click",function(){ + $('.vnc').live("click",function(){ //Which VM is it? var id = $(this).attr('vm_id'); //Set attribute to dialog @@ -1029,7 +1036,7 @@ function setupVNC(){ function vncCallback(request,response){ rfb = new RFB({'target': $D('VNC_canvas'), - 'encrypt': false, + 'encrypt': use_wss, 'true_color': true, 'local_cursor': true, 'shared': true, @@ -1049,6 +1056,20 @@ function vncCallback(request,response){ } +function vncIcon(vm){ + var gr_icon; + if (vnc_enable){ + gr_icon = ''; + gr_icon += '\"'+tr("Open'; + } + else { + gr_icon = '\"'+tr("VNC'; + } + return gr_icon; +} + +/* + function vncIcon(vm){ var graphics = vm.TEMPLATE.GRAPHICS; var state = vm.STATE; @@ -1063,6 +1084,9 @@ function vncIcon(vm){ return gr_icon; } +*/ + +/* function vmMonitorError(req,error_json){ var message = error_json.error.message; var info = req.request.data[0].monitor; @@ -1070,9 +1094,9 @@ function vmMonitorError(req,error_json){ var id_suffix = labels.replace(/,/g,'_'); var id = '#vm_monitor_'+id_suffix; $('#vm_monitoring_tab '+id).html('
'+message+'
'); -} +}*/ + -*/ // At this point the DOM is ready and the sunstone.js ready() has been run. $(document).ready(function(){ @@ -1102,7 +1126,7 @@ $(document).ready(function(){ //setupCreateVMDialog(); setupSaveasDialog(); setVMAutorefresh(); - //setupVNC(); + setupVNC(); initCheckAllBoxes(dataTable_vMachines); tableCheckboxesListener(dataTable_vMachines); diff --git a/src/cloud/occi/lib/ui/public/js/plugins/configuration.js b/src/cloud/occi/lib/ui/public/js/plugins/configuration.js index c92844ac1e..de32d3e084 100644 --- a/src/cloud/occi/lib/ui/public/js/plugins/configuration.js +++ b/src/cloud/occi/lib/ui/public/js/plugins/configuration.js @@ -49,16 +49,29 @@ var config_tab = { Sunstone.addMainTab('config_tab',config_tab); $(document).ready(function(){ - if (lang) - $('table#config_table #lang_sel option[value="'+lang+'"]').attr('selected','selected'); - $('table#config_table #lang_sel').change(function(){ - setLang($(this).val()); - }); - $('#li_config_tab').click(function(){ hideDialog(); }); + //Set lang to the right value + if (lang) + $('table#config_table #lang_sel option[value="'+lang+'"]').attr('selected','selected'); + + //Listen to changes in language + $('table#config_table #lang_sel').change(function(){ + setLang($(this).val()); + }); + + //Vendor customization, change small logo $('div#logo img').attr('src',logo_small); + $.get('ui/config/vnc',function(response){ + if (response == "true" || response == "yes") + vnc_enable=true; //defined in compute.js + }); + + $.get('ui/config/wss', function(response){ + if (response == "true" || response == "yes") + use_wss=true; //defined in compute.js + }); }); \ No newline at end of file