<commit_before>/**
 * 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 com.sequenceiq.ambari.client

import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import groovy.util.logging.Slf4j
import groovyx.net.http.ContentType
import groovyx.net.http.HttpResponseException
import groovyx.net.http.RESTClient
import org.apache.commons.io.IOUtils
import org.apache.http.NoHttpResponseException
import org.apache.http.client.ClientProtocolException
import java.net.ConnectException
import java.net.NoRouteToHostException
import java.net.UnknownHostException

/**
 * Basic client to send requests to the Ambari server.
 */
@Slf4j
class AmbariClient {

  private static final int PAD = 30
  private static final int OK_RESPONSE = 200
  private static final String SLAVE = "slave_"
  boolean debugEnabled = false;
  def RESTClient ambari
  def slurper = new JsonSlurper()
  def clusterName

  /**
   * Connects to the ambari server.
   *
   * @param host host of the Ambari server; default value is localhost
   * @param port port of the Ambari server; default value is 8080
   * @param user username of the Ambari server; default is admin
   * @param password password fom the Ambari server; default is <PASSWORD>
   */
  AmbariClient(host = 'localhost', port = '8080', user = 'admin', password = '<PASSWORD>') {
    ambari = new RESTClient("http://${host}:${port}/api/v1/" as String)
    ambari.headers['Authorization'] = 'Basic ' + "$user:$password".getBytes('iso-8859-1').encodeBase64()
    ambari.headers['X-Requested-By'] = 'ambari'
  }

  /**
   * Connects to the ambari server.
   *
   * @param restClient underlying client
   * @param slurper slurper to parse responses
   */
  AmbariClient(RESTClient restClient, JsonSlurper slurper) {
    this.ambari = restClient
    this.slurper = slurper
  }

  /**
   * Sets the debug variable. Used by printing the API calls for the Ambari Shell.
   *
   * @param enabled enable or disable
   */
  def setDebugEnabled(boolean enabled) {
    debugEnabled = enabled;
  }

  /**
   * Returns the name of the cluster.
   *
   * @return the name of the cluster of null if no cluster yet
   */
  def String getClusterName() {
    if (!clusterName) {
      def clusters = getClusters();
      if (clusters) {
        clusterName = clusters.items[0]?.Clusters?.cluster_name
      }
    }
    return clusterName
  }

  /**
   * Adds a registered node to the cluster.
   *
   * @param hostName new node's hostname
   * @throws HttpResponseException if the node is not registered with ambari
   */
  def addHost(String hostName) throws HttpResponseException {
    if (debugEnabled) {
      println "[DEBUG] POST ${ambari.getUri()}clusters/${getClusterName()}/hosts/$hostName"
    }
    ambari.post(path: "clusters/${getClusterName()}/hosts/$hostName", { it })
  }

  /**
   * Returns the state of the host.
   *
   * @param host host's internal hostname
   * @return state of the host
   */
  String getHostState(String host) {
    getAllResources("hosts/$host").Hosts.host_status
  }

  /**
   * Create a new Ambari user.
   */
  def createUser(String user, String password, boolean admin) {
    def roles = ["user"]
    if (admin) {
      roles << "admin"
    }
    def context = ["Users": ["roles": roles.join(','), "password": password]]
    ambari.post(path: "users/$user", body: new JsonBuilder(context).toPrettyString(), { it })
  }

  /**
   * Delete an Ambari user.
   */
  def deleteUser(String user) {
    ambari.delete(path: "users/$user")
  }

  /**
   * Change the password of an Ambari user.
   */
  def changePassword(String user, String oldPassword, String newPassword, boolean admin) {
    def roles = ["user"]
    if (admin) {
      roles << "admin"
    }
    def context = ["Users": ["roles": roles.join(','), "password": <PASSWORD>, "old_password": <PASSWORD>]]
    ambari.put(path: "users/$user", body: new JsonBuilder(context).toPrettyString(), requestContentType: ContentType.URLENC)
  }

