Package buildslave :: Package commands :: Module fs
[frames] | no frames]

Source Code for Module buildslave.commands.fs

  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  import os 
 17  import sys 
 18  import shutil 
 19   
 20  from twisted.internet import defer 
 21  from twisted.python import runtime, log 
 22   
 23  from buildslave import runprocess 
 24  from buildslave.commands import base, utils 
25 26 -class MakeDirectory(base.Command):
27 """This is a Command which creates a directory. The args dict contains 28 the following keys: 29 30 - ['dir'] (required): subdirectory which the command will create, 31 relative to the builder dir 32 33 MakeDirectory creates the following status messages: 34 - {'rc': rc} : when the process has terminated 35 """ 36 37 header = "mkdir" 38
39 - def start(self):
40 args = self.args 41 # args['dir'] is relative to Builder directory, and is required. 42 assert args['dir'] is not None 43 dirname = os.path.join(self.builder.basedir, args['dir']) 44 45 try: 46 if not os.path.isdir(dirname): 47 os.makedirs(dirname) 48 self.sendStatus({'rc': 0}) 49 except: 50 self.sendStatus({'rc': 1})
51
52 -class RemoveDirectory(base.Command):
53 """This is a Command which removes a directory. The args dict contains 54 the following keys: 55 56 - ['dir'] (required): subdirectory which the command will create, 57 relative to the builder dir. This argument 58 can be either a single directory name as a string 59 or list of directory names as a list. 60 61 - ['timeout']: seconds of silence tolerated before we kill off the 62 command 63 64 - ['maxTime']: seconds before we kill off the command 65 66 67 RemoveDirectory creates the following status messages: 68 - {'rc': rc} : when the process has terminated 69 """ 70 71 header = "rmdir" 72
73 - def setup(self,args):
74 self.logEnviron = args.get('logEnviron',True)
75 76 77 @defer.deferredGenerator
78 - def start(self):
79 args = self.args 80 # args['dir'] is relative to Builder directory, and is required. 81 assert args['dir'] is not None 82 dirnames = args['dir'] 83 84 self.timeout = args.get('timeout', 120) 85 self.maxTime = args.get('maxTime', None) 86 self.rc = 0 87 if type(dirnames) is list: 88 assert len(dirnames) != 0 89 for dirname in dirnames: 90 wfd = defer.waitForDeferred(self.removeSingleDir(dirname)) 91 yield wfd 92 res = wfd.getResult() 93 # Even if single removal of single file/dir consider it as 94 # failure of whole command, but continue removing other files 95 # Send 'rc' to master to handle failure cases 96 if res != 0: 97 self.rc = res 98 else: 99 wfd = defer.waitForDeferred(self.removeSingleDir(dirnames)) 100 yield wfd 101 self.rc = wfd.getResult() 102 103 self.sendStatus({'rc': self.rc})
104
105 - def removeSingleDir(self, dirname):
106 # TODO: remove the old tree in the background 107 self.dir = os.path.join(self.builder.basedir, dirname) 108 if runtime.platformType != "posix": 109 # if we're running on w32, use rmtree instead. It will block, 110 # but hopefully it won't take too long. 111 utils.rmdirRecursive(self.dir) 112 d = defer.succeed(0) 113 else: 114 d = self._clobber(None) 115 116 return d
117
118 - def _clobber(self, dummy, chmodDone = False):
119 command = ["rm", "-rf", self.dir] 120 c = runprocess.RunProcess(self.builder, command, self.builder.basedir, 121 sendRC=0, timeout=self.timeout, maxTime=self.maxTime, 122 logEnviron=self.logEnviron, usePTY=False) 123 124 self.command = c 125 # sendRC=0 means the rm command will send stdout/stderr to the 126 # master, but not the rc=0 when it finishes. That job is left to 127 # _sendRC 128 d = c.start() 129 # The rm -rf may fail if there is a left-over subdir with chmod 000 130 # permissions. So if we get a failure, we attempt to chmod suitable 131 # permissions and re-try the rm -rf. 132 if not chmodDone: 133 d.addCallback(self._tryChmod) 134 return d
135
136 - def _tryChmod(self, rc):
137 assert isinstance(rc, int) 138 if rc == 0: 139 return defer.succeed(0) 140 # Attempt a recursive chmod and re-try the rm -rf after. 141 142 command = ["chmod", "-Rf", "u+rwx", os.path.join(self.builder.basedir, self.dir)] 143 if sys.platform.startswith('freebsd'): 144 # Work around a broken 'chmod -R' on FreeBSD (it tries to recurse into a 145 # directory for which it doesn't have permission, before changing that 146 # permission) by running 'find' instead 147 command = ["find", os.path.join(self.builder.basedir, self.dir), 148 '-exec', 'chmod', 'u+rwx', '{}', ';' ] 149 c = runprocess.RunProcess(self.builder, command, self.builder.basedir, 150 sendRC=0, timeout=self.timeout, maxTime=self.maxTime, 151 logEnviron=self.logEnviron, usePTY=False) 152 153 self.command = c 154 d = c.start() 155 d.addCallback(lambda dummy: self._clobber(dummy, True)) 156 return d
157
158 -class CopyDirectory(base.Command):
159 """This is a Command which copies a directory. The args dict contains 160 the following keys: 161 162 - ['fromdir'] (required): subdirectory which the command will copy, 163 relative to the builder dir 164 - ['todir'] (required): subdirectory which the command will create, 165 relative to the builder dir 166 167 - ['timeout']: seconds of silence tolerated before we kill off the 168 command 169 170 - ['maxTime']: seconds before we kill off the command 171 172 173 CopyDirectory creates the following status messages: 174 - {'rc': rc} : when the process has terminated 175 """ 176 177 header = "rmdir" 178
179 - def setup(self,args):
180 self.logEnviron = args.get('logEnviron',True)
181
182 - def start(self):
183 args = self.args 184 # args['todir'] is relative to Builder directory, and is required. 185 # args['fromdir'] is relative to Builder directory, and is required. 186 assert args['todir'] is not None 187 assert args['fromdir'] is not None 188 189 fromdir = os.path.join(self.builder.basedir, args['fromdir']) 190 todir = os.path.join(self.builder.basedir, args['todir']) 191 192 self.timeout = args.get('timeout', 120) 193 self.maxTime = args.get('maxTime', None) 194 195 if runtime.platformType != "posix": 196 self.sendStatus({'header': "Since we're on a non-POSIX platform, " 197 "we're not going to try to execute cp in a subprocess, but instead " 198 "use shutil.copytree(), which will block until it is complete. " 199 "fromdir: %s, todir: %s\n" % (fromdir, todir)}) 200 shutil.copytree(fromdir, todir) 201 d = defer.succeed(0) 202 else: 203 if not os.path.exists(os.path.dirname(todir)): 204 os.makedirs(os.path.dirname(todir)) 205 if os.path.exists(todir): 206 # I don't think this happens, but just in case.. 207 log.msg("cp target '%s' already exists -- cp will not do what you think!" % todir) 208 209 command = ['cp', '-R', '-P', '-p', fromdir, todir] 210 c = runprocess.RunProcess(self.builder, command, self.builder.basedir, 211 sendRC=False, timeout=self.timeout, maxTime=self.maxTime, 212 logEnviron=self.logEnviron, usePTY=False) 213 self.command = c 214 d = c.start() 215 d.addCallback(self._abandonOnFailure) 216 217 # always set the RC, regardless of platform 218 d.addCallbacks(self._sendRC, self._checkAbandoned) 219 return d
220
221 -class StatFile(base.Command):
222 """This is a command which stats a file on the slave. The args dict contains the following keys: 223 224 - ['file'] (required): file to stat 225 226 StatFile creates the following status messages: 227 - {'rc': rc} : 0 if the file is found, 1 otherwise 228 - {'stat': stat} : if the files is found, stat contains the result of os.stat 229 """ 230 231 header = "stat" 232
233 - def start(self):
234 args = self.args 235 # args['dir'] is relative to Builder directory, and is required. 236 assert args['file'] is not None 237 filename = os.path.join(self.builder.basedir, args['file']) 238 239 try: 240 stat = os.stat(filename) 241 self.sendStatus({'stat': tuple(stat)}) 242 self.sendStatus({'rc': 0}) 243 except: 244 self.sendStatus({'rc': 1})
245