Advertisement
bret_miller

xenmigrate.py

Dec 6th, 2011
3,457
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 19.46 KB | None | 0 0
  1. #!/usr/bin/env python
  2. """
  3. xenmigrate.py
  4. Xen Migrate
  5. Migrate XenServer to Open Source Xen
  6. (c)2011 Jolokia Networks and Mark Pace -- jolokianetworks.com
  7. pace@jolokianetworks.com
  8. AGPL License
  9. USE THIS SOFTWARE AT YOUR OWN RISK!
  10. PLEASE REPORT BUGS SO THEY CAN GET FIXED!
  11. """
  12.  
  13. import gzip
  14. import fnmatch
  15. import os
  16. import subprocess
  17. import sys
  18.  
  19. def docmd(cmd):
  20.     """
  21.    run a command and return the communicate PIPE
  22.    """
  23.     if debug:
  24.         print 'running cmd       :',cmd
  25.     execute=subprocess.Popen([cmd],shell=True,stdout=subprocess.PIPE)
  26.     return execute.communicate()[0]
  27.  
  28. def exportvm(vmname,lvdev,destfile,gz=False):
  29.     """
  30.    export lvdev to dest
  31.    """
  32.     if debug:
  33.         print 'exporting vm      :',vmuuid
  34.     # we'll need to handle difference block sizes at some point
  35.     blocksize=1024*1024
  36.     notification=float(2**30) # 2**30=GB
  37.     if gz:
  38.         notification=notification/4
  39.     vmuuid=getvmuuid(vmname)
  40.     vmstatus=getvmstatus(vmuuid)
  41.     if vmstatus=='running':
  42.         cmd='xe vm-shutdown -u root uuid='+vmuuid
  43.         if debug:
  44.             print 'halting vm uuid   :',vmuuid
  45.         docmd(cmd)
  46.     vmstatus=getvmstatus(vmuuid)
  47.     if vmstatus=='halted':
  48.         if not os.path.exists(destfile):
  49.             try:
  50.                 print '\nActivating Volume:'
  51.                 cmd='lvchange -v -ay '+lvdev
  52.                 lvchange=docmd(cmd)
  53.                 source=open(lvdev,'rb')
  54.                 if gz:
  55.                     dest=gzip.GzipFile(destfile,'wb')
  56.                 else:
  57.                     dest=open(destfile,'wb')
  58.                 noticetick=notification/(2**30)
  59.                 print '\nRW notification every: '+str(noticetick)+'GB'
  60.                 notification=notification/blocksize
  61.                 sys.stdout.write('Exporting: ')
  62.                 write=0
  63.                 while True:
  64.                     write=write+1
  65.                     data=source.read(blocksize)
  66.                     if write%notification==0:
  67.                         sys.stdout.write(str((write/notification)*noticetick)+'GBr')
  68.                     if len(data)==0:
  69.                         break #EOF
  70.                     dest.write(data)
  71.                     if write%notification==0:
  72.                         sys.stdout.write('w ')
  73.                     sys.stdout.flush()
  74.                 print '\nSuccessful export'
  75.             finally:
  76.                 try:
  77.                     source.close()
  78.                     dest.close()
  79.                 finally:
  80.                     print '\nDeactivating Volume:'
  81.                     cmd='lvchange -v -an '+lvdev
  82.                     docmd(cmd)
  83.         else:
  84.             print 'ERROR: destination file '+destfile+' exists.'
  85.     else:
  86.         print 'ERROR: vm status:',vmstatus,'vm needs to be halted to migrate'
  87.  
  88. def importvm(lvdest,sourcefile,vgdest,lvsize,gz=False):
  89.     """
  90.    import a raw vmfile into a logical volume
  91.    """
  92.     if debug:
  93.         print 'importing vm from :',sourcefile
  94.         print 'to logical volume :',lvdest
  95.         print 'on volume group   :',vgdest
  96.         print 'with gz           :',gz
  97.     blocksize=1024*1024
  98.     notification=float(2**30) # 2**30=GB
  99.     if gz:
  100.         notification=notification/4
  101.     lvexists=0
  102.     lvvgs=getlvdevlist()
  103.     for lvvg in lvvgs:
  104.         if lvdest==lvvg[0]:
  105.             print 'ERROR: lv '+lvdest+' exists cannot import'
  106.             lvexists=1
  107.     if not lvexists:
  108.         cmd='lvcreate -v -n '+lvdest+' -L '+lvsize+'G '+vgdest
  109.         print '\nCreating Logical Volume:'
  110.         docmd(cmd)
  111.         try:
  112.             if gz:
  113.                 source=gzip.GzipFile(sourcefile,'rb')
  114.             else:
  115.                 source=open(sourcefile,'rb')
  116.             destlv='/dev/'+vgdest+'/'+lvdest
  117.             dest=open(destlv,'wb')
  118.             noticetick=notification/(2**30)
  119.             print '\nRW notification every: '+str(noticetick)+'GB'
  120.             notification=notification/blocksize
  121.             sys.stdout.write('Importing: ')
  122.             write=0
  123.             while True:
  124.                 write+=1
  125.                 data=source.read(blocksize)
  126.                 if write%notification==0:
  127.                     sys.stdout.write(str((write/notification)*noticetick)+'GBr')
  128.                 if len(data)==0:
  129.                     break # EOF
  130.                 dest.write(data)
  131.                 if write%notification==0:
  132.                     sys.stdout.write('w ')
  133.                 sys.stdout.flush()
  134.             print '\nSuccessful import'
  135.         finally:
  136.             try:
  137.                 source.close()
  138.                 dest.close()
  139.             finally:
  140.                 print
  141.     else:
  142.         print 'ERROR: logical volume '+lvdest+' exists'
  143.  
  144. def importxenserverdisk(sourcefile,diskuuid,vmuuid,gz=False):
  145.     """
  146.    import disk from sourcefile into xenserver
  147.    """
  148.     if debug:
  149.         print 'importing vm from :',sourcefile
  150.         print 'to disk uuid      :',diskuuid
  151.         print 'with gz           :',gz
  152.     blocksize=1024*1024
  153.     notification=float(2**30) # 2**30=GB
  154.     if gz:
  155.         notification=notification/4
  156.     vmstatus=getvmstatus(vmuuid)
  157.     if vmstatus=='running':
  158.         cmd='xe vm-shutdown -u root uuid='+vmuuid
  159.         if debug:
  160.             print 'halting vm uuid   :',vmuuid
  161.         docmd(cmd)
  162.     vmstatus=getvmstatus(vmuuid)
  163.     if vmstatus=='halted':    
  164.         if os.path.exists(sourcefile):
  165.             try:
  166.                 lvdev=getlvdevxen(diskuuid)[0]
  167.                 print 'to logical volume :',lvdev
  168.                 print '\nActivating Volume:'
  169.                 cmd='lvchange -v -ay '+lvdev
  170.                 lvchange=docmd(cmd)
  171.                 if gz:
  172.                     source=gzip.GzipFile(sourcefile,'rb')
  173.                 else:
  174.                     source=open(sourcefile,'rb')
  175.                 dest=open(lvdev,'wb')
  176.                 noticetick=notification/(2**30)
  177.                 print '\nRW notification every: '+str(noticetick)+'GB'
  178.                 notification=notification/blocksize
  179.                 sys.stdout.write('Importing: ')
  180.                 write=0
  181.                 while True:
  182.                     write=write+1
  183.                     data=source.read(blocksize)
  184.                     if write%notification==0:
  185.                         sys.stdout.write(str((write/notification)*noticetick)+'GBr')
  186.                     if len(data)==0:
  187.                         break #EOF
  188.                     dest.write(data)
  189.                     if write%notification==0:
  190.                         sys.stdout.write('w ')
  191.                     sys.stdout.flush()
  192.                 print '\nSuccessful import'
  193.             finally:
  194.                 try:
  195.                     source.close()
  196.                     dest.close()
  197.                 finally:
  198.                     print '\nDeactivating Volume:'
  199.                     cmd='lvchange -v -an '+lvdev
  200.                     docmd(cmd)
  201.         else:
  202.             print 'ERROR: source file '+sourcefile+' does not exist.'
  203.     else:
  204.         print 'ERROR: vm status:',vmstatus,'vm needs to be halted to import disk'
  205.  
  206.  
  207. def getdiskuuidvm(diskuuid):
  208.     """
  209.    get vm uuid from disk uuid and return it
  210.    """
  211.     if debug:
  212.         print 'vm from disk uuid :',diskuuid
  213.     cmd='xe vbd-list vdi-uuid='+diskuuid
  214.     response=docmd(cmd).split('vm-uuid ( RO): ')
  215.     vmuuid=response[1].split('\n')[0]
  216.     return vmuuid    
  217.  
  218. def getlvdevlist():
  219.     """
  220.    get logical volume and volume group list and return it
  221.    """
  222.     lvvgs=[]
  223.     sep=','
  224.     cmd='lvs --separator \''+sep+'\''
  225.     vgdevs=docmd(cmd).split('\n')
  226.     del vgdevs[0]
  227.     del vgdevs[-1]
  228.     for vgdev in vgdevs:
  229.         lv=vgdev.split(sep)[0][2:]
  230.         vg=vgdev.split(sep)[1]
  231.         size=vgdev.split(sep)[3][:-1]
  232.         lvvgs.append([lv,vg,size])
  233.     return lvvgs
  234.  
  235. def getlvdevxen(vmdiskuuid):
  236.     """
  237.    take the vmdisk uuid and return the logical volume device name
  238.    """
  239.     if debug:
  240.         print 'get lv from uuid  :',vmdiskuuid
  241.     lvvgs=getlvdevlist()
  242.     for lvvg in lvvgs:
  243.         if vmdiskuuid in lvvg[0]:
  244.             lvdev='/dev/'+lvvg[1]+'/'+lvvg[0]
  245.             return lvdev,lvvg[2]
  246.     return None,None
  247.  
  248. def getvmdiskuuid(vmuuid):
  249.     """
  250.    get the vmdisk uuids from the vmuuid
  251.    return disk uuids in list
  252.    """
  253.     if debug:
  254.         print 'disk from uuid    :',vmuuid
  255.     diskuuid=[]
  256.     cmd='xe vbd-list vm-uuid='+vmuuid
  257.     response=docmd(cmd).split('vdi-uuid ( RO): ')
  258.     del response[0]
  259.     for index,uuid in enumerate(response):
  260.         curuuid=uuid.split('\n')[0]
  261.         if curuuid!='<not in database>':
  262.             partid=uuid.split('\n')[2].split(': ')[1]
  263.             diskuuid.append([curuuid,partid])
  264.     return diskuuid
  265.  
  266. def getvmstatus(vmuuid):
  267.     cmd='xe vm-list uuid='+vmuuid
  268.     response=docmd(cmd).split('power-state ( RO): ')[1].split('\n')[0]
  269.     return response
  270.  
  271. def getvmuuid(vmname):
  272.     """
  273.    get the vmuuid from the name-label of a vm
  274.    return uuid
  275.    """
  276.     if debug:
  277.         print 'uuid from name    :',vmname
  278.     try:
  279.         cmd='xe vm-list name-label=\''+vmname+'\''
  280.         uuid=docmd(cmd).split(':')[1].split(' ')[1][:-1]
  281.         return uuid
  282.     except IndexError:
  283.         return 'vm not found'
  284.  
  285. def reftoraw(refdir,rawfile,gz=False):
  286.     """
  287.    take the ref directory of an xva file and create a raw importable file
  288.    """
  289.     if debug:
  290.         print 'ref dir           :',refdir
  291.         print 'to raw file       :',rawfile
  292.         print 'gzip              :',gz
  293.     blocksize=1024*1024
  294.     notification=float(2**30) # 2**30=GB
  295.     if gz:
  296.         notification=notification/4
  297.     numfiles=0
  298.     for dirobj in os.listdir(refdir):
  299.         try:
  300.             numfile=int(dirobj)
  301.         except ValueError, TypeError:
  302.             numfile=0;
  303.         if numfile>numfiles:
  304.             numfiles=numfile
  305.     print 'last file         :',numfiles+1
  306.     print 'disk image size   :',(numfiles+1)/1024,'GB'
  307.     if os.path.isdir(refdir):
  308.         # This may cause problems in Windows!
  309.         if refdir[-1]!='/':
  310.             refdir+='/'
  311.         if not os.path.exists(rawfile):
  312.             try:
  313.                 filenum=0
  314.                 noticetick=notification/(2**30)
  315.                 print '\nRW notification every: '+str(noticetick)+'GB'
  316.                 notification=notification/blocksize
  317.                 if gz:
  318.                     dest=gzip.GzipFile(rawfile,'wb')
  319.                 else:
  320.                     dest=open(rawfile,'wb')
  321.                 sys.stdout.write('Converting: ')
  322.                 if gz:
  323.                     blankblock=''
  324.                     for loop in range(blocksize):
  325.                         blankblock+='\x00'
  326.                 while filenum<=numfiles:
  327.                     if (filenum+1)%notification==0:
  328.                         sys.stdout.write(str(((filenum+1)/notification)*noticetick)+'GBr')
  329.                     filename=str(filenum)
  330.                     while len(filename)<8:
  331.                         filename='0'+filename
  332.                     if os.path.exists(refdir+filename):
  333.                         source=open(refdir+filename,'rb')
  334.                         while True:
  335.                             data=source.read(blocksize)
  336.                             if len(data)==0:
  337.                                 source.close()
  338.                                 #sys.stdout.write(str('\nProcessing '+refdir+filename+'...'))
  339.                                 break # EOF
  340.                             dest.write(data)
  341.                     else:
  342.                         #print '\n'+refdir+filename+' not found, skipping...'
  343.                         if gz:
  344.                             dest.write(blankblock)
  345.                         else:
  346.                             dest.seek(blocksize,1)
  347.                     if (filenum+1)%notification==0:
  348.                         sys.stdout.write('w ')
  349.                     sys.stdout.flush()
  350.                     filenum+=1
  351.                 print '\nSuccessful convert'
  352.             finally:
  353.                 try:
  354.                     dest.close()
  355.                     source.close()
  356.                 finally:
  357.                     print
  358.         else:
  359.             print 'ERROR: rawfile '+rawfile+' exists'
  360.     else:
  361.         print 'ERROR: refdir '+refdir+' does not exist'
  362.  
  363. def vmdktoraw(vmdkfile,rawfile,gz):
  364.     """
  365.    take the ref directory of an xva file and create a raw importable file
  366.    """
  367.     if debug:
  368.         print 'vmdk              :',vmdkfile
  369.         print 'to raw            :',rawfile
  370.         print 'gzip              :',gz
  371.     if (not gz and not os.path.exists(rawfile)) or ((gz and not os.path.exists(rawfile+'.gz')) and (gz and not os.path.exists(rawfile))):
  372.         try:
  373.             cmd='qemu-img convert '+vmdkfile+' -O raw '+rawfile
  374.             print 'Converting...'
  375.             response=docmd(cmd)
  376.             print response
  377.             if gz:
  378.                 cmd='gzip -v '+rawfile
  379.                 print 'Gzipping...'
  380.                 response=docmd(cmd)
  381.             print 'Sucessful convert'
  382.         except:
  383.             print 'ERROR: problem converting file (do you have qemu-img installed?)'
  384.     else:
  385.         if gz:
  386.             print 'ERROR: rawfile '+rawfile+' or '+rawfile+'.gz exists'
  387.         else:
  388.             print 'ERROR: rawfile '+rawfile+' exists'
  389.                    
  390. ##
  391. ## Main Program
  392. ##
  393.  
  394. if __name__=='__main__':
  395.     # globals
  396.     global debug
  397.     debug=False
  398.     # Hello world
  399.     print 'xenmigrate 0.7.4 -- 2011.09.13\n(c)2011 Jolokia Networks and Mark Pace -- jolokianetworks.com\n'
  400.     # process arguments
  401.     from optparse import OptionParser
  402.     parser=OptionParser(usage='%prog [-cdhiltvxz] [vmname]|[exportLVdev]|[importVolGroup]|[importdiskuuid]|[converttofile]')
  403.     parser.add_option('-c','--convert',action='store',type='string',dest='convert',metavar='DIR',help='convert DIR or vmdk to importable rawfile')
  404.     parser.add_option('-d','--disk',action='store_true',dest='disk',help='display vm disk uuids',default=False)
  405.     parser.add_option('--debug',action='store_true',dest='debug',help='display debug info',default=False)
  406.     parser.add_option('-i','--import',action='store',type='string',dest='doimport',metavar='FILE',help='import from FILE to [type=xen:importVolGroup]|\n[type=xenserver:importdiskuuid]')
  407.     parser.add_option('-l','--lvdev',action='store_true',dest='lvdev',help='display vm logical volume devices',default=False)
  408.     parser.add_option('-t','--type',action='store',type='string',dest='type',metavar='TYPE',help='import to [xen]|[xenserver]',default='xen')
  409.     parser.add_option('-x','--export',action='store',type='string',dest='export',metavar='FILE',help='export from Xen Server or from Logical Volume dev to FILE')
  410.     parser.add_option('-z','--gzip',action='store_true',dest='gz',help='use compression for import, export, or convert (SLOW!)',default=False)
  411.     (opts,args)=parser.parse_args()    
  412.     if len(args)<1:
  413.         parser.print_help()
  414.         sys.exit(1)
  415.     if opts.debug:
  416.         debug=True
  417.     if opts.disk or opts.lvdev or opts.export:
  418.         vmname=args[0]
  419.         if '/dev' in vmname and opts.export:
  420.             #print 'export dev        :',vmname
  421.             pass
  422.         else:
  423.             vmuuid=getvmuuid(vmname)
  424.             print 'vm name-label     :',vmname
  425.             print 'vm uuid           :',vmuuid
  426.             vmdiskuuids=getvmdiskuuid(vmuuid)
  427.             for vmdiskuuid in vmdiskuuids:
  428.                 print 'vm disk uuid      :',vmdiskuuid[0]
  429.                 print 'vm disk partid    :',vmdiskuuid[1]
  430.                 if opts.lvdev:
  431.                     lvdev,lvsize=getlvdevxen(vmdiskuuid[0])
  432.                     if lvdev is not None:
  433.                         print 'vm disk dev name  :',lvdev
  434.                         print 'vm disk size      :',lvsize+'GB'
  435.                     else:
  436.                         print 'vm disk dev name  : not found in mounted storage repositories'
  437.     if opts.export and opts.doimport:
  438.         print 'ERROR: export and import cannot be run at the same time'
  439.     elif opts.export and opts.convert:
  440.         print 'ERROR: export and convert cannot be run at the same time'
  441.     elif opts.doimport and opts.convert:
  442.         print 'ERROR: import and convert cannot be run at the same time'
  443.     elif opts.export and opts.doimport and opts.convert:
  444.         print 'ERROR: you have got to be kidding me -- need some more options to run at the same time?'
  445.     elif opts.export:        
  446.         if '/dev' in vmname:
  447.             vmdiskuuids=[vmname]
  448.             # need some logic here to test for logical volume so we don't just blow up
  449.             # we should get the lvsive of the dev 0.7.2 here we come!
  450.             # using type might be a good idea too 0.7.3 probably
  451.         else:
  452.             vmdiskuuids=getvmdiskuuid(vmuuid)
  453.         for vmdiskuuid in vmdiskuuids:
  454.             if '/dev' in vmname:
  455.                 lvdev=vmname
  456.                 lvsize='xen'
  457.             else:
  458.                 lvdev,lvsize=getlvdevxen(vmdiskuuid[0])
  459.             if lvdev is not None:
  460.                 exportname=opts.export
  461.                 if exportname[-3:]=='.gz':
  462.                     opts.gz=True
  463.                     exportname=exportname[:-3]
  464.                 exportname=exportname+'_'+vmdiskuuid[1]+'_'+lvsize
  465.                 if opts.gz:
  466.                     exportname=exportname+'.gz'
  467.                 print 'export dev        :',lvdev
  468.                 print 'to raw file       :',exportname
  469.                 if lvdev:
  470.                     exportvm(vmname,lvdev,exportname,opts.gz)
  471.         print 'You many need to restart your VM:'
  472.         print 'xe vm-startup -u root uuid='+vmuuid
  473.     elif opts.doimport:
  474.         importname=opts.doimport
  475.         if importname[-3:]=='.gz':
  476.             opts.gz=True
  477.             importname=importname[:-3]
  478.         if opts.type=='xen':
  479.             lvsize=importname.split('_')[-1]
  480.             lvpartid=importname.split('_')[-2]
  481.             lvdesttmp=importname.split('/')[-1]
  482.             for index in range(len(lvdesttmp.split('_'))-2):
  483.                 if index==0:
  484.                     lvdest=lvdesttmp.split('_')[0]
  485.                 else:
  486.                     lvdest=lvdest+'_'+lvdesttmp.split('_')[index]
  487.             print 'import raw file   :',opts.doimport
  488.             print 'to lv             :',lvdest
  489.             print 'in vg             :',args[0]
  490.             print 'lv size           :',lvsize+'GB'
  491.             print 'xen config partid :',lvpartid
  492.             importvm(lvdest,opts.doimport,args[0],lvsize,opts.gz)
  493.         elif opts.type=='xenserver':
  494.             print 'import raw file   :',opts.doimport
  495.             print 'to disk uuid      :',args[0]
  496.             vmuuid=getdiskuuidvm(args[0])
  497.             print 'vm uuid           :',vmuuid
  498.             importxenserverdisk(opts.doimport,args[0],vmuuid,opts.gz)
  499.         else:
  500.             print 'ERROR: unknown Xen type for import'
  501.     elif opts.convert:
  502.         if os.path.isdir(opts.convert):
  503.             print 'convert ref dir   :',opts.convert
  504.             print 'to raw file       :',args[0]
  505.             reftoraw(opts.convert,args[0],opts.gz)
  506.         elif os.path.isfile(opts.convert):
  507.             if opts.convert[-5:]=='.vmdk':
  508.                 filename=args[0]
  509.                 if filename[-3:]=='.gz':
  510.                     opts.gz=True
  511.                     filename=filename[:-3]
  512.                 print 'convert vmdk file :',opts.convert
  513.                 print 'to raw file       :',filename
  514.                 vmdktoraw(opts.convert,filename,opts.gz)
  515.             else:
  516.                 print 'ERROR: unknown file convert format'
  517.         else:
  518.             print 'ERROR: convert source directory or file does not exist'
  519.             sys.exit(1)
  520.  
  521.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement