Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 1441
Línea 1338... Línea 1338...
1338
     */
1338
     */
1339
    public function is_installed() {
1339
    public function is_installed() {
1340
        return function_exists('solr_get_version');
1340
        return function_exists('solr_get_version');
1341
    }
1341
    }
Línea -... Línea 1342...
-
 
1342
 
-
 
1343
    /** @var int When using the capath option, we generate a bundle containing all the pem files, cached 10 mins. */
-
 
1344
    const CA_PATH_CACHE_TIME = 600;
-
 
1345
 
-
 
1346
    /** @var int Expired cache files are deleted after this many seconds. */
-
 
1347
    const CA_PATH_CACHE_DELETE_AFTER = 60;
-
 
1348
 
-
 
1349
    /**
-
 
1350
     * Gets status of Solr server.
-
 
1351
     *
-
 
1352
     * The result has the following fields:
-
 
1353
     * - connected - true if we got a valid JSON response from server
-
 
1354
     * - foundcore - true if we found the core defined in config (this could be false if schema not set up)
-
 
1355
     *
-
 
1356
     * It may have these other fields:
-
 
1357
     * - error - text if anything went wrong
-
 
1358
     * - exception - if an exception was thrown
-
 
1359
     * - indexsize - index size in bytes if we found what it is
-
 
1360
     *
-
 
1361
     * @param int $timeout Optional timeout in seconds, otherwise uses config value
-
 
1362
     * @return array Array with information about status
-
 
1363
     * @since Moodle 5.0
-
 
1364
     */
-
 
1365
    public function get_status($timeout = 0): array {
-
 
1366
        $result = ['connected' => false, 'foundcore' => false];
-
 
1367
        try {
-
 
1368
            $options = [];
-
 
1369
            if ($timeout) {
-
 
1370
                $options['connect_timeout'] = $timeout;
-
 
1371
                $options['read_timeout'] = $timeout;
-
 
1372
            }
-
 
1373
            $before = microtime(true);
-
 
1374
            try {
-
 
1375
                $response = $this->raw_get_request('admin/cores', $options);
-
 
1376
            } finally {
-
 
1377
                $result['time'] = microtime(true) - $before;
-
 
1378
            }
-
 
1379
            $status = $response->getStatusCode();
-
 
1380
            if ($status !== 200) {
-
 
1381
                $result['error'] = 'Unsuccessful status code: ' . $status;
-
 
1382
                return $result;
-
 
1383
            }
-
 
1384
            $decoded = json_decode($response->getBody()->getContents());
-
 
1385
            if (!$decoded) {
-
 
1386
                $result['error'] = 'Invalid JSON';
-
 
1387
                return $result;
-
 
1388
            }
-
 
1389
            // Provided we get some valid JSON then probably Solr exists and is responding.
-
 
1390
            // Any following errors we don't count as not connected (ERROR display in the check)
-
 
1391
            // because maybe it happens if Solr changes their JSON format in a future version.
-
 
1392
            $result['connected'] = true;
-
 
1393
            if (!property_exists($decoded, 'status')) {
-
 
1394
                $result['error'] = 'Unexpected JSON: no core status';
-
 
1395
                return $result;
-
 
1396
            }
-
 
1397
            foreach ($decoded->status as $core) {
-
 
1398
                $match = false;
-
 
1399
                if (!property_exists($core, 'name')) {
-
 
1400
                    $result['error'] = 'Unexpected JSON: core has no name';
-
 
1401
                    return $result;
-
 
1402
                }
-
 
1403
                if ($core->name === $this->config->indexname) {
-
 
1404
                    $match = true;
-
 
1405
                }
-
 
1406
                if (!$match && property_exists($core, 'cloud')) {
-
 
1407
                    if (!property_exists($core->cloud, 'collection')) {
-
 
1408
                        $result['error'] = 'Unexpected JSON: core cloud has no name';
-
 
1409
                        return $result;
-
 
1410
                    }
-
 
1411
                    if ($core->cloud->collection === $this->config->indexname) {
-
 
1412
                        $match = true;
-
 
1413
                    }
-
 
1414
                }
-
 
1415
 
-
 
1416
                if ($match) {
-
 
1417
                    $result['foundcore'] = true;
-
 
1418
                    if (!property_exists($core, 'index')) {
-
 
1419
                        $result['error'] = 'Unexpected JSON: core has no index';
-
 
1420
                        return $result;
-
 
1421
                    }
-
 
1422
                    if (!property_exists($core->index, 'sizeInBytes')) {
-
 
1423
                        $result['error'] = 'Unexpected JSON: core index has no sizeInBytes';
-
 
1424
                        return $result;
-
 
1425
                    }
-
 
1426
                    $result['indexsize'] = $core->index->sizeInBytes;
-
 
1427
                    return $result;
-
 
1428
                }
-
 
1429
            }
-
 
1430
            $result['error'] = 'Could not find core matching ' . $this->config->indexname;;
-
 
1431
            return $result;
-
 
1432
        } catch (\Throwable $t) {
-
 
1433
            $result['error'] = 'Exception occurred: ' . $t->getMessage();
-
 
1434
            $result['exception'] = $t;
-
 
1435
            return $result;
-
 
1436
        }
-
 
1437
    }
