def destroy(self, path): """ Destroy: send EXIT to bootstrap, XFER files to the successor of this node and destroy the FUSE mount """ # notify bootstrap that we left try: req_pkt = protocol.construct_packet(protocol.Verbs.EXIT, protocol.Status.OK, {}) protocol.sock_send_recv(self.bootstrap_ip, req_pkt, protocol.BOOTSTRAP_PORT) except ProtocolError: pass except IOError as ex: logging.exception(ex) pass # remove our self so relocate_files will not calculate our self as the succ for any files try: hashing.HASHING_OBJ.remove_node(hashing.HASHING_OBJ.id_hash) except ZeroDivisionError: # thrown if last node is exiting pass relocate_files(True) # destroy return super(Difuse, self).destroy(path)
def write(self, path, data, offset, fh): """ Write """ path = path[1:] if path in os.listdir(LOCAL_ROOT): path = join(LOCAL_ROOT, path) fd = os.open(path, os.O_WRONLY) os.lseek(fd, offset, os.SEEK_SET) writen = os.write(fd, data) os.close(fd) return writen try: host_ip = protocol.lookup(self.bootstrap_ip, path) req_payload = { 'filename': path, 'offset': offset, 'bytes': list(data) } req_pkt = protocol.construct_packet(protocol.Verbs.WRITE_REQ, protocol.Status.OK, req_payload) header, payload = protocol.sock_send_recv(host_ip, req_pkt) logging.debug(header) if header.status != protocol.Status.OK.name: if header.status == protocol.Status.EACCES.name: raise FuseOSError(errno.EACCES) elif header.status == protocol.Status.ENOENT.name: raise FuseOSError(errno.ENOENT) else: raise FuseOSError(errno.EIO) return payload['cnt'] except (ProtocolError, KeyError): raise FuseOSError(errno.EIO)
def unlink(self, path): """ unlink: lookup the location of the file and send request to host. if local, remove. """ path = path[1:] # check if local, if local send unlink req and return if path in os.listdir(LOCAL_ROOT): os.remove(join(LOCAL_ROOT, path)) return req_payload = {'filename': path} req_pkt = protocol.construct_packet(protocol.Verbs.UNLINK_REQ, protocol.Status.OK, req_payload) # not local, lookup host_ip = protocol.lookup(self.bootstrap_ip, path) try: header, _ = protocol.sock_send_recv(host_ip, req_pkt) if header.status != protocol.Status.OK.name: if header.status == protocol.Status.ENOENT.name: raise FuseOSError(errno.ENOENT) elif header.status == protocol.Status.EACCES.name: raise FuseOSError(errno.EACCES) else: raise FuseOSError(errno.EIO) except IOError: raise FuseOSError(errno.EIO)
def truncate(self, path, length, fh=None): """ Truncate """ path = path[1:] if path in os.listdir(LOCAL_ROOT): os.truncate(join(LOCAL_ROOT, path), length) try: host_ip = protocol.lookup(self.bootstrap_ip, path) except FileNotFoundError: raise FuseOSError(errno.ENOENT) try: req_payload = {'filename': path, 'len': length} req_pkt = protocol.construct_packet(protocol.Verbs.TRUNC_REQ, protocol.Status.OK, req_payload) header, payload = protocol.sock_send_recv(host_ip, req_pkt) if header.status != protocol.Status.OK.name: if header.status == protocol.Status.EACCES.name: raise FuseOSError(errno.EACCES) elif header.status == protocol.Status.ENOENT.name: raise FuseOSError(errno.ENOENT) else: raise FuseOSError(errno.EIO) except ProtocolError: raise FuseOSError(errno.EIO)
def read(self, path, size, offset, fh): """ Read """ path = path[1:] if path in os.listdir(LOCAL_ROOT): with open(join(LOCAL_ROOT, path), 'rb') as f: f.seek(offset) return f.read(size) try: host_ip = protocol.lookup(self.bootstrap_ip, path) req_payload = {'filename': path, 'cnt': size, 'offset': offset} req_pkt = protocol.construct_packet(protocol.Verbs.READ_REQ, protocol.Status.OK, req_payload) header, payload = protocol.sock_send_recv(host_ip, req_pkt) if header.status != protocol.Status.OK.name: if header.status == protocol.Status.ENOENT.name: raise FileNotFoundError elif header.status == protocol.Status.EACCES.name: raise PermissionError return bytes(payload['bytes']) except ProtocolError as ex: logging.exception(ex) raise FuseOSError(errno.EIO) except FileNotFoundError: raise FuseOSError(errno.ENOENT) except PermissionError: raise FuseOSError(errno.EACCES)
def __init__(self, bootstrap_ip, fuse_mount): """ init the node server, join the cluster, construct the listening socket """ self.fuse_mount = fuse_mount self.bootstrap_ip = bootstrap_ip # establish server socket self.sock = socket.socket() self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((HOST, protocol.PORT)) self.sock.listen(5) # Send JOIN to the bootstrap and initialize the Hashing data structures req_pkt = protocol.construct_packet(protocol.Verbs.JOIN, protocol.Status.OK, {}) try: # send JOIN and receive IP_TABLE header, payload = protocol.sock_send_recv(bootstrap_ip, req_pkt, protocol.BOOTSTRAP_PORT) logging.debug(f'received {header}') if header.status != protocol.Status.OK.name: raise IOError('Did not received status: ok') # init the hashing object using the Bootstrap's data ip_table = Hashing.deserializable_ip_table(payload['ip_table']) hash_unit = sorted(ip_table.keys()) id_hash = Hashing.difuse_hash(payload['ip']) hashing.HASHING_OBJ = Hashing(hash_unit, ip_table, id_hash) logging.info(f'ID: {id_hash}') # if files need to be relocated, relocate them relocate_files() except IOError as ex: logging.exception(ex)
def relocate_files(relocate_all=False): """ Scans the LOCAL_ROOT directory for files that need to be relocated and sends XFER_RES to each node where files should be transferred. :relocate_all: Default false. If true, all files on local root will be transferred to the successor :return: None """ # table that maps an ID hash to a list of dictionary to send xfer_res_table = {id_hash: [] for id_hash in hashing.HASHING_OBJ.hash_unit} # build xfer res table for each file in the local root for filename in os.listdir(LOCAL_ROOT): file_hash = Hashing.difuse_hash(filename) try: file_succ = Hashing.succ(file_hash) except ZeroDivisionError: # thrown if last node exiting return logging.debug(f'filename: {filename} | file_hash: {file_hash}') if relocate_all or file_succ != hashing.HASHING_OBJ.id_hash: try: with open(join(LOCAL_ROOT, filename), 'rb') as f: file_bytes = f.read() xfer_res_table[file_succ].append({ 'filename': filename, 'bytes': list(file_bytes) }) except Exception as ex: logging.exception(ex) pass # send the XFER_RES for id_hash, xfer_list in xfer_res_table.items(): # no files to transfer, do not send anything if not xfer_list: continue conn_ip = hashing.HASHING_OBJ.ip_table[id_hash] logging.info(f'relocating {xfer_list} to {conn_ip}') xfer_res_pkt = protocol.construct_packet(protocol.Verbs.XFER, protocol.Status.OK, {'files': xfer_list}) try: protocol.sock_send_recv(conn_ip, xfer_res_pkt) except Exception as ex: logging.exception(ex) else: for xfer_file in xfer_list: os.remove(join(LOCAL_ROOT, xfer_file['filename']))
def create(self, path, mode, fi=None): """ :param path: filename :param mode: mode to creat :param fi: no idea :return: """ path = path[1:] hash_id = hashing.HASHING_OBJ.difuse_hash(path) succ = hashing.HASHING_OBJ.succ(hash_id) host_ip = hashing.HASHING_OBJ.ip_table[succ] # if the successor is local id, create the file here if succ == hashing.HASHING_OBJ.id_hash: try: open(join(LOCAL_ROOT, path), 'xb').close() except OSError as ex: raise FuseOSError(ex.errno) else: self.fd += 1 return self.fd # non local creation necessary logging.info(f'sending create {path} to {host_ip}') payload = {'filename': path} pkt = protocol.construct_packet(protocol.Verbs.CREATE, protocol.Status.OK, payload) try: header, payload = protocol.sock_send_recv(host_ip, pkt) if header.status == protocol.Status.EEXIST.name: raise FileExistsError if header.status == protocol.Status.EACCES.name: raise PermissionError if header.status != protocol.Status.OK.name: raise FuseOSError(errno.EIO) except FileExistsError: raise FuseOSError(errno.EEXIST) except PermissionError: raise FuseOSError(errno.EACCES) except ProtocolError: raise FuseOSError(errno.EIO) else: self.fd += 1 return self.fd
def getattr(self, path, fh=None): """ stat/getattr syscall """ if path == '/': return self._stat_dict(os.stat(LOCAL_ROOT)) path = path[1:] if path in os.listdir(LOCAL_ROOT): stat = os.stat(join(LOCAL_ROOT, path)) stat_dict = self._stat_dict(stat) stat_dict['st_uid'] = os.getuid() stat_dict['st_gid'] = os.getgid() return stat_dict host_ip = protocol.lookup(self.bootstrap_ip, path) try: pkt = protocol.construct_packet(protocol.Verbs.STAT_REQ, protocol.Status.OK, {'filename': path}) header, payload = protocol.sock_send_recv(host_ip, pkt) logging.debug(f'getattr received {header}') if header.verb != protocol.Verbs.STAT_RES.name: raise FuseOSError(errno.EIO) if header.status == protocol.Status.ENOENT.name: raise FuseOSError(errno.ENOENT) stat = os.stat_result(payload['stat']) stat_dict = self._stat_dict(stat) stat_dict['st_uid'] = os.getuid() stat_dict['st_gid'] = os.getgid() return stat_dict except FuseOSError: raise except (ProtocolError, Exception) as ex: logging.exception(ex) raise FuseOSError(errno.EIO) from ex