Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Cache administration helper.
19
 *
20
 * This file is part of Moodle's cache API, affectionately called MUC.
21
 * It contains the components that are requried in order to use caching.
22
 *
23
 * @package    core
24
 * @category   cache
25
 * @author     Peter Burnett <peterburnett@catalyst-au.net>
26
 * @copyright  2020 Catalyst IT
27
 * @copyright  2012 Sam Hemelryk
28
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29
 */
30
 
31
namespace core_cache;
32
 
33
defined('MOODLE_INTERNAL') || die();
34
use cache_helper, cache_store, cache_config, cache_factory, cache_definition;
35
 
36
/**
37
 * Administration helper base class.
38
 *
39
 * Defines abstract methods for a subclass to define the admin page.
40
 *
41
 * @package     core
42
 * @category    cache
43
 * @author      Peter Burnett <peterburnett@catalyst-au.net>
44
 * @copyright   2020 Catalyst IT
45
 * @copyright  2012 Sam Hemelryk
46
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
47
 */
48
abstract class administration_helper extends cache_helper {
49
 
50
    /**
51
     * Returns an array containing all of the information about stores a renderer needs.
52
     * @return array
53
     */
54
    public static function get_store_instance_summaries(): array {
55
        $return = array();
56
        $default = array();
57
        $instance = \cache_config::instance();
58
        $stores = $instance->get_all_stores();
59
        $locks = $instance->get_locks();
60
        foreach ($stores as $name => $details) {
61
            $class = $details['class'];
62
            $store = false;
63
            if ($class::are_requirements_met()) {
64
                $store = new $class($details['name'], $details['configuration']);
65
            }
66
            $lock = (isset($details['lock'])) ? $locks[$details['lock']] : $instance->get_default_lock();
67
            $record = array(
68
                'name' => $name,
69
                'plugin' => $details['plugin'],
70
                'default' => $details['default'],
71
                'isready' => $store ? $store->is_ready() : false,
72
                'requirementsmet' => $class::are_requirements_met(),
73
                'mappings' => 0,
74
                'lock' => $lock,
75
                'modes' => array(
76
                    cache_store::MODE_APPLICATION =>
77
                        ($class::get_supported_modes($return) & cache_store::MODE_APPLICATION) == cache_store::MODE_APPLICATION,
78
                    cache_store::MODE_SESSION =>
79
                        ($class::get_supported_modes($return) & cache_store::MODE_SESSION) == cache_store::MODE_SESSION,
80
                    cache_store::MODE_REQUEST =>
81
                        ($class::get_supported_modes($return) & cache_store::MODE_REQUEST) == cache_store::MODE_REQUEST,
82
                ),
83
                'supports' => array(
84
                    'multipleidentifiers' => $store ? $store->supports_multiple_identifiers() : false,
85
                    'dataguarantee' => $store ? $store->supports_data_guarantee() : false,
86
                    'nativettl' => $store ? $store->supports_native_ttl() : false,
87
                    'nativelocking' => ($store instanceof \cache_is_lockable),
88
                    'keyawareness' => ($store instanceof \cache_is_key_aware),
89
                    'searchable' => ($store instanceof \cache_is_searchable)
90
                ),
91
                'warnings' => $store ? $store->get_warnings() : array()
92
            );
93
            if (empty($details['default'])) {
94
                $return[$name] = $record;
95
            } else {
96
                $default[$name] = $record;
97
            }
98
        }
99
 
100
        ksort($return);
101
        ksort($default);
102
        $return = $return + $default;
103
 
104
        $mappings = $instance->get_definition_mappings();
105
        foreach ($mappings as $mapping) {
106
            if (!array_key_exists($mapping['store'], $return)) {
107
                continue;
108
            }
109
            $return[$mapping['store']]['mappings']++;
110
        }
111
 
112
        // Now get all definitions, and if not mapped, increment the defaults for the mode.
113
        $modemappings = $instance->get_mode_mappings();
114
        foreach ($instance->get_definitions() as $definition) {
115
            // Construct the definition name to search for.
116
            $defname = $definition['component'] . '/' . $definition['area'];
117
            // Skip if definition is already mapped.
118
            if (array_search($defname, array_column($mappings, 'definition')) !== false) {
119
                continue;
120
            }
121
 
122
            $mode = $definition['mode'];
123
            // Get the store name of the default mapping from the mode.
124
            $index = array_search($mode, array_column($modemappings, 'mode'));
125
            $store = $modemappings[$index]['store'];
126
            $return[$store]['mappings']++;
127
        }
128
 
129
        return $return;
130
    }
131
 
132
    /**
133
     * Returns an array of information about plugins, everything a renderer needs.
134
     *
135
     * @return array for each store, an array containing various information about each store.
136
     *     See the code below for details
137
     */
138
    public static function get_store_plugin_summaries(): array {
139
        $return = array();
140
        $plugins = \core_component::get_plugin_list_with_file('cachestore', 'lib.php', true);
141
        foreach ($plugins as $plugin => $path) {
142
            $class = 'cachestore_'.$plugin;
143
            $return[$plugin] = array(
144
                'name' => get_string('pluginname', 'cachestore_'.$plugin),
145
                'requirementsmet' => $class::are_requirements_met(),
146
                'instances' => 0,
147
                'modes' => array(
148
                    cache_store::MODE_APPLICATION => ($class::get_supported_modes() & cache_store::MODE_APPLICATION),
149
                    cache_store::MODE_SESSION => ($class::get_supported_modes() & cache_store::MODE_SESSION),
150
                    cache_store::MODE_REQUEST => ($class::get_supported_modes() & cache_store::MODE_REQUEST),
151
                ),
152
                'supports' => array(
153
                    'multipleidentifiers' => ($class::get_supported_features() & cache_store::SUPPORTS_MULTIPLE_IDENTIFIERS),
154
                    'dataguarantee' => ($class::get_supported_features() & cache_store::SUPPORTS_DATA_GUARANTEE),
155
                    'nativettl' => ($class::get_supported_features() & cache_store::SUPPORTS_NATIVE_TTL),
156
                    'nativelocking' => (in_array('cache_is_lockable', class_implements($class))),
157
                    'keyawareness' => (array_key_exists('cache_is_key_aware', class_implements($class))),
158
                ),
159
                'canaddinstance' => ($class::can_add_instance() && $class::are_requirements_met())
160
            );
161
        }
162
 
163
        $instance = cache_config::instance();
164
        $stores = $instance->get_all_stores();
165
        foreach ($stores as $store) {
166
            $plugin = $store['plugin'];
167
            if (array_key_exists($plugin, $return)) {
168
                $return[$plugin]['instances']++;
169
            }
170
        }
171
 
172
        return $return;
173
    }
174
 
175
    /**
176
     * Returns an array about the definitions. All the information a renderer needs.
177
     *
178
     * @return array for each store, an array containing various information about each store.
179
     *     See the code below for details
180
     */
181
    public static function get_definition_summaries(): array {
182
        $factory = cache_factory::instance();
183
        $config = $factory->create_config_instance();
184
        $storenames = array();
185
        foreach ($config->get_all_stores() as $key => $store) {
186
            if (!empty($store['default'])) {
187
                $storenames[$key] = new \lang_string('store_'.$key, 'cache');
188
            } else {
189
                $storenames[$store['name']] = $store['name'];
190
            }
191
        }
192
        /* @var cache_definition[] $definitions */
193
        $definitions = [];
194
        $return = [];
195
        foreach ($config->get_definitions() as $key => $definition) {
196
            $definitions[$key] = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
197
        }
198
        foreach ($definitions as $id => $definition) {
199
            $mappings = array();
200
            foreach (cache_helper::get_stores_suitable_for_definition($definition) as $store) {
201
                $mappings[] = $storenames[$store->my_name()];
202
            }
203
            $return[$id] = array(
204
                'id' => $id,
205
                'name' => $definition->get_name(),
206
                'mode' => $definition->get_mode(),
207
                'component' => $definition->get_component(),
208
                'area' => $definition->get_area(),
209
                'mappings' => $mappings,
210
                'canuselocalstore' => $definition->can_use_localstore(),
211
                'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false),
212
                'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true),
213
                'userinputsharingkey' => $definition->get_user_input_sharing_key()
214
            );
215
        }
216
        return $return;
217
    }
218
 
219
    /**
220
     * Get the default stores for all modes.
221
     *
222
     * @return array An array containing sub-arrays, one for each mode.
223
     */
224
    public static function get_default_mode_stores(): array {
225
        global $OUTPUT;
226
        $instance = cache_config::instance();
227
        $adequatestores = cache_helper::get_stores_suitable_for_mode_default();
228
        $icon = new \pix_icon('i/warning', new \lang_string('inadequatestoreformapping', 'cache'));
229
        $storenames = array();
230
        foreach ($instance->get_all_stores() as $key => $store) {
231
            if (!empty($store['default'])) {
232
                $storenames[$key] = new \lang_string('store_'.$key, 'cache');
233
            }
234
        }
235
        $modemappings = array(
236
            cache_store::MODE_APPLICATION => array(),
237
            cache_store::MODE_SESSION => array(),
238
            cache_store::MODE_REQUEST => array(),
239
        );
240
        foreach ($instance->get_mode_mappings() as $mapping) {
241
            $mode = $mapping['mode'];
242
            if (!array_key_exists($mode, $modemappings)) {
243
                debugging('Unknown mode in cache store mode mappings', DEBUG_DEVELOPER);
244
                continue;
245
            }
246
            if (array_key_exists($mapping['store'], $storenames)) {
247
                $modemappings[$mode][$mapping['store']] = $storenames[$mapping['store']];
248
            } else {
249
                $modemappings[$mode][$mapping['store']] = $mapping['store'];
250
            }
251
            if (!array_key_exists($mapping['store'], $adequatestores)) {
252
                $modemappings[$mode][$mapping['store']] = $modemappings[$mode][$mapping['store']].' '.$OUTPUT->render($icon);
253
            }
254
        }
255
        return $modemappings;
256
    }
257
 
258
    /**
259
     * Returns an array summarising the locks available in the system.
260
     *
261
     * @return array array of lock summaries.
262
     */
263
    public static function get_lock_summaries(): array {
264
        $locks = array();
265
        $instance = cache_config::instance();
266
        $stores = $instance->get_all_stores();
267
        foreach ($instance->get_locks() as $lock) {
268
            $default = !empty($lock['default']);
269
            if ($default) {
270
                $name = new \lang_string($lock['name'], 'cache');
271
            } else {
272
                $name = $lock['name'];
273
            }
274
            $uses = 0;
275
            foreach ($stores as $store) {
276
                if (!empty($store['lock']) && $store['lock'] === $lock['name']) {
277
                    $uses++;
278
                }
279
            }
280
            $lockdata = array(
281
                'name' => $name,
282
                'default' => $default,
283
                'uses' => $uses,
284
                'type' => get_string('pluginname', $lock['type'])
285
            );
286
            $locks[$lock['name']] = $lockdata;
287
        }
288
        return $locks;
289
    }
290
 
291
    /**
292
     * Given a sharing option hash this function returns an array of strings that can be used to describe it.
293
     *
294
     * @param int $sharingoption The sharing option hash to get strings for.
295
     * @param bool $isselectedoptions Set to true if the strings will be used to view the selected options.
296
     * @return array An array of lang_string's.
297
     */
298
    public static function get_definition_sharing_options(int $sharingoption, bool $isselectedoptions = true): array {
299
        $options = array();
300
        $prefix = ($isselectedoptions) ? 'sharingselected' : 'sharing';
301
        if ($sharingoption & cache_definition::SHARING_ALL) {
302
            $options[cache_definition::SHARING_ALL] = new \lang_string($prefix.'_all', 'cache');
303
        }
304
        if ($sharingoption & cache_definition::SHARING_SITEID) {
305
            $options[cache_definition::SHARING_SITEID] = new \lang_string($prefix.'_siteid', 'cache');
306
        }
307
        if ($sharingoption & cache_definition::SHARING_VERSION) {
308
            $options[cache_definition::SHARING_VERSION] = new \lang_string($prefix.'_version', 'cache');
309
        }
310
        if ($sharingoption & cache_definition::SHARING_INPUT) {
311
            $options[cache_definition::SHARING_INPUT] = new \lang_string($prefix.'_input', 'cache');
312
        }
313
        return $options;
314
    }
315
 
316
    /**
317
     * Get an array of stores that are suitable to be used for a given definition.
318
     *
319
     * @param string $component
320
     * @param string $area
321
     * @return array Array containing 3 elements
322
     *      1. An array of currently used stores
323
     *      2. An array of suitable stores
324
     *      3. An array of default stores
325
     */
326
    public static function get_definition_store_options(string $component, string $area): array {
327
        $factory = cache_factory::instance();
328
        $definition = $factory->create_definition($component, $area);
329
        $config = cache_config::instance();
330
        $currentstores = $config->get_stores_for_definition($definition);
331
        $possiblestores = $config->get_stores($definition->get_mode(), $definition->get_requirements_bin());
332
 
333
        $defaults = array();
334
        foreach ($currentstores as $key => $store) {
335
            if (!empty($store['default'])) {
336
                $defaults[] = $key;
337
                unset($currentstores[$key]);
338
            }
339
        }
340
        foreach ($possiblestores as $key => $store) {
341
            if ($store['default']) {
342
                unset($possiblestores[$key]);
343
                $possiblestores[$key] = $store;
344
            }
345
        }
346
        return array($currentstores, $possiblestores, $defaults);
347
    }
348
 
349
    /**
350
     * This function must be implemented to display options for store plugins.
351
     *
352
     * @param string $name the name of the store plugin.
353
     * @param array $plugindetails array of store plugin details.
354
     * @return array array of actions.
355
     */
356
    public function get_store_plugin_actions(string $name, array $plugindetails): array {
357
        return array();
358
    }
359
 
360
    /**
361
     * This function must be implemented to display options for store instances.
362
     *
363
     * @param string $name the store instance name.
364
     * @param array $storedetails array of store instance details.
365
     * @return array array of actions.
366
     */
367
    public function get_store_instance_actions(string $name, array $storedetails): array {
368
        return array();
369
    }
370
 
371
    /**
372
     * This function must be implemented to display options for definition mappings.
373
     *
374
     * @param context $context the context for the definition.
375
     * @param array $definitionsummary the definition summary.
376
     * @return array array of actions.
377
     */
378
    public function get_definition_actions(\context $context, array $definitionsummary): array {
379
        return array();
380
    }
381
 
382
    /**
383
     * This function must be implemented to get addable locks.
384
     *
385
     * @return array array of locks that are addable.
386
     */
387
    public function get_addable_lock_options(): array {
388
        return array();
389
    }
390
 
391
    /**
392
     * This function must be implemented to perform any page actions by a child class.
393
     *
394
     * @param string $action the action to perform.
395
     * @param array $forminfo empty array to be set by actions.
396
     * @return array array of form info.
397
     */
398
    abstract public function perform_cache_actions(string $action, array $forminfo): array;
399
 
400
    /**
401
     * This function must be implemented to display the cache admin page.
402
     *
403
     * @param \core_cache\output\renderer $renderer the renderer used to generate the page.
404
     * @return string the HTML for the page.
405
     */
406
    abstract public function generate_admin_page(\core_cache\output\renderer $renderer): string;
407
 
408
    /**
409
     * Gets usage information about the whole cache system.
410
     *
411
     * This is a slow function and should only be used on an admin information page.
412
     *
413
     * The returned array lists all cache definitions with fields 'cacheid' and 'stores'. For
414
     * each store, the following fields are available:
415
     *
416
     * - name (store name)
417
     * - class (e.g. cachestore_redis)
418
     * - supported (true if we have any information)
419
     * - items (number of items stored)
420
     * - mean (mean size of item)
421
     * - sd (standard deviation for item sizes)
422
     * - margin (margin of error for mean at 95% confidence)
423
     * - storetotal (total usage for store if known, otherwise null)
424
     *
425
     * The storetotal field will be the same for every cache that uses the same store.
426
     *
427
     * @param int $samplekeys Number of keys to sample when checking size of large caches
428
     * @return array Details of cache usage
429
     */
430
    abstract public function get_usage(int $samplekeys): array;
431
}