|
@@ -1,6 +1,7 @@
|
|
|
import contextlib
|
|
|
import os
|
|
|
import platform
|
|
|
+import psutil
|
|
|
import shlex
|
|
|
import subprocess
|
|
|
import sys
|
|
@@ -67,15 +68,34 @@ class Job(object):
|
|
|
def initPool():
|
|
|
if Job.pool:
|
|
|
maxthreads = Job.maxthreads
|
|
|
- if len(Job.pool) < maxthreads:
|
|
|
- maxthreads = len(Job.pool)
|
|
|
+ queued = Job.queued()
|
|
|
+ running = len(Job.running())
|
|
|
+ if maxthreads > running:
|
|
|
+ if len(queued) < maxthreads - running:
|
|
|
+ maxthreads = len(queued)
|
|
|
|
|
|
- for i in range(0, maxthreads):
|
|
|
- Job.pool.pop(0).start()
|
|
|
+ if maxthreads:
|
|
|
+ for i in range(0, maxthreads):
|
|
|
+ print(queued[i])
|
|
|
+ queued[i].start()
|
|
|
|
|
|
@staticmethod
|
|
|
def finished():
|
|
|
- return not [x.state for x in Job.pool]
|
|
|
+ return [x for x in Job.pool if x.state in (Job.FAILED, Job.SUCCESSFUL)]
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def pending():
|
|
|
+ return [x for x in Job.pool
|
|
|
+ if x.state not in (Job.FAILED, Job.SUCCESSFUL)]
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def running():
|
|
|
+ return [x for x in Job.pool
|
|
|
+ if x.state is Job.RUNNING]
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def queued():
|
|
|
+ return [x for x in Job.pending() if x.state is Job.QUEUED]
|
|
|
|
|
|
def __init__(self, backup, config):
|
|
|
self._config = config
|
|
@@ -96,7 +116,10 @@ class Job(object):
|
|
|
self.setState(Job.QUEUED)
|
|
|
|
|
|
elif self._state is Job.RUNNING:
|
|
|
- code = self._process.poll()
|
|
|
+ code = self._process.poll() or self._process.returncode
|
|
|
+ if code is None and not psutil.pid_exists(self._process.pid):
|
|
|
+ code = -1
|
|
|
+
|
|
|
if code is not None:
|
|
|
self.setState(Job.FAILED if code else Job.SUCCESSFUL)
|
|
|
Job.initPool()
|
|
@@ -234,6 +257,8 @@ class Job(object):
|
|
|
self.log('{0} -> {1}'.format(
|
|
|
self.getState(self._state), self.getState(state)))
|
|
|
self._state = state
|
|
|
+ if state in (Job.SUCCESSFUL, Job.FAILED):
|
|
|
+ self.logfile.close()
|
|
|
|
|
|
def getState(self, state=None):
|
|
|
return {
|
|
@@ -372,6 +397,8 @@ class Backup(object):
|
|
|
for job in self.ready:
|
|
|
job.queue()
|
|
|
|
|
|
+ Job.initPool()
|
|
|
+
|
|
|
elif self._status is Backup.QUEUED and self not in Backup._queue:
|
|
|
self.setStatus(Backup.READY)
|
|
|
|
|
@@ -405,8 +432,9 @@ class Backup(object):
|
|
|
def jobs(self):
|
|
|
if not hasattr(self, '_jobs'):
|
|
|
self._jobs = []
|
|
|
- for job in self.config['jobs']:
|
|
|
- self._jobs.append(Job(self, job))
|
|
|
+ if 'jobs' in self.config:
|
|
|
+ for job in self.config['jobs']:
|
|
|
+ self._jobs.append(Job(self, job))
|
|
|
|
|
|
return self._jobs
|
|
|
|
|
@@ -427,6 +455,8 @@ class Backup(object):
|
|
|
self.log('{0} -> {1}'.format(
|
|
|
self.getStatus(self._status), self.getStatus(status)))
|
|
|
self._status = status
|
|
|
+ if status in (Backup.SUCCESSFUL, Backup.FAILED):
|
|
|
+ self.logfile.close()
|
|
|
|
|
|
def getStatus(self, status=None):
|
|
|
return {
|
|
@@ -448,6 +478,8 @@ class Backup(object):
|
|
|
for job in self.ready:
|
|
|
job.queue()
|
|
|
|
|
|
+ Job.initPool()
|
|
|
+
|
|
|
|
|
|
def config():
|
|
|
if hasattr(config, '_handle'):
|