provision.drush.inc

Provision Drush commands.

This module provides a framework for a Drupal site to manage and install new Drupal sites, using the command line Drush utility.

It allows for pluggable 'provisioning modules' that can extend and modify the tasks that are taken during installation.

Each site has the following commands that can be run on it.

Implemented : install - Install a new Drupal site. The install command uses 3 separate hooks to do its job, namely hook_pre_provision_install(), hook_provision_install() and hook_post_provision_install(). verify - Recreate all configuration files, to be in synch with changes in the front end. And test that they are correct. stats - Return an associated array of site statistics. (implemented in provision_stats module, is thus optional) import - Import the details of an already existing site into the provisioning framework. This command inspects the settings.php and generates the site.php file that the framework uses for configuration. backup - Generates a tarball containing the sites directory, the site data configuration and the database dump. This allows the tarball to act as a 'site package', which can be redeployed on other installations, or used for an upgrade. disable - Disable an installed Drupal site. Changes the virtual host config file so that it redirects to provision_disabled_site_redirect_url enable - Re-enable a site that has already been disabled. Recreates the virtual host file. delete - In a site context: generates a back up of the site, and then removes all references to it. In a platform context: removes the platform and its vhost config from the server if no sites are currently running on it restore - Revert to a previous backup of the site.

deploy - Accepts a site package (backup) as argument, and redeploys it, running the upgrade processes on it. Uses hook_provision_pre_upgrade(), hook_provision_upgrade() and hook_provision_post_upgrade() hooks, and allows clean roll back if any errors occur. Will include stringent checking of module versions, and allow unit tests to be run. lock - Lock a platform so that sites cannot be provisioned on it. This does not disable or delete the platform nor any sites currently provisioned on it. unlock - Unlock a platform so that sites can be provisioned on it.

login-reset - Generate a one-time login reset URL.

Functions

Namesort descending Description
drush_provision_save
drush_provision_verify
provision_count_cpus determine the number of CPU on the machine
provision_drush_command Implementation of hook_drush_command().
provision_drush_init Implements hook_drush_init().
provision_hosting_feature_enabled Check whether a Hosting feature is enabled.
provision_load_critical determine if overall load of the machine is critical
_provision_default_web_group
_provision_drush_check_load This will abort any process running drush if the load is critical.
_provision_drush_check_user This will abort any process running drush provision commands if the user running the command is root.

Constants

File

