1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16  import time 
 17  from xml.dom import minidom 
 18   
 19  from twisted.python import log 
 20  from twisted.internet import defer 
 21  from twisted.web import client 
 22   
 23  from buildbot.changes import base 
 24  from buildbot.util import epoch2datetime 
 27 -    def __init__(self, value="InvalidResultError"): 
  30          return repr(self.value) 
   31   
 34   
 37   
 40   
 42      """I hold a list of CiNodes""" 
 45   
 47          if len(self.nodes) != len(other.nodes): 
 48              return False 
 49          for i in range(len(self.nodes)): 
 50              if self.nodes[i].log != other.nodes[i].log \ 
 51                or self.nodes[i].who != other.nodes[i].who \ 
 52                or self.nodes[i].date != other.nodes[i].date \ 
 53                or len(self.nodes[i].files) != len(other.nodes[i].files): 
 54                  return -1 
 55   
 56                  for j in range(len(self.nodes[i].files)): 
 57                      if self.nodes[i].files[j].revision \ 
 58                        != other.nodes[i].files[j].revision \ 
 59                        or self.nodes[i].files[j].filename \ 
 60                        != other.nodes[i].files[j].filename: 
 61                          return -1 
 62   
 63          return 0 
   64   
 66      """I hold information baout one <ci> node, including a list of files""" 
 67 -    def __init__(self, log="", who="", date=0, files=[]): 
  68          self.log = log 
 69          self.who = who 
 70          self.date = date 
 71          self.files = files 
   72   
 74      """I hold information about one <f> node""" 
 75 -    def __init__(self, revision="", filename=""): 
   78   
 80      """I parse the XML result from a bonsai cvsquery.""" 
 81   
 83          try: 
 84           
 85           
 86           
 87           
 88              data = data.decode("latin1") 
 89              data = data.encode("ascii", "replace") 
 90              self.dom = minidom.parseString(data) 
 91              log.msg(data) 
 92          except: 
 93              raise InvalidResultError("Malformed XML in result") 
 94   
 95          self.ciNodes = self.dom.getElementsByTagName("ci") 
 96          self.currentCiNode = None  
 97          self.fileNodes = None  
 98          self.currentFileNode = None  
 99          self.bonsaiResult = self._parseData() 
 100   
102          return self.bonsaiResult 
 103   
105          """Returns data from a Bonsai cvsquery in a BonsaiResult object""" 
106          nodes = [] 
107          try: 
108              while self._nextCiNode(): 
109                  files = [] 
110                  try: 
111                      while self._nextFileNode(): 
112                          files.append(FileNode(self._getRevision(), 
113                                                self._getFilename())) 
114                  except NoMoreFileNodes: 
115                      pass 
116                  except InvalidResultError: 
117                      raise 
118                  cinode = CiNode(self._getLog(), self._getWho(), 
119                                  self._getDate(), files) 
120                   
121                  if not cinode.log and nodes and \ 
122                          not nodes[-1].log and \ 
123                          cinode.who == nodes[-1].who and \ 
124                          cinode.date == nodes[-1].date: 
125                      nodes[-1].files += cinode.files 
126                  else: 
127                      nodes.append(cinode) 
128   
129          except NoMoreCiNodes: 
130              pass 
131          except (InvalidResultError, EmptyResult): 
132              raise 
133   
134          return BonsaiResult(nodes) 
 135   
136   
138          """Iterates to the next <ci> node and fills self.fileNodes with 
139             child <f> nodes""" 
140          try: 
141              self.currentCiNode = self.ciNodes.pop(0) 
142              if len(self.currentCiNode.getElementsByTagName("files")) > 1: 
143                  raise InvalidResultError("Multiple <files> for one <ci>") 
144   
145              self.fileNodes = self.currentCiNode.getElementsByTagName("f") 
146          except IndexError: 
147               
148              if not self.currentCiNode: 
149                  raise EmptyResult 
150              else: 
151                  raise NoMoreCiNodes 
152   
153          return True 
 154   
