o
    bt                     @   s   d dl Z d dlZd dlZd dlZd dlZddlmZ ddlmZ ddl	m
Z
 edejv Zg Zg ZG dd deZG d	d
 d
eZe ZG dd deZG dd deZG dd deZG dd deZG dd deZG dd deZdS )    N   )log)XMLAPI)xmlutilVIRTINST_TEST_SUITEc                   @   s<   e Zd ZdZdZdZdZdZdZdZ	dZ
dd Zdd	 ZdS )
XMLManualActionz[
    Helper class for tracking and performing the user requested manual
    XML action
    r         Nc                 C   s|   ddd}| j r|| j tjS | jr|| jtjS | j}| jr#| j}ndt|vr/td| |	dd\}}||tj
|S )Nc                 S   s
   | ||fS N )xavr   r   ./usr/share/virt-manager/virtinst/xmlbuilder.py_ret,      
z+XMLManualAction._process_args.<locals>._ret=z4%s: Setting xpath must be in the form of XPATH=VALUEr   r
   )xpath_deleter   ACTION_DELETExpath_createACTION_CREATE	xpath_setxpath_valuestr	Exceptionrsplit
ACTION_SET)selfr   xpathvalr   r   r   _process_args+   s    
zXMLManualAction._process_argsc                 C   sx   | j s| js| js| jsd S |  \}}}|dr||}|| jkr'd}n|| jkr/d}n|p2d }|j	
|| d S )N.FT)r   r   r   r   r    
startswithmake_abs_xpathr   r   xmlapiset_xpath_content)r   xmlstater   actionvaluesetvalr   r   r   perform?   s"   



zXMLManualAction.perform)__name__
__module____qualname____doc__r   r   r   r   r   r   r   r    r*   r   r   r   r   r      s    r   c                   @   s8   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d ZdS )_XMLPropertyCachezn
    Cache lookup tables mapping classes to their associated
    XMLProperty and XMLChildProperty classes
    c                 C   s   i | _ i | _d S r
   )_name_to_prop_prop_to_namer   r   r   r   __init__X   s   
z_XMLPropertyCache.__init__c                 C   s   t |d |j }|| jvr;i }tt|d d D ]}|j D ]\}}t||r4|||< || j	|< q"q|| j|< | j| S )N-)
r   r+   r0   reversedtypemro__dict__items
isinstancer1   )r   cls
checkclass	cachenameretckeyr   r   r   r   _get_prop_cache\   s   




z!_XMLPropertyCache._get_prop_cachec                 C      |  |jtS r
   )rB   	__class__XMLPropertyr   instr   r   r   get_xml_propsh      z_XMLPropertyCache.get_xml_propsc                 C   rC   r
   )rB   rD   XMLChildPropertyrF   r   r   r   get_child_propsk   rI   z!_XMLPropertyCache.get_child_propsc                 C   s
   | j | S r
   )r1   )r   propinstr   r   r   get_prop_namen   r   z_XMLPropertyCache.get_prop_nameN)	r+   r,   r-   r.   r3   rB   rH   rK   rM   r   r   r   r   r/   S   s    r/   c                   @   s*   e Zd ZdZd
ddZdd Zdd Zd	S )_XMLChildListz
    Little wrapper for a list containing XMLChildProperty output.
    This is just to insert a dynamically created add_new() function
    which instantiates and appends a new child object
    Tc                 C   s4   t |  || _|| _|| _|D ]}| | qd S r
   )listr3   _childclass_xmlbuilder_is_xmlappend)r   
childclasscopylist
xmlbuilderis_xmlir   r   r   r3   {   s   
z_XMLChildList.__init__c                 C   s   d}| j r
| jjf}| j| S )z>
        Instantiate a new child object and return it
        r   )rR   rQ   connrP   )r   argsr   r   r   new   s   

z_XMLChildList.newc                 C   s,   |   }| jr| j| |S | | |S )zJ
        Instantiate a new child object, append it, and return it
        )r[   rR   rQ   	add_childrS   )r   objr   r   r   add_new   s   
