@Override
    protected double maintain() {
        int attempts = 0;
        int failures = 0;
        NodeList rebuildCandidates = nodeRepository().nodes().list().rebuilding(true);
        try (var locked = nodeRepository().nodes().lockAndGetAll(rebuildCandidates.asList(), Optional.of(Duration.ofSeconds(10)))) {
            List<Node> rebuilding = locked.nodes().stream()
                    .map(NodeMutex::node)
                    .filter(node -> node.status().wantToRebuild())
                    .toList();
            RebuildResult result = hostProvisioner.replaceRootDisk(rebuilding);
            for (Node updated : result.completed()) {
                if (!updated.status().wantToRebuild()) {
                    nodeRepository().nodes().write(updated, () -> {});
                }
            }
            for (var entry : result.failed().entrySet()) {
                ++failures;
                log.log(Level.WARNING, "Failed to replace root disk on " + entry.getKey() + ", will retry in " +
                        interval() + ": " + Exceptions.toMessageString(entry.getValue()));
            }
            attempts += result.completed().size() + result.failed().size();
        }

        NodeList hostsBootingAfterRebuild = nodeRepository().nodes().list().bootingAfterRebuild();
        try (var locked = nodeRepository().nodes().lockAndGetAll(hostsBootingAfterRebuild.asList(), Optional.of(Duration.ofSeconds(10)))) {
            List<Node> starting = locked.nodes().stream()
                    .map(NodeMutex::node)
                    .filter(node -> node.status().bootingAfterRebuild())
                    .toList();
            RebuildResult startResult = hostProvisioner.startHosts(starting);

            for (Node updated : startResult.completed()) {
                NodeList children = nodeRepository().nodes().list().childrenOf(updated);
                restoreSnapshotsOf(children);
                nodeRepository().nodes().write(updated, () -> {});
            }

            for (var entry : startResult.failed().entrySet()) {
                ++failures;
                log.log(Level.WARNING, "Failed to start " + entry.getKey() + ", will retry in " +
                        interval() + ": " + Exceptions.toMessageString(entry.getValue()));
            }
            attempts += startResult.completed().size() + startResult.failed().size();
        }

        return asSuccessFactorDeviation(attempts, failures);
    }
Rebuilds hosts by replacing the root disk (only supports hosts with remote storage).

@author mpolden
node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DiskReplacer.java