/*
 * 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.configuration;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.apache.ambari.annotations.ClusterScale;
import org.apache.ambari.annotations.ConfigurationMarkdown;
import org.apache.ambari.annotations.Experimental;
import org.apache.ambari.annotations.ExperimentalFeature;
import org.apache.ambari.annotations.Markdown;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.CommandExecutionType;
import org.apache.ambari.server.actionmanager.HostRoleCommand;
import org.apache.ambari.server.controller.spi.PropertyProvider;
import org.apache.ambari.server.controller.utilities.ScalingThreadPoolExecutor;
import org.apache.ambari.server.events.listeners.alerts.AlertReceivedListener;
import org.apache.ambari.server.orm.JPATableGenerationStrategy;
import org.apache.ambari.server.orm.PersistenceType;
import org.apache.ambari.server.orm.dao.HostRoleCommandStatusSummaryDTO;
import org.apache.ambari.server.security.ClientSecurityType;
import org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosAuthenticationProperties;
import org.apache.ambari.server.security.encryption.CredentialProvider;
import org.apache.ambari.server.state.services.MetricsRetrievalService;
import org.apache.ambari.server.state.services.RetryUpgradeActionService;
import org.apache.ambari.server.state.stack.OsFamily;
import org.apache.ambari.server.topology.addservice.GroupByComponentsStrategy;
import org.apache.ambari.server.topology.addservice.HostGroupStrategy;
import org.apache.ambari.server.upgrade.AbstractUpgradeCatalog;
import org.apache.ambari.server.utils.AmbariPath;
import org.apache.ambari.server.utils.DateUtils;
import org.apache.ambari.server.utils.HostUtils;
import org.apache.ambari.server.utils.PasswordUtils;
import org.apache.ambari.server.utils.ShellCommandUtil;
import org.apache.ambari.server.utils.StageUtils;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Charsets;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * The {@link Configuration} class is used to read from the
 * {{ambari.properties}} file and manage/expose the configuration properties.
 * Each property is wrapped in a {@link ConfigurationProperty} type, allowing
 * the coupling of the key, default value, and type.
 * <p/>
 * The {@link Configuration#main(String[])} method can be invoked to produce <a
 * href=https://en.wikipedia.org/wiki/Markdown>Markdown</a> of all of the
 * properties, along with their default values and descriptions. This should be
 * done for every release so that the documentation of the configuration
 * properties can be kept current. The {@code index_template.md} file is used to
 * generate the markdown.
 * <p/>
 * The markdown can also be generated by running the {@code site} phase in maven
 * or by running the {@code exec:java} goal directly:
 *
 * <pre>
 * mvn site
 * mvn exec:java
 * mvn exec:java@configuration-markdown (on maven 3.3.1 and later)
 * </pre>
 */
@Singleton
public class Configuration {
  /**
   * JVM property with optional path to Makrdown template file.
   */
  private static final String AMBARI_CONFIGURATION_MD_TEMPLATE_PROPERTY = "ambari.configuration.md.template";

  /**
   * The file to generate the complete Markdown documentation.
   */
  private static final String MARKDOWN_TEMPLATE_FILE = "index_template.md";

  /**
   * The key to replace with the content of all of the properties.
   */
  private static final String MARKDOWN_CONFIGURATION_TABLE_KEY = "$configuration-properties";

  /**
   * The key to replace with the content of recomended baseline values.
   */
  private static final String MARKDOWN_BASELINE_VALUES_KEY = "$baseline-values";

  /**
   * The template to use when appending a related property in the description.
   */
  private static final String MARKDOWN_RELATED_TO_TEMPLATE = " This property is related to `%s`.";

  /**
   * The HTML {@code <br/>} tag.
   */
  private static final String HTML_BREAK_TAG = "<br/>";

  private static final String AGENT_CONFIGS_DEFAULT_SECTION = "agentConfig";

  /**
   * Used to determine which repository validation strings should be used
   * depending on the OS.
   */
  @Inject
  private OsFamily osFamily;

  /**
   * The filename of the {@link Properties} file which contains all of the
   * configurations for Ambari.
   */
  private static final String CONFIG_FILE = "ambari.properties";

