o
    bI                     @   s   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 ddlmZ ddl	m
Z
 ddl	mZ dd	lmZ dd
l	m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G dd dZG dd deZdS )    N   )generatename)progress)xmlutil)Guest)DeviceInterface)
DeviceDisk)log)DeviceChannelc              
   C   s   z|  |}W n tjy   Y dS w z#td| | dkr*td| |  td| |  W dS  tjyS } ztd|t	|d }t
|dd}~ww )z@
    Remove the existing VM with the same name if requested
    NzExplicitly replacing guest '%s'zDestroying guest '%s'zUndefining guest '%s'z+Could not remove old vm '%(vm)s': %(error)s)vmerror)lookupByNamelibvirtlibvirtErrorr	   debugIDdestroyundefine_strRuntimeError)connnamer   emsg r   */usr/share/virt-manager/virtinst/cloner.py_replace_vm   s&   
r   c                    s   d}t ||}d}d}|r,t d| }d}|r$tt| d }|d|  } fdd}|d	 }tj||d
||dS )zt
    If the orig name is "foo-clone", we don't want the clone to be
    "foo-clone-clone", we want "foo-clone1"
    z-clone[1-9]*$r   Fz[1-9]+$TNc                    s   t  j| S N)r   check_libvirt_collisionr   )nr   r   r   cbA   s   z _generate_clone_name.<locals>.cb-clone )sep	start_num	force_num)researchgroupintr   startr   generate_name)r   basenameregexmatchr'   r(   	num_matchr#   r   r"   r   _generate_clone_name1   s   r3   c           
         s   |du rdS |}d}d|v r&t |ddd dkr&|dd\}}d| }tj|}tj|}|d }|r>||kr>|}tj||} fdd}	tj||	|d	S )
z
    Generate desired cloned disk path name, derived from the
    original path, original VM name, and proposed new VM name
    Nr%   .r      r$   c                    s   t  | S r   )r   path_definitely_exists)pr"   r   r   r#   f      z%_generate_clone_disk_path.<locals>.cb)suffix)	lenrsplitospathdirnamer/   joinr   r.   )
r   orignamenewnameorigpathr=   r9   r>   r/   	clonebaser#   r   r"   r   _generate_clone_disk_pathI   s    rD   c                 C   s8   z|  |W S  tjy   ttdt| }|d w )NzDomain '%s' was not found.)r   r   r   
ValueErrorr   r   )r   r   r   r   r   r   
_lookup_vmk   s   rF   c                 C   s>   d}d}t | jtj| | ||}|| 	  |S )Ngư>F)
r   build_vol_installr   r<   r=   r/   get_source_pathget_parent_poolset_input_volget_vol_object)	orig_disknew_disksizesparsevol_installr   r   r   _build_clone_vol_installs   s   
rQ   c                 C   s   | j }tj}|stj}t|}|| ||_|s|  |S | rQ| sG|j	|j
ksG|  rGt|  tjrG| rGt| tjsQttd|  |  rj| rjt| |}|sd|j|_|| n
|  rt|| | |  |S )NzCClone onto existing storage volume is not currently supported: '%s')r   r   DEVICE_DISKDEVICE_CDROMset_source_pathdevicevalidaterK   	is_remotetype
TYPE_BLOCKrH   r<   accessR_OKW_OKr   r   wants_storage_creationrQ   capacity
allocationset_vol_installset_local_disk_to_clone)rL   	clonepathallow_createrO   r   rU   rM   rP   r   r   r   _build_clone_disk   sJ   

rd   c                 C   sN   |   rtd|   S | jdkr%| jj}|dkrtdd S td| S dS )zN
    If the disk storage is not cloneable, return a string explaining why
    zDisk path '%s' does not exist.networkrbdz)Cloning rbd volumes is not yet supported.z8 https://github.com/virt-manager/virt-manager/issues/177z(Disk network type '%s' is not cloneable.N)r]   r   rH   rX   sourceprotocol)diskprotor   r   r   _get_cloneable_msg   s   
rk   c                 C   s6   |   rtdS | jrtdS | js| jrtdS d S )NzNo storage to clone.z	Read OnlyzMarked as shareable)is_emptyr   	read_only	shareabletransient_shareBacking)ri   r   r   r   _get_shareable_msg   s   rp   c                   @   s   e Zd ZdZdZd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d Zdd Zdd Zdd Zdd Zdd ZdS ) _CloneDiskInfoa  
    Class that tracks some additional information about how we want
    to default handle each disk of the source VM

    For any source disk there's 3 main scenarios:

    * clone: Copy contents from src to dst. If dst path doesn't
            exist we attempt to create it. If it exists we overwrite it
    * preserve: Destination path is an existing, and no copying is performed.
    * share: Original disk XML is used unchanged for the new disk
    r         c                 C   sb   t |j| d| _| j  d | _t| j| _d| _d | _	d | _
