''' To do: handle nodes disapearing reinitialize might need some work redirect (redirect() from flask can be used, we need to make the if statements to trigger them) (basically if hashit(key) != node.get_id() then redirect it to the node who's ID == hashit(key)) Rehash probably needs some work a lot of other stuff ''' from flask import Flask, abort, request, make_response, jsonify, redirect import requests as req import re import json app = Flask(__name__) import os THIS IS A TEST class Node(object): def __init__(self, ID=0, IP=None, Port=None, Pair=None, node_list=None, other_IDs=None): # ID is 0 - number of nodes to make it easier to decide which node gets what self.ID = ID self.IP = IP self.Port = Port self.Pair = Pair self.node_list = node_list self.other_IDs = other_IDs # self.is_cor = is_cor # do i even need these? def get_ip(self): return self.IP def get_port(self): return self.Port def get_id(self): return self.ID def get_pair(self): return self.Pair def get_list(self): return self.node_list def get_other_ids(self): return self.other_IDs node_port = os.environ.get('PORT', 8080) node_ip = os.environ.get('IP', '0.0.0.0') node_pair = os.getenv('IPPORT') other_nodes = os.getenv('VIEW') other_nodes.split(",") number_of_nodes = len(other_nodes) node_id = other_nodes.index(node_pair) # returns index of node_pai in other_nodes node_id_list = {} """ #Linear probing to determine IDs for temp in other_nodes: temp_key = (int(temp) % number_of_nodes) i=0 #im afraid to do while(True) while (i < number_of_nodes+1): if (node_id_list[temp_key] == None): node_id_list[temp_key] = temp if (temp == node_pair): node_id = temp_key break elif (temp_key < number_of_nodes-1): temp_key+=1 else: temp_key = 0 i+=1 """ node = Node() def initialize(): node.ID = node_id node.IP = node_ip node.Port = node_port node.Pair = node_pair node.node_list = other_nodes node.other_IDs = node_id_list # hashing function def hashit(key): asciiKey = ''.join(str(ord(c)) for c in key) """ k = int(key) #hash_key = k % number_of_nodes hash_key = k % len(node.get_list()) """ return int(asciiKey) % len(other_nodes) # check if the given key belongs to this node def node_check(key): k = hashit(key) # return k == node.ID return k is node_id # get the node that has the key def get_real_node(key): k = hashit(key) id_list = node.get_other_ids() # redirect(id_list[k] + "/kvs") return id_list[k] # resets each of the IDs of the nodes but does not rehash def reinitialize(new_node, flag): temp_list = node.get_list() # add node address if flag == 0: temp_list.append(new_node) # delete node address else: temp_list.popitem(new_node) node.node_list = temp_list num_nodes = len(node.get_list()) temp_id_list = {} for temp in node.node_list: temp_key = (int(temp) % num_nodes) i = 0 # im afraid to do while(True) while (i < num_nodes + 1): if (temp_id_list[temp_key] == None): temp_id_list[temp_key] = temp if (temp == node.get_pair()): node.ID = temp_key break elif (temp_key < num_nodes - 1): temp_key += 1 else: temp_key = 0 i += 1 node.other_IDs = temp_id_list def set_new_node(new_ipport, signal): url = 'http://' + new_ipport + '/kvs/view_update?type=update' up = req.put(url, data={'ip_port': signal}) # i might be doing this wrong, probably dont need to return anything return up.content def get_other_ids(): for temp in node.get_list(): temp_key = (int(temp) % len(node.get_list())) i = 0 temp_id_list = {} while (i < len(node.get_list()) + 1): if (temp_id_list[temp_key] == None): temp_id_list[temp_key] = temp if (temp == node.get_pair()): node.ID = temp_key break elif (temp_key < len(node.get_list()) - 1): temp_key += 1 else: temp_key = 0 i += 1 return temp_id_list # going to work on this # asks the node that is going to be shutdown to return their dic def get_backup_dic(): pass def rehash(dic): # not going to work ''' temp_dic = {} for k in dic.keys(): temp_dic[hashit(k)] = dic[k] return temp_dic ''' for temp_key in dic: url = 'http://' + node.get_pair() + '/kvs/' + temp_key temp_value = dic[temp_key] reup = req.put(url, data={'val': temp_value}) return reup.content def remove_view_change(ip_port): other_nodes.remove(ip_port) # remove ip_port from VIEW try: node_id = other_nodes.index(node_pair) # update node_id except IndexError: node_id = -1 # you are not in view for key in dictionary: node_location = hashit(key) if node_id is not node_location: # (key,value) not on right node url_str = 'http://' + other_nodes[node_location] + '/kvs/' + key + '-d "val=' + dictionary[key] + '"' req.put(url_str) # store key in correct node del dictionary[key] # remove key from dictionary # if you are the node being removed if node_pair is ip_port: # send view_update msg to all other nodes in view for node in other_nodes: url_str = 'http://' + node + '/kvs/view_update?type=remove -d "ip_port=' + ip_port + '"' req.put(url_str) # WIP def add_view_change(ip_port): if ip_port not in other_nodes: # send view_update msg to all other nodes in view for node in other_nodes: url_str = 'http://' + node + '/kvs/view_update?type=add -d "ip_port=' + ip_port + '"' req.put(url_str) other_nodes.append(ip_port) # add ip_port from VIEW try: node_id = other_nodes.index(node_pair) # update node_id except IndexError: node_id = -1 # you are not in view for key in dictionary: node_location = hashit(key) if node_id is not node_location: # (key,value) not on right node url_str = 'http://' + other_nodes[node_location] + '/kvs/' + key + '-d "val=' + dictionary[key] + '"' req.put(url_str) # store key in correct node del dictionary[key] # remove key from dictionary # if you are the node being added def is_main(): return not ('MAINIP' in os.environ) @app.route('/hello', methods=['GET']) def hello(): if is_main(): return "Hello world!" else: url_str = 'http://' + os.environ['MAINIP'] + '/hello' content = req.get(url_str).content return "forwarding hello world" + content @app.route('/view_update', methods=['GET']) def hello(): if is_main(): return "Hello world!" else: url_str = 'http://' + os.environ['MAINIP'] + '/hello' content = req.get(url_str).content return "forwarding hello world" + content @app.route('/echo', methods=['GET']) def echo(): msg = request.args.get('msg') if msg is None: return "" else: return msg ''' @app.route('/', methods=['POST']) def error(): abort(404) ''' dictionary = {} @app.route('/kvs/<key>', methods=['PUT', 'GET', 'DELETE']) def kvs(key): # view_update # I'm pretty sure im doing this wrong # might need to check if it is a put request if key == 'view_update': # get add or delete update_type = request.args.get('type') new_node_data = request.form('ip_port') if update_type == 'add': reinitialize(new_node_data, 0) set_new_node(new_node_data, node.get_list()) response = jsonify(msg='success') return make_response(response, {"msg": "success"}) if update_type == 'delete': reinitialize(new_node_data, 1) # need to make a way to save all the key:values for rehash get_backup_dic() response = jsonify(msg='success') return make_response(response, {"msg": "success"}) # if this node is the new node, it'll get a msg to update if update_type == 'update': # give the new node the list of other nodes node.node_list = new_node_data # update the ID and the list of other IDs node.other_IDs = get_other_ids() # need to write this function # probably recursively call kvs rehash() if not (re.match("^[A-Za-z0-9_]*$", key) and len(key) <= 250 and len(key) >= 1): # key check status_code = 404 response = jsonify(msg='error', error='key is not alphanumeric or not within 1-250 char limit') return make_response(response, status_code, {"msg": "error"}) if request.method == 'GET': try: if is_main(): if key in dictionary: status_code = 200 response = jsonify(msg='success', value=dictionary[key]) return make_response(response, status_code, {"msg": "success"}) else: status_code = 404 response = jsonify(msg='error', error='key does not exist') return make_response(response, status_code, {"msg": "error"}) else: url_str = 'http://' + os.environ['MAINIP'] + '/kvs/' + key r = req.get(url_str) return r.content, r.status_code except(req.exceptions.ConnectionError, req.exceptions.Timeout): status_code = 404 response = jsonify(msg='error', error='service is not avalible') return make_response(response, status_code, {"msg": "error"}) elif request.method == 'DELETE': try: if is_main: if key in dictionary: dictionary.pop(key) status_code = 200 response = jsonify(msg='success') return make_response(response, status_code, {"msg": "success"}) else: status_code = 404 # This should be an error # return json.dumps[dictionary(msg = 'success', error = 'key does not exist')], status_code response = jsonify(msg='error', error='key does not exist') return make_response(response, status_code, {"msg": "error"}) else: url_str = 'http://' + os.environ['MAINIP'] + '/kvs/' + key r = req.delete(url_str) return r.content, r.status_code except(req.exceptions.ConnectionError, req.exceptions.Timeout): status_code = 404 response = jsonify(msg='error', error='service is not avalible') return make_response(response, status_code, {"msg": "error"}) elif request.method == 'PUT': try: if is_main: # changed to form as it works for python3 # val = request.args.get('val') val = request.form('val') if key in dictionary: dictionary[key] = val status_code = 200 response = jsonify(msg='success', replaced=1) return make_response(response, status_code, {"msg": "success"}) else: dictionary[key] = val status_code = 201 response = jsonify(msg='success', replaced=0) return make_response(response, status_code, {"msg": "success"}) else: url_str = 'http://' + os.environ['MAINIP'] + '/kvs/' + key # changed because <key> does nothing? r = req.get(url_str) return r.content, r.status_code except(req.exceptions.ConnectionError, req.exceptions.Timeout): status_code = 404 response = jsonify(msg='error', error='service is not avalible') return make_response(response, status_code, {"msg": "error"}) if __name__ == '__main__': app.config['MAX_CONTENT_LENGTH'] = 1572864 # bigger than 1.5 mb # app.run(debug=True, port=(os.environ.get('PORT')), host=(os.environ.get('IP'))) # app.run(debug=True, port=8080, host='0.0.0.0') initialize() app.run(debug=True, port=int(node_port), host=node_ip)
Run
Reset
Share
Import
Link
Embed
Language▼
English
中文
Python Fiddle
Python Cloud IDE
Follow @python_fiddle
Browser Version Not Supported
Due to Python Fiddle's reliance on advanced JavaScript techniques, older browsers might have problems running it correctly. Please download the latest version of your favourite browser.
Chrome 10+
Firefox 4+
Safari 5+
IE 10+
Let me try anyway!
url:
Go
Python Snippet
Stackoverflow Question