sample

from datetime import datetime, timedelta
from airflow.decorators import dag, task, task_group, sensor
from airflow.providers.ssh.hooks.ssh import SSHHook
from airflow.models import XCom
from airflow.utils.session import create_session
from airflow.exceptions import AirflowSensorTimeout
from typing import Dict, List
import yaml
import time

ssh_hook = SSHHook(ssh_conn_id='linux_server')

def load_config(config_path: str) -> dict:
   with open(config_path, 'r') as f:
       return yaml.safe_load(f)

def _send_missing_files_alert(file_type: str, missing_files: list, **context):
   task_instance = context['task_instance']
   last_alert_time = task_instance.xcom_pull(key=f'last_alert_{file_type}', default=0)
   current_time = time.time()
   
   if current_time - last_alert_time >= 60:
       # Implement alert logic (email/slack)
       print(f"Alert for {file_type}: {missing_files}")  # Replace with actual alert
       task_instance.xcom_push(key=f'last_alert_{file_type}', value=current_time)

@task_group
def process_files(config: dict, file_type: str):
   @sensor(poke_interval=180, timeout=3600, soft_fail=True)
   def file_sensor(**context):
       with ssh_hook.get_conn() as ssh_client:
           missing_zip_files = []
           missing_contents = {}
           
           for item in config:
               cmd = f"test -f /path/to/{file_type}/files/{item['zip_file']} && echo 1 || echo 0"
               stdin, stdout, stderr = ssh_client.exec_command(cmd)
               if stdout.read().decode().strip() == "0":
                   missing_zip_files.append(item['zip_file'])
                   continue
                   
               cmd = f"unzip -Z1 /path/to/{file_type}/files/{item['zip_file']}"
               stdin, stdout, stderr = ssh_client.exec_command(cmd)
               zip_contents = set(stdout.read().decode().splitlines())
               missing = set(item['expected_files']) - zip_contents
               if missing:
                   missing_contents[item['zip_file']] = list(missing)

           if missing_zip_files:
               _send_missing_files_alert(f"{file_type}_missing_zips", missing_zip_files, **context)
               return False
               
           if missing_contents:
               missing_files_info = []
               for zip_file, missing_files in missing_contents.items():
                   missing_files_info.append(f"Zip: {zip_file}, Missing files: {', '.join(missing_files)}")
               _send_missing_files_alert(f"{file_type}_missing_contents", missing_files_info, **context)
               cleanup_xcom()
               raise AirflowSensorTimeout(f"Missing expected files in {file_type} zips")
               
           return True

   @task
   def cleanup_xcom():
       with create_session() as session:
           session.query(XCom).filter(
               XCom.execution_date <= datetime.now() - timedelta(days=30)
           ).delete()

   @task(task_id=f'unzip_{file_type}')
   def unzip_files():
       with ssh_hook.get_conn() as ssh_client:
           cmds = []
           for item in config:
               cmds.extend([
                   f"mkdir -p /path/to/{file_type}/temp",
                   f"cd /path/to/{file_type}/files",
                   f"unzip -o {item['zip_file']} -d /path/to/{file_type}/temp"
               ])
           ssh_client.exec_command(" && ".join(cmds))

   @task(task_id=f'move_{file_type}_to_prod')
   def move_to_prod():
       with ssh_hook.get_conn() as ssh_client:
           cmds = [
               f"mkdir -p /path/to/{file_type}/prod",
               f"mv /path/to/{file_type}/temp/* /path/to/{file_type}/prod/",
               f"rm -rf /path/to/{file_type}/temp"
           ]
           ssh_client.exec_command(" && ".join(cmds))

   @task(task_id=f'cleanup_{file_type}_work_dir')
   def cleanup_work_dir():
       with ssh_hook.get_conn() as ssh_client:
           ssh_client.exec_command(f"rm -rf /path/to/{file_type}/temp")

   sensor_task = file_sensor()
   unzip = unzip_files()
   move = move_to_prod()
   cleanup = cleanup_work_dir()
   
   sensor_task >> unzip >> move
   sensor_task.on_failure_callback = cleanup

   return move

@dag(
   dag_id='file_processing_workflow',
   schedule='@daily',
   start_date=datetime(2025, 1, 1),
   catchup=False,
   tags=['file_processing'],
   default_args={
       'owner': 'airflow',
       'retries': 1,
       'retry_delay': timedelta(minutes=5),
       'email_on_failure': True
   }
)
def file_processing_workflow():
   vector_config = load_config('vector_config.yml')
   control_config = load_config('control_config.yml')

   vector_move = process_files(vector_config, 'vector')
   control_move = process_files(control_config, 'control')

   @task
   def archive_files():
       with ssh_hook.get_conn() as ssh_client:
           date_suffix = "$(date +%Y%m%d)"
           cmds = [
               "cd /path/to/vector/prod",
               f"tar -czf /path/to/archive/vector_{date_suffix}.tar.gz *",
               "cd /path/to/control/prod",
               f"tar -czf /path/to/archive/control_{date_suffix}.tar.gz *"
           ]
           ssh_client.exec_command(" && ".join(cmds))

   archive = archive_files()
   [vector_move, control_move] >> archive

dag = file_processing_workflow()

Leave a Reply

https://www.linkedin.com/company/77619036/admin/

welcome.

We connect you to a world of houseplants and urban gardening tailored to your home

Bloomer

Login to your account