| /* |
| * 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; |
| } |
| } |