modules/frontend/app/configuration/services/Clusters.ts (507 lines of code) (raw):

/* * 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. */ import get from 'lodash/get'; import find from 'lodash/find'; import {from} from 'rxjs'; import ObjectID from 'bson-objectid/objectid'; import {uniqueName} from 'app/utils/uniqueName'; import omit from 'lodash/fp/omit'; import {DiscoveryKinds, FailoverSPIs, LoadBalancingKinds, ShortCluster, ShortDomainModel} from '../types'; import {Menu} from 'app/types'; const uniqueNameValidator = (defaultName = '') => (a, items = []) => { return a && !items.some((b) => b._id !== a._id && (a.name || defaultName) === (b.name || defaultName)); }; export default class Clusters { static $inject = ['$http', 'JDBC_LINKS']; discoveries: Menu<DiscoveryKinds> = [ {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'} ]; minMemoryPolicySize = 10485760; // In bytes ackSendThreshold = { min: 1, default: 16 }; messageQueueLimit = { min: 0, default: 1024 }; unacknowledgedMessagesBufferSize = { min: ( currentValue = this.unacknowledgedMessagesBufferSize.default, messageQueueLimit = this.messageQueueLimit.default, ackSendThreshold = this.ackSendThreshold.default ) => { if (currentValue === this.unacknowledgedMessagesBufferSize.default) return currentValue; const {validRatio} = this.unacknowledgedMessagesBufferSize; return Math.max(messageQueueLimit * validRatio, ackSendThreshold * validRatio); }, validRatio: 5, default: 0 }; sharedMemoryPort = { default: 48100, min: -1, max: 65535, invalidValues: [0] }; /** * Cluster-related configuration stuff */ constructor(private $http: ng.IHttpService, private JDBC_LINKS) {} getConfiguration(clusterID: string) { return this.$http.get(`/api/v1/configuration/${clusterID}`); } getAllConfigurations() { return this.$http.get('/api/v1/configuration/list'); } getCluster(clusterID: string) { return this.$http.get(`/api/v1/configuration/clusters/${clusterID}`); } getClusterCaches(clusterID: string) { return this.$http.get(`/api/v1/configuration/clusters/${clusterID}/caches`); } getClusterModels(clusterID: string) { return this.$http.get<{data: ShortDomainModel[]}>(`/api/v1/configuration/clusters/${clusterID}/models`); } getClusterIGFSs(clusterID) { return this.$http.get(`/api/v1/configuration/clusters/${clusterID}/igfss`); } getClustersOverview() { return this.$http.get<{data: ShortCluster[]}>('/api/v1/configuration/clusters/'); } getClustersOverview$() { return from(this.getClustersOverview()); } saveCluster(cluster) { return this.$http.post('/api/v1/configuration/clusters/save', cluster); } saveCluster$(cluster) { return from(this.saveCluster(cluster)); } removeCluster(cluster) { return this.$http.post('/api/v1/configuration/clusters/remove', {_id: cluster}); } removeCluster$(cluster) { return from(this.removeCluster(cluster)); } saveBasic(changedItems) { return this.$http.put('/api/v1/configuration/clusters/basic', changedItems); } saveAdvanced(changedItems) { return this.$http.put('/api/v1/configuration/clusters/', changedItems); } getBlankCluster() { return { _id: ObjectID.generate(), activeOnStart: true, cacheSanityCheckEnabled: true, atomicConfiguration: {}, cacheKeyConfiguration: [], deploymentSpi: { URI: { uriList: [], scanners: [] } }, marshaller: {}, peerClassLoadingLocalClassPathExclude: [], sslContextFactory: { trustManagers: [] }, swapSpaceSpi: {}, transactionConfiguration: {}, dataStorageConfiguration: { pageSize: null, concurrencyLevel: null, defaultDataRegionConfiguration: { name: 'default' }, dataRegionConfigurations: [] }, memoryConfiguration: { pageSize: null, memoryPolicies: [{ name: 'default', maxSize: null }] }, hadoopConfiguration: { nativeLibraryNames: [] }, serviceConfigurations: [], executorConfiguration: [], sqlConnectorConfiguration: { tcpNoDelay: true }, clientConnectorConfiguration: { tcpNoDelay: true, jdbcEnabled: true, odbcEnabled: true, thinClientEnabled: true, useIgniteSslContextFactory: true }, space: void 0, discovery: { kind: 'Multicast', Vm: {addresses: ['127.0.0.1:47500..47510']}, Multicast: {addresses: ['127.0.0.1:47500..47510']}, Jdbc: {initSchema: true}, Cloud: {regions: [], zones: []} }, 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: [], igfss: [], models: [], checkpointSpi: [], loadBalancingSpi: [], autoActivationEnabled: true }; } failoverSpis: Menu<FailoverSPIs> = [ {value: 'JobStealing', label: 'Job stealing'}, {value: 'Never', label: 'Never'}, {value: 'Always', label: 'Always'}, {value: 'Custom', label: 'Custom'} ]; toShortCluster(cluster) { return { _id: cluster._id, name: cluster.name, discovery: cluster.discovery.kind, cachesCount: (cluster.caches || []).length, modelsCount: (cluster.models || []).length, igfsCount: (cluster.igfss || []).length }; } jdbcDriverURL(dataSrc) { return this.JDBC_LINKS[get(dataSrc, 'dialect')]; } requiresProprietaryDrivers(dataSrc) { return !!this.jdbcDriverURL(dataSrc); } dataRegion = { name: { default: 'default', invalidValues: ['sysMemPlc'] }, initialSize: { default: 268435456, min: 10485760 }, maxSize: { default: '0.2 * totalMemoryAvailable', min: (dataRegion) => { if (!dataRegion) return; return dataRegion.initialSize || this.dataRegion.initialSize.default; } }, evictionThreshold: { step: 0.001, max: 0.999, min: 0.5, default: 0.9 }, emptyPagesPoolSize: { default: 100, min: 11, max: (cluster, dataRegion) => { if (!cluster || !dataRegion || !dataRegion.maxSize) return; const perThreadLimit = 10; // Took from Ignite const maxSize = dataRegion.maxSize; const pageSize = cluster.dataStorageConfiguration.pageSize || this.dataStorageConfiguration.pageSize.default; const maxPoolSize = Math.floor(maxSize / pageSize / perThreadLimit); return maxPoolSize; } }, metricsSubIntervalCount: { default: 5, min: 1, step: 1 }, metricsRateTimeInterval: { min: 1000, default: 60000, step: 1000 } }; makeBlankDataRegionConfiguration() { return {_id: ObjectID.generate()}; } addDataRegionConfiguration(cluster) { const dataRegionConfigurations = get(cluster, 'dataStorageConfiguration.dataRegionConfigurations'); if (!dataRegionConfigurations) return; return dataRegionConfigurations.push(Object.assign(this.makeBlankDataRegionConfiguration(), { name: uniqueName('New data region', dataRegionConfigurations.concat(cluster.dataStorageConfiguration.defaultDataRegionConfiguration)) })); } memoryPolicy = { name: { default: 'default', invalidValues: ['sysMemPlc'] }, initialSize: { default: 268435456, min: 10485760 }, maxSize: { default: '0.8 * totalMemoryAvailable', min: (memoryPolicy) => { return memoryPolicy.initialSize || this.memoryPolicy.initialSize.default; } }, customValidators: { defaultMemoryPolicyExists: (name, items = []) => { const def = this.memoryPolicy.name.default; const normalizedName = (name || def); if (normalizedName === def) return true; return items.some((policy) => (policy.name || def) === normalizedName); }, uniqueMemoryPolicyName: (a, items = []) => { const def = this.memoryPolicy.name.default; return !items.some((b) => b._id !== a._id && (a.name || def) === (b.name || def)); } }, emptyPagesPoolSize: { default: 100, min: 11, max: (cluster, memoryPolicy) => { if (!memoryPolicy || !memoryPolicy.maxSize) return; const perThreadLimit = 10; // Took from Ignite const maxSize = memoryPolicy.maxSize; const pageSize = cluster.memoryConfiguration.pageSize || this.memoryConfiguration.pageSize.default; const maxPoolSize = Math.floor(maxSize / pageSize / perThreadLimit); return maxPoolSize; } } }; getDefaultClusterMemoryPolicy(cluster) { const def = this.memoryPolicy.name.default; const normalizedName = get(cluster, 'memoryConfiguration.defaultMemoryPolicyName') || def; return get(cluster, 'memoryConfiguration.memoryPolicies', []).find((p) => { return (p.name || def) === normalizedName; }); } makeBlankCheckpointSPI() { return { FS: { directoryPaths: [] }, S3: { awsCredentials: { kind: 'Basic' }, clientConfiguration: { retryPolicy: { kind: 'Default' }, useReaper: true } } }; } addCheckpointSPI(cluster) { const item = this.makeBlankCheckpointSPI(); cluster.checkpointSpi.push(item); return item; } makeBlankLoadBalancingSpi() { return { Adaptive: { loadProbe: { Job: {useAverage: true}, CPU: { useAverage: true, useProcessors: true }, ProcessingTime: {useAverage: true} } } }; } addLoadBalancingSpi(cluster) { return cluster.loadBalancingSpi.push(this.makeBlankLoadBalancingSpi()); } loadBalancingKinds: Menu<LoadBalancingKinds> = [ {value: 'RoundRobin', label: 'Round-robin'}, {value: 'Adaptive', label: 'Adaptive'}, {value: 'WeightedRandom', label: 'Random'}, {value: 'Custom', label: 'Custom'} ]; makeBlankMemoryPolicy() { return {_id: ObjectID.generate()}; } addMemoryPolicy(cluster) { const memoryPolicies = get(cluster, 'memoryConfiguration.memoryPolicies'); if (!memoryPolicies) return; return memoryPolicies.push(Object.assign(this.makeBlankMemoryPolicy(), { // Blank name for default policy if there are not other policies name: memoryPolicies.length ? uniqueName('New memory policy', memoryPolicies) : '' })); } // For versions 2.1-2.2, use dataStorageConfiguration since 2.3 memoryConfiguration = { pageSize: { default: 1024 * 2, values: [ {value: null, label: 'Default (2kb)'}, {value: 1024 * 1, label: '1 kb'}, {value: 1024 * 2, label: '2 kb'}, {value: 1024 * 4, label: '4 kb'}, {value: 1024 * 8, label: '8 kb'}, {value: 1024 * 16, label: '16 kb'} ] }, systemCacheInitialSize: { default: 41943040, min: 10485760 }, systemCacheMaxSize: { default: 104857600, min: (cluster) => { return get(cluster, 'memoryConfiguration.systemCacheInitialSize') || this.memoryConfiguration.systemCacheInitialSize.default; } } }; // Added in 2.3 dataStorageConfiguration = { pageSize: { default: 1024 * 4, values: [ {value: null, label: 'Default (4kb)'}, {value: 1024 * 1, label: '1 kb'}, {value: 1024 * 2, label: '2 kb'}, {value: 1024 * 4, label: '4 kb'}, {value: 1024 * 8, label: '8 kb'}, {value: 1024 * 16, label: '16 kb'} ] }, systemRegionInitialSize: { default: 41943040, min: 10485760 }, systemRegionMaxSize: { default: 104857600, min: (cluster) => { return get(cluster, 'dataStorageConfiguration.systemRegionInitialSize') || this.dataStorageConfiguration.systemRegionInitialSize.default; } } }; persistenceEnabled(dataStorage) { return !!(get(dataStorage, 'defaultDataRegionConfiguration.persistenceEnabled') || find(get(dataStorage, 'dataRegionConfigurations'), (storeCfg) => storeCfg.persistenceEnabled)); } swapSpaceSpi = { readStripesNumber: { default: 'availableProcessors', customValidators: { powerOfTwo: (value) => { return !value || ((value & -value) === value); } } } }; makeBlankServiceConfiguration() { return {_id: ObjectID.generate()}; } addServiceConfiguration(cluster) { if (!cluster.serviceConfigurations) cluster.serviceConfigurations = []; cluster.serviceConfigurations.push(Object.assign(this.makeBlankServiceConfiguration(), { name: uniqueName('New service configuration', cluster.serviceConfigurations) })); } serviceConfigurations = { serviceConfiguration: { name: { customValidators: { uniqueName: uniqueNameValidator('') } } } }; systemThreadPoolSize = { default: 'max(8, availableProcessors) * 2', min: 2 }; rebalanceThreadPoolSize = { default: 1, min: 1, max: (cluster) => { return cluster.systemThreadPoolSize ? cluster.systemThreadPoolSize - 1 : void 0; } }; addExecutorConfiguration(cluster) { if (!cluster.executorConfiguration) cluster.executorConfiguration = []; const item = {_id: ObjectID.generate(), name: ''}; cluster.executorConfiguration.push(item); return item; } executorConfigurations = { allNamesExist: (executorConfigurations = []) => { return executorConfigurations.every((ec) => ec && ec.name); }, allNamesUnique: (executorConfigurations = []) => { const uniqueNames = new Set(executorConfigurations.map((ec) => ec.name)); return uniqueNames.size === executorConfigurations.length; } }; executorConfiguration = { name: { customValidators: { uniqueName: uniqueNameValidator() } } }; marshaller = { kind: { default: 'BinaryMarshaller' } }; odbc = { odbcEnabled: { correctMarshaller: (cluster, odbcEnabled) => { const marshallerKind = get(cluster, 'marshaller.kind') || this.marshaller.kind.default; return !odbcEnabled || marshallerKind === this.marshaller.kind.default; }, correctMarshallerWatch: (root) => `${root}.marshaller.kind` } }; swapSpaceSpis = [ {value: 'FileSwapSpaceSpi', label: 'File-based swap'}, {value: null, label: 'Not set'} ]; affinityFunctions = [ {value: 'Rendezvous', label: 'Rendezvous'}, {value: 'Custom', label: 'Custom'}, {value: null, label: 'Default'} ]; normalize = omit(['__v', 'space']); addPeerClassLoadingLocalClassPathExclude(cluster) { if (!cluster.peerClassLoadingLocalClassPathExclude) cluster.peerClassLoadingLocalClassPathExclude = []; return cluster.peerClassLoadingLocalClassPathExclude.push(''); } addBinaryTypeConfiguration(cluster) { if (!cluster.binaryConfiguration.typeConfigurations) cluster.binaryConfiguration.typeConfigurations = []; const item = {_id: ObjectID.generate()}; cluster.binaryConfiguration.typeConfigurations.push(item); return item; } addLocalEventListener(cluster) { if (!cluster.localEventListeners) cluster.localEventListeners = []; const item = {_id: ObjectID.generate()}; cluster.localEventListeners.push(item); return item; } }