1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16  """ 
 17  Support for schedulers in the database 
 18  """ 
 19   
 20  from buildbot.util import json 
 21  import sqlalchemy as sa 
 22  import sqlalchemy.exc 
 23  from twisted.python import log 
 24  from buildbot.db import base 
 25   
 27      """ 
 28      A DBConnectorComponent to handle maintaining schedulers' state in the db. 
 29      """ 
 30   
 32          """Get this scheduler's state, as a dictionary.  Returs a Deferred""" 
 33          def thd(conn): 
 34              schedulers_tbl = self.db.model.schedulers 
 35              q = sa.select([ schedulers_tbl.c.state ], 
 36                      whereclause=(schedulers_tbl.c.schedulerid == schedulerid)) 
 37              row = conn.execute(q).fetchone() 
 38              if not row: 
 39                  return {}  
 40              try: 
 41                  return json.loads(row.state) 
 42              except: 
 43                  log.msg("JSON error loading state for scheduler #%s" % (schedulerid,)) 
 44                  return {} 
  45          return self.db.pool.do(thd) 
  46   
 48          """Set this scheduler's stored state, represented as a JSON-able 
 49          dictionary.  Returs a Deferred.  Note that this will overwrite any 
 50          existing state; be careful with updates!""" 
 51          def thd(conn): 
 52              schedulers_tbl = self.db.model.schedulers 
 53              q = schedulers_tbl.update( 
 54                      whereclause=(schedulers_tbl.c.schedulerid == schedulerid)) 
 55              conn.execute(q, state=json.dumps(state)) 
  56          return self.db.pool.do(thd) 
 57   
 58       
 60          """Record a collection of classifications in the scheduler_changes 
 61          table. CLASSIFICATIONS is a dictionary mapping CHANGEID to IMPORTANT 
 62          (boolean).  Returns a Deferred.""" 
 63          def thd(conn): 
 64              tbl = self.db.model.scheduler_changes 
 65              ins_q = tbl.insert() 
 66              upd_q = tbl.update( 
 67                      ((tbl.c.schedulerid == schedulerid) 
 68                      & (tbl.c.changeid == sa.bindparam('wc_changeid')))) 
 69              for changeid, important in classifications.items(): 
 70                   
 71                   
 72                  imp_int = important and 1 or 0 
 73                  try: 
 74                      conn.execute(ins_q, 
 75                              schedulerid=schedulerid, 
 76                              changeid=changeid, 
 77                              important=imp_int) 
 78                  except (sqlalchemy.exc.ProgrammingError, 
 79                          sqlalchemy.exc.IntegrityError): 
 80                       
 81                      conn.execute(upd_q, 
 82                              wc_changeid=changeid, 
 83                              important=imp_int) 
  84   
 85          return self.db.pool.do(thd) 
 86   
 88          """ 
 89          Flush all scheduler_changes for L{schedulerid}, limiting to those less 
 90          than C{less_than} if the parameter is supplied.  Returns a Deferred. 
 91          """ 
 92          def thd(conn): 
 93              scheduler_changes_tbl = self.db.model.scheduler_changes 
 94              wc = (scheduler_changes_tbl.c.schedulerid == schedulerid) 
 95              if less_than is not None: 
 96                  wc = wc & (scheduler_changes_tbl.c.changeid < less_than) 
 97              q = scheduler_changes_tbl.delete(whereclause=wc) 
 98              conn.execute(q) 
  99          return self.db.pool.do(thd) 
100   
103          """ 
104          Return the scheduler_changes rows for this scheduler, in the form of a 
105          dictionary mapping changeid to a boolean (important).  Returns a 
106          Deferred. 
107   
108          @param schedulerid: scheduler to look up changes for 
109          @type schedulerid: integer 
110   
111          @param branch: limit to changes with this branch 
112          @type branch: string or None (for default branch) 
113   
114          @returns: dictionary via Deferred 
115          """ 
116          def thd(conn): 
117              scheduler_changes_tbl = self.db.model.scheduler_changes 
118              changes_tbl = self.db.model.changes 
119   
120              wc = (scheduler_changes_tbl.c.schedulerid == schedulerid) 
121              if branch is not self.Thunk: 
122                  wc = wc & ( 
123                      (scheduler_changes_tbl.c.changeid == changes_tbl.c.changeid) & 
124                      (changes_tbl.c.branch == branch)) 
125              q = sa.select( 
126                  [ scheduler_changes_tbl.c.changeid, scheduler_changes_tbl.c.important ], 
127                  whereclause=wc) 
128              return dict([ (r.changeid, [False,True][r.important]) for r in conn.execute(q) ]) 
 129          return self.db.pool.do(thd) 
130   
132          """ 
133          Get the schedulerid for the given scheduler, creating a new schedulerid 
134          if none is found. 
135   
136          Note that this makes no attempt to "claim" the schedulerid: schedulers 
137          with the same name and class, but running in different masters, will be 
138          assigned the same schedulerid - with disastrous results. 
139   
140          @param sched_name: the scheduler's configured name 
141          @param sched_class: the class name of this scheduler 
142          @returns: schedulerid, via a Deferred 
143          """ 
144          def thd(conn): 
145               
146              schedulers_tbl = self.db.model.schedulers 
147              q = schedulers_tbl.select( 
148                      whereclause=( 
149                          (schedulers_tbl.c.name == sched_name) & 
150                          ((schedulers_tbl.c.class_name == sched_class) | 
151                           (schedulers_tbl.c.class_name == '')))) 
152              res = conn.execute(q) 
153              row = res.fetchone() 
154              res.close() 
155   
156               
157               
158               
159               
160              if not row: 
161                  q = schedulers_tbl.insert() 
162                  res = conn.execute(q, 
163                          name=sched_name, 
164                          class_name=sched_class, 
165                          state='{}') 
166                  return res.inserted_primary_key[0] 
167   
168               
169              if row.class_name == '': 
170                  q = schedulers_tbl.update( 
171                      whereclause=( 
172                          (schedulers_tbl.c.name == sched_name) & 
173                          (schedulers_tbl.c.class_name == ''))) 
174                  conn.execute(q, class_name=sched_class) 
175              return row.schedulerid 
 176          return self.db.pool.do(thd) 
177