  /**
   * PREFIX_DIR is shared in ambari-agent.ini and should only be called by unit
   * tests. For all server-side processing, it should be retrieved from
   * <code>HostImpl.getPrefix()</code>
   */
  public static final String PREFIX_DIR = "/var/lib/ambari-agent/data";

  /**
   * The minimum JDK version supported by Ambari.
   */
  public static final float JDK_MIN_VERSION = 1.7f;

  /**
   * The prefix for any configuration property which will be appended to
   * {@code eclipselink.jdbc.property.} before being passed into EclipseLink.
   * These properties are driver-specific properties only and do not affect
   * EclipseLink.
   */
  public static final String SERVER_JDBC_PROPERTIES_PREFIX = "server.jdbc.properties.";

  /**
   * A prefix for any EclipseLink-specifc properties which are passed into
   * EclipseLink to alter its behavior. For example:
   * {@code server.persistence.properties.eclipselink.jdbc.batch-writing.size=25 ->
   * eclipselink.jdbc.batch-writing.size=25}
   */
  public static final String SERVER_PERSISTENCE_PROPERTIES_PREFIX = "server.persistence.properties.";

  /**
   * A replacement string to use for the RCA JDBC URLs in order to supply the
   * Ambari Server hostname.
   */
  public static final String HOSTNAME_MACRO = "{hostname}";

  /**
   * The name of the persistence unit in {@code persistence.xml} for the Ambari
   * Server.
   */
  public static final String JDBC_UNIT_NAME = "ambari-server";

  /**
   * The JDBC URL to use for local {@link DatabaseType#POSTGRES} connections.
   */
  public static final String JDBC_LOCAL_URL = "jdbc:postgresql://localhost/";

  /**
   * The schema to use when creating a {@link DatabaseType#DERBY} database for
   * unit tests.
   */
  public static final String DEFAULT_DERBY_SCHEMA = "ambari";

  /**
   * The schema to use when creating a {@link DatabaseType#H2} database for
   * unit tests.
   */
  public static final String DEFAULT_H2_SCHEMA = "ambari";

  /**
   * The JDBC URL to use when creating a {@link DatabaseType#H2} database for
   * unit tests.
   */
  public static final String JDBC_IN_MEMORY_URL = String.format(
      "jdbc:h2:mem:%1$s;ALIAS_COLUMN_NAME=TRUE;INIT=CREATE SCHEMA IF NOT EXISTS %1$s\\;SET SCHEMA %1$s;",
      DEFAULT_DERBY_SCHEMA);

  /**
   * The H2 driver to use when creating a {@link DatabaseType#H2} database
   * for unit tests.
   */
  public static final String JDBC_IN_MEMORY_DRIVER = "org.h2.Driver";

  /**
   * The H2 default user driver to use when creating a {@link DatabaseType#H2} database
   * for unit tests.
   */
  public static final String JDBC_IN_MEMORY_USER = "sa";

  /**
   * The H2 default password to use when creating a {@link DatabaseType#H2} database
   * for unit tests.
   */
  public static final String JDBC_IN_MEMORY_PASSWORD = "";

  /**
   * The JSSE property which governs the location of the keystore file
   * containing the collection of CA certificates trusted by Ambari.
   */
  public static final String JAVAX_SSL_TRUSTSTORE = "javax.net.ssl.trustStore";

  /**
   * The JSSE property which governs password to the keystore file containing
   * the collection of CA certificates trusted by Ambari.
   */
  public static final String JAVAX_SSL_TRUSTSTORE_PASSWORD = "javax.net.ssl.trustStorePassword";

  /**
   * The JSSE property which governs the type of the keystore file containing
   * the collection of CA certificates trusted by Ambari.
   */
  public static final String JAVAX_SSL_TRUSTSTORE_TYPE = "javax.net.ssl.trustStoreType";

  /**
   * The configuration tag for {@code mapreduce2-log4j}
   */
  public static final String MAPREDUCE2_LOG4J_CONFIG_TAG = "mapreduce2-log4j";

  /**
   * Threadpool sizing based on the number of available processors multiplied by
   * 2.
   */
  public static final int PROCESSOR_BASED_THREADPOOL_CORE_SIZE_DEFAULT = 2
      * Runtime.getRuntime().availableProcessors();

