def feed(self): """(Greedy) Returns this FeedTask's feed (list of wrappers). The first access of this property triggers a GET of the feed if it has not already been fetched, so use this only if you need the EntryWrappers outside of the execution itself. """ if self._feed is None: self._feed = self._getter.get() if len(self._feed) == 0: raise ex.FeedTaskEmptyFeed() # Do we need to refresh the feed based on having been run? # If we haven't replicated WrapperTasks yet, there's no chance we're # out of sync - and we don't want to trigger GET/replication. if self._tx_by_uuid: # Rebuild the entire feed from the WrapperTasks' .wrappers. # TAG_WRAPPER_SYNC # Note that, if this happens while the WrapperTasks are running, # we may be grabbing the wrapper from a WrapperTask "while" it is # being changed as the result of an update(). This is threadsafe as # long as the assignment (by WrapperTask.execute) and the accessor # (WrapperTask.wrapper) remain atomic by using simple =/return. for wrap in self._feed: if self.get_wrapper(wrap.uuid).etag != wrap.etag: # Refresh needed self._feed = [tx.wrapper for tx in self.wrapper_tasks.values()] break return self._feed
def __init__(self, name, feed_or_getter, max_workers=10, update_timeout=-1): """Create a FeedTask with a FeedGetter (preferred) or existing feed. :param name: A descriptive string name. This will be used along with each wrapper's UUID to generate the name for that wrapper's WrapperTask. :param feed_or_getter: pypowervm.wrappers.entry_wrapper.FeedGetter or an already-fetched feed (list of EntryWrappers) over which to operate. :param max_workers: (Optional) Integer indicating the maximum number of worker threads to run in parallel within the .flow or by the .execute method. See concurrent.futures.ThreadPoolExecutor(max_workers). :param update_timeout: (Optional) Integer number of seconds after which to time each WrapperTask's POST request. -1, the default, causes the request to use the timeout value configured on the Session belonging to the Adapter. """ super(FeedTask, self).__init__(name) if isinstance(feed_or_getter, ewrap.FeedGetter): self._feed = None self._getter = feed_or_getter elif isinstance(feed_or_getter, list): # Make sure the feed has something in it. if len(feed_or_getter) == 0: raise ex.FeedTaskEmptyFeed() # Make sure it's a list of EntryWrapper if [ i for i in feed_or_getter if not isinstance(i, ewrap.EntryWrapper) ]: raise ValueError("List must contain EntryWrappers " "exclusively.") self._feed = feed_or_getter self._getter = None else: raise ValueError( _("Must supply either a list of EntryWrappers or " "a FeedGetter.")) # Max WrapperTasks to run in parallel self.max_workers = max_workers self.update_timeout = update_timeout # Map of {uuid: WrapperTask}. We keep this empty until we need the # individual WraperTasks. This is triggered by .wrapper_tasks and # .get_wrapper(uuid) (and obviously executing). self._tx_by_uuid = {} # Until we *need* individual WrapperTasks, save subtasks in one place. # EntryWrapperGetter is a cheat to allow us to build the WrapperTask. self._common_tx = WrapperTask( 'internal', ewrap.EntryWrapperGetter(None, ewrap.Wrapper, None)) self._post_exec = []