5
5
import json
6
6
import time
7
7
from shlex import split as shlexsplit
8
- from .errors import SocketError
8
+ from .errors import SocketError , MountNonExistentError , MountExistsError
9
9
10
+ def get_existing_mounts (vm_name ):
11
+ vm = MultipassClient ().get_vm (vm_name )
12
+ return vm .info ().get ('info' ).get (vm_name ).get ("mounts" )
10
13
11
14
# Added decorator to automatically retry on unpredictable module failures
12
15
def retry_on_failure (ExceptionsToCheck , max_retries = 5 , delay = 5 , backoff = 2 ):
@@ -29,6 +32,7 @@ class MultipassVM:
29
32
def __init__ (self , vm_name , multipass_cmd ):
30
33
self .cmd = multipass_cmd
31
34
self .vm_name = vm_name
35
+
32
36
# Will retry to execute info() if SocketError occurs
33
37
@retry_on_failure (ExceptionsToCheck = SocketError )
34
38
def info (self ):
@@ -48,6 +52,7 @@ def info(self):
48
52
else :
49
53
raise Exception ("Multipass info command failed: {0}" .format (stderr .decode (encoding = "utf-8" )))
50
54
return json .loads (stdout )
55
+
51
56
def delete (self , purge = False ):
52
57
cmd = [self .cmd , "delete" , self .vm_name ]
53
58
if purge :
@@ -67,8 +72,10 @@ def delete(self, purge=False):
67
72
self .vm_name , stderr .decode (encoding = "utf-8" )
68
73
)
69
74
)
75
+
70
76
def shell (self ):
71
77
raise Exception ("The shell command is not supported in the Multipass SDK. Consider using exec." )
78
+
72
79
def exec (self , cmd_to_execute , working_directory = "" ):
73
80
cmd = [self .cmd , "exec" , self .vm_name ]
74
81
if working_directory :
@@ -84,18 +91,21 @@ def exec(self, cmd_to_execute, working_directory=""):
84
91
if (exitcode != 0 ):
85
92
raise Exception ("Multipass exec command failed: {0}" .format (stderr .decode (encoding = "utf-8" )))
86
93
return stdout , stderr
94
+
87
95
def stop (self ):
88
96
cmd = [self .cmd , "stop" , self .vm_name ]
89
97
try :
90
98
subprocess .check_output (cmd )
91
99
except :
92
100
raise Exception ("Error stopping Multipass VM {0}" .format (self .vm_name ))
101
+
93
102
def start (self ):
94
103
cmd = [self .cmd , "start" , self .vm_name ]
95
104
try :
96
105
subprocess .check_output (cmd )
97
106
except :
98
107
raise Exception ("Error starting Multipass VM {0}" .format (self .vm_name ))
108
+
99
109
def restart (self ):
100
110
cmd = [self .cmd , "restart" , self .vm_name ]
101
111
try :
@@ -109,6 +119,7 @@ class MultipassClient:
109
119
"""
110
120
def __init__ (self , multipass_cmd = "multipass" ):
111
121
self .cmd = multipass_cmd
122
+
112
123
def launch (self , vm_name = None , cpu = 1 , disk = "5G" , mem = "1G" , image = None , cloud_init = None ):
113
124
if (not vm_name ):
114
125
# similar to Multipass's VM name generator
@@ -124,20 +135,24 @@ def launch(self, vm_name=None, cpu=1, disk="5G", mem="1G", image=None, cloud_ini
124
135
except :
125
136
raise Exception ("Error launching Multipass VM {0}" .format (vm_name ))
126
137
return MultipassVM (vm_name , self .cmd )
138
+
127
139
def transfer (self , src , dest ):
128
140
cmd = [self .cmd , "transfer" , src , dest ]
129
141
try :
130
142
subprocess .check_output (cmd )
131
143
except :
132
144
raise Exception ("Multipass transfer command failed." )
145
+
133
146
def get_vm (self , vm_name ):
134
147
return MultipassVM (vm_name , self .cmd )
148
+
135
149
def purge (self ):
136
150
cmd = [self .cmd , "purge" ]
137
151
try :
138
152
subprocess .check_output (cmd )
139
153
except :
140
154
raise Exception ("Purge command failed." )
155
+
141
156
def list (self ):
142
157
cmd = [self .cmd , "list" , "--format" , "json" ]
143
158
out = subprocess .Popen (cmd ,
@@ -148,6 +163,7 @@ def list(self):
148
163
if (not exitcode == 0 ):
149
164
raise Exception ("Multipass list command failed: {0}" .format (stderr ))
150
165
return json .loads (stdout )
166
+
151
167
def find (self ):
152
168
cmd = [self .cmd , "find" , "--format" , "json" ]
153
169
out = subprocess .Popen (cmd ,
@@ -158,30 +174,52 @@ def find(self):
158
174
if (not exitcode == 0 ):
159
175
raise Exception ("Multipass find command failed: {0}" .format (stderr ))
160
176
return json .loads (stdout )
161
- def mount (self , src , target ):
162
- cmd = [self .cmd , "mount" , src , target ]
163
- try :
164
- subprocess .check_output (cmd )
165
- except :
166
- raise Exception ("Multipass mount command failed." )
167
- def unmount (self , mount ):
168
- cmd = [self .cmd , "unmount" , mount ]
169
- try :
170
- subprocess .check_output (cmd )
171
- except :
172
- raise Exception ("Multipass unmount command failed." )
177
+
178
+ def mount (self , src , target , mount_type = 'classic' , uid_maps = [], gid_maps = []):
179
+ mount_options = ["--type" , mount_type ]
180
+ for uid_map in uid_maps :
181
+ mount_options .extend (["--uid-map" , uid_map ])
182
+ for gid_map in gid_maps :
183
+ mount_options .extend (["--gid-map" , gid_map ])
184
+ cmd = [self .cmd , "mount" ] + mount_options + [src , target ]
185
+ out = subprocess .Popen (cmd , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
186
+ _ ,stderr = out .communicate ()
187
+ exitcode = out .wait ()
188
+ stderr_cleaned = stderr .decode (encoding = "utf-8" ).splitlines ()
189
+ if (not exitcode == 0 ):
190
+ for error_msg in stderr_cleaned :
191
+ if 'is already mounted' in error_msg :
192
+ raise MountExistsError
193
+ raise Exception ("Multipass mount command failed: {0}" .format (stderr .decode (encoding = "utf-8" ).rstrip ()))
194
+
195
+ def umount (self , mount ):
196
+ cmd = [self .cmd , "umount" , mount ]
197
+ out = subprocess .Popen (cmd ,
198
+ stdout = subprocess .PIPE ,
199
+ stderr = subprocess .PIPE )
200
+ _ ,stderr = out .communicate ()
201
+ exitcode = out .wait ()
202
+ stderr_cleaned = stderr .decode (encoding = "utf-8" ).splitlines ()
203
+ if (not exitcode == 0 ):
204
+ for error_msg in stderr_cleaned :
205
+ if 'is not mounted' in error_msg :
206
+ raise MountNonExistentError
207
+ raise Exception ("{}" .format (stderr .decode (encoding = "utf-8" ).rstrip ()))
208
+
173
209
def recover (self , vm_name ):
174
210
cmd = [self .cmd , "recover" , vm_name ]
175
211
try :
176
212
subprocess .check_output (cmd )
177
213
except :
178
214
raise Exception ("Multipass recover command failed." )
215
+
179
216
def suspend (self ):
180
217
cmd = [self .cmd , "suspend" ]
181
218
try :
182
219
subprocess .check_output (cmd )
183
220
except :
184
221
raise Exception ("Multipass suspend command failed." )
222
+
185
223
def get (self , key ):
186
224
cmd = [self .cmd , "get" , key ]
187
225
out = subprocess .Popen (cmd ,
0 commit comments