Package com.cloudera.flume.master

Source Code of com.cloudera.flume.master.ConfigManager

/**
* Licensed to Cloudera, Inc. under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  Cloudera, Inc. 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.
*/
package com.cloudera.flume.master;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cloudera.flume.conf.Context;
import com.cloudera.flume.conf.FlumeBuilder;
import com.cloudera.flume.conf.FlumeConfigData;
import com.cloudera.flume.conf.FlumeConfiguration;
import com.cloudera.flume.conf.FlumeSpecException;
import com.cloudera.flume.conf.FlumeSpecGen;
import com.cloudera.flume.conf.LogicalNodeContext;
import com.cloudera.flume.reporter.ReportEvent;
import com.cloudera.flume.reporter.ReportUtil;
import com.cloudera.flume.reporter.Reportable;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;

/**
* This maintains the global configuration state of the flume nodes.
*/
public class ConfigManager implements ConfigurationManager {
  static final Logger LOG = LoggerFactory.getLogger(ConfigManager.class);
  ConfigStore cfgStore;
  Map<String, String> logicalToPhysical = new HashMap<String, String>();

  public ConfigManager(ConfigStore store) {
    cfgStore = store;
  }

  /**
   * Used for testing - defaults to a MemoryBackedConfigStore
   */
  public ConfigManager() {
    cfgStore = new MemoryBackedConfigStore();
  }

  synchronized public FlumeConfigData getConfig(String host) {
    return cfgStore.getConfig(host);
  }

  @Override
  public String getName() {
    return "configuration manager";
  }

  /**
   * This sets a specified configuration. Only valid source and sinks are
   * allowed. An exception is thrown if any piece is unparsable, or fails to
   * instantiate.
   */
  synchronized public void setConfig(String logicalNode, String flowid,
      String source, String sink) throws IOException {

    try {
      // make sure the sink specified is parsable and instantiable.

      // TODO the first arg should be physical node name
      Context ctx = new LogicalNodeContext(logicalNode, logicalNode);
      FlumeBuilder.buildSink(ctx, sink);
      FlumeBuilder.buildSource(ctx, source);
    } catch (Exception e) {
      throw new IllegalArgumentException(
          "Attempted to write an invalid sink/source: " + e.getMessage(), e);
    }

    cfgStore.setConfig(logicalNode, flowid, source, sink);
  }

  /**
   * Sets many configurations in one operation. The change should be atomic. All
   * changes go in or none of them go in (and exception thrown).
   */
  synchronized public void setBulkConfig(Map<String, FlumeConfigData> configs)
      throws IOException {
    cfgStore.bulkSetConfig(configs);
  }

  /**
   * Returns a copy of the map of all configurations
   */
  synchronized public Map<String, FlumeConfigData> getAllConfigs() {
    return new HashMap<String, FlumeConfigData>(cfgStore.getConfigs());
  }

  /**
   * Returns a copy of the translations of all configurations
   */
  synchronized public Map<String, FlumeConfigData> getTranslatedConfigs() {
    // translated and non translated are the same on the StoreConfigManager
    return new HashMap<String, FlumeConfigData>(cfgStore.getConfigs());
  }

  /**
   * internal method to dump flume config data.
   */
  static void appendHtmlFlumeConfigData(StringBuilder html, String name,
      FlumeConfigData fcd) {
    html.append("\n<tr>");
    html.append("<td>" + name + "</td>");
    FlumeConfigData cfg = fcd;
    html.append("<td>" + new Date(cfg.timestamp) + "</td>");
    html.append("<td>" + cfg.flowID + "</td>");
    html.append("<td>" + cfg.sourceConfig + "</td>");
    html.append("<td>" + cfg.sinkConfig + "</td>");
    html.append("</tr>\n");
  }

  /**
   * internal method to dump logical physical mapping.
   */
  static void appendHtmlPhysicalLogicalMapping(StringBuilder html,
      String physical, Collection<String> logicals) {
    html.append("\n<tr>");
    html.append("<td>" + physical + "</td>");
    Collection<String> lns = logicals;
    html.append("<td>" + StringUtils.join(lns, ',') + "</td>");
    html.append("</tr>\n");
  }

