diff --git a/install.sh b/install.sh index 991f8a3a89..e5a607317b 100755 --- a/install.sh +++ b/install.sh @@ -1195,6 +1195,7 @@ SUNSTONE_PUBLIC_JS_FILES="src/sunstone/public/js/layout.js \ src/sunstone/public/js/sunstone.js \ src/sunstone/public/js/sunstone-util.js \ src/sunstone/public/js/opennebula.js \ + src/sunstone/public/js/monitoring.js \ src/sunstone/public/js/locale.js" SUNSTONE_PUBLIC_JS_PLUGINS_FILES="\ @@ -1269,6 +1270,7 @@ SUNSTONE_PUBLIC_VENDOR_JQUERYLAYOUT="\ SUNSTONE_PUBLIC_VENDOR_FLOT="\ src/sunstone/public/vendor/flot/jquery.flot.min.js \ src/sunstone/public/vendor/flot/jquery.flot.navigate.min.js \ +src/sunstone/public/vendor/flot/jquery.flot.pie.min.js \ src/sunstone/public/vendor/flot/LICENSE.txt \ src/sunstone/public/vendor/flot/NOTICE \ src/sunstone/public/vendor/flot/README.txt" diff --git a/src/sunstone/public/css/application.css b/src/sunstone/public/css/application.css index 89339e91ca..bed2567dca 100644 --- a/src/sunstone/public/css/application.css +++ b/src/sunstone/public/css/application.css @@ -644,7 +644,7 @@ ul.action_list li a:hover{ font-family: serif; } -.legend { +.legend_p { color: #636663; margin-left: 10px; margin-top: 3px; @@ -653,6 +653,14 @@ ul.action_list li a:hover{ /* border-bottom: 1px solid #A3A3A3; */ } -.legend:before { +.legend_p:before { content: '⇨ '; +} + +.big_text { + color: #333333; + font-size: 5em; + font-family: serif; + text-align:center; + vertical-align:middle; } \ No newline at end of file diff --git a/src/sunstone/public/js/monitoring.js b/src/sunstone/public/js/monitoring.js new file mode 100644 index 0000000000..6bf9369f2f --- /dev/null +++ b/src/sunstone/public/js/monitoring.js @@ -0,0 +1,392 @@ +var SunstoneMonitoring = { + monitor : function(resource, list){ + + if (!SunstoneMonitoringConfig[resource]) + return false + + var monConfigs = SunstoneMonitoringConfig[resource].monitor + var monitoring = {} + for (conf in monConfigs){ + var conf_obj = monConfigs[conf] + var plotID = conf + var series = conf_obj.operation(resource, list, conf_obj) + monitoring[plotID]=series + } + + //Call back after monitorization is done + SunstoneMonitoringConfig[resource].plot(monitoring) + }, + plot : function(resource,plotID,container,series){ + var config = SunstoneMonitoringConfig[resource].monitor[plotID] + var options = config.plotOptions + $.plot(container,series,options) + }, + ops : { + partition : function(resource,list,config){ + var path = config.path + var partitionPath = config.partitionPath + var dataType = config.dataType + var partitions = {} + for (var i=0; i< list.length; i++){ + var elem = list[i][resource] + var value = path ? parseInt(explore_path(elem,path),10) : 1 + var partition = explore_path(elem, partitionPath) + + //Things on cluster none + if (partitionPath == "CLUSTER" && !partition.length) + partition = "none" + + if (!partitions[partition]) + partitions[partition] = value + else + partitions[partition] += value + } + + var series = [] + var i = 0; + for (partition in partitions) { + var value = partitions[partition] + var data; + switch (dataType){ + case "pie": + data = value; break + case "bars": + data = [[i,value]]; break + case "horizontal_bars": + data = [[value,i]]; break + default: + data = value; + } + var color = config.colorize ? config.colorize(partition) : null + series.push({ label: partition, + data: data, + color: color + }) + i++ + } + + return series + }, + hostCpuUsagePartition : function(resource,list,config){ + partitions = { + "Idle" : 0, + "Ok" : 0, + "Used" : 0, + "Working" : 0, + "Overloaded" : 0 + } + + for (var i=0; i< list.length; i++){ + var elem = list[i].HOST + var value = elem.HOST_SHARE.USED_CPU * 100 / + elem.HOST_SHARE.MAX_CPU + if (value > 80) + partitions["Overloaded"]++ + else if (value > 60) + partitions["Working"]++ + else if (value > 40) + partitions["Used"]++ + else if (value > 20) + partitions["Ok"]++ + else + partitions["Idle"]++ + } + + series = []; + for (partition in partitions) { + var data = partitions[partition] + var color = config.colorize ? config.colorize(partition) : null + series.push({ label: partition, + data: data, + }) + } + return series + }, + totalize : function(resource,list,config){ + return list.length + }, + singleBar : function(resource,list,config){ + var paths = config.paths + + var totals = new Array(paths.length) + for (var i=0; i< totals.length; i++) totals[i] = 0 + + var series = [] + + for (var i=0; i< list.length; i++){ + var elem = list[i][resource] + for (var j=0; j< paths.length; j++) + totals[j] += parseInt(explore_path(elem,paths[j]),10) + } + + for (var i=0; i< totals.length; i++){ + series.push({ + data: [[totals[i],0]], + label: paths[i], + color: config.colorize? config.colorize(paths[i]) : null + }) + } + return series + } + } +} + + +var SunstoneMonitoringConfig = { + "HOST" : { + plot: function(mon){ + plotHostMonitoring(mon) //not defined at parsing moment + }, + monitor : { + "statePie" : { + partitionPath: "STATE", + operation: SunstoneMonitoring.ops.partition, + dataType: "pie", + colorize: function(state){ + switch (state) { + case '0': return "rgb(239,201,86)" //yellow + case '1': return "rgb(175,216,248)" //blue + case '2': return "rgb(108,183,108)" //green + case '3': return "rgb(203,75,75)" //red + case '4': return "rgb(71,71,71)" //gray + case '5': return "rgb(160,160,160)" //light gray + } + }, + plotOptions : { + series: { pie: { show: true } }, + legend : { + labelFormatter: function(label, series){ + return OpenNebula.Helper.resource_state("host_simple",label) + + ' - ' + series.data[0][1] + ' (' + + Math.floor(series.percent) + '%' + ')'; + } + } + } + }, + "cpuPerCluster" : { + path: ["HOST_SHARE","CPU_USAGE"], + partitionPath: "CLUSTER_ID", + operation: SunstoneMonitoring.ops.partition, + dataType: "bars", + plotOptions: { + series: { bars: {show: true, barWidth: 0.5 }}, + xaxis: { show: false }, + yaxis: { min: 0 }, + legend : { + noColumns: 2, + labelFormatter: function(label){ + if (label == "-1") return "none" + return getClusterName(label) + } + } + } + }, + "memoryPerCluster" : { + path: ["HOST_SHARE","MEM_USAGE"], + partitionPath: "CLUSTER_ID", + operation: SunstoneMonitoring.ops.partition, + dataType: "bars", + plotOptions: { + series: { bars: {show: true, barWidth: 0.5 }}, + xaxis: { show: false }, + yaxis: { + tickFormatter : function(val,axis) { + return humanize_size(val); + }, + min: 0 + }, + legend : { + noColumns: 2, + labelFormatter: function(label){ + if (label == "-1") return "none" + return getClusterName(label) + } + } + } + }, + "globalCpuUsage" : { + partitionPath: ["HOST_SHARE", "USED_CPU"], + dataType: "pie", + operation: SunstoneMonitoring.ops.hostCpuUsagePartition, + plotOptions: { + series: { pie: { show: true } }, + } + }, + "totalHosts" : { + operation: SunstoneMonitoring.ops.totalize + }, + "cpuUsageBar" : { + paths: [ + ["HOST_SHARE","MAX_CPU"], + ["HOST_SHARE","USED_CPU"], + ["HOST_SHARE","CPU_USAGE"], + ], + operation: SunstoneMonitoring.ops.singleBar, + plotOptions: { + series: { bars: { show: true, + horizontal: true, + barWidth: 0.5 } + }, + yaxis: { show: false }, + xaxis: { min:0 }, + legend: { + noColumns: 3, + container: '#cpuUsageBar_legend', + labelFormatter: function(label, series){ + return label[1].toLowerCase() + } + } + } + }, + "memoryUsageBar" : { + paths: [ + ["HOST_SHARE","MAX_MEM"], + ["HOST_SHARE","USED_MEM"], + ["HOST_SHARE","MEM_USAGE"], + ], + operation: SunstoneMonitoring.ops.singleBar, + plotOptions: { + series: { bars: { show: true, + horizontal: true, + barWidth: 0.5 } + }, + yaxis: { show: false }, + xaxis: { + tickFormatter : function(val,axis) { + return humanize_size(val); + }, + min: 0 + }, + legend: { + noColumns: 3, + container: '#memoryUsageBar_legend', + labelFormatter: function(label, series){ + return label[1].toLowerCase() + } + } + } + }, + } + }, + "USER" : { + plot: function(mon){ + plotUserMonitoring(mon) + }, + monitor: { + "usersPerGroup" : { + partitionPath: "GNAME", + operation: SunstoneMonitoring.ops.partition, + dataType: "bars", + plotOptions: { + series: { bars: {show: true, barWidth: 0.5 }}, + xaxis: { show: false }, + yaxis: { tickDecimals: 0, + min: 0 }, + legend : { + noColumns: 2, + } + } + }, + "totalUsers" : { + operation: SunstoneMonitoring.ops.totalize + }, + } + }, + "ACL" : { + plot: function(mon){ + plotAclMonitoring(mon) + }, + monitor: { + "totalAcls" : { + operation: SunstoneMonitoring.ops.totalize + } + }, + }, + "GROUP" : { + plot: function(mon){ + plotGroupMonitoring(mon) + }, + monitor: { + "totalGroups" : { + operation: SunstoneMonitoring.ops.totalize + } + }, + }, + "VM" : { + plot: function(mon){ + plotVMMonitoring(mon) + }, + monitor: { + "totalVMs" : { + operation: SunstoneMonitoring.ops.totalize + }, + "statePie" : { + partitionPath: "STATE", + operation: SunstoneMonitoring.ops.partition, + dataType: "pie", + colorize: function(state){ + switch (state) { + case '0': return "rgb(160,160,160)" //light gray - init + case '1': return "rgb(239,201,86)" //yellow - pending + case '2': return "rgb(237,154,64)" //orange - hold + case '3': return "rgb(108,183,108)" //green - active + case '4': return "rgb(175,216,248)" //blue - stopped + case '5': return "rgb(112,164,205)" //dark blue - suspended + case '6': return "rgb(71,71,71)" //gray - done + case '7': return "rgb(203,75,75)" //red - failed + + } + }, + plotOptions : { + series: { pie: { show: true } }, + legend : { + labelFormatter: function(label, series){ + return OpenNebula.Helper.resource_state("vm",label) + + ' - ' + series.data[0][1] + ' (' + + Math.floor(series.percent) + '%' + ')'; + } + } + } + }, + "netUsageBar" : { + paths: [ "NET_RX", "NET_TX" ], + operation: SunstoneMonitoring.ops.singleBar, + plotOptions: { + series: { bars: { show: true, + horizontal: true, + barWidth: 0.5 } + }, + yaxis: { show: false }, + xaxis: { + min: 0, + tickFormatter : function(val,axis) { + return humanize_size(val); + }, + }, + legend: { + noColumns: 3, + container: '#netUsageBar_legend', + labelFormatter: function(label, series){ + return label + " - " + humanize_size(series.data[0][0]) + } + } + } + }, + } + } +} + + + + +function explore_path(elem,path){ + if (!$.isArray(path)) //base case - non array + return elem[path] + else if (path.length == 1) //base case - array + return elem[path[0]] + else {//recurse + var array = path.slice(0) //clone path + var p = array.splice(0,1) + return explore_path(elem[p],array) + } +} \ No newline at end of file diff --git a/src/sunstone/public/js/opennebula.js b/src/sunstone/public/js/opennebula.js index 2e4cd6a96b..1324599362 100644 --- a/src/sunstone/public/js/opennebula.js +++ b/src/sunstone/public/js/opennebula.js @@ -249,8 +249,10 @@ var OpenNebula = { data: {timeout: timeout}, dataType: "json", success: function(response){ + var list = OpenNebula.Helper.pool(resource,response) + SunstoneMonitoring.monitor(resource, list) return callback ? - callback(request, OpenNebula.Helper.pool(resource,response)) : null; + callback(request, list) : null; }, error: function(response) { diff --git a/src/sunstone/public/js/plugins/acls-tab.js b/src/sunstone/public/js/plugins/acls-tab.js index 877ccf0012..2e54bf4147 100644 --- a/src/sunstone/public/js/plugins/acls-tab.js +++ b/src/sunstone/public/js/plugins/acls-tab.js @@ -40,7 +40,7 @@ var acls_tab_content = '\ \
\ ?\ -

\ +

\ '+tr("This table shows the ACLs rules broken down to easier the reading and meaning of each one. You can show the ACL original string by clicking on Show/Hide columns.")+'\

\
\ diff --git a/src/sunstone/public/js/plugins/config-tab.js b/src/sunstone/public/js/plugins/config-tab.js index 8e583f9ddd..7907a523c6 100644 --- a/src/sunstone/public/js/plugins/config-tab.js +++ b/src/sunstone/public/js/plugins/config-tab.js @@ -51,10 +51,10 @@ var config_tab_content = \
\ ?\ -

\ +

\ '+tr("These options are stored in your OpenNebula user template.")+'\

\ -

\ +

\ '+tr("WSS connection requires additional configuration of Sunstone Server and that the SSL certificate is considered valid by your browser.")+'\

\
\ diff --git a/src/sunstone/public/js/plugins/dashboard-tab.js b/src/sunstone/public/js/plugins/dashboard-tab.js index 2cf395e157..c9588b59e2 100644 --- a/src/sunstone/public/js/plugins/dashboard-tab.js +++ b/src/sunstone/public/js/plugins/dashboard-tab.js @@ -14,90 +14,51 @@ /* limitations under the License. */ /* -------------------------------------------------------------------------- */ -/** HISTORY_LENGTH currently ignored on server, but it does not harm to have it**/ -var HISTORY_LENGTH=40; -var GRAPH_AUTOREFRESH_INTERVAL=60000; //60 secs - -var graph1 = { - title : "graph1", - monitor_resources : "HOST_SHARE/CPU_USAGE,HOST_SHARE/USED_CPU,HOST_SHARE/MAX_CPU", - history_length : HISTORY_LENGTH -}; - -var graph2 = { - title : "graph2", - monitor_resources : "HOST_SHARE/MEM_USAGE,HOST_SHARE/USED_MEM,HOST_SHARE/MAX_MEM", - history_length : HISTORY_LENGTH -}; - -var graph3 = { - title : "graph3", - monitor_resources : "total,active,error", - history_length : HISTORY_LENGTH -}; - -var graph4 = { - title : "graph4", - monitor_resources : "NET_TX,NET_RX", - history_length : HISTORY_LENGTH -}; - var dashboard_tab_content = '\ \ -
\ -\ +\ -
\ +\ \ \ \ \
\
\ -

' + tr("Summary of resources") + '

\ +

' + tr("Hosts") + '

\
\ -\ \ +\ \ - \ - \ + \ + \ + \ \ \ - \ - \ + \ + \ + \ +\ + \ + \ + \ + \ \ \ - \ - \ + \ + \ +\ + \ + \ + \ + \ \ \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ + \ \ +\
' + tr("Hosts (total/active)") + '' + tr("Total Hosts") + '' + tr("State") + '' + tr("Global CPU Usage") + '
' + tr("Clusters") + '
\ +
' + tr("Used vs. Max CPU") + '
' + tr("Groups") + '\ +
\ +
' + tr("Used vs. Max Memory") + '
' + tr("VM Templates") + '
' + - tr("VM Instances")+ ' (' + - tr("total") + '/' + - tr("running") + '/' + - tr("failed") + ')
' + tr("Virtual Networks") + '
' + tr("Datastores") + '
' + tr("Images") + '
' + tr("Users")+'
' + tr("ACL Rules") + '\ +
\ +
\ \
\ @@ -107,49 +68,108 @@ var dashboard_tab_content =
\
\ -

' + tr("Quickstart") + '

\ - \ +

' + tr("Clusters") + '

\ +
\ +\ + \ +\ + \ + \ + \ + \ + \ + \ + \ +\ + \ + \ + \ + \ + \ + \ + \ +\ +
' + tr("Allocated CPU per cluster") + '
' + tr("Allocated Memory per cluster") + '
\ +\ +
\
\
\
\ -\ +
\ +\ \ \ @@ -166,6 +186,90 @@ var dashboard_tab = { Sunstone.addMainTab('dashboard_tab',dashboard_tab); +var $dashboard; + +function updateDashboard(){ + //mock +} + +function plotHostMonitoring(monitoring){ + $('#totalHosts', $dashboard).text(monitoring['totalHosts']) + delete monitoring['totalHosts'] + + if (!$dashboard.is(':visible')) return; + + for (plotID in monitoring){ + var container = $('div#'+plotID,$dashboard); + SunstoneMonitoring.plot("HOST", + plotID, + container, + monitoring[plotID]); + }; +} + +function plotUserMonitoring(monitoring){ + $('#totalUsers', $dashboard).text(monitoring['totalUsers']) + + if (!$dashboard.is(':visible')) return; + + var container = $('div#usersPerGroup',$dashboard); + SunstoneMonitoring.plot('USER', + 'usersPerGroup', + container, + monitoring['usersPerGroup']); +} + +function plotAclMonitoring(monitoring){ + $('#totalAcls', $dashboard).text(monitoring['totalAcls']) +} + +function plotGroupMonitoring(monitoring){ + $('#totalGroups', $dashboard).text(monitoring['totalGroups']) +} + +//Permanent storage for last value of aggregated network usage +//Used to calculate bandwidth +var netUsage = { + time : new Date().getTime(), + up : 0, + down : 0 +} + +function plotVMMonitoring(monitoring){ + $('#totalVMs', $dashboard).text(monitoring['totalVMs']) + + var t = ((new Date().getTime()) - netUsage.time) / 1000 //in secs + var bandwidth_up = monitoring['netUsageBar'][1].data[0][0] - netUsage.up + bandwidth_up /= t + var bandwidth_up_str = humanize_size(bandwidth_up) + "/s" //bytes /sec + var bandwidth_down = monitoring['netUsageBar'][0].data[0][0] - netUsage.down + bandwidth_down /= t + var bandwidth_down_str = humanize_size(bandwidth_down) + "/s" //bytes /sec + + if (bandwidth_up >= 0) + $('#bandwidth_up', $dashboard).text(bandwidth_up_str) + if (bandwidth_down >= 0) + $('#bandwidth_down', $dashboard).text(bandwidth_down_str) + + netUsage.time = new Date().getTime() + netUsage.up = monitoring['netUsageBar'][1].data[0][0] + netUsage.down = monitoring['netUsageBar'][0].data[0][0] + + if (!$dashboard.is(':visible')) return; + + var container = $('div#vmStatePie',$dashboard); + SunstoneMonitoring.plot('VM', + 'statePie', + container, + monitoring['statePie']); + + container = $('div#netUsageBar',$dashboard); + SunstoneMonitoring.plot('VM', + 'netUsageBar', + container, + monitoring['netUsageBar']); +} + function plot_global_graph(data,info){ var context = $('#historical_table',main_tabs_context); var id = info.title; @@ -212,95 +316,7 @@ function plot_global_graph(data,info){ $.plot($('#'+id+'_graph',context),series,options); } -function graph_autorefresh(){ - setInterval(function(){ - refresh_graphs(); - },GRAPH_AUTOREFRESH_INTERVAL+someTime()); - -} - -function refresh_graphs(){ - Sunstone.runAction("Host.monitor_all", graph1); - Sunstone.runAction("Host.monitor_all", graph2); - Sunstone.runAction("VM.monitor_all", graph3); - Sunstone.runAction("VM.monitor_all", graph4); -} $(document).ready(function(){ - emptyDashboard(); - -// refresh_graphs(); -// graph_autorefresh(); - -}); - -//puts the dashboard values into "retrieving" -function emptyDashboard(){ - $("#dashboard_tab .value_td span",main_tabs_context).html(spinner); -} - - -function updateDashboard(what,json_info){ - var db = $('#dashboard_tab',main_tabs_context); - switch (what){ - case "hosts": - var total_hosts=json_info.length; - var active_hosts=0; - $.each(json_info,function(){ - if (parseInt(this.HOST.STATE) < 3){ - active_hosts++;} - }); - $('#total_hosts',db).html(total_hosts+' / '); - $('#active_hosts',db).html(active_hosts); - break; - case "groups": - var total_groups=json_info.length; - $('#total_groups',db).html(total_groups); - break; - case "vms": - var total_vms=json_info.length; - var running_vms=0; - failed_vms=0; - $.each(json_info,function(){ - vm_state = parseInt(this.VM.STATE); - if (vm_state == 3){ - running_vms++; - } - else if (vm_state == 7) { - failed_vms++; - } - }); - $('#total_vms',db).html(total_vms+' / '); - $('#running_vms',db).html(running_vms+' / '); - $('#failed_vms',db).html(failed_vms); - break; - case "vnets": - var total_vnets=json_info.length; - $('#total_vnets',db).html(total_vnets); - break; - case "users": - var total_users=json_info.length; - $('#total_users',db).html(total_users); - break; - case "images": - var total_images=json_info.length; - $('#total_images',db).html(total_images); - break; - case "templates": - var total_templates=json_info.length; - $('#total_templates',db).html(total_templates); - break; - case "acls": - var total_acls=json_info.length; - $('#total_acls',db).html(total_acls); - break; - case "clusters": - var total_clusters=json_info.length; - $('#total_clusters',db).html(total_clusters); - break; - case "datastores": - var total_datastores=json_info.length; - $('#total_datastores',db).html(total_datastores); - break; - } -}; + $dashboard = $('#dashboard_tab', main_tabs_context); +}); \ No newline at end of file diff --git a/src/sunstone/public/js/plugins/datastores-tab.js b/src/sunstone/public/js/plugins/datastores-tab.js index 0cd99ff457..195eb2a750 100644 --- a/src/sunstone/public/js/plugins/datastores-tab.js +++ b/src/sunstone/public/js/plugins/datastores-tab.js @@ -41,7 +41,7 @@ var datastores_tab_content = '\
\
\ -

' + tr("Historical monitoring information") + '

\ +

' + tr("Virtual Machines") + '

\
\ -\
\
\
\
\ ?\ -

\ +

\ '+tr("Datatables are sets of images which share a common transfer driver. i.e. Images in a SSH datastore will be copied to the hosts using SSH when deploying a Virtual Machine.")+'\

\
\ diff --git a/src/sunstone/public/js/plugins/groups-tab.js b/src/sunstone/public/js/plugins/groups-tab.js index d0e3834c6b..307e868ced 100644 --- a/src/sunstone/public/js/plugins/groups-tab.js +++ b/src/sunstone/public/js/plugins/groups-tab.js @@ -37,7 +37,7 @@ var groups_tab_content = '\
\
\ ?\ -

\ +

\ '+tr("Tip: Refresh the list if it only shows user ids in the Users column.")+'\

\
\ diff --git a/src/sunstone/public/js/plugins/hosts-tab.js b/src/sunstone/public/js/plugins/hosts-tab.js index 32a5316ba0..7ce1947885 100644 --- a/src/sunstone/public/js/plugins/hosts-tab.js +++ b/src/sunstone/public/js/plugins/hosts-tab.js @@ -59,13 +59,13 @@ var hosts_tab_content = '\
\
\ ?\ -

\ +

\ '+tr("CPU Use is calculated as the minimum between (total CPU - real CPU usage) and (allocated CPU). Real CPU usage is provided by the hosts monitoring driver. Available CPU is calculated using the information from the CPU setting of the VMs running on that host (allocated CPU)")+'\

\ -

\ +

\ '+tr("Memory use is calculated according to the information provided by the host monitoring driver.")+'\

\ -

\ +

\ '+tr("You can get monitoring graphs by clicking in the desired host and visiting the monitoring information tab. Note that oneacctd must be running for this information to be updated/available.")+'\

\
\ diff --git a/src/sunstone/public/js/plugins/images-tab.js b/src/sunstone/public/js/plugins/images-tab.js index 43a2660033..61e0e1d83a 100644 --- a/src/sunstone/public/js/plugins/images-tab.js +++ b/src/sunstone/public/js/plugins/images-tab.js @@ -44,7 +44,7 @@ var images_tab_content = '\
\
\ ?\ -

\ +

\ '+tr("Size and registration time are hidden colums. Note that persistent images can only be used by 1 VM. To change image datastore, please re-register the image.")+'\

\
\ diff --git a/src/sunstone/public/js/plugins/templates-tab.js b/src/sunstone/public/js/plugins/templates-tab.js index 88543322a1..2870cd6209 100644 --- a/src/sunstone/public/js/plugins/templates-tab.js +++ b/src/sunstone/public/js/plugins/templates-tab.js @@ -37,10 +37,10 @@ var templates_tab_content = '\ \
\ ?\ -

\ +

\ '+tr("Clicking `instantiate` will instantly create new Virtual Machines from the selected templates and name one-id. If you want to assign a specific name to a new VM, or launch several instances at once, use Virtual Machines->New button.")+'\

\ -

\ +

\ '+tr("You can clone a template to obtain a copy from an existing template. This copy will be owned by you.")+'\

\
\ diff --git a/src/sunstone/public/js/plugins/users-tab.js b/src/sunstone/public/js/plugins/users-tab.js index 34001fafd7..78563dfaab 100644 --- a/src/sunstone/public/js/plugins/users-tab.js +++ b/src/sunstone/public/js/plugins/users-tab.js @@ -41,12 +41,12 @@ var users_tab_content = '\ \
\ ?\ -

\ +

\ '+ tr("Tip: You can save any information in the user template, in the form of VAR=VAL.")+ '\

\ -

\ +

\ '+ tr("Tip: SSH authentication method is not available for web UI access.")+ '\ diff --git a/src/sunstone/public/js/plugins/vms-tab.js b/src/sunstone/public/js/plugins/vms-tab.js index fef0aaf422..922af774ff 100644 --- a/src/sunstone/public/js/plugins/vms-tab.js +++ b/src/sunstone/public/js/plugins/vms-tab.js @@ -74,10 +74,10 @@ var vms_tab_content = '\ \

\ ?\ -

\ +

\ '+tr("CPU, Memory and Start time are hidden columns by default. You can get monitoring graphs by clicking on the desired VM and visiting the monitoring information tab.")+'\

\ -

\ +

\ '+tr("VNC console requires previous install of the noVNC addon. Check Sunstone documentation for more information.")+'\

\
\ diff --git a/src/sunstone/public/js/plugins/vnets-tab.js b/src/sunstone/public/js/plugins/vnets-tab.js index 31f68bb3c1..e67ab575e5 100644 --- a/src/sunstone/public/js/plugins/vnets-tab.js +++ b/src/sunstone/public/js/plugins/vnets-tab.js @@ -40,7 +40,7 @@ var vnets_tab_content = '\ \
\ ?\ -

\ +

\ '+tr("Tip: edit the leases of a network by clicking on one and going to the lease management tab.")+'\

\
\ diff --git a/src/sunstone/public/vendor/flot/jquery.flot.pie.min.js b/src/sunstone/public/vendor/flot/jquery.flot.pie.min.js new file mode 100644 index 0000000000..b7bf870d75 --- /dev/null +++ b/src/sunstone/public/vendor/flot/jquery.flot.pie.min.js @@ -0,0 +1 @@ +(function(b){function c(D){var h=null;var L=null;var n=null;var B=null;var p=null;var M=0;var F=true;var o=10;var w=0.95;var A=0;var d=false;var z=false;var j=[];D.hooks.processOptions.push(g);D.hooks.bindEvents.push(e);function g(O,N){if(N.series.pie.show){N.grid.show=false;if(N.series.pie.label.show=="auto"){if(N.legend.show){N.series.pie.label.show=false}else{N.series.pie.label.show=true}}if(N.series.pie.radius=="auto"){if(N.series.pie.label.show){N.series.pie.radius=3/4}else{N.series.pie.radius=1}}if(N.series.pie.tilt>1){N.series.pie.tilt=1}if(N.series.pie.tilt<0){N.series.pie.tilt=0}O.hooks.processDatapoints.push(E);O.hooks.drawOverlay.push(H);O.hooks.draw.push(r)}}function e(P,N){var O=P.getOptions();if(O.series.pie.show&&O.grid.hoverable){N.unbind("mousemove").mousemove(t)}if(O.series.pie.show&&O.grid.clickable){N.unbind("click").click(l)}}function G(O){var P="";function N(S,T){if(!T){T=0}for(var R=0;Rh.width-n){B=h.width-n}}}function v(O){for(var N=0;N0){R.push({data:[[1,P]],color:N,label:a.series.pie.combine.label,angle:(P*(Math.PI*2))/M,percent:(P/M*100)})}return R}function r(S,Q){if(!L){return}ctx=Q;I();var T=S.getData();var P=0;while(F&&P0){n*=w}P+=1;N();if(a.series.pie.tilt<=0.8){O()}R()}if(P>=o){N();L.prepend('
Could not draw pie with labels contained inside canvas
')}if(S.setSeries&&S.insertLegend){S.setSeries(T);S.insertLegend()}function N(){ctx.clearRect(0,0,h.width,h.height);L.children().filter(".pieLabel, .pieLabelBackground").remove()}function O(){var Z=5;var Y=15;var W=10;var X=0.02;if(a.series.pie.radius>1){var U=a.series.pie.radius}else{var U=n*a.series.pie.radius}if(U>=(h.width/2)-Z||U*a.series.pie.tilt>=(h.height/2)-Y||U<=W){return}ctx.save();ctx.translate(Z,Y);ctx.globalAlpha=X;ctx.fillStyle="#000";ctx.translate(B,p);ctx.scale(1,a.series.pie.tilt);for(var V=1;V<=W;V++){ctx.beginPath();ctx.arc(0,0,U,0,Math.PI*2,false);ctx.fill();U-=V}ctx.restore()}function R(){startAngle=Math.PI*a.series.pie.startAngle;if(a.series.pie.radius>1){var U=a.series.pie.radius}else{var U=n*a.series.pie.radius}ctx.save();ctx.translate(B,p);ctx.scale(1,a.series.pie.tilt);ctx.save();var Y=startAngle;for(var W=0;W1e-9){ctx.moveTo(0,0)}else{if(b.browser.msie){ab-=0.0001}}ctx.arc(0,0,U,Y,Y+ab,false);ctx.closePath();Y+=ab;if(aa){ctx.fill()}else{ctx.stroke()}}function V(){var ac=startAngle;if(a.series.pie.label.radius>1){var Z=a.series.pie.label.radius}else{var Z=n*a.series.pie.label.radius}for(var ab=0;ab=a.series.pie.label.threshold*100){aa(T[ab],ac,ab)}ac+=T[ab].angle}function aa(ap,ai,ag){if(ap.data[0][1]==0){return}var ar=a.legend.labelFormatter,aq,ae=a.series.pie.label.formatter;if(ar){aq=ar(ap.label,ap)}else{aq=ap.label}if(ae){aq=ae(aq,ap)}var aj=((ai+ap.angle)+ai)/2;var ao=B+Math.round(Math.cos(aj)*Z);var am=p+Math.round(Math.sin(aj)*Z)*a.series.pie.tilt;var af=''+aq+"";L.append(af);var an=L.children("#pieLabel"+ag);var ad=(am-an.height()/2);var ah=(ao-an.width()/2);an.css("top",ad);an.css("left",ah);if(0-ad>0||0-ah>0||h.height-(ad+an.height())<0||h.width-(ah+an.width())<0){F=true}if(a.series.pie.label.background.opacity!=0){var ak=a.series.pie.label.background.color;if(ak==null){ak=ap.color}var al="top:"+ad+"px;left:"+ah+"px;";b('
').insertBefore(an).css("opacity",a.series.pie.label.background.opacity)}}}}}function J(N){if(a.series.pie.innerRadius>0){N.save();innerRadius=a.series.pie.innerRadius>1?a.series.pie.innerRadius:n*a.series.pie.innerRadius;N.globalCompositeOperation="destination-out";N.beginPath();N.fillStyle=a.series.pie.stroke.color;N.arc(0,0,innerRadius,0,Math.PI*2,false);N.fill();N.closePath();N.restore();N.save();N.beginPath();N.strokeStyle=a.series.pie.stroke.color;N.arc(0,0,innerRadius,0,Math.PI*2,false);N.stroke();N.closePath();N.restore()}}function s(Q,R){for(var S=false,P=-1,N=Q.length,O=N-1;++P1?O.series.pie.radius:n*O.series.pie.radius;for(var Q=0;Q1?P.series.pie.radius:n*P.series.pie.radius;R.save();R.translate(B,p);R.scale(1,P.series.pie.tilt);for(i=0;i1e-9){R.moveTo(0,0)}R.arc(0,0,N,S.startAngle,S.startAngle+S.angle,false);R.closePath();R.fill()}}}var a={series:{pie:{show:false,radius:"auto",innerRadius:0,startAngle:3/2,tilt:1,offset:{top:0,left:"auto"},stroke:{color:"#FFF",width:1},label:{show:"auto",formatter:function(d,e){return'
'+d+"
"+Math.round(e.percent)+"%
"},radius:1,background:{color:null,opacity:0},threshold:0},combine:{threshold:-1,color:null,label:"Other"},highlight:{opacity:0.5}}}};b.plot.plugins.push({init:c,options:a,name:"pie",version:"1.0"})})(jQuery); \ No newline at end of file diff --git a/src/sunstone/views/index.erb b/src/sunstone/views/index.erb index 979c687c09..62abea7888 100644 --- a/src/sunstone/views/index.erb +++ b/src/sunstone/views/index.erb @@ -20,6 +20,7 @@ + @@ -37,6 +38,7 @@ + <% @plugins["plugins"].each do |plugin| %>