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 psycopg2 
 34  import sys 
 35  import fcntl 
 36  import os 
 37  import apt_pkg 
 38  import time 
 39  import errno 
 40  from glob import glob 
 41  from re import findall 
 42   
 43  from daklib import utils 
 44  from daklib.config import Config 
 45  from daklib.dak_exceptions import DBUpdateError 
 46  from daklib.daklog import Logger 
 47   
 48  ################################################################################ 
 49   
 50  Cnf = None 
 51   
 52  ################################################################################ 
 53   
 54   
55 -class UpdateDB:
56 - def usage(self, exit_code=0):
57 print("""Usage: dak update-db 58 Updates dak's database schema to the lastest version. You should disable crontabs while this is running 59 60 -h, --help show this help and exit. 61 -y, --yes do not ask for confirmation""") 62 sys.exit(exit_code)
63 64 ################################################################################ 65
66 - def update_db_to_zero(self):
67 """ This function will attempt to update a pre-zero database schema to zero """ 68 69 # First, do the sure thing, and create the configuration table 70 try: 71 print("Creating configuration table ...") 72 c = self.db.cursor() 73 c.execute("""CREATE TABLE config ( 74 id SERIAL PRIMARY KEY NOT NULL, 75 name TEXT UNIQUE NOT NULL, 76 value TEXT 77 );""") 78 c.execute("INSERT INTO config VALUES ( nextval('config_id_seq'), 'db_revision', '0')") 79 self.db.commit() 80 81 except psycopg2.ProgrammingError: 82 self.db.rollback() 83 print("Failed to create configuration table.") 84 print("Can the projectB user CREATE TABLE?") 85 print("") 86 print("Aborting update.") 87 sys.exit(-255)
88 89 ################################################################################ 90
91 - def get_db_rev(self):
92 # We keep database revision info the config table 93 # Try and access it 94 95 try: 96 c = self.db.cursor() 97 q = c.execute("SELECT value FROM config WHERE name = 'db_revision';") 98 return c.fetchone()[0] 99 100 except psycopg2.ProgrammingError: 101 # Whoops .. no config table ... 102 self.db.rollback() 103 print("No configuration table found, assuming dak database revision to be pre-zero") 104 return -1
105 106 ################################################################################ 107
108 - def get_transaction_id(self):
109 ''' 110 Returns the current transaction id as a string. 111 ''' 112 cursor = self.db.cursor() 113 cursor.execute("SELECT txid_current();") 114 id = cursor.fetchone()[0] 115 cursor.close() 116 return id
117 118 ################################################################################ 119
120 - def update_db(self):
121 # Ok, try and find the configuration table 122 print("Determining dak database revision ...") 123 cnf = Config() 124 logger = Logger('update-db') 125 modules = [] 126 127 try: 128 # Build a connect string 129 if "DB::Service" in cnf: 130 connect_str = "service=%s" % cnf["DB::Service"] 131 else: 132 connect_str = "dbname=%s" % (cnf["DB::Name"]) 133 if "DB::Host" in cnf and cnf["DB::Host"] != '': 134 connect_str += " host=%s" % (cnf["DB::Host"]) 135 if "DB::Port" in cnf and cnf["DB::Port"] != '-1': 136 connect_str += " port=%d" % (int(cnf["DB::Port"])) 137 138 self.db = psycopg2.connect(connect_str) 139 140 db_role = cnf.get("DB::Role") 141 if db_role: 142 self.db.cursor().execute('SET ROLE "{}"'.format(db_role)) 143 144 except Exception as e: 145 print("FATAL: Failed connect to database (%s)" % str(e)) 146 sys.exit(1) 147 148 database_revision = int(self.get_db_rev()) 149 logger.log(['transaction id before update: %s' % self.get_transaction_id()]) 150 151 if database_revision == -1: 152 print("dak database schema predates update-db.") 153 print("") 154 print("This script will attempt to upgrade it to the lastest, but may fail.") 155 print("Please make sure you have a database backup handy. If you don't, press Ctrl-C now!") 156 print("") 157 print("Continuing in five seconds ...") 158 time.sleep(5) 159 print("") 160 print("Attempting to upgrade pre-zero database to zero") 161 162 self.update_db_to_zero() 163 database_revision = 0 164 165 dbfiles = glob(os.path.join(os.path.dirname(__file__), 'dakdb/update*.py')) 166 required_database_schema = max(int(x) for x in findall(r'update(\d+).py', " ".join(dbfiles))) 167 168 print("dak database schema at %d" % database_revision) 169 print("dak version requires schema %d" % required_database_schema) 170 171 if database_revision < required_database_schema: 172 print("\nUpdates to be applied:") 173 for i in range(database_revision, required_database_schema): 174 i += 1 175 dakdb = __import__("dakdb", globals(), locals(), ['update' + str(i)]) 176 update_module = getattr(dakdb, "update" + str(i)) 177 print("Update %d: %s" % (i, next(s for s in update_module.__doc__.split("\n") if s))) 178 modules.append((update_module, i)) 179 if not Config().find_b("Update-DB::Options::Yes", False): 180 prompt = "\nUpdate database? (y/N) " 181 answer = utils.input_or_exit(prompt) 182 if answer.upper() != 'Y': 183 sys.exit(0) 184 else: 185 print("no updates required") 186 logger.log(["no updates required"]) 187 sys.exit(0) 188 189 for module in modules: 190 (update_module, i) = module 191 try: 192 update_module.do_update(self) 193 message = "updated database schema from %d to %d" % (database_revision, i) 194 print(message) 195 logger.log([message]) 196 except DBUpdateError as e: 197 # Seems the update did not work. 198 print("Was unable to update database schema from %d to %d." % (database_revision, i)) 199 print("The error message received was %s" % (e)) 200 logger.log(["DB Schema upgrade failed"]) 201 logger.close() 202 utils.fubar("DB Schema upgrade failed") 203 database_revision += 1 204 logger.close()
205 206 ################################################################################ 207
208 - def init(self):
209 cnf = Config() 210 arguments = [('h', "help", "Update-DB::Options::Help"), 211 ("y", "yes", "Update-DB::Options::Yes")] 212 for i in ["help"]: 213 key = "Update-DB::Options::%s" % i 214 if key not in cnf: 215 cnf[key] = "" 216 217 arguments = apt_pkg.parse_commandline(cnf.Cnf, arguments, sys.argv) 218 219 options = cnf.subtree("Update-DB::Options") 220 if options["Help"]: 221 self.usage() 222 elif arguments: 223 utils.warn("dak update-db takes no arguments.") 224 self.usage(exit_code=1) 225 226 try: 227 if os.path.isdir(cnf["Dir::Lock"]): 228 lock_fd = os.open(os.path.join(cnf["Dir::Lock"], 'daily.lock'), os.O_RDONLY | os.O_CREAT) 229 fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) 230 else: 231 utils.warn("Lock directory doesn't exist yet - not locking") 232 except OSError as e: 233 if e.errno in (errno.EACCES, errno.EAGAIN): 234 utils.fubar("Couldn't obtain lock, looks like archive is doing something, try again later.") 235 else: 236 raise 237 238 self.update_db()
239 240 241 ################################################################################ 242 243 if __name__ == '__main__': 244 app = UpdateDB() 245 app.init() 246 247
248 -def main():
249 app = UpdateDB() 250 app.init()
251