mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
Merge branch 'feature-3532'
This commit is contained in:
commit
ea84c6ef95
@ -406,8 +406,44 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
|
||||
|
||||
CLIHelper.print_header(str_h1 % "MONITORING INFORMATION", false)
|
||||
|
||||
wilds = host.wilds
|
||||
|
||||
host.delete_element("TEMPLATE/VM")
|
||||
host.delete_element("TEMPLATE_WILDS")
|
||||
|
||||
puts host.template_str
|
||||
|
||||
puts
|
||||
CLIHelper.print_header("WILD VIRTUAL MACHINES", false)
|
||||
puts
|
||||
|
||||
format = "%30s %36s %4s %10s"
|
||||
CLIHelper.print_header(format % ["NAME", "IMPORT_ID", "CPU", "MEMORY"],
|
||||
true)
|
||||
|
||||
wilds.each do |wild|
|
||||
if wild['IMPORT_TEMPLATE']
|
||||
wild_tmplt = Base64::decode64(wild['IMPORT_TEMPLATE']).split("\n")
|
||||
name = wild_tmplt.select { |line|
|
||||
line[/^NAME/]
|
||||
}[0].split("=")[1].gsub("\"", " ").strip
|
||||
import = wild_tmplt.select { |line|
|
||||
line[/^IMPORT_VM_ID/]
|
||||
}[0].split("=")[1].gsub("\"", " ").strip
|
||||
memory = wild_tmplt.select { |line|
|
||||
line[/^MEMORY/]
|
||||
}[0].split("=")[1].gsub("\"", " ").strip
|
||||
cpu = wild_tmplt.select { |line|
|
||||
line[/^CPU/]
|
||||
}[0].split("=")[1].gsub("\"", " ").strip
|
||||
else
|
||||
name = wild['DEPLOY_ID']
|
||||
import = memory = cpu = "-"
|
||||
end
|
||||
|
||||
puts format % [name, import, cpu, memory]
|
||||
end
|
||||
|
||||
puts
|
||||
CLIHelper.print_header("VIRTUAL MACHINES", false)
|
||||
puts
|
||||
|
@ -239,4 +239,17 @@ cmd=CommandParser::CmdParser.new(ARGV) do
|
||||
o.rename(args[1])
|
||||
end
|
||||
end
|
||||
|
||||
importvm_desc = <<-EOT.unindent
|
||||
Import VM to OpenNebula
|
||||
EOT
|
||||
|
||||
command :importvm, importvm_desc, :hostid, :name do
|
||||
helper.perform_action(args[0], options, "imported") do |o|
|
||||
rc = o.info
|
||||
next rc if OpenNebula.is_error?(rc)
|
||||
|
||||
o.import_wild(args[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -387,6 +387,8 @@ int Host::update_info(Template &tmpl,
|
||||
zombie << zname;
|
||||
}
|
||||
}
|
||||
|
||||
delete *it;
|
||||
}
|
||||
else if (rc == 0) //not ours
|
||||
{
|
||||
@ -405,9 +407,9 @@ int Host::update_info(Template &tmpl,
|
||||
}
|
||||
|
||||
wild << wname;
|
||||
}
|
||||
|
||||
delete *it;
|
||||
obj_template->set(*it);
|
||||
}
|
||||
}
|
||||
|
||||
for(set_it = tmp_lost_vms.begin(); set_it != tmp_lost_vms.end(); set_it++)
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
|
||||
require 'opennebula/pool_element'
|
||||
require 'base64'
|
||||
|
||||
module OpenNebula
|
||||
class Host < PoolElement
|
||||
@ -187,6 +188,37 @@ module OpenNebula
|
||||
return call(HOST_METHODS[:rename], @pe_id, name)
|
||||
end
|
||||
|
||||
# Imports a wild VM from the host and puts it in running state
|
||||
#
|
||||
# @param name [String] Name of the VM to import
|
||||
#
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def import_wild(name)
|
||||
vms = importable_wilds.select {|vm| vm['VM_NAME'] == name }
|
||||
|
||||
if vms.length == 0
|
||||
return OpenNebula::Error.new("No importable wilds with name " <<
|
||||
"'#{name}' found.")
|
||||
elsif vms.length > 1
|
||||
return OpenNebula::Error.new("More than one importable wild " <<
|
||||
"with name '#{name}' found.")
|
||||
end
|
||||
|
||||
wild = vms.first
|
||||
|
||||
template = Base64.decode64(wild['IMPORT_TEMPLATE'])
|
||||
|
||||
xml = OpenNebula::VirtualMachine.build_xml
|
||||
vm = OpenNebula::VirtualMachine.new(xml, @client)
|
||||
|
||||
rc = vm.allocate(template)
|
||||
|
||||
return rc if OpenNebula.is_error?(rc)
|
||||
|
||||
vm.deploy(id, false)
|
||||
end
|
||||
|
||||
#######################################################################
|
||||
# Helpers to get Host information
|
||||
#######################################################################
|
||||
@ -206,6 +238,22 @@ module OpenNebula
|
||||
SHORT_HOST_STATES[state_str]
|
||||
end
|
||||
|
||||
# Returns the <TEMPLATE> element in text form
|
||||
# indent:: _Boolean_ indents the resulting string, default true
|
||||
def template_str(indent=true)
|
||||
template_like_str('TEMPLATE', indent)
|
||||
end
|
||||
|
||||
# Get wild VMs in the host
|
||||
def wilds
|
||||
[self.to_hash['HOST']['TEMPLATE']['VM']].flatten.compact
|
||||
end
|
||||
|
||||
# Get importable wild VMs in the host
|
||||
def importable_wilds
|
||||
wilds.select {|w| Hash === w && w['IMPORT_TEMPLATE'] }
|
||||
end
|
||||
|
||||
private
|
||||
def set_enabled(enabled)
|
||||
return Error.new('ID not defined') if !@pe_id
|
||||
|
@ -139,7 +139,7 @@ module OpenNebula
|
||||
}
|
||||
end
|
||||
|
||||
# Gets an array of text from elemenets extracted
|
||||
# Gets an array of text from elements extracted
|
||||
# using the XPATH expression passed as filter
|
||||
def retrieve_elements(filter)
|
||||
elements_array = Array.new
|
||||
@ -162,7 +162,7 @@ module OpenNebula
|
||||
|
||||
end
|
||||
|
||||
# Gets an attribute from an elemenT
|
||||
# Gets an attribute from an element
|
||||
# key:: _String_ xpath for the element
|
||||
# name:: _String_ name of the attribute
|
||||
def attr(key,name)
|
||||
|
@ -213,6 +213,7 @@ tabs:
|
||||
actions:
|
||||
Template.refresh: true
|
||||
Template.create_dialog: true
|
||||
Template.import_dialog: false
|
||||
Template.update_dialog: true
|
||||
Template.instantiate_vms: true
|
||||
Template.chown: true
|
||||
@ -379,6 +380,7 @@ tabs:
|
||||
actions:
|
||||
Network.refresh: true
|
||||
Network.create_dialog: true
|
||||
Network.import_dialog: false
|
||||
Network.update_dialog: true
|
||||
Network.addtocluster: true
|
||||
Network.chown: true
|
||||
|
@ -213,6 +213,7 @@ tabs:
|
||||
actions:
|
||||
Template.refresh: true
|
||||
Template.create_dialog: true
|
||||
Template.import_dialog: true
|
||||
Template.update_dialog: true
|
||||
Template.instantiate_vms: true
|
||||
Template.chown: true
|
||||
@ -379,6 +380,7 @@ tabs:
|
||||
actions:
|
||||
Network.refresh: true
|
||||
Network.create_dialog: true
|
||||
Network.import_dialog: true
|
||||
Network.update_dialog: true
|
||||
Network.addtocluster: true
|
||||
Network.chown: true
|
||||
|
@ -214,6 +214,7 @@ tabs:
|
||||
actions:
|
||||
Template.refresh: true
|
||||
Template.create_dialog: true
|
||||
Template.import_dialog: false
|
||||
Template.update_dialog: true
|
||||
Template.instantiate_vms: true
|
||||
Template.chown: false
|
||||
@ -380,6 +381,7 @@ tabs:
|
||||
actions:
|
||||
Network.refresh: true
|
||||
Network.create_dialog: false
|
||||
Network.import_dialog: false
|
||||
Network.update_dialog: true
|
||||
Network.addtocluster: false
|
||||
Network.chown: false
|
||||
|
@ -18,6 +18,9 @@
|
||||
/* HOST_HISTORY_LENGTH is ignored by server */
|
||||
var HOST_HISTORY_LENGTH = 40;
|
||||
|
||||
/* Hypervisors from where you can import VMs*/
|
||||
var HYPERVISORS_CAN_IMPORT_VM = ["vcenter", "ec2"]
|
||||
|
||||
var create_host_tmpl =
|
||||
'<div class="row">\
|
||||
<div class="large-12 columns">\
|
||||
@ -778,14 +781,17 @@ function updateHostInfo(request,host){
|
||||
var stripped_host_template = {};
|
||||
var unshown_values = {};
|
||||
|
||||
if (host_info.TEMPLATE.HYPERVISOR && host_info.TEMPLATE.HYPERVISOR.toLowerCase() != "vcenter")
|
||||
var hypervisor_name = host_info.TEMPLATE.HYPERVISOR ? host_info.TEMPLATE.HYPERVISOR.toLowerCase() : "-" ;
|
||||
|
||||
|
||||
if (!$.inArray(hypervisor_name, HYPERVISORS_CAN_IMPORT_VM))
|
||||
{
|
||||
stripped_host_template = host_info.TEMPLATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (key in host_info.TEMPLATE)
|
||||
if(!key.match(/HOST/))
|
||||
if(!key.match(/^HOST$/) && !key.match(/^VM$/) && !key.match(/^WILDS$/))
|
||||
stripped_host_template[key]=host_info.TEMPLATE[key];
|
||||
else
|
||||
unshown_values[key]=host_info.TEMPLATE[key];
|
||||
@ -856,10 +862,6 @@ function updateHostInfo(request,host){
|
||||
<td class="key_td">' + tr("Real CPU") + '</td>\
|
||||
<td class="value_td" colspan="2" style="width:50%;">'+cpu_bars.real+'</td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td class="key_td">' + tr("Running VMs") + '</td>\
|
||||
<td class="value_td" colspan="2">'+host_info.HOST_SHARE.RUNNING_VMS+'</td>\
|
||||
</tr>\
|
||||
</tbody>\
|
||||
</table>' +
|
||||
insert_datastores_capacity_table(host_info.HOST_SHARE) +
|
||||
@ -962,18 +964,92 @@ function updateHostInfo(request,host){
|
||||
</div>'
|
||||
}
|
||||
|
||||
var wilds_info_tab = {
|
||||
title: tr("WILDS"),
|
||||
icon: "fa-hdd-o",
|
||||
content : '<div id="datatable_host_wilds_info_div" class="row">\
|
||||
<div class="large-12 columns">\
|
||||
<button id="import_wilds" class="button tiny success right radius" >'+tr("Import Wilds")+'</button>\
|
||||
<table id="datatable_host_wilds" class="datatable twelve">\
|
||||
<thead>\
|
||||
<tr>\
|
||||
<th></th>\
|
||||
<th>' + tr("VM name") + '</th>\
|
||||
<th>' + tr("Remote ID") + '</th>\
|
||||
</tr>\
|
||||
</thead>\
|
||||
<tbody id="tbody_host_wilds">\
|
||||
</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
</div>'
|
||||
}
|
||||
|
||||
// Add event listener for importing WILDS
|
||||
$('#import_wilds').die( "click" );
|
||||
$('#import_wilds').live('click', function () {
|
||||
$.each($("#import_wild_checker:checked", "#datatable_host_wilds"), function(){
|
||||
var vm_json = {
|
||||
"vm": {
|
||||
"vm_raw": $(this).data("wild_template")
|
||||
}
|
||||
};
|
||||
|
||||
var import_host_id = $(this).data("host_id");
|
||||
var wild_row = $(this).closest('tr');
|
||||
|
||||
// Create the VM in OpenNebula
|
||||
OpenNebula.VM.create({
|
||||
timeout: true,
|
||||
data: vm_json,
|
||||
success: function(request, response) {
|
||||
OpenNebula.Helper.clear_cache("VM");
|
||||
|
||||
var extra_info = {};
|
||||
|
||||
extra_info['host_id'] = import_host_id;
|
||||
extra_info['ds_id'] = -1;
|
||||
extra_info['enforce'] = false;
|
||||
|
||||
// Deploy the VM
|
||||
Sunstone.runAction("VM.silent_deploy_action",
|
||||
response.VM.ID,
|
||||
extra_info);
|
||||
|
||||
// Notify
|
||||
notifyCustom(tr("VM imported"), " ID: " + response.VM.ID, false);
|
||||
|
||||
// Delete row (shouldn't be there in next monitorization)
|
||||
dataTable_wilds_hosts = $("#datatable_host_wilds").dataTable();
|
||||
dataTable_wilds_hosts.fnDeleteRow(wild_row);
|
||||
|
||||
},
|
||||
error: function (request, error_json){
|
||||
notifyError(error_json.error.message || tr("Cannot contact server: is it running and reachable?"));
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
//Sunstone.updateInfoPanelTab(info_panel_name,tab_name, new tab object);
|
||||
Sunstone.updateInfoPanelTab("host_info_panel","host_info_tab",info_tab);
|
||||
Sunstone.updateInfoPanelTab("host_info_panel","host_monitoring_tab",monitor_tab);
|
||||
Sunstone.updateInfoPanelTab("host_info_panel","host_vms_tab",vms_info_tab);
|
||||
|
||||
if (host_info.TEMPLATE.HYPERVISOR == "vcenter") {
|
||||
hypervisor_name = host_info.TEMPLATE.HYPERVISOR ? host_info.TEMPLATE.HYPERVISOR.toLowerCase() : "-";
|
||||
|
||||
if (hypervisor_name == "vcenter") {
|
||||
Sunstone.updateInfoPanelTab("host_info_panel","host_esx_tab",esx_info_tab);
|
||||
}
|
||||
|
||||
if ($.inArray(hypervisor_name, HYPERVISORS_CAN_IMPORT_VM)) {
|
||||
Sunstone.updateInfoPanelTab("host_info_panel","host_wilds_tab",wilds_info_tab);
|
||||
}
|
||||
|
||||
Sunstone.popUpInfoPanel("host_info_panel", "hosts-tab");
|
||||
|
||||
if (host_info.TEMPLATE.HYPERVISOR == "vcenter") {
|
||||
// ESX datatable
|
||||
var dataTable_esx_hosts = $("#datatable_host_esx",main_tabs_context).dataTable({
|
||||
"bSortClasses" : false,
|
||||
"bDeferRender": true
|
||||
@ -1005,6 +1081,42 @@ function updateHostInfo(request,host){
|
||||
}
|
||||
}
|
||||
|
||||
if ($.inArray(hypervisor_name, HYPERVISORS_CAN_IMPORT_VM)) {
|
||||
// WILDS datatable
|
||||
var dataTable_wilds_hosts = $("#datatable_host_wilds",main_tabs_context).dataTable({
|
||||
"bSortClasses" : false,
|
||||
"bDeferRender": true
|
||||
});
|
||||
|
||||
var wilds_list_array = [];
|
||||
|
||||
if (host_info.TEMPLATE.VM) {
|
||||
wilds = host_info.TEMPLATE.VM;
|
||||
|
||||
$.each(wilds, function(){
|
||||
name = this.VM_NAME;
|
||||
safe_name = name.replace(/ /g,"_").replace(/\./g,"_");
|
||||
deploy_id = this.DEPLOY_ID;
|
||||
|
||||
wilds_list_array.push([
|
||||
'<input type="checkbox" id="import_wild_checker" class="import_'+safe_name+'" unchecked/>',
|
||||
name,
|
||||
deploy_id
|
||||
]);
|
||||
|
||||
dataTable_wilds_hosts.fnAddData(wilds_list_array);
|
||||
|
||||
$(".import_"+safe_name, dataTable_wilds_hosts).data("wild_template", atob(this.IMPORT_TEMPLATE));
|
||||
$(".import_"+safe_name, dataTable_wilds_hosts).data("host_id", host_info.ID);
|
||||
|
||||
wilds_list_array = [];
|
||||
});
|
||||
}
|
||||
|
||||
delete host_info.TEMPLATE.WILDS;
|
||||
delete host_info.TEMPLATE.VM;
|
||||
}
|
||||
|
||||
var dataTable_host_vMachines = $("#datatable_host_vms", $("#host_info_panel")).dataTable({
|
||||
"bSortClasses" : false,
|
||||
"bDeferRender": true,
|
||||
@ -1153,112 +1265,6 @@ function fillVCenterTemplates(opts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Retrieve the list of running VMs 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 fillVCenterVMs(opts) {
|
||||
var path = '/vcenter/vms';
|
||||
opts.container.html(generateAdvancedSection({
|
||||
html_id: path,
|
||||
title: tr("Running VMs"),
|
||||
content: '<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>'
|
||||
}))
|
||||
|
||||
$('a', 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">' + tr("Please select the vCenter running VMs to be imported to OpenNebula.") + '</p>' +
|
||||
'</div>' +
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
|
||||
$.each(response, function(datacenter_name, vms){
|
||||
$('<div class="row">' +
|
||||
'<div class="large-12 columns">' +
|
||||
'<h5>' +
|
||||
datacenter_name + ' ' + tr("DataCenter") +
|
||||
'</h5>' +
|
||||
'</div>' +
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
|
||||
if (vms.length == 0) {
|
||||
$('<div class="row">' +
|
||||
'<div class="large-12 columns">' +
|
||||
'<label>' +
|
||||
tr("No new running VMs found in this DataCenter") +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
} else {
|
||||
$.each(vms, function(id, vm){
|
||||
if (vm.host_id === parseInt(vm.host_id, 10)) {
|
||||
var trow = $('<div class="vcenter_vm">' +
|
||||
'<div class="row">' +
|
||||
'<div class="large-10 columns">' +
|
||||
'<label>' +
|
||||
'<input type="checkbox" class="vm_name" checked/> ' +
|
||||
vm.name + ' <span style="color: #999">' + vm.host + '</span>' +
|
||||
'</vm>' +
|
||||
'<div class="large-12 columns vcenter_vm_response">'+
|
||||
'</div>'+
|
||||
'</div>' +
|
||||
'<div class="large-2 columns vcenter_vm_result">'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
|
||||
$(".vm_name", trow).data("vm_name", vm.name)
|
||||
$(".vm_name", trow).data("one_vm", vm.one)
|
||||
$(".vm_name", trow).data("vm_to_host", vm.host_id)
|
||||
}
|
||||
});
|
||||
|
||||
if ($(".vcenter_vm").length == 0) {
|
||||
$('<div class="row">' +
|
||||
'<div class="large-12 columns">' +
|
||||
'<label>' +
|
||||
tr("No new running VMs found in this DataCenter") +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
error: function(response){
|
||||
opts.container.html("");
|
||||
onError({}, OpenNebula.Error(response));
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Retrieve the list of networks from vCenter and fill the container with them
|
||||
|
||||
@ -1580,13 +1586,6 @@ function setupCreateHostDialog(){
|
||||
vcenter_host: vcenter_host
|
||||
});
|
||||
|
||||
fillVCenterVMs({
|
||||
container: vms_container,
|
||||
vcenter_user: vcenter_user,
|
||||
vcenter_password: vcenter_password,
|
||||
vcenter_host: vcenter_host
|
||||
});
|
||||
|
||||
fillVCenterNetworks({
|
||||
container: networks_container,
|
||||
vcenter_user: vcenter_user,
|
||||
|
@ -1387,6 +1387,13 @@ var template_actions = {
|
||||
}
|
||||
},
|
||||
|
||||
"Template.import_dialog" : {
|
||||
type: "create",
|
||||
call: function(){
|
||||
popUpTemplateImportDialog();
|
||||
}
|
||||
},
|
||||
|
||||
"Template.update_dialog" : {
|
||||
type: "custom",
|
||||
call: function(){
|
||||
@ -1611,6 +1618,15 @@ var template_buttons = {
|
||||
type: "create_dialog",
|
||||
layout: "create"
|
||||
},
|
||||
|
||||
"Template.import_dialog" : {
|
||||
type: "create_dialog",
|
||||
layout: "create",
|
||||
text: tr("Import"),
|
||||
icon: '<i class="fa fa-download">',
|
||||
alwaysActive: true
|
||||
},
|
||||
|
||||
"Template.update_dialog" : {
|
||||
type: "action",
|
||||
layout: "main",
|
||||
@ -5070,6 +5086,227 @@ function popUpTemplateCloneDialog(){
|
||||
$("input[name='name']",dialog).focus();
|
||||
}
|
||||
|
||||
function popUpTemplateImportDialog(){
|
||||
setupTemplateImportDialog();
|
||||
var dialog = $('#template_import_dialog');
|
||||
$(dialog).foundation().foundation('reveal', 'open');
|
||||
}
|
||||
|
||||
// Template import dialog
|
||||
function setupTemplateImportDialog(){
|
||||
//Append to DOM
|
||||
dialogs_context.append('<div id="template_import_dialog"></div>');
|
||||
var dialog = $('#template_import_dialog',dialogs_context);
|
||||
|
||||
//Put HTML in place
|
||||
|
||||
var html = '<div class="row">\
|
||||
<h3 id="import_template_header" class="subheader">'+tr("Import vCenter VM Templates")+'</h3>\
|
||||
</div>\
|
||||
<div class="row vcenter_credentials">\
|
||||
<fieldset>\
|
||||
<legend>'+tr("vCenter")+'</legend>\
|
||||
<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_templates">'+tr("Get VM Templates")+'</a>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="vcenter_templates">\
|
||||
</div>\
|
||||
<br>\
|
||||
<div class="row">\
|
||||
<div class="large-12 columns">\
|
||||
<br>\
|
||||
<a class="button radius small right success" id="import_vcenter_templates">'+tr("Import")+'</a>\
|
||||
</div>\
|
||||
</div>\
|
||||
</fieldset>\
|
||||
<a class="close-reveal-modal">×</a>\
|
||||
</div>\
|
||||
';
|
||||
|
||||
|
||||
dialog.html(html);
|
||||
dialog.addClass("reveal-modal medium").attr("data-reveal", "");
|
||||
|
||||
$("#get_vcenter_templates", dialog).on("click", function(){
|
||||
var templates_container = $(".vcenter_templates", dialog);
|
||||
|
||||
var vcenter_user = $("#vcenter_user", dialog).val();
|
||||
var vcenter_password = $("#vcenter_password", dialog).val();
|
||||
var vcenter_host = $("#vcenter_host", dialog).val();
|
||||
|
||||
fillVCenterTemplates({
|
||||
container: templates_container,
|
||||
vcenter_user: vcenter_user,
|
||||
vcenter_password: vcenter_password,
|
||||
vcenter_host: vcenter_host
|
||||
});
|
||||
|
||||
|
||||
return false;
|
||||
})
|
||||
|
||||
$("#import_vcenter_templates", dialog).on("click", function(){
|
||||
$(this).hide();
|
||||
|
||||
$.each($(".template_name:checked", dialog), function(){
|
||||
var template_context = $(this).closest(".vcenter_template");
|
||||
|
||||
$(".vcenter_template_result:not(.success)", template_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 template_json = {
|
||||
"vmtemplate": {
|
||||
"template_raw": $(this).data("one_template")
|
||||
}
|
||||
};
|
||||
|
||||
OpenNebula.Template.create({
|
||||
timeout: true,
|
||||
data: template_json,
|
||||
success: function(request, response) {
|
||||
OpenNebula.Helper.clear_cache("VMTEMPLATE");
|
||||
$(".vcenter_template_result", template_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_template_response", template_context).html('<p style="font-size:12px" class="running-color">'+
|
||||
tr("Template created successfully")+' ID:'+response.VMTEMPLATE.ID+
|
||||
'</p>');
|
||||
Sunstone.runAction('Template.refresh');
|
||||
},
|
||||
error: function (request, error_json){
|
||||
$(".vcenter_template_result", template_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_template_response", template_context).html('<p style="font-size:12px" class="error-color">'+
|
||||
(error_json.error.message || tr("Cannot contact server: is it running and reachable?"))+
|
||||
'</p>');
|
||||
Sunstone.runAction('Template.refresh');
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Retrieve the list of templates 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 fillVCenterTemplates(opts) {
|
||||
var path = '/vcenter/templates';
|
||||
opts.container.html(generateAdvancedSection({
|
||||
html_id: path,
|
||||
title: tr("Templates"),
|
||||
content: '<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>'
|
||||
}))
|
||||
|
||||
$('a', 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">' + tr("Please select the vCenter Templates to be imported to OpenNebula.") + '</p>' +
|
||||
'</div>' +
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
|
||||
$.each(response, function(datacenter_name, templates){
|
||||
$('<div class="row">' +
|
||||
'<div class="large-12 columns">' +
|
||||
'<h5>' +
|
||||
datacenter_name + ' ' + tr("DataCenter") +
|
||||
'</h5>' +
|
||||
'</div>' +
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
|
||||
if (templates.length == 0) {
|
||||
$('<div class="row">' +
|
||||
'<div class="large-12 columns">' +
|
||||
'<label>' +
|
||||
tr("No new templates found in this DataCenter") +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
} else {
|
||||
$.each(templates, function(id, template){
|
||||
var trow = $('<div class="vcenter_template">' +
|
||||
'<div class="row">' +
|
||||
'<div class="large-10 columns">' +
|
||||
'<label>' +
|
||||
'<input type="checkbox" class="template_name" checked/> ' +
|
||||
template.name + ' <span style="color: #999">' + template.host + '</span>' +
|
||||
'</label>' +
|
||||
'<div class="large-12 columns vcenter_template_response">'+
|
||||
'</div>'+
|
||||
'</div>' +
|
||||
'<div class="large-2 columns vcenter_template_result">'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
|
||||
$(".template_name", trow).data("template_name", template.name)
|
||||
$(".template_name", trow).data("one_template", template.one)
|
||||
});
|
||||
};
|
||||
});
|
||||
},
|
||||
error: function(response){
|
||||
opts.container.html("");
|
||||
onError({}, OpenNebula.Error(response));
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Instantiate dialog
|
||||
// Sets up the instiantiate template dialog and all the processing associated to it
|
||||
function setupInstantiateTemplateDialog(){
|
||||
|
@ -409,6 +409,12 @@ var vm_actions = {
|
||||
notify: true
|
||||
},
|
||||
|
||||
"VM.silent_deploy_action" : {
|
||||
type: "single",
|
||||
call: OpenNebula.VM.deploy,
|
||||
error: onError
|
||||
},
|
||||
|
||||
"VM.migrate" : {
|
||||
type: "custom",
|
||||
call: function(){
|
||||
|
@ -264,6 +264,13 @@ var vnet_actions = {
|
||||
}
|
||||
},
|
||||
|
||||
"Network.import_dialog" : {
|
||||
type: "create",
|
||||
call: function(){
|
||||
popUpNetworkImportDialog();
|
||||
}
|
||||
},
|
||||
|
||||
"Network.list" : {
|
||||
type: "list",
|
||||
call: OpenNebula.Network.list,
|
||||
@ -564,6 +571,13 @@ var vnet_buttons = {
|
||||
type: "create_dialog",
|
||||
layout: "create"
|
||||
},
|
||||
"Network.import_dialog" : {
|
||||
type: "create_dialog",
|
||||
layout: "create",
|
||||
text: tr("Import"),
|
||||
icon: '<i class="fa fa-download">',
|
||||
alwaysActive: true
|
||||
},
|
||||
"Network.update_dialog" : {
|
||||
type: "action",
|
||||
layout: "main",
|
||||
@ -2350,6 +2364,374 @@ function setupARTableSelect(section, context_id, vnet_id_fn){
|
||||
return setupResourceTableSelect(section, context_id, options);
|
||||
}
|
||||
|
||||
function popUpNetworkImportDialog(){
|
||||
setupNetworkImportDialog();
|
||||
var dialog = $('#network_import_dialog');
|
||||
$(dialog).foundation().foundation('reveal', 'open');
|
||||
}
|
||||
|
||||
// Netowrk import dialog
|
||||
function setupNetworkImportDialog(){
|
||||
//Append to DOM
|
||||
dialogs_context.append('<div id="network_import_dialog"></div>');
|
||||
var dialog = $('#network_import_dialog',dialogs_context);
|
||||
|
||||
//Put HTML in place
|
||||
|
||||
var html = '<div class="row">\
|
||||
<h3 id="import_network_header" class="subheader">'+tr("Import vCenter Networks")+'</h3>\
|
||||
</div>\
|
||||
<div class="row vcenter_credentials">\
|
||||
<fieldset>\
|
||||
<legend>'+tr("vCenter")+'</legend>\
|
||||
<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 class="vcenter_networks">\
|
||||
</div>\
|
||||
<br>\
|
||||
<div class="row">\
|
||||
<div class="large-12 columns">\
|
||||
<br>\
|
||||
<a class="button radius small right success" id="import_vcenter_networks">'+tr("Import")+'</a>\
|
||||
</div>\
|
||||
</div>\
|
||||
</fieldset>\
|
||||
<a class="close-reveal-modal">×</a>\
|
||||
</div>\
|
||||
';
|
||||
|
||||
|
||||
dialog.html(html);
|
||||
dialog.addClass("reveal-modal medium").attr("data-reveal", "");
|
||||
|
||||
$("#get_vcenter_networks", dialog).on("click", function(){
|
||||
var networks_container = $(".vcenter_networks", dialog);
|
||||
|
||||
var vcenter_user = $("#vcenter_user", dialog).val();
|
||||
var vcenter_password = $("#vcenter_password", dialog).val();
|
||||
var vcenter_host = $("#vcenter_host", dialog).val();
|
||||
|
||||
|
||||
fillVCenterNetworks({
|
||||
container: networks_container,
|
||||
vcenter_user: vcenter_user,
|
||||
vcenter_password: vcenter_password,
|
||||
vcenter_host: vcenter_host
|
||||
});
|
||||
|
||||
return false;
|
||||
})
|
||||
|
||||
$("#import_vcenter_networks", dialog).on("click", function(){
|
||||
$(this).hide();
|
||||
|
||||
$.each($(".network_name:checked", dialog), 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
|
||||
}
|
||||
};
|
||||
|
||||
OpenNebula.Network.create({
|
||||
timeout: true,
|
||||
data: vnet_json,
|
||||
success: function(request, response) {
|
||||
OpenNebula.Helper.clear_cache("VNET");
|
||||
$(".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">'+
|
||||
tr("Virtual Network created successfully")+' ID:'+response.VNET.ID+
|
||||
'</p>');
|
||||
Sunstone.runAction("Network.refresh");
|
||||
},
|
||||
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 || tr("Cannot contact server: is it running and reachable?"))+
|
||||
'</p>');
|
||||
Sunstone.runAction("Network.refresh");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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.html(generateAdvancedSection({
|
||||
html_id: path,
|
||||
title: tr("Networks"),
|
||||
content: '<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>'
|
||||
}))
|
||||
|
||||
$('a', 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">' + 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 + ' ' + tr("DataCenter") +
|
||||
'</h5>' +
|
||||
'</div>' +
|
||||
'</div>').appendTo($(".content", opts.container))
|
||||
|
||||
if (networks.length == 0) {
|
||||
$('<div class="row">' +
|
||||
'<div class="large-12 columns">' +
|
||||
'<label>' +
|
||||
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>' + 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 + ' <span style="color: #999">' + network.type + '</span>' +
|
||||
'</label>' +
|
||||
'</div>'+
|
||||
'<div class="large-2 columns">'+
|
||||
'<label>' + tr("SIZE") +
|
||||
'<input type="text" class="netsize" value="255"/>' +
|
||||
'</label>' +
|
||||
'</div>'+
|
||||
'<div class="large-2 columns">'+
|
||||
'<label>' + 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>' + tr("MAC") +
|
||||
'<input type="text" class="eth_mac_net" placeholder="'+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>' + tr("MAC") +
|
||||
'<input type="text" class="eth_mac_net" placeholder="'+tr("Optional")+'"/>' +
|
||||
'</label>'+
|
||||
'</div>';
|
||||
break;
|
||||
case 'IP4':
|
||||
net_form_str =
|
||||
'<div class="large-4 columns">'+
|
||||
'<label>' + tr("IP START") +
|
||||
'<input type="text" class="four_ip_net"/>' +
|
||||
'</label>'+
|
||||
'</div>'+
|
||||
'<div class="large-4 columns">'+
|
||||
'<label>' + tr("MAC") +
|
||||
'<input type="text" class="eth_mac_net" placeholder="'+tr("Optional")+'"/>' +
|
||||
'</label>'+
|
||||
'</div>';
|
||||
break;
|
||||
case 'IP6':
|
||||
net_form_str =
|
||||
'<div class="large-4 columns">'+
|
||||
'<label>' + tr("MAC") +
|
||||
'<input type="text" class="eth_mac_net"/>' +
|
||||
'</label>'+
|
||||
'</div>'+
|
||||
'<div class="large-6 columns">'+
|
||||
'<label>' + tr("GLOBAL PREFIX") +
|
||||
'<input type="text" class="six_global_net" placeholder="'+tr("Optional")+'"/>' +
|
||||
'</label>'+
|
||||
'</div>'+
|
||||
'<div class="large-6 columns">'+
|
||||
'<label>' + tr("ULA_PREFIX") +
|
||||
'<input type="text" class="six_ula_net" placeholder="'+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.html("");
|
||||
onError({}, OpenNebula.Error(response));
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//The DOM is ready and the ready() from sunstone.js
|
||||
//has been executed at this point.
|
||||
$(document).ready(function(){
|
||||
|
@ -868,7 +868,8 @@ function insertButtonsInTab(tab_name, panel_name, panel_buttons, custom_context)
|
||||
switch (button.layout) {
|
||||
case "create":
|
||||
button_context = $("#"+custom_id+"create_buttons", buttons_row);
|
||||
text = button.text ? '<i class="fa fa-plus"/> ' + button.text : '<i class="fa fa-plus"/>';
|
||||
icon = button.icon ? button.icon : '<i class="fa fa-plus"/>';
|
||||
text = button.text ? icon + ' ' + button.text : icon;
|
||||
str_class.push("success", "button", "small", "radius");
|
||||
button_code = '<button class="'+str_class.join(' ')+'" href="'+button_name+'">'+text+'</button>';
|
||||
break;
|
||||
|
@ -121,6 +121,9 @@ instance_types:
|
||||
t1.micro:
|
||||
cpu: 1
|
||||
memory: 0.615
|
||||
t2.micro:
|
||||
cpu: 1
|
||||
memory: 1
|
||||
cg1.4xlarge:
|
||||
cpu: 16
|
||||
memory: 22.5
|
||||
|
@ -198,7 +198,8 @@ class EC2Driver
|
||||
:ip_address,
|
||||
:subnet_id,
|
||||
:security_groups,
|
||||
:instance_type
|
||||
:instance_type,
|
||||
:image_id
|
||||
]
|
||||
|
||||
# EC2 constructor, loads credentials and endpoint
|
||||
@ -340,11 +341,16 @@ class EC2Driver
|
||||
|
||||
poll_data=parse_poll(i)
|
||||
|
||||
vm_template_to_one = vm_to_one(i)
|
||||
vm_template_to_one = Base64.encode64(vm_template_to_one).gsub("\n","")
|
||||
|
||||
one_id = i.tags['ONE_ID']
|
||||
|
||||
vms_info << "VM=[\n"
|
||||
vms_info << " ID=#{one_id || -1},\n"
|
||||
vms_info << " DEPLOY_ID=#{i.instance_id},\n"
|
||||
vms_info << " VM_NAME=#{i.instance_id},\n"
|
||||
vms_info << " IMPORT_TEMPLATE=\"#{vm_template_to_one}\",\n"
|
||||
vms_info << " POLL=\"#{poll_data}\" ]\n"
|
||||
|
||||
if one_id
|
||||
@ -420,7 +426,7 @@ private
|
||||
ec2
|
||||
end
|
||||
|
||||
# Retrive the vm information from the EC2 instance
|
||||
# Retrieve the vm information from the EC2 instance
|
||||
def parse_poll(instance)
|
||||
begin
|
||||
info = "#{POLL_ATTRIBUTE[:usedmemory]}=0 " \
|
||||
@ -567,5 +573,28 @@ private
|
||||
exit(-1)
|
||||
end
|
||||
end
|
||||
|
||||
# Build template for importation
|
||||
def vm_to_one(instance)
|
||||
cpu, mem = instance_type_capacity(instance.instance_type)
|
||||
|
||||
mem = mem.to_i / 1024 # Memory for templates expressed in MB
|
||||
|
||||
str = "NAME = \"Instance from #{instance.id}\"\n"\
|
||||
"CPU = \"#{cpu}\"\n"\
|
||||
"vCPU = \"#{cpu}\"\n"\
|
||||
"MEMORY = \"#{mem}\"\n"\
|
||||
"HYPERVISOR = \"ec2\"\n"\
|
||||
"PUBLIC_CLOUD = [\n"\
|
||||
" TYPE =\"ec2\",\n"\
|
||||
" AMI =\"#{instance.image_id}\"\n"\
|
||||
"]\n"\
|
||||
"IMPORT_VM_ID = \"#{instance.id}\"\n"\
|
||||
"SCHED_REQUIREMENTS=\"NAME=\\\"#{@host}\\\"\"\n"\
|
||||
"DESCRIPTION = \"Instance imported from EC2, from instance"\
|
||||
" #{instance.id}, AMI #{instance.image_id}\"\n"
|
||||
|
||||
str
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -18,6 +18,17 @@
|
||||
|
||||
require 'pp'
|
||||
require 'rexml/document'
|
||||
require 'base64'
|
||||
require 'uri'
|
||||
|
||||
begin
|
||||
require 'rubygems'
|
||||
require 'json'
|
||||
|
||||
JSON_LOADED = true
|
||||
rescue LoadError
|
||||
JSON_LOADED = false
|
||||
end
|
||||
|
||||
ENV['LANG']='C'
|
||||
|
||||
@ -69,7 +80,9 @@ module KVM
|
||||
values[:usedcpu] = cpu[vm[:pid]] if cpu[vm[:pid]]
|
||||
values[:usedmemory] = [resident_mem, max_mem].max
|
||||
|
||||
values.merge!(get_interface_statistics(one_vm))
|
||||
xml = dump_xml(vmid)
|
||||
|
||||
values.merge!(get_interface_statistics(one_vm, xml))
|
||||
|
||||
return values
|
||||
end
|
||||
@ -125,7 +138,17 @@ module KVM
|
||||
values[:usedcpu] = cpu[vm[:pid]] if cpu[vm[:pid]]
|
||||
values[:usedmemory] = [resident_mem, max_mem].max
|
||||
|
||||
values.merge!(get_interface_statistics(name))
|
||||
xml = dump_xml(name)
|
||||
|
||||
values.merge!(get_interface_statistics(name, xml))
|
||||
values.merge!(get_disk_usage(xml))
|
||||
|
||||
if !name.match(/^one-\d+/)
|
||||
uuid, template = xml_to_one(xml)
|
||||
values[:template] = Base64.encode64(template).delete("\n")
|
||||
values[:vm_name] = name
|
||||
vm[:name] = uuid
|
||||
end
|
||||
|
||||
vms_info[vm[:name]] = values
|
||||
end
|
||||
@ -213,11 +236,19 @@ module KVM
|
||||
hash
|
||||
end
|
||||
|
||||
# Get dumpxml output of a VM
|
||||
# @param the ID of the VM as defined in libvirt
|
||||
# @return [String] xml output of virsh dumpxml
|
||||
def self.dump_xml(vmid)
|
||||
`#{virsh(:dumpxml)} '#{vmid}'`
|
||||
end
|
||||
|
||||
# Aggregate statics of all VM NICs
|
||||
# @param the ID of the VM as defined in libvirt
|
||||
# @param text [nil, String] dumpxml output or nil to execute dumpxml
|
||||
# @return [Hash] with network stats, by name [symbol] :netrx, :nettx
|
||||
def self.get_interface_statistics(vmid)
|
||||
text = `#{virsh(:dumpxml)} #{vmid}`
|
||||
def self.get_interface_statistics(vmid, text = nil)
|
||||
text = dump_xml(vmid) if !text
|
||||
|
||||
return {} if $?.exitstatus != 0
|
||||
|
||||
@ -278,6 +309,156 @@ module KVM
|
||||
'-'
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_disk_usage(xml)
|
||||
return {} if !JSON_LOADED
|
||||
|
||||
doc=REXML::Document.new(xml)
|
||||
size = 0
|
||||
|
||||
data = {
|
||||
:disk_actual_size => 0.0,
|
||||
:disk_virtual_size => 0.0
|
||||
}
|
||||
|
||||
doc.elements.each('domain/devices/disk/source') do |ele|
|
||||
next if !ele.attributes['file']
|
||||
|
||||
text = `qemu-img info --output=json #{ele.attributes['file']}`
|
||||
next if !$? || !$?.success?
|
||||
|
||||
json = JSON.parse(text)
|
||||
|
||||
data[:disk_actual_size] += json['actual-size'].to_f/1024/1024
|
||||
data[:disk_virtual_size] += json['virtual-size'].to_f/1024/1024
|
||||
end
|
||||
|
||||
data[:disk_actual_size] = data[:disk_actual_size].round
|
||||
data[:disk_virtual_size] = data[:disk_virtual_size].round
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
# Convert the output of dumpxml to an OpenNebula template
|
||||
# @param xml [String] output of dumpxml
|
||||
# @return [Array] uuid and OpenNebula template encoded in base64
|
||||
def self.xml_to_one(xml)
|
||||
doc = REXML::Document.new(xml)
|
||||
|
||||
name = REXML::XPath.first(doc, '/domain/name').text
|
||||
uuid = REXML::XPath.first(doc, '/domain/uuid').text
|
||||
vcpu = REXML::XPath.first(doc, '/domain/vcpu').text
|
||||
memory = REXML::XPath.first(doc, '/domain/memory').text.to_i / 1024
|
||||
arch = REXML::XPath.first(doc, '/domain/os/type').attributes['arch']
|
||||
|
||||
=begin
|
||||
disks = []
|
||||
REXML::XPath.each(doc, '/domain/devices/disk') do |d|
|
||||
type = REXML::XPath.first(d, '//disk').attributes['type']
|
||||
driver = REXML::XPath.first(d, '//disk/driver').attributes['type']
|
||||
source = REXML::XPath.first(d, '//disk/source').attributes[type]
|
||||
target = REXML::XPath.first(d, '//disk/target').attributes['dev']
|
||||
|
||||
disks << {
|
||||
:type => type,
|
||||
:driver => driver,
|
||||
:source => source,
|
||||
:target => target
|
||||
}
|
||||
end
|
||||
|
||||
disks_txt = ''
|
||||
|
||||
disks.each do |disk|
|
||||
disks_txt << "DISK=[\n"
|
||||
disks_txt << " SOURCE=\"#{disk[:source]}\",\n"
|
||||
disks_txt << " DRIVER=\"#{disk[:driver]}\",\n"
|
||||
disks_txt << " TARGET=\"#{disk[:target]}\""
|
||||
disks_txt << "]\n"
|
||||
end
|
||||
|
||||
|
||||
interfaces = []
|
||||
REXML::XPath.each(doc,
|
||||
"/domain/devices/interface[@type='bridge']") do |i|
|
||||
mac = REXML::XPath.first(i, '//interface/mac').
|
||||
attributes['address']
|
||||
bridge = REXML::XPath.first(i, '//interface/source').
|
||||
attributes['bridge']
|
||||
model = REXML::XPath.first(i, '//interface/model').
|
||||
attributes['type']
|
||||
|
||||
interfaces << {
|
||||
:mac => mac,
|
||||
:bridge => bridge,
|
||||
:model => model
|
||||
}
|
||||
end
|
||||
|
||||
interfaces_txt = ''
|
||||
|
||||
interfaces.each do |interface|
|
||||
interfaces_txt << "NIC=[\n"
|
||||
interfaces_txt << " MAC=\"#{interface[:mac]}\",\n"
|
||||
interfaces_txt << " BRIDGE=\"#{interface[:bridge]}\",\n"
|
||||
interfaces_txt << " MODEL=\"#{interface[:model]}\""
|
||||
interfaces_txt << "]\n"
|
||||
end
|
||||
=end
|
||||
|
||||
spice = REXML::XPath.first(doc,
|
||||
"/domain/devices/graphics[@type='spice']")
|
||||
spice = spice.attributes['port'] if spice
|
||||
|
||||
spice_txt = ''
|
||||
if spice
|
||||
spice_txt = %Q<GRAPHICS = [ TYPE="spice", PORT="#{spice}" ]>
|
||||
end
|
||||
|
||||
vnc = REXML::XPath.first(doc, "/domain/devices/graphics[@type='vnc']")
|
||||
vnc = vnc.attributes['port'] if vnc
|
||||
|
||||
vnc_txt = ''
|
||||
if vnc
|
||||
vnc_txt = %Q<GRAPHICS = [ TYPE="vnc", PORT="#{vnc}" ]>
|
||||
end
|
||||
|
||||
|
||||
feature_list = %w{acpi apic pae}
|
||||
features = []
|
||||
|
||||
feature_list.each do |feature|
|
||||
if REXML::XPath.first(doc, "/domain/features/#{feature}")
|
||||
features << feature
|
||||
end
|
||||
end
|
||||
|
||||
feat = []
|
||||
features.each do |feature|
|
||||
feat << %Q[ #{feature.upcase}="yes"]
|
||||
end
|
||||
|
||||
features_txt = "FEATURES=[\n"
|
||||
features_txt << feat.join(",\n")
|
||||
features_txt << "]\n"
|
||||
|
||||
|
||||
template = <<EOT
|
||||
NAME="#{name}"
|
||||
CPU=#{vcpu}
|
||||
VCPU=#{vcpu}
|
||||
MEMORY=#{memory}
|
||||
HYPERVISOR="kvm"
|
||||
IMPORT_VM_ID="#{uuid}"
|
||||
OS=[ARCH="#{arch}"]
|
||||
#{features_txt}
|
||||
#{spice_txt}
|
||||
#{vnc_txt}
|
||||
EOT
|
||||
|
||||
|
||||
return uuid, template
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
@ -309,6 +490,15 @@ module XEN
|
||||
# @return [Hash, nil] Hash with the VM information or nil in case of error
|
||||
def self.get_all_vm_info
|
||||
begin
|
||||
begin
|
||||
list_long = get_vm_list_long
|
||||
rescue
|
||||
list_long = []
|
||||
end
|
||||
|
||||
vm_templates = get_vm_templates(list_long)
|
||||
vm_disk_stats = get_vm_disk_stats(list_long)
|
||||
|
||||
text = `#{CONF['XM_POLL']}`
|
||||
|
||||
return nil if $?.exitstatus != 0
|
||||
@ -334,16 +524,26 @@ module XEN
|
||||
domain_lines.each do |dom|
|
||||
dom_data = dom.gsub('no limit', 'no-limit').strip.split
|
||||
|
||||
name = dom_data[0]
|
||||
|
||||
dom_hash = Hash.new
|
||||
|
||||
dom_hash[:name] = dom_data[0]
|
||||
dom_hash[:name] = name
|
||||
dom_hash[:vm_name] = name
|
||||
dom_hash[:state] = get_state(dom_data[1])
|
||||
dom_hash[:usedcpu] = dom_data[3]
|
||||
dom_hash[:usedmemory] = dom_data[4]
|
||||
dom_hash[:nettx] = dom_data[10].to_i * 1024
|
||||
dom_hash[:netrx] = dom_data[11].to_i * 1024
|
||||
|
||||
domains[dom_hash[:name]] = dom_hash
|
||||
if !name.match(/^one-\d/) && vm_templates[name]
|
||||
dom_hash[:template] =
|
||||
Base64.encode64(vm_templates[name]).delete("\n")
|
||||
end
|
||||
|
||||
dom_hash.merge!(vm_disk_stats[name]) if vm_disk_stats[name]
|
||||
|
||||
domains[name] = dom_hash
|
||||
end
|
||||
|
||||
domains
|
||||
@ -374,6 +574,72 @@ module XEN
|
||||
'-'
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_vm_list_long
|
||||
return {} if !JSON_LOADED
|
||||
|
||||
text = `#{CONF['XM_LIST']} -l`
|
||||
doms = JSON.parse(text)
|
||||
end
|
||||
|
||||
def self.get_vm_templates(doms)
|
||||
dom_tmpl = {}
|
||||
|
||||
doms.each do |dom|
|
||||
name = dom['config']['c_info']['name']
|
||||
name = URI.escape(name)
|
||||
|
||||
tmp = %Q<NAME = "#{name}"\n>
|
||||
tmp << %Q<IMPORT_VM_ID = "#{name}"\n>
|
||||
|
||||
vcpus = dom['config']['b_info']['max_vcpus'].to_i
|
||||
vcpus = 1 if vcpus < 1
|
||||
|
||||
tmp << %Q<CPU = #{vcpus}\n>
|
||||
tmp << %Q<VCPU = #{vcpus}\n>
|
||||
|
||||
memory = dom['config']['b_info']['max_memkb']
|
||||
memory /= 1024
|
||||
|
||||
tmp << %Q<MEMORY = #{memory}\n>
|
||||
|
||||
dom_tmpl[name] = tmp
|
||||
end
|
||||
|
||||
dom_tmpl
|
||||
end
|
||||
|
||||
def self.get_vm_disk_stats(doms)
|
||||
dom_disk_stats = {}
|
||||
|
||||
doms.each do |dom|
|
||||
data = {
|
||||
:disk_actual_size => 0.0,
|
||||
:disk_virtual_size => 0.0
|
||||
}
|
||||
|
||||
dom['config']['disks'].each do |disk|
|
||||
next if !disk['pdev_path']
|
||||
|
||||
path = disk['pdev_path']
|
||||
|
||||
text = `qemu-img info --output=json #{path}`
|
||||
next if !$? || !$?.success?
|
||||
|
||||
json = JSON.parse(text)
|
||||
|
||||
data[:disk_actual_size] += json['actual-size'].to_f/1024/1024
|
||||
data[:disk_virtual_size] += json['virtual-size'].to_f/1024/1024
|
||||
end
|
||||
|
||||
data[:disk_actual_size] = data[:disk_actual_size].round
|
||||
data[:disk_virtual_size] = data[:disk_virtual_size].round
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
dom_disk_stats
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
@ -410,7 +676,7 @@ def setup_hypervisor
|
||||
case hypervisor.name
|
||||
when 'XEN'
|
||||
file = 'xenrc'
|
||||
vars = %w{XM_POLL}
|
||||
vars = %w{XM_POLL XM_LIST}
|
||||
when 'KVM'
|
||||
file = 'kvmrc'
|
||||
vars = %w{LIBVIRT_URI}
|
||||
@ -467,7 +733,6 @@ end
|
||||
|
||||
def print_all_vm_info(hypervisor)
|
||||
require 'yaml'
|
||||
require 'base64'
|
||||
require 'zlib'
|
||||
|
||||
vms = hypervisor.get_all_vm_info
|
||||
@ -493,9 +758,17 @@ def print_all_vm_template(hypervisor)
|
||||
number = name.split('-').last
|
||||
end
|
||||
|
||||
vm_name = data[:vm_name]
|
||||
|
||||
string = "VM=[\n"
|
||||
string << " ID=#{number},\n"
|
||||
string << " DEPLOY_ID=#{name},\n"
|
||||
string << %Q( VM_NAME="#{vm_name}",\n) if vm_name
|
||||
|
||||
if data[:template]
|
||||
string << %Q( IMPORT_TEMPLATE="#{data[:template]}",\n)
|
||||
data.delete(:template)
|
||||
end
|
||||
|
||||
values = data.map do |key, value|
|
||||
print_data(key, value)
|
||||
|
@ -644,6 +644,10 @@ class VCenterHost < ::OpenNebula::Host
|
||||
str_info << "ID=#{number},"
|
||||
str_info << "DEPLOY_ID=\"#{vm.vm.config.uuid}\","
|
||||
str_info << "VM_NAME=\"#{name}\","
|
||||
if number == -1
|
||||
vm_template_to_one = Base64.encode64(vm.vm_to_one).gsub("\n","")
|
||||
str_info << "IMPORT_TEMPLATE=\"#{vm_template_to_one}\","
|
||||
end
|
||||
str_info << "POLL=\"#{vm.info}\"]"
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user