#!/usr/bin/env python3

import ftplib
import gzip
import os
import sys
from datetime import datetime

host = 'ftp.ncdc.noaa.gov'
base = '/pub/data/noaa'
retries = 3

def connect(host):
    ftp = ftplib.FTP(host)
    ftp.login()
    return ftp

def status(msg):
    sys.stderr.write('%s %s\n' % (datetime.utcnow().isoformat(), msg))
    sys.stderr.write('reporter:status:%s\n' % msg)

def fail(year, filename):
    status('Failed to download %s/%s' % (year, filename))
    sys.stderr.write('reporter:counter:NCDC Download Failures,%s,1\n' % year)

ftp = connect(host)
for line in sys.stdin:
    (year, filename) = line.strip().split()
    for i in range(retries):
        status('Downloading file %s/%s (FTP attempt %d of %d)' % (year, filename, i + 1, retries))
        try:
            ftp.retrbinary('RETR %s/%s/%s' % (base, year, filename), open(filename, 'wb').write)
        except ftplib.all_errors as error:
            sys.stderr.write('%s\n' % error)
            if str(error).startswith('421'):
                sys.stderr.write('Attempting reconnection after idle timeout\n')
                try:
                    ftp = connect(host)
                    sys.stderr.write('Reconnection succeeded\n')
                except ftplib.all_errors as error:
                    sys.stderr.write('%s\n' % error)
                    fail(year, filename)
                    break
            continue
        status('Decompressing file %s/%s' % (year, filename))
        count = 0
        for record in gzip.open(filename, 'rb'):
            print('%s\t%s' % (year, record.decode('ISO-8859-1').strip()))
            count += 1
        os.remove(filename)
        sys.stderr.write('reporter:counter:NCDC Download Records,%s,%d\n' % (year, count))
        break
    else:
        fail(year, filename)
ftp.quit()

@@ -53,16 +53,28 @@
 port os%0A
+import time%0A
 import s
@@ -550,16 +550,136 @@
  %25 year)
+%0A    try:%0A        os.remove(filename)%0A    except FileNotFoundError:%0A        pass # File might not even have been created
 %0A%0Aftp = 
@@ -1147,31 +1147,21 @@
        s
-ys.stderr.write
+tatus
 ('Attemp
@@ -1196,18 +1196,16 @@
  timeout
-%5Cn
 ')%0A     
@@ -1281,31 +1281,21 @@
        s
-ys.stderr.write
+tatus
 ('Reconn
@@ -1310,18 +1310,16 @@
 ucceeded
-%5Cn
 ')%0A     
@@ -1482,39 +1482,346 @@
     
-break%0A            continue%0A    
+sys.exit(1) # Fail the map task if we can't even reconnect%0A            else:%0A                time.sleep(30) # Unknown error, so delay in case it's something transient%0A            continue # Retry download%0A        break # Download succeeded%0A    else:%0A        fail(year, filename) # Retries exhausted, move on to next file%0A        continue%0A
     
@@ -1874,20 +1874,16 @@
 ename))%0A
-    
     coun
@@ -1892,20 +1892,16 @@
 = 0%0A    
- 
-   
 for reco
@@ -1941,20 +1941,16 @@
         
-   
- 
 print('%25
@@ -2011,20 +2011,16 @@
         
-    
 count +=
@@ -2022,20 +2022,16 @@
 nt += 1%0A
-    
     os.r
@@ -2042,28 +2042,24 @@
 e(filename)%0A
-    
     sys.stde
@@ -2137,61 +2137,8 @@
 t))%0A
-        break%0A    else:%0A        fail(year, filename)%0A
 ftp.

Improve download mapper error handling