<commit_before>package org.batfish.question.ipsecsessionstatus;

import static com.google.common.base.MoreObjects.firstNonNull;
import static org.batfish.datamodel.questions.IpsecSessionStatus.IKE_PHASE1_FAILED;
import static org.batfish.datamodel.questions.IpsecSessionStatus.IKE_PHASE1_KEY_MISMATCH;
import static org.batfish.datamodel.questions.IpsecSessionStatus.IPSEC_PHASE2_FAILED;
import static org.batfish.datamodel.questions.IpsecSessionStatus.IPSEC_SESSION_ESTABLISHED;
import static org.batfish.datamodel.questions.IpsecSessionStatus.MISSING_END_POINT;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multiset;
import com.google.common.graph.ValueGraph;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.batfish.common.Answerer;
import org.batfish.common.NetworkSnapshot;
import org.batfish.common.plugin.IBatfish;
import org.batfish.common.util.IpsecUtil;
import org.batfish.datamodel.Configuration;
import org.batfish.datamodel.IpsecDynamicPeerConfig;
import org.batfish.datamodel.IpsecPeerConfig;
import org.batfish.datamodel.IpsecPeerConfigId;
import org.batfish.datamodel.IpsecSession;
import org.batfish.datamodel.NetworkConfigurations;
import org.batfish.datamodel.answers.AnswerElement;
import org.batfish.datamodel.answers.Schema;
import org.batfish.datamodel.collections.NodeInterfacePair;
import org.batfish.datamodel.pojo.Node;
import org.batfish.datamodel.questions.DisplayHints;
import org.batfish.datamodel.questions.IpsecSessionStatus;
import org.batfish.datamodel.questions.Question;
import org.batfish.datamodel.table.ColumnMetadata;
import org.batfish.datamodel.table.Row;
import org.batfish.datamodel.table.Row.RowBuilder;
import org.batfish.datamodel.table.TableAnswerElement;
import org.batfish.datamodel.table.TableMetadata;
import org.batfish.specifier.AllNodesNodeSpecifier;
import org.batfish.specifier.ConstantEnumSetSpecifier;
import org.batfish.specifier.SpecifierFactories;
import org.batfish.specifier.parboiled.Grammar;

class IpsecSessionStatusAnswerer extends Answerer {
  static final String COL_INITIATOR = "Node";
  static final String COL_INIT_INTERFACE = "Node_Interface";
  static final String COL_INIT_IP = "Node_IP";
  static final String COL_RESPONDER = "Remote_Node";
  static final String COL_RESPONDER_INTERFACE = "Remote_Node_Interface";
  static final String COL_RESPONDER_IP = "Remote_Node_IP";
  static final String COL_STATUS = "Status";
  static final String COL_TUNNEL_INTERFACES = "Tunnel_Interfaces";
  private static final String NOT_APPLICABLE = "Not Applicable";

  IpsecSessionStatusAnswerer(Question question, IBatfish batfish) {
    super(question, batfish);
  }

  @Override
  public AnswerElement answer(NetworkSnapshot snapshot) {
    IpsecSessionStatusQuestion question = (IpsecSessionStatusQuestion) _question;
    Map<String, Configuration> configurations = _batfish.loadConfigurations(snapshot);
    NetworkConfigurations networkConfigurations = NetworkConfigurations.of(configurations);
    ValueGraph<IpsecPeerConfigId, IpsecSession> ipsecTopology =
        IpsecUtil.initIpsecTopology(configurations).getGraph();

    Set<String> initiatorNodes =
        SpecifierFactories.getNodeSpecifierOrDefault(
                question.getNodes(), AllNodesNodeSpecifier.INSTANCE)
            .resolve(_batfish.specifierContext(snapshot));
    Set<String> responderNodes =
        SpecifierFactories.getNodeSpecifierOrDefault(
                question.getRemoteNodes(), AllNodesNodeSpecifier.INSTANCE)
            .resolve(_batfish.specifierContext(snapshot));
    Set<IpsecSessionStatus> statuses =
        SpecifierFactories.getEnumSetSpecifierOrDefault(
                question.getStatus(),
                Grammar.IPSEC_SESSION_STATUS_SPECIFIER,
                new ConstantEnumSetSpecifier<>(ImmutableSet.copyOf(IpsecSessionStatus.values())))
            .resolve();

    TableAnswerElement answerElement = new TableAnswerElement(createTableMetaData(question));

    Multiset<IpsecSessionInfo> ipsecSessionInfos =
        rawAnswer(networkConfigurations, ipsecTopology, initiatorNodes, responderNodes);
    answerElement.postProcessAnswer(
        question,
        ipsecSessionInfos.stream()
            .filter(ipsecSessionInfo -> statuses.contains(ipsecSessionInfo.getIpsecSessionStatus()))
            .map(IpsecSessionStatusAnswerer::toRow)
            .collect(Collectors.toCollection(HashMultiset::create)));
    return answerElement;
  }

