def _handshake_to_new_node(client, sid): ## add client info into children document _add_children(client, sid) s_obj = WebServerModel.objects(sid=sid) logger.info("client id:"+client['nid']+" has joined the system") message = json.dumps({ 'action':api._ACTION_KEYS[0], 'data': { 'server_id':s_obj.scalar('sid').get(), 'server_public_key':s_obj.scalar('public_key').get(), 'common_key_private':s_obj.scalar('common_key_private').get() } }) encrypted_message = api.encrypt_msg(client['public_key'], message) childs = s_obj.get().children message = {} for child in childs: if not message: message = { 'action':api._ACTION_KEYS[1], 'data':[ { 'nid':child.nid, 'public_key':child.public_key }] } else: message['data'].append( { 'nid':child.nid, 'public_key':child.public_key }) ## TODO: set try and except retry = 0 while retry < api.MAX_TRY: try: proxy = callme.Proxy( server_id=client['nid'], amqp_host="localhost", timeout=3) proxy.use_server(client['nid']).execute_command(encrypted_message) retry = api.MAX_TRY except: retry+=1 print "passing master server info failed... {} times".format(retry) else: ## broadcast of a new client to all nodes _broadcast_to_nodes(api.EXCHANGE_FOR_ALL, api.encrypt_msg(s_obj.scalar('common_key_public').get(), json.dumps(message)))
def execute_command(self, encrypted_action): print "server calling rpc" data = json.loads( api.decrypt_msg(self.model.private_key, encrypted_action)) action = data['action'] print action ## response from master server has arrived, stop trying to reach other servers if self.timer.is_alive(): self.timer.stop() ## server sent info if (action == api._ACTION_KEYS[0]): self._store_server_info(data['data'], self.model.nid) ## add comments elif(action == api._ACTION_KEYS[5]): data['nid'] = self.model.nid """data['content'].update( { 'nid':self.model.nid, 'sid':data['sid'] })""" return api.encrypt_msg(AppModel.objects( nid=self.model.nid).get().server_public_key, app_api.add_comments(**data))
def _refresh_common_key(sid): s_obj = WebServerModel.objects(sid=sid) if s_obj.get().children: logger.info("common key timeout") new_prk = api.generate_private_key() message = { "action":api._ACTION_KEYS[3], "data": { "common_key_private":new_prk.exportKey().decode('utf-8') } } try: if _broadcast_to_nodes(api.EXCHANGE_FOR_ALL, api.encrypt_msg(s_obj.scalar('common_key_public').get(), message)) == False: logger.error("Message broadcast failed") #else: res = s_obj.update_one( common_key_public=api.generate_public_key(new_prk).exportKey().decode('utf-8'), common_key_private=message['data']['common_key_private']) s_obj.get().save() ## make log of updated common key _make_log(api._ACTION_KEYS[3], sid, "common key updated") except: logger.error("try again") threading.Timer(api.COMMON_KEY_TIMEOUT, _refresh_common_key, [sid]).start()
def _clean_failed_garbages(sid): garbages = q.get_custom_list(api.FAILED_NODES) message = json.dumps( { 'action':api._ACTION_KEYS[2], 'data':garbages }) _broadcast_to_nodes( api.EXCHANGE_FOR_ALL, api.encrypt_msg( WebServerModel.objects( sid=sid).scalar( 'common_key_public').get(), message)) if WebServerModel.objects(sid=sid).update(pull_all__children= map(lambda x:{ 'nid':x }, garbages))>1: logger.info("cleaned failed nodes") else: logger.error("cleanning failed or nothing to clean") threading.Timer(api.CLEAN_GARBAGE_NODES, _clean_failed_garbages, [sid]).start()
def _create_usersession(uname, sid): sk = api._generate_session_key() mUserSession = UserSession( session_id=sk, username = uname ) pk = WebServerModel.objects(sid=sid).get().public_key print pk return api.encrypt_msg(pk, sk)
def _send_add_comment_call(**data): if data['nid'] == None: return False retry = 0 prm = None while retry < api.MAX_TRY: try: # do not retry pushing node id inth the queue over as it failed first time if retry == 0: prm = _coordinate_acc_to_res( sid=data['sid'], nid=data['nid']) proxy = callme.Proxy( server_id=data['nid'], amqp_host="localhost", timeout=3) for c in WebServerModel.objects(sid=data['sid']).get().children: if c.nid == data['nid']: response = proxy.use_server(c.nid).execute_command( api.encrypt_msg(c.public_key, { 'action':api._ACTION_KEYS[5], ## access to shared resource has been permitted 'permission':prm, 'sid':data['sid'], 'content': { 'by':data['by'], 'session_id':data['session_id'], 'comment':data['comment'], 'created_at':data['created_at'] } })) return response ## given node is not matching with any of registered node retry+=1 print "trying {}".format(retry) except: retry+=1 print "node {} is not responding.. trying {} times...".format(data['nid'], retry) # finally return False
def _inspect_inactive_children(sid): #print "inspection...{}".format(sid) s_obj = WebServerModel.objects(sid=sid) children = s_obj.get().children if children: for child in children: #logger.info(child.last_access) #logger.info(api._get_current_time()) if api._get_unix_from_datetime( api._get_current_time())-api._get_unix_from_datetime( child.last_access)>api.TIME_DETERMINE_INACTIVE: logger.info(child.nid+" -> user logged out") ## pull inactive user s_obj.update_one(pull__children__nid=child.nid) s_obj.get().save() ## notify all clients about inactive user message = json.dumps( { 'action':api._ACTION_KEYS[2], 'data':{ "nid":child.nid, } }) ## remove lock on shared resource if the inactive child is holding it if q.get(api.RES_HOLDER) == child.nid: logger.info("logging out user had locks") _coordinate_acc_to_res(sid, child.nid, api._ACTION_KEYS[6]) _broadcast_to_nodes(api.EXCHANGE_FOR_ALL, api.encrypt_msg(s_obj.scalar('common_key_public').get(), message)) _make_log(api._ACTION_KEYS[2], sid, child.nid) ## TODO: try deamon thread threading.Timer(api.INSPECTION_TIME_INTERVAL, _inspect_inactive_children, [sid]).start()
def _coordinate_acc_to_res(sid=None, nid=None, action=None): ## done working message from a node who has access to the shared resource if action == api._ACTION_KEYS[6]: q.pop() q.delete(api.RES_HOLDER) _make_log("resource_released", sid, nid) ## next node in the queue nxt = q.bottom() if nxt: logger.info("next node exists") logger.info(nxt) # if next resource accessor is master node himself if nxt == sid: _upload_user_sessions(sid) _coordinate_acc_to_res(sid, sid, api._ACTION_KEYS[6]) ## save as node which holding lock to the resource at present q.append(api.RES_HOLDER, nxt) for child in WebServerModel.objects(sid=sid).get().children: if child.nid == nxt: _send_direct_msg(nxt, api.encrypt_msg(child.public_key, { 'action':api._ACTION_KEYS[8], # action : access permission 'by':sid })) break _make_log("resource_lock", sid, nxt) else: logger.info("queue is empty") return True elif action == None and nid == None: if q.length() == 0: q.push(sid) #q.push(nid) # for debugging q.append(api.RES_HOLDER, sid) _make_log("resource_lock", sid, sid) return True else: q.push(sid) return False elif action == None: for child in WebServerModel.objects(sid=sid).get().children: ## node exists if child.nid == nid: if q.length() == 0: q.push(nid) #q.push(nid) # for debugging q.append(api.RES_HOLDER, nid) _make_log("resource_lock", sid, nid) return True else: q.push(nid) return False ## not registered node logger.error("not registered node") return False