  /**
   * Creates two tables for display in the primitive webapp.
   *
   * TODO convert to a generic report
   */
  @Override
  synchronized public ReportEvent getMetrics() {
    StringBuilder html = new StringBuilder();
    html.append("<h2>Node configuration</h2>\n<table border=\"1\"><tr>"
        + "<th>Node</th><th>Version</th><th>Flow ID</th><th>Source</th>"
        + "<th>Sink</th></tr>");
    Map<String, FlumeConfigData> cfgs = new TreeMap<String, FlumeConfigData>(
        cfgStore.getConfigs());
    synchronized (cfgs) {
      for (Entry<String, FlumeConfigData> e : cfgs.entrySet()) {
        appendHtmlFlumeConfigData(html, e.getKey(), e.getValue());
      }
    }
    html.append("</table>\n\n");

    // a table that has a mapping from physical nodes to logical nodes.
    html.append("<h2>Physical/Logical Node mapping</h2>\n<table border=\"1\">"
        + "<tr><th>physical node</th><th>logical node</th></tr>");
    Multimap<String, String> nodes = cfgStore.getLogicalNodeMap();
    synchronized (nodes) {
      for (Entry<String, Collection<String>> e : nodes.asMap().entrySet()) {
        appendHtmlPhysicalLogicalMapping(html, e.getKey(), e.getValue());
      }
    }
    html.append("</table>\n\n");

    return ReportEvent.createLegacyHtmlReport("configs", html.toString());
  }

  @Override
  public Map<String, Reportable> getSubMetrics() {
    return ReportUtil.noChildren();
  }

  /**
   * Loads configuration from a file. Synchronized to prevent races on
   * file/buffer allocate that could happen if saveConfig run concurrently.
   */
  synchronized public void loadConfigFile(String from) throws IOException {
    File f = new File(from);
    LOG.info("Loading configuration from: " + f.getAbsolutePath());
    FileInputStream r = null;
    try {
      r = new FileInputStream(f);
      long len = f.length();
      Preconditions.checkArgument(len <= Integer.MAX_VALUE);
      byte[] buf = new byte[(int) len];
      r.read(buf);
      String fullspec = new String(buf);
      List<FlumeNodeSpec> cfgs = FlumeSpecGen.generate(fullspec);
      for (FlumeNodeSpec spec : cfgs) {
        setConfig(spec.node, FlumeConfiguration.get().getDefaultFlowName(),
            spec.src, spec.sink);
      }
    } catch (FlumeSpecException e) {
      LOG.debug("Invalid Flume specification", e);
      throw new IOException(e.getMessage());
    } finally {
      if (r != null) {
        r.close();
      }
    }
  }

  /**
   * Saves all node configs to a file called s.
   *
   * synchronized to prevent race if multiple saveConfigs run concurrently. Does
   * not protect potential race caused by external concurrent FS modifications.
   */
  synchronized public void saveConfigFile(String s) throws IOException {
    File targ = new File(s); // final destination
    LOG.info("Saving configuration to: " + targ.getAbsolutePath());

    File targ2 = new File(s + "~"); // backup file

    // write here before move/overwrite.
    File tmp = File.createTempFile("current-", ".flume", targ.getParentFile());

    // This function follows file name, not the file. (e.g. the tmp name will be
    // deleted on exit, the renamed will remain.).
    tmp.deleteOnExit();
    PrintWriter out = new PrintWriter(new FileWriter(tmp));

    // write all specs to tmp.
    Map<String, FlumeConfigData> cfgs = cfgStore.getConfigs();
    for (Entry<String, FlumeConfigData> e : cfgs.entrySet()) {
      String name = e.getKey();
      String snk = e.getValue().getSinkConfig();
      String src = e.getValue().getSourceConfig();
      out.println(FlumeBuilder.toLine(name, src, snk));
    }
    out.close();

    // remove old backup, move previous to backup, and write new copy to
    // current.
    if (!targ2.delete()) {
      // targ2 may not exist, so only warn here
      LOG.warn("Unable to delete config backup file: " + targ2);
    }
    if (targ.exists()) {
      // there can be a race here..

      // TODO (jon) Java 7 has atomic move functions, move to this once it is
      // adopted.

      // move old file to ~
      if (!targ.renameTo(targ2)) {
        LOG.warn("Unable to make backup of config file: " + targ + " to "
            + targ2);
      }
    }
    if (!tmp.renameTo(targ)) {
      throw new IOException("Unable to rename " + tmp + " to " + targ);
    }
  }

  @Override
  synchronized public List<String> getLogicalNode(String physNode) {
    return cfgStore.getLogicalNodes(physNode);
  }

  /**
   * @inheritDoc
   */
  @Override
  synchronized public Map<String, Integer> getChokeMap(String physNode) {
    return cfgStore.getChokeMap(physNode);
  }

