Package dak :: Module update_db
[hide private]
[frames] | no frames]

Source Code for Module dak.update_db

  1  #! /usr/bin/env python3 
  2   
  3  """Database Update Main Script 
  4   
  5  @contact: Debian FTP Master <ftpmaster@debian.org> 
  6  # Copyright (C) 2008  Michael Casadevall <mcasadevall@debian.org> 
  7  @license: GNU General Public License version 2 or later 
  8  """ 
  9   
 10  # This program is free software; you can redistribute it and/or modify 
 11  # it under the terms of the GNU General Public License as published by 
 12  # the Free Software Foundation; either version 2 of the License, or 
 13  # (at your option) any later version. 
 14   
 15  # This program is distributed in the hope that it will be useful, 
 16  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 17  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 18  # GNU General Public License for more details. 
 19   
 20  # You should have received a copy of the GNU General Public License 
 21  # along with this program; if not, write to the Free Software 
 22  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 23   
 24  ################################################################################ 
 25   
 26  # <Ganneff> when do you have it written? 
 27  # <NCommander> Ganneff, after you make my debian account 
 28  # <Ganneff> blackmail wont work 
 29  # <NCommander> damn it 
 30   
 31  ################################################################################ 
 32   
 33  import errno 
 34  import fcntl 
 35  import os 
 36  import sys 
 37  import time 
 38  from glob import glob 
 39  from re import findall 
 40   
 41  import apt_pkg 
 42  import psycopg2 
 43   
 44  from daklib import utils 
 45  from daklib.config import Config 
 46  from daklib.dak_exceptions import DBUpdateError 
 47  from daklib.daklog import Logger 
 48   
 49  ################################################################################ 
 50   
 51  Cnf = None 
 52   
 53  ################################################################################ 
 54   
 55   
