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: anatoly techtonik
Subject: Re: [Fab-user] Wait for parallel task to complete
Date: Thu, 10 May 2012 21:16:52 +0300

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]