// 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: dictionary['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') && $trafficType.data('traffic-type-data').label.length >= 1; }); 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; cloudStack.dialog.createForm({ form: { title: _l('label.edit.traffic.type'), desc: _l('message.edit.traffic.type'), fields: { label: { label: hypervisor + ' ' + _l('label.traffic.label'), defaultValue: trafficData.label } } }, 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: 'Remove this physical network' }) .append('').addClass('icon').html(' '); var $icon = $('
').addClass('physical-network-icon'); var $nameField = $('
').addClass('field name').append( $('
').addClass('name').append( $('