You are here

hosting_server.module in Hostmaster (Aegir) 6

File

modules/hosting/server/hosting_server.module
View source
<?php

include_once "hosting_server.service.inc";
include_once "hosting.ip.inc";

/**
 * Implementation of hook_node_info().
 */
function hosting_server_node_info() {
  $types["server"] = array(
    "type" => 'server',
    "name" => 'Server',
    "module" => 'hosting_server',
    "has_title" => true,
    "title_label" => t('Host name'),
    "description" => hosting_node_help("server"),
    "has_body" => 0,
    "body_label" => '',
    "min_word_count" => 0,
  );
  return $types;
}

/**
 * Implementation of hook_menu().
 */
function hosting_server_menu() {
  $items = array();
  $items['hosting/servers'] = array(
    'title' => 'Servers',
    'description' => 'List of servers',
    'page callback' => 'hosting_server_listing',
    'access arguments' => array(
      'view server',
    ),
  );
  return $items;
}

/**
 * Implementation of hook_hosting_feature().
 */
function hosting_server_hosting_feature() {
  $features['server'] = array(
    'title' => t('Server administration'),
    'description' => t('Manage multiple servers.'),
    'status' => HOSTING_FEATURE_ENABLED,
    'node' => 'server',
  );
  return $features;
}

/**
 * Implementation of hook_hosting_tasks()
 */
function hosting_server_hosting_tasks() {
  $tasks = array();
  $tasks['server']['verify'] = array(
    'title' => t('Verify'),
    'description' => t('Verify that the server is correctly installed and working.'),
    'weight' => 10,
    'provision_save' => TRUE,
  );

  /**
  * @TODO: support various other tasks like Delete
   $tasks['server']['delete'] = array(
     'title' => t('Delete'),
     'description' => t('Delete the server.'),
   );
  */
  return $tasks;
}

/**
 * Implementation of hook_perm()
 */
function hosting_server_perm() {
  return array(
    'create server',
    'view server',
    'edit server',
    'delete server',
  );
}

/**
 * Implementation of hook_access()
 */
function hosting_server_access($op, $node, $account) {
  if (!hosting_feature('server')) {
    return FALSE;
  }
  switch ($op) {
    case 'create':
      return user_access('create server', $account);
    case 'view':
      return user_access('view server', $account);
    case 'update':
      return user_access('edit server', $account);
    case 'delete':
      return user_access('delete server', $account);
  }
}

/**
 * Return an associative array of services enabled on this system.
 */
function hosting_server_services() {
  static $services;
  if (!isset($services)) {
    $services = module_invoke_all('hosting_service_type');
    foreach (module_implements('hosting_service') as $module) {
      foreach (module_invoke($module, 'hosting_service') as $service => $service_type) {
        module_load_include('service.inc', $module);
        $services[$service_type]['types'][$service] = 'hostingService_' . $service_type . '_' . $service;
      }
    }
  }
  return $services;
}

/**
 * Factory method for generating new instance of a service class.
 */
function hosting_services_new_object($name, $type, $node, $values = null) {
  $services = hosting_server_services();
  $class = $services[$name]['types'][$type];
  if (!$class) {
    return;
  }
  return new $class($node, $values);
}

/**
 * Add a service to an existing server node.
 */
function hosting_services_add(&$node, $name, $type, $values = array()) {
  $values['available'] = isset($values['available']) ? $values['available'] : 1;
  if ($service_object = hosting_services_new_object($name, $type, $node, $values)) {
    $node->services[$name] = $service_object;
  }
}

/**
 * Initializes the service objects associated with a server node object.
 *
 * @param
 *   $node The server node object.
 *
 * @return
 *   The server node object with an additional 'services' property. The
 *   services property is an associative array with the name of the service
 *   as key, and the service object as value.
 */
function hosting_server_init_services(&$node) {
  $node->services = array();
  $result = db_query("SELECT service, type FROM {hosting_service} WHERE vid=%d AND available=1", $node->vid);
  while ($record = db_fetch_object($result)) {
    $name = $record->service;
    if ($service_object = hosting_services_new_object($name, $record->type, $node)) {
      $node->services[$name] = $service_object;
    }
  }
  return $node;
}

/**
 * Translate a server form submission into populated server objects on the node.*/
function hosting_server_services_from_post(&$node) {
  if (isset($node->services) && is_array($node->services)) {
    foreach (hosting_server_services() as $name => $data) {
      if (isset($node->services[$name]) && is_array($node->services[$name])) {
        if ($node->services[$name]['type'] !== 'null') {
          if ($service_object = hosting_services_new_object($name, $node->services[$name]['type'], $node, $node->services[$name][$node->services[$name]['type']])) {
            $node->services[$name] = hosting_services_new_object($name, $node->services[$name]['type'], $node, $node->services[$name][$node->services[$name]['type']]);
            $node->services[$name]->available = TRUE;
          }
        }
        else {
          unset($node->services[$name]);
        }
      }
    }
  }
}

/**
 * Invoke a method on all enabled services.
 */
function hosting_server_invoke_services(&$node, $method, &$arg1 = null, &$arg2 = null, &$arg3 = null, &$arg4 = null) {
  $return = array();
  foreach ($node->services as $name => $service) {
    $result = $service
      ->{$method}($arg1, $arg2, $arg3, $arg4);
    if (isset($result) && is_array($result)) {
      $return = array_merge_recursive($return, $result);
    }
    else {
      if (isset($result)) {
        $return[] = $result;
      }
    }
  }
  return $return;
}

/**
 * Implementation of hook_form().
 */
function hosting_server_form(&$node) {
  drupal_add_js(drupal_get_path('module', 'hosting_server') . '/hosting_server.js');
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Server hostname'),
    '#required' => TRUE,
    '#default_value' => $node->title,
    '#description' => t('The host name of the server. This is the address that will be used by Hostmaster to connect to the server to issue commands. It is to resolve to the internal network, if you have such a separation.<br /><strong>Be careful when changing the node title, it is used to create the SQL users if the IP address, below, is not set.</strong>'),
    '#weight' => -10,
  );
  $form['ip_addresses'] = array(
    '#type' => 'textarea',
    '#title' => t('Ip addresses'),
    '#description' => t('A list of IP addresses this server is publicly available under, one per line. If none is specified, a DNS lookup will be performed based on the server hostname above. <br /><strong>This should point to the public network, if you have such a separation.</strong>'),
    '#default_value' => implode("\n", _hosting_ip_list($node->ip_addresses)),
    '#weight' => -9,
  );
  $form['services'] = array(
    '#weight' => -5,
    '#tree' => TRUE,
  );
  if (!$node->nid) {
    $node->services = array();
  }
  foreach (hosting_server_services() as $name => $service) {
    $form['services'][$name] = array(
      '#type' => 'fieldset',
      '#title' => $service['title'],
      '#prefix' => '<div class="hosting-service-form">',
      '#suffix' => '</div>',
      '#weight' => isset($service['weight']) ? $service['weight'] : 0,
    );
    $form['services'][$name]['type'] = array(
      '#type' => 'radios',
      '#weight' => -99,
      '#options' => array_merge(array(
        'null' => 'None',
      ), drupal_map_assoc(array_keys($service['types']))),
      '#default_value' => isset($node->services[$name]->available) && $node->services[$name]->available ? $node->services[$name]->type : 'null',
    );

    // Service-specific configuration.
    foreach ($service['types'] as $type => $class) {
      $form['services'][$name][$type] = array(
        '#prefix' => '<div id="provision-service-settings-' . $name . '-' . $type . '" class="provision-service-settings-' . $name . '">',
        '#suffix' => '</div>',
      );
      if (isset($node->services[$name]) && $node->services[$name]->type === $type) {
        $node->services[$name]
          ->form($form['services'][$name][$type]);
      }
      else {
        $service_object = hosting_services_new_object($name, $type, $node);
        $service_object
          ->form($form['services'][$name][$type]);
      }
    }
  }
  return $form;
}

/**
 * Implementation of hook_presave()
 *
 * We resolve the server name to IP addresses if none has been given
 * by the operator. we also fire up the regular services hooks.
 */
function hosting_nodeapi_server_presave(&$node) {
  if (empty($node->ip_addresses)) {

    // this returns an array or FALSE
    $ips = gethostbynamel($node->title);
    if ($ips) {
      drupal_set_message(t('Initialized the IP to %ip based on hostname %name. If an HTTP service is enabled, this will be used to create database grants so make sure it is the right address, as seen from the database server.', array(
        '%ip' => join(',', $ips),
        '%name' => $node->title,
      )), 'message');
      $node->ip_addresses = $ips;
    }
    else {
      drupal_set_message(t("Could not resolve IP address of server %name, not automatically setting IP address. DNS may fail.", array(
        '%name' => $node->title,
      )));
    }
  }
  hosting_server_services_from_post($node);
}

