We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Vagrant provider repeatedly errors on formation if node dir is deleted
Needs to be more robust in some error cases such as this one:
1) Provision a controller but somehow forget to add _deis-controler_ to the admins group, despite all documentation and fuschia-colored warnings at the command-line
2) Create a formation and scale it upward, e.g. `deis nodes:scale form1 runtime=2`
3) Try to scale down the formation, get an appropriate error about "couldn't remove chef node"
4) All subsequent formation commands--including destroy!--will fail when trying to access the local vagrant node dir, which apparently was removed in step 3).

This shouldn't happen often, but it can and I think ignoring this error at least in the case of `deis formations:destroy` would provide a way out of this dead end.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `provider/vagrant.py`
Content:
```
1 """
2 Deis cloud provider implementation for local vagrant setups.
3 """
4 
5 from __future__ import unicode_literals
6 
7 from api.ssh import exec_ssh, connect_ssh
8 
9 import json
10 import logging
11 import string
12 import subprocess
13 import uuid
14 
15 from api.models import Layer
16 from api.models import Node
17 
18 logger = logging.getLogger(__name__)
19 
20 # Collect details for connecting to the host machine
21 try:
22     HOST_NODES_DIR = open('/home/vagrant/.host_nodes_dir').read().strip()
23     PKEY = open('/home/vagrant/.ssh/id_rsa').read()
24 except IOError as err:
25     logger.warn(err)
26 
27 
28 def seed_flavors():
29     """Seed the database with default flavors for vagrant.
30 
31     :rtype: list of dicts containing flavor data
32     """
33     flavors = []
34     for m in ['512', '1024', '2048']:
35         flavors.append({
36             'id': "vagrant-{}".format(m),
37             'provider': 'vagrant',
38             'params': json.dumps({
39                 'memory': m
40             })
41         })
42     return flavors
43 
44 
45 def build_layer(layer):
46     """
47     Build a layer.
48 
49     :param layer: a dict containing formation, id, params, and creds info
50     """
51 
52     # This can also be done with `deis layers:update` now.
53     layer_ = Layer.objects.get(id=layer['id'], formation__id=layer['formation'])
54     layer_.ssh_username = 'vagrant'
55     layer_.save()
56 
57 
58 def destroy_layer(layer):
59     """
60     Destroy a layer.
61 
62     :param layer: a dict containing formation, id, params, and creds info
63     """
64     pass
65 
66 
67 def build_node(node):
68     """
69     Build a node.
70 
71     :param node: a dict containing formation, layer, params, and creds info.
72     :rtype: a tuple of (provider_id, fully_qualified_domain_name, metadata)
73     """
74 
75     # Can't use the vagrant UUID because it's not booted yet
76     uid = str(uuid.uuid1())
77 
78     # Create a new Vagrantfile from a template
79     node['params'].setdefault('memory', '512')
80     template = open('/opt/deis/controller/contrib/vagrant/nodes_vagrantfile_template.rb')
81     raw = string.Template(template.read())
82     result = raw.substitute({
83         'id': uid,
84         'ipaddress': '192.168.61.' + str(Node.objects.all().count() + 100),
85         'memory': node['params']['memory']
86     })
87 
88     # Make a folder for the VM with its own Vagrantfile. Vagrant will then create a .vagrant folder
89     # there too when it first gets booted.
90     node_dir = HOST_NODES_DIR + '/' + uid
91     mkdir = 'mkdir -p ' + node_dir
92     cp_tpl = 'echo "' + result.replace('"', '\\"') + '" > ' + node_dir + '/Vagrantfile'
93     _host_ssh(commands=[mkdir, cp_tpl], creds=node['creds'])
94 
95     # Boot the VM
96     _run_vagrant_command(uid, args=['up'], creds=node['creds'])
97 
98     # Copy the layer's public SSH key to the VM so that the Controller can access it.
99     _run_vagrant_command(
100         uid,
101         args=[
102             'ssh',
103             '-c',
104             '"echo \\"' + node['ssh_public_key'] + '\\" >> /home/vagrant/.ssh/authorized_keys"'
105         ],
106         creds=node['creds'],
107     )
108 
109     provider_id = uid
110     fqdn = provider_id
111     if not fqdn.endswith('.local'):
112         fqdn += '.local'  # hostname is broadcast via avahi-daemon
113     metadata = {
114         'id': uid,
115         'fqdn': fqdn,
116         'flavor': node['params']['memory']
117     }
118     return provider_id, fqdn, metadata
119 
120 
121 def destroy_node(node):
122     """
123     Destroy a node.
124 
125     :param node: a dict containing a node's provider_id, params, and creds
126     """
127 
128     # This is useful if node creation failed. So that there's a record in the DB, but it has no
129     # ID associated with it.
130     if node['provider_id'] is None:
131         return
132 
133     # Shut the VM down and destroy it
134     _run_vagrant_command(node['provider_id'], args=['destroy', '--force'], creds=node['creds'])
135     node_dir = HOST_NODES_DIR + '/' + node['provider_id']
136 
137     # Sanity check before `rm -rf`
138     if 'contrib/vagrant' not in node_dir:
139         raise RuntimeError("Aborted node destruction: attempting to 'rm -rf' unexpected directory")
140 
141     # Completely remove the folder that contained the VM
142     rm_vagrantfile = 'rm ' + node_dir + '/Vagrantfile'
143     rm_node_dir = 'rm -rf ' + node_dir
144     _host_ssh(commands=[rm_vagrantfile, rm_node_dir], creds=node['creds'])
145 
146 
147 def _run_vagrant_command(node_id, args=[], creds={}):
148     """
149     args: A tuple of arguments to a vagrant command line.
150     e.g. ['up', 'my_vm_name', '--no-provision']
151     """
152 
153     cd = 'cd ' + HOST_NODES_DIR + '/' + node_id
154     command = ['vagrant'] + [arg for arg in args if arg is not None]
155     return _host_ssh(commands=[cd, ' '.join(command)], creds=creds)
156 
157 
158 def _host_ssh(creds={}, commands=[]):
159     """
160     Connect to the host machine. Namely the user's local machine.
161     """
162     if creds == {}:
163         raise RuntimeError("No credentials provided to _host_ssh()")
164     command = ' && '.join(commands)
165 
166     # First check if we can access the host machine. It's likely that their
167     # IP address changes every time they request a DHCP lease.
168     # TODO: Find a way of passing this error onto the CLI client.
169     try:
170         subprocess.check_call([
171             'nc', '-z', '-w2', creds['host'], '22'
172         ], stderr=subprocess.PIPE)
173     except subprocess.CalledProcessError:
174         raise RuntimeError("Couldn't ping port 22 at host with IP " + creds['host'])
175 
176     ssh = connect_ssh(creds['user'], creds['host'], 22, PKEY, timeout=120)
177     result, status = exec_ssh(ssh, command)
178     if status > 0:
179         raise RuntimeError(
180             'SSH to Vagrant host error: ' + result.decode('utf-8') +
181             'Command: ' + command.decode('utf-8'))
182     return result
183
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/provider/vagrant.py b/provider/vagrant.py
--- a/provider/vagrant.py
+++ b/provider/vagrant.py
@@ -131,17 +131,25 @@
         return
 
     # Shut the VM down and destroy it
-    _run_vagrant_command(node['provider_id'], args=['destroy', '--force'], creds=node['creds'])
-    node_dir = HOST_NODES_DIR + '/' + node['provider_id']
-
-    # Sanity check before `rm -rf`
-    if 'contrib/vagrant' not in node_dir:
-        raise RuntimeError("Aborted node destruction: attempting to 'rm -rf' unexpected directory")
-
-    # Completely remove the folder that contained the VM
-    rm_vagrantfile = 'rm ' + node_dir + '/Vagrantfile'
-    rm_node_dir = 'rm -rf ' + node_dir
-    _host_ssh(commands=[rm_vagrantfile, rm_node_dir], creds=node['creds'])
+    try:
+        _run_vagrant_command(node['provider_id'], args=['destroy', '--force'], creds=node['creds'])
+        node_dir = HOST_NODES_DIR + '/' + node['provider_id']
+
+        # Sanity check before `rm -rf`
+        if 'contrib/vagrant' not in node_dir:
+            raise RuntimeError(
+                "Aborted node destruction: attempting to 'rm -rf' unexpected directory")
+
+        # Completely remove the folder that contained the VM
+        rm_vagrantfile = 'rm ' + node_dir + '/Vagrantfile'
+        rm_node_dir = 'rm -rf ' + node_dir
+        _host_ssh(commands=[rm_vagrantfile, rm_node_dir], creds=node['creds'])
+    except RuntimeError as err:
+        # If we couldn't cd to the node dir, just log that as a warning
+        if 'No such file or directory' in str(err):
+            logger.warn(err)
+        else:
+            raise
 
 
 def _run_vagrant_command(node_id, args=[], creds={}):

{"golden_diff": "diff --git a/provider/vagrant.py b/provider/vagrant.py\n--- a/provider/vagrant.py\n+++ b/provider/vagrant.py\n@@ -131,17 +131,25 @@\n         return\n \n     # Shut the VM down and destroy it\n-    _run_vagrant_command(node['provider_id'], args=['destroy', '--force'], creds=node['creds'])\n-    node_dir = HOST_NODES_DIR + '/' + node['provider_id']\n-\n-    # Sanity check before `rm -rf`\n-    if 'contrib/vagrant' not in node_dir:\n-        raise RuntimeError(\"Aborted node destruction: attempting to 'rm -rf' unexpected directory\")\n-\n-    # Completely remove the folder that contained the VM\n-    rm_vagrantfile = 'rm ' + node_dir + '/Vagrantfile'\n-    rm_node_dir = 'rm -rf ' + node_dir\n-    _host_ssh(commands=[rm_vagrantfile, rm_node_dir], creds=node['creds'])\n+    try:\n+        _run_vagrant_command(node['provider_id'], args=['destroy', '--force'], creds=node['creds'])\n+        node_dir = HOST_NODES_DIR + '/' + node['provider_id']\n+\n+        # Sanity check before `rm -rf`\n+        if 'contrib/vagrant' not in node_dir:\n+            raise RuntimeError(\n+                \"Aborted node destruction: attempting to 'rm -rf' unexpected directory\")\n+\n+        # Completely remove the folder that contained the VM\n+        rm_vagrantfile = 'rm ' + node_dir + '/Vagrantfile'\n+        rm_node_dir = 'rm -rf ' + node_dir\n+        _host_ssh(commands=[rm_vagrantfile, rm_node_dir], creds=node['creds'])\n+    except RuntimeError as err:\n+        # If we couldn't cd to the node dir, just log that as a warning\n+        if 'No such file or directory' in str(err):\n+            logger.warn(err)\n+        else:\n+            raise\n \n \n def _run_vagrant_command(node_id, args=[], creds={}):\n", "issue": "Vagrant provider repeatedly errors on formation if node dir is deleted\nNeeds to be more robust in some error cases such as this one:\n1) Provision a controller but somehow forget to add _deis-controler_ to the admins group, despite all documentation and fuschia-colored warnings at the command-line\n2) Create a formation and scale it upward, e.g. `deis nodes:scale form1 runtime=2`\n3) Try to scale down the formation, get an appropriate error about \"couldn't remove chef node\"\n4) All subsequent formation commands--including destroy!--will fail when trying to access the local vagrant node dir, which apparently was removed in step 3).\n\nThis shouldn't happen often, but it can and I think ignoring this error at least in the case of `deis formations:destroy` would provide a way out of this dead end.\n\n", "before_files": [{"content": "\"\"\"\nDeis cloud provider implementation for local vagrant setups.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nfrom api.ssh import exec_ssh, connect_ssh\n\nimport json\nimport logging\nimport string\nimport subprocess\nimport uuid\n\nfrom api.models import Layer\nfrom api.models import Node\n\nlogger = logging.getLogger(__name__)\n\n# Collect details for connecting to the host machine\ntry:\n    HOST_NODES_DIR = open('/home/vagrant/.host_nodes_dir').read().strip()\n    PKEY = open('/home/vagrant/.ssh/id_rsa').read()\nexcept IOError as err:\n    logger.warn(err)\n\n\ndef seed_flavors():\n    \"\"\"Seed the database with default flavors for vagrant.\n\n    :rtype: list of dicts containing flavor data\n    \"\"\"\n    flavors = []\n    for m in ['512', '1024', '2048']:\n        flavors.append({\n            'id': \"vagrant-{}\".format(m),\n            'provider': 'vagrant',\n            'params': json.dumps({\n                'memory': m\n            })\n        })\n    return flavors\n\n\ndef build_layer(layer):\n    \"\"\"\n    Build a layer.\n\n    :param layer: a dict containing formation, id, params, and creds info\n    \"\"\"\n\n    # This can also be done with `deis layers:update` now.\n    layer_ = Layer.objects.get(id=layer['id'], formation__id=layer['formation'])\n    layer_.ssh_username = 'vagrant'\n    layer_.save()\n\n\ndef destroy_layer(layer):\n    \"\"\"\n    Destroy a layer.\n\n    :param layer: a dict containing formation, id, params, and creds info\n    \"\"\"\n    pass\n\n\ndef build_node(node):\n    \"\"\"\n    Build a node.\n\n    :param node: a dict containing formation, layer, params, and creds info.\n    :rtype: a tuple of (provider_id, fully_qualified_domain_name, metadata)\n    \"\"\"\n\n    # Can't use the vagrant UUID because it's not booted yet\n    uid = str(uuid.uuid1())\n\n    # Create a new Vagrantfile from a template\n    node['params'].setdefault('memory', '512')\n    template = open('/opt/deis/controller/contrib/vagrant/nodes_vagrantfile_template.rb')\n    raw = string.Template(template.read())\n    result = raw.substitute({\n        'id': uid,\n        'ipaddress': '192.168.61.' + str(Node.objects.all().count() + 100),\n        'memory': node['params']['memory']\n    })\n\n    # Make a folder for the VM with its own Vagrantfile. Vagrant will then create a .vagrant folder\n    # there too when it first gets booted.\n    node_dir = HOST_NODES_DIR + '/' + uid\n    mkdir = 'mkdir -p ' + node_dir\n    cp_tpl = 'echo \"' + result.replace('\"', '\\\\\"') + '\" > ' + node_dir + '/Vagrantfile'\n    _host_ssh(commands=[mkdir, cp_tpl], creds=node['creds'])\n\n    # Boot the VM\n    _run_vagrant_command(uid, args=['up'], creds=node['creds'])\n\n    # Copy the layer's public SSH key to the VM so that the Controller can access it.\n    _run_vagrant_command(\n        uid,\n        args=[\n            'ssh',\n            '-c',\n            '\"echo \\\\\"' + node['ssh_public_key'] + '\\\\\" >> /home/vagrant/.ssh/authorized_keys\"'\n        ],\n        creds=node['creds'],\n    )\n\n    provider_id = uid\n    fqdn = provider_id\n    if not fqdn.endswith('.local'):\n        fqdn += '.local'  # hostname is broadcast via avahi-daemon\n    metadata = {\n        'id': uid,\n        'fqdn': fqdn,\n        'flavor': node['params']['memory']\n    }\n    return provider_id, fqdn, metadata\n\n\ndef destroy_node(node):\n    \"\"\"\n    Destroy a node.\n\n    :param node: a dict containing a node's provider_id, params, and creds\n    \"\"\"\n\n    # This is useful if node creation failed. So that there's a record in the DB, but it has no\n    # ID associated with it.\n    if node['provider_id'] is None:\n        return\n\n    # Shut the VM down and destroy it\n    _run_vagrant_command(node['provider_id'], args=['destroy', '--force'], creds=node['creds'])\n    node_dir = HOST_NODES_DIR + '/' + node['provider_id']\n\n    # Sanity check before `rm -rf`\n    if 'contrib/vagrant' not in node_dir:\n        raise RuntimeError(\"Aborted node destruction: attempting to 'rm -rf' unexpected directory\")\n\n    # Completely remove the folder that contained the VM\n    rm_vagrantfile = 'rm ' + node_dir + '/Vagrantfile'\n    rm_node_dir = 'rm -rf ' + node_dir\n    _host_ssh(commands=[rm_vagrantfile, rm_node_dir], creds=node['creds'])\n\n\ndef _run_vagrant_command(node_id, args=[], creds={}):\n    \"\"\"\n    args: A tuple of arguments to a vagrant command line.\n    e.g. ['up', 'my_vm_name', '--no-provision']\n    \"\"\"\n\n    cd = 'cd ' + HOST_NODES_DIR + '/' + node_id\n    command = ['vagrant'] + [arg for arg in args if arg is not None]\n    return _host_ssh(commands=[cd, ' '.join(command)], creds=creds)\n\n\ndef _host_ssh(creds={}, commands=[]):\n    \"\"\"\n    Connect to the host machine. Namely the user's local machine.\n    \"\"\"\n    if creds == {}:\n        raise RuntimeError(\"No credentials provided to _host_ssh()\")\n    command = ' && '.join(commands)\n\n    # First check if we can access the host machine. It's likely that their\n    # IP address changes every time they request a DHCP lease.\n    # TODO: Find a way of passing this error onto the CLI client.\n    try:\n        subprocess.check_call([\n            'nc', '-z', '-w2', creds['host'], '22'\n        ], stderr=subprocess.PIPE)\n    except subprocess.CalledProcessError:\n        raise RuntimeError(\"Couldn't ping port 22 at host with IP \" + creds['host'])\n\n    ssh = connect_ssh(creds['user'], creds['host'], 22, PKEY, timeout=120)\n    result, status = exec_ssh(ssh, command)\n    if status > 0:\n        raise RuntimeError(\n            'SSH to Vagrant host error: ' + result.decode('utf-8') +\n            'Command: ' + command.decode('utf-8'))\n    return result\n", "path": "provider/vagrant.py"}], "after_files": [{"content": "\"\"\"\nDeis cloud provider implementation for local vagrant setups.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nfrom api.ssh import exec_ssh, connect_ssh\n\nimport json\nimport logging\nimport string\nimport subprocess\nimport uuid\n\nfrom api.models import Layer\nfrom api.models import Node\n\nlogger = logging.getLogger(__name__)\n\n# Collect details for connecting to the host machine\ntry:\n    HOST_NODES_DIR = open('/home/vagrant/.host_nodes_dir').read().strip()\n    PKEY = open('/home/vagrant/.ssh/id_rsa').read()\nexcept IOError as err:\n    logger.warn(err)\n\n\ndef seed_flavors():\n    \"\"\"Seed the database with default flavors for vagrant.\n\n    :rtype: list of dicts containing flavor data\n    \"\"\"\n    flavors = []\n    for m in ['512', '1024', '2048']:\n        flavors.append({\n            'id': \"vagrant-{}\".format(m),\n            'provider': 'vagrant',\n            'params': json.dumps({\n                'memory': m\n            })\n        })\n    return flavors\n\n\ndef build_layer(layer):\n    \"\"\"\n    Build a layer.\n\n    :param layer: a dict containing formation, id, params, and creds info\n    \"\"\"\n\n    # This can also be done with `deis layers:update` now.\n    layer_ = Layer.objects.get(id=layer['id'], formation__id=layer['formation'])\n    layer_.ssh_username = 'vagrant'\n    layer_.save()\n\n\ndef destroy_layer(layer):\n    \"\"\"\n    Destroy a layer.\n\n    :param layer: a dict containing formation, id, params, and creds info\n    \"\"\"\n    pass\n\n\ndef build_node(node):\n    \"\"\"\n    Build a node.\n\n    :param node: a dict containing formation, layer, params, and creds info.\n    :rtype: a tuple of (provider_id, fully_qualified_domain_name, metadata)\n    \"\"\"\n\n    # Can't use the vagrant UUID because it's not booted yet\n    uid = str(uuid.uuid1())\n\n    # Create a new Vagrantfile from a template\n    node['params'].setdefault('memory', '512')\n    template = open('/opt/deis/controller/contrib/vagrant/nodes_vagrantfile_template.rb')\n    raw = string.Template(template.read())\n    result = raw.substitute({\n        'id': uid,\n        'ipaddress': '192.168.61.' + str(Node.objects.all().count() + 100),\n        'memory': node['params']['memory']\n    })\n\n    # Make a folder for the VM with its own Vagrantfile. Vagrant will then create a .vagrant folder\n    # there too when it first gets booted.\n    node_dir = HOST_NODES_DIR + '/' + uid\n    mkdir = 'mkdir -p ' + node_dir\n    cp_tpl = 'echo \"' + result.replace('\"', '\\\\\"') + '\" > ' + node_dir + '/Vagrantfile'\n    _host_ssh(commands=[mkdir, cp_tpl], creds=node['creds'])\n\n    # Boot the VM\n    _run_vagrant_command(uid, args=['up'], creds=node['creds'])\n\n    # Copy the layer's public SSH key to the VM so that the Controller can access it.\n    _run_vagrant_command(\n        uid,\n        args=[\n            'ssh',\n            '-c',\n            '\"echo \\\\\"' + node['ssh_public_key'] + '\\\\\" >> /home/vagrant/.ssh/authorized_keys\"'\n        ],\n        creds=node['creds'],\n    )\n\n    provider_id = uid\n    fqdn = provider_id\n    if not fqdn.endswith('.local'):\n        fqdn += '.local'  # hostname is broadcast via avahi-daemon\n    metadata = {\n        'id': uid,\n        'fqdn': fqdn,\n        'flavor': node['params']['memory']\n    }\n    return provider_id, fqdn, metadata\n\n\ndef destroy_node(node):\n    \"\"\"\n    Destroy a node.\n\n    :param node: a dict containing a node's provider_id, params, and creds\n    \"\"\"\n\n    # This is useful if node creation failed. So that there's a record in the DB, but it has no\n    # ID associated with it.\n    if node['provider_id'] is None:\n        return\n\n    # Shut the VM down and destroy it\n    try:\n        _run_vagrant_command(node['provider_id'], args=['destroy', '--force'], creds=node['creds'])\n        node_dir = HOST_NODES_DIR + '/' + node['provider_id']\n\n        # Sanity check before `rm -rf`\n        if 'contrib/vagrant' not in node_dir:\n            raise RuntimeError(\n                \"Aborted node destruction: attempting to 'rm -rf' unexpected directory\")\n\n        # Completely remove the folder that contained the VM\n        rm_vagrantfile = 'rm ' + node_dir + '/Vagrantfile'\n        rm_node_dir = 'rm -rf ' + node_dir\n        _host_ssh(commands=[rm_vagrantfile, rm_node_dir], creds=node['creds'])\n    except RuntimeError as err:\n        # If we couldn't cd to the node dir, just log that as a warning\n        if 'No such file or directory' in str(err):\n            logger.warn(err)\n        else:\n            raise\n\n\ndef _run_vagrant_command(node_id, args=[], creds={}):\n    \"\"\"\n    args: A tuple of arguments to a vagrant command line.\n    e.g. ['up', 'my_vm_name', '--no-provision']\n    \"\"\"\n\n    cd = 'cd ' + HOST_NODES_DIR + '/' + node_id\n    command = ['vagrant'] + [arg for arg in args if arg is not None]\n    return _host_ssh(commands=[cd, ' '.join(command)], creds=creds)\n\n\ndef _host_ssh(creds={}, commands=[]):\n    \"\"\"\n    Connect to the host machine. Namely the user's local machine.\n    \"\"\"\n    if creds == {}:\n        raise RuntimeError(\"No credentials provided to _host_ssh()\")\n    command = ' && '.join(commands)\n\n    # First check if we can access the host machine. It's likely that their\n    # IP address changes every time they request a DHCP lease.\n    # TODO: Find a way of passing this error onto the CLI client.\n    try:\n        subprocess.check_call([\n            'nc', '-z', '-w2', creds['host'], '22'\n        ], stderr=subprocess.PIPE)\n    except subprocess.CalledProcessError:\n        raise RuntimeError(\"Couldn't ping port 22 at host with IP \" + creds['host'])\n\n    ssh = connect_ssh(creds['user'], creds['host'], 22, PKEY, timeout=120)\n    result, status = exec_ssh(ssh, command)\n    if status > 0:\n        raise RuntimeError(\n            'SSH to Vagrant host error: ' + result.decode('utf-8') +\n            'Command: ' + command.decode('utf-8'))\n    return result\n", "path": "provider/vagrant.py"}]}