Skip to content

Commit

Permalink
Migrate to persistent target domains instead of transient ones
Browse files Browse the repository at this point in the history
Signed-off-by: Jed Lejosne <[email protected]>
  • Loading branch information
jean-edouard committed Feb 19, 2021
1 parent f1b17a2 commit 3505763
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ go-build:

gosec:
hack/dockerized "GOSEC=${GOSEC} ./hack/gosec.sh"

coverage:
hack/dockerized "./hack/coverage.sh ${WHAT}"

Expand Down
75 changes: 71 additions & 4 deletions pkg/virt-launcher/virtwrap/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ package virtwrap
*/

import (
"bytes"
"context"
"encoding/xml"
"fmt"
"io"
"net"
"os"
"path/filepath"
Expand Down Expand Up @@ -318,7 +320,7 @@ func (l *LibvirtDomainManager) setMigrationResultHelper(vmi *v1.VirtualMachineIn
}

func prepareMigrationFlags(isBlockMigration, isUnsafeMigration, allowAutoConverge, allowPostyCopy bool) libvirt.DomainMigrateFlags {
migrateFlags := libvirt.MIGRATE_LIVE | libvirt.MIGRATE_PEER2PEER
migrateFlags := libvirt.MIGRATE_LIVE | libvirt.MIGRATE_PEER2PEER | libvirt.MIGRATE_PERSIST_DEST

if isBlockMigration {
migrateFlags |= libvirt.MIGRATE_NON_SHARED_INC
Expand Down Expand Up @@ -419,6 +421,63 @@ func getDiskTargetsForMigration(dom cli.VirDomain, vmi *v1.VirtualMachineInstanc
return copyDisks
}

func domXMLWithoutKubevirtMetadata(dom cli.VirDomain, vmi *v1.VirtualMachineInstance) (string, error) {
xmlstr, err := dom.GetXMLDesc(libvirt.DOMAIN_XML_MIGRATABLE)
if err != nil {
log.Log.Object(vmi).Reason(err).Error("Live migration failed. Failed to get XML.")
return "", err
}
decoder := xml.NewDecoder(bytes.NewReader([]byte(xmlstr)))
var buf bytes.Buffer
encoder := xml.NewEncoder(&buf)

depth := 0
inMeta := false
inMetaKV := false
for {
token, err := decoder.Token()
if err == io.EOF {
break
}
if err != nil {
log.Log.Object(vmi).Errorf("error getting token: %v\n", err)
break
}

switch v := token.(type) {
case xml.StartElement:
if depth == 1 && v.Name.Local == "metadata" {
inMeta = true
} else if inMeta && depth == 2 && v.Name.Local == "kubevirt" {
inMetaKV = true
}
depth++
case xml.EndElement:
depth--
if inMetaKV && depth == 2 && v.Name.Local == "kubevirt" {
inMetaKV = false
continue // Skip </kubevirt>
}
if inMeta && depth == 1 && v.Name.Local == "metadata" {
inMeta = false
}
}
if inMetaKV {
continue // We're inside metadata/kubevirt, continuing to skip elements
}

if err := encoder.EncodeToken(xml.CopyToken(token)); err != nil {
log.Log.Object(vmi).Reason(err)
}
}

if err := encoder.Flush(); err != nil {
log.Log.Object(vmi).Reason(err)
}

return string(buf.Bytes()), nil
}

func (l *LibvirtDomainManager) asyncMigrate(vmi *v1.VirtualMachineInstance, options *cmdclient.MigrationOptions) {

go func(l *LibvirtDomainManager, vmi *v1.VirtualMachineInstance) {
Expand Down Expand Up @@ -481,10 +540,18 @@ func (l *LibvirtDomainManager) asyncMigrate(vmi *v1.VirtualMachineInstance, opti
return
}

xmlstr, err := domXMLWithoutKubevirtMetadata(dom, vmi)
if err != nil {
log.Log.Object(vmi).Reason(err).Error("Live migration failed. Could not compute target XML.")
return
}

params := &libvirt.DomainMigrateParameters{
Bandwidth: bandwidth, // MiB/s
URI: migrURI,
URISet: true,
Bandwidth: bandwidth, // MiB/s
URI: migrURI,
URISet: true,
DestXML: xmlstr,
DestXMLSet: true,
}
copyDisks := getDiskTargetsForMigration(dom, vmi)
if len(copyDisks) != 0 {
Expand Down
52 changes: 51 additions & 1 deletion pkg/virt-launcher/virtwrap/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,7 @@ var _ = Describe("Manager", func() {
migrationMode := migrationType == "postCopy"

flags := prepareMigrationFlags(isBlockMigration, isUnsafeMigration, allowAutoConverge, migrationMode)
expectedMigrateFlags := libvirt.MIGRATE_LIVE | libvirt.MIGRATE_PEER2PEER
expectedMigrateFlags := libvirt.MIGRATE_LIVE | libvirt.MIGRATE_PEER2PEER | libvirt.MIGRATE_PERSIST_DEST

if isBlockMigration {
expectedMigrateFlags |= libvirt.MIGRATE_NON_SHARED_INC
Expand Down Expand Up @@ -1534,6 +1534,56 @@ var _ = Describe("getDetachedDisks", func() {
)
})

var _ = Describe("domXMLWithoutKubevirtMetadata", func() {
var ctrl *gomock.Controller
var mockDomain *cli.MockVirDomain
BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
mockDomain = cli.NewMockVirDomain(ctrl)
})
It("should remove only the kubevirt metadata", func() {
domXML := `<domain type="kvm" id="1">
<name>kubevirt</name>
<metadata>
<kubevirt xmlns="http://kubevirt.io">
<metadata>
<kubevirt>nested</kubevirt>
</metadata>
<uid>d38cac9c-435b-42d5-960e-06e8d41146e8</uid>
<graceperiod>
<deletionGracePeriodSeconds>0</deletionGracePeriodSeconds>
</graceperiod>
</kubevirt>
<othermetadata>
<kubevirt>
<keepme>42</keepme>
</kubevirt>
</othermetadata>
</metadata>
<kubevirt>this should stay</kubevirt>
</domain>`
// domXMLWithoutKubevirtMetadata() removes the kubevirt block but not its ident, which is its own token, hence the blank line below
expectedXML := `<domain type="kvm" id="1">
<name>kubevirt</name>
<metadata>
<othermetadata>
<kubevirt>
<keepme>42</keepme>
</kubevirt>
</othermetadata>
</metadata>
<kubevirt>this should stay</kubevirt>
</domain>`
mockDomain.EXPECT().Free()
vmi := newVMI("testns", "kubevirt")
mockDomain.EXPECT().GetXMLDesc(libvirt.DOMAIN_XML_MIGRATABLE).MaxTimes(1).Return(string(domXML), nil)
newXML, err := domXMLWithoutKubevirtMetadata(mockDomain, vmi)
Expect(err).To(BeNil())
Expect(newXML).To(Equal(expectedXML))
})
})

func newVMI(namespace, name string) *v1.VirtualMachineInstance {
vmi := v1.NewMinimalVMIWithNS(namespace, name)
v1.SetObjectDefaults_VirtualMachineInstance(vmi)
Expand Down

0 comments on commit 3505763

Please sign in to comment.