1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-16 22:50:10 +03:00

Feature #3748: Refactor vCenter network import, add vnet import form-panel

This commit is contained in:
Carlos Martín 2015-06-22 12:50:23 +02:00
parent 82b7a3913f
commit c849192b31
10 changed files with 485 additions and 299 deletions

View File

@ -13,7 +13,7 @@ define(function(require) {
var OpenNebulaHost = require('opennebula/host');
var OpenNebulaTemplate = require('opennebula/template');
var OpenNebulaVM = require('opennebula/vm');
var OpenNebulaNetwork = require('opennebula/network');
var VCenterNetworks = require('utils/vcenter/networks');
/*
TEMPLATES
@ -43,6 +43,8 @@ define(function(require) {
}
}
this.vCenterNetworks = new VCenterNetworks();
BaseFormPanel.call(this);
};
@ -61,10 +63,14 @@ define(function(require) {
*/
function _htmlWizard() {
return TemplateWizardHTML({formPanelId: this.formPanelId});
return TemplateWizardHTML({
'formPanelId': this.formPanelId,
'vcenterNetworksHTML': this.vCenterNetworks.html()
});
}
function _setup(context) {
var that = this;
$(".drivers", context).hide();
@ -93,6 +99,8 @@ define(function(require) {
}
});
$("#host_type_mad", context).change();
$("#get_vcenter_clusters", context).on("click", function() {
// TODO notify if credentials empty
var container = $(".vcenter_clusters", context);
@ -168,7 +176,6 @@ define(function(require) {
var templates_container = $(".vcenter_templates", context);
var vms_container = $(".vcenter_vms", context);
var networks_container = $(".vcenter_networks", context);
var vcenter_user = $("#vcenter_user", context).val();
var vcenter_password = $("#vcenter_password", context).val();
@ -181,12 +188,12 @@ define(function(require) {
vcenter_host: vcenter_host
});
fillVCenterNetworks({
container: networks_container,
vcenter_user: vcenter_user,
vcenter_password: vcenter_password,
vcenter_host: vcenter_host
});
that.vCenterNetworks.insert({
container: context,
vcenter_user: vcenter_user,
vcenter_password: vcenter_password,
vcenter_host: vcenter_host
});
},
error: function(response) {
$(".vcenter_clusters", context).hide();
@ -349,104 +356,7 @@ define(function(require) {
});
});
$.each($(".network_name:checked", context), function() {
var network_context = $(this).closest(".vcenter_network");
$(".vcenter_network_result:not(.success)", network_context).html(
'<span class="fa-stack fa-2x" style="color: #dfdfdf">' +
'<i class="fa fa-cloud fa-stack-2x"></i>' +
'<i class="fa fa-spinner fa-spin fa-stack-1x fa-inverse"></i>' +
'</span>');
var network_size = $(".netsize", network_context).val();
var network_tmpl = $(this).data("one_network");
var netname = $(this).data("network_name");
var type = $('.type_select', network_context).val();
var ar_array = [];
ar_array.push("TYPE=" + type);
ar_array.push("SIZE=" + network_size);
switch (type) {
case 'ETHER':
var mac = $('.eth_mac_net', network_context).val();
if (mac) {
ar_array.push("MAC=" + mac);
}
break;
case 'IP4':
var mac = $('.four_mac_net', network_context).val();
var ip = $('.four_ip_net', network_context).val();
if (mac) {
ar_array.push("MAC=" + mac);
}
if (ip) {
ar_array.push("IP=" + ip);
}
break;
case 'IP6':
var mac = $('.six_mac_net', network_context).val();
var gp = $('.six_global_net', network_context).val();
var ula = $('.six_mac_net', network_context).val();
if (mac) {
ar_array.push("MAC=" + mac);
}
if (gp) {
ar_array.push("GLOBAL_PREFIX=" + gp);
}
if (ula) {
ar_array.push("ULA_PREFIX=" + ula);
}
break;
}
network_tmpl += "\nAR=["
network_tmpl += ar_array.join(",\n")
network_tmpl += "]"
if ($(".vlaninfo", network_context)) {
network_tmpl += "VLAN=\"YES\"\n";
network_tmpl += "VLAN_ID=" + $(".vlaninfo", network_context).val() + "\n";
}
var vnet_json = {
"vnet": {
"vnet_raw": network_tmpl
}
};
OpenNebulaNetwork.create({
timeout: true,
data: vnet_json,
success: function(request, response) {
$(".vcenter_network_result", network_context).addClass("success").html(
'<span class="fa-stack fa-2x" style="color: #dfdfdf">' +
'<i class="fa fa-cloud fa-stack-2x"></i>' +
'<i class="fa fa-check fa-stack-1x fa-inverse"></i>' +
'</span>');
$(".vcenter_network_response", network_context).html('<p style="font-size:12px" class="running-color">' +
Locale.tr("Virtual Network created successfully") + ' ID:' + response.VNET.ID +
'</p>');
},
error: function (request, error_json) {
$(".vcenter_network_result", network_context).html('<span class="fa-stack fa-2x" style="color: #dfdfdf">' +
'<i class="fa fa-cloud fa-stack-2x"></i>' +
'<i class="fa fa-warning fa-stack-1x fa-inverse"></i>' +
'</span>');
$(".vcenter_network_response", network_context).html('<p style="font-size:12px" class="error-color">' +
(error_json.error.message || Locale.tr("Cannot contact server: is it running and reachable?")) +
'</p>');
}
});
});
that.vCenterNetworks.import();
return false;
});
@ -619,181 +529,4 @@ define(function(require) {
return false;
}
/*
Retrieve the list of networks from vCenter and fill the container with them
opts = {
datacenter: "Datacenter Name",
cluster: "Cluster Name",
container: Jquery div to inject the html,
vcenter_user: vCenter Username,
vcenter_password: vCenter Password,
vcenter_host: vCenter Host
}
*/
function fillVCenterNetworks(opts) {
var path = '/vcenter/networks';
opts.container.show();
$(".accordion_advanced_toggle", opts.container).trigger("click");
$.ajax({
url: path,
type: "GET",
data: {timeout: false},
dataType: "json",
headers: {
"X_VCENTER_USER": opts.vcenter_user,
"X_VCENTER_PASSWORD": opts.vcenter_password,
"X_VCENTER_HOST": opts.vcenter_host
},
success: function(response){
$(".content", opts.container).html("");
$('<div class="row">' +
'<div class="large-12 columns">' +
'<p style="color: #999">' + Locale.tr("Please select the vCenter Networks to be imported to OpenNebula.") + '</p>' +
'</div>' +
'</div>').appendTo($(".content", opts.container))
$.each(response, function(datacenter_name, networks){
$('<div class="row">' +
'<div class="large-12 columns">' +
'<h5>' +
datacenter_name + ' ' + Locale.tr("DataCenter") +
'</h5>' +
'</div>' +
'</div>').appendTo($(".content", opts.container))
if (networks.length == 0) {
$('<div class="row">' +
'<div class="large-12 columns">' +
'<label>' +
Locale.tr("No new networks found in this DataCenter") +
'</label>' +
'</div>' +
'</div>').appendTo($(".content", opts.container))
} else {
$.each(networks, function(id, network){
var netname = network.name.replace(" ","_");
var vlan_info = ""
if (network.vlan) {
var vlan_info = '<div class="vlan_info">' +
'<div class="large-4 columns">'+
'<label>' + Locale.tr("VLAN") +
'<input type="text" class="vlaninfo" value="'+network.vlan+'" disabled/>' +
'</label>'+
'</div>'+
'</div>';
}
var trow = $('<div class="vcenter_network">' +
'<div class="row">' +
'<div class="large-10 columns">' +
'<div class="large-12 columns">' +
'<label>' +
'<input type="checkbox" class="network_name" checked/> ' +
network.name + '&emsp;<span style="color: #999">' + network.type + '</span>' +
'</label>' +
'</div>'+
'<div class="large-2 columns">'+
'<label>' + Locale.tr("SIZE") +
'<input type="text" class="netsize" value="255"/>' +
'</label>' +
'</div>'+
'<div class="large-2 columns">'+
'<label>' + Locale.tr("TYPE") +
'<select class="type_select">'+
'<option value="ETHER">eth</option>' +
'<option value="IP4">ipv4</option>'+
'<option value="IP6">ipv6</option>' +
'</select>' +
'</label>' +
'</div>'+
'<div class="net_options">' +
'<div class="large-4 columns">'+
'<label>' + Locale.tr("MAC") +
'<input type="text" class="eth_mac_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>'+
'</div>'+
vlan_info +
'<div class="large-12 columns vcenter_network_response">'+
'</div>'+
'</div>' +
'<div class="large-2 columns vcenter_network_result">'+
'</div>'+
'</div>'+
'</div>').appendTo($(".content", opts.container))
$('.type_select', trow).on("change",function(){
var network_context = $(this).closest(".vcenter_network");
var type = $(this).val();
var net_form_str = ''
switch(type) {
case 'ETHER':
net_form_str =
'<div class="large-4 columns">'+
'<label>' + Locale.tr("MAC") +
'<input type="text" class="eth_mac_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>';
break;
case 'IP4':
net_form_str =
'<div class="large-4 columns">'+
'<label>' + Locale.tr("IP START") +
'<input type="text" class="four_ip_net"/>' +
'</label>'+
'</div>'+
'<div class="large-4 columns">'+
'<label>' + Locale.tr("MAC") +
'<input type="text" class="eth_mac_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>';
break;
case 'IP6':
net_form_str =
'<div class="large-4 columns">'+
'<label>' + Locale.tr("MAC") +
'<input type="text" class="eth_mac_net"/>' +
'</label>'+
'</div>'+
'<div class="large-6 columns">'+
'<label>' + Locale.tr("GLOBAL PREFIX") +
'<input type="text" class="six_global_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>'+
'<div class="large-6 columns">'+
'<label>' + Locale.tr("ULA_PREFIX") +
'<input type="text" class="six_ula_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>';
break;
}
$('.net_options', network_context).html(net_form_str);
});
$(".network_name", trow).data("network_name", netname)
$(".network_name", trow).data("one_network", network.one)
});
};
});
},
error: function(response){
opts.container.hide();
Notifier.onError({}, OpenNebulaError(response));
}
});
return false;
}
});

View File

@ -141,13 +141,8 @@
<br>
<div class="vcenter_vms"></div>
<br>
<div class="vcenter_networks hidden">
{{#advancedSection (tr "Networks") }}
<span class="fa-stack fa-2x" style="color: #dfdfdf">
<i class="fa fa-cloud fa-stack-2x"></i>
<i class="fa fa-spinner fa-spin fa-stack-1x fa-inverse"></i>
</span>
{{/advancedSection}}
<div>
{{{vcenterNetworksHTML}}}
</div>
<div class="row import_vcenter_clusters_div hidden">
<div class="large-12 columns">

View File

@ -21,8 +21,9 @@ define(function(require) {
];
var _formPanels = [
require('./vnets-tab/form-panels/create')
]
require('./vnets-tab/form-panels/create'),
require('./vnets-tab/form-panels/import')
];
var VNetsTab = {
tabId: TAB_ID,

View File

@ -12,6 +12,7 @@ define(function(require) {
var ADD_AR_DIALOG_ID = require('./dialogs/add-ar/dialogId');
var UPDATE_AR_DIALOG_ID = require('./dialogs/update-ar/dialogId');
var RESERVE_DIALOG_ID = require('./dialogs/reserve/dialogId');
var IMPORT_DIALOG_ID = require('./form-panels/import/formPanelId');
var _actions = {
"Network.create" : {
@ -36,15 +37,12 @@ define(function(require) {
}
},
// TODO
/*
"Network.import_dialog" : {
type: "create",
call: function(){
popUpNetworkImportDialog();
type: "custom",
call: function() {
Sunstone.showFormPanel(TAB_ID, IMPORT_DIALOG_ID, "import");
}
},
*/
"Network.list" : {
type: "list",

View File

@ -0,0 +1,104 @@
define(function(require) {
/*
DEPENDENCIES
*/
require('foundation.tab');
var BaseFormPanel = require('utils/form-panels/form-panel');
var Sunstone = require('sunstone');
var Locale = require('utils/locale');
var VCenterNetworks = require('utils/vcenter/networks');
/*
TEMPLATES
*/
var TemplateHTML = require('hbs!./import/html');
/*
CONSTANTS
*/
var FORM_PANEL_ID = require('./import/formPanelId');
var TAB_ID = require('../tabId');
/*
CONSTRUCTOR
*/
function FormPanel() {
this.formPanelId = FORM_PANEL_ID;
this.tabId = TAB_ID;
this.actions = {
'import': {
'title': Locale.tr("Import vCenter Networks"),
'buttonText': Locale.tr("Import"),
'resetButton': true
}
};
this.vCenterNetworks = new VCenterNetworks();
BaseFormPanel.call(this);
}
FormPanel.FORM_PANEL_ID = FORM_PANEL_ID;
FormPanel.prototype = Object.create(BaseFormPanel.prototype);
FormPanel.prototype.constructor = FormPanel;
FormPanel.prototype.htmlWizard = _htmlWizard;
FormPanel.prototype.submitWizard = _submitWizard;
FormPanel.prototype.onShow = _onShow;
FormPanel.prototype.setup = _setup;
return FormPanel;
/*
FUNCTION DEFINITIONS
*/
function _htmlWizard() {
return TemplateHTML({
'formPanelId': this.formPanelId,
'vcenterNetworksHTML': this.vCenterNetworks.html()
});
}
function _setup(context) {
var that = this;
Sunstone.disableFormPanelSubmit(TAB_ID);
$("#get_vcenter_networks", context).on("click", function(){
Sunstone.enableFormPanelSubmit(TAB_ID);
var vcenter_user = $("#vcenter_user", context).val();
var vcenter_password = $("#vcenter_password", context).val();
var vcenter_host = $("#vcenter_host", context).val();
that.vCenterNetworks.insert({
container: context,
vcenter_user: vcenter_user,
vcenter_password: vcenter_password,
vcenter_host: vcenter_host
});
return false;
});
return false;
}
function _submitWizard(context) {
var that = this;
Sunstone.hideFormPanelLoading(TAB_ID);
Sunstone.disableFormPanelSubmit(TAB_ID);
this.vCenterNetworks.import();
return false;
}
function _onShow(context) {
}
});

View File

@ -0,0 +1,12 @@
<form data-abide="ajax" id="{{formPanelId}}Advanced" class="custom creation">
<div class="row">
<div class="large-12 columns">
<p>{{tr "Write the Virtual Network template here"}}</p>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<textarea id="template" rows="15" required></textarea>
</div>
</div>
</form>

View File

@ -0,0 +1,3 @@
define(function(require){
return 'importVNetForm';
});

View File

@ -0,0 +1,29 @@
<form data-abide="ajax" id="{{formPanelId}}Wizard" class="custom creation">
<div class="row collapse vcenter_credentials">
<div class="row">
<div class="large-6 columns">
<label for="vcenter_user">{{tr "User"}}</label>
<input type="text" name="vcenter_user" id="vcenter_user" />
</div>
<div class="large-6 columns">
<label for="vcenter_host">{{tr "Hostname"}}</label>
<input type="text" name="vcenter_host" id="vcenter_host" />
</div>
</div>
<div class="row">
<div class="large-6 columns">
<label for="vcenter_password">{{tr "Password"}}</label>
<input type="password" name="vcenter_password" id="vcenter_password" />
</div>
<div class="large-6 columns">
<br>
<a class="button radius small right" id="get_vcenter_networks">
{{tr "Get Networks"}}
</a>
</div>
</div>
</div>
<div class="row collapse">
{{{vcenterNetworksHTML}}}
</div>
</form>

View File

@ -0,0 +1,305 @@
define(function(require) {
// Dependencies
var Locale = require('utils/locale');
var OpenNebulaNetwork = require('opennebula/network');
var OpenNebulaError = require('opennebula/error');
var TemplateHTML = require('hbs!./networks/html');
function VCenterNetworks() {
return this;
}
VCenterNetworks.prototype = {
'html': _html,
'insert': _fillVCenterNetworks,
'import': _import
};
VCenterNetworks.prototype.constructor = VCenterNetworks;
return VCenterNetworks;
function _html() {
return '<div class="vcenter_networks hidden"></div>';
}
/*
Retrieve the list of networks from vCenter and fill the container with them
opts = {
datacenter: "Datacenter Name",
cluster: "Cluster Name",
container: Jquery div to inject the html,
vcenter_user: vCenter Username,
vcenter_password: vCenter Password,
vcenter_host: vCenter Host
}
*/
function _fillVCenterNetworks(opts) {
var path = '/vcenter/networks';
var context = $(".vcenter_networks", opts.container);
context.html( TemplateHTML({}) );
context.show();
$(".accordion_advanced_toggle", context).trigger("click");
$.ajax({
url: path,
type: "GET",
data: {timeout: false},
dataType: "json",
headers: {
"X_VCENTER_USER": opts.vcenter_user,
"X_VCENTER_PASSWORD": opts.vcenter_password,
"X_VCENTER_HOST": opts.vcenter_host
},
success: function(response){
$(".content", context).html("");
$('<div class="row">' +
'<div class="large-12 columns">' +
'<p style="color: #999">' + Locale.tr("Please select the vCenter Networks to be imported to OpenNebula.") + '</p>' +
'</div>' +
'</div>').appendTo($(".content", context))
$.each(response, function(datacenter_name, networks){
$('<div class="row">' +
'<div class="large-12 columns">' +
'<h5>' +
datacenter_name + ' ' + Locale.tr("DataCenter") +
'</h5>' +
'</div>' +
'</div>').appendTo($(".content", context))
if (networks.length == 0) {
$('<div class="row">' +
'<div class="large-12 columns">' +
'<label>' +
Locale.tr("No new networks found in this DataCenter") +
'</label>' +
'</div>' +
'</div>').appendTo($(".content", context))
} else {
$.each(networks, function(id, network){
var netname = network.name.replace(" ","_");
var vlan_info = ""
if (network.vlan) {
var vlan_info = '<div class="vlan_info">' +
'<div class="large-4 columns">'+
'<label>' + Locale.tr("VLAN") +
'<input type="text" class="vlaninfo" value="'+network.vlan+'" disabled/>' +
'</label>'+
'</div>'+
'</div>';
}
var trow = $('<div class="vcenter_network">' +
'<div class="row">' +
'<div class="large-10 columns">' +
'<div class="large-12 columns">' +
'<label>' +
'<input type="checkbox" class="network_name" checked/> ' +
network.name + '&emsp;<span style="color: #999">' + network.type + '</span>' +
'</label>' +
'</div>'+
'<div class="large-2 columns">'+
'<label>' + Locale.tr("SIZE") +
'<input type="text" class="netsize" value="255"/>' +
'</label>' +
'</div>'+
'<div class="large-2 columns">'+
'<label>' + Locale.tr("TYPE") +
'<select class="type_select">'+
'<option value="ETHER">eth</option>' +
'<option value="IP4">ipv4</option>'+
'<option value="IP6">ipv6</option>' +
'</select>' +
'</label>' +
'</div>'+
'<div class="net_options">' +
'<div class="large-4 columns">'+
'<label>' + Locale.tr("MAC") +
'<input type="text" class="eth_mac_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>'+
'</div>'+
vlan_info +
'<div class="large-12 columns vcenter_network_response">'+
'</div>'+
'</div>' +
'<div class="large-2 columns vcenter_network_result">'+
'</div>'+
'</div>'+
'</div>').appendTo($(".content", context))
$('.type_select', trow).on("change",function(){
var network_context = $(this).closest(".vcenter_network");
var type = $(this).val();
var net_form_str = ''
switch(type) {
case 'ETHER':
net_form_str =
'<div class="large-4 columns">'+
'<label>' + Locale.tr("MAC") +
'<input type="text" class="eth_mac_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>';
break;
case 'IP4':
net_form_str =
'<div class="large-4 columns">'+
'<label>' + Locale.tr("IP START") +
'<input type="text" class="four_ip_net"/>' +
'</label>'+
'</div>'+
'<div class="large-4 columns">'+
'<label>' + Locale.tr("MAC") +
'<input type="text" class="eth_mac_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>';
break;
case 'IP6':
net_form_str =
'<div class="large-4 columns">'+
'<label>' + Locale.tr("MAC") +
'<input type="text" class="eth_mac_net"/>' +
'</label>'+
'</div>'+
'<div class="large-6 columns">'+
'<label>' + Locale.tr("GLOBAL PREFIX") +
'<input type="text" class="six_global_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>'+
'<div class="large-6 columns">'+
'<label>' + Locale.tr("ULA_PREFIX") +
'<input type="text" class="six_ula_net" placeholder="'+Locale.tr("Optional")+'"/>' +
'</label>'+
'</div>';
break;
}
$('.net_options', network_context).html(net_form_str);
});
$(".network_name", trow).data("network_name", netname)
$(".network_name", trow).data("one_network", network.one)
});
};
});
},
error: function(response){
context.hide();
Notifier.onError({}, OpenNebulaError(response));
}
});
}
function _import(context) {
$.each($(".network_name:checked", context), function() {
var network_context = $(this).closest(".vcenter_network");
$(".vcenter_network_result:not(.success)", network_context).html(
'<span class="fa-stack fa-2x" style="color: #dfdfdf">' +
'<i class="fa fa-cloud fa-stack-2x"></i>' +
'<i class="fa fa-spinner fa-spin fa-stack-1x fa-inverse"></i>' +
'</span>');
var network_size = $(".netsize", network_context).val();
var network_tmpl = $(this).data("one_network");
var netname = $(this).data("network_name");
var type = $('.type_select', network_context).val();
var ar_array = [];
ar_array.push("TYPE=" + type);
ar_array.push("SIZE=" + network_size);
switch (type) {
case 'ETHER':
var mac = $('.eth_mac_net', network_context).val();
if (mac) {
ar_array.push("MAC=" + mac);
}
break;
case 'IP4':
var mac = $('.four_mac_net', network_context).val();
var ip = $('.four_ip_net', network_context).val();
if (mac) {
ar_array.push("MAC=" + mac);
}
if (ip) {
ar_array.push("IP=" + ip);
}
break;
case 'IP6':
var mac = $('.six_mac_net', network_context).val();
var gp = $('.six_global_net', network_context).val();
var ula = $('.six_mac_net', network_context).val();
if (mac) {
ar_array.push("MAC=" + mac);
}
if (gp) {
ar_array.push("GLOBAL_PREFIX=" + gp);
}
if (ula) {
ar_array.push("ULA_PREFIX=" + ula);
}
break;
}
network_tmpl += "\nAR=["
network_tmpl += ar_array.join(",\n")
network_tmpl += "]"
if ($(".vlaninfo", network_context)) {
network_tmpl += "VLAN=\"YES\"\n";
network_tmpl += "VLAN_ID=" + $(".vlaninfo", network_context).val() + "\n";
}
var vnet_json = {
"vnet": {
"vnet_raw": network_tmpl
}
};
OpenNebulaNetwork.create({
timeout: true,
data: vnet_json,
success: function(request, response) {
$(".vcenter_network_result", network_context).addClass("success").html(
'<span class="fa-stack fa-2x" style="color: #dfdfdf">' +
'<i class="fa fa-cloud fa-stack-2x"></i>' +
'<i class="fa fa-check fa-stack-1x fa-inverse"></i>' +
'</span>');
$(".vcenter_network_response", network_context).html('<p style="font-size:12px" class="running-color">' +
Locale.tr("Virtual Network created successfully") + ' ID:' + response.VNET.ID +
'</p>');
},
error: function (request, error_json) {
$(".vcenter_network_result", network_context).html('<span class="fa-stack fa-2x" style="color: #dfdfdf">' +
'<i class="fa fa-cloud fa-stack-2x"></i>' +
'<i class="fa fa-warning fa-stack-1x fa-inverse"></i>' +
'</span>');
$(".vcenter_network_response", network_context).html('<p style="font-size:12px" class="error-color">' +
(error_json.error.message || Locale.tr("Cannot contact server: is it running and reachable?")) +
'</p>');
}
});
});
}
});

View File

@ -0,0 +1,6 @@
{{#advancedSection (tr "Networks") }}
<span class="fa-stack fa-2x" style="color: #dfdfdf">
<i class="fa fa-cloud fa-stack-2x"></i>
<i class="fa fa-spinner fa-spin fa-stack-1x fa-inverse"></i>
</span>
{{/advancedSection}}