Attached is code for a drush command that will enqueue Aegir tasks for enabled sites, as if it had been done via the Aegir user interface.
If this could be part of Aegir that would be pretty exciting so please review it and tell me if there is an interest and if so what changes are required or desired.
thanks,
Ken Wolf

CommentFileSizeAuthor
#3 aegir_task_enqueue.patch7.03 KBjoshk
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Anonymous’s picture

Category: task » feature

Hi Ken,

Your patch didn't upload, can you upload it again as a comment to this issue?

Ken Wolf’s picture

<?php
// $Id: $

/**
* @file
* This file contains logic that enables drush to enqueue Aegir tasks.
*
* It implements the drush command 'task-enqueue'
*/

/**
* Implementation of hook_drush_help().
*
* This function is called whenever a drush user calls
* 'drush help '
*
* @param
* A string with the help section (prepend with 'drush:')
*
* @return
* A string with the help text for your command.
*/
function bw_aegir_task_enqueue_drush_help($section) {
switch ($section) {
case 'drush:task-enqueue':
return 'Enqueue Aegir site tasks as if requested via Aegir UI.';
}
}

/**
* Implementation of hook_drush_command()
*
* @return drush command data
*/
function bw_aegir_task_enqueue_drush_command() {
$items = array();
$items['task-enqueue'] = array(
'description' => 'Enqueue Aegir site tasks as if requested via Aegir UI.',
'arguments' => array(
'task' =>
'Required. This is the first parameter. '.
'The name of a site task that is selectable in the Aegir UI, '.
'that does not require parameters (in the Aegir UI)'.
"and that may be done on an 'enabled' site".
' ',
'site' =>
'Required. This is the second parameter. '.
"The word 'all' or the name of a site (site folder). ".
"a value of 'all' will enqueue the task on all the client sites. ".
' ',
'additiona_sites' =>
"Optional. The names of sites (site folders). ",
),
'examples' => array(
"drush --uri='http://my_aegir' task-enqueue 'backup' 'site1' 'site2'" =>
'Enqueues sites named site1 and site2 to be backed up, '.
'exactly as if it was done from the Aegir UI.',
),
'options' => array(
'--uri' => 'Required. Identifies the URI of the Aegir drupal site to use'.
' ',
'--root' => 'Optional. Identifies the Drupal root directory to use. '.
'When this is not set drush defaults to the current folder.',
),
'aliases' => array('enq'),
);
return $items;
}

/**
* Callback for the task_enqueue drush command.
*/
function drush_bw_aegir_task_enqueue_task_enqueue() {

drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_FULL);

$args = func_get_args();

if (bw_aegir_task_enqueue_validate_request(count($args), $args[0]) != TRUE) {
return;
}

$task = $args[0];
unset($args[0]);

if ($args[0] == 'all') {
$sites = bw_aegir_task_enqueue_get_sites();
}
else {
$sites = array();
foreach ($args as $sitename) {
$site = hosting_get_site_by_url($sitename);
if ($site == FALSE) {
$err_msg = sprintf(
dt('An invalid site name ( %s ) was passed in.'),$sitename);
drush_set_error('DRUSH_AEGIR_TASK_ENQUEUE_TASK', $err_msg);
}
else {
$sites[] = $site;
}
}
}

foreach ($sites as $site) {
// The task was already validated against a general list of possible tasks.
// Here we validate it against the list of possible tasks for this site, now.
$possible_tasks = hosting_task_fetch_tasks($site->nid);
$site_task = $possible_tasks[$task];
if (isset($site_task['hidden']) && $site_task['hidden'] == 1) {
$msg = dt("%s was not enqueued for site %s because it is not availabe for this site. In th Aegir ui it is 'hidden'.");
}
else if( isset($site_task['task_status']) && $site_task['task_status'] == HOSTING_TASK_QUEUED) {
$msg = dt('%s was not enqueued for site %s because it is already enqueued.');
}
else if( isset($site_task['task_status']) && $site_task['task_status'] == HOSTING_TASK_PROCESSING) {
$msg = dt('%s was not enqueued for site %s because it is currently processing.');
}
else {
bw_aegir_task_enqueue_add_task($site, $task);
$msg = dt('%s was enqueued for site %s.');
}
drush_log(sprintf($msg, $task, $site->title));
}

return;
}

