How to Manage or Avoid Duplicate Schedules and Alerts During Looker Migration

During migration, two Looker instances—one Looker (original), one Looker (Google Cloud core)—will run in parallel for some period of time. Any automatic activity that takes place (such as alerts and scheduled data deliveries, as well as background activity that accesses backend databases) may be duplicated. To avoid duplicate activity, you may want to delete automatic alerts and schedules in either the Looker (original) or the Looker (Google Cloud core) instance.

In our example, we will be demonstrating how to disable schedules and alerts for a Looker (original) instance using the Looker SDK for Python. In our code snippets, we will refer to Looker (original) as [LookerHosted] and Looker (Google Cloud core) instance as [LookerCore].

Install Looker-SDK

!pip install looker-sdk 

import looker_sdk
from looker_sdk.sdk.api40 import models
import csv


Create looker.ini file

Create looker.ini file with the following 2 sections:

  • LookerHosted
  • LookerCore
[LookerHosted]
base_url=
client_id=
client_secret=
verify_ssl=true

[LookerCore]
base_url=
client_id=
client_secret=
verify_ssl=true

Step 1: Disable all schedules in Looker Hosted instance

This code fetches all schedules in the Looker Hosted instance and sets them to enabled=False. Additionally, a csv file is created with a list of the schedules.

sdk_looker_hosted = looker_sdk.init40(config_file="looker.ini", section="LookerHosted")

# Fetch all schedules
schedules = sdk_looker_hosted.all_scheduled_plans(all_users=True)

# Open a CSV file for writing
with open('looker_schedules.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(["Schedule ID", "Name", "User ID", "Enabled", "Next Run", "Status"]) # Write header row

    for schedule in schedules:
        schedule_id = schedule.id

        # Disable the schedule
        try:
            sdk_looker_hosted.update_scheduled_plan(
                scheduled_plan_id=schedule_id,
                body=models.WriteScheduledPlan(enabled=False),
            )
            print(f"Schedule ID {schedule_id} successfully disabled")
            writer.writerow([schedule.id, schedule.name, schedule.user_id, schedule.enabled, schedule.next_run_at, "successfully disabled"]) # Write each schedule's data to the CSV file
        except looker_sdk.error.SDKError as e:
            print(f"Error disabling schedule ID {schedule_id}: {e}")
            writer.writerow([schedule.id, schedule.name, schedule.user_id, schedule.enabled, schedule.next_run_at, f"Error disabling schedule ID {schedule_id}: {e}"]) # Write each schedule's data to the CSV file

**Important: Save the csv file to a safe location.

Step 2: Disable all alerts in Looker Hosted instance

sdk_looker_hosted = looker_sdk.init40(config_file="looker.ini", section="LookerHosted")

# Use sdk.search_alerts() instead of sdk.all_alerts()
alerts = sdk_looker_hosted.search_alerts(all_owners=True)
for alert in alerts:
  if alert.is_disabled == False:
    try:
        sdk_looker_hosted.update_alert_field(alert_id = alert.id, body=models.AlertPatch(is_disabled=True))
        print(f"Alert {alert.id} ({alert.field.title}) disabled successfully.")
    except looker_sdk.error.SDKError as e:
        print(f"Error disabling alert {alert.id}: {e}")

Appendix: Re-enable schedules in Looker Hosted instance

Use this code snippet if you re-enable your schedules that are saved in the csv file which had been disabled in earlier steps on your Looker Hosted Instance.

# Initialize the SDK
sdk_looker_hosted = looker_sdk.init40(config_file="looker.ini", section="LookerHosted")
csv_file_path = '/content/looker_schedules.csv'  # Replace with your CSV file path


with open(csv_file_path, 'r') as csvfile:
    reader = csv.DictReader(csvfile)  # Use DictReader to access columns by name
    for row in reader:
          schedule_id = row['Schedule ID']
          try:
              sdk_looker_hosted.update_scheduled_plan(
                  scheduled_plan_id=schedule_id,
                  body=models.WriteScheduledPlan(enabled=True),
              )
              print(f"Schedule ID {schedule_id} successfully enabled")
          except looker_sdk.error.SDKError as e:
              print(f"Error enabling schedule ID {schedule_id}: {e}")


Appendix: Re-enable schedules based on a range in Looker Hosted instance

Use this code snippet if you need to bulk-enable schedules across a specific range which had been disabled in earlier steps on your Looker Hosted Instance.

If for some reason the csv file was not saved, this will assist you with enabling all schedules which had previously been disabled in earlier steps on your Looker Hosted Instance. To do this, update the second number in the range (1, x) to a number large enough to include all schedules. A new schedule may need to be created as a test to find the max schedule id.

sdk_looker_core = looker_sdk.init40(config_file="looker.ini", section="LookerCore")

# Programmatically enable all schedules in range below

# Update the second number in the range to a number large enough to include all schedules
for schedule in range(1, 11):
    schedule_id = str(schedule)

    # Enable the schedule
    try:
        sdk_looker_core.update_scheduled_plan(
            scheduled_plan_id=schedule_id,
            body=models.WriteScheduledPlan(enabled=True),
        )
        print(f"Schedule ID {schedule_id} successfully enabled")
    except looker_sdk.error.SDKError as e:
        print(f"Error enabeling schedule ID {schedule_id}: {e}")

Appendix: Re-enable all alerts in Looker Hosted instance

If you need to re-enable all alerts which had been disabled in earlier steps on your Looker Hosted Instance use this code snippet below.

sdk_looker_hosted = looker_sdk.init40(config_file="looker.ini", section="LookerHosted")

# Use sdk.search_alerts() instead of sdk.all_alerts()
alerts = sdk_looker_hosted.search_alerts(all_owners=True)
for alert in alerts:
  if alert.is_disabled == True:
    try:
        sdk_looker_hosted.update_alert_field(alert_id = alert.id, body=models.AlertPatch(is_disabled=False))
        print(f"Alert {alert.id} ({alert.field.title}) enabled successfully.")
    except looker_sdk.error.SDKError as e:
        print(f"Error enabling alert {alert.id}: {e}")

Rather than disabling schedules, admins may temporarily change the SMTP settings to a “black hole” address or a single testing inbox. This allows the system to think it’s sending mail without the data actually reaching the end users. The drawback is if the SMTP server rejects the connection it could trigger a continuous retry loop, clogging the task queue with “phantom” jobs.

4 Likes