1# This file is part of Buildbot. Buildbot is free software: you can 2# redistribute it and/or modify it under the terms of the GNU General Public 3# License as published by the Free Software Foundation, version 2. 4# 5# This program is distributed in the hope that it will be useful, but WITHOUT 6# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 7# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 8# details. 9# 10# You should have received a copy of the GNU General Public License along with 11# this program; if not, write to the Free Software Foundation, Inc., 51 12# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 13# 14# Copyright Buildbot Team Members 15 16 17from twisted.internet import defer 18 19from buildbot.data import base 20from buildbot.data import types 21from buildbot.util import identifiers 22 23 24class EndpointMixin: 25 26 def db2data(self, dbdict): 27 data = { 28 'logid': dbdict['id'], 29 'name': dbdict['name'], 30 'slug': dbdict['slug'], 31 'stepid': dbdict['stepid'], 32 'complete': dbdict['complete'], 33 'num_lines': dbdict['num_lines'], 34 'type': dbdict['type'], 35 } 36 return defer.succeed(data) 37 38 39class LogEndpoint(EndpointMixin, base.BuildNestingMixin, base.Endpoint): 40 41 isCollection = False 42 pathPatterns = """ 43 /logs/n:logid 44 /steps/n:stepid/logs/i:log_slug 45 /builds/n:buildid/steps/i:step_name/logs/i:log_slug 46 /builds/n:buildid/steps/n:step_number/logs/i:log_slug 47 /builders/n:builderid/builds/n:build_number/steps/i:step_name/logs/i:log_slug 48 /builders/n:builderid/builds/n:build_number/steps/n:step_number/logs/i:log_slug 49 /builders/i:buildername/builds/n:build_number/steps/i:step_name/logs/i:log_slug 50 /builders/i:buildername/builds/n:build_number/steps/n:step_number/logs/i:log_slug 51 """ 52 53 @defer.inlineCallbacks 54 def get(self, resultSpec, kwargs): 55 if 'logid' in kwargs: 56 dbdict = yield self.master.db.logs.getLog(kwargs['logid']) 57 return (yield self.db2data(dbdict)) if dbdict else None 58 59 stepid = yield self.getStepid(kwargs) 60 if stepid is None: 61 return None 62 63 dbdict = yield self.master.db.logs.getLogBySlug(stepid, 64 kwargs.get('log_slug')) 65 return (yield self.db2data(dbdict)) if dbdict else None 66 67 68class LogsEndpoint(EndpointMixin, base.BuildNestingMixin, base.Endpoint): 69 70 isCollection = True 71 pathPatterns = """ 72 /steps/n:stepid/logs 73 /builds/n:buildid/steps/i:step_name/logs 74 /builds/n:buildid/steps/n:step_number/logs 75 /builders/n:builderid/builds/n:build_number/steps/i:step_name/logs 76 /builders/n:builderid/builds/n:build_number/steps/n:step_number/logs 77 /builders/i:buildername/builds/n:build_number/steps/i:step_name/logs 78 /builders/i:buildername/builds/n:build_number/steps/n:step_number/logs 79 """ 80 81 @defer.inlineCallbacks 82 def get(self, resultSpec, kwargs): 83 stepid = yield self.getStepid(kwargs) 84 if not stepid: 85 return [] 86 logs = yield self.master.db.logs.getLogs(stepid=stepid) 87 results = [] 88 for dbdict in logs: 89 results.append((yield self.db2data(dbdict))) 90 return results 91 92 93class Log(base.ResourceType): 94 95 name = "log" 96 plural = "logs" 97 endpoints = [LogEndpoint, LogsEndpoint] 98 keyField = "logid" 99 eventPathPatterns = """ 100 /logs/:logid 101 /steps/:stepid/logs/:slug 102 """ 103 subresources = ["LogChunk"] 104 105 class EntityType(types.Entity): 106 logid = types.Integer() 107 name = types.String() 108 slug = types.Identifier(50) 109 stepid = types.Integer() 110 complete = types.Boolean() 111 num_lines = types.Integer() 112 type = types.Identifier(1) 113 114 entityType = EntityType(name, 'Log') 115 116 @defer.inlineCallbacks 117 def generateEvent(self, _id, event): 118 # get the build and munge the result for the notification 119 build = yield self.master.data.get(('logs', str(_id))) 120 self.produceEvent(build, event) 121 122 @base.updateMethod 123 @defer.inlineCallbacks 124 def addLog(self, stepid, name, type): 125 slug = identifiers.forceIdentifier(50, name) 126 while True: 127 try: 128 logid = yield self.master.db.logs.addLog( 129 stepid=stepid, name=name, slug=slug, type=type) 130 except KeyError: 131 slug = identifiers.incrementIdentifier(50, slug) 132 continue 133 self.generateEvent(logid, "new") 134 return logid 135 136 @base.updateMethod 137 @defer.inlineCallbacks 138 def appendLog(self, logid, content): 139 res = yield self.master.db.logs.appendLog(logid=logid, content=content) 140 self.generateEvent(logid, "append") 141 return res 142 143 @base.updateMethod 144 @defer.inlineCallbacks 145 def finishLog(self, logid): 146 res = yield self.master.db.logs.finishLog(logid=logid) 147 self.generateEvent(logid, "finished") 148 return res 149 150 @base.updateMethod 151 def compressLog(self, logid): 152 return self.master.db.logs.compressLog(logid=logid) 153