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 threads, 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 28 header = "mkdir" 29
30 - def start(self):
31 args = self.args 32 # args['dir'] is relative to Builder directory, and is required. 33 assert args['dir'] is not None 34 dirname = os.path.join(self.builder.basedir, args['dir']) 35 36 try: 37 if not os.path.isdir(dirname): 38 os.makedirs(dirname) 39 self.sendStatus({'rc': 0}) 40 except: 41 self.sendStatus({'rc': 1})
42
43 -class RemoveDirectory(base.Command):
44 45 header = "rmdir" 46
47 - def setup(self,args):
48 self.logEnviron = args.get('logEnviron',True)
49 50 51 @defer.deferredGenerator
52 - def start(self):
53 args = self.args 54 # args['dir'] is relative to Builder directory, and is required. 55 assert args['dir'] is not None 56 dirnames = args['dir'] 57 58 self.timeout = args.get('timeout', 120) 59 self.maxTime = args.get('maxTime', None) 60 self.rc = 0 61 if type(dirnames) is list: 62 assert len(dirnames) != 0 63 for dirname in dirnames: 64 wfd = defer.waitForDeferred(self.removeSingleDir(dirname)) 65 yield wfd 66 res = wfd.getResult() 67 # Even if single removal of single file/dir consider it as 68 # failure of whole command, but continue removing other files 69 # Send 'rc' to master to handle failure cases 70 if res != 0: 71 self.rc = res 72 else: 73 wfd = defer.waitForDeferred(self.removeSingleDir(dirnames)) 74 yield wfd 75 self.rc = wfd.getResult() 76 77 self.sendStatus({'rc': self.rc})
78
79 - def removeSingleDir(self, dirname):
80 self.dir = os.path.join(self.builder.basedir, dirname) 81 if runtime.platformType != "posix": 82 d = threads.deferToThread(utils.rmdirRecursive, self.dir) 83 def cb(_): 84 return 0 # rc=0
85 def eb(f): 86 self.sendStatus({'header' : 'exception from rmdirRecursive\n' + f.getTraceback()}) 87 return -1 # rc=-1
88 d.addCallbacks(cb, eb) 89 else: 90 d = self._clobber(None) 91 92 return d 93
94 - def _clobber(self, dummy, chmodDone = False):
95 command = ["rm", "-rf", self.dir] 96 c = runprocess.RunProcess(self.builder, command, self.builder.basedir, 97 sendRC=0, timeout=self.timeout, maxTime=self.maxTime, 98 logEnviron=self.logEnviron, usePTY=False) 99 100 self.command = c 101 # sendRC=0 means the rm command will send stdout/stderr to the 102 # master, but not the rc=0 when it finishes. That job is left to 103 # _sendRC 104 d = c.start() 105 # The rm -rf may fail if there is a left-over subdir with chmod 000 106 # permissions. So if we get a failure, we attempt to chmod suitable 107 # permissions and re-try the rm -rf. 108 if not chmodDone: 109 d.addCallback(self._tryChmod) 110 return d
111
112 - def _tryChmod(self, rc):
113 assert isinstance(rc, int) 114 if rc == 0: 115 return defer.succeed(0) 116 # Attempt a recursive chmod and re-try the rm -rf after. 117 118 command = ["chmod", "-Rf", "u+rwx", os.path.join(self.builder.basedir, self.dir)] 119 if sys.platform.startswith('freebsd'): 120 # Work around a broken 'chmod -R' on FreeBSD (it tries to recurse into a 121 # directory for which it doesn't have permission, before changing that 122 # permission) by running 'find' instead 123 command = ["find", os.path.join(self.builder.basedir, self.dir), 124 '-exec', 'chmod', 'u+rwx', '{}', ';' ] 125 c = runprocess.RunProcess(self.builder, command, self.builder.basedir, 126 sendRC=0, timeout=self.timeout, maxTime=self.maxTime, 127 logEnviron=self.logEnviron, usePTY=False) 128 129 self.command = c 130 d = c.start() 131 d.addCallback(lambda dummy: self._clobber(dummy, True)) 132 return d
133
134 -class CopyDirectory(base.Command):
135 136 header = "cpdir" 137
138 - def setup(self,args):
139 self.logEnviron = args.get('logEnviron',True)
140
141 - def start(self):
142 args = self.args 143 # args['todir'] is relative to Builder directory, and is required. 144 # args['fromdir'] is relative to Builder directory, and is required. 145 assert args['todir'] is not None 146 assert args['fromdir'] is not None 147 148 fromdir = os.path.join(self.builder.basedir, args['fromdir']) 149 todir = os.path.join(self.builder.basedir, args['todir']) 150 151 self.timeout = args.get('timeout', 120) 152 self.maxTime = args.get('maxTime', None) 153 154 if runtime.platformType != "posix": 155 d = threads.deferToThread(shutil.copytree, fromdir, todir) 156 def cb(_): 157 return 0 # rc=0
158 def eb(f): 159 self.sendStatus({'header' : 'exception from copytree\n' + f.getTraceback()}) 160 return -1 # rc=-1
161 d.addCallbacks(cb, eb) 162 @d.addCallback 163 def send_rc(rc): 164 self.sendStatus({'rc' : rc}) 165 else: 166 if not os.path.exists(os.path.dirname(todir)): 167 os.makedirs(os.path.dirname(todir)) 168 if os.path.exists(todir): 169 # I don't think this happens, but just in case.. 170 log.msg("cp target '%s' already exists -- cp will not do what you think!" % todir) 171 172 command = ['cp', '-R', '-P', '-p', fromdir, todir] 173 c = runprocess.RunProcess(self.builder, command, self.builder.basedir, 174 sendRC=False, timeout=self.timeout, maxTime=self.maxTime, 175 logEnviron=self.logEnviron, usePTY=False) 176 self.command = c 177 d = c.start() 178 d.addCallback(self._abandonOnFailure) 179 180 d.addCallbacks(self._sendRC, self._checkAbandoned) 181 return d 182
183 -class StatFile(base.Command):
184 185 header = "stat" 186
187 - def start(self):
188 args = self.args 189 # args['dir'] is relative to Builder directory, and is required. 190 assert args['file'] is not None 191 filename = os.path.join(self.builder.basedir, args['file']) 192 193 try: 194 stat = os.stat(filename) 195 self.sendStatus({'stat': tuple(stat)}) 196 self.sendStatus({'rc': 0}) 197 except: 198 self.sendStatus({'rc': 1})
199