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.audit.request.eventcreator;
  
  import java.util.Map;
  import java.util.Set;
  
  import org.apache.ambari.server.api.services.Request;
  import org.apache.ambari.server.api.services.Result;
  import org.apache.ambari.server.api.services.ResultStatus;
  import org.apache.ambari.server.audit.event.AuditEvent;
  import org.apache.ambari.server.audit.event.request.DeleteServiceRequestAuditEvent;
  import org.apache.ambari.server.audit.event.request.StartOperationRequestAuditEvent;
  import org.apache.ambari.server.controller.internal.RequestOperationLevel;
  import org.apache.ambari.server.controller.internal.ServiceResourceProvider;
  import org.apache.ambari.server.controller.spi.Resource;
  
  import com.google.common.collect.ImmutableSet;
  
  /**
   * This creator handles operation requests (start, stop, install, etc)
   * For resource type {@link Resource.Type#Service}
   * and request types {@link Request.Type#POST}, {@link Request.Type#PUT} and {@link Request.Type#DELETE}
   */
  public class ServiceEventCreator implements RequestAuditEventCreator {
  
    /**
     * Set of {@link Request.Type}s that are handled by this plugin
     */
    private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.PUT, Request.Type.POST, Request.Type.DELETE).build();
  
    /**
     * Set of {@link Resource.Type}s that are handled by this plugin
     */
    private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.Service).build();
  
    /**
     * {@inheritDoc}
     */
    @Override
    public Set<Request.Type> getRequestTypes() {
      return requestTypes;
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    public Set<Resource.Type> getResourceTypes() {
      return resourceTypes;
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    public Set<ResultStatus.STATUS> getResultStatuses() {
      // null makes this default
      return null;
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    public AuditEvent createAuditEvent(Request request, Result result) {
  
      if (request.getRequestType() == Request.Type.DELETE) {
        return DeleteServiceRequestAuditEvent.builder()
          .withTimestamp(System.currentTimeMillis())
          .withRequestType(request.getRequestType())
          .withResultStatus(result.getStatus())
          .withUrl(request.getURI())
          .withRemoteIp(request.getRemoteAddress())
            .withService(request.getResource().getKeyValueMap().get(Resource.Type.Service))
          .build();
      }
  
      String operation = getOperation(request);
  
      Long requestId = null;
      if (containsRequestId(result)) {
        requestId = getRequestId(result);
      }
  
      StartOperationRequestAuditEvent.StartOperationAuditEventBuilder auditEventBuilder = StartOperationRequestAuditEvent.builder()
        .withOperation(operation)
        .withRemoteIp(request.getRemoteAddress())
        .withTimestamp(System.currentTimeMillis())
        .withRequestId(String.valueOf(requestId));
  
      if (result.getStatus().isErrorState()) {
        auditEventBuilder.withReasonOfFailure(result.getStatus().getMessage());
      }
  
      return auditEventBuilder.build();
    }
  
    /**
     * Generates operation name based on the request. It checks the operation level, the host name, the service name, the status
     * and whether this is a maintenance mode switch change.
     * @param request
     * @return
     */
    private String getOperation(Request request) {
      if (request.getBody().getRequestInfoProperties() != null && request.getBody().getRequestInfoProperties().containsKey(RequestOperationLevel.OPERATION_LEVEL_ID)) {
        String operation = "";
        if ("CLUSTER".equals(request.getBody().getRequestInfoProperties().get(RequestOperationLevel.OPERATION_LEVEL_ID))) {
          for (Map<String, Object> map : request.getBody().getPropertySets()) {
            if (map.containsKey(ServiceResourceProvider.SERVICE_SERVICE_STATE_PROPERTY_ID)) {
              operation = String.valueOf(map.get(ServiceResourceProvider.SERVICE_SERVICE_STATE_PROPERTY_ID)) + ": all services"
                + " (" + request.getBody().getRequestInfoProperties().get(RequestOperationLevel.OPERATION_CLUSTER_ID) + ")";
              break;
            }
          }
        }
        if ("SERVICE".equals(request.getBody().getRequestInfoProperties().get(RequestOperationLevel.OPERATION_LEVEL_ID))) {
          for (Map<String, Object> map : request.getBody().getPropertySets()) {
            if (map.containsKey(ServiceResourceProvider.SERVICE_SERVICE_STATE_PROPERTY_ID)) {
              operation = String.valueOf(map.get(ServiceResourceProvider.SERVICE_SERVICE_STATE_PROPERTY_ID)) + ": " + map.get(ServiceResourceProvider.SERVICE_SERVICE_NAME_PROPERTY_ID)
                + " (" + request.getBody().getRequestInfoProperties().get(RequestOperationLevel.OPERATION_CLUSTER_ID) + ")";
              break;
            }
          }
        }
        return operation;
      }
  
      for (Map<String, Object> map : request.getBody().getPropertySets()) {
        if (map.containsKey(ServiceResourceProvider.SERVICE_MAINTENANCE_STATE_PROPERTY_ID)) {
          return "Turn " + map.get(ServiceResourceProvider.SERVICE_MAINTENANCE_STATE_PROPERTY_ID) + " Maintenance Mode for " + map.get(ServiceResourceProvider.SERVICE_SERVICE_NAME_PROPERTY_ID);
        }
      }
      return null;
    }
  
    /**
     * Returns request id from the result
     * @param result
     * @return
     */
    private Long getRequestId(Result result) {
      return (Long) result.getResultTree().getChild("request").getObject().getPropertiesMap().get("Requests").get("id");
    }
  
    /**
     * Checks if request id can be found in the result
     * @param result
     * @return
     */
    private boolean containsRequestId(Result result) {
      return result.getResultTree().getChild("request") != null
        && result.getResultTree().getChild("request").getObject() != null
        && result.getResultTree().getChild("request").getObject().getPropertiesMap().get("Requests") != null
        && result.getResultTree().getChild("request").getObject().getPropertiesMap().get("Requests").get("id") != null;
    }
  }
  