50 |
50 |
#: Time after which daemon must be listening
|
51 |
51 |
DEFAULT_LISTEN_TIMEOUT = 10
|
52 |
52 |
|
|
53 |
#: Progress update interval
|
|
54 |
DEFAULT_PROGRESS_INTERVAL = 60
|
|
55 |
|
53 |
56 |
__slots__ = [
|
54 |
57 |
"error",
|
55 |
58 |
"ready",
|
56 |
59 |
"listen",
|
57 |
60 |
"connect",
|
|
61 |
"progress",
|
58 |
62 |
]
|
59 |
63 |
|
60 |
64 |
def __init__(self, connect,
|
61 |
65 |
listen=DEFAULT_LISTEN_TIMEOUT,
|
62 |
66 |
error=DEFAULT_ERROR_TIMEOUT,
|
63 |
|
ready=DEFAULT_READY_TIMEOUT):
|
|
67 |
ready=DEFAULT_READY_TIMEOUT,
|
|
68 |
progress=DEFAULT_PROGRESS_INTERVAL):
|
64 |
69 |
"""Initializes this class.
|
65 |
70 |
|
66 |
71 |
@type connect: number
|
... | ... | |
71 |
76 |
@param error: Length of time until errors cause hard failure
|
72 |
77 |
@type ready: number
|
73 |
78 |
@param ready: Timeout for daemon to become ready
|
|
79 |
@type progress: number
|
|
80 |
@param progress: Progress update interval
|
74 |
81 |
|
75 |
82 |
"""
|
76 |
83 |
self.error = error
|
77 |
84 |
self.ready = ready
|
78 |
85 |
self.listen = listen
|
79 |
86 |
self.connect = connect
|
|
87 |
self.progress = progress
|
80 |
88 |
|
81 |
89 |
|
82 |
90 |
class ImportExportCbBase(object):
|
... | ... | |
101 |
109 |
|
102 |
110 |
"""
|
103 |
111 |
|
|
112 |
def ReportProgress(self, ie, private):
|
|
113 |
"""Called when new progress information should be reported.
|
|
114 |
|
|
115 |
@type ie: Subclass of L{_DiskImportExportBase}
|
|
116 |
@param ie: Import/export object
|
|
117 |
@param private: Private data passed to import/export object
|
|
118 |
|
|
119 |
"""
|
|
120 |
|
104 |
121 |
def ReportFinished(self, ie, private):
|
105 |
122 |
"""Called when a transfer has finished.
|
106 |
123 |
|
... | ... | |
157 |
174 |
self._ts_connected = None
|
158 |
175 |
self._ts_finished = None
|
159 |
176 |
self._ts_cleanup = None
|
|
177 |
self._ts_last_progress = None
|
160 |
178 |
self._ts_last_error = None
|
161 |
179 |
|
162 |
180 |
# Transfer status
|
... | ... | |
178 |
196 |
return None
|
179 |
197 |
|
180 |
198 |
@property
|
|
199 |
def progress(self):
|
|
200 |
"""Returns transfer progress information.
|
|
201 |
|
|
202 |
"""
|
|
203 |
if not self._daemon:
|
|
204 |
return None
|
|
205 |
|
|
206 |
return (self._daemon.progress_mbytes,
|
|
207 |
self._daemon.progress_throughput,
|
|
208 |
self._daemon.progress_percent,
|
|
209 |
self._daemon.progress_eta)
|
|
210 |
|
|
211 |
@property
|
181 |
212 |
def active(self):
|
182 |
213 |
"""Determines whether this transport is still active.
|
183 |
214 |
|
... | ... | |
345 |
376 |
|
346 |
377 |
return False
|
347 |
378 |
|
|
379 |
def _CheckProgress(self):
|
|
380 |
"""Checks whether a progress update should be reported.
|
|
381 |
|
|
382 |
"""
|
|
383 |
if ((self._ts_last_progress is None or
|
|
384 |
_TimeoutExpired(self._ts_last_progress, self._timeouts.progress)) and
|
|
385 |
self._daemon and
|
|
386 |
self._daemon.progress_mbytes is not None and
|
|
387 |
self._daemon.progress_throughput is not None):
|
|
388 |
self._cbs.ReportProgress(self, self._private)
|
|
389 |
self._ts_last_progress = time.time()
|
|
390 |
|
348 |
391 |
def CheckFinished(self):
|
349 |
392 |
"""Checks whether the daemon exited.
|
350 |
393 |
|
... | ... | |
358 |
401 |
return True
|
359 |
402 |
|
360 |
403 |
if self._daemon.exit_status is None:
|
|
404 |
# TODO: Adjust delay for ETA expiring soon
|
|
405 |
self._CheckProgress()
|
361 |
406 |
return False
|
362 |
407 |
|
363 |
408 |
self._ts_finished = time.time()
|
... | ... | |
404 |
449 |
"""Finalizes this import/export.
|
405 |
450 |
|
406 |
451 |
"""
|
407 |
|
assert error or self.success is not None
|
408 |
|
|
409 |
452 |
if self._daemon_name:
|
410 |
453 |
logging.info("Finalizing %s %r on %s",
|
411 |
454 |
self.MODE_TEXT, self._daemon_name, self.node_name)
|
... | ... | |
576 |
619 |
return self._ts_begin
|
577 |
620 |
|
578 |
621 |
|
|
622 |
def FormatProgress(progress):
|
|
623 |
"""Formats progress information for user consumption
|
|
624 |
|
|
625 |
"""
|
|
626 |
(mbytes, throughput, percent, _) = progress
|
|
627 |
|
|
628 |
parts = [
|
|
629 |
utils.FormatUnit(mbytes, "h"),
|
|
630 |
|
|
631 |
# Not using FormatUnit as it doesn't support kilobytes
|
|
632 |
"%0.1f MiB/s" % throughput,
|
|
633 |
]
|
|
634 |
|
|
635 |
if percent is not None:
|
|
636 |
parts.append("%d%%" % percent)
|
|
637 |
|
|
638 |
# TODO: Format ETA
|
|
639 |
|
|
640 |
return utils.CommaJoin(parts)
|
|
641 |
|
|
642 |
|
579 |
643 |
class ImportExportLoop:
|
580 |
644 |
MIN_DELAY = 1.0
|
581 |
645 |
MAX_DELAY = 20.0
|
... | ... | |
772 |
836 |
self.feedback_fn("%s is sending data on %s" %
|
773 |
837 |
(dtp.data.name, ie.node_name))
|
774 |
838 |
|
|
839 |
def ReportProgress(self, ie, dtp):
|
|
840 |
"""Called when new progress information should be reported.
|
|
841 |
|
|
842 |
"""
|
|
843 |
progress = ie.progress
|
|
844 |
if not progress:
|
|
845 |
return
|
|
846 |
|
|
847 |
self.feedback_fn("%s sent %s" % (dtp.data.name, FormatProgress(progress)))
|
|
848 |
|
775 |
849 |
def ReportFinished(self, ie, dtp):
|
776 |
850 |
"""Called when a transfer has finished.
|
777 |
851 |
|
... | ... | |
993 |
1067 |
|
994 |
1068 |
self._feedback_fn("Disk %s is now sending data" % idx)
|
995 |
1069 |
|
|
1070 |
def ReportProgress(self, ie, private):
|
|
1071 |
"""Called when new progress information should be reported.
|
|
1072 |
|
|
1073 |
"""
|
|
1074 |
(idx, _) = private
|
|
1075 |
|
|
1076 |
progress = ie.progress
|
|
1077 |
if not progress:
|
|
1078 |
return
|
|
1079 |
|
|
1080 |
self._feedback_fn("Disk %s sent %s" % (idx, FormatProgress(progress)))
|
|
1081 |
|
996 |
1082 |
def ReportFinished(self, ie, private):
|
997 |
1083 |
"""Called when a transfer has finished.
|
998 |
1084 |
|