You are here

elasticsearch_connector.module in Elasticsearch Connector 7

This module provide an interface to connecting to the elasticsearch cluster and implementing the official Elasticsearch library.

TODO: There must be a default connection. Cluster is still the default connection. Please change the default setting on the cluster you wish to set as default.

Created on Dec 21, 2013

File

elasticsearch_connector.module
View source
<?php

/**
 * @file
 * This module provide an interface to connecting to the elasticsearch
 * cluster and implementing the official Elasticsearch library.
 *
 * TODO: There must be a default connection. Cluster is still the default connection. Please change the default setting on the cluster you wish to set as default.
 *
 * Created on Dec 21, 2013
 */
define('ELASTICSEARCH_CONNECTOR_STATUS_INACTIVE', '0');
define('ELASTICSEARCH_CONNECTOR_STATUS_ACTIVE', '1');
define('ELASTICSEARCH_CONNECTOR_CLUSTER_STATUS_OK', '200');
define('ELASTICSEARCH_CONNECTOR_DEFAULT_TIMEOUT', '3');

/**
 * Implements hook_help().
 */
function elasticsearch_connector_help($path, $arg) {
  switch ($path) {
    case 'admin/help#elasticsearch_connector':
      return '<p>' . t('Abstraction of making connection to the elasticsearch
        server. This module is API for a whole bunch of functionalities connected
        with this module.
        If you don\'t have installed server, you can use a hosted solution.');
  }
}

/**
 * Implements hook_cron()
 */
function elasticsearch_connector_cron() {

  // TODO: Check cluster node state and update cluster nodes if any changes.
  // Do this only if we have auto-node update configuration enabled.
  // The default state of the auto mode will be activeated!
}

/**
 * Implements hook_permission().
 */
function elasticsearch_connector_permission() {
  return array(
    'administer elasticsearch connector' => array(
      'title' => t('Administer elasticsearch connector'),
      'description' => t('Provide access to administer elasticsearch clusters.'),
    ),
  );
}

/**
 * Implements hook_theme().
 */
function elasticsearch_connector_theme() {
  return array(
    'elasticsearch_connector_page' => array(
      'render element' => 'page',
      'template' => 'elasticsearch-connector-dialog-page',
    ),
  );
}

/**
 * Implements hook_menu().
 */
