5
5
import ipaddress
6
6
import random
7
7
import string
8
+ import subprocess
8
9
from dataclasses import dataclass , field
9
10
from pathlib import Path
10
11
@@ -67,9 +68,14 @@ def __init__(self, netns, ssh_key: Path, host, user, *, on_error=None):
67
68
68
69
self ._on_error = on_error
69
70
71
+ @property
72
+ def user_host (self ):
73
+ """remote address for in SSH format <user>@<IP>"""
74
+ return f"{ self .user } @{ self .host } "
75
+
70
76
def remote_path (self , path ):
71
77
"""Convert a path to remote"""
72
- return f"{ self .user } @ { self . host } :{ path } "
78
+ return f"{ self .user_host } :{ path } "
73
79
74
80
def _scp (self , path1 , path2 , options ):
75
81
"""Copy files to/from the VM using scp."""
@@ -111,21 +117,12 @@ def run(self, cmd_string, timeout=None, *, check=False, debug=False):
111
117
112
118
If `debug` is set, pass `-vvv` to `ssh`. Note that this will clobber stderr.
113
119
"""
114
- command = [
115
- "ssh" ,
116
- * self .options ,
117
- f"{ self .user } @{ self .host } " ,
118
- cmd_string ,
119
- ]
120
+ command = ["ssh" , * self .options , self .user_host , cmd_string ]
120
121
121
122
if debug :
122
123
command .insert (1 , "-vvv" )
123
124
124
- return self ._exec (
125
- command ,
126
- timeout ,
127
- check = check ,
128
- )
125
+ return self ._exec (command , timeout , check = check )
129
126
130
127
def check_output (self , cmd_string , timeout = None , * , debug = False ):
131
128
"""Same as `run`, but raises an exception on non-zero return code of remote command"""
@@ -144,6 +141,27 @@ def _exec(self, cmd, timeout=None, check=False):
144
141
145
142
raise
146
143
144
+ # pylint:disable=invalid-name
145
+ def Popen (
146
+ self ,
147
+ cmd : str ,
148
+ stdin = subprocess .DEVNULL ,
149
+ stdout = subprocess .PIPE ,
150
+ stderr = subprocess .PIPE ,
151
+ ** kwargs ,
152
+ ) -> subprocess .Popen :
153
+ """Execute the command in the guest and return a Popen object.
154
+
155
+ pop = uvm.ssh.Popen("while true; do echo $(date -Is) $RANDOM; sleep 1; done")
156
+ pop.stdout.read(16)
157
+ """
158
+ cmd = ["ssh" , * self .options , self .user_host , cmd ]
159
+ if self .netns is not None :
160
+ cmd = ["ip" , "netns" , "exec" , self .netns ] + cmd
161
+ return subprocess .Popen (
162
+ cmd , stdin = stdin , stdout = stdout , stderr = stderr , ** kwargs
163
+ )
164
+
147
165
148
166
def mac_from_ip (ip_address ):
149
167
"""Create a MAC address based on the provided IP.
0 commit comments