|   |  r/|   d S d S )Nparsexmlr   )r   r   get_xmlri   set_backend_for_existing_pathrM   rp   
_share_msg_cloneable_msg_newpath_msg_actionset_clone_requestedget_share_msgset_share_requested)selfsrcdiskr   r   r   __init__   s   
z_CloneDiskInfo.__init__c                 C      | j | jfv S r   )r{   _ACTION_CLONEr   r   r   r   is_clone_requested      z!_CloneDiskInfo.is_clone_requestedc                 C   r   r   )r{   _ACTION_SHAREr   r   r   r   is_share_requested   r   z!_CloneDiskInfo.is_share_requestedc                 C   r   r   )r{   _ACTION_PRESERVEr   r   r   r   is_preserve_requested   r   z$_CloneDiskInfo.is_preserve_requestedc                 C   s   || j kr
|| _ d S d S r   )r{   )r   actionr   r   r   _set_action   s   

z_CloneDiskInfo._set_actionc                 C      |  | j d S r   )r   r   r   r   r   r   r|         z"_CloneDiskInfo.set_clone_requestedc                 C   r   r   )r   r   r   r   r   r   r~      r   z"_CloneDiskInfo.set_share_requestedc                 C   r   r   )r   r   r   r   r   r   set_preserve_requested   r   z%_CloneDiskInfo.set_preserve_requestedc              
   C   s   |    }|r|  }|rd S zt| j|||| _W d S  tyD } ztjddd t	d|t
|d }|| _W Y d }~d S d }~ww )NzError setting clone path.T)exc_infoz4Could not use path '%(path)s' for cloning: %(error)s)r=   r   )r   get_cloneable_msgClonerbuild_clone_diskri   rM   	Exceptionr	   r   r   r   rz   )r   r=   rO   rc   r   r   errr   r   r   set_new_path   s"   

z_CloneDiskInfo.set_new_pathc                 C      | j S r   )rx   r   r   r   r   r}        z_CloneDiskInfo.get_share_msgc                 C   s   | j dkrt| j| _ | j S )Nr   )ry   rk   ri   r   r   r   r   r     s   
z _CloneDiskInfo.get_cloneable_msgc                 C   r   r   )rz   r   r   r   r   get_newpath_msg  r   z_CloneDiskInfo.get_newpath_msgc                 C   sT   |   r|  r|  }td| }t||  rd S |  r(|  }t|d S )Nz1Could not determine original disk information: %s)r   r   r   rE   r   r   )r   r   r   r   r   r   raise_error  s   z_CloneDiskInfo.raise_errorN)__name__
__module____qualname____doc__r   r   r   r   r   r   r   r   r|   r~   r   r   r}   r   r   r   r   r   r   r   rq      s$    rq   c                   @   s   e Zd Zedd Zedd Zedd Zd*dd	Zd
d Zdd Z	e
dd Ze
dd Ze
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+d(d)ZdS ),r   c                 C   s
   t | |S r   )r3   )r   r/   r   r   r   generate_clone_name  s   
zCloner.generate_clone_namec                 C      t | |||S r   )rD   )r   r@   rA   rB   r   r   r   generate_clone_disk_path      zCloner.generate_clone_disk_pathc                 C   r   r   )rd   )rL   rb   rc   rO   r   r   r   r   $  r   zCloner.build_clone_diskNc                 C   sF   || _ d | _d | _g | _d | _| || d | _d| _d| _d| _	d S )NTF)
r   
_src_guest
_new_guest
_diskinfos_nvram_diskinfo	_init_src_new_nvram_path_sparse_replace_reflink)r   r   src_namesrc_xmlr   r   r   r   (  s   
zCloner.__init__c           	      C   s   |s"t | j|}| d }|tjfvrttdtj}||}t	
d| t| j|d| _t| j|d| _|   | jjjD ]
}| jt| qAdd | jD D ]}|j}t	
d| |  qT| jjjr~t| j}|| jjj t|| _dS dS )	zf
        Set up the source VM info we are cloning, from passed in VM name
        or full XML
        r   z Domain to clone must be shutoff.zOriginal XML:
%srt   c                 S   s   g | ]}|  r|qS r   )r   ).0dr   r   r   
<listcomp>S  s    z$Cloner._init_src.<locals>.<listcomp>zWants cloning: size=%s path=%sN)rF   r   infor   VIR_DOMAIN_SHUTOFFr   r   VIR_DOMAIN_XML_SECUREXMLDescr	   r   r   r   r   _init_new_guestdevicesri   r   appendrq   get_sizerH   r<   nvramr   rT   r   )	r   r   r   domstatusflagsri   diskinfo	old_nvramr   r   r   r   <  s.   


zCloner._init_srcc                 C   s   d| j _d| j _d| j _t| j| j _| j jjD ]}|j	r-|j	dkr-t
td d|_	q| j jjD ]}d|_t| j|_q3| j jjD ]}|jtjkr_|jr_|jjr_|j|jjv r_d|j_qEt| j| j}t
d| | | dS )zS
        Perform the series of unconditional new VM changes we always make
        Nr   zLSetting the graphics device port to autoport, in order to avoid conflicting.zAuto-generated clone name '%s')r   idtitleuuidr   generate_uuidr   r   graphicsportr	   warningr   	interface
