public List<Node> deallocateRecursively(String hostname, Agent agent, String reason) {
        Node nodeToDirty = node(hostname).orElseThrow(() -> new NoSuchNodeException("Could not deallocate " + hostname + ": Node not found"));
        List<Node> nodesToDirty = new ArrayList<>();
        try (RecursiveNodeMutexes locked = lockAndGetRecursively(hostname, Optional.empty())) {
            for (NodeMutex child : locked.children())
                if (child.node().state() != Node.State.dirty)
                    nodesToDirty.add(child.node());

            if (locked.parent().node().state() != State.dirty)
                nodesToDirty.add(locked.parent().node());

            List<String> hostnamesNotAllowedToDirty = nodesToDirty.stream()
                                                                  .filter(node -> node.state() != Node.State.provisioned)
                                                                  .filter(node -> node.state() != Node.State.failed)
                                                                  .filter(node -> node.state() != Node.State.parked)
                                                                  .filter(node -> node.state() != Node.State.breakfixed)
                                                                  .map(Node::hostname).toList();
            if ( ! hostnamesNotAllowedToDirty.isEmpty())
                illegal("Could not deallocate " + nodeToDirty + ": " +
                        hostnamesNotAllowedToDirty + " are not in states [provisioned, failed, parked, breakfixed]");

            return nodesToDirty.stream().map(node -> deallocate(node, agent, reason)).toList();
        }
    }
Move nodes to the dirty state
node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java