1342
 
1438
 
1343
    /**
1439
    /**
1344
     * Returns the solr client instance.
1440
     * Returns the solr client instance.
1345
     *
1441
     *
1346
     * We don't reuse SolrClient if we are on libcurl 7.35.0, due to a bug in that version of curl.
1442
     * We don't reuse SolrClient if we are on libcurl 7.35.0, due to a bug in that version of curl.
Línea 1451... Línea 1547...
1451
 
1547
 
1452
        return $this->curl;
1548
        return $this->curl;
Línea 1453... Línea 1549...
1453
    }
1549
    }
1454
 
1550
 
1455
    /**
1551
    /**
1456
     * Return a Moodle url object for the server connection.
1552
     * Return a Moodle url object for the raw server URL (containing all indexes).
1457
     *
1553
     *
1458
     * @param string $path The solr path to append.
1554
     * @param string $path The solr path to append.
1459
     * @return \moodle_url
1555
     * @return \moodle_url
1460
     */
1556
     */
1461
    public function get_connection_url($path) {
1557
    public function get_server_url(string $path): \moodle_url {
1462
        // Must use the proper protocol, or SSL will fail.
1558
        // Must use the proper protocol, or SSL will fail.
1463
        $protocol = !empty($this->config->secure) ? 'https' : 'http';
1559
        $protocol = !empty($this->config->secure) ? 'https' : 'http';
1464
        $url = $protocol . '://' . rtrim($this->config->server_hostname, '/');
1560
        $url = $protocol . '://' . rtrim($this->config->server_hostname, '/');
1465
        if (!empty($this->config->server_port)) {
1561
        if (!empty($this->config->server_port)) {
1466
            $url .= ':' . $this->config->server_port;
1562
            $url .= ':' . $this->config->server_port;
1467
        }
-
 
1468
        $url .= '/solr/' . $this->config->indexname . '/' . ltrim($path, '/');
1563
        }
1469
 
1564
        $url .= '/solr/' . ltrim($path, '/');
Línea 1470... Línea 1565...
1470
        return new \moodle_url($url);
1565
        return new \moodle_url($url);
-
 
1566
    }
-
 
1567
 
-
 
1568
    /**
-
 
1569
     * Return a Moodle url object for the server connection including the search index.
-
 
1570
     *
-
 
1571
     * @param string $path The solr path to append.
-
 
1572
     * @return \moodle_url
-
 
1573
     */
-
 
1574
    public function get_connection_url($path) {
-
 
1575
        return $this->get_server_url($this->config->indexname . '/' . ltrim($path, '/'));
-
 
1576
    }
-
 
1577
 
-
 
1578
    /**
-
 
1579
     * Calls the Solr engine with a GET request (for things the Solr extension doesn't support).
-
 
1580
     *
-
 
1581
     * This has similar result to get_curl_object but uses the newer (mockable) Guzzle HTTP client.
-
 
1582
     *
-
 
1583
     * @param string $path URL path (after /solr/) e.g. 'admin/cores?action=STATUS&core=frog'
-
 
1584
     * @param array $overrideoptions Optional array of Guzzle options, will override config
-
 
1585
     * @return \Psr\Http\Message\ResponseInterface Response message from Guzzle
-
 
1586
     * @throws \GuzzleHttp\Exception\GuzzleException If any problem connecting
-
 
1587
     * @since Moodle 5.0
-
 
1588
     */
-
 
1589
    public function raw_get_request(
-
 
1590
        string $path,
-
 
1591
        array $overrideoptions = [],
-
 
1592
    ): \Psr\Http\Message\ResponseInterface {
-
 
1593
        $client = \core\di::get(\core\http_client::class);
-
 
1594
        return $client->get(
-
 
1595
            $this->get_server_url($path)->out(false),
-
 
1596
            $this->get_http_client_options($overrideoptions),
-
 
1597
        );
-
 
1598
    }
-
 
1599
 
-
 
1600
    /**
-
 
1601
     * Gets the \core\http_client options for a connection.
-
 
1602
     *
-
 
1603
     * @param array $overrideoptions Optional array to override some of the options
-
 
1604
     * @return array Array of http_client options
-
 
1605
     */
-
 
1606
    protected function get_http_client_options(array $overrideoptions = []): array {
-
 
1607
        $options = [
-
 
1608
            'connect_timeout' => !empty($this->config->server_timeout) ? (int)$this->config->server_timeout : 30,
-
 
1609
        ];
-
 
1610
        $options['read_timeout'] = $options['connect_timeout'];
-
 
1611
        if (!empty($this->config->server_username)) {
-
 
1612
            $options['auth'] = [$this->config->server_username, $this->config->server_password];
-
 
1613
        }
-
 
1614
        if (!empty($this->config->ssl_cert)) {
-
 
1615
            $options['cert'] = $this->config->ssl_cert;
-
 
1616
        }
-
 
1617
        if (!empty($this->config->ssl_key)) {
-
 
1618
            if (!empty($this->config->ssl_keypassword)) {
-
 
1619
                $options['ssl_key'] = [$this->config->ssl_key, $this->config->ssl_keypassword];
-
 
1620
            } else {
-
 
1621
                $options['ssl_key'] = $this->config->ssl_key;
-
 
1622
            }
-
 
1623
        }
-
 
1624
        if (!empty($this->config->ssl_cainfo)) {
-
 
1625
            $options['verify'] = $this->config->ssl_cainfo;
-
 
1626
        } else if (!empty($this->config->ssl_capath)) {
-
 
1627
            // Guzzle doesn't support a whole path of CA certs, so we have to make a single file
-
 
1628
            // with all the *.pem files in that directory. It needs to be in filesystem so we can
-
 
1629
            // use it directly, let's put it in local cache for 10 minutes.
-
 
1630
            $cachefolder = make_localcache_directory('search_solr');
-
 
1631
            $prefix = 'capath.' . sha1($this->config->ssl_capath);
-
 
1632
            $now = \core\di::get(\core\clock::class)->time();
-
 
1633
            $got = false;
-
 
1634
            foreach (scandir($cachefolder) as $filename) {
-
 
1635
                // You are not allowed to overwrite files in localcache folders so we use files
-
 
1636
                // with the time in, and delete old files with a 1 minute delay to avoid race
-
 
1637
                // conditions.
-
 
1638
                if (preg_match('~^(.*)\.([0-9]+)$~', $filename, $matches)) {
-
 
1639
                    [1 => $fileprefix, 2 => $time] = $matches;
-
 
1640
                    $pathname = $cachefolder . '/' . $filename;
-
 
1641
                    if ($time > $now - self::CA_PATH_CACHE_TIME && $fileprefix === $prefix) {
-
 
1642
                        $options['verify'] = $pathname;
-
 
1643
                        $got = true;
-
 
1644
                        break;
-
 
1645
                    } else if ($time <= $now - self::CA_PATH_CACHE_TIME - self::CA_PATH_CACHE_DELETE_AFTER) {
-
 
1646
                        unlink($pathname);
-
 
1647
                    }
-
 
1648
                }
-
 
1649
            }
-
 
1650
 
-
 
1651
            if (!$got) {
-
 
1652
                // If we don't have it yet, we need to make the cached file.
-
 
1653
                $allpems = '';
-
 
1654
                foreach (scandir($this->config->ssl_capath) as $filename) {
-
 
1655
                    if (preg_match('~\.pem$~', $filename)) {
-
 
1656
                        $pathname = $this->config->ssl_capath . '/' . $filename;
-
 
1657
                        $allpems .= file_get_contents($pathname) . "\n\n";
-
 
1658
                    }
-
 
1659
                }
-
 
1660
                $pathname = $cachefolder . '/' . $prefix . '.' . $now;
-
 
1661
                file_put_contents($pathname, $allpems);
-
 
1662
                $options['verify'] = $pathname;
-
 
1663
            }
-
 
1664
        }
-
 
1665
 
-
 
1666
        // Apply other/overridden options.
-
 
1667
        foreach ($overrideoptions as $name => $value) {
-
 
1668
            $options[$name] = $value;
-
 
1669
        }
-
 
1670
 
-
 
1671
        return $options;
1471
    }
1672
    }
1472
 
1673
 
1473
    /**
1674
    /**
1474
     * Solr includes group support in the execute_query function.
1675
     * Solr includes group support in the execute_query function.
1475
     *
1676
     *