/* * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial agreement. */ package io.crate.udc.ping; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import io.crate.ClusterIdService; import io.crate.Version; import io.crate.monitor.ExtendedNodeInfo; import io.crate.settings.SharedSettings; import org.apache.logging.log4j.Logger; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentFactory; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; public class PingTask extends TimerTask { private static final TimeValue HTTP_TIMEOUT = new TimeValue(5, TimeUnit.SECONDS); private static final Logger logger = Loggers.getLogger(PingTask.class); private final ClusterService clusterService; private final ClusterIdService clusterIdService; private final ExtendedNodeInfo extendedNodeInfo; private final String pingUrl; private final Settings settings; private String licenseIdent; private AtomicLong successCounter = new AtomicLong(0); private AtomicLong failCounter = new AtomicLong(0); public PingTask(ClusterService clusterService, ClusterIdService clusterIdService, ExtendedNodeInfo extendedNodeInfo, String pingUrl, ClusterSettings clusterSettings, Settings settings) { this.clusterService = clusterService; this.clusterIdService = clusterIdService; this.extendedNodeInfo = extendedNodeInfo; this.pingUrl = pingUrl; this.settings = settings; this.licenseIdent = SharedSettings.LICENSE_IDENT_SETTING.setting().get(settings); clusterSettings.addSettingsUpdateConsumer(SharedSettings.LICENSE_IDENT_SETTING.setting(), this::setLicenseIdent); } @SuppressWarnings("unchecked") private Map<String, Object> getKernelData() { return extendedNodeInfo.osInfo().kernelData(); } @Nullable private String getClusterId() { // wait until clusterId is available (master has been elected) try { return clusterIdService.clusterId().get().value().toString(); } catch (InterruptedException | ExecutionException e) { if (logger.isTraceEnabled()) { logger.trace("Error getting cluster id", e); } return null; } } private Boolean isMasterNode() { return clusterService.localNode().isMasterNode(); } @VisibleForTesting String isEnterprise() { return SharedSettings.ENTERPRISE_LICENSE_SETTING.setting().getRaw(settings); } @VisibleForTesting String getLicenseIdent() { return licenseIdent; } private void setLicenseIdent(String licenseIdent) { this.licenseIdent = licenseIdent; } private Map<String, Object> getCounters() { return new HashMap<String, Object>() {{ put("success", successCounter.get()); put("failure", failCounter.get()); }}; } @Nullable @VisibleForTesting String getHardwareAddress() { String macAddress = extendedNodeInfo.networkInfo().primaryInterface().macAddress(); return (macAddress == null || macAddress.equals("")) ? null : macAddress.toLowerCase(Locale.ENGLISH); } private String getCrateVersion() { return Version.CURRENT.number(); } private String getJavaVersion() { return System.getProperty("java.version"); } private URL buildPingUrl() throws URISyntaxException, IOException, NoSuchAlgorithmException { URI uri = new URI(this.pingUrl); Map<String, String> queryMap = new HashMap<>(); queryMap.put("cluster_id", getClusterId()); // block until clusterId is available queryMap.put("kernel", XContentFactory.jsonBuilder().map(getKernelData()).string()); queryMap.put("master", isMasterNode().toString()); queryMap.put("enterprise", isEnterprise()); queryMap.put("ping_count", XContentFactory.jsonBuilder().map(getCounters()).string()); queryMap.put("hardware_address", getHardwareAddress()); queryMap.put("crate_version", getCrateVersion()); queryMap.put("java_version", getJavaVersion()); queryMap.put("license_ident", getLicenseIdent()); if (logger.isDebugEnabled()) { logger.debug("Sending data: {}", queryMap); } final Joiner joiner = Joiner.on('='); List<String> params = new ArrayList<>(queryMap.size()); for (Map.Entry<String, String> entry : queryMap.entrySet()) { if (entry.getValue() != null) { params.add(joiner.join(entry.getKey(), entry.getValue())); } } String query = Joiner.on('&').join(params); return new URI( uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), query, uri.getFragment() ).toURL(); } @Override public void run() { try { URL url = buildPingUrl(); if (logger.isDebugEnabled()) { logger.debug("Sending UDC information to {}...", url); } HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout((int) HTTP_TIMEOUT.millis()); conn.setReadTimeout((int) HTTP_TIMEOUT.millis()); if (conn.getResponseCode() >= 300) { throw new Exception(String.format(Locale.ENGLISH, "%s Responded with Code %d", url.getHost(), conn.getResponseCode())); } if (logger.isDebugEnabled()) { BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); String line = reader.readLine(); while (line != null) { logger.debug(line); line = reader.readLine(); } reader.close(); } else { conn.getInputStream().close(); } successCounter.incrementAndGet(); } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug("Error sending UDC information", e); } failCounter.incrementAndGet(); } } }