Merge pull request #51818 from nextcloud/info-storage-command

feat: add command to get basic storage info
pull/52870/head
Robin Appelman 5 months ago committed by GitHub
commit 07fa9b9311
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 102
      core/Command/Info/FileUtils.php
  2. 49
      core/Command/Info/Storage.php
  3. 43
      core/Command/Info/Storages.php
  4. 4
      core/register_command.php
  5. 2
      lib/composer/composer/autoload_classmap.php
  6. 2
      lib/composer/composer/autoload_static.php

@ -14,6 +14,7 @@ use OCA\Files_External\Config\ExternalMountPoint;
use OCA\Files_Sharing\SharedMount;
use OCA\GroupFolders\Mount\GroupMountPoint;
use OCP\Constants;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\FileInfo;
use OCP\Files\Folder;
@ -23,14 +24,19 @@ use OCP\Files\Mount\IMountPoint;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IDBConnection;
use OCP\Share\IShare;
use OCP\Util;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @psalm-type StorageInfo array{numeric_id: int, id: string, available: bool, last_checked: ?\DateTime, files: int, mount_id: ?int}
*/
class FileUtils {
public function __construct(
private IRootFolder $rootFolder,
private IUserMountCache $userMountCache,
private IDBConnection $connection,
) {
}
@ -220,4 +226,100 @@ class FileUtils {
}
return $count;
}
public function getNumericStorageId(string $id): ?int {
if (is_numeric($id)) {
return (int)$id;
}
$query = $this->connection->getQueryBuilder();
$query->select('numeric_id')
->from('storages')
->where($query->expr()->eq('id', $query->createNamedParameter($id)));
$result = $query->executeQuery()->fetchOne();
return $result ? (int)$result : null;
}
/**
* @param int|null $limit
* @return ?StorageInfo
* @throws \OCP\DB\Exception
*/
public function getStorage(int $id): ?array {
$query = $this->connection->getQueryBuilder();
$query->select('numeric_id', 's.id', 'available', 'last_checked', 'mount_id')
->selectAlias($query->func()->count('fileid'), 'files')
->from('storages', 's')
->innerJoin('s', 'filecache', 'f', $query->expr()->eq('f.storage', 's.numeric_id'))
->leftJoin('s', 'mounts', 'm', $query->expr()->eq('s.numeric_id', 'm.storage_id'))
->where($query->expr()->eq('s.numeric_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
->groupBy('s.numeric_id', 's.id', 's.available', 's.last_checked', 'mount_id');
$row = $query->executeQuery()->fetch();
if ($row) {
return [
'numeric_id' => $row['numeric_id'],
'id' => $row['id'],
'files' => $row['files'],
'available' => (bool)$row['available'],
'last_checked' => $row['last_checked'] ? new \DateTime('@' . $row['last_checked']) : null,
'mount_id' => $row['mount_id'],
];
} else {
return null;
}
}
/**
* @param int|null $limit
* @return \Iterator<StorageInfo>
* @throws \OCP\DB\Exception
*/
public function listStorages(?int $limit): \Iterator {
$query = $this->connection->getQueryBuilder();
$query->select('numeric_id', 's.id', 'available', 'last_checked', 'mount_id')
->selectAlias($query->func()->count('fileid'), 'files')
->from('storages', 's')
->innerJoin('s', 'filecache', 'f', $query->expr()->eq('f.storage', 's.numeric_id'))
->leftJoin('s', 'mounts', 'm', $query->expr()->eq('s.numeric_id', 'm.storage_id'))
->groupBy('s.numeric_id', 's.id', 's.available', 's.last_checked', 'mount_id')
->orderBy('files', 'DESC');
if ($limit !== null) {
$query->setMaxResults($limit);
}
$result = $query->executeQuery();
while ($row = $result->fetch()) {
yield [
'numeric_id' => $row['numeric_id'],
'id' => $row['id'],
'files' => $row['files'],
'available' => (bool)$row['available'],
'last_checked' => $row['last_checked'] ? new \DateTime('@' . $row['last_checked']) : null,
'mount_id' => $row['mount_id'],
];
}
}
/**
* @param StorageInfo $storage
* @return array
*/
public function formatStorage(array $storage): array {
return [
'numeric_id' => $storage['numeric_id'],
'id' => $storage['id'],
'files' => $storage['files'],
'available' => $storage['available'] ? 'true' : 'false',
'last_checked' => $storage['last_checked']?->format(\DATE_ATOM),
'external_mount_id' => $storage['mount_id'],
];
}
/**
* @param \Iterator<StorageInfo> $storages
* @return \Iterator
*/
public function formatStorages(\Iterator $storages): \Iterator {
foreach ($storages as $storage) {
yield $this->formatStorage($storage);
}
}
}

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Core\Command\Info;
use OC\Core\Command\Base;
use OCP\IDBConnection;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class Storage extends Base {
public function __construct(
private readonly IDBConnection $connection,
private readonly FileUtils $fileUtils,
) {
parent::__construct();
}
protected function configure(): void {
parent::configure();
$this
->setName('info:storage')
->setDescription('Get information a single storage')
->addArgument('storage', InputArgument::REQUIRED, 'Storage to get information for');
}
public function execute(InputInterface $input, OutputInterface $output): int {
$storage = $input->getArgument('storage');
$storageId = $this->fileUtils->getNumericStorageId($storage);
if (!$storageId) {
$output->writeln('<error>No storage with id ' . $storage . ' found</error>');
return 1;
}
$info = $this->fileUtils->getStorage($storageId);
if (!$info) {
$output->writeln('<error>No storage with id ' . $storage . ' found</error>');
return 1;
}
$this->writeArrayInOutputFormat($input, $output, $this->fileUtils->formatStorage($info));
return 0;
}
}

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Core\Command\Info;
use OC\Core\Command\Base;
use OCP\IDBConnection;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Storages extends Base {
public function __construct(
private readonly IDBConnection $connection,
private readonly FileUtils $fileUtils,
) {
parent::__construct();
}
protected function configure(): void {
parent::configure();
$this
->setName('info:storages')
->setDescription('List storages ordered by the number of files')
->addOption('count', 'c', InputOption::VALUE_REQUIRED, 'Number of storages to display', 25)
->addOption('all', 'a', InputOption::VALUE_NONE, 'Display all storages');
}
public function execute(InputInterface $input, OutputInterface $output): int {
$count = (int)$input->getOption('count');
$all = $input->getOption('all');
$limit = $all ? null : $count;
$storages = $this->fileUtils->listStorages($limit);
$this->writeStreamingTableInOutputFormat($input, $output, $this->fileUtils->formatStorages($storages), 100);
return 0;
}
}

@ -53,6 +53,8 @@ use OC\Core\Command\Group\AddUser;
use OC\Core\Command\Group\RemoveUser;
use OC\Core\Command\Info\File;
use OC\Core\Command\Info\Space;
use OC\Core\Command\Info\Storage;
use OC\Core\Command\Info\Storages;
use OC\Core\Command\Integrity\CheckApp;
use OC\Core\Command\Integrity\CheckCore;
use OC\Core\Command\Integrity\SignApp;
@ -144,6 +146,8 @@ if ($config->getSystemValueBool('installed', false)) {
$application->add(Server::get(File::class));
$application->add(Server::get(Space::class));
$application->add(Server::get(Storage::class));
$application->add(Server::get(Storages::class));
$application->add(Server::get(ConvertType::class));
$application->add(Server::get(ConvertMysqlToMB4::class));

@ -1266,6 +1266,8 @@ return array(
'OC\\Core\\Command\\Info\\File' => $baseDir . '/core/Command/Info/File.php',
'OC\\Core\\Command\\Info\\FileUtils' => $baseDir . '/core/Command/Info/FileUtils.php',
'OC\\Core\\Command\\Info\\Space' => $baseDir . '/core/Command/Info/Space.php',
'OC\\Core\\Command\\Info\\Storage' => $baseDir . '/core/Command/Info/Storage.php',
'OC\\Core\\Command\\Info\\Storages' => $baseDir . '/core/Command/Info/Storages.php',
'OC\\Core\\Command\\Integrity\\CheckApp' => $baseDir . '/core/Command/Integrity/CheckApp.php',
'OC\\Core\\Command\\Integrity\\CheckCore' => $baseDir . '/core/Command/Integrity/CheckCore.php',
'OC\\Core\\Command\\Integrity\\SignApp' => $baseDir . '/core/Command/Integrity/SignApp.php',

@ -1307,6 +1307,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Command\\Info\\File' => __DIR__ . '/../../..' . '/core/Command/Info/File.php',
'OC\\Core\\Command\\Info\\FileUtils' => __DIR__ . '/../../..' . '/core/Command/Info/FileUtils.php',
'OC\\Core\\Command\\Info\\Space' => __DIR__ . '/../../..' . '/core/Command/Info/Space.php',
'OC\\Core\\Command\\Info\\Storage' => __DIR__ . '/../../..' . '/core/Command/Info/Storage.php',
'OC\\Core\\Command\\Info\\Storages' => __DIR__ . '/../../..' . '/core/Command/Info/Storages.php',
'OC\\Core\\Command\\Integrity\\CheckApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckApp.php',
'OC\\Core\\Command\\Integrity\\CheckCore' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckCore.php',
'OC\\Core\\Command\\Integrity\\SignApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/SignApp.php',

Loading…
Cancel
Save