2014-12-09 16:22:51 +03:00
#
# Copyright 2014 Red Hat, Inc.
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2014-12-09 16:22:51 +03:00
#
2015-09-06 17:36:17 +03:00
import logging
2014-12-09 16:22:51 +03:00
import re
2018-01-27 22:19:12 +03:00
import urllib . parse
2014-12-09 16:22:51 +03:00
2015-09-06 17:36:17 +03:00
2015-09-22 18:34:15 +03:00
def sanitize_xml_for_test_define ( xml ) :
2015-09-06 17:36:17 +03:00
import difflib
orig = xml
xml = re . sub ( " arch= \" .* \" " , " arch= \" i686 \" " , xml )
xml = re . sub ( " domain type= \" .* \" " , " domain type= \" test \" " , xml )
xml = re . sub ( " machine type= \" .* \" " , " " , xml )
xml = re . sub ( " >exe< " , " >hvm< " , xml )
2015-09-22 18:26:13 +03:00
xml = re . sub ( " >linux< " , " >xen< " , xml )
2015-09-06 17:36:17 +03:00
diff = " \n " . join ( difflib . unified_diff ( orig . split ( " \n " ) ,
xml . split ( " \n " ) ) )
if diff :
logging . debug ( " virtinst test sanitizing diff \n : %s " , diff )
return xml
class URI ( object ) :
2014-12-09 16:22:51 +03:00
"""
Parse an arbitrary URI into its individual parts
"""
def __init__ ( self , uri ) :
self . uri = uri
Fix URI parsing when username contains @ sign
On a domain-joined host the URI wasn't getting properly parsed due to the username containing an @ sign.
Before:
DEBUG (sshtunnels:263) ['ssh', 'ssh', '-l', 'eduardok', 'ad.mydomain.com@kvmhost1', 'sh -c', '\'nc -q 2>&1 | grep "requires an argument" >/dev/null;if [ $? -eq 0 ] ; then CMD="nc -q 0 127.0.0.1 5900";else CMD="nc 127.0.0.1 5900";fi;eval "$CMD";\'']
After:
DEBUG (sshtunnels:263) ['ssh', 'ssh', '-l', 'eduardok@ad.mydomain.com', 'kvmhost1', 'sh -c', '\'nc -q 2>&1 | grep "requires an argument" >/dev/null;if [ $? -eq 0 ] ; then CMD="nc -q 0 127.0.0.1 5900";else CMD="nc 127.0.0.1 5900";fi;eval "$CMD";\'']
2018-04-16 23:45:27 +03:00
split_uri = self . _split ( uri )
self . scheme = split_uri [ 0 ]
( self . username , self . hostname , self . path , self . query ,
self . fragment ) = map ( urllib . parse . unquote , split_uri [ 1 : ] )
2014-12-09 16:22:51 +03:00
self . transport = ' '
if " + " in self . scheme :
self . scheme , self . transport = self . scheme . rsplit ( " + " , 1 )
self . port = ' '
self . is_ipv6 = False
if self . hostname . startswith ( " [ " ) and " ] " in self . hostname :
if " ]: " in self . hostname :
self . hostname , self . port = self . hostname . rsplit ( " : " , 1 )
self . hostname = " " . join ( self . hostname [ 1 : ] . split ( " ] " , 1 ) )
self . is_ipv6 = True
elif " : " in self . hostname :
self . hostname , self . port = self . hostname . split ( " : " , 1 )
2014-12-09 18:15:27 +03:00
self . host_is_ipv4_string = bool ( re . match ( " ^[0-9.]+$ " , self . hostname ) )
2014-12-09 16:22:51 +03:00
###################
# Private helpers #
###################
def _split ( self , uri ) :
def splitnetloc ( url , start = 0 ) :
for c in ' /?# ' : # the order is important!
delim = url . find ( c , start )
if delim > = 0 :
break
else :
delim = len ( url )
return url [ start : delim ] , url [ delim : ]
2014-12-09 18:15:27 +03:00
scheme = username = netloc = query = fragment = ' '
2014-12-09 16:22:51 +03:00
i = uri . find ( " : " )
if i > 0 :
scheme , uri = uri [ : i ] . lower ( ) , uri [ i + 1 : ]
if uri [ : 2 ] == ' // ' :
netloc , uri = splitnetloc ( uri , 2 )
offset = netloc . find ( " @ " )
if offset > 0 :
username = netloc [ 0 : offset ]
netloc = netloc [ offset + 1 : ]
if ' # ' in uri :
uri , fragment = uri . split ( ' # ' , 1 )
if ' ? ' in uri :
uri , query = uri . split ( ' ? ' , 1 )
return scheme , username , netloc , uri , query , fragment
2015-09-06 17:36:17 +03:00
class MagicURI ( object ) :
"""
Handle magic virtinst URIs we use for the test suite and UI testing .
This allows a special URI to override various features like capabilities
XML , reported connection and libvirt versions , that enable testing
different code paths .
A magic URI has 3 parts :
1 ) Magic prefix __virtinst_test__
2 ) Actual openable URI , usually a test : / / / URI
3 ) Comma separated options
The available options are :
* ' predictable ' : Generate predictable UUIDs , MAC addresses , and
temporary file names .
* ' remote ' : Have the code consider this as a remote URI
* ' session ' : Have the code consider this as a session URI
* ' connver= %d ' : Override the connection ( hv ) version
* ' libver= %d ' : Override the libvirt version
* ' caps= %s ' : Points to a file with capabilities XML , that will
be returned in conn . getCapabilities . Ex .
files in test / capabilities - xml /
* ' domcaps= %s ' : Points to a file with domain capabilities XML , that
will be returned in conn . getDomainCapabilities
2017-02-21 17:28:00 +03:00
* qemu , xen , lxc or vz : Fake the specified hypervisor
2015-09-06 17:36:17 +03:00
See tests / utils . py for example URLs
"""
VIRTINST_URI_MAGIC_PREFIX = " __virtinst_test__ "
@staticmethod
def uri_is_magic ( uri ) :
return uri . startswith ( MagicURI . VIRTINST_URI_MAGIC_PREFIX )
def __init__ ( self , uri ) :
if not self . uri_is_magic ( uri ) :
raise RuntimeError ( " uri= %s is not virtinst magic URI " % uri )
2019-06-08 00:50:58 +03:00
from . cli import parse_optstr_tuples
2015-09-06 17:36:17 +03:00
uri = uri . replace ( self . VIRTINST_URI_MAGIC_PREFIX , " " )
ret = uri . split ( " , " , 1 )
self . open_uri = ret [ 0 ]
2016-06-13 21:16:36 +03:00
opts = dict ( parse_optstr_tuples ( len ( ret ) > 1 and ret [ 1 ] or " " ) )
2015-09-06 17:36:17 +03:00
def pop_bool ( field ) :
ret = field in opts
opts . pop ( field , None )
return ret
self . predictable = pop_bool ( " predictable " )
self . remote = pop_bool ( " remote " )
self . session = pop_bool ( " session " )
self . capsfile = opts . pop ( " caps " , None )
self . domcapsfile = opts . pop ( " domcaps " , None )
self . hv = None
if pop_bool ( " qemu " ) :
self . hv = " qemu "
if pop_bool ( " lxc " ) :
self . hv = " lxc "
if pop_bool ( " xen " ) :
self . hv = " xen "
2017-02-21 17:28:00 +03:00
if pop_bool ( " vz " ) :
self . hv = " vz "
2015-09-06 17:36:17 +03:00
self . conn_version = opts . pop ( " connver " , None )
if self . conn_version :
self . conn_version = int ( self . conn_version )
elif self . hv :
self . conn_version = 10000000000
self . libvirt_version = opts . pop ( " libver " , None )
if self . libvirt_version :
self . libvirt_version = int ( self . libvirt_version )
if opts :
raise RuntimeError ( " Unhandled virtinst test uri options %s " % opts )
2014-12-09 16:22:51 +03:00
##############
# Public API #
##############
2015-09-06 17:36:17 +03:00
def make_fake_uri ( self ) :
"""
If self . hv is set , we need to make a fake URI so that Connection
URI handling bits have something to work with .
"""
if self . hv :
return self . hv + " +abc:///system "
return self . open_uri
def overwrite_conn_functions ( self , conn ) :
"""
After the connection is open , we need to stub out various functions
depending on what magic bits the user specified in the URI
"""
# Fake capabilities
if self . capsfile :
2017-05-05 21:19:54 +03:00
capsxml = open ( self . capsfile ) . read ( )
2015-09-06 17:36:17 +03:00
conn . getCapabilities = lambda : capsxml
# Fake domcapabilities. This is insufficient since output should
# vary per type/arch/emulator combo, but it can be expanded later
# if needed
if self . domcapsfile :
2017-05-05 21:19:54 +03:00
domcapsxml = open ( self . domcapsfile ) . read ( )
2015-09-06 17:36:17 +03:00
def fake_domcaps ( emulator , arch , machine , virttype , flags = 0 ) :
ignore = emulator
ignore = flags
ignore = machine
ignore = virttype
ret = domcapsxml
if arch :
ret = re . sub ( " arch>.+</arch " , " arch> %s </arch " % arch , ret )
return ret
conn . getDomainCapabilities = fake_domcaps
if self . hv :
2016-06-17 15:07:36 +03:00
origcreate = conn . createXML
2015-09-06 17:36:17 +03:00
origdefine = conn . defineXML
def newcreate ( xml , flags ) :
2015-09-22 18:34:15 +03:00
xml = sanitize_xml_for_test_define ( xml )
2015-09-06 17:36:17 +03:00
return origcreate ( xml , flags )
def newdefine ( xml ) :
2015-09-22 18:34:15 +03:00
xml = sanitize_xml_for_test_define ( xml )
2015-09-06 17:36:17 +03:00
return origdefine ( xml )
2016-06-17 15:07:36 +03:00
conn . createXML = newcreate
2015-09-06 17:36:17 +03:00
conn . defineXML = newdefine