示例#1
0
 def handle_read_req(conn, payload):
     """
     Handles a read_req
     """
     res_pkt = b''
     try:
         path = join(LOCAL_ROOT, payload['filename'])
         offset = payload['offset']
         cnt = payload['cnt']
         f = open(path, 'rb')
     except FileNotFoundError:
         res_pkt = protocol.construct_packet(protocol.Verbs.READ_RES,
                                             protocol.Status.ENOENT, {})
     except Exception as ex:
         logging.exception(ex)
         res_pkt = protocol.construct_packet(protocol.Verbs.READ_RES,
                                             protocol.Status.ENOENT, {})
     else:
         f.seek(offset)
         buf = f.read(cnt)
         res_pkt = protocol.construct_packet(protocol.Verbs.READ_RES,
                                             protocol.Status.ERROR, {})
         read_res = list(buf)
         res_payload = {'bytes': read_res, 'cnt': len(read_res)}
         res_pkt = protocol.construct_packet(protocol.Verbs.READ_RES,
                                             protocol.Status.OK,
                                             res_payload)
         f.close()
     finally:
         conn.send(res_pkt)
示例#2
0
 def handle_write_req(conn, payload):
     """
     Handle write request
     """
     res_pkt = b''
     try:
         path = join(LOCAL_ROOT, payload['filename'])
         offset = payload['offset']
         data = bytes(payload['bytes'])
         fd = os.open(path, os.O_WRONLY)
     except FileNotFoundError:
         res_pkt = protocol.construct_packet(protocol.Verbs.WRITE_RES,
                                             protocol.Status.ENOENT, {})
     except Exception as ex:
         logging.exception(ex)
         res_pkt = protocol.construct_packet(protocol.Verbs.WRITE_RES,
                                             protocol.Status.ERROR, {})
     else:
         os.lseek(fd, offset, os.SEEK_SET)
         writen = os.write(fd, data)
         res_payload = {'cnt': writen}
         res_pkt = protocol.construct_packet(protocol.Verbs.WRITE_RES,
                                             protocol.Status.OK,
                                             res_payload)
         os.close(fd)
     finally:
         conn.send(res_pkt)
示例#3
0
 def handle_create(conn, payload):
     """
     Handles CREATE: creates a file in the local filesystem
     :param: conn: open connection
     :param: payload: contains the filename of the file to send
     :return: None
     """
     try:
         name = payload['filename']
     except KeyError as ex:
         logging.exception(ex)
         return
     pkt = b''
     try:
         open(join(LOCAL_ROOT, name), 'x').close()
     except FileExistsError:
         pkt = protocol.construct_packet(protocol.Verbs.ERROR,
                                         protocol.Status.EEXIST, {})
     except PermissionError:
         pkt = protocol.construct_packet(protocol.Verbs.ERROR,
                                         protocol.Status.EACCES, {})
     except Exception as ex:
         pkt = protocol.construct_packet(protocol.Verbs.ERROR,
                                         protocol.Status.ERROR, {})
         logging.exception(ex)
     else:
         pkt = protocol.construct_packet(protocol.Verbs.OK,
                                         protocol.Status.OK, {})
     finally:
         conn.send(pkt)
示例#4
0
    def handle_stat_req(conn, payload):
        """
        Handles a stat_req

        :conn: connection to requesting node
        :payload: contains the filename to stat
        """
        res_pkt = b''
        try:
            stat = os.stat(join(LOCAL_ROOT, payload['filename']))
        except FileNotFoundError as ex:
            logging.exception(ex)
            res_pkt = protocol.construct_packet(protocol.Verbs.STAT_RES,
                                                protocol.Status.ENOENT, {})
        except PermissionError as ex:
            logging.exception(ex)
            res_pkt = protocol.construct_packet(protocol.Verbs.STAT_RES,
                                                protocol.Status.EACCES, {})
        except Exception:
            res_pkt = protocol.construct_packet(protocol.Verbs.STAT_RES,
                                                protocol.Status.ERROR, {})
        else:
            res_payload = {'stat': stat}
            res_pkt = protocol.construct_packet(protocol.Verbs.STAT_RES,
                                                protocol.Status.OK,
                                                res_payload)
        finally:
            conn.send(res_pkt)
