Professional Documents
Culture Documents
VCB Vsphere Backup
VCB Vsphere Backup
Thisdocumentintroducescodedeveloperstotheconceptsandproceduresnecessarytocreatebackupand
restoresoftwareforvirtualmachinesandVMwarevSphere(formerlyVMwareInfrastructure,orVI).You
shouldbefamiliarwithprogrammingconcepts,practices,andtechniques.Youshouldalsobefamiliarwith
theseconcepts:virtualmachine,snapshot,andvSphere(ESXandvCenter).Thisdocumentdoesnotduplicate
informationavailableelsewhere.Whennecessary,itreferstootherdocuments.
YoushouldbefamiliarwithnavigatingtheWebbasedVMwarevSphereAPIReferenceDocumentation,andread
partsoftheVMwarevSphereWebServicesSDKProgrammingGuide,especiallytheintroductorychapters.Both
areavailableathttp://www.vmware.com/support/developer/vcsdk.Thisbackgroundreadingcoversmanyof
thebasicconceptsthatareimportantforvSpheredeployments.Thisdocumentisdividedintofoursections:
ConceptualOverviewonpage 1
HighLevelImplementationonpage 2
LowLevelProceduresonpage 8
ChangedBlockTrackingonVirtualDisksonpage 21
IncrementalRestoreofBackupDataonpage 23
Ifyouarelookingforahighleveloverviewofbackupandrestore,readthefirstsection.Ifyouaredesigning
yourtoplevelprogramstructure,alsoreadthesecondsection.Implementersoflowlevelcodeshouldalso
readthethirdsection.Eachsectionassumesthatyouhaveabsorbedknowledgefromtheprecedingsection.
Conceptual Overview
Thissectionsummarizesthebackupprocessandtherestoreprocess.
Contacttheservermachinecontainingthetargetvirtualmachine.
Commandthatservertoproduceasnapshotofthetargetvirtualmachine.
Usetheservertogainaccesstothevirtualdisk(s)andfilesinthesnapshot.
Capturethevirtualdiskdataandvirtualmachineconfigurationinformation(vim.vm.ConfigInfo).
Commandtheservertodestroythebackupsnapshot.
Asideeffectofsteponeisbeingabletodeterminethearrangementanddescriptionofvirtualmachinesonthe
server.Thisinformationisusefulfordeterminingthetargetvirtualmachine.
Copyright 2009 VMware, Inc. All rights reserved.
Contacttheserverandcommandittohaltandpoweroffthetargetvirtualmachine.
Usetheservertogainaccesstothevirtualdisk(s).
Transfertheimage(s)ofthedisk(s)fromthebackupprogram.
Contacttheserver.
Commandtheservertocreateanewvirtualmachineanditsvirtualdisksusingtheconfiguration
informationinstep4ofthebackupprocessabove.
Transferthevirtualdiskdatatothenewlycreatedvirtualdisk(s).Thisincludesthevirtualdiskformatting
information,soitisunnecessarytobuildanykindoffilesystemonthevirtualdisk(s).
Summary of Requirements
Abackupprogrammustbeableto:
ContactaVMwarehost,displayinformationaboutthevirtualmachinesthatitcontrols,andmanipulate
thosevirtualmachines.
Forbackup,instructeachvirtualmachinetocreateatemporarysnapshot,andtransfersnapshotdatato
thebackupapplication.
Forrestore,instructthehosttohaltorrecreateatargetvirtualmachine,andrestorethedataforthat
virtualmachinefromthebackupapplication.
Contactingtheserver
Extractinginformationfromtheserver
Causingtheservertoperformactions
Givinginstancespecificcommandsandcontrol
Transferringdata
Exceptforthelast,datatransfer,alltheabovecategoriesaresupportedbytheVMwarevSphereSDK,which
containsawidevarietyofmanagementandcontrolinterfaces.
UsingvCenterorthevSphereAPI,itisevenpossibletoidentifyanyVirtualApp(vApp)thatisinstalledand
whatvirtualmachinesareassociatedwiththeit.ThismakesitpossibletobackupthevAppasaunit.
ToreducetheresourcesusedbythevSphere,itisgenerallyrecommendedthatthenumberofconnections(or
Sessions)toitbeminimized.Asaresult,itisinthebestinterestsofanyprogramthatcommunicateswiththe
vSpheretocreateoneSessionandshareitwithallelementsoftheprogramthatneedtoexchangeinformation
withtheserver.Thismeansthatifyourprogramsupportsmultiplethreads,thenyourprogramneedsto
multiplextheuseoftheconnectionobjectthroughtheuseofaccesscontrollocks(mutexandthelike).
ItisalsoimportanttonotethatallvSphereSDKoperationsproceedfromaninstanceofaSessionobjectthat
yourapplicationrequestsafterloggingintothevSphere.Yourapplicationmightalsocreateobjectsthrough
thevSphereSDKthatareSessionspecificandthereforewouldnotbeknowntoportionsofyourapplication
thatareusingadifferentSession.
Folder
ComputeResource
ResourcePool
DataCenter
VirtualMachine
ItisacharacteristicofallManagedObjectsthattheyhaveaMoReftotheManagedObjectthatservesasthe
parenttotheManagedObject.ThisparentMoRefallowsyoutoreconstructtheobjecthierarchyexposedby
thevSphereSDK.Ingeneralthehierarchyisatreelikestructurealongthelinesof:
RootFolder>DataCenter>ComputeResource>ResourcePool>VirtualMachine
Therearevariationsonthistheme,dependingonwhetheryouconnecttovCenterordirectlytoanESXhost,
buttheoverallorganizationislikethestructureabove.EachManagedObjectalsohasaNameproperty.
FordirectconnectionstoESX/ESXi,thehostaddressandmoRefuniquelyidentifyavirtualmachine.However
thismoRefcouldbedifferentfromtheonethatvCenterServerreturns,hencethefallbacktoinstanceUuid.
InstanceUuidisnewinVMwarevSphere4.0.Inpreviousreleases,thefallbackisinsteadUuid.
PollingChecksforchanges.Theresultiseithernochangeoranobjectcontainingthechanges.The
advantageofthismechanismisthatexceptforthepollrequest,itinvolvesnochatterexceptforreporting.
WaitforupdatesWaitforupdatesisbasicallyablockingcalltothePropertyCollector.Thisisonly
usefulifyoudedicateaprogramthreadwaitingforthecalltounblock.Thesingleadvantageofthiscall
isthatthereisnochatteratallonthecommunicationsthreadunlesssomethingmustbereported.
TheVMwarevSphereWebServicesSDKProgrammingGuidecontainsalotofinformationaboutthe
PropertyCollector.Thisisaverypowerfulinterface,requiringgreatattentiontodetail.Backuprelated
featuresofthePropertyCollectorarecoveredinLowLevelProceduresonpage 8ofthisdocument.The
nextsectionhighlightssomecharacteristicsofthePropertyCollectorasanoverview.
PropertyCollector Use
ThisdocumentassumesthatyouwanttokeepupwithchangesintheconfigurationoftheVMwareserver,
andthereforeplantousetheupdatetrackingcapabilityofthePropertyCollector.ThePropertyCollector
requirestwofairlycomplexarguments:thePropertySpecandtheObjectSpec.
TheObjectSpeccontainsinstructionstothePropertyCollectordescribingwheretolookforthedesired
data.BecauseconfigurationinformationintheVMwareserverisorganizedlikeadirectorytree,the
ObjectSpecmustdescribehowtotraversethistreetoobtainthedesiredinformation.Thenetresultisa
complex,nested,andrecursivelistofinstructions.Fortunately,onceyouhavedeterminedthelocationofall
thedesiredinformationis,theObjectSpecneededtodeterminethelayoutofavSphereobjecthierarchycan
beastaticunvaryingobject.SeethecodeexampleinsectionUnderstandinganObjectSpeconpage 9.
ThePropertySpecisalistofdesiredpropertyinformation.Formulatingalistthatincludesallofthedesired
informationcantakesomeefforttocompile,butoncedetermined,thiscanbeastaticobjectalso.
ThedatareturnedfromthePropertyCollectorisacontainerclasscalledPropertyFilterUpdate.This
classisanitembyitemlistofchangestothelistofrequestedproperties.Everyiteminthiscontaineris
identifiedwithoneofthefollowingkeys:enter(add),leave(delete),andmodify.Onthefirstdatarequest,
everydataitemisincluded,andeverydataitemismarkedenter.
ThePropertyCollectorpresentsitsresultsinwhatamountstorandomorder.SinceallManagedObjects
haveaparentproperty,youcanreconstructtheconfigurationhierarchybybuildingatreeinmemory,using
theparentidentificationtoorganize.Therootfolderisidentifiedastheonlyfolderwithoutaparent.
Useful Information
YoucanfindmostoftheinformationthatisusefultoabackupprogramintheVirtualMachineManaged
Object.ThisManagedObjectcontainsalotofinformation,butthefollowingisprobablymostuseful:
VirtualDisksNames,Types,andCapacities.
MachineTypeandConfigurationWhateverwouldbeusefulin(re)creatingavirtualmachine.Thislist
mightincludesuchinformationasnumberofCPUsandmemorysize.
DisplayNamesThesenamesappearinVMwareproductssuchastheVMwarevSphereClient.You
shouldkeeptrackofthesenamesandcorrelatethemforconsistencybetweenyourproductandwhat
VMwaredisplaysinitsproducts.
SomethingtorememberisthatVMwaresupportsanumberofvirtualdiskimplementations.Thetypeofdisk
implementationisimportantfortworeasons:
AdiskbackedbyaphysicalcompatibilityRDMmostlybypassestheESXstoragestack.Asaresult,you
cannotmakeasnapshotofthistypeofvirtualdisk.Therefore,thistypeofvirtualdiskcannotbebacked
upusingthesnapshotmethoddescribedinthisdocument.
Onrestore,youshouldrecreatevirtualdiskwiththesamedisktypeastheoriginalvirtualmachineused.
CreatingaTemporarySnapshotontheTargetVirtualMachineonpage 5
ExtractingtheBackupDatafromtheTargetVirtualMachineonpage 5,saveconfigurationinformation
DeletingtheTemporarySnapshotonpage 6
TheVixDiskLibAPIallowsabackupapplicationtoperformafullbackupofavirtualmachine.Thenewer
VixMntapilibrarycanextractinformationaboutaguestoperatingsystemfromitsvirtualdisks,soyour
backupapplicationcandeterminewhattypeofoperatingsystemisinstalledthere.Thisallowsmountingthe
volumestodevicenodes,sothatapplicationscanperformfileorientedbackupsofavirtualmachine.
RestoringanExistingVirtualMachinetoaKnownStateonpage 6
CreatingaNewVirtualMachineonpage 6
Shutdownthevirtualmachine(ifitisnotalreadyshutdown).
Forobvioussecurityreasons,youarenotgrantedwriteaccesstothedisksofarunningvirtualmachine.
Beforeyoushutitdown,youneedtodeterminetherunstateofthevirtualmachine.Thisisinformation
thatisavailablefromthePropertyCollector,andifyouhavebeenkeepingthisinformationuptodate,
thenyourapplicationalreadyknowstherunstateofthevirtualmachine.
TochangetherunstateofavirtualmachinerequiresthatyoudeterminetheMoRefofthevirtualmachine.
YoucanthenusethisMoRefinaPowerOnVM_Taskcallthroughtheconnectionyoumaketotheserver.
TheprocedureforaccomplishingvirtualmachineshutdownisoutlinedintheVMwarevSphereWeb
ServicesSDKProgrammingGuideinthePerformingPowerOperationsonaVirtualMachinesection.
Restorethecontentsofthevirtualdisks.
Theprocessofrestoringthediskdatarequiresthatyouobtainthecurrentnamesofthevirtualdisks.This
processissimilartotheonedescribedinExtractingtheBackupDatafromtheTargetVirtualMachine
onpage 5,exceptinthiscaseyouobtainthisinformationdirectlyfromthevirtualmachineandnotfrom
asnapshot.Thetargetforthesaveddiskdatamustbetheactualdiskname(includinganysequence
numberthatmayexist),becausethecurrentincarnationofthevirtualmachinemaybederivedfromone
ormoresnapshots.
RestoringdiskdatarequirestheuseoftheVixDiskLibinterface.Documentationofthisinterfaceisat
http://www.vmware.com/support/developer/vddk/.TheVixDiskLib_Write()functionallowsyouto
openthevirtualmachinesvirtualdisksandwriteyourrestoredata.VixDiskLibfunctionstransferdata
toevensectorboundariesonly,andthetransferlengthmustbeanevenmultipleofasectorsize.
Sincethevirtualdiskexists,itisnotnecessarytorestorethediskconfigurationinformationmentionedin
ExtractingtheBackupDatafromtheTargetVirtualMachineonpage 5.
Createthevirtualmachine.
TheprocessforcreatingavirtualmachineisdescribedintheVMwarevSphereWebServicesSDK
ProgrammingGuideintheCreatingaVirtualMachinesection.Tocreateanewvirtualmachine,youmust
usetheinformationaboutvirtualmachineconfigurationthatyouderivedandsavedduringthebackup
process.Youshouldallowuserstheopportunitytorenamethevirtualmachineduringthisprocessincase
theywanttoclonethevirtualmachine.Youalsomightconsiderofferinguserstheopportunityto
changethevirtualmachineslayout(forinstance,storingthevirtualdisksondifferentdatastores).
Creatingthevirtualdisksforthismachineisalsodoneatthetimeyoucreatethevirtualmachine.This
processisfairlycomplicated.SeethesectionLowLevelProceduresonpage 8fordetails.
2
Restorethevirtualdiskdata.
Thisprocessissimilartorestoringthecontentsofvirtualdiskswiththefollowingexception:youmust
useaVixDiskLibfunctiontosetthediskconfigurationkey/valuedataintothevirtualdiskbeforewriting
anybackedupdatatothedisk.ThenuseVixDiskLibtorestoredatatothedisk,asdescribedabove.
Poweronthevirtualmachine.
Locatethepathnamesofallthevirtualdisksassociatedwithasnapshot.
CallVixDiskLib_Open()toopenallofthesevirtualdisks.ThisgivesyouanumberofVixDiskLib
handles,whichyoushouldstoreinanarray.
CallVixMntapi_OpenDiskSet()tocreateaVixDiskSetHandle,passinginthearrayofVixDiskLib
handlesthatyoucreatedinstep2.
PassVixDiskSetHandleasaparametertoVixMntapi_GetVolumeHandles()toobtainanarrayof
VixVolumeHandlepointerstoallvolumesinthediskset.
CallVixMntapi_GetOsInfo()todeterminewhatkindofoperatingsystemisinvolved,andwhere
importantpiecesofinformationaretobefound.
Forimportantvolumes,callVixMntapi_MountVolume()thenVixMntapi_GetVolumeInfo(),which
revealshowthevolumeissetup.
Ifyouneedinformationabouthowtheguestoperatingsystemseesthedataonthisvolume,youcanlook
inthedatastructureVixVolumeInforeturnedbyVixMntapi_GetVolumeInfo().
VixVolumeInfo::symbolicLink,obtainedusingVixMntapi_GetVolumeInfo(),isthepathonthe
proxywhereyoucanaccessthefilesysteminthevirtualdiskusingordinaryopen,read,andwritecalls.
Onceyouaredonewithaccessingfilesinavolume,thereareVixMntapiproceduresfortakingdownthe
abstractionthatyouhavecreated.Thesecallsare:
VixMntapi_DismountVolume()
VixMntapi_FreeOsInfo()
VixMntapi_FreeVolumeInfo()
VixMntapi_CloseDiskSet()
ThisleavestheVixDiskLibhandlesthatyouobtainedinthebeginning;youmustdisposeofthoseproperly.
Summary
Intheprecedingsectionsyouhaveseenatahighlevel:
Howtocontactaserver
Howtoextractinformationfromthatserver
Howtomanipulateitemsontheserver
Howtobackupandrestoredatafromvirtualdisks
Howtoaccessfiledataonvirtualdisks
Thefollowingsectionscoverthesameinformationatalowerlevel.
Createtheserviceinstance:
ManagedObjectReference svcRef = new ManagedObjectReference();
svcRef.setType("ServiceInstance");
svcRef.setValue("ServiceInstance");
Locatetheservice:
VimServiceLocator locator = new VimServiceLocator();
locator.setMaintainSession(true);
VimPortType serviceConnection = locator.getVimPort("https://your_server/sdk");
ServiceInstanceContent serviceContent = serviceConnection.retrieveContent(svcRef);
ManagedObjectReference sessionManager = serviceInstance.getSessionManager();
UserSession us = serviceConnection.login(sessionManager, username, password, null);
The PropertyCollector
ThePropertyCollectorisdescribedinsomedetailintheVMwarevSphereWebServicesSDKProgramming
Guide.Thissectionappliesthosedetailstothebackuptask.
PropertyCollector Arguments
ThePropertyCollectorusestworelativelycomplicatedargumentstructures.Aswasmentionedin
PropertyCollectorUseonpage 4,theseargumentsarePropertySpecandObjectSpec.ThePropertySpec
isalistoftheinformationdesired,andtheObjectSpecisalistofinstructionsindicatingwherethedesired
informationcanbefound.Intheory,youcanalmostdirectlyaddressanobjectusingitsMoRef.Inthatcasean
ObjectSpeccouldbeverysimple.However,gettingtheMoRefinthefirstplacecanbeachallengewhena
complicatedObjectSpecisrequired.ToformulatethiscomplicatedObjectSpec,youneedtounderstandthe
structureoftheavailabledata,whichcanbeaconfusingexercise.Thisiscomplicatedbythefactthatan
ObjectSpeccancontainrecursiveelements.
Understanding an ObjectSpec
AnObjectSpecisalistofObjectSpecelements.Eachelementdescribesthetypeofobject,andgivesa
selectionspec.Firstconsiderthetypeofobject.MoreAboutManagedObjectsonpage 3describesfive
typesofManagedObjects.Hereishowyoutraverseobjects(howoneManagedObjectleadstoanother).
FolderIfyoureadtheVMwarevSphereSDKReferenceGuidesectionaboutaFolderManagedObject,you
seethatoneoftheitemscontainedintheFolderiscalledchildEntity,whichisalistofMoRefsthatcan
containanyofthefollowingManagedObjecttypes:Folder,DataCenter,ComputeResource,orVirtual
Machine.ThismeansthataFoldercanbeaparenttoanyoftheManagedObjectsinthislist.
DataCenterDataCenterhastwoitemsthatleadtootherManagedObjects.Theseare:
hostFolderAMoReftoaFoldercontainingalistofComputeResourcescomprisingaDataCenter.
vmFolderAMoReftoaFoldercontainingtheVirtualMachinesthatarepartoftheDataCenter.If
itisyourobjectivetoduplicatethedisplayseeninaVirtualClientGUI,thenthisFolderisoflimited
usebecauseitdoesnotdescribetheResourcePoolthatistheparentofthevirtualmachine.
ComputeResourceAComputeResourceisbasicallyhardware.AComputeResourcemaybecomposed
ofmultiplehostsystems.ThishardwarerepresentsresourcesthatyoucanusetoimplementaVirtual
Machineobject.However,aVirtualMachinesisalwaysachildofaResourcePool,whichisusedtocontrol
thesharingoftherealmachinesresourcesamongtheVirtualMachineobjects.AComputeResource
containsanitemnamedresourcePool,whichisaMoReftoaResourcePool.
VirtualAppAVirtualApp(vApp)isacollectionofVirtualMachinesthatmakeupasingleapplication.
ThisisaspecialformofResourcePool(definedbelow).AVirtualAppmayhavethreetypesofchildren:
VirtualMachineAfoldernamedvmcontainsalistofMoRefstochildVirtualMachines.
resourcePoolAfoldercontainingalistofMoRefspointingtochildResourcePoolsorVirtualApps.
VirtualAppAVirtualAppcanbecomposedofotherVirtualApps.
ResourcePoolYoucansegmenttheresourcesofaVirtualAppusingaResourcePool.
ResourcePoolResourcePoolcontainstwochilditems:
resourcePoolAfoldercontainingalistofMoRefspointingtochildResourcePoolsorVirtualApps.
vmAlistofMoRefstochildVirtualMachinesthatemploytheresourcesoftheparentResourcePool.
AVirtualMachinealwayslistsaResourcePoolasitsparent.
VirtualMachineVirtualMachinecanbeconsideredtobeanendobjectandassuchyouneednot
describeanytraversalforthisobject.
YoumustunderstandthattheObjectSpecdoesnothavetoleadyouanyfartherthantheMoRefofatarget
object.YoucangatherinformationabouttheManagedObjectitselfusingtheMoRefandthePropertySpec.
ThisisdescribedindetailinthesectionUnderstandingaPropertySpeconpage 10.
InthedocumentVMwarevSphereWebServicesSDKProgrammingGuide,anObjectSpecelement(calleda
TraversalSpec)isdescribedascontainingthefollowingelements:
TypeThetypeofobjectbeingreferenced.
PathTheelementcontainedintheobjectthatisusedtosteertraversal.
NameOptionalreferencenamethatyoucanusetoreferencethisTraversalSpecinanotherSelectSet.
SelectSetAnarraycontainingeitherSelectionSpecorTraversalSpecelements.
SelectionSpecisadirecttargetfortraversal,asisTraversalSpec(aclassextendingSelectionSpec).Itis
intheSelectSetthatrecursioncanoccur.
Ifyouwishtotraversetheentireconfigurationtreeforaserver,thenyouneedonlytherootnodeMoRef,
whichisalwaysaFolder.ThisrootfoldernameisavailablebyusingthefunctiongetRootFolderfromthe
serviceinstancethatconnectstothevSphere.AlloftheabovegoesintothisJavacodesample:
// Remember, TraversalSpec objects can use a symbolic name.
Understanding a PropertySpec
APropertySpecisalistofindividualpropertiesthatcanbefoundatplacesidentifiedbytheObjectSpec.
OncethePropertyCollectorhasaMoRef,itcanthenreturnthepropertiesassociatedwiththatMoRef.
Thiscanincludenestedproperties.Nestedpropertiesarepropertiesthatcanbefoundinsideofproperties
identifiedatthetopleveloftheManagedObject.Nestedpropertiesareidentifiedbyadotnotation.
AnexampleofnestedpropertiescanbedrawnfromtheVirtualMachineManagedObject.Ifyouexaminethe
VMwarevSphereSDKReferenceGuideyouseethataVirtualMachinehasthepropertyidentifiedassummary,
whichidentifiesaVirtualMachineSummaryManagedObject.Inturn,theVirtualMachineSummarycontains
propertyconfig,whichidentifiesaVirtualMachineConfigSummary.VirtualMachineConfigSummaryhas
apropertycalledname,whichisastringcontainingthedisplaynameoftheVirtualMachine.Youcanaccess
thisfinalpropertyusingstringvaluesummary.config.name.ForalltheVirtualMachineConfigSummary
information,stringvaluesummary.configcausesreturnofallVirtualMachineConfigSummaryproperties.
ThePropertyCollectorrequiresanarrayofPropertySpecelements.Eachelementincludes:
TypeThetypeofobjectthatidentifiestheenclosedlistofproperties.
PathSetAnarrayofstringscontainingnamesofpropertiestobereturned,includingnestedproperties.
10
Itisnecessarytoaddanelementforeachtypeofobjectthatyouwishtoqueryforproperties.Thefollowingis
acodesampleofaPropertySpec:
// This code demonstrates how to specify a PropertySpec for several types of target objects:
PropertySpec folderSp = new PropertySpec();
folderSp.setType("Folder");
folderSp.setAll(Boolean.FALSE);
folderSp.setPathSet(new String [] {"parent", "name"});
PropertySpec dcSp = new PropertySpec();
dcSp.setType("Datacenter");
dcSp.setAll(Boolean.FALSE);
dcSp.setPathSet(new String [] {"parent","name"});
PropertySpec rpSp = new PropertySpec();
rpSp.setType("ResourcePool");
rpSp.setAll(Boolean.FALSE);
rpSp.setPathSet(new String [] {"parent","name","vm"});
PropertySpec crSp = new PropertySpec();
crSp.setType("ComputeResource");
crSp.setAll(Boolean.FALSE);
crSp.set:PathSet(new String [] {"parent","name"});
PropertySpec vmSp = new PropertySpec();
vmSp.setType("VirtualMachine");
vmSp.setAll(Boolean.FALSE);
vmSp.setPathSet(new String [] {"parent",
"name",
"summary.config",
"snapshot",
"config.hardware.device"});
// Tie it all together
PropertySpec [] pspec = new PropertySpec [] {folderSp,
dcSp,
rpSp,
crSp,
vmSp};
RetrievePropertiesAonetimerequestforallofthedesiredproperties.
UpdatesPropertyCollectorupdaterequeststaketwoforms:pollingandwaiting,discussedbelow.
11
Ifyouwishtobeginpolling,youmaythencallthefunctionCheckForUpdates,whichonthefirsttry(whenit
mustcontainanemptystringfortheversionnumber)returnsacompletedumpofalltherequestedproperties,
alongwithaversionnumber.SubsequentcallstoCheckForUpdatesmustcontainthisversionnumberto
indicatetothePropertyCollectorthatyouseekanychangesthatdeviatefromthisversion.Theresultiseither
apartiallistcontainingonlythechangesfromthepreviousversion(includinganewversionnumber),ora
returncodeindicatingnodatahaschanged.Thefollowingcodesampleshowshowtocheckforupdates:
String updateVersion = ""; // Start with no version
UpdateSet changeData = serviceConnection.CheckForUpdates(pcRef, version);
if (changeData != nil) {
version = changeData.getVersion(); // Extract the version of the data set
}
...
// Get changes since the last version was sent.
UpdateSet latestData = serviceConnection.CheckForUpdates(pcRef, version);
Ifinsteadyouwishtowaitforupdatestooccur,youmustcreateataskthreadthatblocksonthecall
WaitForUpdates.Thistaskreturnschangesonlyastheyoccurandnotatanyothertime.
Extracting Information from the Change Data
ThedatareturnedfromCheckForUpdates(orWaitForUpdates)isanarrayofPropertyFilterUpdate
entries.SinceaPropertyFilterUpdateentryisverygeneric,hereissomecodeshowinghowtoextract
informationfromthePropertyFilterUpdate.
// Extract the PropertyFilterUpdate set from the changeData
//
PropertyFilterUpdate [] updateSet = changeData.getFilterSet();
//
// There is one entry in the updateSet for each filter you registered with the PropertyCollector.
// Since we currently have only one filter, the array length should be one.
//
PropertyFilterUpdate myUpdate = updateSet[0];
ObjectUpdate [] changes = myUpdate.getObjectSet();
for (a = 0; a < changes.length; ++a) {
ObjectUpdate theObject = changes[a];
String objName = theObject.getObj().getMoType().getName();
// Must decide how to handle the value based on the name returned.
// The only names returned are names found in the PropertySpec lists.
...
}
12
Inadditiontothenameofthedisk,thereshouldbeapathtothisdiskontheserver,whichisstoredasa
datastorepath:[storagex]myvmname/mydisk-NNNNNN.vmdk.Thepathcomponentinthesquarebrackets
correspondstothenameofthedatastorethatcontainsthisvirtualdiskwhiletheremainderofthepathstring
representsthelocationrelativetotherootofthisdatastore.Usuallyyoudonothavetobeconcernedwiththis,
sincethevSphereSDKtakescareofthis.Sohowdoyouobtainthispath?Whetherwearetalkingabouta
snapshotorobtainingtheinformationfromthebasevirtualmachine,theprocessisthesame.Theonly
differenceisintheManagedObjectthatweusetoextracttheinformation.
WhetheryouusethePropertyCollectortogetaVirtualMachineoraVirtualMachineSnapshot,youneed
toselecttheproperty:config.hardware.device.ThisreturnsanarrayofVirtualDevicesassociatedwiththe
VirtualMachineorSnapshot.YoumustscanthislistofdevicestoextracttheVirtualDisks.Allthatisnecessary
istoseeifeachVirtualDeviceentryextendstoVirtualDisk.Whenyoufindsuchanentry,examinethe
BackingInfoproperty.Youmustextendthetypeofthebackingpropertytooneofthefollowing:
VirtualDiskFlatVer1BackingInfo
VirtualDiskFlatVer2BackingInfo
VirtualDiskRawDiskMappingVer1BackingInfo
VirtualDiskSparseVer1BackingInfo
VirtualDiskSparseVer2BackingInfo
ItisimportanttoknowwhichbackingtypeisinuseinordertobeabletorecreatetheVirtualDisk.Itisalso
importanttoknowthatyoucannotsnapshotadiskoftypeVirtualDiskRawDiskMappingVer1BackingInfo,
andthereforeyoucannotbackupthistypeofVirtualDisk.
ThepropertiesofinterestarethebackingfileNameandtheVirtualDiskcapacityInKB.Additionally,when
changetrackingisinplace,youshouldalsosavethechangeID.
Creating a Snapshot
Inordertoperformabackupoperation,youmustfirstcreateasnapshotofthetargetvirtualmachine.The
followingcodesampleshowshowtocreateasnapshotonaspecificvirtualmachine:
// At this point we assume the virtual machine is identified as ManagedObjectReference vmMoRef.
String SnapshotName = "Backup";
String SnapshotDescription = "Temporary Snapshot for Backup";
boolean memory_files = false;
boolean quiesce_filesystem = true;
ManagedObjectReference taskRef = serviceConnection.CreateSnapshot_Task(vmMoRef,
SnapshotName, SnapshotDescription, memory_files, quiesce_filesystem);
Thisnextsectionofcodeshowshowtoopenandreadaspecificvirtualdisk:
VixDiskLibHandle diskHandle;
vixError = VixDiskLib_Open(srcConnection, diskPath, flags, &diskHandle);
uint8 mybuffer[some_multiple_of_512];
vixError = VixDiskLib_Read(diskHandle, startSector, numSectors, &mybuffer);
13
Andfinally,closethediskHandle:
vixError = VixDiskLib_Close(diskHandle);
// And if you are completely done with the VixDiskLib
VixDiskLib_Disconnect(srcConnection);
VixDiskLib_Exit();
Deleting a Snapshot
Whenyouaredoneperformingabackup,youneedtodeletethetemporarysnapshot.Youshouldalready
havetheMoRefforthesnapshotfromtheoperationthatcreatedthesnapshotinthefirstplace.Thefollowing
Javacodedemonstrateshowtodeletethesnapshot:
ManagedObjectReference removeSnapshotTask;
SnapshotManagedObject snapshot;
removeSnapshotTask = serviceConnection.removeSnapshot_Task(snapshot, Boolean FALSE);
ThisisthephasewhereitisnecessarytouseVixDiskLibtoreloadthecontentsoftheVirtualDisk,sothe
followingcodeisC,notJava:
// At this point we assume that you already have a VixDiskLib connection to the server machine.
uint8 mybuffer[some_multiple_of_512];
int mylocalfile = open("localfile", openflags);
read(mylocalfile, mybuffer, sizeof mybuffer);
vixError = VixDiskLib_Open(srcConnection, path, flags, &diskHandle);
VixDiskLib_Write(diskHandle, startsector, (sizeof mybuffer) / 512, mybuffer);
14
Edition (32-bit)",
15
//
//
//
//
//
//
//
cpuHotRemoveEnabled = <unset>,
deviceChange = (vim.vm.device.VirtualDeviceSpec) [
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = <unset>,
CDROM
device = (vim.vm.device.VirtualCdrom) {
dynamicType = <unset>,
key number of CDROM
key = -42,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo) {
dynamicType = <unset>,
deviceName = "",
useAutoDetect = <unset>,
exclusive = false,
},
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) {
dynamicType = <unset>,
startConnected = false,
allowGuestControl = true,
connected = false,
},
connects to this controller
controllerKey = 200,
unitNumber = 0,
},
},
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = <unset>,
SCSI controller
device = (vim.vm.device.VirtualLsiLogicController) {
dynamicType = <unset>,
key number of SCSI controller
key = -44,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualDevice.BackingInfo) null,
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) null,
controllerKey = <unset>,
unitNumber = <unset>,
busNumber = 0,
hotAddRemove = <unset>,
sharedBus = "noSharing",
scsiCtlrUnitNumber = <unset>,
},
},
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = <unset>,
Network controller
device = (vim.vm.device.VirtualPCNet32) {
dynamicType = <unset>,
key number of Network controller
key = -48,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualEthernetCard.NetworkBackingInfo) {
dynamicType = <unset>,
deviceName = "Virtual Machine Network",
useAutoDetect = <unset>,
network = <unset>,
inPassthroughMode = <unset>,
},
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) {
dynamicType = <unset>,
16
startConnected = true,
allowGuestControl = true,
connected = true,
},
controllerKey = <unset>,
unitNumber = <unset>,
addressType = "generated",
macAddress = <unset>,
wakeOnLanEnabled = true,
//
//
//
//
//
//
},
},
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = "create",
SCSI disk one
device = (vim.vm.device.VirtualDisk) {
dynamicType = <unset>,
key number for SCSI disk one
key = -1000000,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualDisk.FlatVer2BackingInfo) {
dynamicType = <unset>,
fileName = "",
datastore = <unset>,
diskMode = "persistent",
split = false,
writeThrough = false,
thinProvisioned = <unset>,
eagerlyScrub = <unset>,
uuid = <unset>,
contentId = <unset>,
changeId = <unset>,
parent = (vim.vm.device.VirtualDisk.FlatVer2BackingInfo) null,
},
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) {
dynamicType = <unset>,
startConnected = true,
allowGuestControl = false,
connected = true,
},
controller for SCSI disk one
controllerKey = -44,
unitNumber = 0,
size in MB SCSI disk one
capacityInKB = 524288,
committedSpace = <unset>,
shares = (vim.SharesInfo) null,
},
},
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = "create",
SCSI disk two
device = (vim.vm.device.VirtualDisk) {
dynamicType = <unset>,
key number of SCSI disk two
key = -100,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualDisk.FlatVer2BackingInfo) {
dynamicType = <unset>,
fileName = "",
datastore = <unset>,
diskMode = "persistent",
split = false,
writeThrough = false,
thinProvisioned = <unset>,
17
eagerlyScrub = <unset>,
uuid = <unset>,
contentId = <unset>,
changeId = <unset>,
parent = (vim.vm.device.VirtualDisk.FlatVer2BackingInfo) null,
},
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) {
dynamicType = <unset>,
startConnected = true,
allowGuestControl = false,
connected = true,
},
// controller for SCSI disk two
controllerKey = -44,
unitNumber = 1,
// size in MB SCSI disk two
capacityInKB = 131072,
committedSpace = <unset>,
shares = (vim.SharesInfo) null,
},
}
},
cpuAllocation = (vim.ResourceAllocationInfo) {
dynamicType = <unset>,
reservation = 0,
expandableReservation = <unset>,
limit = <unset>,
shares = (vim.SharesInfo) {
dynamicType = <unset>,
shares = 100,
level = "normal",
},
overheadLimit = <unset>,
},
memoryAllocation = (vim.ResourceAllocationInfo) {
dynamicType = <unset>,
reservation = 0,
expandableReservation = <unset>,
limit = <unset>,
shares = (vim.SharesInfo) {
dynamicType = <unset>,
shares = 100,
level = "normal",
},
overheadLimit = <unset>,
},
cpuAffinity = (vim.vm.AffinityInfo) null,
memoryAffinity = (vim.vm.AffinityInfo) null,
networkShaper = (vim.vm.NetworkShaperInfo) null,
swapPlacement = <unset>,
swapDirectory = <unset>,
preserveSwapOnPowerOff = <unset>,
bootOptions = (vim.vm.BootOptions) null,
appliance = (vim.vService.ConfigSpec) null,
ftInfo = (vim.vm.FaultToleranceConfigInfo) null,
applianceConfigRemoved = <unset>,
vAssertsEnabled = <unset>,
changeTrackingEnabled = <unset>,
}
// end of VirtualMachineConfigSpec
Theinformationaboveappearstobequitecomplex,butmuchoftheinputconsistsofdefaultedvaluestobe
assignedbythesystem.Theremainderofthesuppliedinformationcanbeextractedfromtheoutputofthe
config.hardware.devicetablefromthePropertyCollector.Borrowingheavilyfromthecodeexamplein
theVMwarevSphereWebServicesSDKProgrammingGuide,wegetthefollowingcodethatduplicates
functionalityoftheconfigurationspecificationseenabove:
// Duplicate virtual machine configuration
18
19
deviceConfigSpec[3].setFileOperation(VirtualDeviceConfigSpecFileOperation.create);
deviceConfigSpec[3].setOperation(VirtualDeviceConfigSpecOperation.add);
VirtualDisk disk2 = new VirtualDisk();
VirtualDiskFlatVer2BackingInfo diskfileBacking2 = new VirtualDiskFlatVer2BackingInfo();
diskfileBacking2.setDatastore(datastoreRef);
diskfileBacking2.setFileName(volumeName);
diskfileBacking2.setDiskMode("persistent");
diskfileBacking2.setSplit(new Boolean(false));
diskfileBacking2.setWriteThrough(new Boolean(false));
disk2.setKey(-100);
disk2.setControllerKey(new Integer(-44));
disk2.setUnitNumber(new Integer(1));
disk2.setBacking(diskfileBacking2);
disk2.setCapacityInKB(131072);
deviceConfigSpec[3].setDevice(disk2);
// Finally, formulate the NIC
deviceConfigSpec[4].setOperation(VirtualDeviceConfigSpecOperation.add);
com.VMware.vim.VirtualEthernetCard nic = new VirtualPCNet32();
VirtualEthernetCardNetworkBackingInfo nicBacking = new VirtualEthernetCardNetworkBackingInfo();
nicBacking.setNetwork(networkRef);
nicBacking.setDeviceName(networkName);
nic.setAddressType("generated");
nic.setBacking(nicBacking);
nic.setKey(-48);
deviceConfigSpec[4].setDevice(nic);
// Now that it is all put together, create the virtual machine
// Note that folderMo, hostMo, and resourcePool are MoRefs to the Folder, Host, and ResourcePool
// where the VM is to be created
ManagedObjectReference taskMoRef = serviceConnection.getService().createVM_Task(
folderMo, configSpec, resourcePool, hostMo);
However,othercontrollersanddevicesmustbeexplicitlyincludedintheVirtualMachineConfigSpec.
Someinformationaboutdevicesisalsounneededandcancauseproblemsifsupplied.Eachcontrollerdevice
hasitsvim.vm.device.VirtualController.devicefield,whichisanarrayofdevicesthatreporttothe
controller.Theserverrebuildsthislistwhenavirtualmachineiscreatedusingthe(negative)devicekey
numberssuppliedasaguide.
Therelationshipbetweencontrolleranddevicemustbepreservedusingnegativekeynumbersinthesame
relationshipasinthehardwarearrayoftheVirtualMachineConfigInfo.
OneotherfeatureofaVirtualMachineConfigInforequiressubstitution.VirtualMachineConfigInfo
containsthefieldcpuFeatureMask,whichisanarrayofHostCpuIdInfo.Theentriesinthisarraymustbe
convertedtoArrayUpdateSpecentriesthatcontaintheVirtualMachineCpuIdInfoSpecalongwithafield
calledoperationthatmustcontainthevalueArrayUpdateOperation::add.
TheVirtualMachineCpuIdInfoSpecalsocontainsaHostCpuIdInfothatyoucancopyfromthe
cpuFeatureMaskarrayinVirtualMachineConfigInfo.
EverythingelsecanbecopiedintactfromtheVirtualMachineConfigInfodata.
20
_thisManagedObjectReferencetothevirtualmachine.
snapshotManagedObjectReferencetoaSnapshotofthevirtualmachine.
deviceKeyVirtualdiskforwhichtocomputethechanges.
startOffsetByteoffsetwheretostartcomputingchangestovirtualdisk.Thelengthofvirtualdisk
sector(s)examinedisreturnedinDiskChangeInfo.
changeIdAnidentifierforthestateofavirtualdiskataspecificpointintime.AnewChangeIdresults
everytimesomeonecreatesasnapshot.Youshouldretainthisvaluewiththeversionofchangedatathat
youextract(usingQueryChangedDiskAreas)fromthesnapshotsvirtualdisk.
Whenyoubackupasnapshot,youeitherhaveapreviouslysavedChangeIdoryoudonot.Ifyouhavea
savedChangeId,itidentifiesthelasttimeyouperformedabackup,andtellsthechangedblocktracking
logictoidentifythosethathaveoccurredsincethetimeindicatedbytheChangeId.Ifyoudonothavea
savedChangeId,thenyoumustsaveabaseline(full)backupofthevirtualdisk.Therearetwowaysto
getthisbaselinebackup:(1)istodirectlysaveallofthevirtualdiskscontents,and(2)istoprovidethe
specialChangeId*(star).ThestarChangeIdindicatesthatonlyactiveportionsofthevirtualdisk
shouldbereturnedbyQueryChangedDiskAreas.Forboththinprovisioned(sparse)virtualdisksandfor
ordinaryvirtualdisks,thisrepresentsasubstantialreductionintheamountofdatatobesaved.
Identifierforatimeinthepast.Thiscanbestar*toidentifyallallocatedareasofvirtualdisk,ignoring
unallocatedareas(ofsparsedisk),oritcouldbeachangeIdstringsavedatthetimeofsnapshotbackup.
ItonlymakessensetousethespecialChangeId=*whennopreviousChangeIdexists.Ifaprevious
ChangeIddoesexist,thenQueryChangedDiskAreasreturnsthedisksectorsthatchangedsincethenew
ChangeIdwascollected.Thefollowingtableshowsthealgorithm:
New Change ID
Old Change ID
Result
change0
none
Allinusesectorsofthedisk.
change1
change0
change0
Allsectorsalteredsincechange0.
Poweredonvirtualmachinesmustgothroughastununstuncycle(poweron,resumeaftersuspend,migrate,
orsnapshotcreate/delete/revert)beforethereconfigurationtakeseffect.
21
SelectthevirtualmachineandensurethatSummary>VMVersionsays7forvirtualhardwareversion.
IntheSummarytab,clickEditSettings>Options>Advanced>General.
Intherightsideofthedialogbox,clickConfigurationParameters...
Inthenewdialogbox,locatetherowfornamectkEnabled,andchangeitsvaluefromfalsetotrue.
Seeaboveconcerningthestununstuncycle.
To enable Changed Block Tracking and back up with the VMware vSphere API:
1
Querychangetrackingstatusofthevirtualmachine.Iffalse,activateChangedBlockTracking.
Createasnapshotofthevirtualmachine.Thesnapshotoperationcausesastununstuncycle.
StartingfromthesnapshotsConfigInfo,workyourwaytotheBackingInfoofallvirtualdisksinthe
snapshot.ThisgivesyouthechangeIDsforallthedisksofthevirtualmachine.
HoldontothechangeIDsanddoafullbackupofthesnapshot,sincethisisthefirsttimeforbackup.
Deletethesnapshotwhenyourbackuphascompleted.
Nexttimeyoubackupthisvirtualmachine,createasnapshotanduseQueryChangedDiskAreaswiththe
changeIDsfromyourpreviousbackuptotakeadvantageofChangedBlockTracking.
InformationreturnedfromtheQueryChangedDiskAreasmethodisaVirtualMachine.DiskChangeInfo
dataobjectcontainingadescriptionoftheareaofthediskcovered(startoffsetandlength),andanarrayof
VirtualMachine.DiskChangeInfo.DiskChangeExtentitemsthatdescribethestartingoffsetandlengthof
variousdiskareasthathavechanged.
Tofindchangeinformation,youcanusetheManagedObjectBrowserathttp://<ESXhost>/mobtofollowpath
content>hafolderroot>hadatacenter>datastore>vm>snapshot>config>hardware>virtualDisk.
Changedblocktrackingisassociatedwiththesnapshot,notwiththebasedisk.
Thefollowingcodesampleassumesthat,inthepast,youobtainedacompletecopyofthevirtualdisk,andat
thetimewhenthechangeIdassociatedwiththesnapshotwascollected,youstoreditforuseatalatertime,
whichisnow.Anewsnapshothasbeencreated,andtheappropriateMoRefisavailable:
String changeId; // Already initialized
ManagedObjectReference theSnapshot;
ManagedObjectReference theVM;
int diskDeviceKey;
VirtualMachine.DiskChangeInfo changes;
long position = 0;
do {
changes = theVM.queryChangedDiskAreas(theSnapshot,
diskDeviceKey,
position, changeId);
22
Intheabovecode,QueryChangedDiskAreasiscalledrepeatedly,aspositionmovesthroughthevirtualdisk.
ThisisbecausethenumberofentriesintheChangedDiskAreaarraycouldendupoccupyingalargeamount
ofmemoryfordescribingmanychangestoalargevirtualdisk.
ThechangeId(changedblockID)containsasequencenumberintheform<UUID>/<nnn>.Ifthe<UUID>
changes,itindicatesthattrackinginformationhasbecomeinvalid,necessitatingafullbackup.Otherwise
incrementalbackupscancontinueintheusualpattern.
Troubleshooting
IfyoureconfigureavirtualmachinetosetchangeTrackingEnabled,butthepropertyremainsfalse,check
thatyouhavequeriedthevirtualmachinestatuswithVirtualMachine->config()afterreconfiguration
withVirtualMachine->reconfigure().Yes,youmustdoitagain.Alsomakesurethatthevirtualmachine
ishardwareversion7andhasundergoneastununstuncyclesincereconfiguration.
Virtualhardwareversionis6orearlier.
ThevirtualdiskisaphysicalcompatibilityRDM.
ThevirtualdiskisattachedtoasharedvirtualSCSIbus.
ChangedBlockTrackingcanbeenabledonvirtualmachinesthathavediskslikethese,butwhenqueriedfor
theirchangeID,thesedisksalwaysreturnanemptystring.Soifyouhaveavirtualmachinewitharegular
O/SdiskandapassthroughRDMasadatadiskyoucantrackchangesontheO/Sdiskonly.
Poweroffthevirtualmachine,ifpoweredon.
UsingtheVirtualMachineConfigInfothatcorrespondstothelastknowngoodstateoftheguest
operatingsystem,recreatethevirtualmachineasdescribedinsectionUsingVirtualMachineConfigInfo
toCreateaVirtualMachineonpage 20.
Completelyreloadthebasevirtualdiskusingthefullbackupthatstartedthemostrecentseriesof
incrementalbackups.
Createasnapshot,asachildofthebasevirtualdisk.
23
Sequentiallyrestoretheincrementalbackupdata.Youcandothiseitherforwardsorbackwards.Ifyou
workforwards,therestoremightwritesomesectorsmorethanonce.Ifyouworkbackwards,youmust
keeptrackofwhichsectorswererestoredsoastoavoidrestoringthemagainfromolderdata.
GetthechangeIDofthebackupthisisbeingrestored,andusethechangetrackingAPIstofindtheareas
ofthediskthatneedtoberestored.Thisinformationmustbecached,becauseonceyoustartrestoringa
virtualdisk,thechangetrackingmechanismwillfail.
Restoreonlychangedareastothevirtualdisksreferredtobythesnapshot.Thisensuresthatyoudonot
writethedatatotheredologcreatedbythesnapshot.Whenrestoringathinprovisioned(sparse)disk,
usethestar*changeIDtoavoidwritingzeroestotheunallocatedblocks.
Optionally,reverttothebasevirtualdisk,thuseliminatingthesnapshot.
Conclusion
ThistechnicalnoteintroducedtheVMwarevStorageAPIsforDataProtection,givinganoverviewofbackup
andrestoreoperationsinVMwarevSphere.Theinformationpresentedhereshouldhelpyouunderstandthe
underlyingconceptsandstepstotakeforimplementingapplicationstoprotectvirtualmachinedata.
If you have comments about this documentation, submit your feedback to: docfeedback@vmware.com
VMware, Inc. 3401 Hillview Ave., Palo Alto, CA 94304 www.vmware.com
Copyright 2009 VMware, Inc. All rights reserved. This product is protected by U.S. and international copyright and intellectual property laws. VMware products are covered by
one or more patents listed at http://www.vmware.com/go/patents. VMware, the VMware boxes logo and design, Virtual SMP, and VMotion are registered trademarks or
trademarks of VMware, Inc. in the United States and/or other jurisdictions. All other marks and names mentioned herein may be trademarks of their respective companies.
Item: EN-000165-01 | Revised: 7/7/09
24