/**
* Adds a task to the Aegir Task Queue.
*
* Same as when the Aegir UI is used to request a task
*
* @param

$site * @param $task */ function bw_aegir_task_enqueue_add_task($site, $task) { $node = node_load($site -> nid); $node->no_verify = TRUE; node_save($node); // hosting_add_task is a function in the aegir // hostmaster/modules/hosting/tasks/'hosting_task' module hosting_add_task($node -> nid, $task); } /** * Retrieves all enabled sites that Aegir is managing * * @return sites */ function bw_aegir_task_enqueue_get_sites() { $result = db_query( "SELECT n.nid FROM {node} n, {hosting_site} s ". "WHERE n.nid=s.nid and n.type='site' and s.status = %d", HOSTING_SITE_ENABLED); while ($nid = db_fetch_object($result)) { $sites[$nid->nid] = node_load($nid->nid); } return $sites; } /** * Retrieves valid Aegir enabled sites tasks that do not req parameters * * Tasks that do not req params can be enqueued via drush with this module * * @return valid tasks */ function bw_aegir_task_enqueue_get_valid_task_names_that_req_no_params() { // hosting_available_tasks is a function in the hosting_task module $available_tasks = hosting_available_tasks('site'); // We are only operating on enabled sites so tasks that // require a disabled site are removed from the list of viable tasks unset($available_tasks['enable']); unset($available_tasks['delete']); // For most, and possibly all enabled sites, 'import' and 'install' // are not valid options. I could list them as valid options and let // them get screened out during the task/site validation is done. // Instead I explicitly remove them here, early. I am open to not doing // so if there is a reason not to. I just do not know of any. unset($available_tasks['import']); unset($available_tasks['install']); foreach ($available_tasks as $task_name => $task_info) { // This tests for the existance of a specific task-specific function. // That function only exists when parameters are required for the task. // This test is that same as testing if user input is required, other than // selecting to proceed or cancel, on the confirm screen, // when the task is manually selected from an Aegir site page. $func = sprintf(dt('hosting_task_%s_form'), $task_name); if (function_exists($func)) { unset($available_tasks[$task_name]); } } ksort($available_tasks); return array_keys($available_tasks); } /** * Validation of the request * * @param number of command line arguements * @param the first command line arguement * @return */ function bw_aegir_task_enqueue_validate_request($count_args, $first_arg) { switch ($count_args) { case 0: drush_set_error( 'DRUSH_AEGIR_TASK_ENQUEUE_TASK', dt('No parameters were passed in. Please run drush help task-enqueue')); return FALSE; case 1: drush_set_error( 'DRUSH_AEGIR_TASK_ENQUEUE_TASK', dt('Only one parameters was passed in. Please run drush help task-enqueue.')); return FALSE; default: $tasks = bw_aegir_task_enqueue_get_valid_task_names_that_req_no_params(); if (in_array($first_arg, $tasks) != TRUE) { $valid_tasks = implode (', ', $tasks); $err_msg = sprintf( dt('An invalid task name (%s) was passed in. '). dt('The task parameter should be a value in this list : %s'), $first_arg, $valid_tasks); drush_set_error('DRUSH_AEGIR_TASK_ENQUEUE_TASK', $err_msg); return FALSE; } } return TRUE; }
joshk’s picture

FileSize
7.03 KB

hey here's a slightly cleaned up patch file :)

Anonymous’s picture

Project: Hostmaster (Aegir) » Provision
Version: 6.x-0.4-alpha3 »
Status: Active » Needs review

I think this would be a fantastic addition to Provision, so I'm moving it to the other queue.

Review coming. Thanks!

Anonymous’s picture

Status: Needs review » Needs work

So this drush command works as expected. Thanks very much for the patch! As previously said, I think this would be great as included in Provision, as it'd make the 'headless Aegir' or 'backend only' installation much more powerful.

1) For this reason I suggest we rename it to 'provision-task' which is simpler. Adrian or anarcat can have the yay or nay here, that's just my opinion :)

This would mean renaming all the functions to 'provision_task_' prefixes instead of 'aegir_task_enqueue_'

I think we could also make aegir_task_enqueue_add_task simply 'provision_add_task', it matches hosting_add_task then in the frontend :)

Most of what I have below are just cosmetic suggestions / typos, which is a good thing :)

2)

