1818# You should have received a copy of the GNU Lesser General Public License along
1919# with this program; if not, see <http://www.gnu.org/licenses/>.
2020
21- ''' qvm-start - start a domain'''
21+ """ qvm-start - start a domain"""
2222import asyncio
2323import argparse
2424import string
3232import qubesadmin .exc
3333import qubesadmin .tools
3434
35+
3536class DriveAction (argparse .Action ):
36- ''' Action for argument parser that stores drive image path.'''
37+ """ Action for argument parser that stores drive image path."""
3738
3839 # pylint: disable=redefined-builtin
39- def __init__ (self ,
40- option_strings ,
41- dest = 'drive' ,
42- * ,
43- prefix = 'cdrom:' ,
44- metavar = 'IMAGE' ,
45- required = False ,
46- help = 'Attach drive' ):
47- super ().__init__ (option_strings , dest ,
48- metavar = metavar , help = help )
40+ def __init__ (
41+ self ,
42+ option_strings ,
43+ dest = "drive" ,
44+ * ,
45+ prefix = "cdrom:" ,
46+ metavar = "IMAGE" ,
47+ required = False ,
48+ help = "Attach drive" ,
49+ ):
50+ super ().__init__ (option_strings , dest , metavar = metavar , help = help )
4951 self .prefix = prefix
5052
5153 def __call__ (self , parser , namespace , values , option_string = None ):
@@ -54,30 +56,50 @@ def __call__(self, parser, namespace, values, option_string=None):
5456
5557
5658parser = qubesadmin .tools .QubesArgumentParser (
57- description = 'start a domain' , vmname_nargs = '+' )
59+ description = "start a domain" , vmname_nargs = "+"
60+ )
5861
59- parser .add_argument ('--skip-if-running' ,
60- action = 'store_true' , default = False ,
61- help = 'Do not fail if the qube is already runnning' )
62+ parser .add_argument (
63+ "--skip-if-running" ,
64+ action = "store_true" ,
65+ default = False ,
66+ help = "Do not fail if the qube is already runnning" ,
67+ )
6268
6369parser_drive = parser .add_mutually_exclusive_group ()
6470
65- parser_drive .add_argument ('--drive' , metavar = 'DRIVE' ,
66- help = 'temporarily attach specified drive as CD/DVD or hard disk (can be'
67- ' specified with prefix "hd:" or "cdrom:", default is cdrom)' )
68-
69- parser_drive .add_argument ('--hddisk' ,
70- action = DriveAction , dest = 'drive' , prefix = 'hd:' ,
71- help = 'temporarily attach specified drive as hard disk' )
72-
73- parser_drive .add_argument ('--cdrom' , metavar = 'IMAGE' ,
74- action = DriveAction , dest = 'drive' , prefix = 'cdrom:' ,
75- help = 'temporarily attach specified drive as CD/DVD' )
76-
77- parser_drive .add_argument ('--install-windows-tools' ,
78- action = 'store_const' , dest = 'drive' , default = False ,
79- const = 'cdrom:dom0:/usr/lib/qubes/qubes-windows-tools.iso' ,
80- help = 'temporarily attach Windows tools CDROM to the domain' )
71+ parser_drive .add_argument (
72+ "--drive" ,
73+ metavar = "DRIVE" ,
74+ help = "temporarily attach specified drive as CD/DVD or hard disk (can be"
75+ ' specified with prefix "hd:" or "cdrom:", default is cdrom)' ,
76+ )
77+
78+ parser_drive .add_argument (
79+ "--hddisk" ,
80+ action = DriveAction ,
81+ dest = "drive" ,
82+ prefix = "hd:" ,
83+ help = "temporarily attach specified drive as hard disk" ,
84+ )
85+
86+ parser_drive .add_argument (
87+ "--cdrom" ,
88+ metavar = "IMAGE" ,
89+ action = DriveAction ,
90+ dest = "drive" ,
91+ prefix = "cdrom:" ,
92+ help = "temporarily attach specified drive as CD/DVD" ,
93+ )
94+
95+ parser_drive .add_argument (
96+ "--install-windows-tools" ,
97+ action = "store_const" ,
98+ dest = "drive" ,
99+ default = False ,
100+ const = "cdrom:dom0:/usr/lib/qubes/qubes-windows-tools.iso" ,
101+ help = "temporarily attach Windows tools CDROM to the domain" ,
102+ )
81103
82104
83105def get_drive_assignment (app , drive_str ):
@@ -93,75 +115,86 @@ def get_drive_assignment(app, drive_str):
93115 :param drive_str: drive argument
94116 :return: DeviceAssignment matching *drive_str*
95117 """
96- devtype = ' cdrom'
97- if drive_str .startswith (' cdrom:' ):
98- devtype = ' cdrom'
99- drive_str = drive_str [len (' cdrom:' ) :]
100- elif drive_str .startswith (' hd:' ):
101- devtype = ' disk'
102- drive_str = drive_str [len (' hd:' ) :]
118+ devtype = " cdrom"
119+ if drive_str .startswith (" cdrom:" ):
120+ devtype = " cdrom"
121+ drive_str = drive_str [len (" cdrom:" ) :]
122+ elif drive_str .startswith (" hd:" ):
123+ devtype = " disk"
124+ drive_str = drive_str [len (" hd:" ) :]
103125
104126 try :
105- backend_domain_name , port_id = drive_str .split (':' , 1 )
127+ backend_domain_name , port_id = drive_str .split (":" , 1 )
106128 except ValueError :
107- raise ValueError ("Incorrect image name: image must be in the format "
108- "of VMNAME:full_path, for example "
109- "dom0:/home/user/test.iso" )
129+ raise ValueError (
130+ "Incorrect image name: image must be in the format "
131+ "of VMNAME:full_path, for example "
132+ "dom0:/home/user/test.iso"
133+ )
110134 try :
111135 backend_domain = app .domains [backend_domain_name ]
112136 except KeyError :
113137 raise qubesadmin .exc .QubesVMNotFoundError (
114- 'No such VM: %s' , backend_domain_name )
115- if port_id .startswith ('/' ):
138+ "No such VM: %s" , backend_domain_name
139+ )
140+ if port_id .startswith ("/" ):
116141 # it is a path - if we're running in dom0, try to call losetup to
117142 # export the device, otherwise reject
118- if app .qubesd_connection_type == ' qrexec' :
143+ if app .qubesd_connection_type == " qrexec" :
119144 raise qubesadmin .exc .QubesException (
120- 'Existing block device identifier needed when running from '
121- 'outside of dom0 (see qvm-block)' )
145+ "Existing block device identifier needed when running from "
146+ "outside of dom0 (see qvm-block)"
147+ )
122148 try :
123- if backend_domain .klass == ' AdminVM' :
149+ if backend_domain .klass == " AdminVM" :
124150 loop_name = subprocess .check_output (
125- ['sudo' , 'losetup' , '-f' , '--show' , port_id ])
151+ ["sudo" , "losetup" , "-f" , "--show" , port_id ]
152+ )
126153 loop_name = loop_name .strip ()
127154 else :
128155 untrusted_loop_name , _ = backend_domain .run_with_args (
129- ' losetup' , '-f' , ' --show' , port_id ,
130- user = 'root' )
156+ " losetup" , "-f" , " --show" , port_id , user = "root"
157+ )
131158 untrusted_loop_name = untrusted_loop_name .strip ()
132- allowed_chars = string .ascii_lowercase + string .digits + '/'
133- allowed_chars = allowed_chars .encode (' ascii' )
159+ allowed_chars = string .ascii_lowercase + string .digits + "/"
160+ allowed_chars = allowed_chars .encode (" ascii" )
134161 if not all (c in allowed_chars for c in untrusted_loop_name ):
135162 raise qubesadmin .exc .QubesException (
136- 'Invalid loop device name received from {}' .format (
137- backend_domain .name ))
163+ "Invalid loop device name received from {}" .format (
164+ backend_domain .name
165+ )
166+ )
138167 loop_name = untrusted_loop_name
139168 del untrusted_loop_name
140169 except subprocess .CalledProcessError :
141170 raise qubesadmin .exc .QubesException (
142- 'Failed to setup loop device for %s' , port_id )
143- assert loop_name .startswith (b'/dev/loop' )
144- port_id = loop_name .decode ().split ('/' )[2 ]
171+ "Failed to setup loop device for %s" , port_id
172+ )
173+ assert loop_name .startswith (b"/dev/loop" )
174+ port_id = loop_name .decode ().split ("/" )[2 ]
145175 # wait for device to appear
146176 # FIXME: convert this to waiting for event
147177 timeout = 10
148178 while isinstance (
149- backend_domain .devices [' block' ][port_id ], UnknownDevice
179+ backend_domain .devices [" block" ][port_id ], UnknownDevice
150180 ):
151181 if timeout == 0 :
152182 raise qubesadmin .exc .QubesException (
153- 'Timeout waiting for {}:{} device to appear' .format (
154- backend_domain .name , port_id ))
183+ "Timeout waiting for {}:{} device to appear" .format (
184+ backend_domain .name , port_id
185+ )
186+ )
155187 timeout -= 1
156188 time .sleep (1 )
157189
158- options = {
159- 'devtype' : devtype ,
160- 'read-only' : devtype == 'cdrom'
161- }
190+ options = {"devtype" : devtype , "read-only" : devtype == "cdrom" }
162191 assignment = DeviceAssignment .new (
163- backend_domain = backend_domain , port_id = port_id , devclass = 'block' ,
164- options = options , mode = "required" )
192+ backend_domain = backend_domain ,
193+ port_id = port_id ,
194+ devclass = "block" ,
195+ options = options ,
196+ mode = "required" ,
197+ )
165198
166199 return assignment
167200
@@ -181,7 +214,7 @@ def startup(domain, args=None):
181214 if args .drive :
182215 drive_assignment = get_drive_assignment (args .app , args .drive )
183216 try :
184- domain .devices [' block' ].assign (drive_assignment )
217+ domain .devices [" block" ].assign (drive_assignment )
185218 except Exception :
186219 drive_assignment = None
187220 raise
@@ -190,11 +223,11 @@ def startup(domain, args=None):
190223
191224 if drive_assignment :
192225 # don't reconnect this device after VM reboot
193- domain .devices [' block' ].unassign (drive_assignment )
226+ domain .devices [" block" ].unassign (drive_assignment )
194227 except (IOError , OSError , qubesadmin .exc .QubesException , ValueError ) as e :
195228 if drive_assignment :
196229 try :
197- domain .devices [' block' ].detach (drive_assignment )
230+ domain .devices [" block" ].detach (drive_assignment )
198231 except qubesadmin .exc .QubesException :
199232 pass
200233 raise e
@@ -213,19 +246,19 @@ async def run_async(args=None, app=None):
213246 if isinstance (res , BaseException ):
214247 exit_code = 1
215248 parser .print_error (
216- ' Starting qube failed: {}: {}' .format (qube .name , str (res ))
249+ " Starting qube failed: {}: {}" .format (qube .name , str (res ))
217250 )
218251 return exit_code
219252
220253
221254def main (args = None , app = None ):
222- ''' Main routine of :program:`qvm-start`.
255+ """ Main routine of :program:`qvm-start`.
223256
224257 :param list args: Optional arguments to override those delivered from \
225258 command line.
226- '''
259+ """
227260 return asyncio .run (run_async (args = args , app = app ))
228261
229262
230- if __name__ == ' __main__' :
263+ if __name__ == " __main__" :
231264 sys .exit (main ())
0 commit comments