-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathhosting_reserve.module
executable file
·337 lines (267 loc) · 9.98 KB
/
hosting_reserve.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
<?php
/**
* @file
* Hosting reserve functions, and Drupal hooks.
*/
define('HOSTING_RESERVE_DEFAULT_CLONE_DOMAIN', 'clone');
/**
* Implementation of hook_menu().
*/
function hosting_reserve_menu() {
$items['admin/hosting/reserve'] = array(
'title' => 'Site Reserve',
'description' => 'Configure settings for hosting site reserve',
'page callback' => 'drupal_get_form',
'page arguments' => array('hosting_reserve_settings'),
'access arguments' => array('administer modules'), // TODO find good permission
'type' => MENU_LOCAL_TASK,
);
return $items;
}
/**
* Configuration form for hosting_reserve
*/
function hosting_reserve_settings() {
$form['hosting_reserve_full_size'] = array(
'#type' => 'textfield',
'#title' => t('Full reserve size'),
'#description' => t('The maximum number of sites in the reserve. The template will be cloned until this number is reached.'),
'#default_value' => variable_get('hosting_reserve_full_size', '10'),
'#required' => TRUE,
'#weight' => -15,
);
$form['hosting_reserve_minimum_size'] = array(
'#type' => 'textfield',
'#title' => t('Minimum reserve size'),
'#description' => t('The minimum number of sites in the reserve to try to preserve at all times. When there are fewer than this many reserve sites, we ignore the refilling time window restrictions (see lower). Set to zero to allow full depletion between time windows.'),
'#default_value' => variable_get('hosting_reserve_minimum_size', '1'),
'#weight' => -10,
);
$form['hosting_reserve_clone_life_time'] = array(
'#type' => 'textfield',
'#title' => t('Clone life time (in hours)'),
'#description' => t('Set to zero for infinite. Clones will be deleted if they stay in the "reserve" this long. Clone deletion will only occur within the refill window.'),
'#default_value' => variable_get('hosting_reserve_clone_life_time', '23'),
'#weight' => -5,
);
$server_time_message = t('The current server time is @time', array('@time' => date("H:i")));
$form['hosting_reserve_refill_window'] = array(
'#type' => 'fieldset',
'#title' => t('Time window for refilling'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#description' => t("Unless the reserve is depleted under the \"minimum reserve size\", if you don't limit the hours, the reserve will always be refilling. This could slow down the time between request delivery if you have a few back-to-back, since clone and import tasks would run in-between the renaming tasks."),
'#weight' => -1,
'#prefix' => '<br/>',
'#suffix' => '<br/>',
);
$form['hosting_reserve_refill_window']['hosting_reserve_refill_window_start'] = array(
'#type' => 'textfield',
'#title' => t('Start refilling the reserve at this hour.'),
'#description' => t('Format: 00:00. ') . $server_time_message,
'#default_value' => variable_get('hosting_reserve_refill_window_start', '00:00'),
'#maxlength' => 5,
'#size' => 6,
'#prefix' => '<br/>',
);
$form['hosting_reserve_refill_window']['hosting_reserve_refill_window_end'] = array(
'#type' => 'textfield',
'#title' => t('Stop refilling the reserve at this hour.'),
'#description' => t('Format: 23:59. ') . $server_time_message,
'#default_value' => variable_get('hosting_reserve_refill_window_end', '04:00'),
'#maxlength' => 5,
'#size' => 6,
);
$form['#validate'][] = 'hosting_reserve_settings_form_validate';
return system_settings_form($form);
}
function hosting_reserve_settings_form_validate(&$form, &$form_state) {
$date_format = 'H:i';
$values = $form_state['values'];
if (!empty($values['hosting_reserve_refill_window_start'])) {
if (strtotime($values['hosting_reserve_refill_window_start']) == 0) {
form_set_error('hosting_reserve_refill_window_start', t('Invalid start time'));
}
}
if (!empty($values['hosting_reserve_refill_window_end'])) {
if (strtotime($values['hosting_reserve_refill_window_end']) == 0) {
form_set_error('hosting_reserve_refill_window_end', t('Invalid end time'));
}
}
if (empty($values['hosting_reserve_refill_window_start'])) {
$form_state['values']['hosting_reserve_refill_window_start'] = '00:00';
}
if (empty($values['hosting_reserve_refill_window_end'])) {
$form_state['values']['hosting_reserve_refill_window_end'] = '23:59';
}
if (empty($values['hosting_reserve_minimum_size'])) {
$form_state['values']['hosting_reserve_minimum_size'] = '0';
}
}
/**
* Checks whether we are currently within the allowed time window for refilling
* the site reserve.
*/
function hosting_reserve_is_allowed_time_window($time = NULL) {
if ($time === NULL) {
$time = time();
}
$today = date("D", time());
$start_time = variable_get('hosting_reserve_refill_window_start', '00:00');
$end_time = variable_get('hosting_reserve_refill_window_end', '04:00');
// Date field shenanigans
if (empty($start_time)) {
$start_time = strtotime('00:00'); // Midnight "this morning"
}
else {
$start_time = strtotime($start_time);
}
if (empty($end_time)) {
$end_time = strtotime('23:59');
}
else {
$end_time = strtotime($end_time);
}
if ($time < $start_time) {
// Before the start time
return FALSE;
}
if ($time > $end_time) {
// After the end time
return FALSE;
}
return TRUE;
}
/**
* Get available clones.
*/
function hosting_reserve_get_clones($pattern = NULL, $include_aliased = FALSE) {
if ($pattern == NULL) {
$pattern = '%.' . HOSTING_RESERVE_DEFAULT_CLONE_DOMAIN;
}
$result = db_query("SELECT nid FROM node WHERE type = 'site' AND title LIKE '%s' ORDER BY created ASC", $pattern);
$clones = array();
while ($obj = db_fetch_object ($result)) {
$node = node_load($obj->nid);
if ($node->site_status < 0) {
continue; // Ignore disabled and deleted sites
}
// Exclude 'taken' clones unless otherwise specified
if (empty($node->aliases) || $include_aliased) {
$clones[$obj->nid] = $node;
}
}
return $clones;
}
/**
* Deploys a new site, either from a reserve clone or by cloning the template.
*
* This is the main function to call to deploy a new site.
*/
function hosting_reserve_make_site($url) {
// Get clones
$clones = hosting_reserve_get_clones();
if (!empty($clones)) {
// Deploy first available clone
$clone = array_shift($clones);
hosting_reserve_alias_clone($url, $clone);
return $clone;
}
else {
// If no clones left, clone site as normal
$error_message = '';
hosting_saas_clone_site_from_template($url, $error_message);
return $error_message;
}
}
function hosting_reserve_alias_clone($url, $clone) {
// TODO: Validate URL
$clone->aliases[] = $url;
node_save($clone); // This triggers a Verify
}
/**
* Migrate a clone to the permanent URL.
*
* The alias is a temporary measure, things can get weird if we don't do this.
*/
function hosting_reserve_migrate_clone($url, $clone) {
// 1. Remove the alias
$index = array_search($url, $clone->aliases);
if ($index === FALSE) {
// Wrong site
return FALSE;
}
unset($clone->aliases[$index]);
node_save($clone); // This will trigger a Verify.
// 2. Migrate
$args = array(
'new_uri' => $url,
'new_db_server' => variable_get('hosting_saas_db_server', NULL),
'target_platform' => variable_get('hosting_saas_target_platform', NULL),
);
// Allow other modules to modify the options
module_invoke_all('hosting_saas_migrate_clone', $url, $args);
hosting_add_task($clone->nid, 'migrate', $args);
return TRUE;
}
function hosting_reserve_make_clone(&$error_message = '') {
$clone_id = variable_get('hosting_reserve_next_clone_id', 1);
$clone_domain = variable_get('hosting_reserve_clone_domain', HOSTING_RESERVE_DEFAULT_CLONE_DOMAIN);
$clone_sub_prefix = variable_get('hosting_reserve_clone_sub_prefix', 'hosting-reserve-');
$url = $clone_sub_prefix . $clone_id . '.' . $clone_domain;
module_invoke_all('hosting_reserve_new_clone', $url);
$error_message = '';
hosting_saas_clone_site_from_template($url, $error_message);
// TODO: Find something more elegant... Maybe $error_message as reference arg?
if ($error_message != '') {
return $error_message;
}
// This means the clone_id is not incremented when there is an error.
$clone_id += 1;
variable_set('hosting_reserve_next_clone_id', $clone_id);
return $url;
}
function _hosting_reserve_clean_up_clones() {
$clones = hosting_reserve_get_clones(NULL, TRUE);
foreach ($clones as $nid => $clone) {
// Delete unused clones
if (empty($clone->aliases)) {
$time_since_creation = time() - $clone->created;
$life_time_in_seconds = variable_get('hosting_reserve_clone_life_time', '23') * 60 * 60;
if ($time_since_creation > $life_time_in_seconds) {
// Delete site
hosting_add_task($clone->nid, 'delete');
}
// TODO: Delete last backup?
}
else {
// Migrate the ones with aliases
$url = $clone->aliases[0];
hosting_reserve_migrate_clone($url, $clone);
}
}
}
function hosting_reserve_cron() {
$clones = hosting_reserve_get_clones();
if (hosting_reserve_is_allowed_time_window()) {
_hosting_reserve_clean_up_clones();
if (count($clones) < variable_get('hosting_reserve_full_size', 10)) {
$error_message = '';
// This will fail if a clone is already ongoing, so there's no risk of
// going over the full reserve size while the task is queued.
hosting_reserve_make_clone($error_message);
// TODO: Implement setting to show error messages in the dblog
}
}
else {
if (count($clones) < variable_get('hosting_reserve_minimum_size', 1)) {
watchdog('hosting_reserve', '(cron) Reserve depleted, making new clone.');
hosting_reserve_make_clone();
}
}
}
function hosting_reserve_post_hosting_verify_task($task, $data) {
drush_log('[hosting_reserve] hosting_reserve_post_hosting_verify_task');
// TODO: Determine if the alias was just added
// TODO: Implement hook to allow acting on site after it gets aliased
}