fab-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Fab-user] Wait for parallel task to complete


From: Morgan Goose
Subject: Re: [Fab-user] Wait for parallel task to complete
Date: Thu, 10 May 2012 13:58:42 -0700

Just asked about this, and I was wrong. But you can also use:

execute(parallel(foo), ...)

as the parallel decorator can just be called as a wrapping function.

-goose

On Thu, May 10, 2012 at 1:54 PM, Morgan Goose <address@hidden> wrote:
> Hmm, I think perhaps I may have been wrong that it takes all env
> params. Will look and see if there is a bug or feature request for
> that. But decorating @parallel should still be honored.
>
> -goose
>
> On Thu, May 10, 2012 at 11:16 AM, anatoly techtonik <address@hidden> wrote:
>> Hi Morgan,
>>
>> Thanks for the advice, but keyword arguments from the environment
>> don't work with execute(). The code below gives a traceback:
>>
>> @task
>> def test():
>>  pass
>> @task
>> def deploy():
>>   execute(test, parallel=True)
>>
>> Traceback (most recent call last):
>>  File "/usr/lib/python2.7/site-packages/fabric/main.py", line 712, in main
>>    *args, **kwargs
>>  File "/usr/lib/python2.7/site-packages/fabric/tasks.py", line 327, in 
>> execute
>>    results['<local-only>'] = task.run(*args, **new_kwargs)
>>  File "/usr/lib/python2.7/site-packages/fabric/tasks.py", line 112, in run
>>    return self.wrapped(*args, **kwargs)
>>  File "/prdir/fabfile.py", line 95, in deploy
>>    execute(test, parallel=True)
>>  File "/usr/lib/python2.7/site-packages/fabric/tasks.py", line 327, in 
>> execute
>>    results['<local-only>'] = task.run(*args, **new_kwargs)
>>  File "/usr/lib/python2.7/site-packages/fabric/tasks.py", line 112, in run
>>    return self.wrapped(*args, **kwargs)
>> TypeError: test() takes no arguments (1 given)
>>
>>
>> On Wed, May 2, 2012 at 2:02 AM, Morgan Goose <address@hidden> wrote:
>>> Notice the section in
>>> http://docs.fabfile.org/en/1.4.1/api/core/tasks.html#fabric.tasks.execute
>>> where it mentions that "Any other arguments or keyword arguments will
>>> be passed verbatim into task when it is called, so execute(mytask,
>>> 'arg1', kwarg1='value') will (once per host) invoke mytask('arg1',
>>> kwarg1='value')."
>>>
>>> It is refering, albeit not clearly, to the fact that any keyword that
>>> is given will be used as if it were a member of the env dict. So you
>>> can use any of these env var names to specify things, eg:
>>> parallel=True; 
>>> http://docs.fabfile.org/en/1.4.1/usage/env.html#full-list-of-env-vars
>>>
>>> Also I fumbled the name of the decorator. It's serial:
>>> http://docs.fabfile.org/en/1.4.1/api/core/decorators.html#fabric.decorators.serial
>>>
>>> You can also specify custom args to fab tasks:
>>> http://docs.fabfile.org/en/1.4.1/usage/fab.html#per-task-arguments
>>>
>>> And with that you could make the deploy task take a role name, or list
>>> of hosts to then use as the lists, local and remote, to use in your
>>> script.
>>>
>>> -goose
>>>
>>> On Tue, May 1, 2012 at 12:01 PM, anatoly techtonik <address@hidden> wrote:
>>>> Hi Morgan,
>>>>
>>>> I've tried your example, which is:
>>>>
>>>>  local = ['a','b']
>>>>  remote = ['c','d']
>>>>
>>>>  def test:pass
>>>>  def update:pass
>>>>
>>>> address@hidden
>>>> address@hidden
>>>>  def deploy():
>>>>     execute(test, hosts=local+remote)
>>>>     execute(update, hosts=local+remote)
>>>>
>>>> At first it didn't run at all with Fabric 1.4.1, unable to call
>>>> @sequential decorator:
>>>>
>>>>  Traceback (most recent call last):
>>>>    ...
>>>>    File "/checkout83/fabfile.py", line 20, in <module>
>>>>     address@hidden
>>>>  NameError: name 'sequential' is not defined
>>>>
>>>> I commented the @sequential, after which `fab deploy` run sequentially:
>>>>  local test
>>>>  remote test
>>>>  local update
>>>>  remote update
>>>>
>>>> That's much better, but it still impossible to specify hosts from
>>>> command line. `fab -H local deploy` still runs additional command on
>>>> the remote, and `fab -H local,remote deploy` run everything twice on
>>>> both hosts.
>>>>
>>>> I couldn't find parallel among params for execute -
>>>> http://docs.fabfile.org/en/1.4.1/api/core/tasks.html#fabric.tasks.execute
>>>> so I've just decorated functions:
>>>>
>>>>  local = ['a','b']
>>>>  remote = ['c','d']
>>>>
>>>> address@hidden
>>>> address@hidden
>>>>  def test:pass
>>>> address@hidden
>>>> address@hidden
>>>>  def update:pass
>>>>
>>>> address@hidden
>>>>  def deploy():
>>>>     execute(test, hosts=local+remote)
>>>>     execute(update, hosts=local+remote)
>>>>
>>>> I must admit that although not ideally, but this works. I just need to
>>>> make sure that deploy is always called with empty host list and I need
>>>> to find a way to specify hosts from command line for the subtasks.
>>>> --
>>>> anatoly t.
>>>>
>>>>
>>>> On Fri, Apr 27, 2012 at 11:48 PM, Morgan Goose <address@hidden> wrote:
>>>>> The example I gave you and how to run it did all of those things, sans
>>>>> the parallel. Execute has parallel as a param. Use that in your master
>>>>> task's executes, and you'll have everything. If that still isn't what
>>>>> you're looking for, we're going to need more information. Eg, like you
>>>>> did before where you said how you're seeing it run, and how you'd
>>>>> instead like it to run, complete with how you ran it with fab, and how
>>>>> the fabfile looks.
>>>>>
>>>>> -goose
>>>>>
>>>>> On Thu, Apr 26, 2012 at 10:32 PM, anatoly techtonik <address@hidden> 
>>>>> wrote:
>>>>>> Thanks for the explanation. I read the tutorial. Still I see I can't
>>>>>> construct the system that will satisfy the following usability
>>>>>> requirements:
>>>>>>
>>>>>>  1. Define (or override) hosts from command line
>>>>>>  2. Execute each scheduled task in parallel
>>>>>>  3. Wait until previous task completes successfully on all servers
>>>>>> before moving to the next one
>>>>>>  4. Ability to run each task separately or use master task as a helper 
>>>>>> command
>>>>>>
>>>>>> Command line hosts (1) is needed to test new nodes and control
>>>>>> deployment from 3rd party application.
>>>>>> Parallel execution (2) and (3) is critical to minimize servers downtime.
>>>>>> Master task (4) is also highly desired, because full deployment process
>>>>>> contains a lot of steps.
>>>>>>
>>>>>> It seems that this could be real with the @mastertask decorator that
>>>>>> would make task run without servers at all. With it the following
>>>>>> could work as expected.
>>>>>>
>>>>>> @task
>>>>>> @parallel
>>>>>> def test:pass
>>>>>>
>>>>>> @task
>>>>>> @parallel
>>>>>> def update:pass
>>>>>>
>>>>>> @mastertask
>>>>>> def deploy():
>>>>>>   execute(test)
>>>>>>   execute(update)
>>>>>>
>>>>>> --
>>>>>> anatoly t.
>>>>>>
>>>>>>
>>>>>> On Fri, Apr 27, 2012 at 1:28 AM, Morgan Goose <address@hidden> wrote:
>>>>>>> I just read that last line, where you said you wanted to run them with
>>>>>>> hosts defined at runtime. You can either use functions to generate the
>>>>>>> hosts lists or with said example i gave change it up to get rid of the
>>>>>>> deploy task, and then run the fabfile like:
>>>>>>>    $ fab -H local,remote test update
>>>>>>>
>>>>>>> As that will be sequential and honor the host list as well. It will
>>>>>>> also run the test on all hosts before moving to the next task listed,
>>>>>>> update.
>>>>>>>
>>>>>>> -goose
>>>>>>>
>>>>>>> On Thu, Apr 26, 2012 at 3:25 PM, Morgan Goose <address@hidden> wrote:
>>>>>>>> First have you read the tutorial? This is the section that will get
>>>>>>>> you in on host list defining, and point you the the more detailed
>>>>>>>> information that I link to below it:
>>>>>>>> http://docs.fabfile.org/en/1.4.1/tutorial.html#defining-connections-beforehand
>>>>>>>> http://docs.fabfile.org/en/1.4.1/usage/execution.html#host-lists
>>>>>>>>
>>>>>>>> As to what you want to do. Each task will loop over it's list of hosts
>>>>>>>> to run on. So top down it's TaskA over all HostListA's hosts. Then if
>>>>>>>> no errors move to the other task with the same or unique host list
>>>>>>>> (defined either globally or per task).
>>>>>>>>
>>>>>>>> From what you're wanting to do you will construct the fabfile like 
>>>>>>>> this:
>>>>>>>>
>>>>>>>> local = ['a','b']
>>>>>>>> remote = ['c','d']
>>>>>>>>
>>>>>>>> def test:pass
>>>>>>>> def update:pass
>>>>>>>>
>>>>>>>> @task
>>>>>>>> @sequential
>>>>>>>> def deploy():
>>>>>>>>    execute(test, hosts=local+remote)
>>>>>>>>    execute(update, hosts=local+remote)
>>>>>>>>
>>>>>>>>
>>>>>>>> Then all hosts will have the test task run on them before moving to
>>>>>>>> looping over the two hosts lists and running the update. You then call
>>>>>>>> it simply with:
>>>>>>>>
>>>>>>>> $ fab deploy
>>>>>>>>
>>>>>>>> And you never run the test and update tasks themselves. Nor do you
>>>>>>>> define anything to be parallel. As parallel runs on the task would
>>>>>>>> test on both hosts in parallel, and then deploy to both hosts in
>>>>>>>> parallel. This was, running sequential and explicitly defined to do
>>>>>>>> so, will fail fast on the test hosts if one dies. Because the host
>>>>>>>> list's order is honored.
>>>>>>>>
>>>>>>>> -goose
>>>>>>>>
>>>>>>>> On Wed, Apr 25, 2012 at 1:13 PM, anatoly techtonik <address@hidden> 
>>>>>>>> wrote:
>>>>>>>>> On Sat, Apr 21, 2012 at 7:51 PM, Jeff Forcier <address@hidden> wrote:
>>>>>>>>>> On Fri, Apr 20, 2012 at 5:31 AM, anatoly techtonik <address@hidden> 
>>>>>>>>>> wrote:
>>>>>>>>>>
>>>>>>>>>>> Is it possible in fabric to wait until a subtask completes on all
>>>>>>>>>>> servers successfully before moving to next step?
>>>>>>>>>>
>>>>>>>>>> You need to use execute() to treat subroutines as if they were full
>>>>>>>>>> fledged tasks. execute() is the machinery that says "take this
>>>>>>>>>> function and run it once per host in this list of hosts." Right now
>>>>>>>>>> that machinery is just applying implicitly to your deploy() task and
>>>>>>>>>> you're probably using env.hosts to set your host list.
>>>>>>>>>>
>>>>>>>>>> Remove env.hosts and maybe make that host list a role in 
>>>>>>>>>> env.roledefs.
>>>>>>>>>> Then you can do this:
>>>>>>>>>>
>>>>>>>>>>    env.roledefs = {'myrole': ['a', 'b', 'c']}
>>>>>>>>>>
>>>>>>>>>>    def test(): ...
>>>>>>>>>>    def update(): ...
>>>>>>>>>>
>>>>>>>>>>    def deploy():
>>>>>>>>>>        execute(test, role='myrole')
>>>>>>>>>>        execute(update, role='myrole')
>>>>>>>>>>
>>>>>>>>>> That should have the effect you want: "fab deploy" => first test()
>>>>>>>>>> runs once per host, and when it's all done, update() will run once 
>>>>>>>>>> per
>>>>>>>>>> host. deploy() itself will end up running only one time total -- it's
>>>>>>>>>> just a "meta" task now.
>>>>>>>>>
>>>>>>>>> It took time to realize the that execute() iterates over the global
>>>>>>>>> list of hosts. I expected that the following two to be equivalent, but
>>>>>>>>> they were not:
>>>>>>>>>
>>>>>>>>>  1. fab -H local,remote test update
>>>>>>>>>  2. fab -H local,remote deploy
>>>>>>>>>
>>>>>>>>> I used the script without roles:
>>>>>>>>>
>>>>>>>>>  def test(): ...
>>>>>>>>>  def update(): ...
>>>>>>>>>  def deploy():
>>>>>>>>>      execute(test)
>>>>>>>>>      execute(update)
>>>>>>>>>
>>>>>>>>> 1st execution variant is fully synchronous (i.e. next task doesn't
>>>>>>>>> start until previous finishes) and gave the sequence:
>>>>>>>>>
>>>>>>>>>  local test
>>>>>>>>>  local update
>>>>>>>>>  remote test
>>>>>>>>>  remove update
>>>>>>>>>
>>>>>>>>> but the 2nd variant with subtasks was confusing (I indented to see
>>>>>>>>> what's going on):
>>>>>>>>>
>>>>>>>>>  local deploy
>>>>>>>>>    local test
>>>>>>>>>    remote test
>>>>>>>>>    local update
>>>>>>>>>    remote update
>>>>>>>>>  remote deploy
>>>>>>>>>    local test
>>>>>>>>>    remote test
>>>>>>>>>    local update
>>>>>>>>>    remote update
>>>>>>>>>
>>>>>>>>> I found fabric pretty counter-intuitive in this case. I tried to fix
>>>>>>>>> that without roles by explicitly passing current host:
>>>>>>>>>
>>>>>>>>>  def test(): ...
>>>>>>>>>  def update(): ...
>>>>>>>>>  def deploy():
>>>>>>>>>      execute(test, host=env.host_string)
>>>>>>>>>      execute(update, host=env.host_string)
>>>>>>>>>
>>>>>>>>> This gives:
>>>>>>>>>  local deploy
>>>>>>>>>    local test
>>>>>>>>>    local update
>>>>>>>>>  remote deploy
>>>>>>>>>    remote test
>>>>>>>>>    remove update
>>>>>>>>>
>>>>>>>>> Still not the desired behavior. The desired is:
>>>>>>>>>  deploy
>>>>>>>>>    local test
>>>>>>>>>    remote test
>>>>>>>>>    wait
>>>>>>>>>    local update
>>>>>>>>>    remote update
>>>>>>>>>
>>>>>>>>> I've tried using @parallel decorator for deploy task and it seemed to
>>>>>>>>> work fine at first.
>>>>>>>>>  local deploy
>>>>>>>>>  remote deploy
>>>>>>>>>  remote test
>>>>>>>>>  local test
>>>>>>>>>  local update
>>>>>>>>>  remote update
>>>>>>>>>
>>>>>>>>> But the test step didn't not synchronize - local update executed while
>>>>>>>>> remote test was still running. It looks like the roles is the only
>>>>>>>>> chance, but I'd like to avoid hardcoding server names at all costs. Is
>>>>>>>>> it possible?
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> anatoly t.
>>>>>>>>>
>>>>>>>>> _______________________________________________
>>>>>>>>> Fab-user mailing list
>>>>>>>>> address@hidden
>>>>>>>>> https://lists.nongnu.org/mailman/listinfo/fab-user



reply via email to

[Prev in Thread] Current Thread [Next in Thread]