示例#5
0
def work_routine(bootstrap):
    """
    Routine for worker threads. gets messages from queue and calls handler
    """
    while True:
        conn, addr = bootstrap.queue.get()
        try:
            buf = conn.recv(protocol.HEADER.sizeof())
            req = protocol.HEADER.parse(buf)
            payload = conn.recv(req.payload_size)
            logging.info(f'received: {req}\n{payload}')
            # JOIN
            if req.verb == protocol.Verbs.JOIN.name:
                handle_join(bootstrap, conn, addr, json.loads(payload))
            # EXIT
            elif req.verb == protocol.Verbs.EXIT.name:
                handle_exit(bootstrap, conn, addr, json.loads(payload))
            # Unsupported/invalid request
            else:
                logging.info(f'unsupported operation {req.verb}')
                err_pkt = protocol.construct_packet(protocol.Verbs.ERROR,
                                                    protocol.Status.ERROR,
                                                    {'msg': 'unsupported operation'})
                conn.send(err_pkt)
        except ConstructError as ex:
            logging.error(f'bad packet {req} from connection {addr}: {ex}')
        finally:
            conn.close()
示例#6
0
 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)
示例#7
0
    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)
示例#8
0
    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)
示例#9
0
 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)
示例#10
0
    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)
示例#11
0
 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)
示例#12
0
 def handle_trunc_req(conn, payload):
     """
     handles trunc requet
     """
     res_pkt = b''
     try:
         path = join(LOCAL_ROOT, payload['filename'])
         size = payload['len']
         os.truncate(path, size)
     except Exception:
         res_pkt = protocol.construct_packet(protocol.Verbs.TRUNC_RES,
                                             protocol.Status.ERROR, {})
     else:
         res_pkt = protocol.construct_packet(protocol.Verbs.TRUNC_RES,
                                             protocol.Status.OK, {})
     finally:
         conn.send(res_pkt)
示例#13
0
 def handle_readdir(conn, _payload):
     '''
     handles READDIR, responding to the connection with a list of the files on the local root
     :param conn: open connection
     :param payload: payload, empty
     :return: None
     '''
     pkt = b''
     try:
         entries = os.listdir(LOCAL_ROOT)
     except Exception:
         pkt = protocol.construct_packet(protocol.Verbs.READDIR_RES,
                                         protocol.Status.ERROR, {})
     else:
         pkt = protocol.construct_packet(protocol.Verbs.READDIR_RES,
                                         protocol.Status.OK,
                                         {'entries': entries})
     finally:
         conn.send(pkt)
示例#14
0
 def serve(self):
     """
     Serves requests from other nodes
     """
     while True:
         conn, addr = self.sock.accept()
         try:
             req = conn.recv(protocol.HEADER.sizeof())
             req = protocol.HEADER.parse(req)
             payload = conn.recv(req.payload_size)
             logging.info(f'received: {req}\n{payload}')
             # STAT_REQ
             if req.verb == protocol.Verbs.STAT_REQ.name:
                 self.handle_stat_req(conn, json.loads(payload))
             # READ_REQ
             elif req.verb == protocol.Verbs.READ_REQ.name:
                 self.handle_read_req(conn, json.loads(payload))
             # WRITE_REQ
             elif req.verb == protocol.Verbs.WRITE_REQ.name:
                 self.handle_write_req(conn, json.loads(payload))
             # TRUNC
             elif req.verb == protocol.Verbs.TRUNC_REQ.name:
                 self.handle_trunc_req(conn, json.loads(payload))
             # UNLINK
             elif req.verb == protocol.Verbs.UNLINK_REQ.name:
                 self.handle_unlink_req(conn, json.loads(payload))
             # XFER
             elif req.verb == protocol.Verbs.XFER.name:
                 self.handle_xfer_res(conn, json.loads(payload))
             # NEW_NODE
             elif req.verb == protocol.Verbs.NEW_NODE.name:
                 self.handle_new_node(conn, json.loads(payload))
             # CREATE
             elif req.verb == protocol.Verbs.CREATE.name:
                 self.handle_create(conn, json.loads(payload))
             # LEFT_NODE
             elif req.verb == protocol.Verbs.LEFT_NODE.name:
                 self.handle_left_node(conn, json.loads(payload))
             # READDIR_REQ
             elif req.verb == protocol.Verbs.READDIR_REQ.name:
                 self.handle_readdir(conn, json.loads(payload))
             # Unsupported/Unexpected request
             else:
                 err_pkt = protocol.construct_packet(
                     protocol.Verbs.ERROR, protocol.Status.ERROR,
                     {'msg': 'unsupported operation'})
                 conn.send(err_pkt)
         except ConstructError:
             logging.error(f'bad packet from {addr}')
         finally:
             conn.close()
