1#! /usr/bin/env python3
3""" Manage build queues
5@contact: Debian FTPMaster <ftpmaster@debian.org>
6@copyright: 2000, 2001, 2002, 2006 James Troup <james@nocrew.org>
7@copyright: 2009 Mark Hymers <mhy@debian.org>
8@copyright: 2012, Ansgar Burchardt <ansgar@debian.org>
10"""
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation; either version 2 of the License, or
15# (at your option) any later version.
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
22# You should have received a copy of the GNU General Public License
23# along with this program; if not, write to the Free Software
24# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26################################################################################
28import apt_pkg
29from datetime import datetime, timedelta
30import sys
31import sqlalchemy.sql as sql
33from daklib import daklog
34from daklib.archive import ArchiveTransaction
35from daklib.dbconn import *
36from daklib.config import Config
38################################################################################
40Options = None
41Logger = None
43################################################################################
46def usage(exit_code=0):
47 print("""Usage: dak manage-build-queues [OPTIONS] buildqueue1 buildqueue2
48Manage the contents of one or more build queues
50 -a, --all run on all known build queues
51 -n, --no-action don't do anything
52 -h, --help show this help and exit""")
54 sys.exit(exit_code)
56################################################################################
59def clean(build_queue, transaction, now=None):
60 session = transaction.session
61 if now is None: 61 ↛ 62line 61 didn't jump to line 62, because the condition on line 61 was never true
62 now = datetime.now()
64 delete_before = now - timedelta(seconds=build_queue.stay_of_execution)
65 suite = build_queue.suite
66 suite_was_changed = False
68 # Remove binaries subject to the following conditions:
69 # 1. Keep binaries that are in policy queues.
70 # 2. Remove binaries that are not in suites.
71 # 3. Remove binaries that have been in the build queue for some time.
72 query = sql.text("""
73 SELECT b.*
74 FROM binaries b
75 JOIN bin_associations ba ON b.id = ba.bin
76 WHERE ba.suite = :suite_id
77 AND NOT EXISTS
78 (SELECT 1 FROM policy_queue_upload_binaries_map pqubm
79 JOIN policy_queue_upload pqu ON pqu.id = pqubm.policy_queue_upload_id
80 JOIN policy_queue pq ON pq.id = pqu.policy_queue_id
81 JOIN suite s ON s.policy_queue_id = pq.id
82 JOIN suite_build_queue_copy sbqc ON sbqc.suite = s.id
83 WHERE pqubm.binary_id = ba.bin AND pq.send_to_build_queues
84 AND sbqc.build_queue_id = :build_queue_id)
85 AND (ba.created < :delete_before
86 OR NOT EXISTS
87 (SELECT 1 FROM bin_associations ba2
88 JOIN suite_build_queue_copy sbqc ON sbqc.suite = ba2.suite
89 WHERE ba2.bin = ba.bin AND sbqc.build_queue_id = :build_queue_id))""")
90 binaries = session.query(DBBinary).from_statement(query) \
91 .params({'build_queue_id': build_queue.queue_id, 'suite_id': suite.suite_id, 'delete_before': delete_before})
92 for binary in binaries:
93 Logger.log(["removed binary from build queue", build_queue.queue_name, binary.package, binary.version])
94 transaction.remove_binary(binary, suite)
95 suite_was_changed = True
97 # Remove sources
98 # Conditions are similar as for binaries, but we also keep sources
99 # if there is a binary in the build queue that uses it.
100 query = sql.text("""
101 SELECT s.*
102 FROM source s
103 JOIN src_associations sa ON s.id = sa.source
104 WHERE sa.suite = :suite_id
105 AND NOT EXISTS
106 (SELECT 1 FROM policy_queue_upload pqu
107 JOIN policy_queue pq ON pq.id = pqu.policy_queue_id
108 JOIN suite s ON s.policy_queue_id = pq.id
109 JOIN suite_build_queue_copy sbqc ON sbqc.suite = s.id
110 WHERE pqu.source_id = sa.source AND pq.send_to_build_queues
111 AND sbqc.build_queue_id = :build_queue_id)
112 AND (sa.created < :delete_before
113 OR NOT EXISTS
114 (SELECT 1 FROM src_associations sa2
115 JOIN suite_build_queue_copy sbqc ON sbqc.suite = sa2.suite
116 WHERE sbqc.build_queue_id = :build_queue_id
117 AND sa2.source = sa.source))
118 AND NOT EXISTS
119 (SELECT 1 FROM bin_associations ba
120 JOIN binaries b ON ba.bin = b.id
121 WHERE ba.suite = :suite_id
122 AND b.source = s.id)""")
123 sources = session.query(DBSource).from_statement(query) \
124 .params({'build_queue_id': build_queue.queue_id, 'suite_id': suite.suite_id, 'delete_before': delete_before})
125 for source in sources:
126 Logger.log(["removed source from build queue", build_queue.queue_name, source.source, source.version])
127 transaction.remove_source(source, suite)
128 suite_was_changed = True
130 if suite_was_changed: 130 ↛ exitline 130 didn't return from function 'clean', because the condition on line 130 was never false
131 suite.update_last_changed()
134def main():
135 global Options, Logger
137 cnf = Config()
139 for i in ["Help", "No-Action", "All"]:
140 key = "Manage-Build-Queues::Options::%s" % i
141 if key not in cnf: 141 ↛ 139line 141 didn't jump to line 139, because the condition on line 141 was never false
142 cnf[key] = ""
144 Arguments = [('h', "help", "Manage-Build-Queues::Options::Help"),
145 ('n', "no-action", "Manage-Build-Queues::Options::No-Action"),
146 ('a', "all", "Manage-Build-Queues::Options::All")]
148 queue_names = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv)
149 Options = cnf.subtree("Manage-Build-Queues::Options")
151 if Options["Help"]:
152 usage()
154 Logger = daklog.Logger('manage-build-queues', Options['No-Action'])
156 starttime = datetime.now()
158 with ArchiveTransaction() as transaction:
159 session = transaction.session
160 if Options['All']: 160 ↛ 161line 160 didn't jump to line 161, because the condition on line 160 was never true
161 if len(queue_names) != 0:
162 print("E: Cannot use both -a and a queue name")
163 sys.exit(1)
164 queues = session.query(BuildQueue)
165 else:
166 queues = session.query(BuildQueue).filter(BuildQueue.queue_name.in_(queue_names))
168 for q in queues:
169 Logger.log(['cleaning queue %s using datetime %s' % (q.queue_name, starttime)])
170 clean(q, transaction, now=starttime)
171 if not Options['No-Action']: 171 ↛ 174line 171 didn't jump to line 174, because the condition on line 171 was never false
172 transaction.commit()
173 else:
174 transaction.rollback()
176 Logger.close()
178#######################################################################################
181if __name__ == '__main__':
182 main()