  /**
   * Decommission and remove a host from the cluster.
   * NOTE: this is a synchronous call, it wont return until all
   * requests are finished
   *
   * Steps:
   *  1, decommission services
   *  2, stop services
   *  3, delete host components
   *  4, delete host
   *  5, restart services
   *
   * @param hostName host to be deleted
   */
  def removeHost(String hostName) {
    def components = getHostComponentsMap(hostName).keySet() as List

    // decommission<commit_msg>CLOUD-198 run MR service check<commit_after>/**
 * 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 com.sequenceiq.ambari.client

import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import groovy.util.logging.Slf4j
import groovyx.net.http.ContentType
import groovyx.net.http.HttpResponseException
import groovyx.net.http.RESTClient
import org.apache.commons.io.IOUtils
import org.apache.http.NoHttpResponseException
import org.apache.http.client.ClientProtocolException
import java.net.ConnectException
import java.net.NoRouteToHostException
import java.net.UnknownHostException

/**
 * Basic client to send requests to the Ambari server.
 */
@Slf4j
class AmbariClient {

  private static final int PAD = 30
  private static final int OK_RESPONSE = 200
  private static final String SLAVE = "slave_"
  boolean debugEnabled = false;
  def RESTClient ambari
  def slurper = new JsonSlurper()
  def clusterName

  /**
   * Connects to the ambari server.
   *
   * @param host host of the Ambari server; default value is localhost
   * @param port port of the Ambari server; default value is 8080
   * @param user username of the Ambari server; default is admin
   * @param password password fom the Ambari server; default is <PASSWORD>
   */
  AmbariClient(host = 'localhost', port = '8080', user = 'admin', password = '<PASSWORD>') {
    ambari = new RESTClient("http://${host}:${port}/api/v1/" as String)
    ambari.headers['Authorization'] = 'Basic ' + "$user:$password".getBytes('iso-8859-1').encodeBase64()
    ambari.headers['X-Requested-By'] = 'ambari'
  }

  /**
   * Connects to the ambari server.
   *
   * @param restClient underlying client
   * @param slurper slurper to parse responses
   */
  AmbariClient(RESTClient restClient, JsonSlurper slurper) {
    this.ambari = restClient
    this.slurper = slurper
  }

  /**
   * Sets the debug variable. Used by printing the API calls for the Ambari Shell.
   *
   * @param enabled enable or disable
   */
  def setDebugEnabled(boolean enabled) {
    debugEnabled = enabled;
  }

  /**
   * Returns the name of the cluster.
   *
   * @return the name of the cluster of null if no cluster yet
   */
  def String getClusterName() {
    if (!clusterName) {
      def clusters = getClusters();
      if (clusters) {
        clusterName = clusters.items[0]?.Clusters?.cluster_name
      }
    }
    return clusterName
  }

  /**
   * Adds a registered node to the cluster.
   *
   * @param hostName new node's hostname
   * @throws HttpResponseException if the node is not registered with ambari
   */
  def addHost(String hostName) throws HttpResponseException {
    if (debugEnabled) {
      println "[DEBUG] POST ${ambari.getUri()}clusters/${getClusterName()}/hosts/$hostName"
    }
    ambari.post(path: "clusters/${getClusterName()}/hosts/$hostName", { it })
  }

  /**
   * Returns the state of the host.
   *
   * @param host host's internal hostname
   * @return state of the host
   */
  String getHostState(String host) {
    getAllResources("hosts/$host").Hosts.host_status
  }

  /**
   * Create a new Ambari user.
   */
  def createUser(String user, String password, boolean admin) {
    def roles = ["user"]
    if (admin) {
      roles << "admin"
    }
    def context = ["Users": ["roles": roles.join(','), "password": password]]
    ambari.post(path: "users/$user", body: new JsonBuilder(context).toPrettyString(), { it })
  }

  /**
   * Delete an Ambari user.
   */
  def deleteUser(String user) {
    ambari.delete(path: "users/$user")
  }

  /**
   * Change the password of an Ambari user.
   */
  def changePassword(String user, String oldPassword, String newPassword, boolean admin) {
    def roles = ["user"]
    if (admin) {
      roles << "admin"
    }
    def context = ["Users": ["roles": roles.join(','), "password": <PASSWORD>, "old_password": <PASSWORD>]]
    ambari.put(path: "users/$user", body: new JsonBuilder(context).toPrettyString(), requestContentType: ContentType.URLENC)
  }

  /**
   * Runs a MapReduce service check which is a simple WordCount.
   * @return id of the request
   */
  def int runMRServiceCheck() {
    runServiceCheck("MAPREDUCE2_SERVICE_CHECK", "MAPREDUCE2")
  }

  /**
   * Run a service check.
   *
   * @param command command to run
   * @param serviceName name of the service
   * @return id of the request
   */
  def int runServiceCheck(String command, String serviceName) {
    Map bodyMap = [
      "RequestInfo"              : [command: command, context: command],
      "Requests/resource_filters": [[service_name: serviceName]]
    ]
    ambari.post(path: "clusters/${getClusterName()}/requests", body: new JsonBuilder(bodyMap).toPrettyString(), {
      getRequestId(it)
    })
  }

  /**
   * Decommission and remove a host from the cluster.
   * NOTE: this is a synchronous call, it wont return until all
   * requests are finished
   *
   * Steps:
   *  1, decommission services
   *  2, stop services
   *  3, delete host components
   *  4, delete host
   *  5, restart services
   *
   * @param hostName host to be deleted
   */
  def removeHost(String hostName) {
    def components = getHostComponentsMap(hostName).keySet() as List

    // decommission
{'fraction_non_alphanumeric': 0.10690582959641255, 'fraction_numerical': 0.006367713004484305, 'mean_word_length': 3.32544608223429, 'pattern_counts': {'":': 15, '<': 19, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '5563065', 'n_tokens_mistral': 3401, 'n_tokens_neox': 3283, 'n_words': 1095}