1
0
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:
Ruben S. Montero 2015-05-08 18:16:12 +02:00
commit ea84c6ef95
17 changed files with 1174 additions and 135 deletions

View File

@ -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

View File

@ -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

View File

@ -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++)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 + '&emsp;<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,

View File

@ -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">&#215;</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 + '&emsp;<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(){

View File

@ -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(){

View File

@ -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">&#215;</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 + '&emsp;<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(){

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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}\"]"
}