  /**
   * Threadpool sizing based on the number of available processors multiplied by
   * 4.
   */
  public static final int PROCESSOR_BASED_THREADPOOL_MAX_SIZE_DEFAULT = 4
      * Runtime.getRuntime().availableProcessors();

  /**
   * A set of all of the custom database connector JAR property names.
   */
  private static final Set<String> dbConnectorPropertyNames = Sets.newHashSet(
      "custom.mysql.jdbc.name", "custom.oracle.jdbc.name", "custom.postgres.jdbc.name",
      "custom.mssql.jdbc.name", "custom.hsqldb.jdbc.name", "custom.sqlanywhere.jdbc.name");

  /**
   * An environment property which can be used to specify the location of the
   * Ambari key services's master key.
   */
  public static final String MASTER_KEY_ENV_PROP = "AMBARI_SECURITY_MASTER_KEY";

  /**
   * The default name of the master key file.
   */
  public static final String MASTER_KEY_FILENAME_DEFAULT = "master";

  /**
   * The default name of the JSSE keystore file used by Ambari.
   */
  public static final String MASTER_KEYSTORE_FILENAME_DEFAULT = "credentials.jceks";

  /**
   * The key in the {@code metainfo} table that represents the version of Ambari
   * that the database is running.
   */
  public static final String SERVER_VERSION_KEY = "version";

  // Ambari server log4j file name
  public static final String AMBARI_LOG_FILE = "log4j.properties";

  /**
   * The maximum value of password history count which can be configured by the user.
   */
  public static final int MAXIMUM_PASSWORD_HISTORY_LIMIT = 10;

  /**
   * The default and minimum value of password history count which can be configured by the user.
   */
  public static final int MINIMUM_PASSWORD_HISTORY_LIMIT = 1;

  @Markdown(
          description = "Interval for heartbeat presence checks.",
          examples = {"60000","600000"} )
  public static final ConfigurationProperty<Integer> HEARTBEAT_MONITORING_INTERVAL = new ConfigurationProperty<>(
          "heartbeat.monitoring.interval",60000);

  /**
   * The directory on the Ambari Server file system used for storing
   * Ambari Agent bootstrap information such as request responses.
   */
  @Markdown(description = "The directory on the Ambari Server file system used for storing Ambari Agent bootstrap information such as request responses.")
  public static final ConfigurationProperty<String> BOOTSTRAP_DIRECTORY = new ConfigurationProperty<>(
      "bootstrap.dir", AmbariPath.getPath("/var/run/ambari-server/bootstrap"));

  /**
   * The directory on the Ambari Server file system used for expanding Views and
   * storing webapp work.
   */
  @Markdown(description = "The directory on the Ambari Server file system used for expanding Views and storing webapp work.")
  public static final ConfigurationProperty<String> VIEWS_DIRECTORY = new ConfigurationProperty<>(
      "views.dir", AmbariPath.getPath("/var/lib/ambari-server/resources/views"));

  /**
   * Determines whether to validate a View's configuration XML file against an XSD.
   */
  @Markdown(description = "Determines whether to validate a View's configuration XML file against an XSD.")
  public static final ConfigurationProperty<String> VIEWS_VALIDATE = new ConfigurationProperty<>(
      "views.validate", "false");

  /**
   * Determines whether the view directory watcher service should be disabled.
   */
  @Markdown(description = "Determines whether the view directory watcher service should be disabled.")
  public static final ConfigurationProperty<String> DISABLE_VIEW_DIRECTORY_WATCHER = new ConfigurationProperty<>(
      "views.directory.watcher.disable", "false");

  /**
   * Determines whether remove undeployed views from the Ambari database.
   */
  @Markdown(description = "Determines whether remove undeployed views from the Ambari database.")
  public static final ConfigurationProperty<String> VIEWS_REMOVE_UNDEPLOYED = new ConfigurationProperty<>(
      "views.remove.undeployed", "false");

  /**
   * The Ambari Server webapp root directory.
   */
  @Markdown(description = "The Ambari Server webapp root directory.")
  public static final ConfigurationProperty<String> WEBAPP_DIRECTORY = new ConfigurationProperty<>(
      "webapp.dir", "web");