  @Override
  synchronized public boolean addLogicalNode(String physNode, String logicNode) {
    if (!logicalToPhysical.containsKey(logicNode)) {
      cfgStore.addLogicalNode(physNode, logicNode);
      logicalToPhysical.put(logicNode, physNode);

      return true;
    } else {
      LOG.warn("Logical node " + logicNode
          + " is already assigned to physical node "
          + logicalToPhysical.get(logicNode) + ". Unmap it first.");
      return false;
    }
  }

  /**
   * Removes a logical node from the logical to physical node mapping. This
   * should eventually cause a node to decommission the node.
   */
  @Override
  synchronized public void unmapLogicalNode(String physNode, String logicNode) {
    if (physNode.equals(logicNode)) {
      LOG.warn("Not allowed to unmap primary logical node from physical node");
      return; // just return here.
    }
    cfgStore.unmapLogicalNode(physNode, logicNode);
    logicalToPhysical.remove(logicNode);
  }

  @Override
  synchronized public String getPhysicalNode(String logicalNode) {
    return logicalToPhysical.get(logicalNode);
  }

  /**
   * This reads a configuration and the sets it again. This updates the version
   * stamp and forces nodes to update their configurations.
   */
  @Override
  synchronized public void refresh(String logicalNode) throws IOException {
    FlumeConfigData fcd = cfgStore.getConfig(logicalNode);
    if (fcd == null) {
      throw new IOException("Unable to refresh logicalNode " + logicalNode
          + ".  It doesn't exist!");
    }

    cfgStore.setConfig(logicalNode, fcd.getFlowID(), fcd.getSourceConfig(), fcd
        .getSinkConfig());
  }

  /**
   * This reads all configuration and the sets all of them again via a bulk
   * update. This updates the version stamp and forces nodes to update their
   * configurations.
   */
  @Override
  synchronized public void refreshAll() throws IOException {
    Map<String, FlumeConfigData> cfgs = new HashMap<String, FlumeConfigData>();
    for (Entry<String, FlumeConfigData> ent : getAllConfigs().entrySet()) {
      cfgs.put(ent.getKey(), ent.getValue());

    }
    setBulkConfig(cfgs);
  }

  /**
   * This removes the logical node data flow configuration from both the flow
   * table and the phys-logical mapping
   */
  @Override
  synchronized public void removeLogicalNode(String logicNode)
      throws IOException {
    cfgStore.removeLogicalNode(logicNode);
    String physical = getPhysicalNode(logicNode);
    if (physical != null) {
      // could be possible if node is unmapped and then later removed
      cfgStore.unmapLogicalNode(physical, logicNode);
      logicalToPhysical.remove(logicNode);
    }
  }

  @Override
  synchronized public void start() throws IOException {
    Preconditions.checkNotNull(cfgStore, "Trying to stop null cfgStore");
    try {
      try {
        cfgStore.init();
        reloadLogicalToPhysical();
      } catch (InterruptedException e) {
        // InterruptedException can be ignored in certain cases
        LOG.warn("ConfigStore was interrupted on startup, this may be ok", e);
      }
    } catch (IOException e) {
      LOG.error("ConfigStore init threw IOException", e);
      throw e;
    }
  }

  @Override
  synchronized public void stop() throws IOException {
    if (cfgStore == null) {
      LOG.warn("Trying to shutdown null cfgStore");
      return;
    }
    cfgStore.shutdown();
  }

  /**
   * Unmaps all logical nodes from physical nodes except for the default one.
   * (logical==physical)
   */
  @Override
  synchronized public void unmapAllLogicalNodes() throws IOException {
    cfgStore.unmapAllLogicalNodes();
    logicalToPhysical.clear();
    refreshAll();
  }

  @Override
  public void updateAll() throws IOException {
    // do nothing.
  }

  @Override
  public String toString() {
    return getAllConfigs().toString();
  }

  /**
   * Returns a copy of the logical node map.
   */
  @Override
  synchronized public Multimap<String, String> getLogicalNodeMap() {
    ListMultimap<String, String> map = ArrayListMultimap
        .<String, String> create(cfgStore.getLogicalNodeMap());
    return map;
  }

  synchronized void reloadLogicalToPhysical() {
    Multimap<String, String> p2n = getLogicalNodeMap();
    logicalToPhysical.clear();
    for (Entry<String, String> e : p2n.entries()) {
      if (logicalToPhysical.containsKey(e.getValue())) {
        LOG.warn("logical node mapped to two physical nodes!");
      }
      logicalToPhysical.put(e.getValue(), e.getKey());
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  synchronized public void addChokeLimit(String physNode, String chokeID,
      int limit) {
    cfgStore.addChokeLimit(physNode, chokeID, limit);
  }

}
TOP

Related Classes of com.cloudera.flume.master.ConfigManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.