  @VisibleForTesting
  static Multiset<IpsecSessionInfo> rawAnswer(
      NetworkConfigurations networkConfigurations,
      ValueGraph<IpsecPeerConfigId, IpsecSession> ipsecTopology,
      Set<String> initiatorNodes,
      Set<String> responderNodes) {
    Multiset<IpsecSessionInfo> ipsecSessionInfos = HashMultiset.create();

    for (IpsecPeerConfigId node : ipsecTopology.nodes()) {
      IpsecPeerConfig ipsecPeerConfig = networkConfigurations.getIpsecPeerConfig(node);
      if (ipsecPeerConfig == null
          || ipsecPeerConfig instanceof IpsecDynamicPeerConfig
          || !initiatorNodes.contains(node.getHostName())) {
        continue;
      }
      IpsecSessionInfo.Builder ipsecSessionInfoBuilder = IpsecSessionInfo.builder();

      ipsecSessionInfoBuilder.setInitiatorHostname(node.getHostName());
      ipsecSessionInfoBuilder.setInitiatorInterface(ipsecPeerConfig.getSourceInterface());
      ipsecSessionInfoBuilder.setInitiatorIp(ipsecPeerConfig.getLocalAddress());
      ipsecSessionInfoBuilder.setInitiatorTunnelInterface(ipsecPeerConfig.getTunnelInterface());<commit_msg>Deterministically order ipsec sessions (#6010)<commit_after>package org.batfish.question.ipsecsessionstatus;

import static com.google.common.base.MoreObjects.firstNonNull;
import static org.batfish.datamodel.questions.IpsecSessionStatus.IKE_PHASE1_FAILED;
import static org.batfish.datamodel.questions.IpsecSessionStatus.IKE_PHASE1_KEY_MISMATCH;
import static org.batfish.datamodel.questions.IpsecSessionStatus.IPSEC_PHASE2_FAILED;
import static org.batfish.datamodel.questions.IpsecSessionStatus.IPSEC_SESSION_ESTABLISHED;
import static org.batfish.datamodel.questions.IpsecSessionStatus.MISSING_END_POINT;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multiset;
import com.google.common.graph.ValueGraph;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import org.batfish.common.Answerer;
import org.batfish.common.NetworkSnapshot;
import org.batfish.common.plugin.IBatfish;
import org.batfish.common.util.IpsecUtil;
import org.batfish.datamodel.Configuration;
import org.batfish.datamodel.IpsecDynamicPeerConfig;
import org.batfish.datamodel.IpsecPeerConfig;
import org.batfish.datamodel.IpsecPeerConfigId;
import org.batfish.datamodel.IpsecSession;
import org.batfish.datamodel.NetworkConfigurations;
import org.batfish.datamodel.answers.AnswerElement;
import org.batfish.datamodel.answers.Schema;
import org.batfish.datamodel.collections.NodeInterfacePair;
import org.batfish.datamodel.pojo.Node;
import org.batfish.datamodel.questions.DisplayHints;
import org.batfish.datamodel.questions.IpsecSessionStatus;
import org.batfish.datamodel.questions.Question;
import org.batfish.datamodel.table.ColumnMetadata;
import org.batfish.datamodel.table.Row;
import org.batfish.datamodel.table.Row.RowBuilder;
import org.batfish.datamodel.table.TableAnswerElement;
import org.batfish.datamodel.table.TableMetadata;
import org.batfish.specifier.AllNodesNodeSpecifier;
import org.batfish.specifier.ConstantEnumSetSpecifier;
import org.batfish.specifier.SpecifierFactories;
import org.batfish.specifier.parboiled.Grammar;

class IpsecSessionStatusAnswerer extends Answerer {
  static final String COL_INITIATOR = "Node";
  static final String COL_INIT_INTERFACE = "Node_Interface";
  static final String COL_INIT_IP = "Node_IP";
  static final String COL_RESPONDER = "Remote_Node";
  static final String COL_RESPONDER_INTERFACE = "Remote_Node_Interface";
  static final String COL_RESPONDER_IP = "Remote_Node_IP";
  static final String COL_STATUS = "Status";
  static final String COL_TUNNEL_INTERFACES = "Tunnel_Interfaces";
  private static final String NOT_APPLICABLE = "Not Applicable";

  IpsecSessionStatusAnswerer(Question question, IBatfish batfish) {
    super(question, batfish);
  }

  @Override
  public AnswerElement answer(NetworkSnapshot snapshot) {
    IpsecSessionStatusQuestion question = (IpsecSessionStatusQuestion) _question;
    Map<String, Configuration> configurations = _batfish.loadConfigurations(snapshot);
    NetworkConfigurations networkConfigurations = NetworkConfigurations.of(configurations);
    ValueGraph<IpsecPeerConfigId, IpsecSession> ipsecTopology =
        IpsecUtil.initIpsecTopology(configurations).getGraph();

    Set<String> initiatorNodes =
        SpecifierFactories.getNodeSpecifierOrDefault(
                question.getNodes(), AllNodesNodeSpecifier.INSTANCE)
            .resolve(_batfish.specifierContext(snapshot));
    Set<String> responderNodes =
        SpecifierFactories.getNodeSpecifierOrDefault(
                question.getRemoteNodes(), AllNodesNodeSpecifier.INSTANCE)
            .resolve(_batfish.specifierContext(snapshot));
    Set<IpsecSessionStatus> statuses =
        SpecifierFactories.getEnumSetSpecifierOrDefault(
                question.getStatus(),
                Grammar.IPSEC_SESSION_STATUS_SPECIFIER,
                new ConstantEnumSetSpecifier<>(ImmutableSet.copyOf(IpsecSessionStatus.values())))
            .resolve();

    TableAnswerElement answerElement = new TableAnswerElement(createTableMetaData(question));

    Multiset<IpsecSessionInfo> ipsecSessionInfos =
        rawAnswer(networkConfigurations, ipsecTopology, initiatorNodes, responderNodes);
    answerElement.postProcessAnswer(
        question,
        ipsecSessionInfos.stream()
            .filter(ipsecSessionInfo -> statuses.contains(ipsecSessionInfo.getIpsecSessionStatus()))
            .map(IpsecSessionStatusAnswerer::toRow)
            .sorted()
            .collect(ImmutableList.toImmutableList()));
    return answerElement;
  }

  @VisibleForTesting
  static Multiset<IpsecSessionInfo> rawAnswer(
      NetworkConfigurations networkConfigurations,
      ValueGraph<IpsecPeerConfigId, IpsecSession> ipsecTopology,
      Set<String> initiatorNodes,
      Set<String> responderNodes) {
    Multiset<IpsecSessionInfo> ipsecSessionInfos = HashMultiset.create();

    for (IpsecPeerConfigId node : ipsecTopology.nodes()) {
      IpsecPeerConfig ipsecPeerConfig = networkConfigurations.getIpsecPeerConfig(node);
      if (ipsecPeerConfig == null
          || ipsecPeerConfig instanceof IpsecDynamicPeerConfig
          || !initiatorNodes.contains(node.getHostName())) {
        continue;
      }
      IpsecSessionInfo.Builder ipsecSessionInfoBuilder = IpsecSessionInfo.builder();

      ipsecSessionInfoBuilder.setInitiatorHostname(node.getHostName());
      ipsecSessionInfoBuilder.setInitiatorInterface(ipsecPeerConfig.getSourceInterface());
      ipsecSessionInfoBuilder.setInitiatorIp(ipsecPeerConfig.getLocalAddress());
      ipsecSessionInfoBuilder.setInitiatorTunnelInterface(ipsecPeerConfig.getTunnelInterface());
{'fraction_non_alphanumeric': 0.0835414692963569, 'fraction_numerical': 0.0008612522607871846, 'mean_word_length': 5.4368070953436805, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4560996', 'n_tokens_mistral': 3492, 'n_tokens_neox': 3363, 'n_words': 498}