  /**
   * The location and name of the Python script used to bootstrap new Ambari Agent hosts.
   */
  @Markdown(description = "The location and name of the Python script used to bootstrap new Ambari Agent hosts.")
  public static final ConfigurationProperty<String> BOOTSTRAP_SCRIPT = new ConfigurationProperty<>(
      "bootstrap.script", AmbariPath.getPath("/usr/lib/ambari-server/lib/ambari_server/bootstrap.py"));

  /**
   * The location and name of the Python script executed on the Ambari Agent
   * host during the bootstrap process.
   */
  @Markdown(description = "The location and name of the Python script executed on the Ambari Agent host during the bootstrap process.")
  public static final ConfigurationProperty<String> BOOTSTRAP_SETUP_AGENT_SCRIPT = new ConfigurationProperty<>(
      "bootstrap.setup_agent.script",
      AmbariPath.getPath("/usr/lib/ambari-server/lib/ambari_server/setupAgent.py"));

  /**
   * The password to set on the {@code AMBARI_PASSPHRASE} environment variable
   * before invoking the bootstrap script.
   */
  @Markdown(description = "The password to set on the `AMBARI_PASSPHRASE` environment variable before invoking the bootstrap script.")
  public static final ConfigurationProperty<String> BOOTSTRAP_SETUP_AGENT_PASSWORD = new ConfigurationProperty<>(
      "bootstrap.setup_agent.password", "password");

  /**
   * The host name of the Ambari Server which will be used by the Ambari Agents
   * for communication.
   */
  @Markdown(description = "The host name of the Ambari Server which will be used by the Ambari Agents for communication.")
  public static final ConfigurationProperty<String> BOOTSTRAP_MASTER_HOSTNAME = new ConfigurationProperty<>(
      "bootstrap.master_host_name", null);

  /**
   * The amount of time that recommendation API data is kept on the Ambari
   * Server file system. This is specified using a {@code hdwmy} syntax for
   * pairing the value with a time unit.
   *
   * @see DateUtils#getDateSpecifiedTimeAgo(String)
   */
  @Markdown(
      description = "The amount of time that Recommendation API data is kept on the Ambari Server file system. This is specified using a `hdwmy` syntax for pairing the value with a time unit (hours, days, weeks, months, years)",
      examples = { "8h", "2w", "1m" } )
  public static final ConfigurationProperty<String> RECOMMENDATIONS_ARTIFACTS_LIFETIME = new ConfigurationProperty<>(
      "recommendations.artifacts.lifetime", "1w");

    @Markdown(
            description = "Maximum number of recommendations artifacts at a given time",
            examples = {"50","10","100"} )
    public static final ConfigurationProperty<Integer> RECOMMENDATIONS_ARTIFACTS_ROLLOVER_MAX = new ConfigurationProperty<>(
            "recommendations.artifacts.rollover.max",100);

  /**
   * The directory on the Ambari Server file system used for storing
   * Recommendation API artifacts.
   */
  @Markdown(description = "The directory on the Ambari Server file system used for storing Recommendation API artifacts.")
  public static final ConfigurationProperty<String> RECOMMENDATIONS_DIR = new ConfigurationProperty<>(
      "recommendations.dir", AmbariPath.getPath("/var/run/ambari-server/stack-recommendations"));

  /**
   * The location and name of the Python stack advisor script executed when
   * configuring services.
   */
  @Markdown(description = "The location and name of the Python stack advisor script executed when configuring services.")
  public static final ConfigurationProperty<String> STACK_ADVISOR_SCRIPT = new ConfigurationProperty<>(
      "stackadvisor.script",
      AmbariPath.getPath("/var/lib/ambari-server/resources/scripts/stack_advisor.py"));

  /**
   * The name of the shell script used to wrap all invocations of Python by Ambari.
   */
  @Markdown(description = "The name of the shell script used to wrap all invocations of Python by Ambari. ")
  public static final ConfigurationProperty<String> AMBARI_PYTHON_WRAP = new ConfigurationProperty<>(
      "ambari.python.wrap", "ambari-python-wrap");

  /**
   * The username of the default user assumed to be executing API calls. When
   * set, authentication is not required in order to login to Ambari or use the
   * REST APIs.
   */
  @Markdown(description = "The username of the default user assumed to be executing API calls. When set, authentication is not required in order to login to Ambari or use the REST APIs.  ")
  public static final ConfigurationProperty<String> API_AUTHENTICATED_USER = new ConfigurationProperty<>(
      "api.authenticated.user", null);

