#!/usr/bin/python3 # # Copyright (c) 2015 Red Hat, Inc. # This file is part of GlusterFS. # # This file is licensed to you under your choice of the GNU Lesser # General Public License, version 3 or any later version (LGPLv3 or # later), or the GNU General Public License, version 2 (GPLv2), in all # cases as published by the Free Software Foundation. from __future__ import print_function import subprocess import os import os.path import logging import argparse import fcntl import logging.handlers import sys import shutil from errno import EEXIST from conf import GLUSTERFS_LIBEXECDIR sys.path.insert(1, GLUSTERFS_LIBEXECDIR) EVENTS_ENABLED = True try: from events.eventtypes import SNAPSHOT_SCHEDULER_INITIALISED \ as EVENT_SNAPSHOT_SCHEDULER_INITIALISED from events.eventtypes import SNAPSHOT_SCHEDULER_INIT_FAILED \ as EVENT_SNAPSHOT_SCHEDULER_INIT_FAILED from events.eventtypes import SNAPSHOT_SCHEDULER_DISABLED \ as EVENT_SNAPSHOT_SCHEDULER_DISABLED from events.eventtypes import SNAPSHOT_SCHEDULER_DISABLE_FAILED \ as EVENT_SNAPSHOT_SCHEDULER_DISABLE_FAILED from events.eventtypes import SNAPSHOT_SCHEDULER_ENABLED \ as EVENT_SNAPSHOT_SCHEDULER_ENABLED from events.eventtypes import SNAPSHOT_SCHEDULER_ENABLE_FAILED \ as EVENT_SNAPSHOT_SCHEDULER_ENABLE_FAILED from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_ADDED \ as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADDED from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_ADD_FAILED \ as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADD_FAILED from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_DELETED \ as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETED from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_DELETE_FAILED \ as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETE_FAILED from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_EDITED \ as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDITED from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_EDIT_FAILED \ as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDIT_FAILED except ImportError: # Events APIs not installed, dummy eventtypes with None EVENTS_ENABLED = False EVENT_SNAPSHOT_SCHEDULER_INITIALISED = None EVENT_SNAPSHOT_SCHEDULER_INIT_FAILED = None EVENT_SNAPSHOT_SCHEDULER_DISABLED = None EVENT_SNAPSHOT_SCHEDULER_DISABLE_FAILED = None EVENT_SNAPSHOT_SCHEDULER_ENABLED = None EVENT_SNAPSHOT_SCHEDULER_ENABLE_FAILED = None EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADDED = None EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADD_FAILED = None EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETED = None EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETE_FAILED = None EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDITED = None EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDIT_FAILED = None SCRIPT_NAME = "snap_scheduler" scheduler_enabled = False log = logging.getLogger(SCRIPT_NAME) SHARED_STORAGE_DIR="/var/run/gluster/shared_storage" GCRON_DISABLED = SHARED_STORAGE_DIR+"/snaps/gcron_disabled" GCRON_ENABLED = SHARED_STORAGE_DIR+"/snaps/gcron_enabled" GCRON_TASKS = SHARED_STORAGE_DIR+"/snaps/glusterfs_snap_cron_tasks" GCRON_CROND_TASK = "/etc/cron.d/glusterfs_snap_cron_tasks" LOCK_FILE_DIR = SHARED_STORAGE_DIR+"/snaps/lock_files/" LOCK_FILE = LOCK_FILE_DIR+"lock_file" TMP_FILE = SHARED_STORAGE_DIR+"/snaps/tmp_file" GCRON_UPDATE_TASK = "/etc/cron.d/gcron_update_task" CURRENT_SCHEDULER = SHARED_STORAGE_DIR+"/snaps/current_scheduler" tasks = {} longest_field = 12 current_scheduler = "" INTERNAL_ERROR = 2 SHARED_STORAGE_DIR_DOESNT_EXIST = 3 SHARED_STORAGE_NOT_MOUNTED = 4 ANOTHER_TRANSACTION_IN_PROGRESS = 5 INIT_FAILED = 6 SCHEDULING_ALREADY_DISABLED = 7 SCHEDULING_ALREADY_ENABLED = 8 NODE_NOT_INITIALISED = 9 ANOTHER_SCHEDULER_ACTIVE = 10 JOB_ALREADY_EXISTS = 11 JOB_NOT_FOUND = 12 INVALID_JOBNAME = 13 INVALID_VOLNAME = 14 INVALID_SCHEDULE = 15 INVALID_ARG = 16 VOLUME_DOES_NOT_EXIST = 17 def print_error (error_num): if error_num == INTERNAL_ERROR: return "Internal Error" elif error_num == SHARED_STORAGE_DIR_DOESNT_EXIST: return "The shared storage directory ("+SHARED_STORAGE_DIR+")" \ " does not exist." elif error_num == SHARED_STORAGE_NOT_MOUNTED: return "The shared storage directory ("+SHARED_STORAGE_DIR+")" \ " is not mounted." elif error_num == ANOTHER_TRANSACTION_IN_PROGRESS: return "Another transaction is in progress." elif error_num == INIT_FAILED: return "Initialisation failed." elif error_num == SCHEDULING_ALREADY_DISABLED: return "Snapshot scheduler is already disabled." elif error_num == SCHEDULING_ALREADY_ENABLED: return "Snapshot scheduler is already enabled." elif error_num == NODE_NOT_INITIALISED: return "The node is not initialised." elif error_num == ANOTHER_SCHEDULER_ACTIVE: return "Another scheduler is active." elif error_num == JOB_ALREADY_EXISTS: return "The job already exists." elif error_num == JOB_NOT_FOUND: return "The job cannot be found." elif error_num == INVALID_JOBNAME: return "The job name is invalid." elif error_num == INVALID_VOLNAME: return "The volume name is invalid." elif error_num == INVALID_SCHEDULE: return "The schedule is invalid." elif error_num == INVALID_ARG: return "The argument is invalid." elif error_num == VOLUME_DOES_NOT_EXIST: return "The volume does not exist." def output(msg): print("%s: %s" % (SCRIPT_NAME, msg)) def initLogger(): log.setLevel(logging.DEBUG) logFormat = "[%(asctime)s %(filename)s:%(lineno)s %(funcName)s] "\ "%(levelname)s %(message)s" formatter = logging.Formatter(logFormat) sh = logging.handlers.SysLogHandler() sh.setLevel(logging.ERROR) sh.setFormatter(formatter) process = subprocess.Popen(["gluster", "--print-logdir"], stdout=subprocess.PIPE) logfile = os.path.join(process.stdout.read()[:-1], SCRIPT_NAME + ".log") fh = logging.FileHandler(logfile) fh.setLevel(logging.DEBUG) fh.setFormatter(formatter) log.addHandler(sh) log.addHandler(fh) def scheduler_status(): ret = INTERNAL_ERROR global scheduler_enabled try: f = os.path.realpath(GCRON_TASKS) if f != os.path.realpath(GCRON_ENABLED) or not os.path.exists(GCRON_ENABLED): log.info("Snapshot scheduler is currently disabled.") scheduler_enabled = False else: log.info("Snapshot scheduler is currently enabled.") scheduler_enabled = True ret = 0 except: log.error("Failed to enable snapshot scheduling. Error: " "Failed to check the status of %s.", GCRON_DISABLED) return ret def enable_scheduler(): ret = scheduler_status() if ret == 0: if not scheduler_enabled: # Check if another scheduler is active. ret = get_current_scheduler() if ret == 0: if (current_scheduler != "none"): print_str = "Failed to enable snapshot scheduling. " \ "Error: Another scheduler is active." log.error(print_str) output(print_str) ret = ANOTHER_SCHEDULER_ACTIVE return ret else: print_str = "Failed to get current scheduler info." log.error(print_str) output(print_str) return ret log.info("Enabling snapshot scheduler.") try: if os.path.exists(GCRON_DISABLED): os.remove(GCRON_DISABLED) if os.path.lexists(GCRON_TASKS): os.remove(GCRON_TASKS) try: f = os.open(GCRON_ENABLED, os.O_CREAT | os.O_NONBLOCK, 0o644) os.close(f) except OSError as e: log.error("Failed to open %s. Error: %s.", GCRON_ENABLED, e) ret = INTERNAL_ERROR return ret os.symlink(GCRON_ENABLED, GCRON_TASKS) update_current_scheduler("cli") log.info("Snapshot scheduling is enabled") output("Snapshot scheduling is enabled") ret = 0 except OSError as e: print_str = ("Failed to enable snapshot scheduling." "Error: {{}}" + e) log.error(print_str) output(print_str) ret = INTERNAL_ERROR else: print_str = "Failed to enable snapshot scheduling. " \ "Error: Snapshot scheduling is already enabled." log.error(print_str) output(print_str) ret = SCHEDULING_ALREADY_ENABLED else: print_str = "Failed to enable snapshot scheduling. " \ "Error: Failed to check scheduler status." log.error(print_str) output(print_str) return ret def disable_scheduler(): ret = scheduler_status() if ret == 0: if scheduler_enabled: log.info("Disabling snapshot scheduler.") try: # Check if another scheduler is active. If not, then # update current scheduler to "none". Else do nothing. ret = get_current_scheduler() if ret == 0: if (current_scheduler == "cli"): update_current_scheduler("none") else: print_str = "Failed to disable snapshot scheduling. " \ "Error: Failed to get current scheduler info." log.error(print_str) output(print_str) return ret if os.path.exists(GCRON_DISABLED): os.remove(GCRON_DISABLED) if os.path.lexists(GCRON_TASKS): os.remove(GCRON_TASKS) f = os.open(GCRON_DISABLED, os.O_CREAT, 0o644) os.close(f) os.symlink(GCRON_DISABLED, GCRON_TASKS) log.info("Snapshot scheduling is disabled") output("Snapshot scheduling is disabled") ret = 0 except OSError as e: print_str = ("Failed to disable snapshot scheduling. Error: " + e) log.error(print_str) output(print_str) ret = INTERNAL_ERROR else: print_str = "Failed to disable scheduling. " \ "Error: Snapshot scheduling is already disabled." log.error(print_str) output(print_str) ret = SCHEDULING_ALREADY_DISABLED else: print_str = "Failed to disable snapshot scheduling. " \ "Error: Failed to check scheduler status." log.error(print_str) output(print_str) ret = INTERNAL_ERROR return ret def load_tasks_from_file(): global tasks global longest_field try: with open(GCRON_ENABLED, 'r') as f: for line in f: line = line.rstrip('\n') if not line: break line = line.split("gcron.py") schedule = line[0].split("root")[0].rstrip(' ') line = line[1].split(" ") volname = line[1] jobname = line[2] longest_field = max(longest_field, len(jobname), len(volname), len(schedule)) tasks[jobname] = schedule+":"+volname f.close() ret = 0 except IOError as e: log.error("Failed to open %s. Error: %s.", GCRON_ENABLED, e) ret = INTERNAL_ERROR return ret def get_current_scheduler(): global current_scheduler try: with open(CURRENT_SCHEDULER, 'r') as f: current_scheduler = f.readline().rstrip('\n') f.close() ret = 0 except IOError as e: log.error("Failed to open %s. Error: %s.", CURRENT_SCHEDULER, e) ret = INTERNAL_ERROR return ret def list_schedules(): log.info("Listing snapshot schedules.") ret = load_tasks_from_file() if ret == 0: if len(tasks) == 0: output("No snapshots scheduled") else: jobname = "JOB_NAME".ljust(longest_field+5) schedule = "SCHEDULE".ljust(longest_field+5) operation = "OPERATION".ljust(longest_field+5) volname = "VOLUME NAME".ljust(longest_field+5) hyphens = "".ljust((longest_field+5) * 4, '-') print(jobname+schedule+operation+volname) print(hyphens) for key in sorted(tasks): jobname = key.ljust(longest_field+5) schedule = tasks[key].split(":")[0].ljust( longest_field + 5) volname = tasks[key].split(":")[1].ljust( longest_field + 5) operation = "Snapshot Create".ljust(longest_field+5) print(jobname+schedule+operation+volname) ret = 0 else: print_str = "Failed to list snapshot schedules. " \ "Error: Failed to load tasks from "+GCRON_ENABLED log.error(print_str) output(print_str) return ret def write_tasks_to_file(): try: with open(TMP_FILE, "w", 0o644) as f: # If tasks is empty, just create an empty tmp file if len(tasks) != 0: for key in sorted(tasks): jobname = key schedule = tasks[key].split(":")[0] volname = tasks[key].split(":")[1] f.write("%s root PATH=$PATH:/usr/local/sbin:/usr/sbin " "gcron.py %s %s\n" % (schedule, volname, jobname)) f.write("\n") f.flush() os.fsync(f.fileno()) f.close() except IOError as e: log.error("Failed to open %s. Error: %s.", TMP_FILE, e) ret = INTERNAL_ERROR return ret shutil.move(TMP_FILE, GCRON_ENABLED) ret = 0 return ret def update_current_scheduler(data): try: with open(TMP_FILE, "w", 0o644) as f: f.write("%s" % data) f.flush() os.fsync(f.fileno()) f.close() except IOError as e: log.error("Failed to open %s. Error: %s.", TMP_FILE, e) ret = INTERNAL_ERROR return ret shutil.move(TMP_FILE, CURRENT_SCHEDULER) ret = 0 return ret def isVolumePresent(volname): success = False if volname == "": log.debug("No volname given") return success cli = ["gluster", "volume", "info", volname] log.debug("Running command '%s'", " ".join(cli)) p = subprocess.Popen(cli, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() rv = p.returncode log.debug("Command '%s' returned '%d'", " ".join(cli), rv) if rv: log.error("Command output:") log.error(err) else: success = True; return success def add_schedules(jobname, schedule, volname): log.info("Adding snapshot schedules.") ret = load_tasks_from_file() if ret == 0: if jobname in tasks: print_str = ("%s already exists in schedule. Use " "'edit' to modify %s" % (jobname, jobname)) log.error(print_str) output(print_str) ret = JOB_ALREADY_EXISTS else: if not isVolumePresent(volname): print_str = ("Volume %s does not exist. Create %s and retry." % (volname, volname)) log.error(print_str) output(print_str) ret = VOLUME_DOES_NOT_EXIST else: tasks[jobname] = schedule + ":" + volname ret = write_tasks_to_file() if ret == 0: # Create a LOCK_FILE for the job job_lockfile = LOCK_FILE_DIR + jobname try: f = os.open(job_lockfile, os.O_CREAT | os.O_NONBLOCK, 0o644) os.close(f) except OSError as e: log.error("Failed to open %s. Error: %s.", job_lockfile, e) ret = INTERNAL_ERROR return ret log.info("Successfully added snapshot schedule %s" % jobname) output("Successfully added snapshot schedule") ret = 0 else: print_str = "Failed to add snapshot schedule. " \ "Error: Failed to load tasks from "+GCRON_ENABLED log.error(print_str) output(print_str) return ret def delete_schedules(jobname): log.info("Delete snapshot schedules.") ret = load_tasks_from_file() if ret == 0: if jobname in tasks: del tasks[jobname] ret = write_tasks_to_file() if ret == 0: # Delete the LOCK_FILE for the job job_lockfile = LOCK_FILE_DIR+jobname try: os.remove(job_lockfile) except OSError as e: log.error("Failed to open %s. Error: %s.", job_lockfile, e) ret = INTERNAL_ERROR return ret log.info("Successfully deleted snapshot schedule %s" % jobname) output("Successfully deleted snapshot schedule") ret = 0 else: print_str = ("Failed to delete %s. Error: No such " "job scheduled" % jobname) log.error(print_str) output(print_str) ret = JOB_NOT_FOUND else: print_str = "Failed to delete snapshot schedule. " \ "Error: Failed to load tasks from "+GCRON_ENABLED log.error(print_str) output(print_str) return ret def edit_schedules(jobname, schedule, volname): log.info("Editing snapshot schedules.") ret = load_tasks_from_file() if ret == 0: if jobname in tasks: if not isVolumePresent(volname): print_str = ("Volume %s does not exist. Create %s and retry." % (volname, volname)) log.error(print_str) output(print_str) ret = VOLUME_DOES_NOT_EXIST else: tasks[jobname] = schedule+":"+volname ret = write_tasks_to_file() if ret == 0: log.info("Successfully edited snapshot schedule %s" % jobname) output("Successfully edited snapshot schedule") else: print_str = ("Failed to edit %s. Error: No such " "job scheduled" % jobname) log.error(print_str) output(print_str) ret = JOB_NOT_FOUND else: print_str = "Failed to edit snapshot schedule. " \ "Error: Failed to load tasks from "+GCRON_ENABLED log.error(print_str) output(print_str) return ret def get_bool_val(): getsebool_cli = ["getsebool", "-a"] p1 = subprocess.Popen(getsebool_cli, stdout=subprocess.PIPE, stderr=subprocess.PIPE) grep_cmd = ["grep", "cron_system_cronjob_use_shares"] p2 = subprocess.Popen(grep_cmd, stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p1.stdout.close() output, err = p2.communicate() rv = p2.returncode if rv: log.error("Command output:") log.error(err) return -1 bool_val = output.split()[2] log.debug("Bool value = '%s'", bool_val) return bool_val def get_selinux_status(): getenforce_cli = ["getenforce"] log.debug("Running command '%s'", " ".join(getenforce_cli)) try: p1 = subprocess.Popen(getenforce_cli, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError as oserr: log.error("Failed to run the command \"getenforce\". Error: %s" %\ oserr) return -1 output, err = p1.communicate() rv = p1.returncode if rv: log.error("Command output:") log.error(err) return -1 else: selinux_status=output.rstrip() log.debug("selinux status: %s", selinux_status) return selinux_status def set_cronjob_user_share(): selinux_status = get_selinux_status() if (selinux_status == -1): log.error("Failed to get selinux status") return -1 elif (selinux_status == "Disabled"): return 0 bool_val = get_bool_val() # In case of a failure (where the boolean value is not) # present in the system, we should not proceed further # We should only proceed when the value is "off" if (bool_val == -1 or bool_val != "off"): return 0 setsebool_cli = ["setsebool", "-P", "cron_system_cronjob_use_shares", "on"] log.debug("Running command '%s'", " ".join(setsebool_cli)) p1 = subprocess.Popen(setsebool_cli, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = p1.communicate() rv = p1.returncode if rv: log.error("Command output:") log.error(err) return rv bool_val = get_bool_val() if (bool_val == "on"): return 0 else: # In case of an error or if boolean is not on # we return a failure here return -1 def initialise_scheduler(): ret = set_cronjob_user_share() if ret: log.error("Failed to set selinux boolean " "cron_system_cronjob_use_shares to 'on'") return ret try: with open(TMP_FILE, "w+", 0o644) as f: updater = ("* * * * * root PATH=$PATH:/usr/local/sbin:" "/usr/sbin gcron.py --update\n") f.write("%s\n" % updater) f.flush() os.fsync(f.fileno()) f.close() except IOError as e: log.error("Failed to open %s. Error: %s.", TMP_FILE, e) ret = INIT_FAILED return ret shutil.move(TMP_FILE, GCRON_UPDATE_TASK) if not os.path.lexists(GCRON_TASKS): try: f = open(GCRON_TASKS, "w", 0o644) f.close() except IOError as e: log.error("Failed to open %s. Error: %s.", GCRON_TASKS, e) ret = INIT_FAILED return ret if os.path.lexists(GCRON_CROND_TASK): os.remove(GCRON_CROND_TASK) os.symlink(GCRON_TASKS, GCRON_CROND_TASK) log.info("Successfully initialised snapshot scheduler for this node") output("Successfully initialised snapshot scheduler for this node") gf_event (EVENT_SNAPSHOT_SCHEDULER_INITIALISED, status="Success") ret = 0 return ret def syntax_checker(args): if hasattr(args, 'jobname'): if (len(args.jobname.split()) != 1): output("Invalid Jobname. Jobname should not be empty and should not contain \" \" character.") ret = INVALID_JOBNAME return ret args.jobname=args.jobname.strip() if hasattr(args, 'volname'): if (len(args.volname.split()) != 1): output("Invalid Volname. Volname should not be empty and should not contain \" \" character.") ret = INVALID_VOLNAME return ret args.volname=args.volname.strip() if hasattr(args, 'schedule'): if (len(args.schedule.split()) != 5): output("Invalid Schedule. Please refer to the following for adding a valid cron schedule") print ("* * * * *") print ("| | | | |") print ("| | | | +---- Day of the Week (range: 1-7, 1 standing for Monday)") print ("| | | +------ Month of the Year (range: 1-12)") print ("| | +-------- Day of the Month (range: 1-31)") print ("| +---------- Hour (range: 0-23)") print ("+------------ Minute (range: 0-59)") ret = INVALID_SCHEDULE return ret ret = 0 return ret def perform_operation(args): if not os.path.exists(CURRENT_SCHEDULER): update_current_scheduler("none") # Initialise snapshot scheduler on local node if args.action == "init": ret = initialise_scheduler() if ret != 0: output("Failed to initialise snapshot scheduling") gf_event (EVENT_SNAPSHOT_SCHEDULER_INIT_FAILED, error=print_error(ret)) return ret # Disable snapshot scheduler if args.action == "disable_force": ret = disable_scheduler() if ret == 0: subprocess.Popen(["touch", "-h", GCRON_TASKS]) gf_event (EVENT_SNAPSHOT_SCHEDULER_DISABLED, status="Successfully Disabled") else: gf_event (EVENT_SNAPSHOT_SCHEDULER_DISABLE_FAILED, error=print_error(ret)) return ret # Check if the symlink to GCRON_TASKS is properly set in the shared storage if (not os.path.lexists(GCRON_UPDATE_TASK) or not os.path.lexists(GCRON_CROND_TASK) or os.readlink(GCRON_CROND_TASK) != GCRON_TASKS): print_str = ("Please run 'snap_scheduler.py' init to initialise " "the snap scheduler for the local node.") log.error(print_str) output(print_str) ret = NODE_NOT_INITIALISED return ret # Check status of snapshot scheduler. if args.action == "status": ret = scheduler_status() if ret == 0: if scheduler_enabled: output("Snapshot scheduling status: Enabled") else: output("Snapshot scheduling status: Disabled") else: output("Failed to check status of snapshot scheduler") return ret # Enable snapshot scheduler if args.action == "enable": ret = enable_scheduler() if ret == 0: subprocess.Popen(["touch", "-h", GCRON_TASKS]) gf_event (EVENT_SNAPSHOT_SCHEDULER_ENABLED, status="Successfully Enabled") else: gf_event (EVENT_SNAPSHOT_SCHEDULER_ENABLE_FAILED, error=print_error(ret)) return ret # Disable snapshot scheduler if args.action == "disable": ret = disable_scheduler() if ret == 0: subprocess.Popen(["touch", "-h", GCRON_TASKS]) gf_event (EVENT_SNAPSHOT_SCHEDULER_DISABLED, status="Successfully Disabled") else: gf_event (EVENT_SNAPSHOT_SCHEDULER_DISABLE_FAILED, error=print_error(ret)) return ret # List snapshot schedules if args.action == "list": ret = list_schedules() return ret # Add snapshot schedules if args.action == "add": ret = syntax_checker(args) if ret != 0: return ret ret = add_schedules(args.jobname, args.schedule, args.volname) if ret == 0: subprocess.Popen(["touch", "-h", GCRON_TASKS]) gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADDED, status="Successfully added job "+args.jobname) else: gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADD_FAILED, status="Failed to add job "+args.jobname, error=print_error(ret)) return ret # Delete snapshot schedules if args.action == "delete": ret = syntax_checker(args) if ret != 0: return ret ret = delete_schedules(args.jobname) if ret == 0: subprocess.Popen(["touch", "-h", GCRON_TASKS]) gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETED, status="Successfully deleted job "+args.jobname) else: gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETE_FAILED, status="Failed to delete job "+args.jobname, error=print_error(ret)) return ret # Edit snapshot schedules if args.action == "edit": ret = syntax_checker(args) if ret != 0: return ret ret = edit_schedules(args.jobname, args.schedule, args.volname) if ret == 0: subprocess.Popen(["touch", "-h", GCRON_TASKS]) gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDITED, status="Successfully edited job "+args.jobname) else: gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDIT_FAILED, status="Failed to edit job "+args.jobname, error=print_error(ret)) return ret ret = INVALID_ARG return ret def gf_event(event_type, **kwargs): if EVENTS_ENABLED: from events.gf_event import gf_event as gfevent gfevent(event_type, **kwargs) def main(argv): initLogger() ret = -1 parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest="action", metavar=('{init, status, enable,' ' disable, list, add,' ' delete, edit}')) subparsers.add_parser('init', help="Initialise the node for snapshot scheduling") subparsers.add_parser("status", help="Check if snapshot scheduling is " "enabled or disabled") subparsers.add_parser("enable", help="Enable snapshot scheduling") subparsers.add_parser("disable", help="Disable snapshot scheduling") subparsers.add_parser("disable_force") subparsers.add_parser("list", help="List snapshot schedules") parser_add = subparsers.add_parser("add", help="Add snapshot schedules") parser_add.add_argument("jobname", help="Job Name") parser_add.add_argument("schedule", help="Schedule") parser_add.add_argument("volname", help="Volume Name") parser_delete = subparsers.add_parser("delete", help="Delete snapshot schedules") parser_delete.add_argument("jobname", help="Job Name") parser_edit = subparsers.add_parser("edit", help="Edit snapshot schedules") parser_edit.add_argument("jobname", help="Job Name") parser_edit.add_argument("schedule", help="Schedule") parser_edit.add_argument("volname", help="Volume Name") args = parser.parse_args(argv) if not os.path.exists(SHARED_STORAGE_DIR): output("Failed: "+SHARED_STORAGE_DIR+" does not exist.") return SHARED_STORAGE_DIR_DOESNT_EXIST if not os.path.ismount(SHARED_STORAGE_DIR): output("Failed: Shared storage is not mounted at "+SHARED_STORAGE_DIR) return SHARED_STORAGE_NOT_MOUNTED if not os.path.exists(SHARED_STORAGE_DIR+"/snaps/"): try: os.makedirs(SHARED_STORAGE_DIR+"/snaps/") except OSError as e: if errno != EEXIST: log.error("Failed to create %s : %s", SHARED_STORAGE_DIR+"/snaps/", e) output("Failed to create %s. Error: %s" % (SHARED_STORAGE_DIR+"/snaps/", e)) return INTERNAL_ERROR if not os.path.exists(GCRON_ENABLED): f = os.open(GCRON_ENABLED, os.O_CREAT | os.O_NONBLOCK, 0o644) os.close(f) if not os.path.exists(LOCK_FILE_DIR): try: os.makedirs(LOCK_FILE_DIR) except OSError as e: if errno != EEXIST: log.error("Failed to create %s : %s", LOCK_FILE_DIR, e) output("Failed to create %s. Error: %s" % (LOCK_FILE_DIR, e)) return INTERNAL_ERROR try: f = os.open(LOCK_FILE, os.O_CREAT | os.O_RDWR | os.O_NONBLOCK, 0o644) try: fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) ret = perform_operation(args) fcntl.flock(f, fcntl.LOCK_UN) except IOError: log.info("%s is being processed by another agent.", LOCK_FILE) output("Another snap_scheduler command is running. " "Please try again after some time.") return ANOTHER_TRANSACTION_IN_PROGRESS os.close(f) except OSError as e: log.error("Failed to open %s : %s", LOCK_FILE, e) output("Failed to open %s. Error: %s" % (LOCK_FILE, e)) return INTERNAL_ERROR return ret if __name__ == "__main__": sys.exit(main(sys.argv[1:]))