2626
2727# python package version
2828# should match r"^__version__ = '(?P<version>[^']+)'$" for setup.py
29- __version__ = '0.5.14 '
29+ __version__ = '0.5.15 '
3030
3131
3232log = logging .getLogger (__name__ )
@@ -300,17 +300,24 @@ def init(self, box_name=None, box_url=None):
300300 self ._call_vagrant_command (['init' , box_name , box_url ])
301301
302302 def up (self , no_provision = False , provider = None , vm_name = None ,
303- provision = None , provision_with = None ):
303+ provision = None , provision_with = None , stream_output = False ):
304304 '''
305- Launch the Vagrant box.
305+ Invoke `vagrant up` to start a box or boxes, possibly streaming the
306+ command output.
306307 vm_name=None: name of VM.
307308 provision_with: optional list of provisioners to enable.
308309 provider: Back the machine with a specific provider
309310 no_provision: if True, disable provisioning. Same as 'provision=False'.
310311 provision: optional boolean. Enable or disable provisioning. Default
311312 behavior is to use the underlying vagrant default.
313+ stream_output: if True, return a generator that yields each line of the
314+ output of running the command. Consume the generator or the
315+ subprocess might hang. if False, None is returned and the command
316+ is run to completion without streaming the output. Defaults to
317+ False.
312318 Note: If provision and no_provision are not None, no_provision will be
313319 ignored.
320+ returns: None or a generator yielding lines of output.
314321 '''
315322 provider_arg = '--provider=%s' % provider if provider else None
316323 prov_with_arg = None if provision_with is None else '--provision-with'
@@ -323,15 +330,14 @@ def up(self, no_provision=False, provider=None, vm_name=None,
323330 no_provision_arg = '--no-provision' if no_provision else None
324331 provision_arg = None if provision is None else '--provision' if provision else '--no-provision'
325332
326- self ._call_vagrant_command (['up' , vm_name , no_provision_arg ,
327- provision_arg , provider_arg ,
328- prov_with_arg , providers_arg ])
329- try :
330- self .conf (vm_name = vm_name ) # cache configuration
331- except subprocess .CalledProcessError :
332- # in multi-VM environments, up() can be used to start all VMs,
333- # however vm_name is required for conf() or ssh_config().
334- pass
333+ args = ['up' , vm_name , no_provision_arg , provision_arg , provider_arg , prov_with_arg , providers_arg ]
334+ if stream_output :
335+ generator = self ._stream_vagrant_command (args )
336+ else :
337+ self ._call_vagrant_command (args )
338+
339+ self ._cached_conf [vm_name ] = None # remove cached configuration
340+ return generator if stream_output else None
335341
336342 def provision (self , vm_name = None , provision_with = None ):
337343 '''
@@ -345,25 +351,40 @@ def provision(self, vm_name=None, provision_with=None):
345351 self ._call_vagrant_command (['provision' , vm_name , prov_with_arg ,
346352 providers_arg ])
347353
348- def reload (self , vm_name = None , provision = None , provision_with = None ):
354+ def reload (self , vm_name = None , provision = None , provision_with = None ,
355+ stream_output = False ):
349356 '''
350357 Quoting from Vagrant docs:
351358 > The equivalent of running a halt followed by an up.
352-
353- > This command is usually required for changes made in the Vagrantfile to take effect. After making any modifications to the Vagrantfile, a reload should be called.
354-
355- > The configured provisioners will not run again, by default. You can force the provisioners to re-run by specifying the --provision flag.
359+ > This command is usually required for changes made in the Vagrantfile
360+ to take effect. After making any modifications to the Vagrantfile, a
361+ reload should be called.
362+ > The configured provisioners will not run again, by default. You can
363+ force the provisioners to re-run by specifying the --provision flag.
356364
357365 provision: optional boolean. Enable or disable provisioning. Default
358366 behavior is to use the underlying vagrant default.
359367 provision_with: optional list of provisioners to enable.
360368 e.g. ['shell', 'chef_solo']
369+ stream_output: if True, return a generator that yields each line of the
370+ output of running the command. Consume the generator or the
371+ subprocess might hang. if False, None is returned and the command
372+ is run to completion without streaming the output. Defaults to
373+ False.
374+ returns: None or a generator yielding lines of output.
361375 '''
362376 prov_with_arg = None if provision_with is None else '--provision-with'
363377 providers_arg = None if provision_with is None else ',' .join (provision_with )
364378 provision_arg = None if provision is None else '--provision' if provision else '--no-provision'
365- self ._call_vagrant_command (['reload' , vm_name , provision_arg ,
366- prov_with_arg , providers_arg ])
379+
380+ args = ['reload' , vm_name , provision_arg , prov_with_arg , providers_arg ]
381+ if stream_output :
382+ generator = self ._stream_vagrant_command (args )
383+ else :
384+ self ._call_vagrant_command (args )
385+
386+ self ._cached_conf [vm_name ] = None # remove cached configuration
387+ return generator if stream_output else None
367388
368389 def suspend (self , vm_name = None ):
369390 '''
@@ -954,6 +975,35 @@ def _run_vagrant_command(self, args):
954975 return compat .decode (subprocess .check_output (command , cwd = self .root ,
955976 env = self .env , stderr = err_fh ))
956977
978+ def _stream_vagrant_command (self , args ):
979+ """
980+ Execute a vagrant command, returning a generator of the output lines.
981+ Caller should consume the entire generator to avoid the hanging the
982+ subprocess.
983+
984+ :param args: Arguments for the Vagrant command.
985+ :return: generator that yields each line of the command stdout.
986+ :rtype: generator iterator
987+ """
988+ py3 = sys .version_info > (3 , 0 )
989+
990+ # Make subprocess command
991+ command = self ._make_vagrant_command (args )
992+ with self .err_cm () as err_fh :
993+ sp_args = dict (args = command , cwd = self .root , env = self .env ,
994+ stdout = subprocess .PIPE , stderr = err_fh , bufsize = 1 )
995+
996+ # Iterate over output lines.
997+ # See http://stackoverflow.com/questions/2715847/python-read-streaming-input-from-subprocess-communicate#17698359
998+ p = subprocess .Popen (** sp_args )
999+ with p .stdout :
1000+ for line in iter (p .stdout .readline , b'' ):
1001+ yield compat .decode (line ) # if PY3 decode bytestrings
1002+ p .wait ()
1003+ # Raise CalledProcessError for consistency with _call_vagrant_command
1004+ if p .returncode != 0 :
1005+ raise subprocess .CalledProcessError (p .returncode , command )
1006+
9571007
9581008class SandboxVagrant (Vagrant ):
9591009 '''
0 commit comments