Merge pull request #7 from digitalocean/migrations

Add support for domain migrations
This commit is contained in:
Ben LeMasurier 2016-08-22 07:21:06 -06:00 committed by GitHub
commit 0417eacbef
4 changed files with 157 additions and 1 deletions

View File

@ -29,10 +29,11 @@ const (
ProcConnectOpen = 1 ProcConnectOpen = 1
ProcConnectClose = 2 ProcConnectClose = 2
ProcDomainLookupByName = 23 ProcDomainLookupByName = 23
ProcDomainMigrateSetMaxSpeed = 207
ProcAuthList = 66 ProcAuthList = 66
ProcConnectGetLibVersion = 157 ProcConnectGetLibVersion = 157
ProcDomainMigrateSetMaxSpeed = 207
ProcConnectListAllDomains = 273 ProcConnectListAllDomains = 273
ProcMigratePerformParams = 305
) )
// qemu procedure identifiers // qemu procedure identifiers

View File

@ -23,6 +23,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/url"
"sync" "sync"
"github.com/davecgh/go-xdr/xdr2" "github.com/davecgh/go-xdr/xdr2"
@ -77,6 +78,56 @@ type qemuError struct {
} `json:"error"` } `json:"error"`
} }
// MigrateFlags specifies options when performing a migration.
type MigrateFlags uint32
const (
// MigrateFlagLive performs a zero-downtime live migration.
MigrateFlagLive MigrateFlags = 1 << iota
// MigrateFlagPeerToPeer creates a direct source to destination control channel.
MigrateFlagPeerToPeer
// MigrateFlagTunneled tunnels migration data over the libvirtd connection.
MigrateFlagTunneled
// MigrateFlagPersistDestination will persist the VM on the destination host.
MigrateFlagPersistDestination
// MigrateFlagUndefineSource undefines the VM on the source host.
MigrateFlagUndefineSource
// MigrateFlagPaused will pause the remote side VM.
MigrateFlagPaused
// MigrateFlagNonSharedDisk migrate non-shared storage with full disk copy.
MigrateFlagNonSharedDisk
// MigrateFlagNonSharedIncremental migrate non-shared storage with incremental copy.
MigrateFlagNonSharedIncremental
// MigrateFlagChangeProtection prevents any changes to the domain configuration through the whole migration process.
MigrateFlagChangeProtection
// MigrateFlagUnsafe will force a migration even when it is considered unsafe.
MigrateFlagUnsafe
// MigrateFlagOffline is used to perform an offline migration.
MigrateFlagOffline
// MigrateFlagCompressed compresses data during migration.
MigrateFlagCompressed
// MigrateFlagAbortOnError will abort a migration on I/O errors encountered during migration.
MigrateFlagAbortOnError
// MigrateFlagAutoConverge forces convergence.
MigrateFlagAutoConverge
// MigrateFlagRDMAPinAll enables RDMA memory pinning.
MigrateFlagRDMAPinAll
)
// Connect establishes communication with the libvirt server. // Connect establishes communication with the libvirt server.
// The underlying libvirt socket connection must be previously established. // The underlying libvirt socket connection must be previously established.
func (l *Libvirt) Connect() error { func (l *Libvirt) Connect() error {
@ -204,6 +255,58 @@ func (l *Libvirt) Events(dom string) (<-chan DomainEvent, error) {
return c, nil return c, nil
} }
// Migrate synchronously migrates the domain specified by dom, e.g.,
// 'prod-lb-01', to the destination hypervisor specified by dest, e.g.,
// 'qemu+tcp://example.com/system'. The flags argument determines the
// type of migration and how it will be performed. For more information
// on available migration flags and their meaning, see MigrateFlag*.
func (l *Libvirt) Migrate(dom string, dest string, flags MigrateFlags) error {
_, err := url.Parse(dest)
if err != nil {
return err
}
d, err := l.lookup(dom)
if err != nil {
return err
}
// Two unknowns remain here , Libvirt specifies RemoteParameters
// and CookieIn. In testing both values are always set to 0 by virsh
// and the source does not provide clear definitions of their purpose.
// For now, using the same zero'd values as done by virsh will be Good Enough.
payload := struct {
Domain Domain
DestinationURI string
RemoteParameters uint32
CookieIn uint32
Flags MigrateFlags
}{
Domain: *d,
DestinationURI: dest,
RemoteParameters: 0,
CookieIn: 0,
Flags: flags,
}
buf, err := encode(&payload)
if err != nil {
return err
}
resp, err := l.request(constants.ProcMigratePerformParams, constants.ProgramRemote, &buf)
if err != nil {
return err
}
r := <-resp
if r.Status != StatusOK {
return decodeError(r.Payload)
}
return nil
}
// MigrateSetMaxSpeed set the maximum migration bandwidth (in MiB/s) for a // MigrateSetMaxSpeed set the maximum migration bandwidth (in MiB/s) for a
// domain which is being migrated to another host. Specifying a negative value // domain which is being migrated to another host. Specifying a negative value
// results in an essentially unlimited value being provided to the hypervisor. // results in an essentially unlimited value being provided to the hypervisor.

View File

@ -43,6 +43,43 @@ func TestDisconnect(t *testing.T) {
} }
} }
func TestMigrate(t *testing.T) {
conn := libvirttest.New()
l := New(conn)
var flags MigrateFlags
flags = MigrateFlagLive |
MigrateFlagPeerToPeer |
MigrateFlagPersistDestination |
MigrateFlagChangeProtection |
MigrateFlagAbortOnError |
MigrateFlagAutoConverge |
MigrateFlagNonSharedDisk
if err := l.Migrate("test", "qemu+tcp://foo/system", flags); err != nil {
t.Fatalf("unexpected live migration error: %v", err)
}
}
func TestMigrateInvalidDest(t *testing.T) {
conn := libvirttest.New()
l := New(conn)
var flags MigrateFlags
flags = MigrateFlagLive |
MigrateFlagPeerToPeer |
MigrateFlagPersistDestination |
MigrateFlagChangeProtection |
MigrateFlagAbortOnError |
MigrateFlagAutoConverge |
MigrateFlagNonSharedDisk
dest := ":$'"
if err := l.Migrate("test", dest, flags); err == nil {
t.Fatalf("expected invalid dest uri %q to fail", dest)
}
}
func TestMigrateSetMaxSpeed(t *testing.T) { func TestMigrateSetMaxSpeed(t *testing.T) {
conn := libvirttest.New() conn := libvirttest.New()
l := New(conn) l := New(conn)

View File

@ -94,6 +94,19 @@ var testDisconnectReply = []byte{
0x00, 0x00, 0x00, 0x00, // status 0x00, 0x00, 0x00, 0x00, // status
} }
var testMigrateReply = []byte{
0x00, 0x00, 0x00, 0x20, // length
0x20, 0x00, 0x80, 0x86, // program
0x00, 0x00, 0x00, 0x01, // version
0x00, 0x00, 0x01, 0x31, // procedure
0x00, 0x00, 0x00, 0x01, // type
0x00, 0x00, 0x00, 0x00, // serial
0x00, 0x00, 0x00, 0x00, // status
// cookie out: 0
0x00, 0x00, 0x00, 0x00,
}
var testRunReply = []byte{ var testRunReply = []byte{
0x00, 0x00, 0x00, 0x74, // length 0x00, 0x00, 0x00, 0x74, // length
0x20, 0x00, 0x80, 0x87, // program 0x20, 0x00, 0x80, 0x87, // program
@ -262,6 +275,8 @@ func (m *MockLibvirt) handleRemote(procedure uint32, conn net.Conn) {
conn.Write(m.reply(testDomainsReply)) conn.Write(m.reply(testDomainsReply))
case constants.ProcDomainMigrateSetMaxSpeed: case constants.ProcDomainMigrateSetMaxSpeed:
conn.Write(m.reply(testSetSpeedReply)) conn.Write(m.reply(testSetSpeedReply))
case constants.ProcMigratePerformParams:
conn.Write(m.reply(testMigrateReply))
} }
} }