| /* |
| * 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. |
| */ |
| |
| // Controller for Clusters screen. |
| export default ['$rootScope', '$scope', '$http', '$state', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteInput', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteEventGroups', 'DemoInfo', 'IgniteLegacyTable', 'IgniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', 'IgniteVersion', |
| function($root, $scope, $http, $state, $timeout, LegacyUtils, Messages, Confirm, Input, Loading, ModelNormalizer, UnsavedChangesGuard, igniteEventGroups, DemoInfo, LegacyTable, Resource, ErrorPopover, FormUtils, Version) { |
| let __original_value; |
| |
| this.available = Version.available.bind(Version); |
| |
| const rebuildDropdowns = () => { |
| $scope.eventStorage = [ |
| {value: 'Memory', label: 'Memory'}, |
| {value: 'Custom', label: 'Custom'} |
| ]; |
| |
| $scope.marshallerVariant = [ |
| {value: 'JdkMarshaller', label: 'JdkMarshaller'}, |
| {value: null, label: 'Default'} |
| ]; |
| |
| if (this.available('2.0.0')) { |
| $scope.eventStorage.push({value: null, label: 'Disabled'}); |
| |
| $scope.eventGroups = _.filter(igniteEventGroups, ({value}) => value !== 'EVTS_SWAPSPACE'); |
| } |
| else { |
| $scope.eventGroups = igniteEventGroups; |
| |
| $scope.marshallerVariant.splice(0, 0, {value: 'OptimizedMarshaller', label: 'OptimizedMarshaller'}); |
| } |
| }; |
| |
| rebuildDropdowns(); |
| |
| const filterModel = () => { |
| if ($scope.backupItem) { |
| if (this.available('2.0.0')) { |
| const evtGrps = _.map($scope.eventGroups, 'value'); |
| |
| _.remove(__original_value, (evtGrp) => !_.includes(evtGrps, evtGrp)); |
| _.remove($scope.backupItem.includeEventTypes, (evtGrp) => !_.includes(evtGrps, evtGrp)); |
| |
| if (_.get($scope.backupItem, 'marshaller.kind') === 'OptimizedMarshaller') |
| $scope.backupItem.marshaller.kind = null; |
| } |
| else if ($scope.backupItem && !_.get($scope.backupItem, 'eventStorage.kind')) |
| _.set($scope.backupItem, 'eventStorage.kind', 'Memory'); |
| } |
| }; |
| |
| Version.currentSbj.subscribe({ |
| next: () => { |
| rebuildDropdowns(); |
| |
| filterModel(); |
| } |
| }); |
| |
| UnsavedChangesGuard.install($scope); |
| |
| const emptyCluster = {empty: true}; |
| |
| const blank = { |
| activeOnStart: true, |
| cacheSanityCheckEnabled: true, |
| atomicConfiguration: {}, |
| binaryConfiguration: {}, |
| cacheKeyConfiguration: [], |
| communication: {}, |
| connector: {}, |
| deploymentSpi: { |
| URI: { |
| uriList: [], |
| scanners: [] |
| } |
| }, |
| discovery: { |
| Cloud: { |
| regions: [], |
| zones: [] |
| } |
| }, |
| marshaller: {}, |
| peerClassLoadingLocalClassPathExclude: [], |
| sslContextFactory: { |
| trustManagers: [] |
| }, |
| swapSpaceSpi: {}, |
| transactionConfiguration: {}, |
| collision: {}, |
| memoryConfiguration: { |
| memoryPolicies: [] |
| }, |
| hadoopConfiguration: { |
| nativeLibraryNames: [] |
| }, |
| serviceConfigurations: [], |
| executorConfiguration: [], |
| sqlConnectorConfiguration: { |
| tcpNoDelay: true |
| } |
| }; |
| |
| const pairFields = { |
| attributes: {id: 'Attribute', idPrefix: 'Key', searchCol: 'name', valueCol: 'key', dupObjName: 'name', group: 'attributes'}, |
| 'collision.JobStealing.stealingAttributes': {id: 'CAttribute', idPrefix: 'Key', searchCol: 'name', valueCol: 'key', dupObjName: 'name', group: 'collision'} |
| }; |
| |
| $scope.tablePairValid = function(item, field, index, stopEdit) { |
| const pairField = pairFields[field.model]; |
| |
| const pairValue = LegacyTable.tablePairValue(field, index); |
| |
| if (pairField) { |
| const model = _.get(item, field.model); |
| |
| if (LegacyUtils.isDefined(model)) { |
| const idx = _.findIndex(model, (pair) => { |
| return pair[pairField.searchCol] === pairValue[pairField.valueCol]; |
| }); |
| |
| // Found duplicate by key. |
| if (idx >= 0 && idx !== index) { |
| if (stopEdit) |
| return false; |
| |
| return ErrorPopover.show(LegacyTable.tableFieldId(index, pairField.idPrefix + pairField.id), 'Attribute with such ' + pairField.dupObjName + ' already exists!', $scope.ui, pairField.group); |
| } |
| } |
| } |
| |
| return true; |
| }; |
| |
| $scope.tableSave = function(field, index, stopEdit) { |
| if (LegacyTable.tablePairSaveVisible(field, index)) |
| return LegacyTable.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit); |
| |
| return true; |
| }; |
| |
| $scope.tableReset = (trySave) => { |
| const field = LegacyTable.tableField(); |
| |
| if (trySave && LegacyUtils.isDefined(field) && !$scope.tableSave(field, LegacyTable.tableEditedRowIndex(), true)) |
| return false; |
| |
| LegacyTable.tableReset(); |
| |
| return true; |
| }; |
| |
| $scope.tableNewItem = function(field) { |
| if ($scope.tableReset(true)) { |
| if (field.type === 'failoverSpi') { |
| if (LegacyUtils.isDefined($scope.backupItem.failoverSpi)) |
| $scope.backupItem.failoverSpi.push({}); |
| else |
| $scope.backupItem.failoverSpi = {}; |
| } |
| else if (field.type === 'loadBalancingSpi') { |
| const newLoadBalancing = {Adaptive: { |
| loadProbe: { |
| Job: {useAverage: true}, |
| CPU: { |
| useAverage: true, |
| useProcessors: true |
| }, |
| ProcessingTime: {useAverage: true} |
| } |
| }}; |
| |
| if (LegacyUtils.isDefined($scope.backupItem.loadBalancingSpi)) |
| $scope.backupItem.loadBalancingSpi.push(newLoadBalancing); |
| else |
| $scope.backupItem.loadBalancingSpi = [newLoadBalancing]; |
| } |
| else if (field.type === 'checkpointSpi') { |
| const newCheckpointCfg = { |
| FS: { |
| directoryPaths: [] |
| }, |
| S3: { |
| awsCredentials: { |
| kind: 'Basic' |
| }, |
| clientConfiguration: { |
| retryPolicy: { |
| kind: 'Default' |
| }, |
| useReaper: true |
| } |
| } |
| }; |
| |
| if (LegacyUtils.isDefined($scope.backupItem.checkpointSpi)) |
| $scope.backupItem.checkpointSpi.push(newCheckpointCfg); |
| else |
| $scope.backupItem.checkpointSpi = [newCheckpointCfg]; |
| } |
| else if (field.type === 'memoryPolicies') |
| $scope.backupItem.memoryConfiguration.memoryPolicies.push({}); |
| else if (field.type === 'serviceConfigurations') |
| $scope.backupItem.serviceConfigurations.push({}); |
| else if (field.type === 'executorConfigurations') |
| $scope.backupItem.executorConfiguration.push({}); |
| else |
| LegacyTable.tableNewItem(field); |
| } |
| }; |
| |
| $scope.tableNewItemActive = LegacyTable.tableNewItemActive; |
| |
| $scope.tableStartEdit = function(item, field, index) { |
| if ($scope.tableReset(true)) |
| LegacyTable.tableStartEdit(item, field, index, $scope.tableSave); |
| }; |
| |
| $scope.tableEditing = LegacyTable.tableEditing; |
| |
| $scope.tableRemove = function(item, field, index) { |
| if ($scope.tableReset(true)) |
| LegacyTable.tableRemove(item, field, index); |
| }; |
| |
| $scope.tablePairSave = LegacyTable.tablePairSave; |
| $scope.tablePairSaveVisible = LegacyTable.tablePairSaveVisible; |
| |
| $scope.attributesTbl = { |
| type: 'attributes', |
| model: 'attributes', |
| focusId: 'Attribute', |
| ui: 'table-pair', |
| keyName: 'name', |
| valueName: 'value', |
| save: $scope.tableSave |
| }; |
| |
| $scope.stealingAttributesTbl = { |
| type: 'attributes', |
| model: 'collision.JobStealing.stealingAttributes', |
| focusId: 'CAttribute', |
| ui: 'table-pair', |
| keyName: 'name', |
| valueName: 'value', |
| save: $scope.tableSave |
| }; |
| |
| $scope.removeFailoverConfiguration = function(idx) { |
| $scope.backupItem.failoverSpi.splice(idx, 1); |
| }; |
| |
| $scope.supportedJdbcTypes = LegacyUtils.mkOptions(LegacyUtils.SUPPORTED_JDBC_TYPES); |
| |
| // We need to initialize backupItem with empty object in order to properly used from angular directives. |
| $scope.backupItem = emptyCluster; |
| |
| $scope.ui = FormUtils.formUI(); |
| $scope.ui.activePanels = [0]; |
| $scope.ui.topPanels = [0]; |
| |
| $scope.saveBtnTipText = FormUtils.saveBtnTipText; |
| $scope.widthIsSufficient = FormUtils.widthIsSufficient; |
| |
| $scope.contentVisible = function() { |
| const item = $scope.backupItem; |
| |
| return !item.empty && (!item._id || _.find($scope.displayedRows, {_id: item._id})); |
| }; |
| |
| $scope.toggleExpanded = function() { |
| $scope.ui.expanded = !$scope.ui.expanded; |
| |
| ErrorPopover.hide(); |
| }; |
| |
| $scope.discoveries = [ |
| {value: 'Vm', label: 'Static IPs'}, |
| {value: 'Multicast', label: 'Multicast'}, |
| {value: 'S3', label: 'AWS S3'}, |
| {value: 'Cloud', label: 'Apache jclouds'}, |
| {value: 'GoogleStorage', label: 'Google cloud storage'}, |
| {value: 'Jdbc', label: 'JDBC'}, |
| {value: 'SharedFs', label: 'Shared filesystem'}, |
| {value: 'ZooKeeper', label: 'Apache ZooKeeper'}, |
| {value: 'Kubernetes', label: 'Kubernetes'} |
| ]; |
| |
| $scope.swapSpaceSpis = [ |
| {value: 'FileSwapSpaceSpi', label: 'File-based swap'}, |
| {value: null, label: 'Not set'} |
| ]; |
| |
| $scope.clusters = []; |
| |
| function _clusterLbl(cluster) { |
| return cluster.name + ', ' + _.find($scope.discoveries, {value: cluster.discovery.kind}).label; |
| } |
| |
| function selectFirstItem() { |
| if ($scope.clusters.length > 0) |
| $scope.selectItem($scope.clusters[0]); |
| } |
| |
| Loading.start('loadingClustersScreen'); |
| |
| // When landing on the page, get clusters and show them. |
| Resource.read() |
| .then(({spaces, clusters, caches, domains, igfss}) => { |
| $scope.spaces = spaces; |
| |
| $scope.clusters = clusters; |
| |
| $scope.caches = _.map(caches, (cache) => { |
| cache.domains = _.filter(domains, ({_id}) => _.includes(cache.domains, _id)); |
| |
| if (_.get(cache, 'nodeFilter.kind') === 'IGFS') |
| cache.nodeFilter.IGFS.instance = _.find(igfss, {_id: cache.nodeFilter.IGFS.igfs}); |
| |
| return {value: cache._id, label: cache.name, cache}; |
| }); |
| |
| $scope.igfss = _.map(igfss, (igfs) => ({value: igfs._id, label: igfs.name, igfs})); |
| |
| _.forEach($scope.clusters, (cluster) => { |
| cluster.label = _clusterLbl(cluster); |
| |
| if (!cluster.collision || !cluster.collision.kind) |
| cluster.collision = {kind: 'Noop', JobStealing: {stealingEnabled: true}, PriorityQueue: {starvationPreventionEnabled: true}}; |
| |
| if (!cluster.failoverSpi) |
| cluster.failoverSpi = []; |
| |
| if (!cluster.logger) |
| cluster.logger = {Log4j: { mode: 'Default'}}; |
| |
| if (!cluster.peerClassLoadingLocalClassPathExclude) |
| cluster.peerClassLoadingLocalClassPathExclude = []; |
| |
| if (!cluster.deploymentSpi) { |
| cluster.deploymentSpi = {URI: { |
| uriList: [], |
| scanners: [] |
| }}; |
| } |
| |
| if (!cluster.memoryConfiguration) |
| cluster.memoryConfiguration = { memoryPolicies: [] }; |
| |
| if (!cluster.hadoopConfiguration) |
| cluster.hadoopConfiguration = { nativeLibraryNames: [] }; |
| |
| if (!cluster.serviceConfigurations) |
| cluster.serviceConfigurations = []; |
| |
| if (!cluster.executorConfiguration) |
| cluster.executorConfiguration = []; |
| }); |
| |
| if ($state.params.linkId) |
| $scope.createItem($state.params.linkId); |
| else { |
| const lastSelectedCluster = angular.fromJson(sessionStorage.lastSelectedCluster); |
| |
| if (lastSelectedCluster) { |
| const idx = _.findIndex($scope.clusters, (cluster) => cluster._id === lastSelectedCluster); |
| |
| if (idx >= 0) |
| $scope.selectItem($scope.clusters[idx]); |
| else { |
| sessionStorage.removeItem('lastSelectedCluster'); |
| |
| selectFirstItem(); |
| } |
| } |
| else |
| selectFirstItem(); |
| } |
| |
| $scope.$watch('ui.inputForm.$valid', function(valid) { |
| if (valid && ModelNormalizer.isEqual(__original_value, $scope.backupItem)) |
| $scope.ui.inputForm.$dirty = false; |
| }); |
| |
| $scope.$watch('backupItem', function(val) { |
| if (!$scope.ui.inputForm) |
| return; |
| |
| const form = $scope.ui.inputForm; |
| |
| if (form.$valid && ModelNormalizer.isEqual(__original_value, val)) |
| form.$setPristine(); |
| else |
| form.$setDirty(); |
| |
| $scope.clusterCaches = _.filter($scope.caches, |
| (cache) => _.find($scope.backupItem.caches, |
| (selCache) => selCache === cache.value |
| ) |
| ); |
| |
| $scope.clusterCachesEmpty = _.clone($scope.clusterCaches); |
| $scope.clusterCachesEmpty.push({label: 'Not set'}); |
| }, true); |
| |
| $scope.$watch('ui.activePanels.length', () => { |
| ErrorPopover.hide(); |
| }); |
| |
| if ($root.IgniteDemoMode && sessionStorage.showDemoInfo !== 'true') { |
| sessionStorage.showDemoInfo = 'true'; |
| |
| DemoInfo.show(); |
| } |
| }) |
| .catch(Messages.showError) |
| .then(() => { |
| $scope.ui.ready = true; |
| $scope.ui.inputForm && $scope.ui.inputForm.$setPristine(); |
| |
| Loading.finish('loadingClustersScreen'); |
| }); |
| |
| $scope.clusterCaches = []; |
| $scope.clusterCachesEmpty = []; |
| |
| $scope.selectItem = function(item, backup) { |
| function selectItem() { |
| $scope.selectedItem = item; |
| |
| try { |
| if (item && item._id) |
| sessionStorage.lastSelectedCluster = angular.toJson(item._id); |
| else |
| sessionStorage.removeItem('lastSelectedCluster'); |
| } |
| catch (ignored) { |
| // No-op. |
| } |
| |
| if (backup) |
| $scope.backupItem = backup; |
| else if (item) |
| $scope.backupItem = angular.copy(item); |
| else |
| $scope.backupItem = emptyCluster; |
| |
| $scope.backupItem = _.merge({}, blank, $scope.backupItem); |
| |
| if ($scope.ui.inputForm) { |
| $scope.ui.inputForm.$error = {}; |
| $scope.ui.inputForm.$setPristine(); |
| } |
| |
| __original_value = ModelNormalizer.normalize($scope.backupItem); |
| |
| filterModel(); |
| |
| if (LegacyUtils.getQueryVariable('new')) |
| $state.go('base.configuration.clusters'); |
| } |
| |
| FormUtils.confirmUnsavedChanges($scope.backupItem && $scope.ui.inputForm && $scope.ui.inputForm.$dirty, selectItem); |
| }; |
| |
| $scope.linkId = () => $scope.backupItem._id ? $scope.backupItem._id : 'create'; |
| |
| function prepareNewItem(linkId) { |
| return _.merge({}, blank, { |
| space: $scope.spaces[0]._id, |
| discovery: { |
| kind: 'Multicast', |
| Vm: {addresses: ['127.0.0.1:47500..47510']}, |
| Multicast: {addresses: ['127.0.0.1:47500..47510']}, |
| Jdbc: {initSchema: true} |
| }, |
| binaryConfiguration: {typeConfigurations: [], compactFooter: true}, |
| communication: {tcpNoDelay: true}, |
| connector: {noDelay: true}, |
| collision: {kind: 'Noop', JobStealing: {stealingEnabled: true}, PriorityQueue: {starvationPreventionEnabled: true}}, |
| failoverSpi: [], |
| logger: {Log4j: { mode: 'Default'}}, |
| caches: linkId && _.find($scope.caches, {value: linkId}) ? [linkId] : [], |
| igfss: linkId && _.find($scope.igfss, {value: linkId}) ? [linkId] : [] |
| }); |
| } |
| |
| // Add new cluster. |
| $scope.createItem = function(linkId) { |
| $timeout(() => FormUtils.ensureActivePanel($scope.ui, 'general', 'clusterNameInput')); |
| |
| $scope.selectItem(null, prepareNewItem(linkId)); |
| }; |
| |
| $scope.indexOfCache = function(cacheId) { |
| return _.findIndex($scope.caches, (cache) => cache.value === cacheId); |
| }; |
| |
| function clusterCaches(item) { |
| return _.filter(_.map($scope.caches, (scopeCache) => scopeCache.cache), |
| (cache) => _.includes(item.caches, cache._id)); |
| } |
| |
| const _objToString = (type, name, prefix = '') => { |
| if (type === 'checkpoint') |
| return prefix + ' checkpoint configuration'; |
| if (type === 'cluster') |
| return prefix + ' discovery IP finder'; |
| |
| return `${prefix} ${type} "${name}"`; |
| }; |
| |
| function checkCacheDatasources(item) { |
| const caches = clusterCaches(item); |
| |
| const checkRes = LegacyUtils.checkDataSources(item, caches); |
| |
| if (!checkRes.checked) { |
| let ids; |
| |
| if (checkRes.secondType === 'cluster') |
| ids = { section: 'general', fieldId: 'dialectInput' }; |
| else if (checkRes.secondType === 'cache') |
| ids = { section: 'general', fieldId: 'cachesInput' }; |
| else if (checkRes.secondType === 'checkpoint') |
| ids = { section: 'checkpoint', fieldId: `checkpointJdbcDialect${checkRes.index}Input` }; |
| else |
| return true; |
| |
| if (checkRes.firstType === checkRes.secondType && checkRes.firstType === 'cache') { |
| return ErrorPopover.show(ids.fieldId, 'Found caches "' + checkRes.firstObj.name + '" and "' + checkRes.secondObj.name + '" with the same data source bean name "' + |
| checkRes.firstDs.dataSourceBean + '" and different database: "' + |
| LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDs.dialect) + '" in ' + _objToString(checkRes.secondType, checkRes.secondObj.name) + ' and "' + |
| LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDs.dialect) + '" in ' + _objToString(checkRes.firstType, checkRes.firstObj.name), |
| $scope.ui, ids.section, 10000); |
| } |
| |
| return ErrorPopover.show(ids.fieldId, 'Found ' + _objToString(checkRes.firstType, checkRes.firstObj.name) + ' with the same data source bean name "' + |
| checkRes.firstDs.dataSourceBean + '" and different database: "' + |
| LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDs.dialect) + '" in ' + _objToString(checkRes.secondType, checkRes.secondObj.name, 'current') + ' and "' + |
| LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDs.dialect) + '" in ' + _objToString(checkRes.firstType, checkRes.firstObj.name), |
| $scope.ui, ids.section, 10000); |
| } |
| |
| return true; |
| } |
| |
| function checkCacheSQLSchemas(item) { |
| const caches = clusterCaches(item); |
| |
| const checkRes = LegacyUtils.checkCacheSQLSchemas(caches); |
| |
| if (!checkRes.checked) { |
| return ErrorPopover.show('cachesInput', |
| 'Found caches "' + checkRes.firstCache.name + '" and "' + checkRes.secondCache.name + '" ' + |
| 'with the same SQL schema name "' + checkRes.firstCache.sqlSchema + '"', |
| $scope.ui, 'general', 10000); |
| } |
| |
| return true; |
| } |
| |
| function checkBinaryConfiguration(item) { |
| const b = item.binaryConfiguration; |
| |
| if (LegacyUtils.isDefined(b)) { |
| if (!_.isEmpty(b.typeConfigurations)) { |
| for (let typeIx = 0; typeIx < b.typeConfigurations.length; typeIx++) { |
| const type = b.typeConfigurations[typeIx]; |
| |
| if (LegacyUtils.isEmptyString(type.typeName)) |
| return ErrorPopover.show('typeName' + typeIx + 'Input', 'Type name should be specified!', $scope.ui, 'binary'); |
| |
| if (_.find(b.typeConfigurations, (t, ix) => ix < typeIx && t.typeName === type.typeName)) |
| return ErrorPopover.show('typeName' + typeIx + 'Input', 'Type with such name is already specified!', $scope.ui, 'binary'); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| function checkCacheKeyConfiguration(item) { |
| const cfgs = item.cacheKeyConfiguration; |
| |
| if (_.isEmpty(cfgs)) |
| return true; |
| |
| for (let typeIx = 0; typeIx < cfgs.length; typeIx++) { |
| const type = cfgs[typeIx]; |
| |
| if (LegacyUtils.isEmptyString(type.typeName)) |
| return ErrorPopover.show('cacheKeyTypeName' + typeIx + 'Input', 'Cache type configuration name should be specified!', $scope.ui, 'cacheKeyCfg'); |
| |
| if (_.find(cfgs, (t, ix) => ix < typeIx && t.typeName === type.typeName)) |
| return ErrorPopover.show('cacheKeyTypeName' + typeIx + 'Input', 'Cache type configuration with such name is already specified!', $scope.ui, 'cacheKeyCfg'); |
| } |
| |
| return true; |
| } |
| |
| function checkCheckpointSpis(item) { |
| const cfgs = item.checkpointSpi; |
| |
| if (_.isEmpty(cfgs)) |
| return true; |
| |
| return _.isNil(_.find(cfgs, (cfg, ix) => { |
| if (_.isNil(cfg.kind)) { |
| ErrorPopover.show('checkpointKind' + ix, 'Choose checkpoint implementation variant', $scope.ui, 'checkpoint'); |
| |
| return true; |
| } |
| |
| switch (cfg.kind) { |
| case 'Cache': |
| const cache = _.get(cfg, 'Cache.cache'); |
| |
| if (_.isNil(cache) || !_.find($scope.backupItem.caches, (selCache) => cache === selCache)) { |
| ErrorPopover.show('checkpointCacheCache' + ix, 'Choose cache from configured cluster caches', $scope.ui, 'checkpoint'); |
| |
| return true; |
| } |
| |
| break; |
| |
| default: break; |
| } |
| |
| return false; |
| })); |
| } |
| |
| function checkCommunicationConfiguration(item) { |
| const c = item.communication; |
| |
| if (LegacyUtils.isDefined(c)) { |
| if (LegacyUtils.isDefined(c.unacknowledgedMessagesBufferSize)) { |
| if (LegacyUtils.isDefined(c.messageQueueLimit) && c.unacknowledgedMessagesBufferSize < 5 * c.messageQueueLimit) |
| return ErrorPopover.show('unacknowledgedMessagesBufferSizeInput', 'Maximum number of stored unacknowledged messages should be at least 5 * message queue limit!', $scope.ui, 'communication'); |
| |
| if (LegacyUtils.isDefined(c.ackSendThreshold) && c.unacknowledgedMessagesBufferSize < 5 * c.ackSendThreshold) |
| return ErrorPopover.show('unacknowledgedMessagesBufferSizeInput', 'Maximum number of stored unacknowledged messages should be at least 5 * ack send threshold!', $scope.ui, 'communication'); |
| } |
| |
| if (c.sharedMemoryPort === 0) |
| return ErrorPopover.show('sharedMemoryPortInput', 'Shared memory port should be more than "0" or equals to "-1"!', $scope.ui, 'communication'); |
| } |
| |
| return true; |
| } |
| |
| function checkDiscoveryConfiguration(item) { |
| const d = item.discovery; |
| |
| if (d) { |
| if ((_.isNil(d.maxAckTimeout) ? 600000 : d.maxAckTimeout) < (d.ackTimeout || 5000)) |
| return ErrorPopover.show('ackTimeoutInput', 'Acknowledgement timeout should be less than max acknowledgement timeout!', $scope.ui, 'discovery'); |
| |
| if (d.kind === 'Vm' && d.Vm && d.Vm.addresses.length === 0) |
| return ErrorPopover.show('addresses', 'Addresses are not specified!', $scope.ui, 'general'); |
| } |
| |
| return true; |
| } |
| |
| function checkLoadBalancingConfiguration(item) { |
| const balancingSpis = item.loadBalancingSpi; |
| |
| return _.isNil(_.find(balancingSpis, (curSpi, curIx) => { |
| if (_.find(balancingSpis, (spi, ix) => curIx > ix && curSpi.kind === spi.kind)) { |
| ErrorPopover.show('loadBalancingKind' + curIx, 'Load balancing SPI of that type is already configured', $scope.ui, 'loadBalancing'); |
| |
| return true; |
| } |
| |
| return false; |
| })); |
| } |
| |
| function checkMemoryConfiguration(item) { |
| const memory = item.memoryConfiguration; |
| |
| if ((memory.systemCacheMaxSize || 104857600) < (memory.systemCacheInitialSize || 41943040)) |
| return ErrorPopover.show('systemCacheMaxSize', 'System cache maximum size should be greater than initial size', $scope.ui, 'memoryConfiguration'); |
| |
| const pageSize = memory.pageSize; |
| |
| if (pageSize > 0 && (pageSize & (pageSize - 1) !== 0)) { |
| ErrorPopover.show('MemoryConfigurationPageSize', 'Page size must be power of 2', $scope.ui, 'memoryConfiguration'); |
| |
| return false; |
| } |
| |
| const dfltPlc = memory.defaultMemoryPolicyName; |
| |
| if (!_.isEmpty(dfltPlc) && !_.find(memory.memoryPolicies, (plc) => plc.name === dfltPlc)) |
| return ErrorPopover.show('defaultMemoryPolicyName', 'Memory policy with that name should be configured', $scope.ui, 'memoryConfiguration'); |
| |
| return _.isNil(_.find(memory.memoryPolicies, (curPlc, curIx) => { |
| if (_.find(memory.memoryPolicies, (plc, ix) => curIx > ix && (curPlc.name || 'default') === (plc.name || 'default'))) { |
| ErrorPopover.show('MemoryPolicyName' + curIx, 'Memory policy with that name is already configured', $scope.ui, 'memoryConfiguration'); |
| |
| return true; |
| } |
| |
| return false; |
| })); |
| } |
| |
| function checkODBC(item) { |
| if (_.get(item, 'odbc.odbcEnabled') && _.get(item, 'marshaller.kind')) |
| return ErrorPopover.show('odbcEnabledInput', 'ODBC can only be used with BinaryMarshaller', $scope.ui, 'odbcConfiguration'); |
| |
| return true; |
| } |
| |
| function checkSwapConfiguration(item) { |
| const swapKind = item.swapSpaceSpi && item.swapSpaceSpi.kind; |
| |
| if (swapKind && item.swapSpaceSpi[swapKind]) { |
| const swap = item.swapSpaceSpi[swapKind]; |
| |
| const sparsity = swap.maximumSparsity; |
| |
| if (LegacyUtils.isDefined(sparsity) && (sparsity < 0 || sparsity >= 1)) |
| return ErrorPopover.show('maximumSparsityInput', 'Maximum sparsity should be more or equal 0 and less than 1!', $scope.ui, 'swap'); |
| |
| const readStripesNumber = swap.readStripesNumber; |
| |
| if (readStripesNumber && !(readStripesNumber === -1 || (readStripesNumber & (readStripesNumber - 1)) === 0)) |
| return ErrorPopover.show('readStripesNumberInput', 'Read stripe size must be positive and power of two!', $scope.ui, 'swap'); |
| } |
| |
| return true; |
| } |
| |
| function checkServiceConfiguration(item) { |
| return _.isNil(_.find(_.get(item, 'serviceConfigurations'), (curSrv, curIx) => { |
| if (_.find(item.serviceConfigurations, (srv, ix) => curIx > ix && curSrv.name === srv.name)) { |
| ErrorPopover.show('ServiceName' + curIx, 'Service configuration with that name is already configured', $scope.ui, 'serviceConfiguration'); |
| |
| return true; |
| } |
| |
| return false; |
| })); |
| } |
| |
| function checkSslConfiguration(item) { |
| const r = item.connector; |
| |
| if (LegacyUtils.isDefined(r)) { |
| if (r.sslEnabled && LegacyUtils.isEmptyString(r.sslFactory)) |
| return ErrorPopover.show('connectorSslFactoryInput', 'SSL factory should not be empty!', $scope.ui, 'connector'); |
| } |
| |
| if (item.sslEnabled) { |
| if (!LegacyUtils.isDefined(item.sslContextFactory) || LegacyUtils.isEmptyString(item.sslContextFactory.keyStoreFilePath)) |
| return ErrorPopover.show('keyStoreFilePathInput', 'Key store file should not be empty!', $scope.ui, 'sslConfiguration'); |
| |
| if (LegacyUtils.isEmptyString(item.sslContextFactory.trustStoreFilePath) && _.isEmpty(item.sslContextFactory.trustManagers)) |
| return ErrorPopover.show('sslConfiguration-title', 'Trust storage file or managers should be configured!', $scope.ui, 'sslConfiguration'); |
| } |
| |
| return true; |
| } |
| |
| function checkPoolSizes(item) { |
| if (item.rebalanceThreadPoolSize && item.systemThreadPoolSize && item.systemThreadPoolSize <= item.rebalanceThreadPoolSize) |
| return ErrorPopover.show('rebalanceThreadPoolSizeInput', 'Rebalance thread pool size exceed or equals System thread pool size!', $scope.ui, 'pools'); |
| |
| return _.isNil(_.find(_.get(item, 'executorConfiguration'), (curExec, curIx) => { |
| if (_.find(item.executorConfiguration, (srv, ix) => curIx > ix && curExec.name === srv.name)) { |
| ErrorPopover.show('ExecutorName' + curIx, 'Executor configuration with that name is already configured', $scope.ui, 'pools'); |
| |
| return true; |
| } |
| |
| return false; |
| })); |
| } |
| |
| // Check cluster logical consistency. |
| function validate(item) { |
| ErrorPopover.hide(); |
| |
| if (LegacyUtils.isEmptyString(item.name)) |
| return ErrorPopover.show('clusterNameInput', 'Cluster name should not be empty!', $scope.ui, 'general'); |
| |
| if (!LegacyUtils.checkFieldValidators($scope.ui)) |
| return false; |
| |
| if (!checkCacheSQLSchemas(item)) |
| return false; |
| |
| if (!checkCacheDatasources(item)) |
| return false; |
| |
| if (!checkBinaryConfiguration(item)) |
| return false; |
| |
| if (!checkCacheKeyConfiguration(item)) |
| return false; |
| |
| if (!checkCheckpointSpis(item)) |
| return false; |
| |
| if (!checkCommunicationConfiguration(item)) |
| return false; |
| |
| if (!checkDiscoveryConfiguration(item)) |
| return false; |
| |
| if (!checkLoadBalancingConfiguration(item)) |
| return false; |
| |
| if (!checkMemoryConfiguration(item)) |
| return false; |
| |
| if (!checkODBC(item)) |
| return false; |
| |
| if (!checkSwapConfiguration(item)) |
| return false; |
| |
| if (!checkServiceConfiguration(item)) |
| return false; |
| |
| if (!checkSslConfiguration(item)) |
| return false; |
| |
| if (!checkPoolSizes(item)) |
| return false; |
| |
| return true; |
| } |
| |
| // Save cluster in database. |
| function save(item) { |
| $http.post('/api/v1/configuration/clusters/save', item) |
| .then(({data}) => { |
| const _id = data; |
| |
| item.label = _clusterLbl(item); |
| |
| $scope.ui.inputForm.$setPristine(); |
| |
| const idx = _.findIndex($scope.clusters, {_id}); |
| |
| if (idx >= 0) |
| _.assign($scope.clusters[idx], item); |
| else { |
| item._id = _id; |
| |
| $scope.clusters.push(item); |
| } |
| |
| _.forEach($scope.caches, (cache) => { |
| if (_.includes(item.caches, cache.value)) |
| cache.cache.clusters = _.union(cache.cache.clusters, [_id]); |
| else |
| _.pull(cache.cache.clusters, _id); |
| }); |
| |
| _.forEach($scope.igfss, (igfs) => { |
| if (_.includes(item.igfss, igfs.value)) |
| igfs.igfs.clusters = _.union(igfs.igfs.clusters, [_id]); |
| else |
| _.pull(igfs.igfs.clusters, _id); |
| }); |
| |
| $scope.selectItem(item); |
| |
| Messages.showInfo(`Cluster "${item.name}" saved.`); |
| }) |
| .catch(Messages.showError); |
| } |
| |
| // Save cluster. |
| $scope.saveItem = function() { |
| const item = $scope.backupItem; |
| |
| const swapConfigured = item.swapSpaceSpi && item.swapSpaceSpi.kind; |
| |
| if (!swapConfigured && _.find(clusterCaches(item), (cache) => cache.swapEnabled)) |
| _.merge(item, {swapSpaceSpi: {kind: 'FileSwapSpaceSpi'}}); |
| |
| if (validate(item)) |
| save(item); |
| }; |
| |
| function _clusterNames() { |
| return _.map($scope.clusters, (cluster) => cluster.name); |
| } |
| |
| // Clone cluster with new name. |
| $scope.cloneItem = function() { |
| if (validate($scope.backupItem)) { |
| Input.clone($scope.backupItem.name, _clusterNames()).then((newName) => { |
| const item = angular.copy($scope.backupItem); |
| |
| delete item._id; |
| item.name = newName; |
| |
| save(item); |
| }); |
| } |
| }; |
| |
| // Remove cluster from db. |
| $scope.removeItem = function() { |
| const selectedItem = $scope.selectedItem; |
| |
| Confirm.confirm('Are you sure you want to remove cluster: "' + selectedItem.name + '"?') |
| .then(function() { |
| const _id = selectedItem._id; |
| |
| $http.post('/api/v1/configuration/clusters/remove', {_id}) |
| .then(() => { |
| Messages.showInfo('Cluster has been removed: ' + selectedItem.name); |
| |
| const clusters = $scope.clusters; |
| |
| const idx = _.findIndex(clusters, (cluster) => cluster._id === _id); |
| |
| if (idx >= 0) { |
| clusters.splice(idx, 1); |
| |
| $scope.ui.inputForm.$setPristine(); |
| |
| if (clusters.length > 0) |
| $scope.selectItem(clusters[0]); |
| else |
| $scope.backupItem = emptyCluster; |
| |
| _.forEach($scope.caches, (cache) => _.remove(cache.cache.clusters, (id) => id === _id)); |
| _.forEach($scope.igfss, (igfs) => _.remove(igfs.igfs.clusters, (id) => id === _id)); |
| } |
| }) |
| .catch(Messages.showError); |
| }); |
| }; |
| |
| // Remove all clusters from db. |
| $scope.removeAllItems = function() { |
| Confirm.confirm('Are you sure you want to remove all clusters?') |
| .then(function() { |
| $http.post('/api/v1/configuration/clusters/remove/all') |
| .then(() => { |
| Messages.showInfo('All clusters have been removed'); |
| |
| $scope.clusters = []; |
| |
| _.forEach($scope.caches, (cache) => cache.cache.clusters = []); |
| _.forEach($scope.igfss, (igfs) => igfs.igfs.clusters = []); |
| |
| $scope.backupItem = emptyCluster; |
| $scope.ui.inputForm.$error = {}; |
| $scope.ui.inputForm.$setPristine(); |
| }) |
| .catch(Messages.showError); |
| }); |
| }; |
| |
| $scope.resetAll = function() { |
| Confirm.confirm('Are you sure you want to undo all changes for current cluster?') |
| .then(function() { |
| $scope.backupItem = $scope.selectedItem ? angular.copy($scope.selectedItem) : prepareNewItem(); |
| $scope.ui.inputForm.$error = {}; |
| $scope.ui.inputForm.$setPristine(); |
| }); |
| }; |
| } |
| ]; |