  /**
   * Determines whether SSL is used in for secure connections to Ambari.
   */
  @Markdown(description = "Determines whether SSL is used in for secure connections to Ambari. When enabled, ambari-server setup-https must be run in order to properly configure keystores.")
  public static final ConfigurationProperty<String> API_USE_SSL = new ConfigurationProperty<>(
      "api.ssl", "false");

  /**
   * Determines whether
   * {@code org.apache.ambari.server.api.AmbariCsrfProtectionFilter} is used to
   * intercept requests and inspect the headers for an {@code X-Requested-By}
   * value. This helps to prevent Cross-Site Request Forgery attacks.
   */
  @Markdown(description = "Determines whether Cross-Site Request Forgery attacks are prevented by looking for the `X-Requested-By` header.")
  public static final ConfigurationProperty<String> API_CSRF_PREVENTION = new ConfigurationProperty<>(
      "api.csrfPrevention.enabled", "true");

  /**
   * Determines whether Gzip handler is enabled for Jetty.
   */
  @Markdown(description = "Determines whether jetty Gzip compression is enabled or not.")
  public static final ConfigurationProperty<String> GZIP_HANDLER_JETTY_ENABLED = new ConfigurationProperty<>(
    "gzip.handler.jetty.enabled", "true");

  /**
   * Determines whether HTTP body data is compressed with GZIP.
   */
  @Markdown(description = "Determines whether data sent to and from the Ambari service should be compressed.")
  public static final ConfigurationProperty<String> API_GZIP_COMPRESSION_ENABLED = new ConfigurationProperty<>(
      "api.gzip.compression.enabled", "true");

  /**
   * Used in conjunction with {@link #API_GZIP_COMPRESSION_ENABLED}, determines
   * the mininum size (in bytes) that an HTTP request must be before it should be
   * compressed.
   */
  @Markdown(description = "Used in conjunction with `api.gzip.compression.enabled`, determines the mininum size that an HTTP request must be before it should be compressed. This is measured in bytes.")
  public static final ConfigurationProperty<String> API_GZIP_MIN_COMPRESSION_SIZE = new ConfigurationProperty<>(
      "api.gzip.compression.min.size", "10240");

  /**
   * Determiens whether communication with the Ambari Agents should have the
   * JSON payloads compressed with GZIP.
   */
  @Markdown(description = "Determiens whether communication with the Ambari Agents should have the JSON payloads compressed with GZIP.")
  public static final ConfigurationProperty<String> AGENT_API_GZIP_COMPRESSION_ENABLED = new ConfigurationProperty<>(
      "agent.api.gzip.compression.enabled", "true");

  /**
   * Determines whether SSL is used to communicate between Ambari Server and Ambari Agents.
   */
  @Markdown(description = "Determines whether SSL is used to communicate between Ambari Server and Ambari Agents.")
  public static final ConfigurationProperty<String> AGENT_USE_SSL = new ConfigurationProperty<>(
      "agent.ssl", "true");

  /**
   * Configurable password policy for Ambari users
   */
  @Markdown(
      description = "Determines Ambari user password policy. Passwords should match the regex")
  public static final ConfigurationProperty<String> PASSWORD_POLICY_REGEXP = new ConfigurationProperty<>(
      "security.password.policy.regexp", ".*");

  /**
   * Configurable password policy for Ambari users
   */
  @Markdown(
      description = "Password policy description that is shown to users")
  public static final ConfigurationProperty<String> PASSWORD_POLICY_DESCRIPTION = new ConfigurationProperty<>(
      "security.password.policy.description", "");

  /**
   * Password history count to validate how many previous passwords should be checked before updating user password.
   */
  @Markdown(
          description = "Password policy to mandate that new password should be different from previous passwords, this would be based on the history count configured by the user. Valid values are 1 to 10.")
  public static final ConfigurationProperty<String> PASSWORD_POLICY_HISTORY_COUNT = new ConfigurationProperty<>(
          "security.password.policy.history.count", "1");