56 -class UpdateDB:
57 - def usage(self, exit_code=0):
58 print( 59 """Usage: dak update-db 60 Updates dak's database schema to the lastest version. You should disable crontabs while this is running 61 62 -h, --help show this help and exit. 63 -y, --yes do not ask for confirmation""" 64 ) 65 sys.exit(exit_code)
66 67 ################################################################################ 68
69 - def update_db_to_zero(self):
70 """This function will attempt to update a pre-zero database schema to zero""" 71 72 # First, do the sure thing, and create the configuration table 73 try: 74 print("Creating configuration table ...") 75 c = self.db.cursor() 76 c.execute( 77 """CREATE TABLE config ( 78 id SERIAL PRIMARY KEY NOT NULL, 79 name TEXT UNIQUE NOT NULL, 80 value TEXT 81 );""" 82 ) 83 c.execute( 84 "INSERT INTO config VALUES ( nextval('config_id_seq'), 'db_revision', '0')" 85 ) 86 self.db.commit() 87 88 except psycopg2.ProgrammingError: 89 self.db.rollback() 90 print("Failed to create configuration table.") 91 print("Can the projectB user CREATE TABLE?") 92 print("") 93 print("Aborting update.") 94 sys.exit(-255)
95 96 ################################################################################ 97
98 - def get_db_rev(self):
99 # We keep database revision info the config table 100 # Try and access it 101 102 try: 103 c = self.db.cursor() 104 c.execute("SELECT value FROM config WHERE name = 'db_revision';") 105 return c.fetchone()[0] 106 107 except psycopg2.ProgrammingError: 108 # Whoops .. no config table ... 109 self.db.rollback() 110 print( 111 "No configuration table found, assuming dak database revision to be pre-zero" 112 ) 113 return -1
114 115 ################################################################################ 116
117 - def get_transaction_id(self):
118 """ 119 Returns the current transaction id as a string. 120 """ 121 cursor = self.db.cursor() 122 cursor.execute("SELECT txid_current();") 123 id = cursor.fetchone()[0] 124 cursor.close() 125 return id
126 127 ################################################################################ 128
129 - def update_db(self):
130 # Ok, try and find the configuration table 131 print("Determining dak database revision ...") 132 cnf = Config() 133 logger = Logger("update-db") 134 modules = [] 135 136 try: 137 # Build a connect string 138 if "DB::Service" in cnf: 139 connect_str = "service=%s" % cnf["DB::Service"] 140 else: 141 connect_str = "dbname=%s" % (cnf["DB::Name"]) 142 if "DB::Host" in cnf and cnf["DB::Host"] != "": 143 connect_str += " host=%s" % (cnf["DB::Host"]) 144 if "DB::Port" in cnf and cnf["DB::Port"] != "-1": 145 connect_str += " port=%d" % (int(cnf["DB::Port"])) 146 147 self.db = psycopg2.connect(connect_str) 148 149 db_role = cnf.get("DB::Role") 150 if db_role: 151 self.db.cursor().execute('SET ROLE "{}"'.format(db_role)) 152 153 except Exception as e: 154 print("FATAL: Failed connect to database (%s)" % str(e)) 155 sys.exit(1) 156 157 database_revision = int(self.get_db_rev()) 158 logger.log(["transaction id before update: %s" % self.get_transaction_id()]) 159 160 if database_revision == -1: 161 print("dak database schema predates update-db.") 162 print("") 163 print( 164 "This script will attempt to upgrade it to the lastest, but may fail." 165 ) 166 print( 167 "Please make sure you have a database backup handy. If you don't, press Ctrl-C now!" 168 ) 169 print("") 170 print("Continuing in five seconds ...") 171 time.sleep(5) 172 print("") 173 print("Attempting to upgrade pre-zero database to zero") 174 175 self.update_db_to_zero() 176 database_revision = 0 177 178 dbfiles = glob(os.path.join(os.path.dirname(__file__), "dakdb/update*.py")) 179 required_database_schema = max( 180 int(x) for x in findall(r"update(\d+).py", " ".join(dbfiles)) 181 ) 182 183 print("dak database schema at %d" % database_revision) 184 print("dak version requires schema %d" % required_database_schema) 185 186 if database_revision < required_database_schema: 187 print("\nUpdates to be applied:") 188 for i in range(database_revision, required_database_schema): 189 i += 1 190 dakdb = __import__("dakdb", globals(), locals(), ["update" + str(i)]) 191 update_module = getattr(dakdb, "update" + str(i)) 192 print( 193 "Update %d: %s" 194 % (i, next(s for s in update_module.__doc__.split("\n") if s)) 195 ) 196 modules.append((update_module, i)) 197 if not Config().find_b("Update-DB::Options::Yes", False): 198 prompt = "\nUpdate database? (y/N) " 199 answer = utils.input_or_exit(prompt) 200 if answer.upper() != "Y": 201 sys.exit(0) 202 else: 203 print("no updates required") 204 logger.log(["no updates required"]) 205 sys.exit(0) 206 207 for module in modules: 208 (update_module, i) = module 209 try: 210 update_module.do_update(self) 211 message = "updated database schema from %d to %d" % ( 212 database_revision, 213 i, 214 ) 215 print(message) 216 logger.log([message]) 217 except DBUpdateError as e: 218 # Seems the update did not work. 219 print( 220 "Was unable to update database schema from %d to %d." 221 % (database_revision, i) 222 ) 223 print("The error message received was %s" % (e)) 224 logger.log(["DB Schema upgrade failed"]) 225 logger.close() 226 utils.fubar("DB Schema upgrade failed") 227 database_revision += 1 228 logger.close()
229 230 ################################################################################ 231
232 - def init(self):
233 cnf = Config() 234 arguments = [ 235 ("h", "help", "Update-DB::Options::Help"), 236 ("y", "yes", "Update-DB::Options::Yes"), 237 ] 238 for i in ["help"]: 239 key = "Update-DB::Options::%s" % i 240 if key not in cnf: 241 cnf[key] = "" 242 243 arguments = apt_pkg.parse_commandline(cnf.Cnf, arguments, sys.argv) 244 245 options = cnf.subtree("Update-DB::Options") 246 if options["Help"]: 247 self.usage() 248 elif arguments: 249 utils.warn("dak update-db takes no arguments.") 250 self.usage(exit_code=1) 251 252 try: 253 if os.path.isdir(cnf["Dir::Lock"]): 254 lock_fd = os.open( 255 os.path.join(cnf["Dir::Lock"], "daily.lock"), 256 os.O_RDONLY | os.O_CREAT, 257 ) 258 fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) 259 else: 260 utils.warn("Lock directory doesn't exist yet - not locking") 261 except OSError as e: 262 if e.errno in (errno.EACCES, errno.EAGAIN): 263 utils.fubar( 264 "Couldn't obtain lock, looks like archive is doing something, try again later." 265 ) 266 else: 267 raise 268 269 self.update_db()
270 271 272 ################################################################################ 273 274 if __name__ == "__main__": 275 app = UpdateDB() 276 app.init() 277 278
279 -def main():
280 app = UpdateDB() 281 app.init()
282