3 """ Database Update Main Script
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
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.
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.
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
24 ################################################################################
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
31 ################################################################################
41 from re import findall
43 from daklib import utils
44 from daklib.config import Config
45 from daklib.dak_exceptions import DBUpdateError
46 from daklib.daklog import Logger
48 ################################################################################
52 ################################################################################
55 def usage (self, exit_code=0):
56 print """Usage: dak update-db
57 Updates dak's database schema to the lastest version. You should disable crontabs while this is running
59 -h, --help show this help and exit."""
63 ################################################################################
65 def update_db_to_zero(self):
66 """ This function will attempt to update a pre-zero database schema to zero """
68 # First, do the sure thing, and create the configuration table
70 print "Creating configuration table ..."
72 c.execute("""CREATE TABLE config (
73 id SERIAL PRIMARY KEY NOT NULL,
74 name TEXT UNIQUE NOT NULL,
77 c.execute("INSERT INTO config VALUES ( nextval('config_id_seq'), 'db_revision', '0')")
80 except psycopg2.ProgrammingError:
82 print "Failed to create configuration table."
83 print "Can the projectB user CREATE TABLE?"
85 print "Aborting update."
88 ################################################################################
91 # We keep database revision info the config table
96 q = c.execute("SELECT value FROM config WHERE name = 'db_revision';")
97 return c.fetchone()[0]
99 except psycopg2.ProgrammingError:
100 # Whoops .. no config table ...
102 print "No configuration table found, assuming dak database revision to be pre-zero"
105 ################################################################################
107 def get_transaction_id(self):
109 Returns the current transaction id as a string.
111 cursor = self.db.cursor()
112 cursor.execute("SELECT txid_current();")
113 id = cursor.fetchone()[0]
117 ################################################################################
120 # Ok, try and find the configuration table
121 print "Determining dak database revision ..."
123 logger = Logger('update-db')
127 # Build a connect string
128 if cnf.has_key("DB::Service"):
129 connect_str = "service=%s" % cnf["DB::Service"]
131 connect_str = "dbname=%s"% (cnf["DB::Name"])
132 if cnf.has_key("DB::Host") and cnf["DB::Host"] != '':
133 connect_str += " host=%s" % (cnf["DB::Host"])
134 if cnf.has_key("DB::Port") and cnf["DB::Port"] != '-1':
135 connect_str += " port=%d" % (int(cnf["DB::Port"]))
137 self.db = psycopg2.connect(connect_str)
139 except Exception as e:
140 print "FATAL: Failed connect to database (%s)" % str(e)
143 database_revision = int(self.get_db_rev())
144 logger.log(['transaction id before update: %s' % self.get_transaction_id()])
146 if database_revision == -1:
147 print "dak database schema predates update-db."
149 print "This script will attempt to upgrade it to the lastest, but may fail."
150 print "Please make sure you have a database backup handy. If you don't, press Ctrl-C now!"
152 print "Continuing in five seconds ..."
155 print "Attempting to upgrade pre-zero database to zero"
157 self.update_db_to_zero()
158 database_revision = 0
160 dbfiles = glob(os.path.join(os.path.dirname(__file__), 'dakdb/update*.py'))
161 required_database_schema = max(map(int, findall('update(\d+).py', " ".join(dbfiles))))
163 print "dak database schema at %d" % database_revision
164 print "dak version requires schema %d" % required_database_schema
166 if database_revision < required_database_schema:
167 print "\nUpdates to be applied:"
168 for i in range(database_revision, required_database_schema):
170 dakdb = __import__("dakdb", globals(), locals(), ['update'+str(i)])
171 update_module = getattr(dakdb, "update"+str(i))
172 print "Update %d: %s" % (i, next(s for s in update_module.__doc__.split("\n") if s))
173 modules.append((update_module, i))
174 prompt = "\nUpdate database? (y/N) "
175 answer = utils.our_raw_input(prompt)
176 if answer.upper() != 'Y':
179 print "no updates required"
180 logger.log(["no updates required"])
183 for module in modules:
184 (update_module, i) = module
186 update_module.do_update(self)
187 message = "updated database schema from %d to %d" % (database_revision, i)
189 logger.log([message])
190 except DBUpdateError as e:
191 # Seems the update did not work.
192 print "Was unable to update database schema from %d to %d." % (database_revision, i)
193 print "The error message received was %s" % (e)
194 logger.log(["DB Schema upgrade failed"])
196 utils.fubar("DB Schema upgrade failed")
197 database_revision += 1
200 ################################################################################
204 arguments = [('h', "help", "Update-DB::Options::Help")]
206 if not cnf.has_key("Update-DB::Options::%s" % (i)):
207 cnf["Update-DB::Options::%s" % (i)] = ""
209 arguments = apt_pkg.parse_commandline(cnf.Cnf, arguments, sys.argv)
211 options = cnf.subtree("Update-DB::Options")
215 utils.warn("dak update-db takes no arguments.")
216 self.usage(exit_code=1)
219 if os.path.isdir(cnf["Dir::Lock"]):
220 lock_fd = os.open(os.path.join(cnf["Dir::Lock"], 'dinstall.lock'), os.O_RDWR | os.O_CREAT)
221 fcntl.lockf(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
223 utils.warn("Lock directory doesn't exist yet - not locking")
225 if errno.errorcode[e.errno] == 'EACCES' or errno.errorcode[e.errno] == 'EAGAIN':
226 utils.fubar("Couldn't obtain lock; assuming another 'dak process-unchecked' is already running.")
231 ################################################################################
233 if __name__ == '__main__':