156          """Iterates to the next <f> node""" 
157          try: 
158              self.currentFileNode = self.fileNodes.pop(0) 
159          except IndexError: 
160              raise NoMoreFileNodes 
161   
162          return True 
 163   
165          """Returns the log of the current <ci> node""" 
166          logs = self.currentCiNode.getElementsByTagName("log") 
167          if len(logs) < 1: 
168              raise InvalidResultError("No log present") 
169          elif len(logs) > 1: 
170              raise InvalidResultError("Multiple logs present") 
171   
172           
173          if logs[0].firstChild: 
174              return logs[0].firstChild.data 
175          return '' 
 176   
178          """Returns the e-mail address of the commiter""" 
179           
180          return str(self.currentCiNode.getAttribute("who")) 
 181   
183          """Returns the date (unix time) of the commit""" 
184           
185          try: 
186              commitDate = int(self.currentCiNode.getAttribute("date")) 
187          except ValueError: 
188              raise InvalidResultError 
189   
190          return commitDate 
 191   
193          """Returns the filename of the current <f> node""" 
194          try: 
195              filename = self.currentFileNode.firstChild.data 
196          except AttributeError: 
197              raise InvalidResultError("Missing filename") 
198   
199          return filename 
 200   
202          return self.currentFileNode.getAttribute("rev") 
  203   
206      compare_attrs = ["bonsaiURL", "pollInterval", "tree", 
207                       "module", "branch", "cvsroot"] 
208   
209 -    def __init__(self, bonsaiURL, module, branch, tree="default", 
210                   cvsroot="/cvsroot", pollInterval=30, project=''): 
 211          self.bonsaiURL = bonsaiURL 
212          self.module = module 
213          self.branch = branch 
214          self.tree = tree 
215          self.cvsroot = cvsroot 
216          self.repository = module != 'all' and module or '' 
217          self.pollInterval = pollInterval 
218          self.lastChange = time.time() 
219          self.lastPoll = time.time() 
 220   
222          str = "" 
223          str += "Getting changes from the Bonsai service running at %s " \ 
224                  % self.bonsaiURL 
225          str += "<br>Using tree: %s, branch: %s, and module: %s" % (self.tree, \ 
226                  self.branch, self.module) 
227          return str 
 228   
230          d = self._get_changes() 
231          d.addCallback(self._process_changes) 
232          return d 
 233   
235          args = ["treeid=%s" % self.tree, "module=%s" % self.module, 
236                  "branch=%s" % self.branch, "branchtype=match", 
237                  "sortby=Date", "date=explicit", 
238                  "mindate=%d" % self.lastChange, 
239                  "maxdate=%d" % int(time.time()), 
240                  "cvsroot=%s" % self.cvsroot, "xml=1"] 
241           
242          url = self.bonsaiURL 
243          url += "/cvsquery.cgi?" 
244          url += "&".join(args) 
245   
246          return url 
 247   
249          url = self._make_url() 
250          log.msg("Polling Bonsai tree at %s" % url) 
251   
252          self.lastPoll = time.time() 
253           
254          return client.getPage(url, timeout=self.pollInterval) 
 255   
256      @defer.deferredGenerator 
258          try: 
259              bp = BonsaiParser(query) 
260              result = bp.getData() 
261          except InvalidResultError, e: 
262              log.msg("Could not process Bonsai query: " + e.value) 
263              return 
264          except EmptyResult: 
265              return 
266   
267          for cinode in result.nodes: 
268              files = [file.filename + ' (revision '+file.revision+')' 
269                       for file in cinode.files] 
270              self.lastChange = self.lastPoll 
271              w = defer.waitForDeferred( 
272                      self.master.addChange(author = cinode.who, 
273                                 files = files, 
274                                 comments = cinode.log, 
275                                 when_timestamp = epoch2datetime(cinode.date), 
276                                 branch = self.branch)) 
277              yield w 
278              w.getResult() 
  279