/**
 * Implementation of hook_validate().
 */
function hosting_server_validate(&$node, &$form) {
  hosting_ip_validate($node);
  if (!_hosting_valid_fqdn($node->title)) {
    form_set_error('title', t('Invalid hostname. Use a proper hostname or an IP address.'));
  }
  if (!$node->nid && db_result(db_query("SELECT COUNT(nid) FROM {node} WHERE type='server' AND title='%s'", $node->title))) {
    form_set_error('title', t("The hostname you have specified is already in use."));
  }
  hosting_server_services_from_post($node);
  hosting_server_invoke_services($node, 'validate', $node, $form);
}

/**
 * Implementation of hook_update().
 *
 * As an existing node is being updated in the database, we need to do our own
 * database updates.
 */
function hosting_server_update($node) {

  // if this is a new node or we're adding a new revision,
  if (!empty($node->revision)) {
    hosting_server_insert($node);
  }
  else {
    hosting_server_invoke_services($node, 'save', $node);
    hosting_server_invoke_services($node, 'update', $node);

    // Remove disabled services
    foreach (array_diff(array_keys(hosting_server_services()), array_keys($node->services)) as $name) {
      db_query("DELETE FROM {hosting_service} WHERE service = '%s' AND nid = %d", $name, $node->nid);
    }
  }
  hosting_ip_save($node, TRUE);
  if (!$node->no_verify) {
    hosting_add_task($node->nid, 'verify');
  }
}

/**
 * Implementation of hook_insert().
 */
function hosting_server_insert($node) {
  if (!$node->revision) {
    $hosting_name = $node->hosting_name ? $node->hosting_name : 'server_' . preg_replace("/[!\\W\\.\\-]/", "", $node->title);
    hosting_context_register($node->nid, $hosting_name);
  }
  db_query("INSERT INTO {hosting_server} (vid, nid) \n        VALUES (%d, %d)", $node->vid, $node->nid);
  hosting_ip_save($node, FALSE);
  hosting_server_invoke_services($node, 'save', $node);
  hosting_server_invoke_services($node, 'insert', $node);
  if (!$node->no_verify) {
    hosting_add_task($node->nid, 'verify');
  }
}

/**
 * Implementation of hook_delete_revision().
 */
function hosting_nodeapi_server_delete_revision(&$node) {
  db_query('DELETE FROM {hosting_server} WHERE vid = %d', $node->vid);
  hosting_ip_delete_revision($node);
  hosting_server_invoke_services($node, 'delete_revision');
}

/**
 * Implementation of hook_delete().
 */
function hosting_server_delete($node) {
  db_query('DELETE FROM {hosting_server} WHERE nid = %d', $node->nid);
  hosting_ip_delete($node);
  hosting_context_delete($node->nid);
  hosting_server_invoke_services($node, 'delete_revision');
}

/**
 * Implementation of hook_load().
 */
function hosting_server_load($node) {
  hosting_server_init_services($node);
  hosting_server_invoke_services($node, 'load');
  $additions = new stdClass();
  $additions->services = $node->services;
  $additions->ip_addresses = hosting_ip_load($node);
  return $additions;
}

/**
 * Menu callback; loads a hosting_task_server node
 *
 * @arg $arg a numeric nid
 */
function hosting_server_node_load($arg) {
  if (!is_numeric($arg)) {
    return FALSE;
  }
  if ($node = node_load($arg)) {
    if ($node->type === 'server') {
      return $node;
    }
  }
  return FALSE;
}

/**
 * Implementation of hook_view().
 */