function aegir_task_enqueue_drush_command() {
  $items = array();
  $items['task-enqueue'] = array(
    'description' => 'Enqueue Aegir site tasks as if requested via Aegir UI.',
    'arguments' => array(
      'task' => "Required. This is the first parameter. The name of a site task that is selectable in the Aegir UI, that does not require parameters (in the Aegir UI) and that may be done on an 'enabled' site",
        'site' => "Required. This is the second parameter. The word 'all' or the name of a site (site folder). a value of 'all' will enqueue the task on all the client sites. ",
        'additiona_sites' => 'Optional. The names of sites (site folders). ',
      ),

Rather than specify 'additional_sites' as a third argument here, it would seem clearer to me if just in the 'site' argument we reworded it like this:

'site' => "Required. This is the second parameter. The word 'all' or the site name. A value of 'all' will enqueue the task on all sites. Multiple sites can be specified in the one command.",

3)

      'options' => array(
        '--uri' => 'Required. Identifies the URI of the Aegir drupal site to use',
        '--root' => 'Optional. Identifies the Drupal root directory to use. When this is not set drush defaults to the current folder.',
      ),

Technically --uri is optional here too. If inside a valid --root, one can cd into the sites/$aegir_site and run Drush commands without it, and it will be bootstrapped.

I'd consider rewording to something like this:

      'options' => array(
        '--root' => 'Optional. Identifies the Drupal root directory to use. When this is not set drush defaults to the current folder.',
        '--uri' => 'Optional. Identifies the URI of the Aegir Drupal site to use. When this is not set, Drush defaults to the current working directory if inside a bootstrappable site folder, or otherwise sites/default, if inside a valid --root.',
      ),

But these are just opinions, not errors, feel free to challenge them :)

4) aegir_task_enqueue_get_sites() is a good function that i think should be in the frontend as 'hosting_get_sites'. Currently our only similar function is hosting_site_list() which is very focused on a rowed layout for display in the frontend.

I think we'd accept a 'hosting_get_sites()' function in a separate patch to hosting_site.module that is the same as your one here, and then you can reuse that function in the backend same as you do for hosting_add_task, hosting_available_tasks etc.

5) You are hiding/preventing certain tasks which is a good idea, and you are correct re: import and install tasks.

However, 'enable' and 'delete' *should* be allowed tasks, but only in certain circumstances: a site should be able to be 'enabled' if it has been 'disabled' (which is an allowed task in your patch). A site should also be able to be 'deleted' after it has been 'disabled'. This is how it works in the frontend - if you can conditionally allow these two tasks, then it'll match the functionality of the frontend. Take a look at the various 'safe_tasks' logic of hosting_task.module in the frontend to see how we do it there.

Since you can access the site node objects, you could check for its HOSTING_SITE_$STATUS in {hosting_site}. Since you are already skipping sites that are deleted via hosting_get_site_by_url(), you could check if a site is in HOSTING_SITE_DISABLED state, and if so, 'enable' and 'delete' should both be allowed.

6) A variation on 5) - certain (in fact most) tasks should not be allowed to be performed on the main 'aegir' site such as disable. We currently check for this in the same place as I mentioned in 5) - that is, in hosting_task_menu_access(). In this case, we check if the site's profile is 'hostmaster' and if so, we return after defining a certain array of safe_tasks

Perhaps we should move some of that functionality out of a GUI-specific function in Hosting, so that it can be reused by the backend here.

Note: I imagine this would also affect the 'all' option, as additional checking would be needed to ensure an unsafe task is sent to the main aegir site.

7) Not a show stopper, but I would love to see this command extend beyond just sites. For instance, platforms can be verified, locked, unlocked or deleted (These are all tasks in the frontend that don't require user-intervention in forms). You could control this similar to how we already do in Provision, via the 'context'. Look at provision/platform/delete.provision.inc to see the interchanging between site and platform. I *think* the same could work here, but not sure.

Once again, thanks for the excellent work here!

adrian’s picture

Status: Needs work » Postponed

this belongs in hosting, but i don't think we should attempt this until after the dev-services stuff has landed because there is no way to refer to the entities from the command line properly, until we introduce short names for them. that would then give us a lookup table.

tasks are now being run on server, platforms and sites, and the only unique identifier we have for them is NID, which we should not be used on the cli at all.

so the command should be :

drush @aegir.site.com hosting-add-task taskname @target

And arguments passed with :

drush @aegir.site.com hosting-add-task migrate @mysite.com --target_platform=@newplatform

i'm postponing this until we get the necessary underlying framework in place to support this properly, because otherwise this code will remain a hack to get around our APIs.

Anonymous’s picture

Project: Provision » Hosting
Version: » 6.x-0.4-alpha3
adrian’s picture

Status: Postponed » Needs work

in head you can now do :

drush @hostmaster hosting-task @site.com verify , and so forth.
if the task doesnt exist, it will create it.

the only bad part is that it can't do arguments. So you can't yet do the --target_platform=@newplatform.

anarcat’s picture

Status: Needs work » Fixed

This is fixed: the arguments stuff is just a separate issue (#1003536: allow arguments to hosting-task @site task).

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.