mirror of https://github.com/micromata/borgbackup-butler.git

Kai Reinhard
05.03.2019 67dd1243073e766178dd70dd2f45aa5bc77ec529
Job monitor: Cancellation of jobs...
9 files modified
129 ■■■■ changed files
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java 8 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java 1 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java 28 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractJob.java 4 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/JobQueue.java 16 ●●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java 34 ●●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/queue/JsonJob.java 4 ●●●● patch | view | raw | blame | history
borgbutler-webapp/src/components/views/jobs/Job.jsx 32 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java
@@ -35,7 +35,7 @@
                .setParams("--version")
                .setDescription("Getting borg version.");
        JobResult<String> jobResult = execute(command).getResult();
        if (jobResult.getStatus() != JobResult.Status.OK) {
        if (jobResult == null || jobResult.getStatus() != JobResult.Status.OK) {
            return null;
        }
        String version = jobResult.getResultObject();
@@ -56,7 +56,7 @@
                .setParams("--json") // --progress has no effect.
                .setDescription("Loading info of repo '" + repoConfig.getDisplayName() + "'.");
        JobResult<String> jobResult = execute(command).getResult();
        if (jobResult.getStatus() != JobResult.Status.OK) {
        if (jobResult == null || jobResult.getStatus() != JobResult.Status.OK) {
            return null;
        }
        String result = jobResult.getResultObject();
@@ -89,7 +89,7 @@
                .setParams("--json") // --progress has no effect.
                .setDescription("Loading list of archives of repo '" + repoConfig.getDisplayName() + "'.");
        JobResult<String> jobResult = execute(command).getResult();
        if (jobResult.getStatus() != JobResult.Status.OK) {
        if (jobResult == null || jobResult.getStatus() != JobResult.Status.OK) {
            log.error("Can't load archives from repo '" + repository.getName() + "'.");
            return;
        }
@@ -131,7 +131,7 @@
                .setParams("--json", "--log-json", "--progress")
                .setDescription("Loading info of archive '" + archive.getName() + "' of repo '" + repoConfig.getDisplayName() + "'.");
        JobResult<String> jobResult = execute(command).getResult();
        if (jobResult.getStatus() != JobResult.Status.OK) {
        if (jobResult == null || jobResult.getStatus() != JobResult.Status.OK) {
            return;
        }
        String result = jobResult.getResultObject();
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java
@@ -108,6 +108,7 @@
    @Override
    public BorgJob<?> clone() {
        BorgJob<?> clone = new BorgJob<>();
        clone.setUniqueJobNumber(getUniqueJobNumber());
        clone.setTitle(getTitle());
        clone.setExecuteStarted(isExecuteStarted());
        clone.setCommandLineAsString(getCommandLineAsString());
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java
@@ -28,11 +28,33 @@
     */
    public List<String> getRepos() {
        List<String> list = new ArrayList<>();
        list.addAll(queueMap.keySet());
        synchronized (queueMap) {
            list.addAll(queueMap.keySet());
        }
        Collections.sort(list);
        return list;
    }
    public void cancelJob(long uniqueJobNumber) {
        AbstractJob<?> job = getQueuedJobByUniqueJobNumber(uniqueJobNumber);
        if (job == null) {
            log.info("Can't cancel job #" + uniqueJobNumber + ". Not found as queued job (may-be already cancelled or finished). Nothing to do.");
            return;
        }
        job.cancel();
    }
    private AbstractJob<?> getQueuedJobByUniqueJobNumber(long uniqueJobNumber) {
        Iterator<JobQueue<String>> it = queueMap.values().iterator();
        while (it.hasNext()) {
            AbstractJob<?> job = it.next().getQueuedJobByUniqueJobNumber(uniqueJobNumber);
            if (job != null) {
                return job;
            }
        }
        return null;
    }
    /**
     * For displaying purposes.
     *
@@ -56,7 +78,9 @@
    }
    private JobQueue<String> getQueue(String repo) {
        return queueMap.get(getQueueName(repo));
        synchronized (queueMap) {
            return queueMap.get(getQueueName(repo));
        }
    }
    private JobQueue<String> ensureAndGetQueue(String repo) {
borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java
@@ -124,7 +124,7 @@
    @Override
    protected void cancelRunningProcess() {
        if (watchdog != null) {
            log.info("Cancelling job: " + getId());
            log.info("Cancelling job #" + getUniqueJobNumber() + ": " + getId());
            watchdog.destroyProcess();
            watchdog = null;
            setCancelled();
borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractJob.java
@@ -27,8 +27,8 @@
    @Setter(AccessLevel.PACKAGE)
    private Future<JobResult<T>> future;
    @Getter
    @Setter(AccessLevel.PACKAGE)
    private long uniqueJobNumber;
    @Setter(AccessLevel.PROTECTED)
    private long uniqueJobNumber = -1;
    public void cancel() {
        if (this.getStatus() == Status.QUEUED) {
borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/JobQueue.java
@@ -29,6 +29,22 @@
    }
    /**
     * Searches only for queued jobs (not done jobs).
     * @param uniqueJobNumber
     * @return The job if any job with the given unique job number is queued, otherwise null.
     */
    public AbstractJob<T> getQueuedJobByUniqueJobNumber(long uniqueJobNumber) {
        Iterator<AbstractJob<T>> it = queue.iterator();
        while (it.hasNext()) {
            AbstractJob<T> job = it.next();
            if (job.getUniqueJobNumber() == uniqueJobNumber) {
                return job;
            }
        }
        return null;
    }
    /**
     * Appends the job if not alread in the queue. Starts the execution if no execution thread is already running.
     *
     * @param job
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java
@@ -59,34 +59,43 @@
    }
    @Path("/cancel")
    @PUT
    @Produces(MediaType.APPLICATION_JSON)
    @GET
    /**
     * @param testMode If true, then a test job list is created.
     * @param prettyPrinter If true then the json output will be in pretty format.
     * @return Job queues as json string.
     * @see JsonUtils#toJson(Object, boolean)
     * @param uniqueJobNumberString The id of the job to cancel.
     */
    public void cancelJob(@QueryParam("job") String job, @QueryParam("prettyPrinter") boolean prettyPrinter) {
        log.info("Cancelling job");
    public void cancelJob(@QueryParam("uniqueJobNumber") String uniqueJobNumberString) {
        Long uniqueJobNumber = null;
        try {
            uniqueJobNumber = Long.parseLong(uniqueJobNumberString);
        } catch (NumberFormatException ex) {
            log.error("Can't cancel job, because unique job number couln't be parsed (long value expected): " + uniqueJobNumberString);
            return;
        }
        BorgQueueExecutor.getInstance().cancelJob(uniqueJobNumber);
    }
    /**
     * Only for test purposes and development.
     *
     * @param prettyPrinter
     * @return
     */
    private String returnTestList(boolean prettyPrinter) {
        if (testList == null) {
            testList = new ArrayList<>();
            long uniqueJobNumber = 100000;
            JsonJobQueue queue = new JsonJobQueue().setRepo("My Computer");
            addTestJob(queue, "info", "my-macbook", 0, 2342);
            addTestJob(queue, "list", "my-macbook", -1, -1);
            addTestJob(queue, "info", "my-macbook", 0, 2342)
                    .setUniqueJobNumber(uniqueJobNumber++);
            addTestJob(queue, "list", "my-macbook", -1, -1)
                    .setUniqueJobNumber(uniqueJobNumber++);
            testList.add(queue);
            queue = new JsonJobQueue().setRepo("My Server");
            addTestJob(queue, "list", "my-server", 0, 1135821);
            addTestJob(queue, "info", "my-server", -1, -1);
            addTestJob(queue, "list", "my-server", 0, 1135821)
                    .setUniqueJobNumber(uniqueJobNumber++);
            addTestJob(queue, "info", "my-server", -1, -1)
                    .setUniqueJobNumber(uniqueJobNumber++);
            testList.add(queue);
        } else {
            for (JsonJobQueue jobQueue : testList) {
@@ -117,6 +126,7 @@
    /**
     * Only for test purposes and development.
     *
     * @param queue
     * @param operation
     * @param host
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/queue/JsonJob.java
@@ -31,11 +31,15 @@
    @Getter
    @Setter
    private String commandLineAsString;
    @Getter
    @Setter
    private long uniqueJobNumber;
    public JsonJob() {
    }
    public JsonJob(BorgJob<?> borgJob) {
        this.uniqueJobNumber = borgJob.getUniqueJobNumber();
        this.cancellationRequested = borgJob.isCancellationRequested();
        this.status = borgJob.getStatus();
        this.title = borgJob.getTitle();
borgbutler-webapp/src/components/views/jobs/Job.jsx
@@ -1,14 +1,22 @@
import React from 'react';
import {Button, Card, CardBody, Collapse, Progress} from 'reactstrap';
import {IconCancelJob} from '../../general/IconComponents'
import {IconCancel} from '../../general/IconComponents'
import {getRestServiceUrl} from "../../../utilities/global";
class Job extends React.Component {
    constructor(props) {
        super(props);
        this.toggle = this.toggle.bind(this);
        this.cacnelJob = this.cancelJob.bind(this);
        this.state = {collapse: false};
    }
    cancelJob = (jobId) => {
        fetch(getRestServiceUrl('jobs/cancel', {
            uniqueJobNumber: jobId
        }));
    };
    toggle() {
        this.setState({collapse: !this.state.collapse});
    }
@@ -25,15 +33,31 @@
        } else {
            content = <Progress color={'info'} value={100}>{job.status}</Progress>
        }
        let cancelDisabled = undefined;
        if (job.status !== 'RUNNING' && job.status !== 'QUEUED') {
            cancelDisabled = true;
        }
        return (
            <div>
                <Button color="link" onClick={this.toggle}>{job.description}</Button>
                <div>{content}<IconCancelJob/></div>
                <div>{content}
                    <Button color={'danger'} onClick={() => this.cancelJob(job.uniqueJobNumber)} disabled={cancelDisabled}><IconCancel/></Button>
                </div>
                <Collapse isOpen={this.state.collapse}>
                    <Card>
                        <CardBody>
                            <table><tbody><tr><th>Status</th><td>{job.status}</td></tr>
                            <tr><th>Command line</th><td>{job.commandLineAsString}</td></tr></tbody></table>
                            <table>
                                <tbody>
                                <tr>
                                    <th>Status</th>
                                    <td>{job.status}</td>
                                </tr>
                                <tr>
                                    <th>Command line</th>
                                    <td>{job.commandLineAsString}</td>
                                </tr>
                                </tbody>
                            </table>
                        </CardBody>
                    </Card>
                </Collapse>