function elasticsearch_connector_menu() {
  $items = array();
  $settings_path = elasticsearch_connector_main_settings_path();
  $items[$settings_path] = array(
    'title' => 'Elasticsearch connector',
    'description' => 'Administer Elasticsearch connector module',
    'position' => 'left',
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'page callback' => 'system_admin_menu_block_page',
    'file' => 'system.admin.inc',
    'file path' => drupal_get_path('module', 'system'),
  );
  $items[$settings_path . '/clusters'] = array(
    'title' => 'Elasticsearch Clusters',
    'description' => 'Showing all available clusters',
    'page callback' => 'elasticsearch_connector_status_page',
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
    'weight' => -10,
  );
  $items[$settings_path . '/clusters/add'] = array(
    'title' => 'Add cluster',
    'description' => 'Add new elasticsearch cluster',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'elasticsearch_connector_edit_cluster',
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
    'type' => MENU_LOCAL_ACTION,
  );
  $items[$settings_path . '/clusters/%elasticsearch_connector_cluster/edit'] = array(
    'title' => 'Edit cluster',
    'description' => 'Edit cluster settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'elasticsearch_connector_edit_cluster',
      4,
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items[$settings_path . '/clusters/%elasticsearch_connector_cluster/info'] = array(
    'title' => 'Elasticsearch cluster info',
    'description' => 'Elasticsearch cluster info',
    'page callback' => 'elasticsearch_connector_info_cluster',
    'page arguments' => array(
      4,
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items[$settings_path . '/clusters/%elasticsearch_connector_cluster/indices'] = array(
    'title' => 'Elasticsearch cluster indices',
    'description' => 'Elasticsearch cluster indices',
    'page callback' => 'elasticsearch_connector_cluster_indices',
    'page arguments' => array(
      4,
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
  );
  $items[$settings_path . '/clusters/%elasticsearch_connector_cluster/indices/add'] = array(
    'title' => 'Add index',
    'description' => 'Add index',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'elasticsearch_connector_cluster_indices_add',
      4,
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
    'type' => MENU_LOCAL_ACTION,
  );
  $items[$settings_path . '/clusters/%elasticsearch_connector_cluster/indices/%elasticsearch_connector_index/edit'] = array(
    'title' => 'Elasticsearch cluster indices',
    'description' => 'Elasticsearch cluster indices',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'elasticsearch_connector_cluster_indices_add',
      4,
      6,
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'load arguments' => array(
      '%map',
      '%index',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
  );
  $items[$settings_path . '/clusters/%elasticsearch_connector_cluster/indices/%elasticsearch_connector_index_valid/aliases'] = array(
    'title' => 'Elasticsearch cluster indices',
    'description' => 'Elasticsearch cluster indices',
    'page callback' => 'elasticsearch_connector_cluster_indices_aliases',
    'page arguments' => array(
      4,
      6,
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
  );
  $items[$settings_path . '/clusters/%elasticsearch_connector_cluster/indices/%elasticsearch_connector_index_valid/delete'] = array(
    'title' => 'Elasticsearch cluster indices',
    'description' => 'Elasticsearch cluster indices',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'elasticsearch_connector_cluster_indices_delete',
      4,
      6,
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
  );
  $items[$settings_path . '/clusters/%elasticsearch_connector_cluster/delete'] = array(
    'title' => 'Delete cluster',
    'description' => 'Delete cluster settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'elasticsearch_connector_delete_cluster',
      4,
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
    'file' => 'elasticsearch_connector.admin.inc',
  );
  $items['elasticsearch-connector-dialog/redirect/%elasticsearch_connector_cluster/%elasticsearch_connector_index_valid'] = array(
    'page callback' => 'elasticsearch_connector_redirect_page',
    'page arguments' => array(
      2,
      3,
    ),
    'access arguments' => array(
      'administer elasticsearch connector',
    ),
  );
  return $items;
}

/**
 * Implements hook_element_info()
 */
function elasticsearch_connector_element_info() {
  return array(
    'ec_clusters' => array(
      '#input' => TRUE,
      '#multiple' => FALSE,
      '#theme' => 'select',
      '#theme_wrappers' => array(
        'form_element',
      ),
      '#process' => array(
        '_elasticsearch_connector_ec_clusters_process',
      ),
    ),
    'ec_index' => array(
      '#input' => TRUE,
      '#tree' => TRUE,
      '#multiple' => FALSE,
      '#theme_wrappers' => array(
        'form_element',
      ),
      '#process' => array(
        '_elasticsearch_connector_ec_index_process',
      ),
      '#attached' => _elasticsearch_connector_ec_index_attached(),
    ),
  );
}

/**
 * Process the ec_cluster element type.
 *
 * @param array $element
 * @return array $element
 */
function _elasticsearch_connector_ec_clusters_process($element, &$form_state, $form) {
  $element = form_process_select($element);
  if (empty($element['#skip_default_options'])) {
    $element['#only_active'] = isset($element['#only_active']) ? $element['#only_active'] : TRUE;
    $element['#empty_option'] = isset($element['#empty_option']) ? $element['#empty_option'] : TRUE;
    $clusters = elasticsearch_connector_cluster_load_all($element['#only_active'], $element['#empty_option']);
    $element['#options'] = $clusters;
  }
  return $element;
}

/**
 * Check if the index is valid and compare it with the indexes comming from Elasticsearch.
 *
 * @param string $index_name
 * @param array $map_array
 * @param int $index
 * @return string|boolean
 */
function elasticsearch_connector_index_load($index_name, $map_array, $index) {
  if (elasticsearch_connector_index_valid_load($index_name)) {
    $cluster = $map_array[4];
    $client = elasticsearch_connector_load_library($cluster);
    if (!empty($client)) {
      $indices = $client
        ->indices()
        ->stats();
      if (isset($indices['indices'][$index_name])) {
        return $index_name;
      }
      else {
        return FALSE;
      }
    }
  }
}

/**
 * Attach required javascript for the ec_index element.
 * @return array
 */
function _elasticsearch_connector_ec_index_attached() {
  return array(
    'js' => array(
      drupal_get_path('module', 'elasticsearch_connector') . '/js/ec-index.js',
    ),
    'css' => array(
      drupal_get_path('module', 'elasticsearch_connector') . '/css/ec-index.css',
    ),
    'library' => array(
      array(
        'system',
        'ui.dialog',
      ),
    ),
  );
}

/**
 * Check if some of the modules locked a major changes on the cluster settings and deletion.
 * Invoke the hooks similar to the module_invoke.
 *
 * @param object $cluster
 * @return array
 */
function _elasticsearch_connector_check_if_cluster_locked($cluster) {
  $locked = array();
  if (!empty($cluster)) {
    $type = 'cluster';
    foreach (module_implements('elasticsearch_connector_edit_lock') as $module) {
      $function = $module . '_elasticsearch_connector_edit_lock';
      $locked_result = $function($type, $cluster, NULL);
      if (!empty($locked_result)) {
        $locked[] = $module;
      }
    }
  }
  return $locked;
}

/**
 * Check if some of the modules locked a major changes on the cluster settings and deletion.
 * @param string $cluster
 * @return array
 */
function _elasticsearch_connector_check_if_index_locked($cluster, $index) {
  $locked = array();
  if (!empty($cluster)) {
    $type = 'index';
    foreach (module_implements('elasticsearch_connector_edit_lock') as $module) {
      $function = $module . '_elasticsearch_connector_edit_lock';
      $locked_result = $function($type, $cluster, $index);
      if (!empty($locked_result)) {
        $locked[] = $module;
      }
    }
  }
  return $locked;
}

/**
 * Implements hook_elasticsearch_connector_edit_lock().
 */
function elasticsearch_connector_elasticsearch_connector_edit_lock($type, $cluster, $index = NULL) {
  if ($type == 'cluster' && $cluster->cluster_id == elasticsearch_connector_get_default_connector()) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Build two dropdowns, one for the cluster and one for the indices.
 * @param array $element
 * @return arrat $element
 */
function _elasticsearch_connector_ec_index_process($element, &$form_state, $form) {
  $element['#tree'] = TRUE;
  $element_id = $element['#id'];
  $wrapper_id = $element_id . '-index-wrapper';

  // TODO: Add icon if the cluster is OK or not.
  if (empty($element['#cluster_id'])) {
    $element['cluster_id'] = array(
      '#type' => 'select',
      '#id' => $element_id . '-cluster-id',
      '#title' => t('Select cluster'),
      '#required' => $element['#required'],
      '#default_value' => isset($element['#default_value']) && is_array($element['#default_value']) && isset($element['#default_value']['cluster_id']) ? $element['#default_value']['cluster_id'] : '',
      // TODO: Allow this option to be overwritten and #value if we had such.
      '#description' => t('Select the cluster.'),
      '#ajax' => array(
        'callback' => '_elasticsearch_connector_ec_index_ajax',
        'wrapper' => $wrapper_id,
        'method' => 'replace',
        'effect' => 'fade',
      ),
    );
  }
  else {
    $element['cluster_id'] = array(
      '#type' => 'value',
      '#value' => $element['#cluster_id'],
    );
  }
  if (!isset($element['cluster_id']['#current_path'])) {
    $element['cluster_id']['#current_path'] = current_path();
  }
  if (empty($element['#skip_default_options'])) {
    $element['#only_active'] = isset($element['#only_active']) ? $element['#only_active'] : TRUE;
    $element['#empty_option'] = isset($element['#empty_option']) ? $element['#empty_option'] : TRUE;
    $clusters = elasticsearch_connector_cluster_load_all($element['#only_active'], $element['#empty_option']);
    $element['cluster_id']['#options'] = $clusters;
  }

  // TODO: We need to handle the incomming tree name if such.
  $links = $manage_indices = array();
  $index_options = array(
    '' => t('Select index'),
  );
  if (is_array($element['#value']) && !empty($element['#value']['cluster_id']) || !empty($element['#cluster_id'])) {
    $cluster_id = is_array($element['#value']) && !empty($element['#value']['cluster_id']) ? $element['#value']['cluster_id'] : $element['#cluster_id'];
    $index_options = array();
    try {
      $index_options = elasticsearch_connector_get_indices_options($cluster_id, TRUE);
    } catch (Exception $e) {
      if (!empty($element['#throw_exp'])) {
        throw $e;
      }
    }
    $links[] = array(
      'title' => t('Add index'),
      'href' => 'admin/config/elasticsearch-connector/clusters/' . $cluster_id . '/indices/add',
      'attributes' => array(
        'target' => '_blank',
        'class' => 'ec-index-dialog ec-index-dialog-add',
      ),
      'query' => array(
        'render' => 'elasticsearch-connector-dialog',
        'index_element_id' => $element_id . '-index',
        'cluster_element_id' => $element_id . '-cluster-id',
      ),
    );
    $manage_indices = array(
      'title' => t('Manage indices'),
      'href' => 'admin/config/elasticsearch-connector/clusters/' . $cluster_id . '/indices',
      'attributes' => array(
        'target' => '_blank',
        'class' => 'manage-indices',
      ),
    );
  }
  $index = '';
  if (isset($element['#value']) && is_array($element['#value']) && isset($element['#value']['index'])) {
    $index = $element['#value']['index'];
  }
  elseif (isset($element['#default_value']) && is_array($element['#default_value']) && isset($element['#default_value']['index'])) {
    $index = $element['#default_value']['index'];
  }
  if (!empty($index)) {
    $links[] = array(
      'title' => t('Edit @index index', array(
        '@index' => $index,
      )),
      'href' => 'admin/config/elasticsearch-connector/clusters/' . $cluster_id . '/indices/' . $index . '/edit',
      'attributes' => array(
        'target' => '_blank',
        'class' => 'ec-index-dialog ec-index-dialog-edit',
      ),
      'query' => array(
        'render' => 'elasticsearch-connector-dialog',
        'index_element_id' => $element_id . '-index',
        'cluster_element_id' => $element_id . '-cluster-id',
      ),
    );
  }
  if (!empty($manage_indices)) {
    $links[] = $manage_indices;
  }
  $element['index'] = array(
    '#type' => 'select',
    '#title' => t('Select index'),
    '#id' => $element_id . '-index',
    '#required' => $element['#required'],
    '#default_value' => isset($element['#default_value']) && is_array($element['#default_value']) && isset($element['#default_value']['index']) ? $element['#default_value']['index'] : '',
    '#description' => t('Select the index.'),
    '#options' => $index_options,
    '#ajax' => array(
      'callback' => '_elasticsearch_connector_ec_index_links_ajax',
      'wrapper' => $element['#id'] . '-dialog-links',
      'method' => 'replace',
      'effect' => 'fade',
    ),
    '#prefix' => '<div id="' . $wrapper_id . '">',
    '#suffix' => '<div id="' . $element['#id'] . '-dialog-links" class="dialog-links ' . $element['#id'] . '">' . theme('links__es_index_links', array(
      'links' => $links,
      'attributes' => array(
        'class' => 'index-dialog-links',
      ),
    )) . '</div></div>',
  );
  unset($element['#title']);
  $context = array(
    'form' => $form,
  );
  drupal_alter('ec_index_process', $element, $form_state, $context);
  return $element;
}

/**
 * Implements hook_page_alter().
 */
function elasticsearch_connector_page_alter(&$page) {
  if (elasticsearch_connector_in_dialog()) {
    unset($page['page_top']);
    unset($page['page_bottom']);
    $page['#theme'] = 'elasticsearch_connector_page';
  }
}

/**
 * Check if we are in a references dialog.
 * @return boolean if we are in a dialog.
 */
function elasticsearch_connector_in_dialog() {
  return isset($_GET['render']) && $_GET['render'] == 'elasticsearch-connector-dialog';
}

/**
 * Check if we should close the dialog upon submition.
 */
function elasticsearch_connector_close_on_submit() {
  return !isset($_GET['closeonsubmit']) || $_GET['closeonsubmit'];
}

/**
 * Sets our destination parameter so that the dialog will close itself after
 * redirect is completed.
 */
function elasticsearch_connector_close_on_redirect($cluster_id, $index_name) {

  // We use $_GET['destination'] since that overrides anything that happens
  // in the form. It is a hack, but it is very effective, since we don't have
  // to be worried about getting overrun by other form submit handlers.
  $_GET['destination'] = 'elasticsearch-connector-dialog/redirect/' . $cluster_id . '/' . $index_name . '?elasticsearch-connector-dialog-close=1&render=elasticsearch-connector-dialog';
  if (isset($_GET['cluster_element_id'])) {
    $_GET['destination'] .= '&index_element_id=' . $_GET['index_element_id'];
  }
  if (isset($_GET['cluster_element_id'])) {
    $_GET['destination'] .= '&cluster_element_id=' . $_GET['cluster_element_id'];
  }
}

/**
 * Page callback for our redirect page.
 */
function elasticsearch_connector_redirect_page($cluster, $index_name) {

  // Add appropriate javascript that will be used by the parent page to
  // fill in the required data.
  if (elasticsearch_connector_in_dialog() && isset($_GET['elasticsearch-connector-dialog-close'])) {
    drupal_add_js(drupal_get_path('module', 'elasticsearch_connector') . '/js/ec-index-child.js');
    drupal_add_js(array(
      'elasticsearch_connector' => array(
        'dialog' => array(
          'cluster_id' => $cluster->cluster_id,
          'index_name' => $index_name,
          'index_element_id' => (string) $_GET['index_element_id'],
          'cluster_element_id' => (string) $_GET['cluster_element_id'],
        ),
      ),
    ), 'setting');
  }
  return '';
}

/**
 * ec_index element ajax callback.
 * @param array $form
 * @param array $form_state
 */
function _elasticsearch_connector_ec_index_ajax($form, $form_state) {
  $element_name = $form_state['triggering_element']['#name'];
  $parents = $form_state['triggering_element']['#parents'];
  $search_key = array_search('cluster_id', $parents);
  $parents[$search_key] = 'index';
  $index_element = drupal_array_get_nested_value($form, $parents);
  return $index_element;
}
function _elasticsearch_connector_ec_index_links_ajax($form, $form_state) {
  $element_name = $form_state['triggering_element']['#name'];
  $parents = $form_state['triggering_element']['#parents'];
  $index_element = drupal_array_get_nested_value($form, $parents);
  return $index_element['#suffix'];
}

/**
 * Return the main path of the elasticsearch connector module.
 * @return string
 */
function elasticsearch_connector_main_settings_path() {
  $settings_path = 'admin/config/elasticsearch-connector';
  return $settings_path;
}

/**
 * Return the basic breadcrumb for the connector module.
 * @param array $links
 */
function elasticsearch_connector_set_breadcrumb($links = array()) {
  $breadcrumb = array(
    l(t('Home'), '<front>'),
    l(t('Administration'), 'admin'),
    l(t('Configuration'), 'admin/config'),
    l(t('Elasticsearch connector'), elasticsearch_connector_main_settings_path()),
  );
  if (!empty($links)) {
    foreach ($links as $link) {
      $breadcrumb[] = $link;
    }
  }
  drupal_set_breadcrumb($breadcrumb);
}

/**
 * Save a cluster configuration object.
 *
 * @param stdclass $cluster
 *   The ElasticSearch cluster configuration object. This object should be
 *   loaded with elasticsearch_connector_cluster_load() or
 *   elasticsearch_connector_cluster_load_all()--otherwise, it is assumed to be
 *   a new configuration.
 */
function elasticsearch_connector_cluster_save($cluster) {
  ctools_include('export');
  $cluster = (object) $cluster;
  $cluster->options = serialize($cluster->options);
  if (isset($cluster->export_type) && $cluster->export_type & EXPORT_IN_DATABASE) {

    // Record exists in the database.
    $result = drupal_write_record('elasticsearch_connector_cluster', $cluster, 'cluster_id');
  }
  else {

    // Record is new, or exists only in code.
    $result = drupal_write_record('elasticsearch_connector_cluster', $cluster);
  }
}

/**
 * Delete an ElasticSearch cluster.
 *
 * @param stdclass $cluster
 *   A cluster configuration object.
 */
function elasticsearch_connector_cluster_delete($cluster) {
  db_delete('elasticsearch_connector_cluster')
    ->condition('cluster_id', $cluster->cluster_id)
    ->execute();
}

/**
 * Get the indeces based on cluster id.
 * @param string $cluster_id
 * @return array Indices
 */
function elasticsearch_connector_get_indices_options($cluster_id, $empty_option = FALSE) {
  $result = array();
  $client = elasticsearch_connector_get_client_by_id($cluster_id);
  if ($client) {
    $indices = $client
      ->indices()
      ->stats();
    drupal_alter('elasticsearch_connector_indices', $indices);
    if ($empty_option) {
      $result[''] = t('Select index');
    }
    if (!empty($indices['indices'])) {
      foreach ($indices['indices'] as $index_name => $index_info) {

        // TODO: Check index status if such e.g. index closed or s.o.
        $result[$index_name] = $index_name;
      }
    }
  }
  return $result;
}

/**
 * Load a cluster object from the database.
 *
 * @see ctools_export_load_object().
 *
 * @param string $cluster_id
 * @return object $cluster
 */
function elasticsearch_connector_cluster_load($cluster_id) {
  ctools_include('export');
  $result = ctools_export_load_object('elasticsearch_connector_cluster', 'names', array(
    $cluster_id,
  ));
  drupal_alter('elasticsearch_connector_clusters', $result);
  if (isset($result[$cluster_id])) {
    if (isset($result[$cluster_id]->options) && !is_array($result[$cluster_id]->options)) {
      $result[$cluster_id]->options = unserialize($result[$cluster_id]->options);
    }
    return $result[$cluster_id];
  }
}

/**
 * Check if the index name has been passed correctly.
 *
 * @param string $index_name
 * @return string boolean
 */
function elasticsearch_connector_index_valid_load($index_name) {
  if (preg_match('/^[a-z][a-z0-9_]*$/i', $index_name)) {
    return $index_name;
  }
  return FALSE;
}

/**
 * Load all cluster objects.
 *
 * @param bool $include_inactive
 *   Included inactive clusters. Defaults to true.
 *
 * @return array
 *   An array of ElasticSearch connection cluster objects.
 */
function elasticsearch_connector_clusters($include_inactive = TRUE) {
  ctools_include('export');
  $clusters = ctools_export_load_object('elasticsearch_connector_cluster', 'all');

  // Filter out inactive clusters.
  foreach ($clusters as $cluster_id => $cluster) {

    // Unserialize the options if they are loaded from database.
    // If they are loaded from exported object it will be array.
    if (isset($cluster->options) && !is_array($cluster->options)) {
      $cluster->options = unserialize($cluster->options);
    }
    if (!$include_inactive && !$cluster->status) {
      unset($clusters[$cluster_id]);
    }
  }
  drupal_alter('elasticsearch_connector_clusters', $clusters);
  return $clusters;
}

/**
 * Prepare list of all clusters by status.
 *
 * @param bool $active
 *   Flag to return clusters with certain status.
 * @param bool $add_empty_option
 *   Flag whether to add empty option to the list.
 *
 * @return array
 *   An array of ElasticSearch connection cluster names, indexed by id.
 */
function elasticsearch_connector_cluster_load_all($active = TRUE, $add_empty_option = FALSE) {
  $clusters = elasticsearch_connector_clusters($active);
  $options = array();
  if ($add_empty_option) {
    $options[''] = t('None');
  }
  foreach ($clusters as $cluster) {
    $options[$cluster->cluster_id] = $cluster->name;
  }
  return $options;
}

/**
 * Get the default connector (cluster) used for elasticsearch.
 *
 * @return string
 */
function elasticsearch_connector_get_default_connector() {
  return variable_get('elasticsearch_connector_get_default_connector', '');
}

/**
 * Set the default connector (cluster) used for elasticsearch.
 *
 * @return string
 */
function elasticsearch_connector_set_default_connector($connection) {
  return variable_set('elasticsearch_connector_get_default_connector', $connection);
}

/**
 * Return the cluster object based on Cluster ID.
 *
 * @param string $cluster_id
 * @param boolean
 * @return \Elasticsearch\Client $client
 */
function elasticsearch_connector_get_client_by_id($cluster_id = NULL, $default_fallback = NULL) {
  if (!isset($cluster_id) && !empty($default_fallback)) {
    $cluster_id = elasticsearch_connector_get_default_connector();
  }
  if (!empty($cluster_id)) {
    $client = FALSE;
    $cluster = elasticsearch_connector_cluster_load($cluster_id);
    if ($cluster) {
      $client = elasticsearch_connector_load_library($cluster);
    }
  }
  return $client;
}

/**
 *
 * @param string $url
 * @return
 */
function elasticsearch_connector_load_library($cluster) {
  static $clients;
  if (!isset($clients[$cluster->cluster_id])) {
    $clients[$cluster->cluster_id] = FALSE;

    // TODO: Handle cluster connection. This should be accomplished if the setting is enabled.
    // If enabled, discover all the nodes in the cluster initialize the Pool connection.
    if (valid_url($cluster->url)) {
      $options = array(
        'hosts' => array(
          $cluster->url,
        ),
        'guzzleOptions' => array(
          'curl.options' => array(
            CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
            CURLOPT_CONNECTTIMEOUT => !empty($cluster->options['timeout']) ? $cluster->options['timeout'] : ELASTICSEARCH_CONNECTOR_DEFAULT_TIMEOUT,
          ),
        ),
      );
      if (!empty($cluster->options['use_authentication'])) {
        $options['connectionParams'] = array(
          'auth' => array(
            $cluster->options['username'],
            $cluster->options['password'],
            $cluster->options['authentication_type'],
          ),
        );
      }
      try {
        if (!class_exists('\\Elasticsearch\\Client') && module_exists('composer_manager')) {

          // If the class doesn't exists try to explicitly load the classes!
          // The issues is in update.php where search api call this function, but the function
          // has not been invoked.
          composer_manager_register_autoloader();
        }
        if (!class_exists('\\Elasticsearch\\Client')) {

          // No library available!
          return FALSE;
        }
        drupal_alter('elasticsearch_connector_load_library_options', $options, $cluster);
        $clients[$cluster->cluster_id] = new \Elasticsearch\Client($options);
      } catch (Exception $e) {
        drupal_set_message($e
          ->getMessage(), 'error');
      }
    }
  }
  return $clients[$cluster->cluster_id];
}

/**
 * Check if the status is OK.
 * @param array $status
 * @return bool
 */
function elasticsearch_connector_check_status($status) {
  if (is_array($status) && $status['status'] == ELASTICSEARCH_CONNECTOR_CLUSTER_STATUS_OK) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Check if the REST response is successful and with status code 200.
 * @param array $response
 * @return boolean
 */
function elasticsearch_connector_check_response_ack($response) {
  if (is_array($response) && !empty($response['acknowledged'])) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Return cluster info.
 * @param object
 *    $cluster - The cluster object.
 * @param boolean
 *    $enable_messages - Set error message if the get status function throw exception.
 * @return return array
 *    Return the info for the cluster:
 *      'info'
 *      'state'
 *      'health'
 */
function elasticsearch_connector_get_cluster_info($cluster, $enable_messages = FALSE) {
  $result = FALSE;
  $client = elasticsearch_connector_load_library($cluster);
  if (!empty($client)) {
    try {
      $info = $client
        ->info();
      $result['client'] = $client;
      $result['info'] = $result['state'] = $result['health'] = $result['stats'] = array();
      if (elasticsearch_connector_check_status($info)) {
        $result['info'] = $info;
        $result['state'] = $client
          ->cluster()
          ->state();
        $result['health'] = $client
          ->cluster()
          ->health();
        $result['stats'] = $client
          ->nodes()
          ->stats();
      }
    } catch (Exception $e) {
      if ($enable_messages) {
        drupal_set_message($e
          ->getMessage(), 'error');
      }
    }
  }
  return $result;
}

/**
 * Get the nodes stats from elasticsearch server.
 * @param \Elasticsearch\Client $client
 * @return array
 */
function elasticsearch_connector_get_cluster_nodes_stat(\Elasticsearch\Client $client) {
  try {
    return $client
      ->nodes()
      ->stats();
  } catch (Exception $e) {
    drupal_set_message($e
      ->getMessage(), 'error');
  }
}

/**
 * Check if a specific plugin exists on all nodes.
 * TODO: This should be changed to check all data Nodes only but for now lets check all of them.
 *
 * Read more about plugins system here:
 * http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-plugins.html
 *
 * @param \Elasticsearch\Client $client
 * @param string $plugin
 * @return boolean
 */
function elasticsearch_connector_check_plugin_exists(\Elasticsearch\Client $client, $plugin_name) {
  $nodes_plugins = array();
  $result = FALSE;
  try {
    $plugins = $client
      ->nodes()
      ->info(array(
      'node_id' => '_all',
    ));
    foreach ($plugins['nodes'] as $elastic_node_id => $elastic_node) {
      $nodes_plugins[$elastic_node_id][$plugin_name] = FALSE;
      foreach ($elastic_node['plugins'] as $plugin) {
        if ($plugin['name'] == $plugin_name) {
          $nodes_plugins[$elastic_node_id][$plugin_name] = TRUE;
        }
      }
      if (empty($nodes_plugins[$elastic_node_id][$plugin_name])) {
        $result = FALSE;
        break;
      }
      else {
        $result = TRUE;
      }
    }
    return $result;
  } catch (Exception $e) {
    drupal_set_message($e
      ->getMessage(), 'error');
    return FALSE;
  }
}

/**
 * Process variables for references_dialog_page.
 */
function template_process_elasticsearch_connector_page(&$variables) {

  // Generate messages last in order to capture as many as possible for the
  // current page.
  if (!isset($variables['messages'])) {
    $variables['messages'] = $variables['page']['#show_messages'] ? theme('status_messages') : '';
  }
}

/**
 * Helper function you can use with #element_validate of any form element
 * you want to be validated as Elasticsearch TTL setting.
 *
 * @param array $element
 * @param array $form_state
 * @param array $form
 * @return void
 */
function _elasticsearch_connector_validate_ttl_field($element, &$form_state, $form) {
  if (!empty($element['#value']) && !preg_match('/^([\\d]+)(d|m|h|ms|w)$/', $element['#value'])) {
    form_error($element, t('Invalid elasticsearch TTL value. Please use the proper syntax e.g. 1d (d (days), m (minutes), h (hours), ms (milliseconds) or w (weeks)).'));
  }
}

/**
 * Helper function you can use with #element_validate of any form element
 * you want to be validated as Elasticsearch type field.
 *
 * @param array $element
 * @param array $form_state
 * @param array $form
 * @return void
 */
function elasticsearch_connector_validate_type_field($element, &$form_state, $form) {
  if (!empty($element['#value']) && !preg_match('/^[a-z][a-z0-9_]*$/i', $element['#value'])) {
    form_error($element, t('Enter a type name that begins with a letter and contains only letters, numbers, and underscores.'));
  }
}

Functions

Namesort descending Description
elasticsearch_connector_check_plugin_exists Check if a specific plugin exists on all nodes. TODO: This should be changed to check all data Nodes only but for now lets check all of them.
elasticsearch_connector_check_response_ack Check if the REST response is successful and with status code 200.
elasticsearch_connector_check_status Check if the status is OK.
elasticsearch_connector_close_on_redirect Sets our destination parameter so that the dialog will close itself after redirect is completed.
elasticsearch_connector_close_on_submit Check if we should close the dialog upon submition.
elasticsearch_connector_clusters Load all cluster objects.
elasticsearch_connector_cluster_delete Delete an ElasticSearch cluster.
elasticsearch_connector_cluster_load Load a cluster object from the database.
elasticsearch_connector_cluster_load_all Prepare list of all clusters by status.
elasticsearch_connector_cluster_save Save a cluster configuration object.
elasticsearch_connector_cron Implements hook_cron()
elasticsearch_connector_elasticsearch_connector_edit_lock Implements hook_elasticsearch_connector_edit_lock().
elasticsearch_connector_element_info Implements hook_element_info()
elasticsearch_connector_get_client_by_id Return the cluster object based on Cluster ID.
elasticsearch_connector_get_cluster_info Return cluster info.
elasticsearch_connector_get_cluster_nodes_stat Get the nodes stats from elasticsearch server.
elasticsearch_connector_get_default_connector Get the default connector (cluster) used for elasticsearch.
elasticsearch_connector_get_indices_options Get the indeces based on cluster id.
elasticsearch_connector_help Implements hook_help().
elasticsearch_connector_index_load Check if the index is valid and compare it with the indexes comming from Elasticsearch.
elasticsearch_connector_index_valid_load Check if the index name has been passed correctly.
elasticsearch_connector_in_dialog Check if we are in a references dialog.
elasticsearch_connector_load_library
elasticsearch_connector_main_settings_path Return the main path of the elasticsearch connector module.
elasticsearch_connector_menu Implements hook_menu().
elasticsearch_connector_page_alter Implements hook_page_alter().
elasticsearch_connector_permission Implements hook_permission().
elasticsearch_connector_redirect_page Page callback for our redirect page.
elasticsearch_connector_set_breadcrumb Return the basic breadcrumb for the connector module.
elasticsearch_connector_set_default_connector Set the default connector (cluster) used for elasticsearch.
elasticsearch_connector_theme Implements hook_theme().
elasticsearch_connector_validate_type_field Helper function you can use with #element_validate of any form element you want to be validated as Elasticsearch type field.
template_process_elasticsearch_connector_page Process variables for references_dialog_page.
_elasticsearch_connector_check_if_cluster_locked Check if some of the modules locked a major changes on the cluster settings and deletion. Invoke the hooks similar to the module_invoke.
_elasticsearch_connector_check_if_index_locked Check if some of the modules locked a major changes on the cluster settings and deletion.
_elasticsearch_connector_ec_clusters_process Process the ec_cluster element type.
_elasticsearch_connector_ec_index_ajax ec_index element ajax callback.
_elasticsearch_connector_ec_index_attached Attach required javascript for the ec_index element.
_elasticsearch_connector_ec_index_links_ajax
_elasticsearch_connector_ec_index_process Build two dropdowns, one for the cluster and one for the indices.
_elasticsearch_connector_validate_ttl_field Helper function you can use with #element_validate of any form element you want to be validated as Elasticsearch TTL setting.

Constants

Namesort descending Description
ELASTICSEARCH_CONNECTOR_CLUSTER_STATUS_OK
ELASTICSEARCH_CONNECTOR_DEFAULT_TIMEOUT
ELASTICSEARCH_CONNECTOR_STATUS_ACTIVE
ELASTICSEARCH_CONNECTOR_STATUS_INACTIVE @file This module provide an interface to connecting to the elasticsearch cluster and implementing the official Elasticsearch library.