diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 50d2745..fdf0f34 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -42,6 +42,8 @@ const ( ProcDomainGetXMLDesc = 14 ProcDomainLookupByName = 23 ProcAuthList = 66 + ProcStoragePoolRefresh = 83 + ProcStoragePoolLookupByName = 84 ProcConnectGetLibVersion = 157 ProcDomainMigrateSetMaxSpeed = 207 ProcDomainGetState = 212 diff --git a/libvirt.go b/libvirt.go index d94b009..b4b5ea3 100644 --- a/libvirt.go +++ b/libvirt.go @@ -572,6 +572,76 @@ func (l *Libvirt) Run(dom string, cmd []byte) ([]byte, error) { return bytes.TrimRight(data[4:], "\x00"), nil } +// StoragePool returns the storage pool associated with the provided name. +// An error is returned if the requested storage pool is not found. +func (l *Libvirt) StoragePool(name string) (*StoragePool, error) { + req := struct { + Name string + }{ + Name: name, + } + + buf, err := encode(&req) + if err != nil { + return nil, err + } + + resp, err := l.request(constants.ProcStoragePoolLookupByName, constants.ProgramRemote, &buf) + if err != nil { + return nil, err + } + + r := <-resp + if r.Status != StatusOK { + return nil, decodeError(r.Payload) + } + + result := struct { + Pool StoragePool + }{} + + dec := xdr.NewDecoder(bytes.NewReader(r.Payload)) + _, err = dec.Decode(&result) + if err != nil { + return nil, err + } + + return &result.Pool, nil +} + +// StoragePoolRefresh refreshes the storage pool specified by name. +func (l *Libvirt) StoragePoolRefresh(name string) error { + pool, err := l.StoragePool(name) + if err != nil { + return err + } + + req := struct { + Pool StoragePool + Flags uint32 + }{ + Pool: *pool, + Flags: 0, // unused per libvirt source, callers should pass 0 + } + + buf, err := encode(&req) + if err != nil { + return err + } + + resp, err := l.request(constants.ProcStoragePoolRefresh, constants.ProgramRemote, &buf) + if err != nil { + return err + } + + r := <-resp + if r.Status != StatusOK { + return decodeError(r.Payload) + } + + return nil +} + // StoragePools returns a list of defined storage pools. Pools are filtered by // the provided flags. See StoragePools*. func (l *Libvirt) StoragePools(flags StoragePoolsFlags) ([]StoragePool, error) { diff --git a/libvirt_integration_test.go b/libvirt_integration_test.go index 73080c4..14f5ba7 100644 --- a/libvirt_integration_test.go +++ b/libvirt_integration_test.go @@ -70,6 +70,40 @@ func TestCapabilities(t *testing.T) { } } +func TestStoragePoolIntegration(t *testing.T) { + l := New(testConn(t)) + defer l.Disconnect() + + if err := l.Connect(); err != nil { + t.Fatal(err) + } + + wantName := "test" + pool, err := l.StoragePool(wantName) + if err != nil { + t.Fatal(err) + } + + gotName := pool.Name + if gotName != wantName { + t.Errorf("expected name %q, got %q", wantName, gotName) + } +} + +func TestStoragePoolInvalidIntegration(t *testing.T) { + l := New(testConn(t)) + defer l.Disconnect() + + if err := l.Connect(); err != nil { + t.Fatal(err) + } + + _, err := l.StoragePool("test-does-not-exist") + if err == nil { + t.Errorf("expected non-existent storage pool return error") + } +} + func TestStoragePoolsIntegration(t *testing.T) { l := New(testConn(t)) defer l.Disconnect() @@ -116,6 +150,34 @@ func TestStoragePoolsAutostartIntegration(t *testing.T) { } } +func TestStoragePoolRefreshIntegration(t *testing.T) { + l := New(testConn(t)) + defer l.Disconnect() + + if err := l.Connect(); err != nil { + t.Fatal(err) + } + + err := l.StoragePoolRefresh("test") + if err != nil { + t.Error(err) + } +} + +func TestStoragePoolRefreshInvalidIntegration(t *testing.T) { + l := New(testConn(t)) + defer l.Disconnect() + + if err := l.Connect(); err != nil { + t.Fatal(err) + } + + err := l.StoragePoolRefresh("test-does-not-exist") + if err == nil { + t.Error("expected non-existent storage pool to fail refresh") + } +} + func TestXMLIntegration(t *testing.T) { l := New(testConn(t)) diff --git a/libvirt_test.go b/libvirt_test.go index f06cc09..f23a132 100644 --- a/libvirt_test.go +++ b/libvirt_test.go @@ -221,6 +221,32 @@ func TestRunFail(t *testing.T) { } } +func TestStoragePool(t *testing.T) { + conn := libvirttest.New() + l := New(conn) + + wantName := "default" + pool, err := l.StoragePool(wantName) + if err != nil { + t.Error(err) + } + + gotName := pool.Name + if gotName != wantName { + t.Errorf("expected name %q, got %q", wantName, gotName) + } + + // bb30a11c-0846-4827-8bba-3e6b5cf1b65f + wantUUID := [constants.UUIDSize]byte{ + 0xbb, 0x30, 0xa1, 0x1c, 0x08, 0x46, 0x48, 0x27, + 0x8b, 0xba, 0x3e, 0x6b, 0x5c, 0xf1, 0xb6, 0x5f, + } + gotUUID := pool.UUID + if gotUUID != wantUUID { + t.Errorf("expected UUID %q, got %q", wantUUID, gotUUID) + } +} + func TestStoragePools(t *testing.T) { conn := libvirttest.New() l := New(conn) @@ -253,6 +279,16 @@ func TestStoragePools(t *testing.T) { } } +func TestStoragePoolRefresh(t *testing.T) { + conn := libvirttest.New() + l := New(conn) + + err := l.StoragePoolRefresh("default") + if err != nil { + t.Error(err) + } +} + func TestUndefine(t *testing.T) { conn := libvirttest.New() l := New(conn) diff --git a/libvirttest/libvirt.go b/libvirttest/libvirt.go index 0d9742e..80bf7d1 100644 --- a/libvirttest/libvirt.go +++ b/libvirttest/libvirt.go @@ -217,6 +217,34 @@ var testDomainStateReply = []byte{ 0x00, 0x00, 0x00, 0x01, // reason } +var testStoragePoolLookup = []byte{ + 0x00, 0x00, 0x00, 0x38, // length + 0x20, 0x00, 0x80, 0x86, // program + 0x00, 0x00, 0x00, 0x01, // version + 0x00, 0x00, 0x00, 0x54, // procedure + 0x00, 0x00, 0x00, 0x01, // type + 0x00, 0x00, 0x00, 0x00, // serial + 0x00, 0x00, 0x00, 0x00, // status + + // pool: name = default + 0x00, 0x00, 0x00, 0x07, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x00, + + // uuid = bb30a11c084648278bba3e6b5cf1b65f + 0xbb, 0x30, 0xa1, 0x1c, 0x08, 0x46, 0x48, 0x27, + 0x8b, 0xba, 0x3e, 0x6b, 0x5c, 0xf1, 0xb6, 0x5f, +} + +var testStoragePoolRefresh = []byte{ + 0x00, 0x00, 0x00, 0x1c, // length + 0x20, 0x00, 0x80, 0x86, // program + 0x00, 0x00, 0x00, 0x01, // version + 0x00, 0x00, 0x00, 0x53, // procedure + 0x00, 0x00, 0x00, 0x01, // type + 0x00, 0x00, 0x00, 0x00, // serial + 0x00, 0x00, 0x00, 0x00, // status +} + var testListPoolsReply = []byte{ 0x00, 0x00, 0x00, 0x40, // length 0x20, 0x00, 0x80, 0x86, // program @@ -333,6 +361,10 @@ func (m *MockLibvirt) handleRemote(procedure uint32, conn net.Conn) { switch procedure { case constants.ProcAuthList: conn.Write(m.reply(testAuthReply)) + case constants.ProcStoragePoolRefresh: + conn.Write(m.reply(testStoragePoolRefresh)) + case constants.ProcStoragePoolLookupByName: + conn.Write(m.reply(testStoragePoolLookup)) case constants.ProcConnectOpen: conn.Write(m.reply(testConnectReply)) case constants.ProcConnectClose: