Пример #1
0
	def remove_candidate(self, candidate):
		'''
		Removes candidate from zookeeper and updates leader queues to
		reflect deleted candidate.

		:param path: string. path to deleted node
		:rtype: True if successful, False otherwise
		'''
		#create node in ZK. Node will be delete when backing framework is closed
		with self.lock:
			del_id = self.counter_by_candidate.get(candidate, None)
			get_logger().info("LeaderQueue.remove_candidate %s" % (del_id))
			if del_id == None:
				return False
		try:
			# delete node
			self.connection.delete(id_to_item_path(self.path, del_id))
			# update the leader queues
			self._handle_remove(del_id)
			#cleanup hashes
			with self.lock:
				del self.counter_by_candidate[candidate]
				return True
		except zookeeper.NoNodeException:
			return False 
Пример #2
0
    def remove_candidate(self, candidate):
        '''
		Removes candidate from zookeeper and updates leader queues to
		reflect deleted candidate.

		:param path: string. path to deleted node
		:rtype: True if successful, False otherwise
		'''
        #create node in ZK. Node will be delete when backing framework is closed
        with self.lock:
            del_id = self.counter_by_candidate.get(candidate, None)
            get_logger().info("LeaderQueue.remove_candidate %s" % (del_id))
            if del_id == None:
                return False
        try:
            # delete node
            self.connection.delete(id_to_item_path(self.path, del_id))
            # update the leader queues
            self._handle_remove(del_id)
            #cleanup hashes
            with self.lock:
                del self.counter_by_candidate[candidate]
                return True
        except zookeeper.NoNodeException:
            return False
Пример #3
0
	def add_listeners(self, add_callback=None, remove_callback=None):
		"""
		Allows caller to add callbacks for item addition and item removal.

		:param add_callback: (Optional) callback that will be called when an \
		  id is created.
		:param remove_callback: (Optional) callback that will be called when \
		  an id is created.

		Callbacks are expected to have the following interface:
		Parameters:

		* dbag - This object
		* affected_id - The id that is being added or deleted, as appropriate \
		  to the call
		"""
		with self.lock:
			if add_callback:
				get_logger().debug(
					"DistributedBag.add_listeners added add listener")
				self.add_callbacks.append(add_callback)
			if remove_callback:
				get_logger().debug(
					"DistributedBag.add_listeners added remove listener")
				self.delete_callbacks.append(remove_callback)
			return self.get_items()
Пример #4
0
    def add_candidate(self, candidate, meta_data=None):
        '''
		Adds new candidate to zookeeper and updates.
		Leader queues to reflect new candidate.

		:param candidate: object that represents the candidate in the queue
		:param meta_data: binary data to store on node
		:rtype: True if operation successfully completed. False otherwise
		'''
        #ensure candidate is not already in queue
        if self.has_candidate(candidate):
            get_logger().warning(
                "LeaderQueue.remove_candidate Candidate already in queue.")
            return False
        else:
            #create node in ZK. Node will be deleted when connection is closed
            flags = zookeeper.EPHEMERAL | zookeeper.SEQUENCE
            newpath = self.connection.create(self.path + PREFIX + PREFIX,
                                             meta_data, zc.zk.OPEN_ACL_UNSAFE,
                                             flags)
            counter = pettingzoo.utils.counter_value(newpath)
            get_logger().info("LeaderQueue.add_candidate %s" % (newpath))
            try:
                #create delete watch
                self._create_deletion_handlers(counter)
            except:
                exc_class, exc, tback = sys.exc_info()
                sys.stderr.write(str(exc_class) + "\n")
                traceback.print_tb(tback)
                raise exc_class, exc, tback
            with self.lock:
                self.counter_by_candidate[candidate] = counter
                self._handle_add(counter, candidate)
            return True
Пример #5
0
	def _process_deleted(self, node):
		"""
		Callback used for Exists object.
		Not intended for external use.
		"""
		del_id = pettingzoo.utils.counter_value(node.path)
		get_logger().debug("LeaderQueue._process_deleted %s" % (del_id))
		self._handle_remove(del_id) 
		with self.lock:
			del self.deletion_handlers[del_id]
Пример #6
0
    def _process_deleted(self, node):
        """
		Callback used for Exists object.
		Not intended for external use.
		"""
        del_id = pettingzoo.utils.counter_value(node.path)
        get_logger().debug("LeaderQueue._process_deleted %s" % (del_id))
        self._handle_remove(del_id)
        with self.lock:
            del self.deletion_handlers[del_id]
Пример #7
0
	def _process_deleted(self, node):
		"""
		Callback used for Deleted object.
		**Not intended for external use.**
		"""
		del_id = pettingzoo.utils.counter_value(node.path)
		get_logger().debug(
			"DistributedBag._process_deleted %s" % (del_id))
		self._on_delete_id(del_id)
		with self.lock:
			del self.deletion_handlers[del_id]
Пример #8
0
	def get(self, item_id):
		"""
		Returns the data payload of a specific item_id's znode

		:param item_id: to retrieve
		:rtype: data stored at id (or absent if invalid id)
		"""
		try:
			get_logger().debug("DistributedBag.get %s" % item_id)
			return self.connection.get(id_to_item_path(self.path, item_id))[0]
		except zookeeper.NoNodeException:
			return None
Пример #9
0
	def _child_callback(self, children):
		path = children.path
		get_logger().info("DistributedMultiConfig._child_callback: %s" % (path))
		config = self._load_znodes(path, add_callback=False)
		callbacks = self.callbacks.get(path, [])
		for callback in callbacks:
			config_list = []
			if config:
				config_list = [conf[1] for conf in config]
			else:
				get_logger().warning(
					"DistributedConfig._child_callback: NO CONFIGS AVAILABLE")
			callback(path, config_list)
Пример #10
0
	def remove(self, item_id):
		"""
		Remove an item from the bag.

		:param item_id: of node to deleteid_to_item_path(path, item_id) \
		  (returned by addElement)
		:rtype: *boolean* true if node was deleted, false if node did not exist
		"""
		try:
			get_logger().debug("DistributedBag.remove %s" % item_id)
			self.connection.delete(id_to_item_path(self.path, item_id))
			return True
		except zookeeper.NoNodeException:
			return False
Пример #11
0
	def _load_znodes(self, path, add_callback=True):
		get_logger().info("DistributedConfig._load_znodes: %s. Callback: %s" %
			(path, add_callback))
		if self.connection.exists(path):
			children = self.connection.children(path)
			if add_callback:
				children(self._child_callback)
				self.children[path] = children
			if len(children) > 0:
				selectee = random.choice([c for c in children])
				znode = path + "/" + selectee
				config = (selectee, yaml.load(self.connection.get(znode)[0]))
				self._store_config_in_cache(path, config)
				return config
Пример #12
0
	def _child_callback(self, children):
		path = children.path
		service_class, service_name = _znode_to_class_and_name(path)
		config = self._load_znodes(path, add_callback=False)
		callbacks = self.callbacks.get(path, [])
		get_logger().info("DistributedConfig._child_callback: %s" % (path))
		for callback in callbacks:
			conf = None
			if config:
				conf = config[1]
			else:
				get_logger().warning(
					"DistributedConfig._child_callback: NO CONFIGS AVAILABLE")
			callback(path, conf)
Пример #13
0
    def _handle_add(self, counter, candidate):
        '''
		Called on node creation. 

		:param counter: (int) counter of newly created candidate
		:param candidate: newly created candidate to add to predecessor \
		  dictionary
		:rtype: None
		'''
        if counter == None:
            get_logger().warning(
                "LeaderQueue._handle_add Unknown candidate %s" % (counter))
        else:
            self._update_predecessor_dict(counter, candidate)
        return None
Пример #14
0
	def _handle_add(self, counter, candidate):
		'''
		Called on node creation. 

		:param counter: (int) counter of newly created candidate
		:param candidate: newly created candidate to add to predecessor \
		  dictionary
		:rtype: None
		'''
		if counter == None:
			get_logger().warning(
				"LeaderQueue._handle_add Unknown candidate %s" % (counter))
		else:
			self._update_predecessor_dict(counter, candidate)
		return None
Пример #15
0
	def _on_delete_id(self, removed_id):
		"""
		Called internally when an item is deleted.
		**Not intended for external use.**
		"""
		path = id_to_item_path(self.path, removed_id)
		get_logger().info("DistributedBag._on_delete_id %s" % (path))
		with self.lock:
			try:
				self.ids.remove(removed_id)
				for callback in self.delete_callbacks:
					callback(self, removed_id)
			except:
				exc_class, exc, tback = sys.exc_info()
				sys.stderr.write(str(exc_class) + "\n")
				traceback.print_tb(tback)
				raise exc_class, exc, tback
Пример #16
0
def write_distributed_config(connection, service_class, service_name, config,
		key=None, interface='eth0', ephemeral=True):
	"""
	Writes a discovery config file out to zookeeper.

	:param connection: a zc.zk.ZooKeeper connection
	:param service_class: the classification of the service \
	  (e.g. mysql, memcached, etc)
	:param service_name: the cluster of the service \
	  (production, staging, etc)
	:param config: A dict containing discovery config values following the \
	  pettingzoo discovery file rules.
	:param key: (Optional) A unique string used to differentiate providers of \
	  a config inside pettingzoo.  If a key is not provided, pettingzoo will \
	  automatically use the ip address of the computer this function is being \
	  called from.  The Key is needed if you wish to explicitly remove a config.
	:param interface: (Default eth0) If falling back to pettingzoo to generate \
	  the key, you can set which networkign device to use to generate the \
	  ip address
	:param ephermeral: (Default True) Determines if this discovery config will \
	  be written to zookeeper as an ephemeral node or not.  Practically what \
	  this means is that if your zookeeper connection closes, zookeeper will \
	  automatically remove the config.  You generally want this to be True.
	:rtype: *str* the key
	"""
	if not key:
		key = _get_local_ip(interface)
	path = _znode_path(service_class, service_name)
	connection.create_recursive(path, "", acl=zc.zk.OPEN_ACL_UNSAFE)
	config = _set_metadata(
		validate_config(config, service_class),
		service_name,
		key)
	payload = yaml.dump(config)
	flags = 0
	if ephemeral:
		flags = zookeeper.EPHEMERAL
	znode = _znode_path(service_class, service_name, key)
	if connection.exists(znode):
		connection.delete(znode)
	connection.create(znode, payload, zc.zk.OPEN_ACL_UNSAFE, flags)
	get_logger().info("write_distributed_config: %s/%s/%s, Ephemeral: %s" %
		(service_class, service_name, key, ephemeral))
	get_logger().debug("%s" % (config))
	return key
Пример #17
0
	def _process_children_changed(self, children):
		"""
		Callback used for Children object.
		**Not intended for external use.**
		"""
		try:
			new_max = pettingzoo.utils.max_counter(children)
			get_logger().debug(
				"DistributedBag._process_children_changed %s" % (new_max))
			with self.lock:
				while self.max_token < new_max:
					self.max_token += 1
					self._on_new_id(self.max_token)
		except:
			exc_class, exc, tback = sys.exc_info()
			sys.stderr.write(str(exc) + "\n")
			traceback.print_tb(tback)
			raise exc_class, exc, tback
Пример #18
0
def remove_stale_config(connection, service_class, service_name, key):
	"""
	This function manually removes a discovery config from zookeeeper.  This can
	be used either to remove non ephemeral discovery configs, when they are no
	longer valid, or to remove ephemeral discovery configs when you don't wish
	to close the zookeeper connection.  For example when your configs are being
	written by a seperate monitoring process.

	:param connection: a zc.zk.ZooKeeper connection
	:param service_class: the classification of the service \
	  (e.g. mysql, memcached, etc)
	:param service_name: the cluster of the service \
	  (production, staging, etc)
	:param key: the key the specific discovery config was written out as.
	"""
	get_logger().info("remove_stale_config: %s/%s/%s" %
		(service_class, service_name, key))
	connection.delete(_znode_path(service_class, service_name, key))
