bret_miller

xenmigrate.py

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