// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
(function($, cloudStack) {
/**
* Serialize form data as object
*/
var getData = function($wizard, options) {
if (!options) options = {};
var $forms = $wizard.find('form').filter(function() {
return !options.all ? !$(this).closest('.multi-edit').size() : true;
});
var $physicalNetworkItems = $wizard.find(
'.steps .setup-physical-network .select-container.multi'
).filter(':not(.disabled)');
var $publicTrafficItems = $wizard.find(
'.steps .setup-public-traffic .data-body .data-item');
var $storageTrafficItems = $wizard.find(
'.steps .setup-storage-traffic .data-body .data-item');
var groupedForms = {};
if ($physicalNetworkItems.find('li.traffic-type-draggable.storage').size()) {
$wizard.find('li.conditional.storage-traffic').show();
} else {
$wizard.find('li.conditional.storage-traffic').hide();
}
if (options.all) {
return cloudStack.serializeForm($forms, {
escapeSlashes: true
});
}
// Group form fields together, by form ID
$forms.each(function() {
var $form = $(this);
var id = $form.attr('rel');
if (!id) return true;
groupedForms[id] = cloudStack.serializeForm($form, {
escapeSlashes: true
});
return true;
});
// Get physical network data
groupedForms.physicalNetworks = $.map(
$physicalNetworkItems,
function(network) {
var $network = $(network);
var $guestForm = $wizard.find('form[guest-network-id=' + $network.index() + ']');
var trafficTypeConfiguration = {};
$network.find('.traffic-type-draggable').each(function() {
var $trafficType = $(this);
var trafficTypeID = $trafficType.attr('traffic-type-id');
trafficTypeConfiguration[trafficTypeID] = $trafficType.data('traffic-type-data');
});
return {
id: $network.index(),
name: $network.find('.field.name input[type=text]').val(),
isolationMethod: $network.find('.field.name select').val(),
// Traffic type list
trafficTypes: $.map(
$network.find('.traffic-type-draggable'),
function(trafficType) {
var $trafficType = $(trafficType);
return $trafficType.attr('traffic-type-id');
}
),
// Traffic type configuration data
trafficTypeConfiguration: trafficTypeConfiguration,
guestConfiguration: $guestForm.size() ? cloudStack.serializeForm($guestForm) : null
};
}
);
// Get public traffic data (multi-edit)
groupedForms.publicTraffic = $.map(
$publicTrafficItems,
function(publicTrafficItem) {
var $publicTrafficItem = $(publicTrafficItem);
var publicTrafficData = {};
var fields = [
'gateway',
'netmask',
'vlanid',
'startip',
'endip'
];
$(fields).each(function() {
publicTrafficData[this] =
$publicTrafficItem.find('td.' + this + ' span').html();
});
return publicTrafficData;
}
);
// Get storage traffic data (multi-edit)
groupedForms.storageTraffic = $.map(
$storageTrafficItems,
function(storageTrafficItem) {
var $storageTrafficItem = $(storageTrafficItem);
var storageTrafficData = {};
var fields = [
'gateway',
'netmask',
'vlan',
'startip',
'endip'
];
$(fields).each(function() {
storageTrafficData[this] =
$storageTrafficItem.find('td.' + this + ' span').html();
});
return storageTrafficData;
}
);
// Hack to fix forward slash JS error
$.each(groupedForms, function(key1, value1) {
$.each(value1, function(key2, value2) {
if (typeof value2 == 'string') {
groupedForms[key1][key2] = value2.replace(/__forwardSlash__/g, '\/');
}
});
});
// Include zone network type
if (groupedForms.zone) {
groupedForms.zone.networkType = $forms.find('input[name=network-model]:checked').val();
// Include zone isolation mode, supported for Advanced zones only
if (groupedForms.zone.networkType == 'Advanced') {
groupedForms.zone.sgEnabled = $forms.find('input[name=zone-advanced-sg-enabled]')
.is(':checked') ? true : false;
}
}
return groupedForms;
};
/**
* Handles validation for custom UI components
*/
var customValidation = {
networkRanges: function($form) {
if ($form.closest('.multi-edit').find('.data-item').size()) {
return true;
}
cloudStack.dialog.notice({
message: _l('message.please.add.at.lease.one.traffic.range')
});
return false;
},
physicalNetworks: function($form) {
var $enabledPhysicalNetworks = $form.filter(':not(.disabled)').filter(function() {
return $(this).find('.traffic-type-draggable').size();
});
var $trafficTypes = $enabledPhysicalNetworks.find('.traffic-type-draggable');
var $configuredTrafficTypes = $trafficTypes.filter(function() {
var $trafficType = $(this);
return ($trafficType.data('traffic-type-data') != null);
});
if ($enabledPhysicalNetworks.size() > 1 &&
$configuredTrafficTypes.size() != $trafficTypes.size()) {
cloudStack.dialog.notice({
message: _l('message.configure.all.traffic.types')
});
return false;
}
return true;
}
};
/**
* Determine if UI components in step should be custom-validated
* (i.e., not a standard form)
*/
var checkCustomValidation = function($step) {
var $multiEditForm = $step.find('.multi-edit form');
var $physicalNetworks = $step.find('.select-container.multi');
var isCustomValidated;
if ($multiEditForm.size()) {
isCustomValidated = customValidation.networkRanges($multiEditForm);
} else if ($physicalNetworks.size()) {
isCustomValidated = customValidation.physicalNetworks($physicalNetworks);
} else {
isCustomValidated = true;
}
return isCustomValidated;
};
var isAdvancedNetwork = function($wizard) {
return getData($wizard, {
all: true
})['network-model'] == 'Advanced';
};
/**
* Setup physical network wizard UI
*/
var physicalNetwork = {
init: function($wizard) {
var $existingPhysicalNetworks = physicalNetwork.getNetworks($wizard);
// Initialize physical networks
if (!$existingPhysicalNetworks.size()) {
physicalNetwork.add($wizard);
} else if (!isAdvancedNetwork($wizard)) {
$existingPhysicalNetworks.filter(':first').siblings().each(function() {
physicalNetwork.remove($(this));
});
}
physicalNetwork.updateNetworks(physicalNetwork.getNetworks($wizard));
$wizard.find('.traffic-types-drag-area ul li').removeClass('required disabled clone');
// Setup clone traffic types
$(physicalNetwork.cloneTrafficTypes($wizard)).each(function() {
var trafficTypeID = this;
$wizard.find('.traffic-types-drag-area ul li').filter(function() {
return $(this).hasClass(trafficTypeID);
}).addClass('clone');
});
// Setup required traffic types
$(physicalNetwork.requiredTrafficTypes($wizard)).each(function() {
var trafficTypeID = this;
var $firstPhysicalNetwork = physicalNetwork.getNetworks($wizard).filter(':first');
// First physical network gets required traffic types
physicalNetwork.assignTrafficType(trafficTypeID, $firstPhysicalNetwork);
$wizard.find('.traffic-types-drag-area ul li').filter(function() {
return $(this).hasClass(trafficTypeID);
}).addClass('required');
});
// Setup disabled traffic types
$(physicalNetwork.disabledTrafficTypes($wizard)).each(function() {
var trafficTypeID = this;
var $trafficType = physicalNetwork.getTrafficType(this, $wizard);
physicalNetwork.unassignTrafficType($trafficType);
$wizard.find('.traffic-types-drag-area ul li').filter(function() {
return $(this).hasClass(trafficTypeID);
}).addClass('disabled');
});
},
/**
* Traffic type edit dialog
*/
editTrafficTypeDialog: function($trafficType) {
var trafficData = $trafficType.data('traffic-type-data') ?
$trafficType.data('traffic-type-data') : {};
var hypervisor = getData($trafficType.closest('.zone-wizard')).zone.hypervisor;
var zoneType = getData($trafficType.closest('.zone-wizard')).zone.networkType;
var fields;
if (hypervisor == 'VMware') {
fields = {
vSwitchName: {
label: 'label.vswitch.name' ,
defaultValue: trafficData.vSwitchName
},
vlanId: {
label: 'label.vlan.id',
defaultValue: trafficData.vlanId
}
};
if(zoneType == 'Advanced') {
if($trafficType.hasClass('guest') || $trafficType.hasClass('public')) {
if(trafficData.vSwitchType == null) {
var useDvs = false;
$.ajax({
url: createURL('listConfigurations'),
data: {
name: 'vmware.use.dvswitch'
},
async: false,
success: function(json) {
if (json.listconfigurationsresponse.configuration[0].value == 'true') {
useDvs = true;
}
}
});
if (useDvs == true) {
var useNexusDvs = false;
$.ajax({
url: createURL('listConfigurations'),
data: {
name: 'vmware.use.nexus.vswitch'
},
async: false,
success: function(json) {
if (json.listconfigurationsresponse.configuration[0].value == 'true') {
useNexusDvs = true;
}
}
});
if (useNexusDvs == true) {
trafficData.vSwitchType = 'nexusdvs';
fields.vSwitchName.defaultValue = 'epp0';
} else {
trafficData.vSwitchType = 'vmwaredvs';
fields.vSwitchName.defaultValue = 'dvSwitch0';
}
} else { //useDvs == false
trafficData.vSwitchType = 'vmwaresvs';
fields.vSwitchName.defaultValue = 'vSwitch0';
}
}
$.extend(fields, {
vSwitchType: {
label: 'label.vSwitch.type',
select: function (args) {
args.response.success({
data: [{
id: 'nexusdvs',
description: 'Cisco Nexus 1000v Distributed Virtual Switch'
}, {
id: 'vmwaresvs',
description: 'VMware vNetwork Standard Virtual Switch'
}, {
id: 'vmwaredvs',
description: 'VMware vNetwork Distributed Virtual Switch'
}]
});
},
defaultValue: trafficData.vSwitchType
}
});
}
}
} else {
fields = {
label: {
label: hypervisor + ' ' + _l('label.traffic.label'),
defaultValue: trafficData.label
}
};
}
cloudStack.dialog.createForm({
form: {
title: _l('label.edit.traffic.type'),
desc: _l('message.edit.traffic.type'),
fields: fields
},
after: function(args) {
$trafficType.data('traffic-type-data', args.data);
}
});
},
/**
* Get required traffic type IDs for proper validation
*/
requiredTrafficTypes: function($wizard) {
return cloudStack.zoneWizard.requiredTrafficTypes({
data: getData($wizard)
});
},
/**
* Get required traffic type IDs for proper validation
*/
disabledTrafficTypes: function($wizard) {
return cloudStack.zoneWizard.disabledTrafficTypes({
data: getData($wizard)
});
},
/**
* Get clone-type traffic type IDs for proper validation
*/
cloneTrafficTypes: function($wizard) {
return cloudStack.zoneWizard.cloneTrafficTypes({
data: getData($wizard)
});
},
/**
* Physical network step: Renumber network form items
*/
renumberFormItems: function($container) {
var $items = $container.find('.select-container.multi');
$items.each(function() {
var $item = $(this);
var $networkName = $item.find('.field.name input[type=text]');
var $networkId = $item.find('input[name=id]');
var $networkTypes = $item.find('.field.network-types input');
var index = $item.index();
$networkId.val(index);
$networkName.attr('name', 'physicalNetworks[' + index + ']' + '.name');
$networkTypes.val(index);
});
},
/**
* Get main physical network wizard step container
*
* @param $elem Any elem within the container
*/
getMainContainer: function($elem) {
return $elem.closest('.steps .setup-physical-network');
},
/**
* Returns traffic elem
*
* @param trafficTypeID ID of desired traffic type
*/
getTrafficType: function(trafficTypeID, $container) {
var $trafficType = $container.find('li.traffic-type-draggable').filter(function() {
return $(this).attr('traffic-type-id') == trafficTypeID;
});
if (physicalNetwork.isTrafficTypeClone($trafficType) && !$container.closest('.select-container.multi').size()) {
// Get traffic type from original container
return $trafficType.filter(function() {
return $(this).closest(
physicalNetwork.getOriginalTrafficContainer($trafficType)
).size();
});
}
return $trafficType;
},
/**
* Get original drag container for traffic type elem
*
* @param $trafficType Traffic type elem
*/
getOriginalTrafficContainer: function($trafficType) {
var $dragContainer = physicalNetwork.getMainContainer($trafficType)
.find('.traffic-types-drag-area ul > li')
.filter(function() {
return $(this).hasClass($trafficType.attr('traffic-type-id'));
})
.find('ul');
return $dragContainer;
},
/**
* Get all physical networks
*
* @param $container Physical network step - main container
*/
getNetworks: function($container) {
return $container.find('.select-container.multi');
},
/**
* Determine if traffic type is a 'cloned' type
*
* @param $trafficType
*/
isTrafficTypeClone: function($trafficType) {
return physicalNetwork.getOriginalTrafficContainer($trafficType).parent().hasClass('clone');
},
/**
* Assigns traffic type to specified physical network
*
* @param trafficTypeID ID of desired traffic type
* @param $physicalNetwork Physical network elem
*/
assignTrafficType: function(trafficTypeID, $physicalNetwork, data) {
var $container = physicalNetwork.getMainContainer($physicalNetwork);
var $trafficType = physicalNetwork.getTrafficType(trafficTypeID, $container);
var $dropArea = $physicalNetwork.find('.drop-container ul');
if ($physicalNetwork.find('.traffic-type-draggable[traffic-type-id=' + trafficTypeID + ']').size()) return false;
if (physicalNetwork.isTrafficTypeClone($trafficType)) {
if (!physicalNetwork.getTrafficType(trafficTypeID, $physicalNetwork).size()) {
$trafficType = $trafficType.clone()
.removeClass('disabled')
.appendTo($dropArea)
.draggable(physicalNetwork.draggableOptions($physicalNetwork.closest('.zone-wizard')));
} else {
return false;
}
} else {
$trafficType.appendTo($dropArea);
}
if (data) {
$trafficType.data('traffic-type-data', data);
}
physicalNetwork.updateNetworks($.merge($physicalNetwork, $physicalNetwork.siblings()));
return $trafficType;
},
/**
* Assigns traffic type to original drag container
*
* @param trafficTypeID ID of desired traffic type
* @param $container Physical network wizard step container
* @param $physicalNetwork (optional) Specific physical network to remove from -- only for clones
*/
unassignTrafficType: function($trafficType) {
var $wizard = $trafficType.closest('.zone-wizard');
var $originalContainer = physicalNetwork.getOriginalTrafficContainer($trafficType);
var $physicalNetworks = physicalNetwork.getNetworks($wizard);
var trafficTypeID = $trafficType.attr('traffic-type-id');
if (!physicalNetwork.isTrafficTypeClone($trafficType) &&
$.inArray(trafficTypeID, physicalNetwork.requiredTrafficTypes($wizard)) == -1) {
$trafficType.appendTo($originalContainer);
} else {
physicalNetwork.assignTrafficType(
trafficTypeID,
$physicalNetworks.filter(':first')
);
if (physicalNetwork.isTrafficTypeClone($trafficType) &&
$physicalNetworks.find('.traffic-type-draggable[traffic-type-id=' + trafficTypeID + ']').size() > 1) {
$trafficType.remove();
}
}
return $trafficType;
},
/**
* Returns true if new physical network item needs to be added
*/
needsNewNetwork: function($containers) {
// Basic zones do not have multiple physical networks
if (!isAdvancedNetwork($containers.closest('.zone-wizard')))
return false;
var $emptyContainers = $containers.filter(function() {
return !$(this).find('li').size();
});
return !$emptyContainers.size() ? $containers.size() : false;
},
/**
* Cleanup physical network containers
*/
updateNetworks: function($containers) {
var $mainContainer = physicalNetwork.getMainContainer($containers);
var $allPhysicalNetworks = physicalNetwork.getNetworks($mainContainer);
var containerTotal = isAdvancedNetwork($containers.closest('.zone-wizard')) ?
2 : 1;
$allPhysicalNetworks.each(function() {
var $ul = $(this).find('.drop-container ul');
if (!$(this).find('li').size()) {
$(this).addClass('disabled');
$ul.fadeOut();
} else {
$(this).removeClass('disabled');
$ul.show();
}
});
$containers.each(function() {
var $currentContainer = $(this);
if (!$currentContainer.find('li').size() &&
$containers.size() > containerTotal) {
$currentContainer.remove();
}
});
$containers = $containers.closest('.setup-physical-network')
.find('.select-container.multi');
if (physicalNetwork.needsNewNetwork($containers)) {
physicalNetwork.add($mainContainer.parent());
}
$containers.filter(':first').find('.remove.physical-network').remove();
return $containers;
},
/**
* Default options for initializing traffic type draggables
*/
draggableOptions: function($wizard) {
return {
appendTo: $wizard,
helper: 'clone',
// Events
start: function(event, ui) {
$(this).addClass('disabled');
},
stop: function(event, ui) {
$(this).removeClass('disabled');
},
cancel: '.edit-traffic-type'
};
},
/**
* Physical network step: Generate new network element
*/
add: function($wizard) {
var $container = $wizard.find('.setup-physical-network .content.input-area form');
var $physicalNetworkItem = $('
').addClass('select-container multi');
var $deleteButton = $('
').addClass('button remove physical-network')
.attr({
title: 'label.remove.this.physical.network'
})
.append('
').addClass('icon').html(' ');
var $icon = $('').addClass('physical-network-icon');
var $nameField = $('
').addClass('field name').append(
$('
').addClass('name').append(
$('