target_devr   generate_macmacaddrchannelrX   r
   	TYPE_UNIXtarget_namerg   r=   r   r   r   r   set_clone_name)r   devifacer   new_namer   r   r   r   ]  s0   zCloner._init_new_guestc                 C   s   | j jS )z<
        The name of the original VM we are cloning
        )r   r   r   r   r   r   r     s   zCloner.src_namec                 C   r   )zB
        The Guest instance of the new XML we will create
        )r   r   r   r   r   	new_guest  s   zCloner.new_guestc                 C   r   r   )r   r   r   r   r   nvram_diskinfo  s   zCloner.nvram_diskinfoc                 C   s   || j _d S r   )r   r   )r   r   r   r   r   r     r8   zCloner.set_clone_namec                 C   s   || j _dS )z5
        Override the new VMs generated UUId
        N)r   r   )r   r   r   r   r   set_clone_uuid  s   zCloner.set_clone_uuidc                 C   s   t || _dS )zo
        If True, don't check for clone name collision, simply undefine
        any conflicting guest.
        N)boolr   r   valr   r   r   set_replace  s   zCloner.set_replacec                 C   
   || _ dS )z3
        If true, use COW lightweight copy
        N)r   )r   reflinkr   r   r   set_reflink     
zCloner.set_reflinkc                 C   r   )zC
        If True, attempt sparse allocation during cloning
        N)r   )r   flgr   r   r   
set_sparse  r   zCloner.set_sparsec                 C   s   | j dd S )z=
        Return the list of _CloneDiskInfo instances
        N)r   r   r   r   r   get_diskinfos  s   zCloner.get_diskinfosc                 C   s   dd |   D S )zM
        Return a list of _CloneDiskInfo that are tagged for cloning
        c                 S   s   g | ]}|  s|qS r   )r   )r   dir   r   r   r     s    
z1Cloner.get_nonshare_diskinfos.<locals>.<listcomp>)r   r   r   r   r   get_nonshare_diskinfos  s   zCloner.get_nonshare_diskinfosc                 C   r   )zk
        If the VM needs to have nvram content cloned, this overrides the
        destination path
        N)r   r   r   r   r   set_nvram_path  s   
zCloner.set_nvram_pathc                 C   s   | j sd S | j}|d u r tj| jjj}tj|d| jj }| j }t	| j
}|| t	| j
}||j  | rZ| rZ|j rZ||| j |  | j|j _nd | _ | | jj_d S )Nz
%s_VARS.fd)r   r   r<   r=   r>   r   r   r?   r   r   r   rT   ri   rH   r   r]   rK   r   r   r   r   rM   get_vol_installr   )r   new_nvram_path	nvram_dirr   	new_nvramr   r   r   r   _prepare_nvram  s.   


zCloner._prepare_nvramc              
   C   sV  zt j| j| jj| j dd W n ty' } ztd| }t|dd}~ww |  D ]f}|j	}|j
sMt| j| j| jj| }||| j |j
sMq,|j
}|sTJ td| |  | jrj| }| j|_| jjj	D ]
}|j|jkry|}	qo|	d |j|	_|j|	_|j|	_|	|  q,|   t| j ! | j! }
td|
 dS )zR
        Validate and set up all parameters needed for the new (clone) VM
        F)check_collisionrV   zInvalid name for new guest: %sNzCloning srcpath=%s dstpath=%szClone guest xml diff:
%s)"r   validate_namer   r   r   r   rE   r   r   ri   rM   r   r   r   r   rH   r   r   r	   r   r   r   r   r   targetrT   rX   driver_namedriver_typer   r   diffr   rv   )r   r   r   r   rL   newpathrM   rP   ri   xmldiskr   r   r   r   prepare  sV   


zCloner.preparec              
   C   s   t d t|}d}z3| jrt| j| jj | j	| j
 }|  }| jr.|| j |D ]}| s7q0|j| q0W n tyZ } zt dt| |rU|   d}~ww t d dS )zs
        Actually perform the duplication: cloning disks if needed and defining
        the new clone xml.
        zStarting duplicate.NzDuplicate failed: %szDuplicating finished.)r	   r   r   ensure_meterr   r   r   r   r   	defineXMLrv   r   r   r   r   rM   build_storager   r   r   )r   meterr   	diskinfosr   r   r   r   r   start_duplicate  s.   

zCloner.start_duplicate)NNr   )r   r   r   staticmethodr   r   r   r   r   r   propertyr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r     s6    



!$


 5r   )r)   r<   r   r%   r   r   r   guestr   r   r   r   loggerr	   r
   r   r3   rD   rF   rQ   rd   rk   rp   rq   objectr   r   r   r   r   <module>   s*   	"+	T