/*
* Copyright 2016 The Simple File Server Authors
*
* Licensed 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.
*/
package org.sfs.nodes;
import com.google.common.base.Optional;
import com.google.common.net.HostAndPort;
import io.vertx.core.Context;
import io.vertx.core.Handler;
import io.vertx.core.logging.Logger;
import org.sfs.Server;
import org.sfs.SfsVertx;
import org.sfs.VertxContext;
import org.sfs.elasticsearch.nodes.GetDocumentsCountForNode;
import org.sfs.filesystem.volume.VolumeManager;
import org.sfs.rx.RxHelper;
import org.sfs.rx.ToVoid;
import org.sfs.vo.TransientServiceDef;
import org.sfs.vo.TransientXFileSystem;
import rx.Observable;
import rx.Subscriber;
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.Path;
import java.util.List;
import static com.google.common.base.Optional.fromNullable;
import static com.google.common.base.Preconditions.checkState;
import static io.vertx.core.logging.LoggerFactory.getLogger;
import static java.lang.Runtime.getRuntime;
import static java.nio.file.Files.getFileStore;
import static org.sfs.rx.Defer.aVoid;
import static org.sfs.rx.Defer.just;
import static rx.Observable.from;
public class NodeStats {
private static Logger LOGGER = getLogger(NodeStats.class);
private VertxContext<Server> vertxContext;
private boolean started = false;
private TransientServiceDef transientServiceDef;
private Long timerId;
public NodeStats() {
}
public Observable<Void> open(VertxContext<Server> vertxContext) {
this.vertxContext = vertxContext;
return aVoid()
.flatMap(aVoid -> generate(vertxContext))
.doOnNext(aVoid -> startTimer())
.doOnNext(aVoid -> started = true);
}
public Observable<Void> close(VertxContext<Server> vertxContext) {
return aVoid()
.doOnNext(aVoid -> started = false)
.doOnNext(aVoid -> stopTimer())
.onErrorResumeNext(throwable -> {
LOGGER.warn("Handling Exception", throwable);
return aVoid();
})
.onErrorResumeNext(throwable -> {
LOGGER.warn("Handling Exception", throwable);
return aVoid();
})
.doOnNext(aVoid -> {
transientServiceDef = null;
});
}
public Observable<Void> forceUpdate(VertxContext<Server> vertxContext) {
return generate(vertxContext);
}
public Optional<TransientServiceDef> getStats() {
checkStarted();
return fromNullable(transientServiceDef);
}
protected void startTimer() {
Handler<Long> handler = new Handler<Long>() {
Handler<Long> _this = this;
@Override
public void handle(Long event) {
generate(vertxContext)
.subscribe(new Subscriber<Void>() {
@Override
public void onCompleted() {
long refreshInterval = vertxContext.verticle().nodes().getNodeStatsRefreshInterval();
timerId = vertxContext.vertx().setTimer(refreshInterval, _this);
}
@Override
public void onError(Throwable e) {
LOGGER.debug("Handling Exception", e);
long refreshInterval = vertxContext.verticle().nodes().getNodeStatsRefreshInterval();
timerId = vertxContext.vertx().setTimer(refreshInterval, _this);
}
@Override
public void onNext(Void aVoid1) {
}
});
}
};
long refreshInterval = vertxContext.verticle().nodes().getNodeStatsRefreshInterval();
timerId = vertxContext.vertx().setTimer(refreshInterval, handler);
}
protected void stopTimer() {
if (timerId != null) {
vertxContext.vertx().cancelTimer(timerId);
}
}
protected void checkStarted() {
checkState(started, "Not started");
}
protected Observable<Void> generate(VertxContext<Server> vertxContext) {
NodeStats _this = this;
SfsVertx sfsVertx = vertxContext.vertx();
Context context = sfsVertx.getOrCreateContext();
return aVoid()
.flatMap(aVoid -> RxHelper.executeBlocking(context, sfsVertx.getBackgroundPool(), () -> {
try {
Nodes nodes = vertxContext.verticle().nodes();
List<HostAndPort> publishAddresses = nodes.getPublishAddresses();
Path workingDirectory = vertxContext.verticle().sfsFileSystem().workingDirectory();
FileStore fileStore = getFileStore(workingDirectory);
TransientXFileSystem fileSystemInfo = new TransientXFileSystem()
.setDevice(fileStore.name())
.setPath(workingDirectory.toString())
.setTotalSpace(fileStore.getTotalSpace())
.setUnallocatedSpace(fileStore.getUnallocatedSpace())
.setUsableSpace(fileStore.getUsableSpace())
.setType(fileStore.type())
.setPartition(workingDirectory.getRoot().toString());
Runtime runtime = getRuntime();
return new TransientServiceDef(nodes.getNodeId())
.setAvailableProcessors(runtime.availableProcessors())
.setFreeMemory(runtime.freeMemory())
.setMaxMemory(runtime.maxMemory())
.setTotalMemory(runtime.totalMemory())
.setDataNode(nodes.isDataNode())
.setMaster(nodes.isMaster())
.setFileSystem(fileSystemInfo)
.setPublishAddresses(publishAddresses);
} catch (IOException e) {
throw new RuntimeException(e);
}
})).flatMap(transientServiceDef -> {
VolumeManager volumeManager = vertxContext.verticle().nodes().volumeManager();
if (volumeManager != null) {
return from(volumeManager.volumes())
.map(volumeManager::get)
.filter(Optional::isPresent)
.map(Optional::get)
.flatMap(volume -> volume.volumeInfo(vertxContext.vertx()))
.toList()
.map(transientServiceDef::setVolumes);
} else {
return Observable.just(transientServiceDef);
}
})
.flatMap(transientServiceDef -> getDocumentCount()
.doOnNext(transientServiceDef::setDocumentCount)
.map(documentCount -> transientServiceDef))
.doOnNext(transientServiceDef1 -> _this.transientServiceDef = transientServiceDef1)
.map(new ToVoid<>());
}
protected Observable<Long> getDocumentCount() {
boolean dataNode = vertxContext.verticle().nodes().isDataNode();
String nodeId = vertxContext.verticle().nodes().getNodeId();
return
dataNode ?
aVoid()
.flatMap(new GetDocumentsCountForNode(vertxContext, nodeId))
: just(0L);
}
}