function hosting_server_view(&$node, $teaser = FALSE, $page = FALSE) {
  hosting_set_breadcrumb($node);
  $node = node_prepare($node, $teaser);
  $services = hosting_server_services();
  $node->content['info'] = array(
    '#prefix' => '<div id="hosting-server-info">',
    '#suffix' => '</div>',
  );
  hosting_ip_view($node);
  if (isset($node->content->info->ip_addresses['#weight'])) {
    $node->content->info->ip_addresses['#weight'] = -30;
  }
  foreach ($node->services as $name => $service) {
    $node->content['info'][$name] = array(
      '#prefix' => '<div class="hosting-service-info hosting-service-' . $name . '-info">',
      '#suffix' => '</div>',
    );
    if (isset($services[$name]['weight'])) {
      $node->content['info'][$name]['#weight'] = $services[$name]['weight'];
    }
    $title = $services[$name]['title'] . ' : ' . $service->type;
    $node->content['info'][$name]['title'] = array(
      '#value' => '<h3>' . $title . '</h3>',
      '#weight' => -20,
    );
    $service
      ->view($node->content['info'][$name]);
  }
  if ($page) {

    // Task list
    if ($node->nid) {
      $node->content['tasks_view'] = array(
        '#type' => 'item',
        '#value' => hosting_task_table($node),
        '#prefix' => '<div id="hosting-task-list">',
        '#suffix' => '</div>',
        '#weight' => 10,
      );
      $settings['hostingTaskRefresh'] = array(
        'nid' => $node->nid,
        'changed' => $node->changed,
      );
      drupal_add_js($settings, 'setting');
      drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
    }
  }
  return $node;
}

/**
 * Output a table of servers with their enabled services.
 */
function hosting_server_listing() {
  $services = hosting_server_services();
  $header = array(
    t('Server'),
  );
  foreach ($services as $service => $data) {
    if ($service !== 'server') {
      $header[] = $data['title'];
    }
  }
  $result = db_query(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE n.type='server' AND n.status = 1 ORDER BY n.title"));
  $rows = array();
  while ($obj = db_fetch_object($result)) {
    $row = array();
    $server = node_load($obj->nid);
    $row[] = l($server->title, 'node/' . $server->nid);
    foreach ($services as $service => $data) {
      if ($service !== 'server') {
        if ($server->services[$service]->available) {
          $row[] = array(
            'class' => 'hosting-service-available',
            'data' => $server->services[$service]->type,
          );
        }
        else {
          $row[] = array(
            'class' => 'hosting-service-unavailable',
            'data' => t('No'),
          );
        }
      }
    }
    $rows[] = $row;
  }
  return theme('table', $header, $rows, array(
    'class' => 'hosting-table',
  ));
}

/**
 * Get servers providing a service.
 *
 * @param $service
 *   Service type string, like 'http' or 'db'
 *
 * @return
 *   An array of enabled servers, keys are the nid's of the nodes representing
 *   them, values are the titles of the servers.
 *
 * @see hook_hosting_servers_titles_alter()
 */
function hosting_get_servers($service) {
  $return = array();
  $result = db_query("SELECT n.nid, n.title FROM {node} n INNER JOIN {hosting_service} s ON n.vid = s.vid AND s.available = 1 AND s.service = '%s'", $service);
  while ($server = db_fetch_object($result)) {
    $return[$server->nid] = $server->title;
  }
  drupal_alter('hosting_servers_titles', $return, $service);
  return $return;
}

Functions

Namesort descending Description
hosting_get_servers Get servers providing a service.
hosting_nodeapi_server_delete_revision Implementation of hook_delete_revision().
hosting_nodeapi_server_presave Implementation of hook_presave()
hosting_server_access Implementation of hook_access()
hosting_server_delete Implementation of hook_delete().
hosting_server_form Implementation of hook_form().
hosting_server_hosting_feature Implementation of hook_hosting_feature().
hosting_server_hosting_tasks Implementation of hook_hosting_tasks()
hosting_server_init_services Initializes the service objects associated with a server node object.
hosting_server_insert Implementation of hook_insert().
hosting_server_invoke_services Invoke a method on all enabled services.
hosting_server_listing Output a table of servers with their enabled services.
hosting_server_load Implementation of hook_load().
hosting_server_menu Implementation of hook_menu().
hosting_server_node_info Implementation of hook_node_info().
hosting_server_node_load Menu callback; loads a hosting_task_server node
hosting_server_perm Implementation of hook_perm()
hosting_server_services Return an associative array of services enabled on this system.
hosting_server_services_from_post Translate a server form submission into populated server objects on the node.
hosting_server_update Implementation of hook_update().
hosting_server_validate Implementation of hook_validate().
hosting_server_view Implementation of hook_view().
hosting_services_add Add a service to an existing server node.
hosting_services_new_object Factory method for generating new instance of a service class.