Пример #19
0
	def _load_znodes(self, path, add_callback=True):
		get_logger().info(
			"DistributedMultiConfig._load_znodes: %s. Callback: %s" %
				(path, add_callback))
		if self.connection.exists(path):
			children = self.connection.children(path)
			if add_callback:
				children(self._child_callback)
				self.children[path] = children
			if len(children) > 0:
				config = []
				for child in children:
					znodep = path + "/" + child
					znode = self.connection.get(znodep)
					single = yaml.load(znode[0])
					config.append((child, single))
				self._store_config_in_cache(path, config)
				return config
Пример #20
0
	def _cleanup_tokens(self, children, max_token):
		"""
		Tokens are used to track the current max token only.  This
		substantially lowers the impact on zookeeper when items are
		added as opposed to tracking children in the /items path,
		especially when /items becomes large.  This method removes
		any errant tokens if it sees any tokens still in the system
		smaller then max_token.
		"""
		for child in children:
			token_id = pettingzoo.utils.counter_value(child)
			if token_id < max_token:
				get_logger().warning(
					"DistributedBag._cleanup_tokens %s" % (token_id))
				try:
					self.connection.adelete(
						id_to_token_path(self.path, token_id))
				except zookeeper.NoNodeException:
					pass # If it doesn't exist, that's ok
Пример #21
0
	def _on_new_id(self, new_id):
		"""
		Called internally when an item is added.
		**Not intended for external use.**
		"""
		try:
			path = id_to_item_path(self.path, new_id)
			get_logger().info("DistributedBag._on_new_id %s" % (path))
			with self.lock:
				self.ids.add(new_id)
				deleted = Deleted(
					self.connection, path, [self._process_deleted])
				self.deletion_handlers[new_id] = deleted
				for callback in self.add_callbacks:
					callback(self, new_id)
		except:
			exc_class, exc, tback = sys.exc_info()
			sys.stderr.write(str(exc_class) + "\n")
			traceback.print_tb(tback)
			raise exc_class, exc, tback
Пример #22
0
	def _populate_ids(self):
		"""Fills out the bag when initial connection to it is made"""
		ichildren = self.connection.children(self.path + ITEM_PATH)
		with self.lock:
			for child in ichildren:
				try:
					new_id = pettingzoo.utils.counter_value(child)
					path = id_to_item_path(self.path, new_id)
					get_logger().info("DistributedBag._on_new_id %s" % (path))
					self.ids.add(new_id)
					deleted = Deleted(
						self.connection, path, [self._process_deleted])
					self.deletion_handlers[new_id] = deleted
					for callback in self.add_callbacks:
						callback(self, new_id)
				except:
					exc_class, exc, tback = sys.exc_info()
					sys.stderr.write(str(exc_class) + "\n")
					traceback.print_tb(tback)
					raise exc_class, exc, tback
Пример #23
0
    def _handle_remove(self, del_id):
        '''
		Called on delete. Updates the candidate by predecessor dict.

		:param del_id: (int) id of deleted candidate
		:rtype: None
		'''

        with self.lock:
            candidate = self.candidate_by_predecessor.get(del_id, None)
            if del_id == None:
                get_logger().warning(
                    "LeaderQueue._handle_remove Unknown candidate %s" %
                    (del_id))
            elif candidate == None:
                get_logger().debug(
                    "LeaderQueue._handle_remove Removed candidate is not" +
                    " a predecessor %s" % (del_id))
            else:
                self._update_predecessor_dict(del_id, candidate)
                del self.candidate_by_predecessor[del_id]
        return None
Пример #24
0
	def _handle_remove(self, del_id): 
		'''
		Called on delete. Updates the candidate by predecessor dict.

		:param del_id: (int) id of deleted candidate
		:rtype: None
		'''

		with self.lock:
			candidate = self.candidate_by_predecessor.get(del_id, None)
			if del_id == None:
				get_logger().warning(
					"LeaderQueue._handle_remove Unknown candidate %s" %
					(del_id))
			elif candidate == None: 
				get_logger().debug(
					"LeaderQueue._handle_remove Removed candidate is not"
					+ " a predecessor %s" % (del_id))
			else:
				self._update_predecessor_dict(del_id, candidate)
				del self.candidate_by_predecessor[del_id]
		return None
Пример #25
0
	def add(self, data, ephemeral=True):
		"""
		Inserts an element into the bag.

		:param data: value stored at element
		:param ephemeral: if true, element is automatically deleted when \
		  backing framework is closed
		:rtype: *int* element id (required for deletion)
		"""
		flags = zookeeper.SEQUENCE
		if ephemeral:
			flags = zookeeper.EPHEMERAL | zookeeper.SEQUENCE
		newpath = self.connection.create(
			self.path + ITEM_PATH + ITEM_PATH,
			data, zc.zk.OPEN_ACL_UNSAFE, flags)
		item_id = pettingzoo.utils.counter_value(newpath)
		get_logger().debug("DistributedBag.add %s: %s" % (item_id, data))
		self.connection.acreate(
			id_to_token_path(self.path, item_id), "", zc.zk.OPEN_ACL_UNSAFE)
		if item_id > 0:
			children = self.connection.children(self.path + TOKEN_PATH)
			self._cleanup_tokens(children, item_id)
		return item_id
Пример #26
0
	def add_candidate(self, candidate, meta_data=None):
		'''
		Adds new candidate to zookeeper and updates.
		Leader queues to reflect new candidate.

		:param candidate: object that represents the candidate in the queue
		:param meta_data: binary data to store on node
		:rtype: True if operation successfully completed. False otherwise
		'''
		#ensure candidate is not already in queue
		if self.has_candidate(candidate):
			get_logger().warning(
				"LeaderQueue.remove_candidate Candidate already in queue.")
			return False
		else:
			#create node in ZK. Node will be deleted when connection is closed
			flags = zookeeper.EPHEMERAL | zookeeper.SEQUENCE
			newpath = self.connection.create(
				self.path + PREFIX + PREFIX,
				meta_data,
				zc.zk.OPEN_ACL_UNSAFE,
				flags)
			counter = pettingzoo.utils.counter_value(newpath)
			get_logger().info("LeaderQueue.add_candidate %s" % (newpath))
			try:
				#create delete watch
				self._create_deletion_handlers(counter)
			except:
				exc_class, exc, tback = sys.exc_info()
				sys.stderr.write(str(exc_class) + "\n")
				traceback.print_tb(tback)
				raise exc_class, exc, tback
			with self.lock:	
				self.counter_by_candidate[candidate] = counter
				self._handle_add(counter, candidate)
			return True
Пример #27
0
	def load_config(self, service_class, service_name, callback=None):
		"""
		Returns a config using the fallback scheme for DistributedDiscovery to
		select a config at random from the available configs for a particular
		service.

		:param service_class: the classification of the service \
		  (e.g. mysql, memcached, etc)
		:param service_name: the cluster of the service \
		  (production, staging, etc)
		:param callback: callback function to call if the config for this \
		  service changes. (Optional)
		:rtype: *list*  The list contains config dicts in the pettingzoo \
		  config format.
		"""
		path = _znode_path(service_class, service_name)
		cached = self._get_config_from_cache(path, callback)
		if cached:
			get_logger().info(
				"DistributedMultiConfig.load_config: %s/%s (cached)" %
					(service_class, service_name))
			rconfig = [
				_set_metadata(
					validate_config(conf[1], service_class),
					service_name, conf[0])
						for conf in cached]
			get_logger().debug("%s" % (rconfig))
			return rconfig
		config = self._load_znodes(path)
		if config:
			get_logger().info(
				"DistributedMultiConfig.load_config: %s/%s (zookeeper)" %
					(service_class, service_name))
			rconfig = [
				_set_metadata(
					validate_config(conf[1], service_class),
					service_name, conf[0])
						for conf in config]
			get_logger().debug("%s" % (rconfig))
			return rconfig
		config = self._load_file_config(service_class, service_name)
		get_logger().info("DistributedMultiConfig.load_config: %s/%s (file)" %
			(service_class, service_name))
		rconfig = [
			_set_metadata(
				validate_config(c, service_class), service_name)
					for c in config]
		get_logger().debug("%s" % (rconfig))
		return rconfig