  /**
   * Determines whether the Ambari Agent host names should be validated against
   * a regular expression to ensure that they are well-formed.
   */
  @Markdown(description = "Determines whether the Ambari Agent host names should be validated against a regular expression to ensure that they are well-formed.<br><br>WARNING: By setting this value to false, host names will not be validated, allowing a possible security vulnerability as described in CVE-2014-3582. See https://cwiki.apache.org/confluence/display/AMBARI/Ambari+Vulnerabilities for more information.")
  public static final ConfigurationProperty<String> SRVR_AGENT_HOSTNAME_VALIDATE = new ConfigurationProperty<>(
      "security.agent.hostname.validate", "true");

  /**
   * Determines whether two-way SSL should be used between Ambari Server and
   * Ambari Agents so that the agents must also use SSL.
   *
   * @see HostUtils#isValidHostname(String)
   */
  @Markdown(description = "Determines whether two-way SSL should be used between Ambari Server and Ambari Agents so that the agents must also use SSL.")
  public static final ConfigurationProperty<String> SRVR_TWO_WAY_SSL = new ConfigurationProperty<>(
      "security.server.two_way_ssl", "false");

  /**
   * The port that the Ambari Server will use to communicate with the agents over SSL.
   */
  @Markdown(description = "The port that the Ambari Server will use to communicate with the agents over SSL.")
  public static final ConfigurationProperty<String> SRVR_TWO_WAY_SSL_PORT = new ConfigurationProperty<>(
      "security.server.two_way_ssl.port", "8441");

  /**
   * The port that the Ambari Agents will use to communicate with the Ambari Server over SSL.
   */
  @Markdown(description = "The port that the Ambari Agents will use to communicate with the Ambari Server over SSL.")
  public static final ConfigurationProperty<String> SRVR_ONE_WAY_SSL_PORT = new ConfigurationProperty<>(
      "security.server.one_way_ssl.port", "8440");

  /**
   * The directory on the Ambari Server where keystores are kept.
   */
  @Markdown(description = "The directory on the Ambari Server where keystores are kept.")
  public static final ConfigurationProperty<String> SRVR_KSTR_DIR = new ConfigurationProperty<>(
      "security.server.keys_dir", ".");

  /**
   * The name of the file that certificates are written to when using {@code openssl ca}.
   */
  @Markdown(description = "The name of the file located in the `security.server.keys_dir` directory where certificates will be generated when Ambari uses the `openssl ca` command.")
  public static final ConfigurationProperty<String> SRVR_CRT_NAME = new ConfigurationProperty<>(
      "security.server.cert_name", "ca.crt");

  /**
   * The name of the file that contains the CA certificate chain for certificate validation during 2-way SSL communication.
   */
  @Markdown(description = "The name of the file located in the `security.server.keys_dir` directory containing the CA certificate chain used to verify certificates during 2-way SSL communications.")
  public static final ConfigurationProperty<String> SRVR_CRT_CHAIN_NAME = new ConfigurationProperty<>(
      "security.server.cert_chain_name", "ca_chain.pem");

  /**
   * The name of the certificate request file used when generating certificates.
   */
  @Markdown(description = "The name of the certificate request file used when generating certificates.")
  public static final ConfigurationProperty<String> SRVR_CSR_NAME = new ConfigurationProperty<>(
      "security.server.csr_name", "ca.csr");

  /**
   * The name of the private key used to sign requests.
   */
  @Markdown(description = "The name of the private key used to sign requests.")
  public static final ConfigurationProperty<String> SRVR_KEY_NAME = new ConfigurationProperty<>(
      "security.server.key_name", "ca.key");

  /**
   * The name of the keystore file, located in {@link #SRVR_KSTR_DIR}.
   */
  @Markdown(description = "The name of the keystore file, located in `security.server.keys_dir`")
  public static final ConfigurationProperty<String> KSTR_NAME = new ConfigurationProperty<>(
      "security.server.keystore_name", "keystore.p12");

  /**
   * The type of the keystore file specified in {@link #KSTR_NAME}. By default
   * self-signed certificates are used and we can use keystore as truststore in
   * PKCS12 format. When CA signed certificates are used truststore should be
   * created in JKS format (truststore.jks)
   */
  @Markdown(description = "The type of the keystore file specified in `security.server.key_name`. Self-signed certificates can be `PKCS12` while CA signed certificates are `JKS`")
  public static final ConfigurationProperty<String> KSTR_TYPE = new ConfigurationProperty<>(
      "security.server.keystore_type", "PKCS12");

