content: /*
   * 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.
   */
  
  package org.apache.ambari.server.notifications.dispatchers;
  
  import java.io.IOException;
  import java.lang.management.ManagementFactory;
  import java.lang.management.RuntimeMXBean;
  import java.math.BigDecimal;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.Set;
  
  import org.apache.ambari.server.notifications.Notification;
  import org.apache.ambari.server.state.alert.AlertNotification;
  import org.apache.ambari.server.state.alert.TargetType;
  import org.apache.ambari.server.state.services.AlertNoticeDispatchService;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.snmp4j.PDU;
  import org.snmp4j.Snmp;
  import org.snmp4j.mp.SnmpConstants;
  import org.snmp4j.smi.Integer32;
  import org.snmp4j.smi.OID;
  import org.snmp4j.smi.OctetString;
  import org.snmp4j.smi.TimeTicks;
  import org.snmp4j.smi.VariableBinding;
  import org.snmp4j.util.DefaultPDUFactory;
  
  import com.google.inject.Singleton;
  
  /**
   * The {@link AmbariSNMPDispatcher} class is used to dispatch {@link AlertNotification} via SNMP using predefined Ambari OIDs.
   *
   * <pre>The base OID for Ambari is 1.3.6.1.4.1.18060.16. Off of this, we define the following:
   * .0 - apacheAmbariTraps
   * .1 - apacheAmbariAlerts
   * From these two roots, we define other bindings:
   * .1.1 - apacheAmbariAlertTable
   * .1.1.1 - apacheAmbariAlertEntry
   * .1.1.1.2 - alertDefinitionName
   * </pre>
   */
  
  @Singleton
  public class AmbariSNMPDispatcher extends SNMPDispatcher {
  
      private static final Logger LOG = LoggerFactory.getLogger(AmbariSNMPDispatcher.class);
  
      public static final String BASE_AMBARI_OID = "1.3.6.1.4.1.18060.16";
      public static final String APACHE_AMBARI_TRAPS_OID = BASE_AMBARI_OID + ".0";
      public static final String AMBARI_ALERT_TRAP_OID = APACHE_AMBARI_TRAPS_OID + ".1";
      public static final String AMBARI_ALERTS_OID = BASE_AMBARI_OID + ".1";
      public static final String AMBARI_ALERT_TABLE_OID = AMBARI_ALERTS_OID + ".1";
      public static final String AMBARI_ALERT_ENTRY_OID = AMBARI_ALERT_TABLE_OID + ".1";
      //ALERT_ENTRY fields
      public static final String AMBARI_ALERT_DEFINITION_ID_OID = AMBARI_ALERT_ENTRY_OID + ".1";
      public static final String AMBARI_ALERT_DEFINITION_NAME_OID = AMBARI_ALERT_ENTRY_OID + ".2";
      public static final String AMBARI_ALERT_DEFINITION_HASH_OID = AMBARI_ALERT_ENTRY_OID + ".3";
      public static final String AMBARI_ALERT_NAME_OID = AMBARI_ALERT_ENTRY_OID + ".4";
      public static final String AMBARI_ALERT_TEXT_OID = AMBARI_ALERT_ENTRY_OID + ".5";
      public static final String AMBARI_ALERT_STATE_OID = AMBARI_ALERT_ENTRY_OID + ".6";
      public static final String AMBARI_ALERT_HOST_NAME_OID = AMBARI_ALERT_ENTRY_OID + ".7";
      public static final String AMBARI_ALERT_SERVICE_NAME_OID = AMBARI_ALERT_ENTRY_OID + ".8";
      public static final String AMBARI_ALERT_COMPONENT_NAME_OID = AMBARI_ALERT_ENTRY_OID + ".9";
  
      protected AmbariSNMPDispatcher(Snmp snmp) {
          super(snmp);
      }
  
      public AmbariSNMPDispatcher(Integer port) throws IOException {
          super(port);
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public String getType() {
          return TargetType.AMBARI_SNMP.name();
      }
  
      /**
       * {@inheritDoc}
       * Uses default Ambari OIDs
       */
      @Override
      protected PDU prepareTrap(Notification notification, SnmpVersion snmpVersion) throws InvalidSnmpConfigurationException {
          AlertNotification alertNotification;
          PDU pdu = DefaultPDUFactory.createPDU(snmpVersion.getTargetVersion());
  
          if (Notification.Type.ALERT.equals(notification.getType())) {
              try {
                  alertNotification = (AlertNotification) notification;
              } catch (ClassCastException e) {
                  LOG.error("Notification wasn't casted to AlertNotification. Returning empty Protocol data unit", e);
                  return pdu;
              }
          } else {
              LOG.error("Notification for AmbariSNMPDispatcher should be of type AlertNotification, but it wasn't. Returning empty Protocol data unit");
              return pdu;
          }
  
          pdu.setType(snmpVersion.getTrapType());
      
          RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
          long uptimeInHundredthsOfSecond = runtimeMXBean.getUptime() / 10;
          pdu.add(new VariableBinding(SnmpConstants.sysUpTime, new TimeTicks(uptimeInHundredthsOfSecond)));
  
         // Set trap oid for PDU
          pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID, new OID(AMBARI_ALERT_TRAP_OID)));
          // Set notification body and subject for PDU objects with identifiers specified in dispatch properties.
          AlertNoticeDispatchService.AlertInfo alertInfo = alertNotification.getAlertInfo();
          addIntVariableBindingCheckForNull(pdu, AMBARI_ALERT_DEFINITION_ID_OID, new BigDecimal(alertInfo.getAlertDefinitionId()).intValueExact());
          addStringVariableBindingCheckForNull(pdu, AMBARI_ALERT_DEFINITION_NAME_OID, alertInfo.getAlertDefinition().getDefinitionName());
          addStringVariableBindingCheckForNull(pdu, AMBARI_ALERT_DEFINITION_HASH_OID, alertInfo.getAlertDefinitionHash());
          addStringVariableBindingCheckForNull(pdu, AMBARI_ALERT_NAME_OID, alertInfo.getAlertName());
          addStringVariableBindingCheckForNull(pdu, AMBARI_ALERT_TEXT_OID, alertInfo.getAlertText());
          addIntVariableBindingCheckForNull(pdu, AMBARI_ALERT_STATE_OID, alertInfo.getAlertState().getIntValue());
          addStringVariableBindingCheckForNull(pdu, AMBARI_ALERT_HOST_NAME_OID, alertInfo.getHostName());
          addStringVariableBindingCheckForNull(pdu, AMBARI_ALERT_SERVICE_NAME_OID, alertInfo.getServiceName());
          addStringVariableBindingCheckForNull(pdu, AMBARI_ALERT_COMPONENT_NAME_OID, alertInfo.getComponentName());
  
          return pdu;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      protected Set<String> getSetOfDefaultNeededPropertyNames() {
          return new HashSet<>(Collections.singletonList(PORT_PROPERTY));
      }
  
      /**
       * Adds new {@link VariableBinding} using provided {@link OID} and value to {@link PDU}
       * if val is null than adds {@link OctetString} with "null" value;
       * @param pdu
       * @param oid
       * @param val
       */
      private void addStringVariableBindingCheckForNull(PDU pdu, String oid, Object val) {
          if (val == null)  {
              pdu.add(new VariableBinding(new OID(oid), new OctetString("null")));
          } else {
              pdu.add(new VariableBinding(new OID(oid),
                      new OctetString(String.valueOf(val))));
          }
      }
      /**
       * Adds new {@link VariableBinding} using provided {@link OID} and value to {@link PDU}
       * if val is null than adds {@link OctetString} with "null" value;
       * @param pdu
       * @param oid
       * @param val
       */
      private void addIntVariableBindingCheckForNull(PDU pdu, String oid, Integer val) {
          if (val == null)  {
              pdu.add(new VariableBinding(new OID(oid), new OctetString("null")));
          } else {
              pdu.add(new VariableBinding(new OID(oid),
                      new Integer32(val)));
          }
      }
  }
  