provision.drush.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Provision Drush commands.
  5. *
  6. *
  7. * This module provides a framework for a Drupal site to manage and install new Drupal sites, using the command line
  8. * Drush utility.
  9. *
  10. * It allows for pluggable 'provisioning modules' that can extend and modify the tasks that are taken during installation.
  11. *
  12. * Each site has the following commands that can be run on it.
  13. *
  14. * Implemented :
  15. * install - Install a new Drupal site. The install command uses 3 separate hooks to do its job,
  16. * namely hook_pre_provision_install(), hook_provision_install() and hook_post_provision_install().
  17. * verify - Recreate all configuration files, to be in synch with changes in the front end. And test that they are correct.
  18. * stats - Return an associated array of site statistics. (implemented in provision_stats module, is thus optional)
  19. * import - Import the details of an already existing site into the provisioning framework.
  20. * This command inspects the settings.php and generates the site.php file that the framework uses for configuration.
  21. * backup - Generates a tarball containing the sites directory, the site data configuration and the database dump.
  22. * This allows the tarball to act as a 'site package', which can be redeployed on other installations,
  23. * or used for an upgrade.
  24. * disable - Disable an installed Drupal site. Changes the virtual host config file so that it redirects to provision_disabled_site_redirect_url
  25. * enable - Re-enable a site that has already been disabled. Recreates the virtual host file.
  26. * delete - In a site context: generates a back up of the site, and then removes all references to it.
  27. * In a platform context: removes the platform and its vhost config from the server if no sites are currently running on it
  28. * restore - Revert to a previous backup of the site.
  29. *
  30. * deploy - Accepts a site package (backup) as argument, and redeploys it, running the upgrade processes on it.
  31. * Uses hook_provision_pre_upgrade(), hook_provision_upgrade() and hook_provision_post_upgrade() hooks,
  32. * and allows clean roll back if any errors occur. Will include stringent checking of module versions,
  33. * and allow unit tests to be run.
  34. * lock - Lock a platform so that sites cannot be provisioned on it. This does not disable or delete the platform
  35. * nor any sites currently provisioned on it.
  36. * unlock - Unlock a platform so that sites can be provisioned on it.
  37. *
  38. * login-reset - Generate a one-time login reset URL.
  39. */
  40. /**
  41. * @defgroup provisiondrush Command line interface for Provision.
  42. * @{
  43. */
  44. include_once('provision.inc');
  45. /**
  46. * Implements hook_drush_init().
  47. */
  48. function provision_drush_init() {
  49. // try to load the drush siterecoard (the "alias") into d
  50. // if a name is provided, it's because we're in provision-save so we
  51. // need to specify it because it's not loaded by drush
  52. $hash_name = drush_get_option('#name') ? '#name' : 'name';
  53. d(drush_get_option($hash_name, '@self', 'alias'), TRUE);
  54. // Make sure that the provision command is not being run as the root user.
  55. _provision_drush_check_user();
  56. // Abort the process if the load is too high.
  57. _provision_drush_check_load();
  58. }
  59. /**
  60. * This will abort any process running drush provision commands if the
  61. * user running the command is root.
  62. */
  63. function _provision_drush_check_user() {
  64. $command = drush_get_command();
  65. $name = posix_getpwuid(posix_geteuid());
  66. if (preg_match("/^provision-\b/", $command['command']) && $name['name'] == 'root') {
  67. return drush_set_error('PROVISION_IS_ROOT', dt('You are running the provision script as the root user. Exiting'));
  68. }
  69. }
  70. /**
  71. * This will abort any process running drush if the load is critical.
  72. *
  73. * @see provision_load_critical()
  74. */
  75. function _provision_drush_check_load() {
  76. $load = sys_getloadavg();
  77. if (provision_load_critical($load)) {
  78. drush_set_error('PROVISION_OVERLOAD', dt("load on system too heavy (@load), aborting", array('@load' => join(" ", $load))));
  79. exit(1);
  80. }
  81. }
  82. /**
  83. * Implementation of hook_drush_command().
  84. */
  85. function provision_drush_command() {
  86. $items['provision-save'] = array(
  87. 'description' => dt('Save Drush alias'),
  88. 'arguments' => array(
  89. '@context_name' => 'Context to save',
  90. ),
  91. 'options' => array_merge(array(
  92. 'context_type' => 'server, platform, or site; default server',
  93. 'delete' => 'Remove the alias.'),
  94. Provision_Context_server::option_documentation(),
  95. Provision_Context_platform::option_documentation(),
  96. Provision_Context_site::option_documentation()),
  97. // we should populate from all known contexts, unfortunately we don't
  98. // enumerate them yet... see https://drupal.org/node/1972286
  99. 'allow-additional-options' => TRUE,
  100. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
  101. );
  102. $items['provision-install'] = array(
  103. 'description' => dt('Provision a new site using the provided data.'),
  104. 'examples' => array(
  105. 'drush @site provision-install' => 'Install the site as defined by the site Drush alias generated with provision-save.',
  106. ),
  107. 'options' => array(
  108. 'client_email' => dt('The email address of the client to use.'),
  109. 'profile' => dt('The profile to use when installing the site.'),
  110. ),
  111. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  112. );
  113. $items['provision-install-backend'] = array(
  114. 'description' => dt('Provision a new site using the provided data.'),
  115. 'options' => array(
  116. 'client_email' => dt('The email address of the client to use.'),
  117. ),
  118. 'hidden' => TRUE,
  119. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_SITE
  120. );
  121. $items['provision-import'] = array(
  122. 'description' => dt('Turn an already running site into a provisioned site.'),
  123. 'options' => array(
  124. 'client_email' => dt('The email address of the client to use.'),
  125. ),
  126. 'examples' => array(
  127. 'drush @site provision-import' => 'Import the site as defined by the site Drush alias generated with provision-save.',
  128. ),
  129. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  130. );
  131. $items['provision-backup'] = array(
  132. 'optional arguments' => array('backup-file' => dt('The file to save the backup to. This will be a gzipped tarball.')),
  133. 'description' => dt('Generate a back up for the site.'),
  134. 'examples' => array(
  135. 'drush @site provision-backup' => 'Back up the site as defined by the site Drush alias generated with provision-save.',
  136. ),
  137. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  138. );
  139. $items['provision-enable'] = array(
  140. 'description' => 'Enable a disabled site.',
  141. 'examples' => array(
  142. 'drush @site provision-enable' => 'Enable the site as defined by the site Drush alias generated with provision-save.',
  143. ),
  144. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  145. );
  146. $items['provision-disable'] = array(
  147. 'arguments' => array('domain.com' => dt('The domain of the site to disable (only if disabled).')),
  148. 'description' => 'Disable a site.',
  149. 'examples' => array(
  150. 'drush @site provision-disable' => 'Disable the site as defined by the site Drush alias generated with provision-save.',
  151. ),
  152. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  153. );
  154. $items['provision-lock'] = array(
  155. 'description' => 'Lock a platform from having any other sites provisioned on it.',
  156. 'examples' => array(
  157. 'drush @platform provision-lock' => 'Lock the platform as defined by the platform Drush alias generated with provision-save.',
  158. ),
  159. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  160. );
  161. $items['provision-unlock'] = array(
  162. 'description' => 'Unlock a platform so that sites can be provisioned on it.',
  163. 'examples' => array(
  164. 'drush @platform provision-unlock' => 'Unlock the platform as defined by the platform Drush alias generated with provision-save.',
  165. ),
  166. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  167. );
  168. $items['provision-verify'] = array(
  169. 'arguments' => array('domain.com' => dt('The domain of the site to verify).')),
  170. 'description' => 'Verify that the provisioning framework is correctly installed.',
  171. 'examples' => array(
  172. 'drush @site provision-verify' => 'Verify the site as defined by the site Drush alias generated with provision-save.',
  173. 'drush @platform provision-verify' => 'Verify the platform as defined by the platform Drush alias generated with provision-save.',
  174. ),
  175. 'options' => array(
  176. 'working-copy' => array(
  177. 'description' => dt('Keep VCS files when building the a platform using Drush make.'),
  178. 'hidden' => TRUE,
  179. ),
  180. 'override_slave_authority' => array(
  181. 'description' => dt('Push the site specific files directory to a slave. This overrides the slaves authority.'),
  182. ),
  183. ),
  184. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH
  185. );
  186. $items['provision-restore'] = array(
  187. 'description' => 'Restore the site to a previous backup. This will also generate a backup of the site as it was.',
  188. 'arguments' => array(
  189. 'site_backup.tar.gz' => dt('The backup to restore the site to.')),
  190. 'examples' => array(
  191. 'drush @site provision-restore ~/backups/some_site.tar.gz' => 'Restore the site to the backup in ~/backups/some_site.tar.gz.',
  192. ),
  193. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  194. );
  195. $items['provision-deploy'] = array(
  196. 'description' => 'Deploy an existing backup to a new url.',
  197. 'arguments' => array(
  198. 'site_backup.tar.gz' => dt('The backup to deploy.')),
  199. 'options' => array(
  200. 'old_uri' => dt('Old site uri to replace in references to sites/example.com/files/ in the database content.)'),
  201. ),
  202. 'examples' => array(
  203. 'drush @site provision-deploy ~/backups/some_site.tar.gz' => 'Deploy the site as defined by the site Drush alias, from the backup in ~/backups/some_site.tar.gz.',
  204. ),
  205. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  206. );
  207. $items['provision-migrate'] = array(
  208. 'description' => 'Migrate a site between platforms.',
  209. 'arguments' => array(
  210. '@platform_name' => dt('The Drush alias of the platform.')),
  211. 'options' => array(
  212. 'profile' => dt('The Drupal profile to use.')),
  213. 'examples' => array(
  214. 'drush @site provision-migrate @platform_name' => 'Migrate the site as defined by the Drush alias, to the platform as defined by the platform\'s Drush alias',
  215. ),
  216. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  217. );
  218. $items['provision-clone'] = array(
  219. 'description' => 'Clone a site between platforms.',
  220. 'arguments' => array(
  221. '@new_site' => dt('The Drush alias of the new site as generated by provision-save.'),
  222. '@platform_name' => dt('The Drush alias of the platform to clone the site onto.')),
  223. 'options' => array(
  224. 'profile' => dt('The Drupal profile to use.')),
  225. 'examples' => array(
  226. 'drush @site provision-clone @new_site @platform_name' => 'Clone the original site to the new site on a platform',
  227. ),
  228. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  229. );
  230. $items['provision-delete'] = array(
  231. 'description' => 'Delete a site or platform.',
  232. 'options' => array(
  233. 'force' => dt('Force deletion.')),
  234. 'examples' => array(
  235. 'drush @site provision-delete' => 'Delete the site as defined by the site Drush alias generated with provision-save.',
  236. ),
  237. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH
  238. );
  239. $items['provision-login-reset'] = array(
  240. 'description' => 'Generate a one-time login reset URL.',
  241. 'examples' => array(
  242. 'drush @site provision-login-reset' => 'Generate a one-time login reset URL for the site as defined by the site Drush alias generated with provision-save.',
  243. ),
  244. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
  245. );
  246. $items['provision-backup-delete'] = array(
  247. 'description' => 'Delete a backup file.',
  248. 'arguments' => array('backup-file' => dt('The backup file to delete. This will be a gzipped tarball.')),
  249. 'examples' => array(
  250. 'drush @site provision-backup-delete /path/to/site_backup.tgz' => 'Delete a backup of this site as defined by the site Drush alias generated with provision-save.',
  251. ),
  252. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH
  253. );
  254. $items['hostmaster-migrate'] = array(
  255. 'description' => dt('Migrate an instance of the Hostmaster front end to a new platform'),
  256. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT,
  257. 'arguments' => array(
  258. 'example.com' => dt('The name of the site to migrate'),
  259. '/path/to/platform' => dt('The platform to migrate the site to.'),
  260. ),
  261. 'options' => array(
  262. 'http_service_type' => dt('Webserver type to configure (default: %webserver)', array('%webserver' => 'apache')),
  263. 'makefile' => dt('The makefile used to create the hostmaster platform (default: %makefile)', array('%makefile' => dirname(__FILE__). '/aegir.make')),
  264. 'working-copy' => dt('Keep VCS files when building the hostmaster platform using Drush make.')
  265. ),
  266. );
  267. $items['hostmaster-install'] = array(
  268. 'description' => dt('Install and verify the Hostmaster frontend.'),
  269. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
  270. 'arguments' => array(
  271. 'example.com' => dt('The URL of the site to install, optional (default: %host).', array('%host' => provision_fqdn())),
  272. ),
  273. 'options' => array
  274. (
  275. 'http_service_type' => dt('Webserver type to configure (default: %webserver)', array('%webserver' => 'apache')),
  276. 'aegir_db_host' => dt('Database host to connect to (default: %host)', array('%host' => 'localhost')),
  277. 'aegir_db_user' => dt('Database user to connect as (default: %user)', array('%user' => 'root')),
  278. 'aegir_db_pass' => dt('Database password to use'),
  279. 'aegir_db_port' => dt('Database port to use (default: %port)', array('%port' => '3306')),
  280. 'client_email' => dt('Email of the first client to create in the frontend'),
  281. 'client_name' => dt('Name of the first client to create in the frontend (default: %user)', array('%user' => 'admin')),
  282. 'makefile' => dt('The makefile used to create the hostmaster platform (default: %makefile)', array('%makefile' => dirname(__FILE__). '/aegir.make')),
  283. 'aegir_host' => dt('Fully qualified domain name of the local server (default: %fqdn)', array('%fqdn' => provision_fqdn())),
  284. 'script_user' => dt('User to run the backend as (default: %user)', array('%user' => provision_current_user())),
  285. 'web_group' => dt('Group the webserver is running as (default: %group)', array('%group' => _provision_default_web_group())),
  286. 'http_port' => dt('Port the webserver is running on (default: %port)', array('%port' => '80')),
  287. 'version' => dt('The version of this released. (default: %version)', array('%version' => provision_version())),
  288. 'aegir_root' => dt('Install aegir in this home directory (default: %home). Do not change unless you know what you are doing.', array('%home' => drush_server_home())),
  289. 'root' => dt('Install the frontend in this directory (default: %home/hostmaster-%version).', array('%home' => drush_server_home(), '%version' => provision_version())),
  290. 'backend-only' => dt('Install just the backend, and not the frontend UI.'),
  291. 'working-copy' => dt('Keep VCS files when building the hostmaster platform using Drush make.')
  292. ),
  293. );
  294. $items['hostmaster-uninstall'] = array(
  295. 'description' => dt('Uninstall the Hostmaster frontend.'),
  296. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_SITE,
  297. 'options' => array
  298. (
  299. 'all' => dt('Destroy *ALL* sites managed by the Aegir frontend'),
  300. ),
  301. );
  302. $items['backend-parse'] = array(
  303. 'description' => dt('Parse the output of --backend commands to a human readable form'),
  304. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
  305. );
  306. return $items;
  307. }
  308. function drush_provision_save($alias = NULL) {
  309. if (drush_get_option('delete', FALSE)) {
  310. // remove an existing alias
  311. $config = new Provision_Config_Drushrc_Alias($alias);
  312. $config->unlink();
  313. }
  314. else {
  315. // trigger additional logic that should happen only on save.
  316. d($alias)->type_invoke('save');
  317. // create or update the record
  318. d($alias)->write_alias();
  319. }
  320. }
  321. function drush_provision_verify() {
  322. provision_backend_invoke(d()->name, 'provision-save');
  323. d()->command_invoke('verify');
  324. }
  325. function _provision_default_web_group() {
  326. $info = posix_getgrgid(posix_getgid());
  327. $common_groups = array(
  328. 'www-data',
  329. 'apache',
  330. 'nginx',
  331. 'www',
  332. '_www',
  333. 'webservd',
  334. 'httpd',
  335. 'nogroup',
  336. 'nobody',
  337. $info['name']);
  338. foreach ($common_groups as $group) {
  339. if (provision_posix_groupname($group)) {
  340. return $group;
  341. break;
  342. }
  343. }
  344. return NULL;
  345. }
  346. /**
  347. * determine the number of CPU on the machine
  348. *
  349. * This tries a best guess at the number of CPUs running on the system. This is
  350. * useful for calculating sane load threshold.
  351. *
  352. * On Linux, this parses /proc/cpuinfo and looks for lines like this:
  353. *
  354. * processor : 0
  355. * ...
  356. * processor : 1
  357. * processor : n
  358. *
  359. * The number of CPUs on the system is n+1, we just count the number of lines.
  360. *
  361. * Other systems remain to be implemented, and would be best implemetend
  362. * through a PECL (or similar) extension that would use the POSIX sysconf
  363. * interface, as such:
  364. *
  365. * ncpus = sysconf(_SC_NPROCESSORS_ONLN);
  366. *
  367. * If no method can be found to figure out the number of CPUs, this will return
  368. * FALSE.
  369. *
  370. * People wishing to extend this to other platforms should look at
  371. * suggestions at:
  372. *
  373. * http://groups.google.com/group/sage-devel/browse_thread/thread/d65209f7ad6057fc
  374. *
  375. * @see provision_load_critical()
  376. * @todo implement for other systems than Linux
  377. */
  378. function provision_count_cpus() {
  379. $ncpus = FALSE;
  380. if (is_readable("/data/all/cpuinfo")) {
  381. # this should work on BOA with a /data/all/cpuinfo generated daily
  382. $cpuinfo = (int) file_get_contents("/data/all/cpuinfo");
  383. if ($cpuinfo !== FALSE && is_numeric($cpuinfo)) {
  384. $ncpus = $cpuinfo;
  385. }
  386. }
  387. elseif (is_readable("/proc/cpuinfo")) {
  388. # this should work on Linux with a /proc filesystem
  389. $cpuinfo = file_get_contents("/proc/cpuinfo");
  390. if ($cpuinfo !== FALSE) {
  391. if (preg_match_all("/^processor.*:.*[0-9]+$/m", $cpuinfo, $matches)) {
  392. $ncpus = count(array_pop($matches));
  393. }
  394. }
  395. }
  396. return $ncpus;
  397. }
  398. define('CRITICAL_LOAD_MULTIPLIER', 5);
  399. define('CRITICAL_LOAD_THRESHOLD', 10);
  400. /**
  401. * determine if overall load of the machine is critical
  402. *
  403. * We use the "average system load" of the system as a metric, as available
  404. * through 'uptime' or in PHP sys_getloadavg() since 5.1. The load is usually
  405. * defined as "the number of processes in the system run queue"
  406. *
  407. * It's not a really reliable metric, but it's the best shot we've got without
  408. * getting into real specific details about I/O, CPU or memory load that are
  409. * going to be even tougher to evaluate.
  410. *
  411. * We base our evaluation on the number of CPUs on the servers. If there are
  412. * more than 5 processes waiting per CPU, we abort completely. If we ignore the
  413. * number of available CPUs, we assume a critical limit is a load of 10.
  414. *
  415. * @see sys_getloadavg()
  416. */
  417. function provision_load_critical($load = NULL, $threshold = NULL) {
  418. if (is_null($load)) {
  419. $load = sys_getloadavg();
  420. }
  421. if (is_null($threshold)) {
  422. if ($ncpus = provision_count_cpus()) {
  423. $threshold = $ncpus * drush_get_option('critical_load_multiplier', CRITICAL_LOAD_MULTIPLIER);
  424. }
  425. else {
  426. // can't determine the number of CPU, we hardcode at load 10
  427. $threshold = drush_get_option('critical_load_threshold', CRITICAL_LOAD_THRESHOLD);
  428. }
  429. }
  430. return ($load[0] > $threshold);
  431. }
  432. /**
  433. * Check whether a Hosting feature is enabled.
  434. */
  435. function provision_hosting_feature_enabled($feature) {
  436. $features = drush_get_option('hosting_features', array());
  437. return array_key_exists($feature, $features) && $features[$feature];
  438. }