z_XMLChildList.add_newN)T)r+   r,   r-   r.   r3   r[   r^   r   r   r   r   rN   u   s
    
	rN   c                   @   s    e Zd Zdd Zedd ZdS )_XMLPropertyBasec                 C   s   d | _ tj| ||d d S )N)fgetfset)	_propnamepropertyr3   )r   r`   ra   r   r   r   r3      s   z_XMLPropertyBase.__init__c                 C   s   | j s	t| | _ | j S )z
        The variable name associated with this XMLProperty. So with
        a definition like

            foo = XMLProperty("./@bar")

        and this will return "foo".
        )rb   
_PropCacherM   r2   r   r   r   propname   s   
z_XMLPropertyBase.propnameN)r+   r,   r-   r3   rc   re   r   r   r   r   r_      s    r_   c                   @   sb   e Zd ZdZdddZdd Zdd	 Zd
d Zdd Zdd Z	dd Z
dd Zdd Zdd ZdS )rJ   az  
    Property that points to a class used for parsing a subsection of
    of the parent XML. For example when we deligate parsing
    /domain/cpu/feature of the /domain/cpu class.

    @child_class: XMLBuilder class this property is tracking. So for
        guest.devices.disk this is DeviceDisk
    @relative_xpath: Relative location where the class is rooted compared
        to its xmlbuilder root path. So if xmlbuilder is ./foo and we
        want to track ./foo/bar/baz instances, set relative_xpath=./bar
    @is_single: If True, this represents an XML node that is only expected
        to appear once, like <domain><cpu>
    r!   Fc                 C   s&   || _ || _|| _t| | jd  d S r
   )child_class	is_singlerelative_xpathr_   r3   _fget)r   rf   rh   rg   r   r   r   r3      s   zXMLChildProperty.__init__c                 C      dt | jt| f S )Nz<XMLChildProperty %s %s>)r   rf   idr2   r   r   r   __repr__      zXMLChildProperty.__repr__c                 C   s*   | j |jvr| jsg |j| j < |j| j  S r
   )re   
_propstorerg   r   rV   r   r   r   _get   s   zXMLChildProperty._getc                 C   s$   | j r| |S t| j| ||S r
   )rg   rp   rN   rf   ro   r   r   r   ri      s   
zXMLChildProperty._fgetc                 C   s>   | j r| |  d S | |d d  D ]}|| qd S r
   )rg   rp   clearremove_childr   rV   r]   r   r   r   rq      s
   zXMLChildProperty.clearc                 C   s   |  ||| d S r
   )rp   insert)r   rV   newobjidxr   r   r   rt      rm   zXMLChildProperty.insertc                 C      |  || d S r
   )rp   rS   )r   rV   ru   r   r   r   rS         zXMLChildProperty.appendc                 C   rw   r
   )rp   removers   r   r   r   ry      rx   zXMLChildProperty.removec                 C   s   ||j | j< d S r
   rn   re   rs   r   r   r   set      zXMLChildProperty.setc                 C   s   | j d |j S )N/)rh   XML_NAME)r   rQ   r]   r   r   r   get_prop_xpath   r|   zXMLChildProperty.get_prop_xpathN)r!   F)r+   r,   r-   r.   r3   rl   rp   ri   rq   rt   rS   ry   r{   r   r   r   r   r   rJ      s    
rJ   c                   @   sj   e Zd Z		dddZdd Zdd Zdd	 Zd
d Zdd Zdd Z	dd Z
dd Zdd Zdd ZdS )rE   Fc                 C   s   || _ | j std|| _|| _|| _|| _|| _tdd | j| j| j| jfD }|dkr4tdd| _	t
r>t|  t| | j| j dS )a7  
        Set a XMLBuilder class property that maps to a value in an XML
        document, indicated by the passed xpath. For example, for a
        <domain><name> the definition may look like:

          name = XMLProperty("./name")

        When building XML from scratch (virt-install), 'name' works
        similar to a regular class property(). When parsing and editing
        existing guest XML, we  use the xpath value to get/set the value
        in the parsed XML document.

        :param xpath: xpath string which maps to the associated property
                      in a typical XML document
        :param name: Just a string to print for debugging, only needed
            if xpath isn't specified.
        :param is_bool: Whether this is a boolean property in the XML
        :param is_int: Whether this is an integer property in the XML
        :param is_yesno: Whether this is a yes/no property in the XML
        :param is_onoff: Whether this is an on/off property in the XML
        :param do_abspath: If True, run os.path.abspath on the passed value
        z"XMLProperty: xpath must be passed.c                 S   s   g | ]}t t|qS r   )intbool).0rX   r   r   r   
<listcomp>	      z(XMLProperty.__init__.<locals>.<listcomp>r   z$Conflict property converter options.FN)_xpathr   DevError_is_bool_is_int	_is_yesno	_is_onoff_do_abspathsum_is_tracked_trackprops	_allpropsrS   r_   r3   gettersetter)r   r   is_boolis_intis_yesnois_onoff
do_abspath	conflictsr   r   r   r3      s&   


zXMLProperty.__init__c                 C   rj   )Nz<XMLProperty %s %s>)r   r   rk   r2   r   r   r   rl     rm   zXMLProperty.__repr__c              
   C   s   | j rt|S | jrA|d urAzi }dt|v rd|d< t|fi |}W |S  ty@ } ztd| |}W Y d }~|S d }~ww | jrX|dkrLd}|S |dkrTd}|S |}|S | j	ro|d	krcd}|S |d
krkd}|S |}|S |}|S )N0x   basez%Error converting XML value to int: %syesTnoFonoff)
r   r   r   r   r   
ValueErrorr   debugr   r   )r   r   	intkwargsr?   er   r   r   _convert_get_value  sF   
zXMLProperty._convert_get_valuec                 C   s   | j r|d urtj|}|S | jr"|du rd}|S |du r d}|S | jr5|du r-d}|S |du r3d}|S | jrP|d urPi }dt|v rHd|d	< t|fi |}|S )
NTr   Fr   r   r   r   r   r   )	r   ospathabspathr   r   r   r   r   )r   r   r   r   r   r   _convert_set_value=  s.   zXMLProperty._convert_set_valuec                 C   s&   |j }| j|v r|| j= ||| j< dS )z
        This stores the value in XMLBuilder._propstore
        dict as propname->value. This saves us from having to explicitly
        track every variable.
        Nrz   )r   rV   r   	propstorer   r   r   _nonxml_fsetQ  s   
zXMLProperty._nonxml_fsetc                 C   s   |j | jdS )zb
        The flip side to nonxml_fset, fetch the value from
        XMLBuilder._propstore
        N)rn   getre   ro   r   r   r   _nonxml_fget]  s   zXMLProperty._nonxml_fgetc                 C   s$   |j }| j|v r| |d  d S d S r
   )rn   re   r   )r   rV   r   r   r   r   rq   d  s   
zXMLProperty.clearc                 C   sF   t r| jst|  d| _| j|jv r| |}n| |}| |S )ao  
        Fetch value at user request. If we are parsing existing XML and
        the user hasn't done a 'set' yet, return the value from the XML,
        otherwise return the value from propstore

        If this is a built from scratch object, we never pull from XML
        since it's known to the empty, and we may want to return
        a 'default' value
        T)	r   r   
_seenpropsrS   re   rn   r   _get_xmlr   )r   rV   r   r   r   r   r   o  s   




zXMLProperty.getterc                 C   s    |j | j}|j j|| jS )zJ
        Actually fetch the associated value from the backing XML
        )	_xmlstater#   r   r$   get_xpath_contentr   )r   rV   r   r   r   r   r     s   zXMLProperty._get_xmlc                 C   s4   t r| jst|  d| _| |}| || dS )z
        Set the value at user request. This just stores the value
        in propstore. Setting the actual XML is only done at
        get_xml time.
        TN)r   r   r   rS   r   r   )r   rV   r   r)   r   r   r   r     s
   


zXMLProperty.setterc                 C   s"   |j | j}|j j|| dS )zC
        Actually set the passed value in the XML document
        N)r   r#   r   r$   r%   )r   rV   r)   r   r   r   r   _set_xml  s   zXMLProperty._set_xmlN)FFFFF)r+   r,   r-   r3   rl   r   r   r   r   rq   r   r   r   r   r   r   r   r   rE      s    
0rE   c                   @   sD   e Zd Zdd Zdd Zdd Zdd Zd	d
 Zdd Zdd Z	dS )	_XMLStatec                 C   sz   || _ d| _d| j v r| j dd }d|tj| f | _|p d| _|o'| p)d| _d | _| o3| | _	| 
|| d S )N :r   z xmlns:%s='%s')
_root_name
_namespacesplitr   
NAMESPACES_relative_object_xpath	abs_xpath_parent_xpathr$   is_buildparse)r   	root_nameparsexmlparentxmlstaterelative_object_xpathnsr   r   r   r3     s   


z_XMLState.__init__c                 C   s   |r|j p| j | _ |j| _d S |sd| j| jf }n| jr0d|vr0|d| j d| j | j }zt|| _W n tyE   td|  w | j sW| j	| j
dd  d S d S )Nz<%s%s/>xmlns<zError parsing xml=
%sr   r5   )r   r$   r   r   replacer   r   r   r   validate_root_namer   )r   r   r   r   r   r   r     s&   z_XMLState.parsec                 C      |pd| _ d S Nr   )r   r   r   r   r   r   set_relative_object_xpath  rI   z#_XMLState.set_relative_object_xpathc                 C   r   r   )r   r   r   r   r   set_parent_xpath  rI   z_XMLState.set_parent_xpathc                 C   s   | dr|dd  }|| S )Nr!   r   )r"   )r   x1x2r   r   r   _join_xpath  s   
z_XMLState._join_xpathc                 C   s   |  | jpd| jp
dS )Nr!   )r   r   r   r2   r   r   r   r     s   z_XMLState.abs_xpathc                 C   s   |  |  pd|S )a  
        Convert a relative xpath to an absolute xpath. So for DeviceDisk
        that's part of a Guest, accessing driver_name will do convert:
            ./driver/@name
        to an absolute xpath like:
            ./devices/disk[3]/driver/@name
        r!   )r   r   r   r   r   r   r#     s   z_XMLState.make_abs_xpathN)
r+   r,   r-   r3   r   r   r   r   r   r#   r   r   r   r   r     s    r   c                   @   s   e Zd ZdZg ZdZdZedd Zedd Z			d5dd	Z
d
d Zdd Zdd Zdd Zd6ddZdd Zdd Zdd Zdd Zdd Zdd Zd d! Zd7d#d$Zd%d& Zd'd( Zd8d)d*Zd+d, Zd-d. Zd/d0 Zd1d2 Zd3d4 ZdS )9
XMLBuilderz>
    Base for all classes which build or parse domain XML
    NFc                 C   s   t | | d S r
   )r   register_namespace)nsnameurir   r   r   r     s   zXMLBuilder.register_namespacec                 C   sL   dg}|st td|  |D ]}||vrqtd| ||d }t |d S )N z#A name must be specified for the %szD%(objecttype)s name '%(name)s' can not contain '%(char)s' character.)
objecttypenamechar)r   _)
name_labelr   forbidr@   msgr   r   r   validate_generic_name   s   
z XMLBuilder.validate_generic_namec                 C   st   || _ | jr|ddd}ddd |D }t | _t| j	|||| _
|   |   ttg | dd| _dS )	z
        Initialize state

        :param conn: VirtinstConnection to validate device against
        :param parsexml: Optional XML string to parse

        The rest of the parameters are for internal use only
        asciiignorer   c                 S   s   g | ]	}|t jv r|qS r   )string	printable)r   r@   r   r   r   r      s    z'XMLBuilder.__init__.<locals>.<listcomp>F)rW   N)rY   _XML_SANITIZEencodedecodejoincollectionsOrderedDictrn   r   r~   r   _validate_xmlbuilder_initial_child_parserN   r   xml_actions)r   rY   r   r   r   r   r   r   r3     s   

zXMLBuilder.__init__c                 C   s   | j jd }t| j |drd S |  }|  }| jD ]}||vr+||vr+td| qg }| D ]}|j	|v rAtd|j	 |
|j	 q2t| j |d d S )N_xmlbuilder_validatedFz'key '%s' must be xml prop or child propz'can't register duplicate child_class=%sT)rD   r+   getattr_all_xml_props_all_child_props_XML_PROP_ORDERr   r   valuesrf   rS   setattr)r   cachekeyxmlprops
childpropsrA   childclasses	childpropr   r   r   r   ,  s*   

zXMLBuilder._validate_xmlbuilderc                 C   s   t |   D ]G}|j}|| |}|jr&|| j| j|d}|| | q| jj	
| j|}t|D ]}d|d  }|| j| j|| d}|| | q5qd S )N)r   r   [%d]r   )rO   r   r   rf   r   rg   rY   r   r{   r$   countr#   rangerS   )r   xmlproprf   	prop_pathr]   	nodecountrv   idxstrr   r   r   r   C  s,   
zXMLBuilder._initial_child_parsec                 C   s"   d| j jdd | jt| f S )Nz
<%s %s %s>r!   r5   )rD   r+   r   r~   rk   r2   r   r   r   rl   Z  s   
zXMLBuilder.__repr__c                 C   s   | j j}| j jr| }| | || j d}|s|S |  d }|	ds9|	dr9|
dd | }|dsB|d7 }|S )z1
        Return XML string of the object
        r!   r5   r   r   r   
)r   r$   r   copy_api_add_parse_bitsget_xmlr#   rstrip
splitlinesr"   r   endswith)r   r$   r?   lastliner   r   r   r   c  s   

zXMLBuilder.get_xmlc                 C   s   t |   }|t |   7 }|D ]}||  qttd| j	 }|s+|r7| jj
| j	  dS | jj
| j	  dS )z
        Wipe out all properties of the object

        :param leave_stub: if True, don't unlink the top stub node,
            see virtinst/cli usage for an explanation
        z^.*\[\d+\]$N)rO   r   r   r   rq   r   rematchr   r   r$   
node_clearnode_force_remove)r   
leave_stubpropspropis_childr   r   r   rq   y  s   zXMLBuilder.clearc                 C   s   dS )z]
        Validate any set values and raise an exception if there's
        a problem
        Nr   r2   r   r   r   validate  s    zXMLBuilder.validatec                 C   s   |}dS )z5
        Encode any default values if needed
        Nr   )r   guestr   r   r   r   set_defaults  s   zXMLBuilder.set_defaultsc                 C   s
   | j  S )z
        Return the location of the object in the XML document. This is
        basically the absolute xpath, but the value returned should be
        treated as opaque, it's just for cross XML comparisons. Used
        in virt-manager code
        )r   r   r2   r   r   r   
get_xml_id  s   
zXMLBuilder.get_xml_idc                 C   s4   | j  }d|vrdS t|ddd dd S )z
        This is basically the offset parsed out of the object's xpath,
        minus 1. So if this is the fifth <disk> in a <domain>, ret=4.
        If this is the only <cpu> in a domain, ret=0.
        [r   r   ])r   r   r   r   stripr   r   r   r   get_xml_idx  s   
zXMLBuilder.get_xml_idxc                 C   
   t | S )zQ
        Return a list of all XMLProperty instances that this class has.
        )rd   rH   r2   r   r   r   r        
zXMLBuilder._all_xml_propsc                 C   r  )zV
        Return a list of all XMLChildProperty instances that this class has.
        )rd   rK   r2   r   r   r   r     r  zXMLBuilder._all_child_propsc                 C   sN   |   }d }t| D ]}|jrq||ju r|} nq|s%td| |S )Nz-Didn't find child property for child_class=%s)r   rO   r   rg   rf   r   r   )r   rf   r   r?   r   r   r   r   _find_child_prop  s   
zXMLBuilder._find_child_propr5   c                 C   sZ   | j | |dkr| j | |  D ]}tt| |g D ]
}|| j   qqdS )z=
        Change the object hierarchy's cached xpaths
        r5   N)	r   r   r   r   r   listifyr   _set_xpathsr   )r   parent_xpathr   re   pr   r   r   r    s   zXMLBuilder._set_xpathsc                 C   s   i }|    D ]>\}}tt| |D ]1}d}|js4|j}||vr&d||< ||  d7  < d||  }|| |}|| j	
 ||  qqdS )z
        Walk the list of child properties and make sure their
        xpaths point at their particular element. Needs to be called
        whenever child objects are added or removed
        r   r   r   r   N)r   r:   r   r  r   rg   rD   r   r  r   r   )r   	typecountre   r   r]   r   
class_typer   r   r   r   _set_child_xpaths  s    zXMLBuilder._set_child_xpathsc                 O   sJ   | j j|i | |  D ]}tt| |g D ]	}|d| j  qqdS )zR
        Set new backing XML objects in ourselves and all our child props
        N)r   r   r   r   r  r   _parse_with_children)r   rZ   kwargsre   r  r   r   r   r    s   zXMLBuilder._parse_with_childrenc                 C   s   |  |j}| }|du r|| | n|| || |   |jjsG|j 	ddd }d|j 
d }| jjt||d | |d| j dS )z
        Insert the passed XMLBuilder object into our XML document. The
        object needs to have an associated mapping via XMLChildProperty
        Nr}   r   r   r   r   )r  rD   r   rS   rt   r  r   r   r   r   r   r$   node_add_xmltextwrapindentr  )r   r]   rv   r   xml	use_xpathr!  r   r   r   r\     s   zXMLBuilder.add_childc                 C   s\   |  |j}|| | |j }| }|dd ||d | jj	| | 
  dS )zw
        Remove the passed XMLBuilder object from our XML document, but
        ensure its data isn't altered.
        N)r  rD   ry   r   r   r   r  r  r$   r  r  )r   r]   r   r   r"  r   r   r   rr     s   
zXMLBuilder.remove_childc                 C   sn   | j js%| }d|d }t| |d  }| j j	|| dS |
 }| | | j||d dS )a6  
        Replace the origobj child with the newobj. For is_build, this
        replaces the objects, but for !is_build this only replaces the
        XML and keeps the object references in place. This is hacky and
        it's fixable but at time or writing it doesn't matter for
        our usecases.
        r   r}   r   )rv   N)r   r   r  r   r   r!  r   r  r$   node_replace_xmlr  rr   r\   )r   origobjru   r   r!  r"  origidxr   r   r   replace_child  s   
zXMLBuilder.replace_childc                 C   s   t | |rdS || jvS )zL
        Return True if the property name has never had a value set
        F)r   rn   )r   re   r   r   r   _prop_is_unset1  s   

zXMLBuilder._prop_is_unsetc                 C   sD   | j  }| jj}z|| j_|  W || j_|| _ S || j_|| _ w )zf
        Callback that adds the implicitly tracked XML properties to
        the backing xml.
        )rn   copyr   r$   _do_add_parse_bits)r   r$   origpropstoreorigapir   r   r   r   >  s   
zXMLBuilder._add_parse_bitsc                    s  |   }|    fdd| jD }t| jD ]}||v r)|| |d| q| v r3|d| qtt 	 D ]}||vrG|
| q<|D ](}||v r\|| | | j|  qJ| v rrtt| |D ]	}|| jj qhqJ| jD ]}|| j qvd S )Nc                    s   g | ]}| vr|qS r   r   )r   r  r   r   r   r   R  r   z1XMLBuilder._do_add_parse_bits.<locals>.<listcomp>r   )r   r   rn   r6   r   ry   rt   sortedrO   keysrS   r   r   r  r   r   r   r$   r   r*   )r   r   do_orderrA   r]   manualactionr   r-  r   r*  L  s0   


zXMLBuilder._do_add_parse_bits)NNN)F)r5   r
   ) r+   r,   r-   r.   r   r~   r   staticmethodr   r   r3   r   r   rl   r   rq   r  r  r  r  r   r   r  r  r  r  r\   rr   r'  r(  r   r*  r   r   r   r   r     sB    


	
	

	r   )r   r   r  r   r   loggerr   r$   r   r   r   r   environr   r   r   objectr   r/   rd   rO   rN   rc   r_   rJ   rE   r   r   r   r   r   r   <module>   s*   6#9 <I