paul@1049 | 1 | #!/usr/bin/env python |
paul@1049 | 2 | |
paul@1049 | 3 | """ |
paul@1049 | 4 | Remove expired events from quota journals. |
paul@1049 | 5 | |
paul@1049 | 6 | Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk> |
paul@1049 | 7 | |
paul@1049 | 8 | This program is free software; you can redistribute it and/or modify it under |
paul@1049 | 9 | the terms of the GNU General Public License as published by the Free Software |
paul@1049 | 10 | Foundation; either version 3 of the License, or (at your option) any later |
paul@1049 | 11 | version. |
paul@1049 | 12 | |
paul@1049 | 13 | This program is distributed in the hope that it will be useful, but WITHOUT |
paul@1049 | 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paul@1049 | 15 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
paul@1049 | 16 | details. |
paul@1049 | 17 | |
paul@1049 | 18 | You should have received a copy of the GNU General Public License along with |
paul@1049 | 19 | this program. If not, see <http://www.gnu.org/licenses/>. |
paul@1049 | 20 | """ |
paul@1049 | 21 | |
paul@1049 | 22 | from os.path import split |
paul@1049 | 23 | import sys |
paul@1049 | 24 | |
paul@1049 | 25 | # Find the modules. |
paul@1049 | 26 | |
paul@1049 | 27 | try: |
paul@1049 | 28 | import imiptools |
paul@1049 | 29 | except ImportError: |
paul@1049 | 30 | parent = split(split(__file__)[0])[0] |
paul@1049 | 31 | if split(parent)[1] == "imip-agent": |
paul@1049 | 32 | sys.path.append(parent) |
paul@1049 | 33 | |
paul@1049 | 34 | from codecs import getwriter |
paul@1049 | 35 | from imiptools.dates import get_datetime, get_default_timezone, get_time, \ |
paul@1049 | 36 | to_utc_datetime |
paul@1049 | 37 | from imip_store import FileJournal |
paul@1049 | 38 | |
paul@1049 | 39 | def remove_expired_entries(entries, expiry): |
paul@1049 | 40 | |
paul@1049 | 41 | "Remove from 'entries' events that end at or before 'expiry'." |
paul@1049 | 42 | |
paul@1049 | 43 | removed = [] |
paul@1049 | 44 | |
paul@1049 | 45 | i = 0 |
paul@1049 | 46 | while i < len(entries): |
paul@1049 | 47 | uid, recurrenceid, duration, found_expiry = entry = entries[i] |
paul@1049 | 48 | found_expiry = get_datetime(found_expiry) |
paul@1049 | 49 | |
paul@1049 | 50 | if found_expiry <= expiry: |
paul@1049 | 51 | removed.append(entry) |
paul@1049 | 52 | del entries[i] |
paul@1049 | 53 | else: |
paul@1049 | 54 | i += 1 |
paul@1049 | 55 | |
paul@1049 | 56 | return removed |
paul@1049 | 57 | |
paul@1049 | 58 | def update_entries(journal, quota, expiry, store, verbose): |
paul@1049 | 59 | |
paul@1049 | 60 | """ |
paul@1049 | 61 | Using the given 'journal' process quota records for the given 'quota', with |
paul@1049 | 62 | the given 'expiry' time used to expire events ending before or at this time, |
paul@1049 | 63 | with None meaning the current time. |
paul@1049 | 64 | |
paul@1049 | 65 | If 'store' is set, the stored details will be updated; otherwise, the |
paul@1049 | 66 | details will be written to standard output. |
paul@1049 | 67 | |
paul@1049 | 68 | If 'verbose' is set, messages will be written to standard error. |
paul@1049 | 69 | """ |
paul@1049 | 70 | |
paul@1049 | 71 | if not store: |
paul@1049 | 72 | stdout = getwriter("utf-8")(sys.stdout) |
paul@1049 | 73 | if verbose: |
paul@1049 | 74 | stderr = getwriter("utf-8")(sys.stderr) |
paul@1049 | 75 | |
paul@1049 | 76 | if not expiry: |
paul@1049 | 77 | expiry = get_time() |
paul@1049 | 78 | |
paul@1049 | 79 | journal.acquire_lock(quota) |
paul@1049 | 80 | |
paul@1049 | 81 | try: |
paul@1049 | 82 | for user in journal.get_quota_users(quota): |
paul@1049 | 83 | if verbose: |
paul@1049 | 84 | print >>stderr, user |
paul@1049 | 85 | |
paul@1049 | 86 | entries = journal.get_entries(quota, user) |
paul@1049 | 87 | removed = remove_expired_entries(entries, expiry) |
paul@1049 | 88 | |
paul@1049 | 89 | if verbose: |
paul@1049 | 90 | for entry in removed: |
paul@1049 | 91 | print >>stderr, "Removed", entry |
paul@1049 | 92 | |
paul@1049 | 93 | # Store the processed entries. |
paul@1049 | 94 | |
paul@1049 | 95 | if store: |
paul@1049 | 96 | journal.set_entries(quota, user, entries) |
paul@1049 | 97 | |
paul@1049 | 98 | # Alternatively, just write the entries to standard output. |
paul@1049 | 99 | |
paul@1049 | 100 | else: |
paul@1049 | 101 | for entry in entries: |
paul@1049 | 102 | print >>stdout, "\t".join([(s or "") for s in entry]) |
paul@1049 | 103 | finally: |
paul@1049 | 104 | journal.release_lock(quota) |
paul@1049 | 105 | |
paul@1049 | 106 | # Main program. |
paul@1049 | 107 | |
paul@1049 | 108 | if __name__ == "__main__": |
paul@1049 | 109 | |
paul@1049 | 110 | # Interpret the command line arguments. |
paul@1049 | 111 | |
paul@1049 | 112 | quotas = [] |
paul@1049 | 113 | args = [] |
paul@1049 | 114 | journal_dir = [] |
paul@1049 | 115 | expiry = [] |
paul@1049 | 116 | ignored = [] |
paul@1049 | 117 | |
paul@1049 | 118 | # Collect quota details first, switching to other arguments when encountering |
paul@1049 | 119 | # switches. |
paul@1049 | 120 | |
paul@1049 | 121 | l = quotas |
paul@1049 | 122 | |
paul@1049 | 123 | for arg in sys.argv[1:]: |
paul@1049 | 124 | if arg in ("-s", "-v"): |
paul@1049 | 125 | args.append(arg) |
paul@1049 | 126 | l = ignored |
paul@1049 | 127 | elif arg == "-j": |
paul@1049 | 128 | l = journal_dir |
paul@1049 | 129 | elif arg == "-e": |
paul@1049 | 130 | l = expiry |
paul@1049 | 131 | else: |
paul@1049 | 132 | l.append(arg) |
paul@1049 | 133 | |
paul@1049 | 134 | try: |
paul@1049 | 135 | quota = quotas[0] |
paul@1049 | 136 | except IndexError: |
paul@1049 | 137 | print >>sys.stderr, """\ |
paul@1049 | 138 | Usage: %s <quota> <options> |
paul@1049 | 139 | |
paul@1049 | 140 | Need a quota along with the -s option if updating the journal. |
paul@1049 | 141 | Specify -v for additional messages on standard error. |
paul@1049 | 142 | |
paul@1049 | 143 | General options: |
paul@1049 | 144 | |
paul@1049 | 145 | -e indicate an expiry time for events (default is now) |
paul@1049 | 146 | -j indicate the journal directory location |
paul@1049 | 147 | """ % split(sys.argv[0])[1] |
paul@1049 | 148 | sys.exit(1) |
paul@1049 | 149 | |
paul@1049 | 150 | # Define any other options. |
paul@1049 | 151 | |
paul@1049 | 152 | store = "-s" in args |
paul@1049 | 153 | verbose = "-v" in args |
paul@1049 | 154 | |
paul@1049 | 155 | # Override defaults if indicated. |
paul@1049 | 156 | |
paul@1049 | 157 | journal_dir = journal_dir and journal_dir[0] or None |
paul@1049 | 158 | expiry = expiry and expiry[0] or None |
paul@1049 | 159 | |
paul@1049 | 160 | if expiry: |
paul@1049 | 161 | expiry = to_utc_datetime(get_datetime(expiry), get_default_timezone()) |
paul@1049 | 162 | if not expiry: |
paul@1049 | 163 | print >>sys.stderr, "Expiry time must be a valid datetime." |
paul@1049 | 164 | sys.exit(1) |
paul@1049 | 165 | |
paul@1049 | 166 | # Obtain store-related objects. |
paul@1049 | 167 | |
paul@1049 | 168 | journal = FileJournal(journal_dir) |
paul@1049 | 169 | |
paul@1049 | 170 | # Obtain a list of users for processing. |
paul@1049 | 171 | |
paul@1049 | 172 | if quota in ("*", "all"): |
paul@1049 | 173 | quotas = journal.get_quotas() |
paul@1049 | 174 | |
paul@1049 | 175 | # Process the given users. |
paul@1049 | 176 | |
paul@1049 | 177 | if verbose: |
paul@1049 | 178 | stderr = getwriter("utf-8")(sys.stderr) |
paul@1049 | 179 | |
paul@1049 | 180 | for quota in quotas: |
paul@1049 | 181 | if verbose: |
paul@1049 | 182 | print >>stderr, quota |
paul@1049 | 183 | update_entries(journal, quota, expiry, store, verbose) |
paul@1049 | 184 | |
paul@1049 | 185 | # vim: tabstop=4 expandtab shiftwidth=4 |