  /**
   * The name of the truststore file ambari uses to store trusted certificates.
   * Located in {@link #SRVR_KSTR_DIR}.
   */
  @Markdown(description = "The name of the truststore file ambari uses to store trusted certificates. Located in `security.server.keys_dir`")
  public static final ConfigurationProperty<String> TSTR_NAME = new ConfigurationProperty<>(
      "security.server.truststore_name", "keystore.p12");

  /**
   * The type of the truststore file specified in {@link #TSTR_NAME}. By default
   * self-signed certificates are used and we can use keystore as truststore in
   * PKCS12 format. When CA signed certificates are used truststore should be
   * created in JKS format (truststore.jks)
   */
  @Markdown(description = "The type of the truststore file specified in `security.server.truststore_name`. Self-signed certificates can be `PKCS12` while CA signed certificates are `JKS`")
  public static final ConfigurationProperty<String> TSTR_TYPE = new ConfigurationProperty<>(
      "security.server.truststore_type", "PKCS12");

  /**
   * The filename which contains the password for the keystores, truststores, and certificates.
   */
  @Markdown(description = "The filename which contains the password for the keystores, truststores, and certificates.")
  public static final ConfigurationProperty<String> SRVR_CRT_PASS_FILE = new ConfigurationProperty<>(
      "security.server.crt_pass_file", "pass.txt");

  /**
   * The password for the keystores, truststores, and certificates. If not specified, then {@link #SRVR_CRT_PASS_FILE} should be used.
   */
  @Markdown(description = "The password for the keystores, truststores, and certificates. If not specified, then `security.server.crt_pass_file` should be used")
  public static final ConfigurationProperty<String> SRVR_CRT_PASS = new ConfigurationProperty<>(
      "security.server.crt_pass", null);

  /**
   * The length of the randomly generated password for keystores and truststores.
   */
  @Markdown(description = "The length of the randomly generated password for keystores and truststores. ")
  public static final ConfigurationProperty<String> SRVR_CRT_PASS_LEN = new ConfigurationProperty<>(
      "security.server.crt_pass.len", "50");

  /**
   * An environment variable which can be used to supply the Ambari Server
   * password when bootstrapping new Ambari Agents.
   */
  @Markdown(description = "An environment variable which can be used to supply the Ambari Server password when bootstrapping new Ambari Agents.")
  public static final ConfigurationProperty<String> PASSPHRASE_ENV = new ConfigurationProperty<>(
      "security.server.passphrase_env_var", "AMBARI_PASSPHRASE");

  /**
   * The password to the Ambari Server to supply to new Ambari Agent hosts being
   * bootstrapped.
   */
  @Markdown(description = "The password to the Ambari Server to supply to new Ambari Agent hosts being bootstrapped.")
  public static final ConfigurationProperty<String> PASSPHRASE = new ConfigurationProperty<>(
      "security.server.passphrase", "AMBARI_PASSPHRASE");

  /**
   * A list of cipher suites which are not strong enough to use and will be excluded when creating SSL connections.
   */
  @Markdown(
      description = "A list of cipher suites which are not strong enough to use and will be excluded when creating SSL connections.",
      examples = {"SSL_RSA_WITH_RC4_128_MD5\\|SSL_RSA_WITH_RC4_12‌​8_MD5"})
  public static final ConfigurationProperty<String> SRVR_DISABLED_CIPHERS = new ConfigurationProperty<>(
      "security.server.disabled.ciphers", "");

  /**
   * The list of protocols which should not be used when creating SSL connections.
   */
  @Markdown(
      description = "The list of protocols which should not be used when creating SSL connections.",
      examples = { "TLSv1.1\\|TLSv1.2" })
  public static final ConfigurationProperty<String> SRVR_DISABLED_PROTOCOLS = new ConfigurationProperty<>(
      "security.server.disabled.protocols", "");

  /**
   * The location on the Ambari Server where all resources exist, including common services, stacks, and scripts.
   */
  @Markdown(description = "The location on the Ambari Server where all resources exist, including common services, stacks, and scripts.")
