[Python-modules-commits] [python-vagrant] 02/06: New upstream version 0.5.15

Hans-Christoph Steiner eighthave at moszumanska.debian.org
Mon Jan 8 21:58:17 UTC 2018


This is an automated email from the git hooks/post-receive script.

eighthave pushed a commit to branch master
in repository python-vagrant.

commit c1538d0c9f0756ba5cbb80dc95c20e6ee2b13c7b
Author: Hans-Christoph Steiner <hans at eds.org>
Date:   Mon Jan 8 22:05:42 2018 +0100

    New upstream version 0.5.15
---
 CHANGELOG.md                     |  11 ++++
 PKG-INFO                         |   2 +-
 python_vagrant.egg-info/PKG-INFO |   2 +-
 tests/test_vagrant.py            |  54 +++++++++++++++++++-
 vagrant/__init__.py              | 108 ++++++++++++++++++++++++++++++---------
 5 files changed, 150 insertions(+), 27 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95ceafb..3633673 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,17 @@
 This document lists the changes (and individuals who contributed to those
 changes) for each release of python-vagrant.
 
+## 0.5.15
+
+- Pull Request #54: Create ssh() method to run shell commands in a VM
+  Authors: Parker Thompson (https://github.com/mothran) and Todd DeLuca
+  (https://github.com/todddeluca)
+- Pull Request #56: Return generator for `up` and `reload` output lines to
+  avoid having entire output in memory.
+  Authors: mmabey (https://github.com/mmabey) and Todd DeLuca
+  (https://github.com/todddeluca)
+  
+
 ## 0.5.14
 
 - Pull Request #51: Add support for the vagrant package command.
diff --git a/PKG-INFO b/PKG-INFO
index 7fadceb..7a2fbcd 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: python-vagrant
-Version: 0.5.14
+Version: 0.5.15
 Summary: Python bindings for interacting with Vagrant virtual machines.
 Home-page: https://github.com/todddeluca/python-vagrant
 Author: Todd Francis DeLuca
diff --git a/python_vagrant.egg-info/PKG-INFO b/python_vagrant.egg-info/PKG-INFO
index 7fadceb..7a2fbcd 100644
--- a/python_vagrant.egg-info/PKG-INFO
+++ b/python_vagrant.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: python-vagrant
-Version: 0.5.14
+Version: 0.5.15
 Summary: Python bindings for interacting with Vagrant virtual machines.
 Home-page: https://github.com/todddeluca/python-vagrant
 Author: Todd Francis DeLuca
diff --git a/tests/test_vagrant.py b/tests/test_vagrant.py
index 21297ed..7b92c1d 100644
--- a/tests/test_vagrant.py
+++ b/tests/test_vagrant.py
@@ -23,7 +23,7 @@ import subprocess
 import sys
 import tempfile
 import time
-from nose.tools import eq_, ok_, with_setup
+from nose.tools import eq_, ok_, with_setup, assert_raises
 
 import vagrant
 from vagrant import compat
@@ -520,6 +520,58 @@ def test_multivm_config():
         eq_(keyfile, parsed_config["IdentityFile"].lstrip('"').rstrip('"'))
 
 
+ at with_setup(make_setup_vm(), teardown_vm)
+def test_ssh_command():
+    '''
+    Test executing a command via ssh on a vm.
+    '''
+    v = vagrant.Vagrant(TD)
+    v.up()
+    output = v.ssh(command='echo hello')
+    assert output.strip() == 'hello'
+
+
+ at with_setup(make_setup_vm(MULTIVM_VAGRANTFILE), teardown_vm)
+def test_ssh_command_multivm():
+    '''
+    Test executing a command via ssh on a specific vm
+    '''
+    v = vagrant.Vagrant(TD)
+    v.up()
+    output = v.ssh(vm_name=VM_1, command='echo hello')
+    assert output.strip() == 'hello'
+    output = v.ssh(vm_name=VM_2, command='echo I like your hat')
+    assert output.strip() == 'I like your hat'
+
+
+ at with_setup(make_setup_vm(), teardown_vm)
+def test_streaming_output():
+    """
+    Test streaming output of up or reload.
+    """
+    test_string = 'Waiting for machine to boot.'
+    v = vagrant.Vagrant(TD)
+
+    with assert_raises(subprocess.CalledProcessError):
+        v.up(vm_name='incorrect-name')
+
+    streaming_up = False
+    for line in v.up(stream_output=True):
+        print('output line:', line)
+        if test_string in line:
+            streaming_up = True
+
+    assert streaming_up
+
+    streaming_reload = False
+    for line in v.reload(stream_output=True):
+        print('output line:', line)
+        if test_string in line:
+            streaming_reload = True
+
+    assert streaming_reload
+
+
 def test_make_file_cm():
     filename = os.path.join(TD, 'test.log')
     if os.path.exists(filename):
diff --git a/vagrant/__init__.py b/vagrant/__init__.py
index cdd93b8..75d5e7c 100644
--- a/vagrant/__init__.py
+++ b/vagrant/__init__.py
@@ -26,7 +26,7 @@ from . import compat
 
 # python package version
 # should match r"^__version__ = '(?P<version>[^']+)'$" for setup.py
-__version__ = '0.5.14'
+__version__ = '0.5.15'
 
 
 log = logging.getLogger(__name__)
@@ -227,8 +227,8 @@ class Vagrant(object):
                  env=None, out_cm=None, err_cm=None):
         '''
         root: a directory containing a file named Vagrantfile.  Defaults to
-        os.getcwd(). This is the directory and Vagrantfile that the Vagrant
-        instance will operate on.
+          os.getcwd(). This is the directory and Vagrantfile that the Vagrant
+          instance will operate on.
         env: a dict of environment variables (string keys and values) passed to
           the vagrant command subprocess or None.  Defaults to None.  If env is
           None, `subprocess.Popen` uses the current process environment.
@@ -300,17 +300,24 @@ class Vagrant(object):
         self._call_vagrant_command(['init', box_name, box_url])
 
     def up(self, no_provision=False, provider=None, vm_name=None,
-           provision=None, provision_with=None):
+           provision=None, provision_with=None, stream_output=False):
         '''
-        Launch the Vagrant box.
+        Invoke `vagrant up` to start a box or boxes, possibly streaming the
+        command output.
         vm_name=None: name of VM.
         provision_with: optional list of provisioners to enable.
         provider: Back the machine with a specific provider
         no_provision: if True, disable provisioning.  Same as 'provision=False'.
         provision: optional boolean.  Enable or disable provisioning.  Default
           behavior is to use the underlying vagrant default.
+        stream_output: if True, return a generator that yields each line of the
+          output of running the command.  Consume the generator or the
+          subprocess might hang.  if False, None is returned and the command
+          is run to completion without streaming the output.  Defaults to
+          False.
         Note: If provision and no_provision are not None, no_provision will be
         ignored.
+        returns: None or a generator yielding lines of output.
         '''
         provider_arg = '--provider=%s' % provider if provider else None
         prov_with_arg = None if provision_with is None else '--provision-with'
@@ -323,15 +330,14 @@ class Vagrant(object):
         no_provision_arg = '--no-provision' if no_provision else None
         provision_arg = None if provision is None else '--provision' if provision else '--no-provision'
 
-        self._call_vagrant_command(['up', vm_name, no_provision_arg,
-                                   provision_arg, provider_arg,
-                                   prov_with_arg, providers_arg])
-        try:
-            self.conf(vm_name=vm_name)  # cache configuration
-        except subprocess.CalledProcessError:
-            # in multi-VM environments, up() can be used to start all VMs,
-            # however vm_name is required for conf() or ssh_config().
-            pass
+        args = ['up', vm_name, no_provision_arg, provision_arg, provider_arg, prov_with_arg, providers_arg]
+        if stream_output:
+            generator = self._stream_vagrant_command(args)
+        else:
+            self._call_vagrant_command(args)
+
+        self._cached_conf[vm_name] = None  # remove cached configuration
+        return generator if stream_output else None
 
     def provision(self, vm_name=None, provision_with=None):
         '''
@@ -345,25 +351,40 @@ class Vagrant(object):
         self._call_vagrant_command(['provision', vm_name, prov_with_arg,
                                    providers_arg])
 
-    def reload(self, vm_name=None, provision=None, provision_with=None):
+    def reload(self, vm_name=None, provision=None, provision_with=None,
+               stream_output=False):
         '''
         Quoting from Vagrant docs:
         > The equivalent of running a halt followed by an up.
-
-        > 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.
-
-        > The configured provisioners will not run again, by default. You can force the provisioners to re-run by specifying the --provision flag.
+        > 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.
+        > The configured provisioners will not run again, by default. You can
+          force the provisioners to re-run by specifying the --provision flag.
 
         provision: optional boolean.  Enable or disable provisioning.  Default
           behavior is to use the underlying vagrant default.
         provision_with: optional list of provisioners to enable.
           e.g. ['shell', 'chef_solo']
+        stream_output: if True, return a generator that yields each line of the
+          output of running the command.  Consume the generator or the
+          subprocess might hang.  if False, None is returned and the command
+          is run to completion without streaming the output.  Defaults to
+          False.
+        returns: None or a generator yielding lines of output.
         '''
         prov_with_arg = None if provision_with is None else '--provision-with'
         providers_arg = None if provision_with is None else ','.join(provision_with)
         provision_arg = None if provision is None else '--provision' if provision else '--no-provision'
-        self._call_vagrant_command(['reload', vm_name, provision_arg,
-                                   prov_with_arg, providers_arg])
+
+        args = ['reload', vm_name, provision_arg, prov_with_arg, providers_arg]
+        if stream_output:
+            generator = self._stream_vagrant_command(args)
+        else:
+            self._call_vagrant_command(args)
+
+        self._cached_conf[vm_name] = None  # remove cached configuration
+        return generator if stream_output else None
 
     def suspend(self, vm_name=None):
         '''
@@ -728,14 +749,24 @@ class Vagrant(object):
         '''
         self._call_vagrant_command(['snapshot', 'delete', name])
 
+    def ssh(self, vm_name=None, command=None, extra_ssh_args=None):
+        '''
+        Execute a command via ssh on the vm specified.
+        command: The command to execute via ssh.
+        extra_ssh_args: Corresponds to '--' option in the vagrant ssh command
+        Returns the output of running the command.
+        '''
+        cmd = ['ssh', vm_name, '--command', command]
+        if extra_ssh_args is not None:
+            cmd += ['--', extra_ssh_args]
+
+        return self._run_vagrant_command(cmd)
+
     def _parse_box_list(self, output):
         '''
         Remove Vagrant usage for unit testing
         '''
         # Parse box list output
-        # Cue snarky comment about how nice it would be if vagrant used JSON
-        # or even had a description of the machine readable output for each
-        # command
 
         boxes = []
         # initialize box values
@@ -944,6 +975,35 @@ class Vagrant(object):
             return compat.decode(subprocess.check_output(command, cwd=self.root,
                                                env=self.env, stderr=err_fh))
 
+    def _stream_vagrant_command(self, args):
+        """
+        Execute a vagrant command, returning a generator of the output lines.
+        Caller should consume the entire generator to avoid the hanging the
+        subprocess.
+
+        :param args: Arguments for the Vagrant command.
+        :return: generator that yields each line of the command stdout.
+        :rtype: generator iterator
+        """
+        py3 = sys.version_info > (3, 0)
+
+        # Make subprocess command
+        command = self._make_vagrant_command(args)
+        with self.err_cm() as err_fh:
+            sp_args = dict(args=command, cwd=self.root, env=self.env,
+                           stdout=subprocess.PIPE, stderr=err_fh, bufsize=1)
+
+            # Iterate over output lines.
+            # See http://stackoverflow.com/questions/2715847/python-read-streaming-input-from-subprocess-communicate#17698359
+            p = subprocess.Popen(**sp_args)
+            with p.stdout:
+                for line in iter(p.stdout.readline, b''):
+                    yield compat.decode(line) # if PY3 decode bytestrings
+            p.wait()
+            # Raise CalledProcessError for consistency with _call_vagrant_command
+            if p.returncode != 0:
+                raise subprocess.CalledProcessError(p.returncode, command)
+
 
 class SandboxVagrant(Vagrant):
     '''

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-vagrant.git



More information about the Python-modules-commits mailing list