def touch(remote_path, mtime=None, atime=None): """Update mtime and atime of a path. Similar to running ``touch remote_path``. :param remote_path: Remote path whose times will get updated. :param mtime: New mtime. If ``None``, uses the current time. :param atime: New atime. Only used if ``mtime`` is not None. Defaults to ``mtime``. :return: Since it always updates the current time, calling this function will always result in a modification. """ # ensure the file exists if not remote.lstat(remote_path): with remote.file(remote_path, 'w') as out: out.write('') if mtime is None: remote.utime(remote_path, None) return Changed(msg=u'Touched {} to current time'.format(remote_path)) else: atime = atime if atime is not None else mtime remote.utime(remote_path, (atime, mtime)) return Changed(msg=u'Touched {} to mtime={}, atime={}'.format( remote_path, mtime, atime))
def info_groups(): groups = OrderedDict() for line in remote.file('/etc/group', 'r'): g = GroupEntry(*line.split(':')) user_list = [u for u in g[3].split(',') if u] groups[g.name] = GroupEntry(g[0], g[1], int(g[2]), user_list) return groups
def info_users(): users = OrderedDict() for line in remote.file('/etc/passwd', 'r'): u = PasswdEntry(*line.split(':')) users[u.name] = PasswdEntry(u[0], u[1], int(u[2]), int(u[3]), u[4], u[5], u[6]) return users
def verify_buffer(self, st, buf, remote_path): with remote.file(remote_path, 'rb') as rf: # enable prefetching if files support it # otherwise, performance is horrible (like disabled pipelining) if hasattr(rf, 'prefetch'): rf.prefetch() rbuf = rf.read() return rbuf == buf
def disable_raspi_config(): # FIXME: use fs.edit here c = False # we need to remove it from profile.d c |= fs.remove_file('/etc/profile.d/raspi-config.sh').changed # FIXME: this should become part of an edit module? lines = [] inittab_changed = False for line in remote.file('/etc/inittab', 'r'): if line.startswith('#') and 'RPICFG_TO_ENABLE' in line: inittab_changed = True lines.append(line[1:line.rfind('#')].strip() + '\n') continue if 'RPICFG_TO_DISABLE' in line: inittab_changed = True continue lines.append(line) if inittab_changed: # FIXME: DO THIS ATOMICALLY? Use UPLOAD? with remote.file('/etc/inittab', 'w') as out: out.write(''.join(lines)) c = True # now just stop running raspi-config _, _, status = proc.run(['killall', 'raspi-config'], status_ok=(0, 1)) # killall will return exit status 1 if not process was found if status != 1: c = True if c: return Changed(msg='Disabled raspi-config') else: return Unchanged(msg='raspi-config already stopped and disabled')
def info_modules(): mods = {} with remote.file("/proc/modules") as pm: for line in pm: name, size, loaded, dependencies, state, offset = line.split() deps = dependencies.strip(",").split(",") if dependencies != "-" else [] mods[name] = { "name": name, "size": int(size), "loaded": int(loaded), "dependencies": deps, "state": state, "offset": int(offset, 16), } return mods
def info_modules(): mods = {} with remote.file('/proc/modules') as pm: for line in pm: name, size, loaded, dependencies, state, offset = line.split() deps = dependencies.strip(',').split( ',') if dependencies != '-' else [] mods[name] = { 'name': name, 'size': int(size), 'loaded': int(loaded), 'dependencies': deps, 'state': state, 'offset': int(offset, 16), } return mods
def verify_file(self, st, local_path, remote_path): with remote.file(remote_path, 'rb') as rf,\ file(local_path, 'rb') as lf: # enable prefetching if files support it # otherwise, performance is horrible (like disabled pipelining) if hasattr(rf, 'prefetch'): rf.prefetch() bufsize = int(config['buffer_size']) while True: rbuf = rf.read(bufsize) lbuf = lf.read(bufsize) if rbuf != lbuf: return False # if both files end at the same time, we're good if rbuf == lbuf == '': return True
def edit(remote_path, create=True): with volatile.file() as tmp: created = False if create and not remote.lstat(remote_path): tmp.write('') created = True else: tmp.write(remote.file(remote_path, 'rb').read()) tmp.close() try: ef = EditableFile(tmp.name) yield ef except Exception: raise else: if created or ef.modified: upload_file(ef.name, remote_path).changed ef.changed = True else: ef.changed = False
def upload_file(self, local_path, remote_path): with file(local_path, 'rb') as src,\ remote.file(remote_path, 'wb') as dst: copyfileobj(src, dst)
def upload_buffer(self, buf, remote_path): with remote.file(remote_path, 'wb') as dst: dst.write(buf)
def run(): log.warning('Running testing module. Do not run this on a real machine!') log.debug('Testing popen') proc = remote.popen(['uname']) stdout, stderr = proc.communicate() assert 'Linux' == stdout.strip() log.debug('Testing getcwd()') assert '/home/vagrant' == remote.getcwd() log.debug('Testing chdir()') remote.chdir('/') assert '/' == remote.getcwd() remote.chdir('/home/vagrant') # create a sample file TESTFN = 'testfile' TESTDN = 'TESTDIR' log.debug('Testing file') with remote.file(TESTFN, mode='w') as out: out.write('test') log.debug('Testing chmod') remote.chmod(TESTFN, 0732) log.debug('Testing mkdir') # FIXME: umask? # FIXME: on exists/conflict? remote.mkdir(TESTDN, 0700) log.debug('Testing listdir') assert TESTFN in remote.listdir('.') assert TESTDN in remote.listdir('.') log.debug('Testing rmdir') remote.rmdir(TESTDN) # FIXME: can't test chown without root access log.debug('Testing normalize') assert '/home' == remote.normalize('./..') log.debug('Testing symlink') remote.symlink('to', 'from') log.debug('Testing lstat') remote.lstat('from') log.debug('Testing readlink') assert remote.readlink('/home/vagrant/from') == 'to' log.debug('Testing rename') remote.rename('from', 'from2') assert remote.readlink('/home/vagrant/from2') == 'to' log.debug('Testing unlink') remote.unlink('/home/vagrant/from2') log.debug('Testing stat') s = remote.stat(TESTFN) assert s.st_uid == 1000 assert s.st_gid == 1000 remote.unlink(TESTFN)