示例#15
0
 def handle_unlink_req(self, conn, payload):
     """
     Handles unlink request, removes file and responds to the conn
     """
     res_pkt = b''
     try:
         os.unlink(join(LOCAL_ROOT, payload['filename']))
     except FileNotFoundError as ex:
         logging.exception(ex)
         res_pkt = protocol.construct_packet(protocol.Verbs.UNLINK_RES,
                                             protocol.Status.ENOENT, {})
     except PermissionError as ex:
         logging.exception(ex)
         res_pkt = protocol.construct_packet(protocol.Verbs.UNLINK_RES,
                                             protocol.Status.EACCES, {})
     except Exception as ex:
         logging.exception(ex)
         res_pkt = protocol.construct_packet(protocol.Verbs.UNLINK_RES,
                                             protocol.Status.ERROR, {})
     else:
         res_pkt = protocol.construct_packet(protocol.Verbs.UNLINK_RES,
                                             protocol.Status.OK, {})
     finally:
         conn.send(res_pkt)
示例#16
0
 def handle_xfer_res(self, conn, payload):
     """
     Handles the XFER_RES, copying all the files in the payload to the local root
     :param conn: connection
     :param payload: payload: list of files
     :return: None
     """
     files_list = payload['files']
     for file_dict in files_list:
         try:
             with open(join(LOCAL_ROOT, file_dict['filename']), 'wb') as f:
                 f.write(bytes(file_dict['bytes']))
         except Exception as ex:
             logging.exception(ex)
     pkt = protocol.construct_packet(protocol.Verbs.OK, protocol.Status.OK,
                                     {})
     conn.send(pkt)
示例#17
0
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']))
示例#18
0
    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
示例#19
0
def handle_exit(_bootstrap, conn, addr, _payload):
    """
    Handles EXIT verb.

    Removes the entry from the IP Table and broadcasts LEFT_NODE to all nodes in the cluster
    """
    # remove node
    hashing.HASHING_OBJ.remove_node(Hashing.difuse_hash(addr[0]))

    # send ack
    try:
        res_pkt = protocol.construct_packet(protocol.Verbs.OK,
                                        protocol.Status.OK,
                                        {})
        conn.send(res_pkt)
    except IOError as ex:
        logging.exception(ex)

    # bcast LEFT_NODE
    protocol.broadcast_no_recv(protocol.Verbs.LEFT_NODE, {'ip': addr[0]})
示例#20
0
def handle_join(_bootstrap, conn, addr, _payload):
    """
    Handles the JOIN protocol verb
    """
    logging.info(f'JOIN request from {addr}')

    # add new node to IP Table
    hashing.HASHING_OBJ.add_node(addr[0])
    # send NEW_NODE to cluster
    protocol.broadcast_no_recv(protocol.Verbs.NEW_NODE, {'ip': addr[0]})
    # send ip table to joining conn
    ip_table_payload = {
        'ip': addr[0],
        'ip_table': hashing.HASHING_OBJ.serializable_ip_table()
    }
    logging.info(f'IP_TABLE: {hashing.HASHING_OBJ.ip_table}')
    ip_table_pkt = protocol.construct_packet(protocol.Verbs.IP_TABLE,
                                             protocol.Status.OK,
                                             ip_table_payload)
    conn.send(ip_table_pkt)
示例#21
0
    def readdir(self, _path, fh):
        """
        readdir: broadcasts a READDIR_REQ to every node in the cluster and collects the results of all the READDIR_RES
        it receives
        """
        # create list of sockets to use for connection
        conns = [
            socket() for _ in range(len(hashing.HASHING_OBJ.ip_table.values()))
        ]

        # broadcast READ DIR
        for conn, host_ip in zip(conns, hashing.HASHING_OBJ.ip_table.values()):
            try:
                conn.connect((host_ip, protocol.PORT))
                pkt = protocol.construct_packet(protocol.Verbs.READDIR_REQ,
                                                protocol.Status.OK, {})
                conn.send(pkt)
            except Exception as ex:
                logging.debug(f'error connecting to {host_ip}')
                logging.exception(ex)
                continue

        entries = []

        # collect the READDIR_RES'
        for conn in conns:
            try:
                header = protocol.HEADER.parse(
                    conn.recv(protocol.HEADER.sizeof()))
                payload = json.loads(conn.recv(header.payload_size))
                if header.status != protocol.Status.OK.name:
                    continue
                entries += payload['entries']
            except (ConstructError, OSError, StreamError) as ex:
                logging.exception(ex)
                continue
            finally:
                conn.close()

        return ['.', '..'] + sorted(entries)
示例#22
0
    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