mirror of
https://github.com/OpenNebula/one.git
synced 2025-02-28 17:57:22 +03:00
* Websocket autorefresh * Integrate autorefresh in sunstone-server Signed-off-by: Frederick Borges <fborges@opennebula.io>
This commit is contained in:
parent
8306405411
commit
94198be481
@ -460,7 +460,8 @@ VAR_DIRS="$VAR_LOCATION/remotes \
|
||||
SUNSTONE_DIRS="$SUNSTONE_LOCATION/routes \
|
||||
$SUNSTONE_LOCATION/models \
|
||||
$SUNSTONE_LOCATION/models/OpenNebulaJSON \
|
||||
$SUNSTONE_LOCATION/views"
|
||||
$SUNSTONE_LOCATION/views \
|
||||
$SUNSTONE_LOCATION/services"
|
||||
|
||||
SUNSTONE_MINIFIED_DIRS="$SUNSTONE_LOCATION/public \
|
||||
$SUNSTONE_LOCATION/public/dist \
|
||||
@ -735,6 +736,7 @@ INSTALL_SUNSTONE_FILES=(
|
||||
SUNSTONE_MODELS_JSON_FILES:$SUNSTONE_LOCATION/models/OpenNebulaJSON
|
||||
SUNSTONE_VIEWS_FILES:$SUNSTONE_LOCATION/views
|
||||
SUNSTONE_ROUTES_FILES:$SUNSTONE_LOCATION/routes
|
||||
SUNSTONE_SERVICES_FILES:$SUNSTONE_LOCATION/services
|
||||
)
|
||||
|
||||
INSTALL_SUNSTONE_PUBLIC_MINIFIED_FILES=(
|
||||
|
@ -124,6 +124,7 @@ group :sunstone do
|
||||
gem 'memcache-client'
|
||||
gem 'dalli'
|
||||
gem 'rotp'
|
||||
gem 'sinatra-websocket'
|
||||
end
|
||||
|
||||
group :oca do
|
||||
|
@ -258,3 +258,10 @@
|
||||
:threshold_min: 0
|
||||
:threshold_low: 33
|
||||
:threshold_high: 66
|
||||
|
||||
################################################################################
|
||||
# Autorefresh websocket configuration
|
||||
################################################################################
|
||||
|
||||
:zeromq_server: tcp://localhost:2101
|
||||
:autorefresh_ip: 127.0.0.1
|
@ -39,6 +39,7 @@ define(function(require) {
|
||||
var Menu = require('utils/menu');
|
||||
var Locale = require('utils/locale');
|
||||
var UserAndZoneTemplate = require('hbs!sunstone/user_and_zone');
|
||||
var Websocket = require("utils/websocket");
|
||||
|
||||
var _commonDialogs = [
|
||||
require('utils/dialogs/confirm'),
|
||||
@ -73,6 +74,8 @@ define(function(require) {
|
||||
Sunstone.showTab(PROVISION_TAB_ID);
|
||||
}
|
||||
|
||||
Websocket.start();
|
||||
|
||||
$('#loading').hide();
|
||||
});
|
||||
|
||||
|
@ -181,6 +181,9 @@ define(function(require) {
|
||||
},
|
||||
"isExtendedVmInfo": _config["system_config"] && _config["system_config"]["get_extended_vm_info"] && _config["system_config"]["get_extended_vm_info"] === "true",
|
||||
"isLogEnabled": _config["zone_id"] === _config["id_own_federation"] ? true : false,
|
||||
"autorefreshWSS": _config["system_config"]["autorefresh_wss"],
|
||||
"autorefreshIP": _config["system_config"]["autorefresh_ip"],
|
||||
"autorefreshPort": _config["system_config"]["autorefresh_port"],
|
||||
};
|
||||
|
||||
return Config;
|
||||
|
@ -811,7 +811,7 @@ define(function(require) {
|
||||
return context.data("element");
|
||||
};
|
||||
|
||||
var _insertPanels = function(tabName, info, contextTabId, context) {
|
||||
var _insertPanels = function(tabName, info, contextTabId, context, autorefresh=false) {
|
||||
var context = context || $(".sunstone-info", $("#" + tabName));
|
||||
|
||||
context.data("element", info[Object.keys(info)[0]]);
|
||||
@ -885,10 +885,12 @@ define(function(require) {
|
||||
|
||||
context.html(html);
|
||||
$.each(SunstoneCfg["tabs"][tabName]["panelInstances"], function(panelName, panel) {
|
||||
panel.setup(context);
|
||||
if (!autorefresh || panelName == "vm_info_tab"){
|
||||
panel.setup(context);
|
||||
|
||||
if(isRefresh && prevPanelStates[panelName] && panel.setState){
|
||||
panel.setState( prevPanelStates[panelName], context );
|
||||
if(isRefresh && prevPanelStates[panelName] && panel.setState){
|
||||
panel.setState( prevPanelStates[panelName], context );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -914,6 +916,11 @@ define(function(require) {
|
||||
}
|
||||
};
|
||||
|
||||
var _autorefreshVM = function(tabName, info, contextTabId, context) {
|
||||
_insertPanels(tabName, info, contextTabId, context, true);
|
||||
};
|
||||
|
||||
|
||||
//Runs a predefined action. Wraps the calls to opennebula.js and
|
||||
//can be use to run action depending on conditions and notify them
|
||||
//if desired. Returns 1 if some problem has been detected: i.e
|
||||
@ -1358,6 +1365,7 @@ define(function(require) {
|
||||
|
||||
"insertTabs": _insertTabs,
|
||||
"insertPanels": _insertPanels,
|
||||
"autorefreshVM": _autorefreshVM,
|
||||
"getElementRightInfo": _getElementRightInfo,
|
||||
|
||||
"showTab": _showTab,
|
||||
|
@ -27,6 +27,7 @@ define(function(require) {
|
||||
var TemplateTableVcenter = require("utils/panel/template-table");
|
||||
var OpenNebula = require("opennebula");
|
||||
var Navigation = require("utils/navigation");
|
||||
var Websocket = require("utils/websocket");
|
||||
|
||||
/*
|
||||
TEMPLATES
|
||||
@ -169,5 +170,8 @@ define(function(require) {
|
||||
}
|
||||
TemplateTable.setup(strippedTemplate, RESOURCE, this.element.ID, context, unshownValues, strippedTemplateVcenter);
|
||||
TemplateTableVcenter.setup(strippedTemplateVcenter, RESOURCE, this.element.ID, context, unshownValues, strippedTemplate);
|
||||
|
||||
Websocket.subscribe(this.element.ID);
|
||||
|
||||
}
|
||||
});
|
||||
|
@ -33,12 +33,12 @@
|
||||
{{{renameTrHTML}}}
|
||||
<tr>
|
||||
<td class="key_td">{{tr "State"}}</td>
|
||||
<td class="value_td">{{stateStr}}</td>
|
||||
<td id="state_value" class="value_td">{{stateStr}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="key_td">{{tr "LCM State"}}</td>
|
||||
<td class="value_td">{{lcmStateStr}}</td>
|
||||
<td id="lcm_state_value" class="value_td">{{lcmStateStr}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
112
src/sunstone/public/app/utils/websocket.js
Normal file
112
src/sunstone/public/app/utils/websocket.js
Normal file
@ -0,0 +1,112 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
define(function (require) {
|
||||
|
||||
var Config = require("sunstone-config");
|
||||
var Sunstone = require('sunstone');
|
||||
|
||||
|
||||
// user config
|
||||
const wss = Config.autorefreshWSS || 'ws';
|
||||
const port = Config.autorefreshPort || 9869;
|
||||
const host = Config.autorefreshIP || '127.0.0.1';
|
||||
|
||||
var address = wss + "://" + host + ":" + port + "/ws";
|
||||
var ws = new WebSocket(address);
|
||||
|
||||
var _start = function () {
|
||||
ws.addEventListener('open', function (event) {
|
||||
console.log("Connected to websocket");
|
||||
ws.readyState = 1;
|
||||
// Send CSRF token
|
||||
var msg = {
|
||||
"STATE": ws.readyState,
|
||||
"ACTION": "authenticate",
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify(msg));
|
||||
});
|
||||
|
||||
// Listen for messages
|
||||
ws.addEventListener('message', function (event) {
|
||||
var vm_info = JSON.parse(event.data);
|
||||
// console.log(vm_info);
|
||||
var response = { "VM": vm_info.HOOK_MESSAGE.VM };
|
||||
var request = {
|
||||
"request": {
|
||||
"data": [response.ID],
|
||||
"method": "show",
|
||||
"resource": "VM"
|
||||
}
|
||||
}
|
||||
|
||||
// update VM
|
||||
|
||||
var TAB_ID = "vms-tab";
|
||||
var tab = $('#' + TAB_ID);
|
||||
Sunstone.getDataTable(TAB_ID).updateElement(request, response);
|
||||
if (Sunstone.rightInfoVisible(tab) && vm_info.HOOK_MESSAGE.RESOURCE_ID == Sunstone.rightInfoResourceId(tab)) {
|
||||
Sunstone.autorefreshVM(TAB_ID, response);
|
||||
}
|
||||
|
||||
if (vm_info.HOOK_MESSAGE.STATE == "DONE"){
|
||||
Sunstone.getDataTable(TAB_ID).waitingNodes();
|
||||
Sunstone.runAction("VM.list", {force: true});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Close Socket when close browser or tab.
|
||||
window.onbeforeunload = function () {
|
||||
_close();
|
||||
};
|
||||
};
|
||||
|
||||
var _subscribe = function (vm_id, context) {
|
||||
var msg = {
|
||||
"SUBSCRIBE": true,
|
||||
"VM": vm_id
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify(msg));
|
||||
};
|
||||
|
||||
var _unsubscribe = function (vm_id) {
|
||||
var msg = {
|
||||
"SUBSCRIBE": false,
|
||||
"VM": vm_id
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify(msg));
|
||||
};
|
||||
|
||||
var _close = function () {
|
||||
ws.onclose = function () { }; // disable onclose handler first
|
||||
ws.close()
|
||||
};
|
||||
|
||||
|
||||
|
||||
var websocket = {
|
||||
"start": _start,
|
||||
"subscribe": _subscribe,
|
||||
"unsubscribe": _unsubscribe,
|
||||
"close": _close
|
||||
};
|
||||
|
||||
return websocket;
|
||||
});
|
@ -123,6 +123,12 @@ require 'CloudAuth'
|
||||
require 'SunstoneServer'
|
||||
require 'SunstoneViews'
|
||||
|
||||
require 'sinatra-websocket'
|
||||
require 'eventmachine'
|
||||
require 'json'
|
||||
require 'active_support/core_ext/hash'
|
||||
require 'ffi-rzmq'
|
||||
|
||||
begin
|
||||
require "SunstoneWebAuthn"
|
||||
webauthn_avail = true
|
||||
@ -161,6 +167,7 @@ CloudServer.print_configuration($conf)
|
||||
set :config, $conf
|
||||
set :bind, $conf[:host]
|
||||
set :port, $conf[:port]
|
||||
set :sockets, []
|
||||
|
||||
if (proxy = $conf[:proxy])
|
||||
ENV['http_proxy'] = proxy
|
||||
@ -262,6 +269,35 @@ configure do
|
||||
set :erb, :trim => '-'
|
||||
end
|
||||
|
||||
#start Autorefresh server
|
||||
|
||||
## 0MQ variables
|
||||
@context = ZMQ::Context.new(1)
|
||||
@subscriber = @context.socket(ZMQ::SUB)
|
||||
|
||||
## Subscribe to VM changes
|
||||
@subscriber.setsockopt(ZMQ::SUBSCRIBE, "EVENT VM")
|
||||
@subscriber.connect($conf[:zeromq_server])
|
||||
|
||||
# Create a thread to get ZeroMQ messages
|
||||
Thread.new do
|
||||
loop do
|
||||
key = ''
|
||||
content = ''
|
||||
|
||||
@subscriber.recv_string(key)
|
||||
@subscriber.recv_string(content)
|
||||
|
||||
message = Hash.from_xml(Base64.decode64(content)).to_json
|
||||
|
||||
if (key != '')
|
||||
settings.sockets.each do |client|
|
||||
client.send(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
$addons = OpenNebulaAddons.new(logger)
|
||||
|
||||
DEFAULT_TABLE_ORDER = "desc"
|
||||
@ -461,6 +497,9 @@ helpers do
|
||||
session[:default_view] = $views_config.available_views(session[:user], session[:user_gname]).first
|
||||
end
|
||||
|
||||
autorefresh_wss = $conf[:autorefresh_support_wss]
|
||||
session[:autorefresh_wss] = autorefresh_wss == 'yes'? 'wss' : 'ws'
|
||||
|
||||
# end user options
|
||||
|
||||
# secure cookies
|
||||
@ -507,7 +546,7 @@ before do
|
||||
@request_body = request.body.read
|
||||
request.body.rewind
|
||||
|
||||
unless %w(/ /login /vnc /spice /version /webauthn_options_for_get).include?(request.path)
|
||||
unless %w(/ /login /vnc /spice /version /webauthn_options_for_get /ws).include?(request.path)
|
||||
halt [401, "csrftoken"] unless authorized? && valid_csrftoken?
|
||||
end
|
||||
|
||||
@ -626,6 +665,27 @@ get '/' do
|
||||
}
|
||||
end
|
||||
|
||||
get '/ws' do
|
||||
logger.info { 'Incomming WS connection' }
|
||||
if request.websocket?
|
||||
request.websocket do |ws|
|
||||
ws.onopen do
|
||||
logger.info { "New client registered" }
|
||||
settings.sockets << ws
|
||||
end
|
||||
|
||||
ws.onmessage do |msg|
|
||||
logger.info { "New message received: #{msg}" }
|
||||
end
|
||||
|
||||
ws.onclose do
|
||||
logger.info { "Client disconnected." }
|
||||
settings.sockets.delete(ws)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get '/login' do
|
||||
content_type 'text/html', :charset => 'utf-8'
|
||||
if !authorized?
|
||||
|
@ -53,7 +53,11 @@
|
||||
'max_upload_file_size' : <%= $conf[:max_upload_file_size] ? $conf[:max_upload_file_size] : "undefined" %>,
|
||||
'leases' : <%= $conf[:leases] ? $conf[:leases].to_json : "null" %>,
|
||||
'mapped_ips' : '<%= $conf[:mapped_ips] ? $conf[:mapped_ips] : false %>',
|
||||
'get_extended_vm_info': '<%= $conf[:get_extended_vm_info] ? $conf[:get_extended_vm_info] : false %>'
|
||||
'get_extended_vm_info': '<%= $conf[:get_extended_vm_info] ? $conf[:get_extended_vm_info] : false %>',
|
||||
'autorefresh_wss': '<%= session[:autorefresh_wss] %>',
|
||||
'autorefresh_ip': '<%= $conf[:autorefresh_ip] %>',
|
||||
'autorefresh_port': '<%= $conf[:port] %>',
|
||||
|
||||
},
|
||||
'view' : view,
|
||||
'available_views' : available_views,
|
||||
|
Loading…
x
Reference in New Issue
Block a user