"Fossies" - the Fresh Open Source Software Archive

Member "glusterfs-8.2/extras/snap_scheduler/snap_scheduler.py" (16 Sep 2020, 33857 Bytes) of package /linux/misc/glusterfs-8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "snap_scheduler.py" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 7.4_vs_7.5.

    1 #!/usr/bin/python3
    2 #
    3 # Copyright (c) 2015 Red Hat, Inc. <http://www.redhat.com>
    4 # This file is part of GlusterFS.
    5 #
    6 # This file is licensed to you under your choice of the GNU Lesser
    7 # General Public License, version 3 or any later version (LGPLv3 or
    8 # later), or the GNU General Public License, version 2 (GPLv2), in all
    9 # cases as published by the Free Software Foundation.
   10 
   11 from __future__ import print_function
   12 import subprocess
   13 import os
   14 import os.path
   15 import logging
   16 import argparse
   17 import fcntl
   18 import logging.handlers
   19 import sys
   20 import shutil
   21 from errno import EEXIST
   22 from conf import GLUSTERFS_LIBEXECDIR
   23 sys.path.insert(1, GLUSTERFS_LIBEXECDIR)
   24 
   25 EVENTS_ENABLED = True
   26 try:
   27     from events.eventtypes import SNAPSHOT_SCHEDULER_INITIALISED \
   28                          as EVENT_SNAPSHOT_SCHEDULER_INITIALISED
   29     from events.eventtypes import SNAPSHOT_SCHEDULER_INIT_FAILED \
   30                          as EVENT_SNAPSHOT_SCHEDULER_INIT_FAILED
   31     from events.eventtypes import SNAPSHOT_SCHEDULER_DISABLED \
   32                          as EVENT_SNAPSHOT_SCHEDULER_DISABLED
   33     from events.eventtypes import SNAPSHOT_SCHEDULER_DISABLE_FAILED \
   34                          as EVENT_SNAPSHOT_SCHEDULER_DISABLE_FAILED
   35     from events.eventtypes import SNAPSHOT_SCHEDULER_ENABLED \
   36                          as EVENT_SNAPSHOT_SCHEDULER_ENABLED
   37     from events.eventtypes import SNAPSHOT_SCHEDULER_ENABLE_FAILED \
   38                          as EVENT_SNAPSHOT_SCHEDULER_ENABLE_FAILED
   39     from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_ADDED \
   40                          as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADDED
   41     from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_ADD_FAILED \
   42                          as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADD_FAILED
   43     from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_DELETED \
   44                          as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETED
   45     from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_DELETE_FAILED \
   46                          as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETE_FAILED
   47     from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_EDITED \
   48                          as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDITED
   49     from events.eventtypes import SNAPSHOT_SCHEDULER_SCHEDULE_EDIT_FAILED \
   50                          as EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDIT_FAILED
   51 except ImportError:
   52     # Events APIs not installed, dummy eventtypes with None
   53     EVENTS_ENABLED = False
   54     EVENT_SNAPSHOT_SCHEDULER_INITIALISED = None
   55     EVENT_SNAPSHOT_SCHEDULER_INIT_FAILED = None
   56     EVENT_SNAPSHOT_SCHEDULER_DISABLED = None
   57     EVENT_SNAPSHOT_SCHEDULER_DISABLE_FAILED = None
   58     EVENT_SNAPSHOT_SCHEDULER_ENABLED = None
   59     EVENT_SNAPSHOT_SCHEDULER_ENABLE_FAILED = None
   60     EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADDED = None
   61     EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADD_FAILED = None
   62     EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETED = None
   63     EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETE_FAILED = None
   64     EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDITED = None
   65     EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDIT_FAILED = None
   66 
   67 SCRIPT_NAME = "snap_scheduler"
   68 scheduler_enabled = False
   69 log = logging.getLogger(SCRIPT_NAME)
   70 SHARED_STORAGE_DIR="/var/run/gluster/shared_storage"
   71 GCRON_DISABLED = SHARED_STORAGE_DIR+"/snaps/gcron_disabled"
   72 GCRON_ENABLED = SHARED_STORAGE_DIR+"/snaps/gcron_enabled"
   73 GCRON_TASKS = SHARED_STORAGE_DIR+"/snaps/glusterfs_snap_cron_tasks"
   74 GCRON_CROND_TASK = "/etc/cron.d/glusterfs_snap_cron_tasks"
   75 LOCK_FILE_DIR = SHARED_STORAGE_DIR+"/snaps/lock_files/"
   76 LOCK_FILE = LOCK_FILE_DIR+"lock_file"
   77 TMP_FILE = SHARED_STORAGE_DIR+"/snaps/tmp_file"
   78 GCRON_UPDATE_TASK = "/etc/cron.d/gcron_update_task"
   79 CURRENT_SCHEDULER = SHARED_STORAGE_DIR+"/snaps/current_scheduler"
   80 tasks = {}
   81 longest_field = 12
   82 current_scheduler = ""
   83 
   84 INTERNAL_ERROR = 2
   85 SHARED_STORAGE_DIR_DOESNT_EXIST = 3
   86 SHARED_STORAGE_NOT_MOUNTED = 4
   87 ANOTHER_TRANSACTION_IN_PROGRESS = 5
   88 INIT_FAILED = 6
   89 SCHEDULING_ALREADY_DISABLED = 7
   90 SCHEDULING_ALREADY_ENABLED = 8
   91 NODE_NOT_INITIALISED = 9
   92 ANOTHER_SCHEDULER_ACTIVE = 10
   93 JOB_ALREADY_EXISTS = 11
   94 JOB_NOT_FOUND = 12
   95 INVALID_JOBNAME = 13
   96 INVALID_VOLNAME = 14
   97 INVALID_SCHEDULE = 15
   98 INVALID_ARG = 16
   99 VOLUME_DOES_NOT_EXIST = 17
  100 
  101 def print_error (error_num):
  102     if error_num == INTERNAL_ERROR:
  103         return "Internal Error"
  104     elif error_num == SHARED_STORAGE_DIR_DOESNT_EXIST:
  105         return "The shared storage directory ("+SHARED_STORAGE_DIR+")" \
  106                " does not exist."
  107     elif error_num == SHARED_STORAGE_NOT_MOUNTED:
  108         return "The shared storage directory ("+SHARED_STORAGE_DIR+")" \
  109                " is not mounted."
  110     elif error_num == ANOTHER_TRANSACTION_IN_PROGRESS:
  111         return "Another transaction is in progress."
  112     elif error_num == INIT_FAILED:
  113         return "Initialisation failed."
  114     elif error_num == SCHEDULING_ALREADY_DISABLED:
  115         return "Snapshot scheduler is already disabled."
  116     elif error_num == SCHEDULING_ALREADY_ENABLED:
  117         return "Snapshot scheduler is already enabled."
  118     elif error_num == NODE_NOT_INITIALISED:
  119         return "The node is not initialised."
  120     elif error_num == ANOTHER_SCHEDULER_ACTIVE:
  121         return "Another scheduler is active."
  122     elif error_num == JOB_ALREADY_EXISTS:
  123         return "The job already exists."
  124     elif error_num == JOB_NOT_FOUND:
  125         return "The job cannot be found."
  126     elif error_num == INVALID_JOBNAME:
  127         return "The job name is invalid."
  128     elif error_num == INVALID_VOLNAME:
  129         return "The volume name is invalid."
  130     elif error_num == INVALID_SCHEDULE:
  131         return "The schedule is invalid."
  132     elif error_num == INVALID_ARG:
  133         return "The argument is invalid."
  134     elif error_num == VOLUME_DOES_NOT_EXIST:
  135         return "The volume does not exist."
  136 
  137 def output(msg):
  138     print("%s: %s" % (SCRIPT_NAME, msg))
  139 
  140 
  141 def initLogger():
  142     log.setLevel(logging.DEBUG)
  143     logFormat = "[%(asctime)s %(filename)s:%(lineno)s %(funcName)s] "\
  144         "%(levelname)s %(message)s"
  145     formatter = logging.Formatter(logFormat)
  146 
  147     sh = logging.handlers.SysLogHandler()
  148     sh.setLevel(logging.ERROR)
  149     sh.setFormatter(formatter)
  150 
  151     process = subprocess.Popen(["gluster", "--print-logdir"],
  152                                stdout=subprocess.PIPE, universal_newlines=True)
  153     logfile = os.path.join(process.stdout.read()[:-1], SCRIPT_NAME + ".log")
  154 
  155     fh = logging.FileHandler(logfile)
  156     fh.setLevel(logging.DEBUG)
  157     fh.setFormatter(formatter)
  158 
  159     log.addHandler(sh)
  160     log.addHandler(fh)
  161 
  162 
  163 def scheduler_status():
  164     ret = INTERNAL_ERROR
  165     global scheduler_enabled
  166     try:
  167         f = os.path.realpath(GCRON_TASKS)
  168         if f != os.path.realpath(GCRON_ENABLED) or not os.path.exists(GCRON_ENABLED):
  169             log.info("Snapshot scheduler is currently disabled.")
  170             scheduler_enabled = False
  171         else:
  172             log.info("Snapshot scheduler is currently enabled.")
  173             scheduler_enabled = True
  174         ret = 0
  175     except:
  176         log.error("Failed to enable snapshot scheduling. Error: "
  177                   "Failed to check the status of %s.", GCRON_DISABLED)
  178 
  179     return ret
  180 
  181 def enable_scheduler():
  182     ret = scheduler_status()
  183     if ret == 0:
  184         if not scheduler_enabled:
  185 
  186             # Check if another scheduler is active.
  187             ret = get_current_scheduler()
  188             if ret == 0:
  189                 if (current_scheduler != "none"):
  190                     print_str = "Failed to enable snapshot scheduling. " \
  191                                 "Error: Another scheduler is active."
  192                     log.error(print_str)
  193                     output(print_str)
  194                     ret = ANOTHER_SCHEDULER_ACTIVE
  195                     return ret
  196             else:
  197                 print_str = "Failed to get current scheduler info."
  198                 log.error(print_str)
  199                 output(print_str)
  200                 return ret
  201 
  202             log.info("Enabling snapshot scheduler.")
  203             try:
  204                 if os.path.exists(GCRON_DISABLED):
  205                     os.remove(GCRON_DISABLED)
  206                 if os.path.lexists(GCRON_TASKS):
  207                     os.remove(GCRON_TASKS)
  208                 try:
  209                     f = os.open(GCRON_ENABLED, os.O_CREAT | os.O_NONBLOCK,
  210                                 0o644)
  211                     os.close(f)
  212                 except OSError as e:
  213                     log.error("Failed to open %s. Error: %s.",
  214                               GCRON_ENABLED, e)
  215                     ret = INTERNAL_ERROR
  216                     return ret
  217                 os.symlink(GCRON_ENABLED, GCRON_TASKS)
  218                 update_current_scheduler("cli")
  219                 log.info("Snapshot scheduling is enabled")
  220                 output("Snapshot scheduling is enabled")
  221                 ret = 0
  222             except OSError as e:
  223                 print_str = ("Failed to enable snapshot scheduling."
  224                              "Error: {{}}" + e)
  225                 log.error(print_str)
  226                 output(print_str)
  227                 ret = INTERNAL_ERROR
  228         else:
  229             print_str = "Failed to enable snapshot scheduling. " \
  230                         "Error: Snapshot scheduling is already enabled."
  231             log.error(print_str)
  232             output(print_str)
  233             ret = SCHEDULING_ALREADY_ENABLED
  234     else:
  235         print_str = "Failed to enable snapshot scheduling. " \
  236                     "Error: Failed to check scheduler status."
  237         log.error(print_str)
  238         output(print_str)
  239 
  240     return ret
  241 
  242 
  243 def disable_scheduler():
  244     ret = scheduler_status()
  245     if ret == 0:
  246         if scheduler_enabled:
  247             log.info("Disabling snapshot scheduler.")
  248             try:
  249                 # Check if another scheduler is active. If not, then
  250                 # update current scheduler to "none". Else do nothing.
  251                 ret = get_current_scheduler()
  252                 if ret == 0:
  253                     if (current_scheduler == "cli"):
  254                         update_current_scheduler("none")
  255                 else:
  256                     print_str = "Failed to disable snapshot scheduling. " \
  257                                 "Error: Failed to get current scheduler info."
  258                     log.error(print_str)
  259                     output(print_str)
  260                     return ret
  261 
  262                 if os.path.exists(GCRON_DISABLED):
  263                     os.remove(GCRON_DISABLED)
  264                 if os.path.lexists(GCRON_TASKS):
  265                     os.remove(GCRON_TASKS)
  266                 f = os.open(GCRON_DISABLED, os.O_CREAT, 0o644)
  267                 os.close(f)
  268                 os.symlink(GCRON_DISABLED, GCRON_TASKS)
  269                 log.info("Snapshot scheduling is disabled")
  270                 output("Snapshot scheduling is disabled")
  271                 ret = 0
  272             except OSError as e:
  273                 print_str = ("Failed to disable snapshot scheduling. Error: "
  274                              + e)
  275                 log.error(print_str)
  276                 output(print_str)
  277                 ret = INTERNAL_ERROR
  278         else:
  279             print_str = "Failed to disable scheduling. " \
  280                         "Error: Snapshot scheduling is already disabled."
  281             log.error(print_str)
  282             output(print_str)
  283             ret = SCHEDULING_ALREADY_DISABLED
  284     else:
  285         print_str = "Failed to disable snapshot scheduling. " \
  286                     "Error: Failed to check scheduler status."
  287         log.error(print_str)
  288         output(print_str)
  289         ret = INTERNAL_ERROR
  290 
  291     return ret
  292 
  293 
  294 def load_tasks_from_file():
  295     global tasks
  296     global longest_field
  297     try:
  298         with open(GCRON_ENABLED, 'r') as f:
  299             for line in f:
  300                 line = line.rstrip('\n')
  301                 if not line:
  302                     break
  303                 line = line.split("gcron.py")
  304                 schedule = line[0].split("root")[0].rstrip(' ')
  305                 line = line[1].split(" ")
  306                 volname = line[1]
  307                 jobname = line[2]
  308                 longest_field = max(longest_field, len(jobname), len(volname),
  309                                     len(schedule))
  310                 tasks[jobname] = schedule+":"+volname
  311             f.close()
  312         ret = 0
  313     except IOError as e:
  314         log.error("Failed to open %s. Error: %s.", GCRON_ENABLED, e)
  315         ret = INTERNAL_ERROR
  316 
  317     return ret
  318 
  319 
  320 def get_current_scheduler():
  321     global current_scheduler
  322     try:
  323         with open(CURRENT_SCHEDULER, 'r') as f:
  324             current_scheduler = f.readline().rstrip('\n')
  325             f.close()
  326         ret = 0
  327     except IOError as e:
  328         log.error("Failed to open %s. Error: %s.", CURRENT_SCHEDULER, e)
  329         ret = INTERNAL_ERROR
  330 
  331     return ret
  332 
  333 
  334 def list_schedules():
  335     log.info("Listing snapshot schedules.")
  336     ret = load_tasks_from_file()
  337     if ret == 0:
  338         if len(tasks) == 0:
  339             output("No snapshots scheduled")
  340         else:
  341             jobname = "JOB_NAME".ljust(longest_field+5)
  342             schedule = "SCHEDULE".ljust(longest_field+5)
  343             operation = "OPERATION".ljust(longest_field+5)
  344             volname = "VOLUME NAME".ljust(longest_field+5)
  345             hyphens = "".ljust((longest_field+5) * 4, '-')
  346             print(jobname+schedule+operation+volname)
  347             print(hyphens)
  348             for key in sorted(tasks):
  349                 jobname = key.ljust(longest_field+5)
  350                 schedule = tasks[key].split(":")[0].ljust(
  351                            longest_field + 5)
  352                 volname = tasks[key].split(":")[1].ljust(
  353                           longest_field + 5)
  354                 operation = "Snapshot Create".ljust(longest_field+5)
  355                 print(jobname+schedule+operation+volname)
  356             ret = 0
  357     else:
  358         print_str = "Failed to list snapshot schedules. " \
  359                     "Error: Failed to load tasks from "+GCRON_ENABLED
  360         log.error(print_str)
  361         output(print_str)
  362 
  363     return ret
  364 
  365 
  366 def write_tasks_to_file():
  367     try:
  368         with open(TMP_FILE, "w", 0o644) as f:
  369             # If tasks is empty, just create an empty tmp file
  370             if len(tasks) != 0:
  371                 for key in sorted(tasks):
  372                     jobname = key
  373                     schedule = tasks[key].split(":")[0]
  374                     volname = tasks[key].split(":")[1]
  375                     f.write("%s root PATH=$PATH:/usr/local/sbin:/usr/sbin "
  376                             "gcron.py %s %s\n" % (schedule, volname, jobname))
  377                 f.write("\n")
  378                 f.flush()
  379                 os.fsync(f.fileno())
  380             f.close()
  381     except IOError as e:
  382         log.error("Failed to open %s. Error: %s.", TMP_FILE, e)
  383         ret = INTERNAL_ERROR
  384         return ret
  385 
  386     shutil.move(TMP_FILE, GCRON_ENABLED)
  387     ret = 0
  388 
  389     return ret
  390 
  391 def update_current_scheduler(data):
  392     try:
  393         with open(TMP_FILE, "w", 0o644) as f:
  394             f.write("%s" % data)
  395             f.flush()
  396             os.fsync(f.fileno())
  397             f.close()
  398     except IOError as e:
  399         log.error("Failed to open %s. Error: %s.", TMP_FILE, e)
  400         ret = INTERNAL_ERROR
  401         return ret
  402 
  403     shutil.move(TMP_FILE, CURRENT_SCHEDULER)
  404     ret = 0
  405 
  406     return ret
  407 
  408 
  409 def isVolumePresent(volname):
  410     success = False
  411     if volname == "":
  412         log.debug("No volname given")
  413         return success
  414 
  415     cli = ["gluster",
  416            "volume",
  417            "info",
  418            volname]
  419     log.debug("Running command '%s'", " ".join(cli))
  420 
  421     p = subprocess.Popen(cli, stdout=subprocess.PIPE,
  422                          stderr=subprocess.PIPE)
  423     out, err = p.communicate()
  424     rv = p.returncode
  425 
  426     log.debug("Command '%s' returned '%d'", " ".join(cli), rv)
  427 
  428     if rv:
  429         log.error("Command output:")
  430         log.error(err)
  431     else:
  432         success = True;
  433 
  434     return success
  435 
  436 
  437 def add_schedules(jobname, schedule, volname):
  438     log.info("Adding snapshot schedules.")
  439     ret = load_tasks_from_file()
  440     if ret == 0:
  441         if jobname in tasks:
  442             print_str = ("%s already exists in schedule. Use "
  443                          "'edit' to modify %s" % (jobname, jobname))
  444             log.error(print_str)
  445             output(print_str)
  446             ret = JOB_ALREADY_EXISTS
  447         else:
  448             if not isVolumePresent(volname):
  449                 print_str = ("Volume %s does not exist. Create %s and retry." %
  450                              (volname, volname))
  451                 log.error(print_str)
  452                 output(print_str)
  453                 ret = VOLUME_DOES_NOT_EXIST
  454             else:
  455                 tasks[jobname] = schedule + ":" + volname
  456                 ret = write_tasks_to_file()
  457                 if ret == 0:
  458                     # Create a LOCK_FILE for the job
  459                     job_lockfile = LOCK_FILE_DIR + jobname
  460                     try:
  461                         f = os.open(job_lockfile, os.O_CREAT | os.O_NONBLOCK,
  462                                     0o644)
  463                         os.close(f)
  464                     except OSError as e:
  465                         log.error("Failed to open %s. Error: %s.",
  466                                   job_lockfile, e)
  467                         ret = INTERNAL_ERROR
  468                         return ret
  469                     log.info("Successfully added snapshot schedule %s" %
  470                              jobname)
  471                     output("Successfully added snapshot schedule")
  472                     ret = 0
  473     else:
  474         print_str = "Failed to add snapshot schedule. " \
  475                     "Error: Failed to load tasks from "+GCRON_ENABLED
  476         log.error(print_str)
  477         output(print_str)
  478 
  479     return ret
  480 
  481 
  482 def delete_schedules(jobname):
  483     log.info("Delete snapshot schedules.")
  484     ret = load_tasks_from_file()
  485     if ret == 0:
  486         if jobname in tasks:
  487             del tasks[jobname]
  488             ret = write_tasks_to_file()
  489             if ret == 0:
  490                 # Delete the LOCK_FILE for the job
  491                 job_lockfile = LOCK_FILE_DIR+jobname
  492                 try:
  493                     os.remove(job_lockfile)
  494                 except OSError as e:
  495                     log.error("Failed to open %s. Error: %s.",
  496                               job_lockfile, e)
  497                     ret = INTERNAL_ERROR
  498                     return ret
  499                 log.info("Successfully deleted snapshot schedule %s"
  500                          % jobname)
  501                 output("Successfully deleted snapshot schedule")
  502                 ret = 0
  503         else:
  504             print_str = ("Failed to delete %s. Error: No such "
  505                          "job scheduled" % jobname)
  506             log.error(print_str)
  507             output(print_str)
  508             ret = JOB_NOT_FOUND
  509     else:
  510         print_str = "Failed to delete snapshot schedule. " \
  511                     "Error: Failed to load tasks from "+GCRON_ENABLED
  512         log.error(print_str)
  513         output(print_str)
  514 
  515     return ret
  516 
  517 
  518 def edit_schedules(jobname, schedule, volname):
  519     log.info("Editing snapshot schedules.")
  520     ret = load_tasks_from_file()
  521     if ret == 0:
  522         if jobname in tasks:
  523             if not isVolumePresent(volname):
  524                 print_str = ("Volume %s does not exist. Create %s and retry." %
  525                              (volname, volname))
  526                 log.error(print_str)
  527                 output(print_str)
  528                 ret = VOLUME_DOES_NOT_EXIST
  529             else:
  530                 tasks[jobname] = schedule+":"+volname
  531                 ret = write_tasks_to_file()
  532                 if ret == 0:
  533                     log.info("Successfully edited snapshot schedule %s" %
  534                              jobname)
  535                     output("Successfully edited snapshot schedule")
  536         else:
  537             print_str = ("Failed to edit %s. Error: No such "
  538                          "job scheduled" % jobname)
  539             log.error(print_str)
  540             output(print_str)
  541             ret = JOB_NOT_FOUND
  542     else:
  543         print_str = "Failed to edit snapshot schedule. " \
  544                     "Error: Failed to load tasks from "+GCRON_ENABLED
  545         log.error(print_str)
  546         output(print_str)
  547 
  548     return ret
  549 
  550 def get_bool_val():
  551     getsebool_cli = ["getsebool",
  552                      "-a"]
  553     p1 = subprocess.Popen(getsebool_cli, stdout=subprocess.PIPE,
  554                           stderr=subprocess.PIPE)
  555 
  556     grep_cmd = ["grep",
  557                 "cron_system_cronjob_use_shares"]
  558     p2 = subprocess.Popen(grep_cmd, stdin=p1.stdout,
  559                           stdout=subprocess.PIPE,
  560                           stderr=subprocess.PIPE)
  561 
  562     p1.stdout.close()
  563     output, err = p2.communicate()
  564     rv = p2.returncode
  565 
  566     if rv:
  567         log.error("Command output:")
  568         log.error(err)
  569         return -1
  570 
  571     bool_val = output.split()[2]
  572     log.debug("Bool value = '%s'", bool_val)
  573 
  574     return bool_val
  575 
  576 def get_selinux_status():
  577     getenforce_cli = ["getenforce"]
  578     log.debug("Running command '%s'", " ".join(getenforce_cli))
  579 
  580     try:
  581         p1 = subprocess.Popen(getenforce_cli, stdout=subprocess.PIPE,
  582                               stderr=subprocess.PIPE)
  583     except OSError as oserr:
  584         log.error("Failed to run the command \"getenforce\". Error: %s" %\
  585                   oserr)
  586         return -1
  587 
  588     output, err = p1.communicate()
  589     rv = p1.returncode
  590 
  591     if rv:
  592         log.error("Command output:")
  593         log.error(err)
  594         return -1
  595     else:
  596         selinux_status=output.rstrip()
  597         log.debug("selinux status: %s", selinux_status)
  598 
  599     return selinux_status
  600 
  601 def set_cronjob_user_share():
  602     selinux_status = get_selinux_status()
  603     if (selinux_status == -1):
  604         log.error("Failed to get selinux status")
  605         return -1
  606     elif (selinux_status == "Disabled"):
  607         return 0
  608 
  609     bool_val = get_bool_val()
  610     # In case of a failure (where the boolean value is not)
  611     # present in the system, we should not proceed further
  612     # We should only proceed when the value is "off"
  613     if (bool_val == -1 or bool_val != "off"):
  614         return 0
  615 
  616     setsebool_cli = ["setsebool", "-P",
  617                      "cron_system_cronjob_use_shares",
  618                      "on"]
  619     log.debug("Running command '%s'", " ".join(setsebool_cli))
  620 
  621     p1 = subprocess.Popen(setsebool_cli, stdout=subprocess.PIPE,
  622                           stderr=subprocess.PIPE)
  623 
  624     output, err = p1.communicate()
  625     rv = p1.returncode
  626 
  627     if rv:
  628         log.error("Command output:")
  629         log.error(err)
  630         return rv
  631 
  632     bool_val = get_bool_val()
  633     if (bool_val == "on"):
  634         return 0
  635     else:
  636         # In case of an error or if boolean is not on
  637         # we return a failure here
  638         return -1
  639 
  640 def initialise_scheduler():
  641     ret = set_cronjob_user_share()
  642     if ret:
  643         log.error("Failed to set selinux boolean "
  644                   "cron_system_cronjob_use_shares to 'on'")
  645         return ret
  646 
  647     try:
  648         with open(TMP_FILE, "w+", 0o644) as f:
  649             updater = ("* * * * * root PATH=$PATH:/usr/local/sbin:"
  650                        "/usr/sbin gcron.py --update\n")
  651             f.write("%s\n" % updater)
  652             f.flush()
  653             os.fsync(f.fileno())
  654             f.close()
  655     except IOError as e:
  656         log.error("Failed to open %s. Error: %s.", TMP_FILE, e)
  657         ret = INIT_FAILED
  658         return ret
  659 
  660     shutil.move(TMP_FILE, GCRON_UPDATE_TASK)
  661 
  662     if not os.path.lexists(GCRON_TASKS):
  663         try:
  664             f = open(GCRON_TASKS, "w", 0o644)
  665             f.close()
  666         except IOError as e:
  667             log.error("Failed to open %s. Error: %s.", GCRON_TASKS, e)
  668             ret = INIT_FAILED
  669             return ret
  670 
  671     if os.path.lexists(GCRON_CROND_TASK):
  672         os.remove(GCRON_CROND_TASK)
  673 
  674     os.symlink(GCRON_TASKS, GCRON_CROND_TASK)
  675 
  676     log.info("Successfully initialised snapshot scheduler for this node")
  677     output("Successfully initialised snapshot scheduler for this node")
  678     gf_event (EVENT_SNAPSHOT_SCHEDULER_INITIALISED, status="Success")
  679 
  680     ret = 0
  681     return ret
  682 
  683 
  684 def syntax_checker(args):
  685     if hasattr(args, 'jobname'):
  686         if (len(args.jobname.split()) != 1):
  687             output("Invalid Jobname. Jobname should not be empty and should not contain \" \" character.")
  688             ret = INVALID_JOBNAME
  689             return ret
  690         args.jobname=args.jobname.strip()
  691 
  692     if hasattr(args, 'volname'):
  693         if (len(args.volname.split()) != 1):
  694             output("Invalid Volname. Volname should not be empty and should not contain \" \" character.")
  695             ret = INVALID_VOLNAME
  696             return ret
  697         args.volname=args.volname.strip()
  698 
  699     if hasattr(args, 'schedule'):
  700         if (len(args.schedule.split()) != 5):
  701             output("Invalid Schedule. Please refer to the following for adding a valid cron schedule")
  702             print ("* * * * *")
  703             print ("| | | | |")
  704             print ("| | | | +---- Day of the Week   (range: 1-7, 1 standing for Monday)")
  705             print ("| | | +------ Month of the Year (range: 1-12)")
  706             print ("| | +-------- Day of the Month  (range: 1-31)")
  707             print ("| +---------- Hour              (range: 0-23)")
  708             print ("+------------ Minute            (range: 0-59)")
  709             ret = INVALID_SCHEDULE
  710             return ret
  711 
  712     ret = 0
  713     return ret
  714 
  715 
  716 def perform_operation(args):
  717     if not os.path.exists(CURRENT_SCHEDULER):
  718         update_current_scheduler("none")
  719 
  720     # Initialise snapshot scheduler on local node
  721     if args.action == "init":
  722         ret = initialise_scheduler()
  723         if ret != 0:
  724             output("Failed to initialise snapshot scheduling")
  725             gf_event (EVENT_SNAPSHOT_SCHEDULER_INIT_FAILED,
  726                       error=print_error(ret))
  727         return ret
  728 
  729     # Disable snapshot scheduler
  730     if args.action == "disable_force":
  731         ret = disable_scheduler()
  732         if ret == 0:
  733             subprocess.Popen(["touch", "-h", GCRON_TASKS])
  734             gf_event (EVENT_SNAPSHOT_SCHEDULER_DISABLED,
  735                       status="Successfully Disabled")
  736         else:
  737             gf_event (EVENT_SNAPSHOT_SCHEDULER_DISABLE_FAILED,
  738                       error=print_error(ret))
  739         return ret
  740 
  741     # Check if the symlink to GCRON_TASKS is properly set in the shared storage
  742     if (not os.path.lexists(GCRON_UPDATE_TASK) or
  743         not os.path.lexists(GCRON_CROND_TASK) or
  744         os.readlink(GCRON_CROND_TASK) != GCRON_TASKS):
  745         print_str = ("Please run 'snap_scheduler.py' init to initialise "
  746                      "the snap scheduler for the local node.")
  747         log.error(print_str)
  748         output(print_str)
  749         ret = NODE_NOT_INITIALISED
  750         return ret
  751 
  752     # Check status of snapshot scheduler.
  753     if args.action == "status":
  754         ret = scheduler_status()
  755         if ret == 0:
  756             if scheduler_enabled:
  757                 output("Snapshot scheduling status: Enabled")
  758             else:
  759                 output("Snapshot scheduling status: Disabled")
  760         else:
  761             output("Failed to check status of snapshot scheduler")
  762         return ret
  763 
  764     # Enable snapshot scheduler
  765     if args.action == "enable":
  766         ret = enable_scheduler()
  767         if ret == 0:
  768             subprocess.Popen(["touch", "-h", GCRON_TASKS])
  769             gf_event (EVENT_SNAPSHOT_SCHEDULER_ENABLED,
  770                       status="Successfully Enabled")
  771         else:
  772             gf_event (EVENT_SNAPSHOT_SCHEDULER_ENABLE_FAILED,
  773                       error=print_error(ret))
  774         return ret
  775 
  776     # Disable snapshot scheduler
  777     if args.action == "disable":
  778         ret = disable_scheduler()
  779         if ret == 0:
  780             subprocess.Popen(["touch", "-h", GCRON_TASKS])
  781             gf_event (EVENT_SNAPSHOT_SCHEDULER_DISABLED,
  782                       status="Successfully Disabled")
  783         else:
  784             gf_event (EVENT_SNAPSHOT_SCHEDULER_DISABLE_FAILED,
  785                       error=print_error(ret))
  786         return ret
  787 
  788     # List snapshot schedules
  789     if args.action == "list":
  790         ret = list_schedules()
  791         return ret
  792 
  793     # Add snapshot schedules
  794     if args.action == "add":
  795         ret = syntax_checker(args)
  796         if ret != 0:
  797             return ret
  798         ret = add_schedules(args.jobname, args.schedule, args.volname)
  799         if ret == 0:
  800             subprocess.Popen(["touch", "-h", GCRON_TASKS])
  801             gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADDED,
  802                       status="Successfully added job "+args.jobname)
  803         else:
  804             gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_ADD_FAILED,
  805                       status="Failed to add job "+args.jobname,
  806                       error=print_error(ret))
  807         return ret
  808 
  809     # Delete snapshot schedules
  810     if args.action == "delete":
  811         ret = syntax_checker(args)
  812         if ret != 0:
  813             return ret
  814         ret = delete_schedules(args.jobname)
  815         if ret == 0:
  816             subprocess.Popen(["touch", "-h", GCRON_TASKS])
  817             gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETED,
  818                       status="Successfully deleted job "+args.jobname)
  819         else:
  820             gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_DELETE_FAILED,
  821                       status="Failed to delete job "+args.jobname,
  822                       error=print_error(ret))
  823         return ret
  824 
  825     # Edit snapshot schedules
  826     if args.action == "edit":
  827         ret = syntax_checker(args)
  828         if ret != 0:
  829             return ret
  830         ret = edit_schedules(args.jobname, args.schedule, args.volname)
  831         if ret == 0:
  832             subprocess.Popen(["touch", "-h", GCRON_TASKS])
  833             gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDITED,
  834                       status="Successfully edited job "+args.jobname)
  835         else:
  836             gf_event (EVENT_SNAPSHOT_SCHEDULER_SCHEDULE_EDIT_FAILED,
  837                       status="Failed to edit job "+args.jobname,
  838                       error=print_error(ret))
  839         return ret
  840 
  841     ret = INVALID_ARG
  842     return ret
  843 
  844 def gf_event(event_type, **kwargs):
  845     if EVENTS_ENABLED:
  846         from events.gf_event import gf_event as gfevent
  847         gfevent(event_type, **kwargs)
  848 
  849 
  850 def main(argv):
  851     initLogger()
  852     ret = -1
  853     parser = argparse.ArgumentParser()
  854     subparsers = parser.add_subparsers(dest="action",
  855                                        metavar=('{init, status, enable,'
  856                                                ' disable, list, add,'
  857                                                ' delete, edit}'))
  858     subparsers.add_parser('init',
  859                           help="Initialise the node for snapshot scheduling")
  860 
  861     subparsers.add_parser("status",
  862                           help="Check if snapshot scheduling is "
  863                           "enabled or disabled")
  864     subparsers.add_parser("enable",
  865                           help="Enable snapshot scheduling")
  866     subparsers.add_parser("disable",
  867                           help="Disable snapshot scheduling")
  868     subparsers.add_parser("disable_force")
  869     subparsers.add_parser("list",
  870                           help="List snapshot schedules")
  871     parser_add = subparsers.add_parser("add",
  872                                        help="Add snapshot schedules")
  873     parser_add.add_argument("jobname", help="Job Name")
  874     parser_add.add_argument("schedule", help="Schedule")
  875     parser_add.add_argument("volname", help="Volume Name")
  876 
  877     parser_delete = subparsers.add_parser("delete",
  878                                           help="Delete snapshot schedules")
  879     parser_delete.add_argument("jobname", help="Job Name")
  880     parser_edit = subparsers.add_parser("edit",
  881                                         help="Edit snapshot schedules")
  882     parser_edit.add_argument("jobname", help="Job Name")
  883     parser_edit.add_argument("schedule", help="Schedule")
  884     parser_edit.add_argument("volname", help="Volume Name")
  885 
  886     args = parser.parse_args(argv)
  887 
  888     if not os.path.exists(SHARED_STORAGE_DIR):
  889         output("Failed: "+SHARED_STORAGE_DIR+" does not exist.")
  890         return SHARED_STORAGE_DIR_DOESNT_EXIST
  891 
  892     if not os.path.ismount(SHARED_STORAGE_DIR):
  893         output("Failed: Shared storage is not mounted at "+SHARED_STORAGE_DIR)
  894         return SHARED_STORAGE_NOT_MOUNTED
  895 
  896     if not os.path.exists(SHARED_STORAGE_DIR+"/snaps/"):
  897         try:
  898             os.makedirs(SHARED_STORAGE_DIR+"/snaps/")
  899         except OSError as e:
  900             if errno != EEXIST:
  901                 log.error("Failed to create %s : %s", SHARED_STORAGE_DIR+"/snaps/", e)
  902                 output("Failed to create %s. Error: %s"
  903                        % (SHARED_STORAGE_DIR+"/snaps/", e))
  904                 return INTERNAL_ERROR
  905 
  906     if not os.path.exists(GCRON_ENABLED):
  907         f = os.open(GCRON_ENABLED, os.O_CREAT | os.O_NONBLOCK, 0o644)
  908         os.close(f)
  909 
  910     if not os.path.exists(LOCK_FILE_DIR):
  911         try:
  912             os.makedirs(LOCK_FILE_DIR)
  913         except OSError as e:
  914             if errno != EEXIST:
  915                 log.error("Failed to create %s : %s", LOCK_FILE_DIR, e)
  916                 output("Failed to create %s. Error: %s"
  917                        % (LOCK_FILE_DIR, e))
  918                 return INTERNAL_ERROR
  919 
  920     try:
  921         f = os.open(LOCK_FILE, os.O_CREAT | os.O_RDWR | os.O_NONBLOCK, 0o644)
  922         try:
  923             fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
  924             ret = perform_operation(args)
  925             fcntl.flock(f, fcntl.LOCK_UN)
  926         except IOError:
  927             log.info("%s is being processed by another agent.", LOCK_FILE)
  928             output("Another snap_scheduler command is running. "
  929                    "Please try again after some time.")
  930             return ANOTHER_TRANSACTION_IN_PROGRESS
  931         os.close(f)
  932     except OSError as e:
  933         log.error("Failed to open %s : %s", LOCK_FILE, e)
  934         output("Failed to open %s. Error: %s" % (LOCK_FILE, e))
  935         return INTERNAL_ERROR
  936 
  937     return ret
  938 
  939 
  940 if __name__ == "__main__":
  941     sys.exit(main(sys.argv[1:]))