From 1b2d460b06a21e2ff589e754296b392d2fd2a315 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sat, 8 Oct 2016 21:52:16 -0400 Subject: [PATCH 01/30] started libvirt-go monitor --- qmp/libvirtgo.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 qmp/libvirtgo.go diff --git a/qmp/libvirtgo.go b/qmp/libvirtgo.go new file mode 100644 index 0000000..d4035f5 --- /dev/null +++ b/qmp/libvirtgo.go @@ -0,0 +1,61 @@ +// Copyright 2016 The go-qemu Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package qmp + +import "net/url" + +// LibvirtGoMonitor is a monitor that wraps the libvirt-go api to +// communicate with a QEMU Machine Protocol (QMP) socket. +// Communication is proxied via the libvirtd daemon. Multiple +// connections to the same hypervisor and domain are permitted. +type LibvirtGoMonitor struct { + domain string + url *url.URL +} + +// Connect - Needs better comments +func (mon LibvirtGoMonitor) Connect() error { + return nil +} + +// Disconnect tears down open QMP socket connections. +func (mon *LibvirtGoMonitor) Disconnect() error { + +} + +// Run executes the given QAPI command against a domain's QEMU instance. +// For a list of available QAPI commands, see: +// http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD +func (mon LibvirtGoMonitor) Run(cmd []byte) ([]byte, error) { + return []byte[], nil +} + +// Events streams QEMU QMP Events. +// If a problem is encountered setting up the event monitor connection +// an error will be returned. Errors encountered during streaming will +// cause the returned event channel to be closed. +func (mon *LibvirtGoMonitor) Events() (<-chan Event, error) { + return nil, nil +} + +// NewLibvirtGoMonitor configures a connection to the provided hypervisor and domain. +// An error is returned if the provided libvirt connection URI is invalid. +// +// Hypervisor URIs may be local or remote, e.g., +// qemu:///system +// qemu+ssh://libvirt@example.com/system +func NewLibvirtGoMonitor(uri, domain string) (*LibvirtGoMonitor, error) { + return nil, nil +} \ No newline at end of file From f2b7da52b9010f29af42cb61bff235cc952d6ef6 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sat, 8 Oct 2016 23:14:01 -0400 Subject: [PATCH 02/30] libvirtgo: added new, connect, run, and disconnect --- qmp/libvirtgo.go | 51 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/qmp/libvirtgo.go b/qmp/libvirtgo.go index d4035f5..4bfdb1d 100644 --- a/qmp/libvirtgo.go +++ b/qmp/libvirtgo.go @@ -14,32 +14,56 @@ package qmp -import "net/url" +import libvirt "github.com/rgbkrk/libvirt-go" -// LibvirtGoMonitor is a monitor that wraps the libvirt-go api to +// LibvirtGoMonitor is a monitor that wraps the libvirt-go package to // communicate with a QEMU Machine Protocol (QMP) socket. // Communication is proxied via the libvirtd daemon. Multiple // connections to the same hypervisor and domain are permitted. type LibvirtGoMonitor struct { - domain string - url *url.URL + domain string + uri string + virConn *libvirt.VirConnection } -// Connect - Needs better comments +// Connect sets up QEMU QMP connection via libvirt using +// the libvirt-go package. +// An error is returned if the libvirtd daemon is unreachable. func (mon LibvirtGoMonitor) Connect() error { - return nil + virConn, err := libvirt.NewVirConnection(mon.uri) + if err == nil { + mon.virConn = &virConn + } + return err } // Disconnect tears down open QMP socket connections. func (mon *LibvirtGoMonitor) Disconnect() error { - + var err error + if mon.virConn != nil { + _, err = mon.virConn.CloseConnection() + mon.virConn = nil + } + return err } // Run executes the given QAPI command against a domain's QEMU instance. // For a list of available QAPI commands, see: // http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD func (mon LibvirtGoMonitor) Run(cmd []byte) ([]byte, error) { - return []byte[], nil + domain, err := mon.virConn.LookupDomainByName(mon.domain) + if err != nil { + return nil, err + } + + result, err := domain.QemuMonitorCommand( + libvirt.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT, + string(cmd)) + if err != nil { + return nil, err + } + + return []byte(result), nil } // Events streams QEMU QMP Events. @@ -47,7 +71,7 @@ func (mon LibvirtGoMonitor) Run(cmd []byte) ([]byte, error) { // an error will be returned. Errors encountered during streaming will // cause the returned event channel to be closed. func (mon *LibvirtGoMonitor) Events() (<-chan Event, error) { - return nil, nil + return nil, nil } // NewLibvirtGoMonitor configures a connection to the provided hypervisor and domain. @@ -56,6 +80,9 @@ func (mon *LibvirtGoMonitor) Events() (<-chan Event, error) { // Hypervisor URIs may be local or remote, e.g., // qemu:///system // qemu+ssh://libvirt@example.com/system -func NewLibvirtGoMonitor(uri, domain string) (*LibvirtGoMonitor, error) { - return nil, nil -} \ No newline at end of file +func NewLibvirtGoMonitor(uri, domain string) *LibvirtGoMonitor { + return &LibvirtGoMonitor{ + uri: uri, + domain: domain, + } +} From 316181f2b84a8b06fc3ad7cd0ed06d983bcda501 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Wed, 12 Oct 2016 18:29:16 -0400 Subject: [PATCH 03/30] qmp/libvirt-go: start build tag support --- qmp/libvirtgo.go | 40 ++++++----------------- qmp/libvirtgo_linux.go | 61 +++++++++++++++++++++++++++++++++++ qmp/libvirtgo_notsupported.go | 41 +++++++++++++++++++++++ 3 files changed, 111 insertions(+), 31 deletions(-) create mode 100644 qmp/libvirtgo_linux.go create mode 100644 qmp/libvirtgo_notsupported.go diff --git a/qmp/libvirtgo.go b/qmp/libvirtgo.go index 4bfdb1d..739a7a8 100644 --- a/qmp/libvirtgo.go +++ b/qmp/libvirtgo.go @@ -21,6 +21,7 @@ import libvirt "github.com/rgbkrk/libvirt-go" // Communication is proxied via the libvirtd daemon. Multiple // connections to the same hypervisor and domain are permitted. type LibvirtGoMonitor struct { + Monitor domain string uri string virConn *libvirt.VirConnection @@ -30,40 +31,19 @@ type LibvirtGoMonitor struct { // the libvirt-go package. // An error is returned if the libvirtd daemon is unreachable. func (mon LibvirtGoMonitor) Connect() error { - virConn, err := libvirt.NewVirConnection(mon.uri) - if err == nil { - mon.virConn = &virConn - } - return err + return mon.connect() } // Disconnect tears down open QMP socket connections. func (mon *LibvirtGoMonitor) Disconnect() error { - var err error - if mon.virConn != nil { - _, err = mon.virConn.CloseConnection() - mon.virConn = nil - } - return err + return mon.disconnect() } // Run executes the given QAPI command against a domain's QEMU instance. // For a list of available QAPI commands, see: // http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD func (mon LibvirtGoMonitor) Run(cmd []byte) ([]byte, error) { - domain, err := mon.virConn.LookupDomainByName(mon.domain) - if err != nil { - return nil, err - } - - result, err := domain.QemuMonitorCommand( - libvirt.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT, - string(cmd)) - if err != nil { - return nil, err - } - - return []byte(result), nil + return mon.run(cmd) } // Events streams QEMU QMP Events. @@ -71,18 +51,16 @@ func (mon LibvirtGoMonitor) Run(cmd []byte) ([]byte, error) { // an error will be returned. Errors encountered during streaming will // cause the returned event channel to be closed. func (mon *LibvirtGoMonitor) Events() (<-chan Event, error) { - return nil, nil + return mon.events() } -// NewLibvirtGoMonitor configures a connection to the provided hypervisor and domain. +// NewLibvirtGoMonitor configures a connection to the provided hypervisor +// and domain. // An error is returned if the provided libvirt connection URI is invalid. // // Hypervisor URIs may be local or remote, e.g., // qemu:///system // qemu+ssh://libvirt@example.com/system -func NewLibvirtGoMonitor(uri, domain string) *LibvirtGoMonitor { - return &LibvirtGoMonitor{ - uri: uri, - domain: domain, - } +func NewLibvirtGoMonitor(uri, domain string) *Monitor { + return newLibvirtGoMonitor(uri, domain) } diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go new file mode 100644 index 0000000..3b6095e --- /dev/null +++ b/qmp/libvirtgo_linux.go @@ -0,0 +1,61 @@ +// Copyright 2016 The go-qemu Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package qmp + +import libvirt "github.com/rgbkrk/libvirt-go" + +func (mon LibvirtGoMonitor) connect() error { + virConn, err := libvirt.NewVirConnection(mon.uri) + if err == nil { + mon.virConn = &virConn + } + return err +} + +func (mon *LibvirtGoMonitor) disconnect() error { + var err error + if mon.virConn != nil { + _, err = mon.virConn.CloseConnection() + mon.virConn = nil + } + return err +} + +func (mon LibvirtGoMonitor) run(cmd []byte) ([]byte, error) { + domain, err := mon.virConn.LookupDomainByName(mon.domain) + if err != nil { + return nil, err + } + + result, err := domain.QemuMonitorCommand( + libvirt.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT, + string(cmd)) + if err != nil { + return nil, err + } + + return []byte(result), nil +} + +func (mon *LibvirtGoMonitor) events() (<-chan Event, error) { + return nil, nil +} + +func newLibvirtGoMonitor(uri, domain string) *Monitor { + return &LibvirtGoMonitor{ + uri: uri, + domain: domain, + } +} diff --git a/qmp/libvirtgo_notsupported.go b/qmp/libvirtgo_notsupported.go new file mode 100644 index 0000000..89154ba --- /dev/null +++ b/qmp/libvirtgo_notsupported.go @@ -0,0 +1,41 @@ +// +build !linux + +// Copyright 2016 The go-qemu Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package qmp + +import "fmt" + +var errorNotSupported = fmt.Errorf("libvirt-go is only supported on Linux") + +func (mon LibvirtGoMonitor) connect() error { + return notSupportedError +} + +func (mon *LibvirtGoMonitor) disconnect() error { + return notSupportedError +} + +func (mon LibvirtGoMonitor) run(cmd []byte) ([]byte, error) { + return nil, notSupportedError +} + +func (mon *LibvirtGoMonitor) events() (<-chan Event, error) { + return nil, notSupportedError +} + +func newLibvirtGoMonitor(uri, domain string) *Monitor { + return &LibvirtGoMonitor{} +} From f675e2d9a72152d420ef83539978a1335ce40a95 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Wed, 12 Oct 2016 19:17:05 -0400 Subject: [PATCH 04/30] qmp/libvirtgo: better code distribution --- qmp/libvirtgo.go | 13 +++++-------- qmp/libvirtgo_linux.go | 17 +++++++++++------ qmp/libvirtgo_notsupported.go | 16 +++++++++------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/qmp/libvirtgo.go b/qmp/libvirtgo.go index 739a7a8..2e76f67 100644 --- a/qmp/libvirtgo.go +++ b/qmp/libvirtgo.go @@ -14,17 +14,14 @@ package qmp -import libvirt "github.com/rgbkrk/libvirt-go" - -// LibvirtGoMonitor is a monitor that wraps the libvirt-go package to +// LibvirtGoMonitor is a Monitor that wraps the libvirt-go package to // communicate with a QEMU Machine Protocol (QMP) socket. -// Communication is proxied via the libvirtd daemon. Multiple +// Communication is provied via the libvirtd daemon. Multiple // connections to the same hypervisor and domain are permitted. type LibvirtGoMonitor struct { Monitor - domain string - uri string - virConn *libvirt.VirConnection + domain string + uri string } // Connect sets up QEMU QMP connection via libvirt using @@ -61,6 +58,6 @@ func (mon *LibvirtGoMonitor) Events() (<-chan Event, error) { // Hypervisor URIs may be local or remote, e.g., // qemu:///system // qemu+ssh://libvirt@example.com/system -func NewLibvirtGoMonitor(uri, domain string) *Monitor { +func NewLibvirtGoMonitor(uri, domain string) Monitor { return newLibvirtGoMonitor(uri, domain) } diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 3b6095e..bea6404 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -16,7 +16,12 @@ package qmp import libvirt "github.com/rgbkrk/libvirt-go" -func (mon LibvirtGoMonitor) connect() error { +type libvirtGoMonitorLinux struct { + LibvirtGoMonitor + virConn *libvirt.VirConnection +} + +func (mon libvirtGoMonitorLinux) connect() error { virConn, err := libvirt.NewVirConnection(mon.uri) if err == nil { mon.virConn = &virConn @@ -24,7 +29,7 @@ func (mon LibvirtGoMonitor) connect() error { return err } -func (mon *LibvirtGoMonitor) disconnect() error { +func (mon *libvirtGoMonitorLinux) disconnect() error { var err error if mon.virConn != nil { _, err = mon.virConn.CloseConnection() @@ -33,7 +38,7 @@ func (mon *LibvirtGoMonitor) disconnect() error { return err } -func (mon LibvirtGoMonitor) run(cmd []byte) ([]byte, error) { +func (mon libvirtGoMonitorLinux) run(cmd []byte) ([]byte, error) { domain, err := mon.virConn.LookupDomainByName(mon.domain) if err != nil { return nil, err @@ -49,12 +54,12 @@ func (mon LibvirtGoMonitor) run(cmd []byte) ([]byte, error) { return []byte(result), nil } -func (mon *LibvirtGoMonitor) events() (<-chan Event, error) { +func (mon *libvirtGoMonitorLinux) events() (<-chan Event, error) { return nil, nil } -func newLibvirtGoMonitor(uri, domain string) *Monitor { - return &LibvirtGoMonitor{ +func newLibvirtGoMonitor(uri, domain string) Monitor { + return &libvirtGoMonitorLinux{ uri: uri, domain: domain, } diff --git a/qmp/libvirtgo_notsupported.go b/qmp/libvirtgo_notsupported.go index 89154ba..5639f12 100644 --- a/qmp/libvirtgo_notsupported.go +++ b/qmp/libvirtgo_notsupported.go @@ -18,24 +18,26 @@ package qmp import "fmt" -var errorNotSupported = fmt.Errorf("libvirt-go is only supported on Linux") - func (mon LibvirtGoMonitor) connect() error { - return notSupportedError + return notSupportedError() } func (mon *LibvirtGoMonitor) disconnect() error { - return notSupportedError + return notSupportedError() } func (mon LibvirtGoMonitor) run(cmd []byte) ([]byte, error) { - return nil, notSupportedError + return nil, notSupportedError() } func (mon *LibvirtGoMonitor) events() (<-chan Event, error) { - return nil, notSupportedError + return nil, notSupportedError() } -func newLibvirtGoMonitor(uri, domain string) *Monitor { +func newLibvirtGoMonitor(uri, domain string) Monitor { return &LibvirtGoMonitor{} } + +func notSupportedError() error { + return fmt.Errorf("libvirt-go is only supported on Linux") +} From e0a60fee00fdcf3858d15b9d67aea5d731c27f6e Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Wed, 12 Oct 2016 19:38:18 -0400 Subject: [PATCH 05/30] qmp/libvirtgo: move exported methods to implementation --- qmp/libvirtgo.go | 38 ----------------------------------- qmp/libvirtgo_linux.go | 28 +++++++++++++++++++++----- qmp/libvirtgo_notsupported.go | 10 ++++----- 3 files changed, 28 insertions(+), 48 deletions(-) diff --git a/qmp/libvirtgo.go b/qmp/libvirtgo.go index 2e76f67..b63286e 100644 --- a/qmp/libvirtgo.go +++ b/qmp/libvirtgo.go @@ -23,41 +23,3 @@ type LibvirtGoMonitor struct { domain string uri string } - -// Connect sets up QEMU QMP connection via libvirt using -// the libvirt-go package. -// An error is returned if the libvirtd daemon is unreachable. -func (mon LibvirtGoMonitor) Connect() error { - return mon.connect() -} - -// Disconnect tears down open QMP socket connections. -func (mon *LibvirtGoMonitor) Disconnect() error { - return mon.disconnect() -} - -// Run executes the given QAPI command against a domain's QEMU instance. -// For a list of available QAPI commands, see: -// http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD -func (mon LibvirtGoMonitor) Run(cmd []byte) ([]byte, error) { - return mon.run(cmd) -} - -// Events streams QEMU QMP Events. -// If a problem is encountered setting up the event monitor connection -// an error will be returned. Errors encountered during streaming will -// cause the returned event channel to be closed. -func (mon *LibvirtGoMonitor) Events() (<-chan Event, error) { - return mon.events() -} - -// NewLibvirtGoMonitor configures a connection to the provided hypervisor -// and domain. -// An error is returned if the provided libvirt connection URI is invalid. -// -// Hypervisor URIs may be local or remote, e.g., -// qemu:///system -// qemu+ssh://libvirt@example.com/system -func NewLibvirtGoMonitor(uri, domain string) Monitor { - return newLibvirtGoMonitor(uri, domain) -} diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index bea6404..7aec0f4 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -21,7 +21,10 @@ type libvirtGoMonitorLinux struct { virConn *libvirt.VirConnection } -func (mon libvirtGoMonitorLinux) connect() error { +// Connect sets up QEMU QMP connection via libvirt using +// the libvirt-go package. +// An error is returned if the libvirtd daemon is unreachable. +func (mon libvirtGoMonitorLinux) Connect() error { virConn, err := libvirt.NewVirConnection(mon.uri) if err == nil { mon.virConn = &virConn @@ -29,7 +32,8 @@ func (mon libvirtGoMonitorLinux) connect() error { return err } -func (mon *libvirtGoMonitorLinux) disconnect() error { +// Disconnect tears down open QMP socket connections. +func (mon *libvirtGoMonitorLinux) Disconnect() error { var err error if mon.virConn != nil { _, err = mon.virConn.CloseConnection() @@ -38,7 +42,10 @@ func (mon *libvirtGoMonitorLinux) disconnect() error { return err } -func (mon libvirtGoMonitorLinux) run(cmd []byte) ([]byte, error) { +// Run executes the given QAPI command against a domain's QEMU instance. +// For a list of available QAPI commands, see: +// http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD +func (mon libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { domain, err := mon.virConn.LookupDomainByName(mon.domain) if err != nil { return nil, err @@ -54,11 +61,22 @@ func (mon libvirtGoMonitorLinux) run(cmd []byte) ([]byte, error) { return []byte(result), nil } -func (mon *libvirtGoMonitorLinux) events() (<-chan Event, error) { +// Events streams QEMU QMP Events. +// If a problem is encountered setting up the event monitor connection +// an error will be returned. Errors encountered during streaming will +// cause the returned event channel to be closed. +func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { return nil, nil } -func newLibvirtGoMonitor(uri, domain string) Monitor { +// NewLibvirtGoMonitor configures a connection to the provided hypervisor +// and domain. +// An error is returned if the provided libvirt connection URI is invalid. +// +// Hypervisor URIs may be local or remote, e.g., +// qemu:///system +// qemu+ssh://libvirt@example.com/system +func NewLibvirtGoMonitor(uri, domain string) Monitor { return &libvirtGoMonitorLinux{ uri: uri, domain: domain, diff --git a/qmp/libvirtgo_notsupported.go b/qmp/libvirtgo_notsupported.go index 5639f12..ae83376 100644 --- a/qmp/libvirtgo_notsupported.go +++ b/qmp/libvirtgo_notsupported.go @@ -18,23 +18,23 @@ package qmp import "fmt" -func (mon LibvirtGoMonitor) connect() error { +func (mon LibvirtGoMonitor) Connect() error { return notSupportedError() } -func (mon *LibvirtGoMonitor) disconnect() error { +func (mon *LibvirtGoMonitor) Disconnect() error { return notSupportedError() } -func (mon LibvirtGoMonitor) run(cmd []byte) ([]byte, error) { +func (mon LibvirtGoMonitor) Run(cmd []byte) ([]byte, error) { return nil, notSupportedError() } -func (mon *LibvirtGoMonitor) events() (<-chan Event, error) { +func (mon *LibvirtGoMonitor) Events() (<-chan Event, error) { return nil, notSupportedError() } -func newLibvirtGoMonitor(uri, domain string) Monitor { +func NewLibvirtGoMonitor(uri, domain string) Monitor { return &LibvirtGoMonitor{} } From f5d43e3421ac29d59e712214fcbb0943340b6867 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Wed, 12 Oct 2016 19:45:05 -0400 Subject: [PATCH 06/30] qmp/libvirtgo: move domain, uri to implementation --- qmp/libvirtgo.go | 2 -- qmp/libvirtgo_linux.go | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qmp/libvirtgo.go b/qmp/libvirtgo.go index b63286e..61999d4 100644 --- a/qmp/libvirtgo.go +++ b/qmp/libvirtgo.go @@ -20,6 +20,4 @@ package qmp // connections to the same hypervisor and domain are permitted. type LibvirtGoMonitor struct { Monitor - domain string - uri string } diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 7aec0f4..442632d 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -18,6 +18,8 @@ import libvirt "github.com/rgbkrk/libvirt-go" type libvirtGoMonitorLinux struct { LibvirtGoMonitor + domain string + uri string virConn *libvirt.VirConnection } From b737ddaf246a13247e911489f79fe989e3ec4b5f Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Wed, 12 Oct 2016 20:25:44 -0400 Subject: [PATCH 07/30] qmp/libvirtgo: fix pointer bug on Connect, Run methods --- qmp/libvirtgo_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 442632d..5fb8bad 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -26,7 +26,7 @@ type libvirtGoMonitorLinux struct { // Connect sets up QEMU QMP connection via libvirt using // the libvirt-go package. // An error is returned if the libvirtd daemon is unreachable. -func (mon libvirtGoMonitorLinux) Connect() error { +func (mon *libvirtGoMonitorLinux) Connect() error { virConn, err := libvirt.NewVirConnection(mon.uri) if err == nil { mon.virConn = &virConn @@ -47,7 +47,7 @@ func (mon *libvirtGoMonitorLinux) Disconnect() error { // Run executes the given QAPI command against a domain's QEMU instance. // For a list of available QAPI commands, see: // http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD -func (mon libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { +func (mon *libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { domain, err := mon.virConn.LookupDomainByName(mon.domain) if err != nil { return nil, err From c4e662b3caeb74d70fb298b6eff63df818899df9 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Wed, 12 Oct 2016 20:26:33 -0400 Subject: [PATCH 08/30] qmp/libvirtgo: add command example --- examples/libvirtgo_run_command/main.go | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 examples/libvirtgo_run_command/main.go diff --git a/examples/libvirtgo_run_command/main.go b/examples/libvirtgo_run_command/main.go new file mode 100644 index 0000000..6887f60 --- /dev/null +++ b/examples/libvirtgo_run_command/main.go @@ -0,0 +1,50 @@ +// Copyright 2016 The go-qemu Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "flag" + "fmt" + "log" + + "github.com/digitalocean/go-qemu/qmp" +) + +var ( + uri = flag.String("uri", "qemu:///system", `URI to connect to the libvirtd host.`) + domainName = flag.String("domainName", "mydomain", "This is the domain to run commands against.") +) + +func main() { + flag.Parse() + + libvirtGoMonitor := qmp.NewLibvirtGoMonitor(*uri, *domainName) + + err := libvirtGoMonitor.Connect() + if err != nil { + log.Fatalf("Unable to connect: %v\n", err) + } + + command := []byte("{\"execute\" : \"query-cpus\"}") + cpus, err := libvirtGoMonitor.Run(command) + if err != nil { + log.Fatalf("Unable to run command: %v\n", err) + } + fmt.Printf("query-cpus: %s\n", string(cpus)) + + if err = libvirtGoMonitor.Disconnect(); err != nil { + log.Fatalf("Unable to disconnect: %v\n", err) + } +} From bfb4beff3b1ccbaa9eac7b3a3a55d8eabf6a92c9 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sat, 15 Oct 2016 16:10:03 -0400 Subject: [PATCH 09/30] qmp/libvirtgo: start events notificaiton --- qmp/libvirtgo_linux.go | 160 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 2 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 5fb8bad..4d2517d 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -14,7 +14,13 @@ package qmp -import libvirt "github.com/rgbkrk/libvirt-go" +import ( + "fmt" + "sync" + "time" + + libvirt "github.com/rgbkrk/libvirt-go" +) type libvirtGoMonitorLinux struct { LibvirtGoMonitor @@ -23,6 +29,10 @@ type libvirtGoMonitorLinux struct { virConn *libvirt.VirConnection } +var once sync.Once + +var eventsTable map[int]string + // Connect sets up QEMU QMP connection via libvirt using // the libvirt-go package. // An error is returned if the libvirtd daemon is unreachable. @@ -68,7 +78,153 @@ func (mon *libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { // an error will be returned. Errors encountered during streaming will // cause the returned event channel to be closed. func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { - return nil, nil + + if mon.virConn == nil { + return nil, fmt.Errorf("Events() need a established connection to proceed.") + } + + eventsChan := make(chan Event) + + initialEventSetup() + + domain, err := mon.virConn.LookupDomainByName(mon.domain) + if err != nil { + return nil, err + } + + // doneChan will allow us to stop receiving events from libvirt + doneChan, err := domainEventRegistration(mon.virConn, &domain, eventsChan) + if err != nil { + return nil, err + } + + go eventRunDefaultImplLoop(doneChan) + + go domainEventDeregister(mon.virConn, doneChan) + + return eventsChan, nil +} + +// Initial setup to receive events. +// It's only done once. +func initialEventSetup() { + once.Do(onceEventRegisterDefaultImpl) + once.Do(populateEventTable) +} + +func onceEventRegisterDefaultImpl() { + eventRegisterId := libvirt.EventRegisterDefaultImpl() + if eventRegisterId == -1 { + fmt.Printf( + "got %d on libvirt.EventRegisterDefaultImpl instead of 0\n", + ret) + //TODO: panic or what??? + } +} + +func populateEventTable() { + eventsTable = map[int]string{ + libvirt.VIR_DOMAIN_EVENT_STARTED: "VIR_DOMAIN_EVENT_STARTED", + libvirt.VIR_DOMAIN_EVENT_SUSPENDED: "VIR_DOMAIN_EVENT_SUSPENDED", + libvirt.VIR_DOMAIN_EVENT_RESUMED: "VIR_DOMAIN_EVENT_RESUMED", + libvirt.VIR_DOMAIN_EVENT_STOPPED: "VIR_DOMAIN_EVENT_STOPPED", + libvirt.VIR_DOMAIN_EVENT_SHUTDOWN: "VIR_DOMAIN_EVENT_SHUTDOWN", + } +} + +func domainEventRegistration(virConn *libvirt.VirConnection, domain *libvirt.VirDomain, + eventsChan chan Event) (chan bool, error) { + // doneChan will allow us to stop receiving events from libvirt + doneChan := make(chan bool) + callback := + libvirt.DomainEventCallback(newEventCallback(eventsChan, doneChan)) + + //TODO: add ability to register for more event types + callbackId := virConn.DomainEventRegister( + *domain, + libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, + &callback, + func() { + //fmt.Printf("VIR_DOMAIN_EVENT_ID_LIFECYCLE called\n") + }, + ) + if callback == -1 { + close(doneChan) + return nil, fmt.Errorf("Domain event registration failed!") + } + + return doneChan, nil +} + +func domainEventDeregister(virConn, doneChan chan bool) { + <-doneChan + ret := virConn.DomainEventDeregister(callbackId) + if ret != 0 { + fmt.Printf("got %d on DomainEventDeregister instead of 0\n", ret) + } +} + +func newEventCallback(eventChan <-chan Event, + doneChan chan bool) libvirt.DomainEventCallback { + return func(c *libvirt.VirConnection, + d *libvirt.VirDomain, + eventDetails interface{}, f func()) int { + + //TODO: Probably need to check + // if eventChan is closed so we stop processing + + // We only support lifecycleEvents for now + if lifecycleEvent, ok := + eventDetails.(libvirt.DomainLifecycleEvent); ok { + eventChan <- constructEvent(lifecycleEvent) + f() + } else { + // Closing the channel indicates error + close(eventChan) + doneChan <- true + } + } +} + +// eventRunDefaultImplLoop keeps polling libvirt for new events +func eventRunDefaultImplLoop(doneChan chan bool) { + //TODO: Maybe interval should come + // from an ENV variable + tick := time.Tick(1 * time.Second) + for { + select { + case <-doneChan: // stop listening for events + return + case <-tick: + go func() { + ret := libvirt.EventRunDefaultImpl() + if ret == -1 { + doneChan <- true + return + } + }() + } + } +} + +func constructEvent(eventDetails libvirt.DomainLifecycleEvent) *Event { + // Technically, the timestamp is not accurate + // as events might occur before + // the next libvirt.EventRunDefaultImpl() execution + // at which point the notification is received. + now := time.Now() + var timestamp struct { + Seconds int64 + Microseconds int64 + } = { + now.Second(), + now.Nanosecond(), + } + return &Event{ + Event: eventsTable[eventDetails.Event], + Data: eventDetails, + Timestamp: timestamp, + } } // NewLibvirtGoMonitor configures a connection to the provided hypervisor From e4d22a691f89e7066fe173c04600ada8a00d71c0 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sat, 15 Oct 2016 16:32:07 -0400 Subject: [PATCH 10/30] qmp/libvirtgo: compiles on linux --- qmp/libvirtgo_linux.go | 54 ++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 4d2517d..7e39ebb 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -93,14 +93,14 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { } // doneChan will allow us to stop receiving events from libvirt - doneChan, err := domainEventRegistration(mon.virConn, &domain, eventsChan) + doneChan, callbackID, err := domainEventRegistration(mon.virConn, &domain, eventsChan) if err != nil { return nil, err } go eventRunDefaultImplLoop(doneChan) - go domainEventDeregister(mon.virConn, doneChan) + go domainEventDeregister(mon.virConn, callbackID, doneChan) return eventsChan, nil } @@ -113,11 +113,11 @@ func initialEventSetup() { } func onceEventRegisterDefaultImpl() { - eventRegisterId := libvirt.EventRegisterDefaultImpl() - if eventRegisterId == -1 { + eventRegisterID := libvirt.EventRegisterDefaultImpl() + if eventRegisterID == -1 { fmt.Printf( "got %d on libvirt.EventRegisterDefaultImpl instead of 0\n", - ret) + eventRegisterID) //TODO: panic or what??? } } @@ -133,14 +133,14 @@ func populateEventTable() { } func domainEventRegistration(virConn *libvirt.VirConnection, domain *libvirt.VirDomain, - eventsChan chan Event) (chan bool, error) { + eventsChan chan Event) (chan bool, int, error) { // doneChan will allow us to stop receiving events from libvirt doneChan := make(chan bool) callback := libvirt.DomainEventCallback(newEventCallback(eventsChan, doneChan)) //TODO: add ability to register for more event types - callbackId := virConn.DomainEventRegister( + callbackID := virConn.DomainEventRegister( *domain, libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, &callback, @@ -148,29 +148,29 @@ func domainEventRegistration(virConn *libvirt.VirConnection, domain *libvirt.Vir //fmt.Printf("VIR_DOMAIN_EVENT_ID_LIFECYCLE called\n") }, ) - if callback == -1 { + if callbackID == -1 { close(doneChan) - return nil, fmt.Errorf("Domain event registration failed!") + return nil, -1, fmt.Errorf("Domain event registration failed!") } - return doneChan, nil + return doneChan, callbackID, nil } -func domainEventDeregister(virConn, doneChan chan bool) { +func domainEventDeregister(virConn *libvirt.VirConnection, callbackID int, doneChan chan bool) { <-doneChan - ret := virConn.DomainEventDeregister(callbackId) + ret := virConn.DomainEventDeregister(callbackID) if ret != 0 { fmt.Printf("got %d on DomainEventDeregister instead of 0\n", ret) } } -func newEventCallback(eventChan <-chan Event, +func newEventCallback(eventChan chan Event, doneChan chan bool) libvirt.DomainEventCallback { return func(c *libvirt.VirConnection, d *libvirt.VirDomain, eventDetails interface{}, f func()) int { - //TODO: Probably need to check + //TODO: Probably need to check // if eventChan is closed so we stop processing // We only support lifecycleEvents for now @@ -183,6 +183,8 @@ func newEventCallback(eventChan <-chan Event, close(eventChan) doneChan <- true } + + return 0 } } @@ -207,22 +209,22 @@ func eventRunDefaultImplLoop(doneChan chan bool) { } } -func constructEvent(eventDetails libvirt.DomainLifecycleEvent) *Event { +func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { // Technically, the timestamp is not accurate - // as events might occur before + // as events might occur before // the next libvirt.EventRunDefaultImpl() execution // at which point the notification is received. now := time.Now() - var timestamp struct { - Seconds int64 - Microseconds int64 - } = { - now.Second(), - now.Nanosecond(), - } - return &Event{ - Event: eventsTable[eventDetails.Event], - Data: eventDetails, + type TimeStamp struct { + Seconds int64 `json:"seconds"` + Microseconds int64 `json:"microseconds"` + } + timestamp := TimeStamp{ + int64(now.Second()), + int64(now.Nanosecond()), + } + return Event{ + Event: eventsTable[eventDetails.Event], Timestamp: timestamp, } } From f92839f2759b70cf7b27165bc5e1a00e2788ca5c Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sun, 16 Oct 2016 15:24:35 -0400 Subject: [PATCH 11/30] qmp/libvirtgo: remove doneChan. change log messages. --- qmp/libvirtgo_linux.go | 45 +++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 7e39ebb..97bb642 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -83,7 +83,7 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { return nil, fmt.Errorf("Events() need a established connection to proceed.") } - eventsChan := make(chan Event) + eventsChan := make(chan Event, 1) initialEventSetup() @@ -92,13 +92,13 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { return nil, err } - // doneChan will allow us to stop receiving events from libvirt - doneChan, callbackID, err := domainEventRegistration(mon.virConn, &domain, eventsChan) + callbackID, err := domainEventRegister(mon.virConn, &domain, eventsChan) if err != nil { return nil, err } - go eventRunDefaultImplLoop(doneChan) + doneChan := make(chan bool) + go pollEventsLoop(doneChan) go domainEventDeregister(mon.virConn, callbackID, doneChan) @@ -116,7 +116,7 @@ func onceEventRegisterDefaultImpl() { eventRegisterID := libvirt.EventRegisterDefaultImpl() if eventRegisterID == -1 { fmt.Printf( - "got %d on libvirt.EventRegisterDefaultImpl instead of 0\n", + "EventRegisterDefaultImpl returned an unexpected value %d\n", eventRegisterID) //TODO: panic or what??? } @@ -132,12 +132,10 @@ func populateEventTable() { } } -func domainEventRegistration(virConn *libvirt.VirConnection, domain *libvirt.VirDomain, - eventsChan chan Event) (chan bool, int, error) { - // doneChan will allow us to stop receiving events from libvirt - doneChan := make(chan bool) +func domainEventRegister(virConn *libvirt.VirConnection, domain *libvirt.VirDomain, + eventsChan chan Event) (int, error) { callback := - libvirt.DomainEventCallback(newEventCallback(eventsChan, doneChan)) + libvirt.DomainEventCallback(newEventCallback(eventsChan)) //TODO: add ability to register for more event types callbackID := virConn.DomainEventRegister( @@ -149,55 +147,48 @@ func domainEventRegistration(virConn *libvirt.VirConnection, domain *libvirt.Vir }, ) if callbackID == -1 { - close(doneChan) return nil, -1, fmt.Errorf("Domain event registration failed!") } - return doneChan, callbackID, nil + return callbackID, nil } func domainEventDeregister(virConn *libvirt.VirConnection, callbackID int, doneChan chan bool) { <-doneChan ret := virConn.DomainEventDeregister(callbackID) if ret != 0 { - fmt.Printf("got %d on DomainEventDeregister instead of 0\n", ret) + fmt.Printf("DomainEventDeregister returned an unexpected value: %d\n", ret) } } -func newEventCallback(eventChan chan Event, - doneChan chan bool) libvirt.DomainEventCallback { +func newEventCallback(eventChan chan Event) libvirt.DomainEventCallback { return func(c *libvirt.VirConnection, d *libvirt.VirDomain, eventDetails interface{}, f func()) int { - //TODO: Probably need to check - // if eventChan is closed so we stop processing - // We only support lifecycleEvents for now if lifecycleEvent, ok := eventDetails.(libvirt.DomainLifecycleEvent); ok { eventChan <- constructEvent(lifecycleEvent) f() - } else { - // Closing the channel indicates error - close(eventChan) - doneChan <- true } + // according to the libvirt-do + // the return code is ignored return 0 } } -// eventRunDefaultImplLoop keeps polling libvirt for new events -func eventRunDefaultImplLoop(doneChan chan bool) { +// pollEventsLoop keeps polling libvirt for new domain events +func pollEventsLoop(doneChan chan bool) { //TODO: Maybe interval should come // from an ENV variable - tick := time.Tick(1 * time.Second) + checkInterval := time.Tick(1 * time.Second) for { select { - case <-doneChan: // stop listening for events + case <-doneChan: // stop polling for events return - case <-tick: + case <-checkInterval: go func() { ret := libvirt.EventRunDefaultImpl() if ret == -1 { From 85c6cebbb8496eb0ecc16cd823f9e69e7732aff0 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sun, 16 Oct 2016 15:26:34 -0400 Subject: [PATCH 12/30] qmp/libvirtgo: fixed return val issue. --- qmp/libvirtgo_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 97bb642..d298ccd 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -147,7 +147,7 @@ func domainEventRegister(virConn *libvirt.VirConnection, domain *libvirt.VirDoma }, ) if callbackID == -1 { - return nil, -1, fmt.Errorf("Domain event registration failed!") + return -1, fmt.Errorf("Domain event registration failed!") } return callbackID, nil From d5503912dc93e98033b814927a71e0b4f51c2684 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sun, 16 Oct 2016 20:19:07 -0400 Subject: [PATCH 13/30] qmp/libvirtgo: fix bugs. add more comments. --- qmp/libvirtgo_linux.go | 89 ++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index d298ccd..4c22658 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -27,16 +27,43 @@ type libvirtGoMonitorLinux struct { domain string uri string virConn *libvirt.VirConnection + once sync.Once } -var once sync.Once +var eventsTable = map[int]string{ + libvirt.VIR_DOMAIN_EVENT_STARTED: "VIR_DOMAIN_EVENT_STARTED", + libvirt.VIR_DOMAIN_EVENT_SUSPENDED: "VIR_DOMAIN_EVENT_SUSPENDED", + libvirt.VIR_DOMAIN_EVENT_RESUMED: "VIR_DOMAIN_EVENT_RESUMED", + libvirt.VIR_DOMAIN_EVENT_STOPPED: "VIR_DOMAIN_EVENT_STOPPED", + libvirt.VIR_DOMAIN_EVENT_SHUTDOWN: "VIR_DOMAIN_EVENT_SHUTDOWN", +} -var eventsTable map[int]string +// NewLibvirtGoMonitor configures a connection to the provided hypervisor +// and domain. +// An error is returned if the provided libvirt connection URI is invalid. +// +// Hypervisor URIs may be local or remote, e.g., +// qemu:///system +// qemu+ssh://libvirt@example.com/system +func NewLibvirtGoMonitor(uri, domain string) Monitor { + return &libvirtGoMonitorLinux{ + uri: uri, + domain: domain, + } +} // Connect sets up QEMU QMP connection via libvirt using // the libvirt-go package. // An error is returned if the libvirtd daemon is unreachable. func (mon *libvirtGoMonitorLinux) Connect() error { + + // As per the libvirt core library: + // For proper event handling, it is important + // that the event implementation is registered + // before a connection to the Hypervisor is opened. + // We only do this once the first a Connect is called. + mon.once.Do(eventRegisterDefaultImpl) + virConn, err := libvirt.NewVirConnection(mon.uri) if err == nil { mon.virConn = &virConn @@ -85,8 +112,6 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { eventsChan := make(chan Event, 1) - initialEventSetup() - domain, err := mon.virConn.LookupDomainByName(mon.domain) if err != nil { return nil, err @@ -97,7 +122,12 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { return nil, err } + //TODO: need a way to trigger doneChan. + // Maybe we need to return another type of Object + // instead of the Event channel so the caller has the + // ability to stop processing events. doneChan := make(chan bool) + go pollEventsLoop(doneChan) go domainEventDeregister(mon.virConn, callbackID, doneChan) @@ -105,14 +135,8 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { return eventsChan, nil } -// Initial setup to receive events. -// It's only done once. -func initialEventSetup() { - once.Do(onceEventRegisterDefaultImpl) - once.Do(populateEventTable) -} - -func onceEventRegisterDefaultImpl() { +// eventRegisterDefaultImpl registers a default event implementation. +func eventRegisterDefaultImpl() { eventRegisterID := libvirt.EventRegisterDefaultImpl() if eventRegisterID == -1 { fmt.Printf( @@ -122,16 +146,8 @@ func onceEventRegisterDefaultImpl() { } } -func populateEventTable() { - eventsTable = map[int]string{ - libvirt.VIR_DOMAIN_EVENT_STARTED: "VIR_DOMAIN_EVENT_STARTED", - libvirt.VIR_DOMAIN_EVENT_SUSPENDED: "VIR_DOMAIN_EVENT_SUSPENDED", - libvirt.VIR_DOMAIN_EVENT_RESUMED: "VIR_DOMAIN_EVENT_RESUMED", - libvirt.VIR_DOMAIN_EVENT_STOPPED: "VIR_DOMAIN_EVENT_STOPPED", - libvirt.VIR_DOMAIN_EVENT_SHUTDOWN: "VIR_DOMAIN_EVENT_SHUTDOWN", - } -} - +// domainEventRegister register with libvirt to receive events for the +// specified domain. func domainEventRegister(virConn *libvirt.VirConnection, domain *libvirt.VirDomain, eventsChan chan Event) (int, error) { callback := @@ -143,7 +159,8 @@ func domainEventRegister(virConn *libvirt.VirConnection, domain *libvirt.VirDoma libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, &callback, func() { - //fmt.Printf("VIR_DOMAIN_EVENT_ID_LIFECYCLE called\n") + // empty on purpose until it's decided + // what to do here. }, ) if callbackID == -1 { @@ -153,6 +170,8 @@ func domainEventRegister(virConn *libvirt.VirConnection, domain *libvirt.VirDoma return callbackID, nil } +// domainEventDeregister stops the registration with libvirt from receiving +// events for the specified domain. func domainEventDeregister(virConn *libvirt.VirConnection, callbackID int, doneChan chan bool) { <-doneChan ret := virConn.DomainEventDeregister(callbackID) @@ -161,6 +180,8 @@ func domainEventDeregister(virConn *libvirt.VirConnection, callbackID int, doneC } } +// newEventCallback convenient function to provide access +// to the eventChan to the returned closure/callback. func newEventCallback(eventChan chan Event) libvirt.DomainEventCallback { return func(c *libvirt.VirConnection, d *libvirt.VirDomain, @@ -200,6 +221,8 @@ func pollEventsLoop(doneChan chan bool) { } } +// constructEvent helper function to map DomainLifecycleEvent +// into Event. func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { // Technically, the timestamp is not accurate // as events might occur before @@ -214,22 +237,12 @@ func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { int64(now.Second()), int64(now.Nanosecond()), } + eventDescription := eventsTable[eventDetails.Event] + data := make(map[string]interface{}) + data[eventDescription] = eventDetails return Event{ - Event: eventsTable[eventDetails.Event], + Event: eventDescription, + Data: data, Timestamp: timestamp, } } - -// NewLibvirtGoMonitor configures a connection to the provided hypervisor -// and domain. -// An error is returned if the provided libvirt connection URI is invalid. -// -// Hypervisor URIs may be local or remote, e.g., -// qemu:///system -// qemu+ssh://libvirt@example.com/system -func NewLibvirtGoMonitor(uri, domain string) Monitor { - return &libvirtGoMonitorLinux{ - uri: uri, - domain: domain, - } -} From bea75d95cda45d99acfeccb53da24def0f5e3588 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sun, 16 Oct 2016 20:19:49 -0400 Subject: [PATCH 14/30] qmp/libvirtgo: change libvirtgo_events example. --- examples/libvirtgo_events/main.go | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 examples/libvirtgo_events/main.go diff --git a/examples/libvirtgo_events/main.go b/examples/libvirtgo_events/main.go new file mode 100644 index 0000000..5585f8f --- /dev/null +++ b/examples/libvirtgo_events/main.go @@ -0,0 +1,55 @@ +// Copyright 2016 The go-qemu Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "flag" + "fmt" + "log" + + "github.com/digitalocean/go-qemu/qmp" +) + +var ( + uri = flag.String("uri", "qemu:///system", `URI to connect to the libvirtd host.`) + domainName = flag.String("domainName", "mydomain", "This is the domain to run commands against.") +) + +func main() { + flag.Parse() + + libvirtGoMonitor := qmp.NewLibvirtGoMonitor(*uri, *domainName) + + err := libvirtGoMonitor.Connect() + if err != nil { + log.Fatalf("Unable to connect: %v\n", err) + } + + eventsChans, err := libvirtGoMonitor.Events() + if err != nil { + log.Fatalf("Unable to register for events: %v\n", err) + } + + fmt.Println("Waiting for Domain events...") + go func() { + for event := range eventsChans { + fmt.Printf("Event: %#v\n", event) + } + }() + + fmt.Println("Press the Enter key to stop") + fmt.Scanln() + libvirtGoMonitor.Disconnect() +} From b35ebff497f77e093bc8e7730f320a6cf3870d98 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sun, 16 Oct 2016 21:03:09 -0400 Subject: [PATCH 15/30] qmp/libvirtgo: remove fmt package. add events interval env variable --- qmp/libvirtgo_linux.go | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 4c22658..ee62699 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -15,7 +15,9 @@ package qmp import ( - "fmt" + "errors" + "log" + "os" "sync" "time" @@ -107,7 +109,7 @@ func (mon *libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { if mon.virConn == nil { - return nil, fmt.Errorf("Events() need a established connection to proceed.") + return nil, errors.New("Events() need a established connection to proceed.") } eventsChan := make(chan Event, 1) @@ -139,7 +141,7 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { func eventRegisterDefaultImpl() { eventRegisterID := libvirt.EventRegisterDefaultImpl() if eventRegisterID == -1 { - fmt.Printf( + log.Printf( "EventRegisterDefaultImpl returned an unexpected value %d\n", eventRegisterID) //TODO: panic or what??? @@ -164,7 +166,7 @@ func domainEventRegister(virConn *libvirt.VirConnection, domain *libvirt.VirDoma }, ) if callbackID == -1 { - return -1, fmt.Errorf("Domain event registration failed!") + return -1, errors.New("Domain event registration failed!") } return callbackID, nil @@ -176,7 +178,7 @@ func domainEventDeregister(virConn *libvirt.VirConnection, callbackID int, doneC <-doneChan ret := virConn.DomainEventDeregister(callbackID) if ret != 0 { - fmt.Printf("DomainEventDeregister returned an unexpected value: %d\n", ret) + log.Printf("DomainEventDeregister returned an unexpected value: %d\n", ret) } } @@ -204,7 +206,7 @@ func newEventCallback(eventChan chan Event) libvirt.DomainEventCallback { func pollEventsLoop(doneChan chan bool) { //TODO: Maybe interval should come // from an ENV variable - checkInterval := time.Tick(1 * time.Second) + checkInterval := time.Tick(getPollInterval()) for { select { case <-doneChan: // stop polling for events @@ -221,6 +223,24 @@ func pollEventsLoop(doneChan chan bool) { } } +// getPollInterval retreives the events poll interval from the +// LIBVIRTGO_EVENTS_INTERVAL environment variable. Defaults to 1s. +func getPollInterval() time.Duration { + intervalStr := os.Getenv("LIBVIRTGO_EVENTS_INTERVAL") + interval, _ := time.ParseDuration("1s") + if intervalStr != "" { + desiredInterval, err := time.ParseDuration(intervalStr) + if err != nil { + log.Printf( + "Unable value specified in 'LIBVIRTGO_EVENTS_INTERVAL': %v\n", + err) + return interval + } + interval = desiredInterval + } + return interval +} + // constructEvent helper function to map DomainLifecycleEvent // into Event. func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { From 0a7d859d21df5a25b8d4e4aacea2e01794c29b30 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sun, 16 Oct 2016 21:57:31 -0400 Subject: [PATCH 16/30] qmp/libvirtgo: construct event changes. --- qmp/libvirtgo_linux.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index ee62699..43bd6b2 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -244,10 +244,9 @@ func getPollInterval() time.Duration { // constructEvent helper function to map DomainLifecycleEvent // into Event. func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { - // Technically, the timestamp is not accurate - // as events might occur before - // the next libvirt.EventRunDefaultImpl() execution - // at which point the notification is received. + // This timestamp represents the moment in time when + // the event was received on this end and not when it + // actually occurred. now := time.Now() type TimeStamp struct { Seconds int64 `json:"seconds"` @@ -255,13 +254,12 @@ func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { } timestamp := TimeStamp{ int64(now.Second()), - int64(now.Nanosecond()), + int64(now.Second() / time.Microsecond), } - eventDescription := eventsTable[eventDetails.Event] data := make(map[string]interface{}) - data[eventDescription] = eventDetails + data["details"] = eventDetails return Event{ - Event: eventDescription, + Event: eventDetails.String(), Data: data, Timestamp: timestamp, } From 50f507fef99e7d9ef2dc4a39d9f4bb3f1ba356b1 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Sun, 16 Oct 2016 22:01:11 -0400 Subject: [PATCH 17/30] qmp/libvirtgo: fix bug --- qmp/libvirtgo_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 43bd6b2..b95e3d3 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -254,7 +254,7 @@ func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { } timestamp := TimeStamp{ int64(now.Second()), - int64(now.Second() / time.Microsecond), + int64(now.Second() / int(time.Microsecond)), } data := make(map[string]interface{}) data["details"] = eventDetails From a4e38259e906cda68f420a4b23f9e11aad8c9949 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Mon, 17 Oct 2016 20:52:50 -0400 Subject: [PATCH 18/30] qmp/libvirtgo: prepare for tests. --- qmp/libvirtgo_linux.go | 173 ++++++++++++++++++++++++++--------------- 1 file changed, 112 insertions(+), 61 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index b95e3d3..e2a49f5 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -26,18 +26,13 @@ import ( type libvirtGoMonitorLinux struct { LibvirtGoMonitor - domain string - uri string - virConn *libvirt.VirConnection - once sync.Once -} - -var eventsTable = map[int]string{ - libvirt.VIR_DOMAIN_EVENT_STARTED: "VIR_DOMAIN_EVENT_STARTED", - libvirt.VIR_DOMAIN_EVENT_SUSPENDED: "VIR_DOMAIN_EVENT_SUSPENDED", - libvirt.VIR_DOMAIN_EVENT_RESUMED: "VIR_DOMAIN_EVENT_RESUMED", - libvirt.VIR_DOMAIN_EVENT_STOPPED: "VIR_DOMAIN_EVENT_STOPPED", - libvirt.VIR_DOMAIN_EVENT_SHUTDOWN: "VIR_DOMAIN_EVENT_SHUTDOWN", + domainName string + domain *libvirt.VirDomain + uri string + virConn *libvirt.VirConnection + once sync.Once + doneChan chan bool + eventsChan chan Event } // NewLibvirtGoMonitor configures a connection to the provided hypervisor @@ -66,35 +61,64 @@ func (mon *libvirtGoMonitorLinux) Connect() error { // We only do this once the first a Connect is called. mon.once.Do(eventRegisterDefaultImpl) - virConn, err := libvirt.NewVirConnection(mon.uri) - if err == nil { - mon.virConn = &virConn + // Already connected? + if mon.virConn != nil { + return nil + } + + virConn, err := NewVirConnectionInternal(mon.uri) + if err != nil { + return err } + + mon.domain, err := &lookupDomainByName(virConn, mon.domainName) + if err != nil { + CloseConnectionInternal(virConn) + return err + } + + mon.virConn = &virConn + mon.eventsChan := make(chan Event) + mon.doneChan := make(chan bool) + return err } +var NewVirConnectionInternal = func(uri string) (*libvirt.VirConnection, error) { + return libvirt.NewVirConnection(uri) +} + // Disconnect tears down open QMP socket connections. func (mon *libvirtGoMonitorLinux) Disconnect() error { var err error + if mon.domain != nil { + mon.domain.Free() + mon.domain = nil + } + if mon.virConn != nil { - _, err = mon.virConn.CloseConnection() + _, err = CloseConnectionInternal(mon.virConn) mon.virConn = nil + close(mon.doneChan) // stop listenning to events + close(mon.eventsChan) // stop sending events to clients } + return err } +var CloseConnectionInternal = func(virConn *libvirt.VirConnection) (int, error) { + return virConn.CloseConnection() +} + // Run executes the given QAPI command against a domain's QEMU instance. // For a list of available QAPI commands, see: // http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD func (mon *libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { - domain, err := mon.virConn.LookupDomainByName(mon.domain) - if err != nil { - return nil, err + if mon.virConn == nil { + return nil, errors.New("Run() need a established connection to proceed.") } - result, err := domain.QemuMonitorCommand( - libvirt.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT, - string(cmd)) + result, err := QemuMonitorCommandInternal(mon.domain,string(cmd)) if err != nil { return nil, err } @@ -102,6 +126,13 @@ func (mon *libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { return []byte(result), nil } +var QemuMonitorCommandInternal = func( + domain *libvirt.VirDomain, cmd string) (string, error) { + return domain.QemuMonitorCommand( + libvirt.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT, + cmd) +} + // Events streams QEMU QMP Events. // If a problem is encountered setting up the event monitor connection // an error will be returned. Errors encountered during streaming will @@ -111,35 +142,26 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { if mon.virConn == nil { return nil, errors.New("Events() need a established connection to proceed.") } - - eventsChan := make(chan Event, 1) - - domain, err := mon.virConn.LookupDomainByName(mon.domain) - if err != nil { - return nil, err - } - - callbackID, err := domainEventRegister(mon.virConn, &domain, eventsChan) + + callbackID, err := domainEventRegister(mon) if err != nil { return nil, err } - //TODO: need a way to trigger doneChan. - // Maybe we need to return another type of Object - // instead of the Event channel so the caller has the - // ability to stop processing events. - doneChan := make(chan bool) + go pollEventsLoop(mon.doneChan) - go pollEventsLoop(doneChan) + go domainEventDeregister(mon.virConn, callbackID) - go domainEventDeregister(mon.virConn, callbackID, doneChan) + return mon.eventsChan, nil +} - return eventsChan, nil +var lookupDomainByName = func(virConn *libvirt.VirConnection, domainName string) (VirDomain, error) { + return virConn.LookupDomainByName(mon.domain) } // eventRegisterDefaultImpl registers a default event implementation. func eventRegisterDefaultImpl() { - eventRegisterID := libvirt.EventRegisterDefaultImpl() + eventRegisterID := eventRegisterDefaultImplInternal() if eventRegisterID == -1 { log.Printf( "EventRegisterDefaultImpl returned an unexpected value %d\n", @@ -148,17 +170,19 @@ func eventRegisterDefaultImpl() { } } +var eventRegisterDefaultImplInternal = func() int { + return libvirt.EventRegisterDefaultImpl() +} + // domainEventRegister register with libvirt to receive events for the // specified domain. -func domainEventRegister(virConn *libvirt.VirConnection, domain *libvirt.VirDomain, - eventsChan chan Event) (int, error) { +func domainEventRegister(mon *libvirtGoMonitorLinux) (int, error) { callback := - libvirt.DomainEventCallback(newEventCallback(eventsChan)) + libvirt.DomainEventCallback(newEventCallback(mon)) //TODO: add ability to register for more event types - callbackID := virConn.DomainEventRegister( - *domain, - libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, + callbackID := domainEventRegisterInternal( + mon, &callback, func() { // empty on purpose until it's decided @@ -172,27 +196,54 @@ func domainEventRegister(virConn *libvirt.VirConnection, domain *libvirt.VirDoma return callbackID, nil } +var domainEventRegisterInternal = func( + mon *libvirtGoMonitorLinux, + callback *libvirt.DomainEventCallback, + fn func(){}) { + return mon.virConn.DomainEventRegister( + mon.domain, + libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, + callback, + fn + ) +} + // domainEventDeregister stops the registration with libvirt from receiving // events for the specified domain. -func domainEventDeregister(virConn *libvirt.VirConnection, callbackID int, doneChan chan bool) { - <-doneChan - ret := virConn.DomainEventDeregister(callbackID) +func domainEventDeregister( + mon *libvirtGoMonitorLinux, + callbackID int) { + <-mon.doneChan + ret := domainEventDeregisterInternal(mon.virConn, callbackID) if ret != 0 { - log.Printf("DomainEventDeregister returned an unexpected value: %d\n", ret) + log.Printf("DomainEventDeregister returned an unexpected value: %d\n", + ret) } } +var domainEventDeregisterInternal = func(virConn *libvirt.VirConnection, + callbackID int) { + return virConn.DomainEventDeregister(callbackID) +} + // newEventCallback convenient function to provide access // to the eventChan to the returned closure/callback. -func newEventCallback(eventChan chan Event) libvirt.DomainEventCallback { +func newEventCallback(mon *libvirtGoMonitorLinux) libvirt.DomainEventCallback { return func(c *libvirt.VirConnection, d *libvirt.VirDomain, eventDetails interface{}, f func()) int { + // if monitor is not connected, + // we should not continue processing messages + // as the mon.eventsChan will be closed. + if mon.virConn == nil { + return + } + // We only support lifecycleEvents for now if lifecycleEvent, ok := eventDetails.(libvirt.DomainLifecycleEvent); ok { - eventChan <- constructEvent(lifecycleEvent) + mon.eventChan <- constructEvent(lifecycleEvent) f() } @@ -204,25 +255,25 @@ func newEventCallback(eventChan chan Event) libvirt.DomainEventCallback { // pollEventsLoop keeps polling libvirt for new domain events func pollEventsLoop(doneChan chan bool) { - //TODO: Maybe interval should come - // from an ENV variable checkInterval := time.Tick(getPollInterval()) for { select { case <-doneChan: // stop polling for events return case <-checkInterval: - go func() { - ret := libvirt.EventRunDefaultImpl() - if ret == -1 { - doneChan <- true - return - } - }() + ret := eventRunDefaultImplInternal() + if ret == -1 { + doneChan <- true + return + } } } } +var eventRunDefaultImplInternal = func() int { + return libvirt.EventRunDefaultImpl() +} + // getPollInterval retreives the events poll interval from the // LIBVIRTGO_EVENTS_INTERVAL environment variable. Defaults to 1s. func getPollInterval() time.Duration { From 7acfe5567154d64e7c3db069e0bf3ec71fc0b4e8 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Mon, 17 Oct 2016 21:09:40 -0400 Subject: [PATCH 19/30] qmp/libvirtgo: fix typos and bugs. --- qmp/libvirtgo_linux.go | 82 ++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index e2a49f5..fb0d938 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -26,13 +26,13 @@ import ( type libvirtGoMonitorLinux struct { LibvirtGoMonitor - domainName string - domain *libvirt.VirDomain - uri string - virConn *libvirt.VirConnection - once sync.Once - doneChan chan bool - eventsChan chan Event + domainName string + domain *libvirt.VirDomain + uri string + virConn *libvirt.VirConnection + once sync.Once + doneChan chan bool + eventsChan chan Event } // NewLibvirtGoMonitor configures a connection to the provided hypervisor @@ -44,8 +44,8 @@ type libvirtGoMonitorLinux struct { // qemu+ssh://libvirt@example.com/system func NewLibvirtGoMonitor(uri, domain string) Monitor { return &libvirtGoMonitorLinux{ - uri: uri, - domain: domain, + uri: uri, + domainName: domain, } } @@ -61,32 +61,34 @@ func (mon *libvirtGoMonitorLinux) Connect() error { // We only do this once the first a Connect is called. mon.once.Do(eventRegisterDefaultImpl) - // Already connected? + // Already connected? if mon.virConn != nil { return nil } - virConn, err := NewVirConnectionInternal(mon.uri) + virConn, err := newVirConnectionInternal(mon.uri) if err != nil { return err } - mon.domain, err := &lookupDomainByName(virConn, mon.domainName) + domain, err := lookupDomainByName(&virConn, mon.domainName) if err != nil { - CloseConnectionInternal(virConn) + closeConnectionInternal(&virConn) return err } - mon.virConn = &virConn - mon.eventsChan := make(chan Event) - mon.doneChan := make(chan bool) - + mon.domain = &domain + mon.virConn = &virConn + mon.eventsChan = make(chan Event) + mon.doneChan = make(chan bool) + return err } -var NewVirConnectionInternal = func(uri string) (*libvirt.VirConnection, error) { +var newVirConnectionInternal = func( + uri string) (libvirt.VirConnection, error) { return libvirt.NewVirConnection(uri) -} +} // Disconnect tears down open QMP socket connections. func (mon *libvirtGoMonitorLinux) Disconnect() error { @@ -97,16 +99,17 @@ func (mon *libvirtGoMonitorLinux) Disconnect() error { } if mon.virConn != nil { - _, err = CloseConnectionInternal(mon.virConn) + _, err = closeConnectionInternal(mon.virConn) mon.virConn = nil close(mon.doneChan) // stop listenning to events close(mon.eventsChan) // stop sending events to clients } - + return err } -var CloseConnectionInternal = func(virConn *libvirt.VirConnection) (int, error) { +var closeConnectionInternal = func( + virConn *libvirt.VirConnection) (int, error) { return virConn.CloseConnection() } @@ -118,7 +121,7 @@ func (mon *libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { return nil, errors.New("Run() need a established connection to proceed.") } - result, err := QemuMonitorCommandInternal(mon.domain,string(cmd)) + result, err := qemuMonitorCommandInternal(mon.domain, string(cmd)) if err != nil { return nil, err } @@ -126,7 +129,7 @@ func (mon *libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { return []byte(result), nil } -var QemuMonitorCommandInternal = func( +var qemuMonitorCommandInternal = func( domain *libvirt.VirDomain, cmd string) (string, error) { return domain.QemuMonitorCommand( libvirt.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT, @@ -140,9 +143,10 @@ var QemuMonitorCommandInternal = func( func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { if mon.virConn == nil { - return nil, errors.New("Events() need a established connection to proceed.") + return nil, + errors.New("Events() need a established connection to proceed.") } - + callbackID, err := domainEventRegister(mon) if err != nil { return nil, err @@ -150,13 +154,15 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { go pollEventsLoop(mon.doneChan) - go domainEventDeregister(mon.virConn, callbackID) + go domainEventDeregister(mon, callbackID) return mon.eventsChan, nil } -var lookupDomainByName = func(virConn *libvirt.VirConnection, domainName string) (VirDomain, error) { - return virConn.LookupDomainByName(mon.domain) +var lookupDomainByName = func( + virConn *libvirt.VirConnection, + domainName string) (libvirt.VirDomain, error) { + return virConn.LookupDomainByName(domainName) } // eventRegisterDefaultImpl registers a default event implementation. @@ -198,13 +204,13 @@ func domainEventRegister(mon *libvirtGoMonitorLinux) (int, error) { var domainEventRegisterInternal = func( mon *libvirtGoMonitorLinux, - callback *libvirt.DomainEventCallback, - fn func(){}) { + callback *libvirt.DomainEventCallback, + fn func()) int { return mon.virConn.DomainEventRegister( - mon.domain, + *mon.domain, libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, callback, - fn + fn, ) } @@ -222,7 +228,7 @@ func domainEventDeregister( } var domainEventDeregisterInternal = func(virConn *libvirt.VirConnection, - callbackID int) { + callbackID int) int { return virConn.DomainEventDeregister(callbackID) } @@ -233,17 +239,17 @@ func newEventCallback(mon *libvirtGoMonitorLinux) libvirt.DomainEventCallback { d *libvirt.VirDomain, eventDetails interface{}, f func()) int { - // if monitor is not connected, + // if monitor is not connected, // we should not continue processing messages - // as the mon.eventsChan will be closed. + // as the mon.eventsChan will be closed. if mon.virConn == nil { - return + return 0 } // We only support lifecycleEvents for now if lifecycleEvent, ok := eventDetails.(libvirt.DomainLifecycleEvent); ok { - mon.eventChan <- constructEvent(lifecycleEvent) + mon.eventsChan <- constructEvent(lifecycleEvent) f() } From 417cec642f03a0389cc75f4d1fc72c76c45c90cf Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Tue, 18 Oct 2016 14:20:15 -0400 Subject: [PATCH 20/30] qmp/libvirtgo: start unit tests --- qmp/libvirtgo_linux_test.go | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 qmp/libvirtgo_linux_test.go diff --git a/qmp/libvirtgo_linux_test.go b/qmp/libvirtgo_linux_test.go new file mode 100644 index 0000000..fae1850 --- /dev/null +++ b/qmp/libvirtgo_linux_test.go @@ -0,0 +1,56 @@ +// Copyright 2016 The go-qemu Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package qmp + +import ( + "testing" + + libvirt "github.com/rgbkrk/libvirt-go" +) + +func TestConnetOK(t *testing.T) { + + eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal + eventRegisterDefaultImplInternal = func() int { + return 0 + } + defer func() { + eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg + }() + + newVirConnectionInternalOrg := newVirConnectionInternal + newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { + return libvirt.VirConnection{}, nil + } + defer func() { + newVirConnectionInternal = newVirConnectionInternalOrg + }() + + lookupDomainByNameOrg := lookupDomainByName + lookupDomainByName = func(virConn *libvirt.VirConnection, + domainName string) (libvirt.VirDomain, error) { + return libvirt.VirDomain{}, nil + } + defer func() { + lookupDomainByName = lookupDomainByNameOrg + }() + + libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + err := libvirtGoMonitor.Connect() + if err != nil { + t.Errorf("Unable to connect: %v\n", err) + } + +} From daac6ad520120731346a46b04bce860fdbbc7cd6 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Tue, 18 Oct 2016 15:38:49 -0400 Subject: [PATCH 21/30] qmp/libvirtgo: add more test --- qmp/libvirtgo_linux_test.go | 184 +++++++++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 2 deletions(-) diff --git a/qmp/libvirtgo_linux_test.go b/qmp/libvirtgo_linux_test.go index fae1850..7ba0dd8 100644 --- a/qmp/libvirtgo_linux_test.go +++ b/qmp/libvirtgo_linux_test.go @@ -15,12 +15,13 @@ package qmp import ( + "errors" "testing" libvirt "github.com/rgbkrk/libvirt-go" ) -func TestConnetOK(t *testing.T) { +func TestLibvirtGoConnetOK(t *testing.T) { eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal eventRegisterDefaultImplInternal = func() int { @@ -50,7 +51,186 @@ func TestConnetOK(t *testing.T) { libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") err := libvirtGoMonitor.Connect() if err != nil { - t.Errorf("Unable to connect: %v\n", err) + t.Errorf("unexpected error: %v\n", err) } } + +func TestLibvirtGoConnectionFailed(t *testing.T) { + + eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal + eventRegisterDefaultImplInternal = func() int { + return 0 + } + defer func() { + eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg + }() + + newVirConnectionInternalOrg := newVirConnectionInternal + newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { + return libvirt.VirConnection{}, errors.New("server down") + } + defer func() { + newVirConnectionInternal = newVirConnectionInternalOrg + }() + + libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + err := libvirtGoMonitor.Connect() + if err == nil { + t.Fatalf("connection error expected") + } + +} + +func TestLibvirtGoDomainLookupFailed(t *testing.T) { + + eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal + eventRegisterDefaultImplInternal = func() int { + return 0 + } + defer func() { + eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg + }() + + newVirConnectionInternalOrg := newVirConnectionInternal + newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { + return libvirt.VirConnection{}, nil + } + defer func() { + newVirConnectionInternal = newVirConnectionInternalOrg + }() + + lookupDomainByNameOrg := lookupDomainByName + lookupDomainByName = func(virConn *libvirt.VirConnection, + domainName string) (libvirt.VirDomain, error) { + return libvirt.VirDomain{}, errors.New("Unable to find domain") + } + defer func() { + lookupDomainByName = lookupDomainByNameOrg + }() + + libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + err := libvirtGoMonitor.Connect() + if err == nil { + t.Errorf("unexpected error: %v\n", err) + } + +} + +func TestLibvirtGoRunOK(t *testing.T) { + + eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal + eventRegisterDefaultImplInternal = func() int { + return 0 + } + defer func() { + eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg + }() + + newVirConnectionInternalOrg := newVirConnectionInternal + newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { + return libvirt.VirConnection{}, nil + } + defer func() { + newVirConnectionInternal = newVirConnectionInternalOrg + }() + + lookupDomainByNameOrg := lookupDomainByName + lookupDomainByName = func(virConn *libvirt.VirConnection, + domainName string) (libvirt.VirDomain, error) { + return libvirt.VirDomain{}, nil + } + defer func() { + lookupDomainByName = lookupDomainByNameOrg + }() + + libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + err := libvirtGoMonitor.Connect() + if err != nil { + t.Errorf("unexpected error: %v\n", err) + } + + expectedOutput := `{"current":true,"CPU":0,"qom_path":"/machine/unattached/device[0]","pc":33504,"halted":false,"thread_id":25213}],"id":"libvirt-32"}` + qemuMonitorCommandInternalOrg := qemuMonitorCommandInternal + qemuMonitorCommandInternal = func( + domain *libvirt.VirDomain, cmd string) (string, error) { + return expectedOutput, nil + } + defer func() { + qemuMonitorCommandInternal = qemuMonitorCommandInternalOrg + }() + + result, err := libvirtGoMonitor.Run([]byte("{\"execute\" : \"query-cpus\"}")) + if err != nil { + t.Fatalf("unexpected error: %v\n", err) + } + + if expectedOutput != string(result) { + t.Fatalf("expected %s and got %s\n", expectedOutput, string(result)) + } +} + +func TestLibvirtGoRunNoConnection(t *testing.T) { + libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + _, err := libvirtGoMonitor.Run([]byte("{\"execute\" : \"query-cpus\"}")) + if err == nil { + t.Fatalf("no connection error expected") + } +} + +func TestLibvirtGoEventsNoConnection(t *testing.T) { + libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + _, err := libvirtGoMonitor.Events() + if err == nil { + t.Fatalf("no connection error expected") + } +} + +func TestLibvirtGoEventsDomainEventRegisterFailed(t *testing.T) { + eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal + eventRegisterDefaultImplInternal = func() int { + return 0 + } + defer func() { + eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg + }() + + newVirConnectionInternalOrg := newVirConnectionInternal + newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { + return libvirt.VirConnection{}, nil + } + defer func() { + newVirConnectionInternal = newVirConnectionInternalOrg + }() + + lookupDomainByNameOrg := lookupDomainByName + lookupDomainByName = func(virConn *libvirt.VirConnection, + domainName string) (libvirt.VirDomain, error) { + return libvirt.VirDomain{}, nil + } + defer func() { + lookupDomainByName = lookupDomainByNameOrg + }() + + libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + err := libvirtGoMonitor.Connect() + if err != nil { + t.Errorf("unexpected error: %v\n", err) + } + + domainEventRegisterInternalOrg := domainEventRegisterInternal + domainEventRegisterInternal = func( + mon *libvirtGoMonitorLinux, + callback *libvirt.DomainEventCallback, + fn func()) int { + return -1 + } + defer func() { + domainEventRegisterInternal = domainEventRegisterInternalOrg + }() + + _, err = libvirtGoMonitor.Events() + if err == nil { + t.Fatalf("domain register error expected") + } +} From 4ef4ca4746e965d369ab57fce3bb4802ff05c7ad Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Tue, 18 Oct 2016 18:06:34 -0400 Subject: [PATCH 22/30] qmp/libvirtgo: add getPollInterval tests --- qmp/libvirtgo_linux.go | 6 +++++- qmp/libvirtgo_linux_test.go | 41 ++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index fb0d938..293d0df 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -24,6 +24,10 @@ import ( libvirt "github.com/rgbkrk/libvirt-go" ) +// LibvirtGoEventsInterval constant used for getPollInterval +// method +const LibvirtGoEventsInterval = "LIBVIRTGO_EVENTS_INTERVAL" + type libvirtGoMonitorLinux struct { LibvirtGoMonitor domainName string @@ -283,7 +287,7 @@ var eventRunDefaultImplInternal = func() int { // getPollInterval retreives the events poll interval from the // LIBVIRTGO_EVENTS_INTERVAL environment variable. Defaults to 1s. func getPollInterval() time.Duration { - intervalStr := os.Getenv("LIBVIRTGO_EVENTS_INTERVAL") + intervalStr := os.Getenv(LibvirtGoEventsInterval) interval, _ := time.ParseDuration("1s") if intervalStr != "" { desiredInterval, err := time.ParseDuration(intervalStr) diff --git a/qmp/libvirtgo_linux_test.go b/qmp/libvirtgo_linux_test.go index 7ba0dd8..2db5c34 100644 --- a/qmp/libvirtgo_linux_test.go +++ b/qmp/libvirtgo_linux_test.go @@ -16,7 +16,9 @@ package qmp import ( "errors" + "os" "testing" + "time" libvirt "github.com/rgbkrk/libvirt-go" ) @@ -166,7 +168,7 @@ func TestLibvirtGoRunOK(t *testing.T) { } if expectedOutput != string(result) { - t.Fatalf("expected %s and got %s\n", expectedOutput, string(result)) + t.Fatalf("Unexpected value. Expected %s and got %s\n", expectedOutput, string(result)) } } @@ -234,3 +236,40 @@ func TestLibvirtGoEventsDomainEventRegisterFailed(t *testing.T) { t.Fatalf("domain register error expected") } } + +func TestLibvirtGoGetPollIntervalDefault(t *testing.T) { + + interval := getPollInterval() + expectedInterval, _ := time.ParseDuration("1s") + + if expectedInterval != interval { + t.Errorf("Unexpected value. Expected [%s] and got [%s]\n", expectedInterval, interval) + } +} + +func TestLibvirtGoGetPollIntervalDefaultInvalid(t *testing.T) { + os.Setenv(LibvirtGoEventsInterval, "invalid") + defer func() { + os.Unsetenv(LibvirtGoEventsInterval) + }() + interval := getPollInterval() + expectedInterval, _ := time.ParseDuration("1s") + + if expectedInterval != interval { + t.Errorf("Unexpected value. Expected [%s] and got [%s]\n", expectedInterval, interval) + } + +} + +func TestLibvirtGoGetPollIntervalDefault5Second(t *testing.T) { + os.Setenv(LibvirtGoEventsInterval, "5s") + defer func() { + os.Unsetenv(LibvirtGoEventsInterval) + }() + interval := getPollInterval() + expectedInterval, _ := time.ParseDuration("5s") + + if expectedInterval != interval { + t.Errorf("Unexpected value. Expected [%s] and got [%s]\n", expectedInterval, interval) + } +} From c3c598c862b4ef386a99fed050c44dcfabdc240f Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Tue, 18 Oct 2016 19:03:02 -0400 Subject: [PATCH 23/30] qmp/libvirtgo: add events OK test --- qmp/libvirtgo_linux.go | 4 +- qmp/libvirtgo_linux_test.go | 82 +++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 293d0df..e42db13 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -24,8 +24,8 @@ import ( libvirt "github.com/rgbkrk/libvirt-go" ) -// LibvirtGoEventsInterval constant used for getPollInterval -// method +// LibvirtGoEventsInterval constant used by the getPollInterval +// method. const LibvirtGoEventsInterval = "LIBVIRTGO_EVENTS_INTERVAL" type libvirtGoMonitorLinux struct { diff --git a/qmp/libvirtgo_linux_test.go b/qmp/libvirtgo_linux_test.go index 2db5c34..8361edb 100644 --- a/qmp/libvirtgo_linux_test.go +++ b/qmp/libvirtgo_linux_test.go @@ -17,6 +17,7 @@ package qmp import ( "errors" "os" + "sync" "testing" "time" @@ -180,6 +181,87 @@ func TestLibvirtGoRunNoConnection(t *testing.T) { } } +func TestLibvirtGoEventsDomainEventRegisterOK(t *testing.T) { + eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal + eventRegisterDefaultImplInternal = func() int { + return 0 + } + defer func() { + eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg + }() + + newVirConnectionInternalOrg := newVirConnectionInternal + newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { + return libvirt.VirConnection{}, nil + } + defer func() { + newVirConnectionInternal = newVirConnectionInternalOrg + }() + + lookupDomainByNameOrg := lookupDomainByName + lookupDomainByName = func(virConn *libvirt.VirConnection, + domainName string) (libvirt.VirDomain, error) { + return libvirt.VirDomain{}, nil + } + defer func() { + lookupDomainByName = lookupDomainByNameOrg + }() + + libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + err := libvirtGoMonitor.Connect() + if err != nil { + t.Errorf("unexpected error: %v\n", err) + } + + var wg sync.WaitGroup + expectedEvent := libvirt.DomainLifecycleEvent{ + Event: libvirt.VIR_DOMAIN_EVENT_STARTED, + Detail: 0, + } + simulateSendingEvents := func(callback libvirt.DomainEventCallback) { + callback(nil, nil, expectedEvent, func() {}) + wg.Done() + } + + domainEventRegisterInternalOrg := domainEventRegisterInternal + domainEventRegisterInternal = func( + mon *libvirtGoMonitorLinux, + callback *libvirt.DomainEventCallback, + fn func()) int { + wg.Add(1) + go simulateSendingEvents(*callback) + + return 0 + } + defer func() { + domainEventRegisterInternal = domainEventRegisterInternalOrg + }() + + events, err := libvirtGoMonitor.Events() + if err != nil { + t.Errorf("unexpected error: %v\n", err) + } + var resultEvent Event + go func() { + wg.Add(1) + for event := range events { + resultEvent = event + wg.Done() + break + } + }() + + wg.Wait() + + detailsEvent, found := resultEvent.Data["details"] + if !found { + t.Errorf("Expected at least one Event") + } + if expectedEvent != detailsEvent { + t.Errorf("Unexpected event. Expected %#v and got %#v\n", expectedEvent, detailsEvent) + } +} + func TestLibvirtGoEventsNoConnection(t *testing.T) { libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") _, err := libvirtGoMonitor.Events() From f6749c9c8a8dae5e381111cfcb20deb447a88bad Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Tue, 18 Oct 2016 19:20:37 -0400 Subject: [PATCH 24/30] qmp/libvirtgo: add libvirtgo_run_command to README section --- examples/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/examples/README.md b/examples/README.md index d4d702e..c6af68a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -121,4 +121,30 @@ You should have an output similar to this: ```{r, engine='bash', count_lines} Connecting to unix:///var/run/libvirt/libvirt-sock Domain should be shut off now +``` + +#### libvirtgo_run_command + +[libvirtgo_run_command](./libvirtgo_run_command) demonstrates how to use + the [libvirtgo Monitor](https://godoc.org/github.com/digitalocean/go-qemu/hypervisor) + package to send a QMP command to the specified domain. + +To run: +```{r, engine='bash', count_lines} + $ go get github.com/digitalocean/go-qemu + Terminal 1: + $ go run examples/libvirtgo_run_command/main.go -uri="qemu:///system" -domainName="ubuntu14.04" + + Terminal 2: + virsh -c qemu:///system + virsh # start ubuntu14.04 +``` + + +You should see an output similar to this on Terminal 1: +```{r, engine='bash', count_lines} +Waiting for Domain events... +Press the Enter key to stop +Event: qmp.Event{Event:"Domain event=\"resumed\" detail=\"unpaused\"", Data:map[string]interface {}{"details":libvirt.DomainLifecycleEvent{Event:4, Detail:0}}, Timestamp:struct { Seconds int64 "json:\"seconds\""; Microseconds int64 "json:\"microseconds\"" }{Seconds:11, Microseconds:0}} +Event: qmp.Event{Event:"Domain event=\"started\" detail=\"booted\"", Data:map[string]interface {}{"details":libvirt.DomainLifecycleEvent{Event:2, Detail:0}}, Timestamp:struct { Seconds int64 "json:\"seconds\""; Microseconds int64 "json:\"microseconds\"" }{Seconds:12, Microseconds:0}} ``` \ No newline at end of file From 4838752df5c5365cae86c5e471449164a588f2ef Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Tue, 18 Oct 2016 19:29:29 -0400 Subject: [PATCH 25/30] qmp/libvirtgo: add libvirtgo_events example to README section --- examples/README.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/examples/README.md b/examples/README.md index c6af68a..b31c7b4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -126,14 +126,33 @@ Domain should be shut off now #### libvirtgo_run_command [libvirtgo_run_command](./libvirtgo_run_command) demonstrates how to use - the [libvirtgo Monitor](https://godoc.org/github.com/digitalocean/go-qemu/hypervisor) + the [libvirtGoMonitorLinux](https://godoc.org/github.com/digitalocean/go-qemu/libvirtGoMonitorLinux) package to send a QMP command to the specified domain. To run: ```{r, engine='bash', count_lines} $ go get github.com/digitalocean/go-qemu + $ go run examples/libvirtgo_run_command/main.go -uri="qemu:///system" -domainName="centos7" +``` + + +You should see an output similar to this: +```{r, engine='bash', count_lines} +query-cpus: {"return":[{"current":true,"CPU":0,"qom_path":"/machine/unattached/device[0]","pc":-2130342250,"halted":true,"thread_id":2462}],"id":"libvirt-36"} +``` + +#### libvirtgo_events + +[libvirtgo_events](./libvirtgo_events) demonstrates how to use + the [libvirtGoMonitorLinux](https://godoc.org/github.com/digitalocean/go-qemu/libvirtGoMonitorLinux) + package to wait for lifecycle events from the specified domain. + +To run: +```{r, engine='bash', count_lines} + $ go get github.com/digitalocean/go-qemu + Terminal 1: - $ go run examples/libvirtgo_run_command/main.go -uri="qemu:///system" -domainName="ubuntu14.04" + $ go run examples/libvirtgo_events/main.go -uri="qemu:///system" -domainName="ubuntu14.04" Terminal 2: virsh -c qemu:///system From 64e311bcc0f8399a389689d3d84e00b806fbabb2 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Thu, 20 Oct 2016 20:02:54 -0400 Subject: [PATCH 26/30] qmp/libvirtgo: changes PR review --- examples/libvirtgo_events/main.go | 14 +-- examples/libvirtgo_run_command/main.go | 27 +++-- qmp/libvirtgo.go | 2 +- qmp/libvirtgo_linux.go | 135 +++++++++++-------------- 4 files changed, 84 insertions(+), 94 deletions(-) diff --git a/examples/libvirtgo_events/main.go b/examples/libvirtgo_events/main.go index 5585f8f..910eb6e 100644 --- a/examples/libvirtgo_events/main.go +++ b/examples/libvirtgo_events/main.go @@ -23,33 +23,33 @@ import ( ) var ( - uri = flag.String("uri", "qemu:///system", `URI to connect to the libvirtd host.`) - domainName = flag.String("domainName", "mydomain", "This is the domain to run commands against.") + uri = flag.String("uri", "qemu:///system", "URI to connect to the libvirtd host.") + domainName = flag.String("domainName", "mydomain", "The domain to run commands against.") ) func main() { flag.Parse() - libvirtGoMonitor := qmp.NewLibvirtGoMonitor(*uri, *domainName) + mon := qmp.NewLibvirtGoMonitor(*uri, *domainName) - err := libvirtGoMonitor.Connect() + err := mon.Connect() if err != nil { log.Fatalf("Unable to connect: %v\n", err) } - eventsChans, err := libvirtGoMonitor.Events() + events, err := mon.Events() if err != nil { log.Fatalf("Unable to register for events: %v\n", err) } fmt.Println("Waiting for Domain events...") go func() { - for event := range eventsChans { + for event := range events { fmt.Printf("Event: %#v\n", event) } }() fmt.Println("Press the Enter key to stop") fmt.Scanln() - libvirtGoMonitor.Disconnect() + mon.Disconnect() } diff --git a/examples/libvirtgo_run_command/main.go b/examples/libvirtgo_run_command/main.go index 6887f60..a59bfa0 100644 --- a/examples/libvirtgo_run_command/main.go +++ b/examples/libvirtgo_run_command/main.go @@ -19,32 +19,39 @@ import ( "fmt" "log" + qemu "github.com/digitalocean/go-qemu" "github.com/digitalocean/go-qemu/qmp" ) var ( - uri = flag.String("uri", "qemu:///system", `URI to connect to the libvirtd host.`) - domainName = flag.String("domainName", "mydomain", "This is the domain to run commands against.") + uri = flag.String("uri", "qemu:///system", "URI to connect to the libvirtd host.") + domainName = flag.String("domainName", "mydomain", "The domain to run commands against.") ) func main() { flag.Parse() - libvirtGoMonitor := qmp.NewLibvirtGoMonitor(*uri, *domainName) + mon := qmp.NewLibvirtGoMonitor(*uri, *domainName) - err := libvirtGoMonitor.Connect() + if err := mon.Connect(); err != nil { + log.Fatalf("failed to connect: %v", err) + } + + domain, err := qemu.NewDomain(mon, *domainName) if err != nil { - log.Fatalf("Unable to connect: %v\n", err) + log.Fatalf("failed to create domain object: %v", err) } + defer domain.Close() - command := []byte("{\"execute\" : \"query-cpus\"}") - cpus, err := libvirtGoMonitor.Run(command) + // domain.CPUs will forward QMP commands to the monitor + cpus, err := domain.CPUs() if err != nil { - log.Fatalf("Unable to run command: %v\n", err) + log.Fatalf("failed to get cpus: %v", err) } - fmt.Printf("query-cpus: %s\n", string(cpus)) - if err = libvirtGoMonitor.Disconnect(); err != nil { + fmt.Printf("CPUs: %v\n", cpus) + + if err = mon.Disconnect(); err != nil { log.Fatalf("Unable to disconnect: %v\n", err) } } diff --git a/qmp/libvirtgo.go b/qmp/libvirtgo.go index 61999d4..ba6262b 100644 --- a/qmp/libvirtgo.go +++ b/qmp/libvirtgo.go @@ -16,7 +16,7 @@ package qmp // LibvirtGoMonitor is a Monitor that wraps the libvirt-go package to // communicate with a QEMU Machine Protocol (QMP) socket. -// Communication is provied via the libvirtd daemon. Multiple +// Communication is proxied via the libvirtd daemon. Multiple // connections to the same hypervisor and domain are permitted. type LibvirtGoMonitor struct { Monitor diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index e42db13..358a4ac 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -16,29 +16,32 @@ package qmp import ( "errors" - "log" - "os" + "fmt" "sync" "time" libvirt "github.com/rgbkrk/libvirt-go" ) -// LibvirtGoEventsInterval constant used by the getPollInterval -// method. -const LibvirtGoEventsInterval = "LIBVIRTGO_EVENTS_INTERVAL" - -type libvirtGoMonitorLinux struct { +// LibvirtGoMonitor is a Monitor that wraps the libvirt-go package to +// communicate with a QEMU Machine Protocol (QMP) socket. +// Communication is proxied via the libvirtd daemon. Multiple +// connections to the same hypervisor and domain are permitted. +type LibvirtGoMonitorLinux struct { LibvirtGoMonitor - domainName string - domain *libvirt.VirDomain - uri string - virConn *libvirt.VirConnection - once sync.Once - doneChan chan bool - eventsChan chan Event + domainName string + domain *libvirt.VirDomain + uri string + virConn *libvirt.VirConnection + once sync.Once + doneChan chan bool + eventsChan chan Event + callbackID int + eventsLoopInterval time.Duration } +const errNoLibvirtConnection = errors.New("need a established connection to proceed") + // NewLibvirtGoMonitor configures a connection to the provided hypervisor // and domain. // An error is returned if the provided libvirt connection URI is invalid. @@ -46,10 +49,11 @@ type libvirtGoMonitorLinux struct { // Hypervisor URIs may be local or remote, e.g., // qemu:///system // qemu+ssh://libvirt@example.com/system -func NewLibvirtGoMonitor(uri, domain string) Monitor { - return &libvirtGoMonitorLinux{ - uri: uri, - domainName: domain, +func NewLibvirtGoMonitor(uri, domain string) *LibvirtGoMonitorLinux { + return &LibvirtGoMonitorLinux{ + uri: uri, + domainName: domain, + eventsLoopInterval: 1 * Second, } } @@ -57,17 +61,18 @@ func NewLibvirtGoMonitor(uri, domain string) Monitor { // the libvirt-go package. // An error is returned if the libvirtd daemon is unreachable. func (mon *libvirtGoMonitorLinux) Connect() error { + // Already connected? + if mon.virConn != nil { + return nil + } // As per the libvirt core library: // For proper event handling, it is important // that the event implementation is registered // before a connection to the Hypervisor is opened. - // We only do this once the first a Connect is called. - mon.once.Do(eventRegisterDefaultImpl) - - // Already connected? - if mon.virConn != nil { - return nil + err := eventRegisterDefaultImpl() + if err != nil { + return err } virConn, err := newVirConnectionInternal(mon.uri) @@ -103,7 +108,12 @@ func (mon *libvirtGoMonitorLinux) Disconnect() error { } if mon.virConn != nil { - _, err = closeConnectionInternal(mon.virConn) + err = domainEventDeregister(mon) + _, closeErr := closeConnectionInternal(mon.virConn) + if closeErr != nil { + // close connection error takes precedence + err = closeErr + } mon.virConn = nil close(mon.doneChan) // stop listenning to events close(mon.eventsChan) // stop sending events to clients @@ -122,7 +132,7 @@ var closeConnectionInternal = func( // http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD func (mon *libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { if mon.virConn == nil { - return nil, errors.New("Run() need a established connection to proceed.") + return nil, errNoLibvirtConnection } result, err := qemuMonitorCommandInternal(mon.domain, string(cmd)) @@ -145,20 +155,17 @@ var qemuMonitorCommandInternal = func( // an error will be returned. Errors encountered during streaming will // cause the returned event channel to be closed. func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { - if mon.virConn == nil { - return nil, - errors.New("Events() need a established connection to proceed.") + return nil, errNoLibvirtConnection } callbackID, err := domainEventRegister(mon) if err != nil { return nil, err } + mon.callbackID = callbackID - go pollEventsLoop(mon.doneChan) - - go domainEventDeregister(mon, callbackID) + go pollEventsLoop(mon.eventsLoopInterval, mon.doneChan) return mon.eventsChan, nil } @@ -170,14 +177,16 @@ var lookupDomainByName = func( } // eventRegisterDefaultImpl registers a default event implementation. -func eventRegisterDefaultImpl() { +func eventRegisterDefaultImpl() error { eventRegisterID := eventRegisterDefaultImplInternal() if eventRegisterID == -1 { - log.Printf( + errMessage := fmt.Srintf( "EventRegisterDefaultImpl returned an unexpected value %d\n", eventRegisterID) - //TODO: panic or what??? + return errMessage } + + return nil } var eventRegisterDefaultImplInternal = func() int { @@ -187,8 +196,7 @@ var eventRegisterDefaultImplInternal = func() int { // domainEventRegister register with libvirt to receive events for the // specified domain. func domainEventRegister(mon *libvirtGoMonitorLinux) (int, error) { - callback := - libvirt.DomainEventCallback(newEventCallback(mon)) + callback := libvirt.DomainEventCallback(newEventCallback(mon)) //TODO: add ability to register for more event types callbackID := domainEventRegisterInternal( @@ -200,7 +208,7 @@ func domainEventRegister(mon *libvirtGoMonitorLinux) (int, error) { }, ) if callbackID == -1 { - return -1, errors.New("Domain event registration failed!") + return -1, errors.New("domain event registration failed") } return callbackID, nil @@ -220,15 +228,13 @@ var domainEventRegisterInternal = func( // domainEventDeregister stops the registration with libvirt from receiving // events for the specified domain. -func domainEventDeregister( - mon *libvirtGoMonitorLinux, - callbackID int) { - <-mon.doneChan - ret := domainEventDeregisterInternal(mon.virConn, callbackID) +func domainEventDeregister(mon *libvirtGoMonitorLinux) error { + ret := domainEventDeregisterInternal(mon.virConn, mon.callbackID) if ret != 0 { - log.Printf("DomainEventDeregister returned an unexpected value: %d\n", - ret) + errMessage := fmt.Sprintf("DomainEventDeregister returned an unexpected value: %d\n", ret) + return errors.New(errMessage) } + return nil } var domainEventDeregisterInternal = func(virConn *libvirt.VirConnection, @@ -251,21 +257,20 @@ func newEventCallback(mon *libvirtGoMonitorLinux) libvirt.DomainEventCallback { } // We only support lifecycleEvents for now - if lifecycleEvent, ok := - eventDetails.(libvirt.DomainLifecycleEvent); ok { + if lifecycleEvent, ok := eventDetails.(libvirt.DomainLifecycleEvent); ok { mon.eventsChan <- constructEvent(lifecycleEvent) f() } - // according to the libvirt-do - // the return code is ignored + // according to the libvirt-go documentation + // the return code from DomainEventCallback is ignored. return 0 } } // pollEventsLoop keeps polling libvirt for new domain events -func pollEventsLoop(doneChan chan bool) { - checkInterval := time.Tick(getPollInterval()) +func pollEventsLoop(eventsLoopInterval time.Duration, doneChan chan bool) { + checkInterval := time.Tick(eventsLoopInterval) for { select { case <-doneChan: // stop polling for events @@ -284,24 +289,6 @@ var eventRunDefaultImplInternal = func() int { return libvirt.EventRunDefaultImpl() } -// getPollInterval retreives the events poll interval from the -// LIBVIRTGO_EVENTS_INTERVAL environment variable. Defaults to 1s. -func getPollInterval() time.Duration { - intervalStr := os.Getenv(LibvirtGoEventsInterval) - interval, _ := time.ParseDuration("1s") - if intervalStr != "" { - desiredInterval, err := time.ParseDuration(intervalStr) - if err != nil { - log.Printf( - "Unable value specified in 'LIBVIRTGO_EVENTS_INTERVAL': %v\n", - err) - return interval - } - interval = desiredInterval - } - return interval -} - // constructEvent helper function to map DomainLifecycleEvent // into Event. func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { @@ -309,13 +296,9 @@ func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { // the event was received on this end and not when it // actually occurred. now := time.Now() - type TimeStamp struct { - Seconds int64 `json:"seconds"` - Microseconds int64 `json:"microseconds"` - } - timestamp := TimeStamp{ - int64(now.Second()), - int64(now.Second() / int(time.Microsecond)), + timestamp := Event.Timestamp{ + Seconds: int64(now.Second()), + Microseconds: int64(now.Second() / int(time.Microsecond)), } data := make(map[string]interface{}) data["details"] = eventDetails From bab3bd7f16ad3fb13ef3547902ad5822c13f9c25 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Thu, 20 Oct 2016 20:46:06 -0400 Subject: [PATCH 27/30] qmp/libvirtgo: use interfaces for internal implmentation --- qmp/libvirtgo_linux.go | 177 +++++++++-------- qmp/libvirtgo_linux_test.go | 374 +++++++++++------------------------- 2 files changed, 217 insertions(+), 334 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 358a4ac..6d97546 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -23,7 +23,7 @@ import ( libvirt "github.com/rgbkrk/libvirt-go" ) -// LibvirtGoMonitor is a Monitor that wraps the libvirt-go package to +// LibvirtGoMonitorLinux is a Monitor that wraps the libvirt-go package to // communicate with a QEMU Machine Protocol (QMP) socket. // Communication is proxied via the libvirtd daemon. Multiple // connections to the same hypervisor and domain are permitted. @@ -38,9 +38,26 @@ type LibvirtGoMonitorLinux struct { eventsChan chan Event callbackID int eventsLoopInterval time.Duration + // internal implementation + libvirtGoMonitorInternal } -const errNoLibvirtConnection = errors.New("need a established connection to proceed") +var errNoLibvirtConnection = errors.New("need a established connection to proceed") + +type libvirtGoMonitorInternal interface { + lookupDomainByNameInternal(virConn *libvirt.VirConnection, domainName string) (libvirt.VirDomain, error) + newVirConnectionInternal(uri string) (libvirt.VirConnection, error) + closeConnectionInternal(virConn *libvirt.VirConnection) (int, error) + qemuMonitorCommandInternal(domain *libvirt.VirDomain, cmd string) (string, error) + eventRegisterDefaultImplInternal() int + domainEventRegisterInternal(virConn *libvirt.VirConnection, domain *libvirt.VirDomain, callback *libvirt.DomainEventCallback, fn func()) int + domainEventDeregisterInternal(virConn *libvirt.VirConnection, callbackID int) int + eventRunDefaultImplInternal() int +} + +type libvirtGoMonitorInternalImpl struct { + libvirtGoMonitorInternal +} // NewLibvirtGoMonitor configures a connection to the provided hypervisor // and domain. @@ -51,16 +68,17 @@ const errNoLibvirtConnection = errors.New("need a established connection to proc // qemu+ssh://libvirt@example.com/system func NewLibvirtGoMonitor(uri, domain string) *LibvirtGoMonitorLinux { return &LibvirtGoMonitorLinux{ - uri: uri, - domainName: domain, - eventsLoopInterval: 1 * Second, + uri: uri, + domainName: domain, + libvirtGoMonitorInternal: &libvirtGoMonitorInternalImpl{}, + eventsLoopInterval: 1 * time.Second, } } // Connect sets up QEMU QMP connection via libvirt using // the libvirt-go package. // An error is returned if the libvirtd daemon is unreachable. -func (mon *libvirtGoMonitorLinux) Connect() error { +func (mon *LibvirtGoMonitorLinux) Connect() error { // Already connected? if mon.virConn != nil { return nil @@ -70,19 +88,19 @@ func (mon *libvirtGoMonitorLinux) Connect() error { // For proper event handling, it is important // that the event implementation is registered // before a connection to the Hypervisor is opened. - err := eventRegisterDefaultImpl() + err := mon.eventRegisterDefaultImpl() if err != nil { return err } - virConn, err := newVirConnectionInternal(mon.uri) + virConn, err := mon.newVirConnectionInternal(mon.uri) if err != nil { return err } - domain, err := lookupDomainByName(&virConn, mon.domainName) + domain, err := mon.lookupDomainByNameInternal(&virConn, mon.domainName) if err != nil { - closeConnectionInternal(&virConn) + mon.closeConnectionInternal(&virConn) return err } @@ -94,13 +112,8 @@ func (mon *libvirtGoMonitorLinux) Connect() error { return err } -var newVirConnectionInternal = func( - uri string) (libvirt.VirConnection, error) { - return libvirt.NewVirConnection(uri) -} - // Disconnect tears down open QMP socket connections. -func (mon *libvirtGoMonitorLinux) Disconnect() error { +func (mon *LibvirtGoMonitorLinux) Disconnect() error { var err error if mon.domain != nil { mon.domain.Free() @@ -109,7 +122,7 @@ func (mon *libvirtGoMonitorLinux) Disconnect() error { if mon.virConn != nil { err = domainEventDeregister(mon) - _, closeErr := closeConnectionInternal(mon.virConn) + _, closeErr := mon.closeConnectionInternal(mon.virConn) if closeErr != nil { // close connection error takes precedence err = closeErr @@ -122,20 +135,15 @@ func (mon *libvirtGoMonitorLinux) Disconnect() error { return err } -var closeConnectionInternal = func( - virConn *libvirt.VirConnection) (int, error) { - return virConn.CloseConnection() -} - // Run executes the given QAPI command against a domain's QEMU instance. // For a list of available QAPI commands, see: // http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD -func (mon *libvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { +func (mon *LibvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { if mon.virConn == nil { return nil, errNoLibvirtConnection } - result, err := qemuMonitorCommandInternal(mon.domain, string(cmd)) + result, err := mon.qemuMonitorCommandInternal(mon.domain, string(cmd)) if err != nil { return nil, err } @@ -154,7 +162,7 @@ var qemuMonitorCommandInternal = func( // If a problem is encountered setting up the event monitor connection // an error will be returned. Errors encountered during streaming will // cause the returned event channel to be closed. -func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { +func (mon *LibvirtGoMonitorLinux) Events() (<-chan Event, error) { if mon.virConn == nil { return nil, errNoLibvirtConnection } @@ -165,42 +173,33 @@ func (mon *libvirtGoMonitorLinux) Events() (<-chan Event, error) { } mon.callbackID = callbackID - go pollEventsLoop(mon.eventsLoopInterval, mon.doneChan) + go mon.pollEventsLoop(mon.eventsLoopInterval, mon.doneChan) return mon.eventsChan, nil } -var lookupDomainByName = func( - virConn *libvirt.VirConnection, - domainName string) (libvirt.VirDomain, error) { - return virConn.LookupDomainByName(domainName) -} - // eventRegisterDefaultImpl registers a default event implementation. -func eventRegisterDefaultImpl() error { - eventRegisterID := eventRegisterDefaultImplInternal() +func (mon *LibvirtGoMonitorLinux) eventRegisterDefaultImpl() error { + eventRegisterID := mon.eventRegisterDefaultImplInternal() if eventRegisterID == -1 { - errMessage := fmt.Srintf( + errMessage := fmt.Sprintf( "EventRegisterDefaultImpl returned an unexpected value %d\n", eventRegisterID) - return errMessage + return errors.New(errMessage) } return nil } -var eventRegisterDefaultImplInternal = func() int { - return libvirt.EventRegisterDefaultImpl() -} - // domainEventRegister register with libvirt to receive events for the // specified domain. -func domainEventRegister(mon *libvirtGoMonitorLinux) (int, error) { +func domainEventRegister(mon *LibvirtGoMonitorLinux) (int, error) { callback := libvirt.DomainEventCallback(newEventCallback(mon)) //TODO: add ability to register for more event types - callbackID := domainEventRegisterInternal( - mon, + callbackID := mon.domainEventRegisterInternal( + mon.virConn, + mon.domain, &callback, func() { // empty on purpose until it's decided @@ -214,22 +213,10 @@ func domainEventRegister(mon *libvirtGoMonitorLinux) (int, error) { return callbackID, nil } -var domainEventRegisterInternal = func( - mon *libvirtGoMonitorLinux, - callback *libvirt.DomainEventCallback, - fn func()) int { - return mon.virConn.DomainEventRegister( - *mon.domain, - libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, - callback, - fn, - ) -} - // domainEventDeregister stops the registration with libvirt from receiving // events for the specified domain. -func domainEventDeregister(mon *libvirtGoMonitorLinux) error { - ret := domainEventDeregisterInternal(mon.virConn, mon.callbackID) +func domainEventDeregister(mon *LibvirtGoMonitorLinux) error { + ret := mon.domainEventDeregisterInternal(mon.virConn, mon.callbackID) if ret != 0 { errMessage := fmt.Sprintf("DomainEventDeregister returned an unexpected value: %d\n", ret) return errors.New(errMessage) @@ -237,14 +224,9 @@ func domainEventDeregister(mon *libvirtGoMonitorLinux) error { return nil } -var domainEventDeregisterInternal = func(virConn *libvirt.VirConnection, - callbackID int) int { - return virConn.DomainEventDeregister(callbackID) -} - // newEventCallback convenient function to provide access // to the eventChan to the returned closure/callback. -func newEventCallback(mon *libvirtGoMonitorLinux) libvirt.DomainEventCallback { +func newEventCallback(mon *LibvirtGoMonitorLinux) libvirt.DomainEventCallback { return func(c *libvirt.VirConnection, d *libvirt.VirDomain, eventDetails interface{}, f func()) int { @@ -269,14 +251,14 @@ func newEventCallback(mon *libvirtGoMonitorLinux) libvirt.DomainEventCallback { } // pollEventsLoop keeps polling libvirt for new domain events -func pollEventsLoop(eventsLoopInterval time.Duration, doneChan chan bool) { +func (mon *LibvirtGoMonitorLinux) pollEventsLoop(eventsLoopInterval time.Duration, doneChan chan bool) { checkInterval := time.Tick(eventsLoopInterval) for { select { case <-doneChan: // stop polling for events return case <-checkInterval: - ret := eventRunDefaultImplInternal() + ret := mon.eventRunDefaultImplInternal() if ret == -1 { doneChan <- true return @@ -285,10 +267,6 @@ func pollEventsLoop(eventsLoopInterval time.Duration, doneChan chan bool) { } } -var eventRunDefaultImplInternal = func() int { - return libvirt.EventRunDefaultImpl() -} - // constructEvent helper function to map DomainLifecycleEvent // into Event. func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { @@ -296,15 +274,60 @@ func constructEvent(eventDetails libvirt.DomainLifecycleEvent) Event { // the event was received on this end and not when it // actually occurred. now := time.Now() - timestamp := Event.Timestamp{ - Seconds: int64(now.Second()), - Microseconds: int64(now.Second() / int(time.Microsecond)), - } data := make(map[string]interface{}) data["details"] = eventDetails - return Event{ - Event: eventDetails.String(), - Data: data, - Timestamp: timestamp, + event := Event{ + Event: eventDetails.String(), + Data: data, } + event.Timestamp.Microseconds = int64(now.Second() / int(time.Microsecond)) + event.Timestamp.Seconds = int64(now.Second()) + return event +} + +/* + Libvirt-go proxy methods + +*/ + +func (mon *libvirtGoMonitorInternalImpl) lookupDomainByNameInternal(virConn *libvirt.VirConnection, domainName string) (libvirt.VirDomain, error) { + return virConn.LookupDomainByName(domainName) +} + +func (mon *libvirtGoMonitorInternalImpl) newVirConnectionInternal(uri string) (libvirt.VirConnection, error) { + return libvirt.NewVirConnection(uri) +} + +func (mon *libvirtGoMonitorInternalImpl) closeConnectionInternal(virConn *libvirt.VirConnection) (int, error) { + return virConn.CloseConnection() +} + +func (mon *libvirtGoMonitorInternalImpl) qemuMonitorCommandInternal(domain *libvirt.VirDomain, cmd string) (string, error) { + return domain.QemuMonitorCommand( + libvirt.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT, + cmd) +} + +func (mon *libvirtGoMonitorInternalImpl) eventRegisterDefaultImplInternal() int { + return libvirt.EventRegisterDefaultImpl() +} + +func (mon *libvirtGoMonitorInternalImpl) domainEventRegisterInternal( + virConn *libvirt.VirConnection, + domain *libvirt.VirDomain, + callback *libvirt.DomainEventCallback, fn func()) int { + return virConn.DomainEventRegister( + *domain, + libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, + callback, + fn, + ) +} + +func (mon *libvirtGoMonitorInternalImpl) domainEventDeregisterInternal(virConn *libvirt.VirConnection, callbackID int) int { + return virConn.DomainEventDeregister(callbackID) +} + +func (mon *libvirtGoMonitorInternalImpl) eventRunDefaultImplInternal() int { + return libvirt.EventRunDefaultImpl() } diff --git a/qmp/libvirtgo_linux_test.go b/qmp/libvirtgo_linux_test.go index 8361edb..41e824d 100644 --- a/qmp/libvirtgo_linux_test.go +++ b/qmp/libvirtgo_linux_test.go @@ -16,160 +16,60 @@ package qmp import ( "errors" - "os" - "sync" "testing" - "time" libvirt "github.com/rgbkrk/libvirt-go" ) func TestLibvirtGoConnetOK(t *testing.T) { - - eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal - eventRegisterDefaultImplInternal = func() int { - return 0 - } - defer func() { - eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg - }() - - newVirConnectionInternalOrg := newVirConnectionInternal - newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { - return libvirt.VirConnection{}, nil - } - defer func() { - newVirConnectionInternal = newVirConnectionInternalOrg - }() - - lookupDomainByNameOrg := lookupDomainByName - lookupDomainByName = func(virConn *libvirt.VirConnection, - domainName string) (libvirt.VirDomain, error) { - return libvirt.VirDomain{}, nil - } - defer func() { - lookupDomainByName = lookupDomainByNameOrg - }() - libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + libvirtGoMonitor.libvirtGoMonitorInternal = &fakeLibvirtGoMonitorInternal{} err := libvirtGoMonitor.Connect() if err != nil { t.Errorf("unexpected error: %v\n", err) } - } func TestLibvirtGoConnectionFailed(t *testing.T) { - - eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal - eventRegisterDefaultImplInternal = func() int { - return 0 - } - defer func() { - eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg - }() - - newVirConnectionInternalOrg := newVirConnectionInternal - newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { - return libvirt.VirConnection{}, errors.New("server down") - } - defer func() { - newVirConnectionInternal = newVirConnectionInternalOrg - }() - libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + libvirtGoMonitor.libvirtGoMonitorInternal = &fakeLibvirtGoMonitorInternal{ + connErr: errors.New("[TEST] server down"), + } err := libvirtGoMonitor.Connect() if err == nil { - t.Fatalf("connection error expected") + t.Errorf("connection error expected") } - } func TestLibvirtGoDomainLookupFailed(t *testing.T) { - - eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal - eventRegisterDefaultImplInternal = func() int { - return 0 - } - defer func() { - eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg - }() - - newVirConnectionInternalOrg := newVirConnectionInternal - newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { - return libvirt.VirConnection{}, nil - } - defer func() { - newVirConnectionInternal = newVirConnectionInternalOrg - }() - - lookupDomainByNameOrg := lookupDomainByName - lookupDomainByName = func(virConn *libvirt.VirConnection, - domainName string) (libvirt.VirDomain, error) { - return libvirt.VirDomain{}, errors.New("Unable to find domain") - } - defer func() { - lookupDomainByName = lookupDomainByNameOrg - }() - libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + libvirtGoMonitor.libvirtGoMonitorInternal = &fakeLibvirtGoMonitorInternal{ + domainErr: errors.New("[TEST] domain not found"), + } err := libvirtGoMonitor.Connect() if err == nil { - t.Errorf("unexpected error: %v\n", err) + t.Errorf("domain error expected") } - } func TestLibvirtGoRunOK(t *testing.T) { - - eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal - eventRegisterDefaultImplInternal = func() int { - return 0 - } - defer func() { - eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg - }() - - newVirConnectionInternalOrg := newVirConnectionInternal - newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { - return libvirt.VirConnection{}, nil - } - defer func() { - newVirConnectionInternal = newVirConnectionInternalOrg - }() - - lookupDomainByNameOrg := lookupDomainByName - lookupDomainByName = func(virConn *libvirt.VirConnection, - domainName string) (libvirt.VirDomain, error) { - return libvirt.VirDomain{}, nil - } - defer func() { - lookupDomainByName = lookupDomainByNameOrg - }() - libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + expectedResult := `{"current":true,"CPU":0,"qom_path":"/machine/unattached/device[0]","pc":33504,"halted":false,"thread_id":25213}],"id":"libvirt-32"}` + libvirtGoMonitor.libvirtGoMonitorInternal = &fakeLibvirtGoMonitorInternal{ + expectedResult: expectedResult, + } err := libvirtGoMonitor.Connect() if err != nil { t.Errorf("unexpected error: %v\n", err) } - expectedOutput := `{"current":true,"CPU":0,"qom_path":"/machine/unattached/device[0]","pc":33504,"halted":false,"thread_id":25213}],"id":"libvirt-32"}` - qemuMonitorCommandInternalOrg := qemuMonitorCommandInternal - qemuMonitorCommandInternal = func( - domain *libvirt.VirDomain, cmd string) (string, error) { - return expectedOutput, nil - } - defer func() { - qemuMonitorCommandInternal = qemuMonitorCommandInternalOrg - }() - result, err := libvirtGoMonitor.Run([]byte("{\"execute\" : \"query-cpus\"}")) if err != nil { t.Fatalf("unexpected error: %v\n", err) } - if expectedOutput != string(result) { - t.Fatalf("Unexpected value. Expected %s and got %s\n", expectedOutput, string(result)) + if expectedResult != string(result) { + t.Fatalf("Unexpected value. Expected %s and got %s\n", expectedResult, string(result)) } } @@ -181,89 +81,57 @@ func TestLibvirtGoRunNoConnection(t *testing.T) { } } -func TestLibvirtGoEventsDomainEventRegisterOK(t *testing.T) { - eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal - eventRegisterDefaultImplInternal = func() int { - return 0 - } - defer func() { - eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg - }() - - newVirConnectionInternalOrg := newVirConnectionInternal - newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { - return libvirt.VirConnection{}, nil - } - defer func() { - newVirConnectionInternal = newVirConnectionInternalOrg - }() - - lookupDomainByNameOrg := lookupDomainByName - lookupDomainByName = func(virConn *libvirt.VirConnection, - domainName string) (libvirt.VirDomain, error) { - return libvirt.VirDomain{}, nil - } - defer func() { - lookupDomainByName = lookupDomainByNameOrg - }() - - libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") - err := libvirtGoMonitor.Connect() - if err != nil { - t.Errorf("unexpected error: %v\n", err) - } - - var wg sync.WaitGroup - expectedEvent := libvirt.DomainLifecycleEvent{ - Event: libvirt.VIR_DOMAIN_EVENT_STARTED, - Detail: 0, - } - simulateSendingEvents := func(callback libvirt.DomainEventCallback) { - callback(nil, nil, expectedEvent, func() {}) - wg.Done() - } - - domainEventRegisterInternalOrg := domainEventRegisterInternal - domainEventRegisterInternal = func( - mon *libvirtGoMonitorLinux, - callback *libvirt.DomainEventCallback, - fn func()) int { - wg.Add(1) - go simulateSendingEvents(*callback) - - return 0 - } - defer func() { - domainEventRegisterInternal = domainEventRegisterInternalOrg - }() - - events, err := libvirtGoMonitor.Events() - if err != nil { - t.Errorf("unexpected error: %v\n", err) - } - var resultEvent Event - go func() { - wg.Add(1) - for event := range events { - resultEvent = event - wg.Done() - break - } - }() - - wg.Wait() - - detailsEvent, found := resultEvent.Data["details"] - if !found { - t.Errorf("Expected at least one Event") - } - if expectedEvent != detailsEvent { - t.Errorf("Unexpected event. Expected %#v and got %#v\n", expectedEvent, detailsEvent) - } -} +// func TestLibvirtGoEventsDomainEventRegisterOK(t *testing.T) { +// libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") +// libvirtGoMonitor.eventDefaultImplProvider = &fakeEventDefaultImplProviderOK{} +// libvirtGoMonitor.connectionProvider = &fakeConnectionProvider{fail: false} +// libvirtGoMonitor.domainFinder = &fakeDomainFinder{fail: false} +// err := libvirtGoMonitor.Connect() +// if err != nil { +// t.Errorf("unexpected error: %v\n", err) +// } + +// var wg sync.WaitGroup +// expectedEvent := libvirt.DomainLifecycleEvent{ +// Event: libvirt.VIR_DOMAIN_EVENT_STARTED, +// Detail: 0, +// } + +// libvirtGoMonitor.domainRegister = &fakeDomainRegisterOK{ +// wg: &wg, +// expectedEvent: &expectedEvent, +// } + +// events, err := libvirtGoMonitor.Events() +// if err != nil { +// t.Errorf("unexpected error: %v\n", err) +// } +// var resultEvent Event +// go func(wg *sync.WaitGroup) { +// wg.Add(1) +// fmt.Println("Waiting for event....") +// for event := range events { +// resultEvent = event +// wg.Done() +// break +// } +// fmt.Println("done waiting....") +// }(&wg) + +// wg.Wait() + +// detailsEvent, found := resultEvent.Data["details"] +// if !found { +// t.Errorf("Expected at least one Event") +// } +// if expectedEvent != detailsEvent { +// t.Errorf("Unexpected event. Expected %#v and got %#v\n", expectedEvent, detailsEvent) +// } +// } func TestLibvirtGoEventsNoConnection(t *testing.T) { libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + libvirtGoMonitor.libvirtGoMonitorInternal = &fakeLibvirtGoMonitorInternal{} _, err := libvirtGoMonitor.Events() if err == nil { t.Fatalf("no connection error expected") @@ -271,87 +139,79 @@ func TestLibvirtGoEventsNoConnection(t *testing.T) { } func TestLibvirtGoEventsDomainEventRegisterFailed(t *testing.T) { - eventRegisterDefaultImplInternalOrg := eventRegisterDefaultImplInternal - eventRegisterDefaultImplInternal = func() int { - return 0 - } - defer func() { - eventRegisterDefaultImplInternal = eventRegisterDefaultImplInternalOrg - }() - - newVirConnectionInternalOrg := newVirConnectionInternal - newVirConnectionInternal = func(uri string) (libvirt.VirConnection, error) { - return libvirt.VirConnection{}, nil - } - defer func() { - newVirConnectionInternal = newVirConnectionInternalOrg - }() - - lookupDomainByNameOrg := lookupDomainByName - lookupDomainByName = func(virConn *libvirt.VirConnection, - domainName string) (libvirt.VirDomain, error) { - return libvirt.VirDomain{}, nil - } - defer func() { - lookupDomainByName = lookupDomainByNameOrg - }() - libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + libvirtGoMonitor.libvirtGoMonitorInternal = &fakeLibvirtGoMonitorInternal{ + domainEventRetCode: -1, + } err := libvirtGoMonitor.Connect() if err != nil { t.Errorf("unexpected error: %v\n", err) } - domainEventRegisterInternalOrg := domainEventRegisterInternal - domainEventRegisterInternal = func( - mon *libvirtGoMonitorLinux, - callback *libvirt.DomainEventCallback, - fn func()) int { - return -1 - } - defer func() { - domainEventRegisterInternal = domainEventRegisterInternalOrg - }() - _, err = libvirtGoMonitor.Events() if err == nil { t.Fatalf("domain register error expected") } } -func TestLibvirtGoGetPollIntervalDefault(t *testing.T) { +type fakeLibvirtGoMonitorInternal struct { + virnConn libvirt.VirConnection + domain libvirt.VirDomain + connErr error + domainErr error + qemuErr error + closeErr error + retCode int + domainEventRetCode int + expectedResult string +} + +func (fake *fakeLibvirtGoMonitorInternal) newVirConnectionInternal(uri string) (libvirt.VirConnection, error) { + return fake.virnConn, fake.connErr +} - interval := getPollInterval() - expectedInterval, _ := time.ParseDuration("1s") +func (fake *fakeLibvirtGoMonitorInternal) lookupDomainByNameInternal(virConn *libvirt.VirConnection, domainName string) (libvirt.VirDomain, error) { + return fake.domain, fake.domainErr +} - if expectedInterval != interval { - t.Errorf("Unexpected value. Expected [%s] and got [%s]\n", expectedInterval, interval) - } +func (fake *fakeLibvirtGoMonitorInternal) qemuMonitorCommandInternal(domain *libvirt.VirDomain, cmd string) (string, error) { + return fake.expectedResult, fake.qemuErr } -func TestLibvirtGoGetPollIntervalDefaultInvalid(t *testing.T) { - os.Setenv(LibvirtGoEventsInterval, "invalid") - defer func() { - os.Unsetenv(LibvirtGoEventsInterval) - }() - interval := getPollInterval() - expectedInterval, _ := time.ParseDuration("1s") +func (fake *fakeLibvirtGoMonitorInternal) domainEventRegisterInternal(virConn *libvirt.VirConnection, domain *libvirt.VirDomain, callback *libvirt.DomainEventCallback, fn func()) int { + return fake.domainEventRetCode +} - if expectedInterval != interval { - t.Errorf("Unexpected value. Expected [%s] and got [%s]\n", expectedInterval, interval) - } +func (fake *fakeLibvirtGoMonitorInternal) eventRegisterDefaultImplInternal() int { + return fake.retCode +} +func (fake *fakeLibvirtGoMonitorInternal) closeConnectionInternal(virConn *libvirt.VirConnection) (int, error) { + return fake.retCode, fake.closeErr } -func TestLibvirtGoGetPollIntervalDefault5Second(t *testing.T) { - os.Setenv(LibvirtGoEventsInterval, "5s") - defer func() { - os.Unsetenv(LibvirtGoEventsInterval) - }() - interval := getPollInterval() - expectedInterval, _ := time.ParseDuration("5s") +func (fake *fakeLibvirtGoMonitorInternal) domainEventDeregisterInternal(virConn *libvirt.VirConnection, callbackID int) int { + return fake.retCode +} - if expectedInterval != interval { - t.Errorf("Unexpected value. Expected [%s] and got [%s]\n", expectedInterval, interval) - } +func (fake *fakeLibvirtGoMonitorInternal) eventRunDefaultImplInternal() int { + return fake.retCode } + +// type fakeDomainRegisterOK struct { +// wg *sync.WaitGroup +// expectedEvent *libvirt.DomainLifecycleEvent +// } + +// func (fake *fakeDomainRegisterOK) domainEventRegisterInternal( +// callback *libvirt.DomainEventCallback, fn func()) int { +// fake.wg.Add(1) +// go fake.simulateSendingEvents(*callback) +// return 0 +// } + +// func (fake *fakeDomainRegisterOK) simulateSendingEvents(callback libvirt.DomainEventCallback) { +// fmt.Println("Sending event...") +// callback(nil, nil, fake.expectedEvent, func() {}) +// fake.wg.Done() +// } From 20740c83790f7aee89cb081dc7d423e5e83712a6 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Thu, 20 Oct 2016 21:58:30 -0400 Subject: [PATCH 28/30] qmp/libvirtgo: fix events test --- qmp/libvirtgo_linux_test.go | 113 ++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 64 deletions(-) diff --git a/qmp/libvirtgo_linux_test.go b/qmp/libvirtgo_linux_test.go index 41e824d..1306024 100644 --- a/qmp/libvirtgo_linux_test.go +++ b/qmp/libvirtgo_linux_test.go @@ -16,6 +16,7 @@ package qmp import ( "errors" + "sync" "testing" libvirt "github.com/rgbkrk/libvirt-go" @@ -81,53 +82,46 @@ func TestLibvirtGoRunNoConnection(t *testing.T) { } } -// func TestLibvirtGoEventsDomainEventRegisterOK(t *testing.T) { -// libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") -// libvirtGoMonitor.eventDefaultImplProvider = &fakeEventDefaultImplProviderOK{} -// libvirtGoMonitor.connectionProvider = &fakeConnectionProvider{fail: false} -// libvirtGoMonitor.domainFinder = &fakeDomainFinder{fail: false} -// err := libvirtGoMonitor.Connect() -// if err != nil { -// t.Errorf("unexpected error: %v\n", err) -// } - -// var wg sync.WaitGroup -// expectedEvent := libvirt.DomainLifecycleEvent{ -// Event: libvirt.VIR_DOMAIN_EVENT_STARTED, -// Detail: 0, -// } - -// libvirtGoMonitor.domainRegister = &fakeDomainRegisterOK{ -// wg: &wg, -// expectedEvent: &expectedEvent, -// } - -// events, err := libvirtGoMonitor.Events() -// if err != nil { -// t.Errorf("unexpected error: %v\n", err) -// } -// var resultEvent Event -// go func(wg *sync.WaitGroup) { -// wg.Add(1) -// fmt.Println("Waiting for event....") -// for event := range events { -// resultEvent = event -// wg.Done() -// break -// } -// fmt.Println("done waiting....") -// }(&wg) - -// wg.Wait() - -// detailsEvent, found := resultEvent.Data["details"] -// if !found { -// t.Errorf("Expected at least one Event") -// } -// if expectedEvent != detailsEvent { -// t.Errorf("Unexpected event. Expected %#v and got %#v\n", expectedEvent, detailsEvent) -// } -// } +func TestLibvirtGoEventsOK(t *testing.T) { + libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") + var wg sync.WaitGroup + expectedEvent := libvirt.DomainLifecycleEvent{ + Event: libvirt.VIR_DOMAIN_EVENT_STARTED, + Detail: 0, + } + libvirtGoMonitor.libvirtGoMonitorInternal = &fakeLibvirtGoMonitorInternal{ + expectedEvent: expectedEvent, + } + + err := libvirtGoMonitor.Connect() + if err != nil { + t.Errorf("unexpected error: %v\n", err) + } + + events, err := libvirtGoMonitor.Events() + if err != nil { + t.Errorf("unexpected error: %v\n", err) + } + var resultEvent Event + wg.Add(1) + go func(wg *sync.WaitGroup) { + for event := range events { + resultEvent = event + wg.Done() + break + } + }(&wg) + + wg.Wait() + + detailsEvent, found := resultEvent.Data["details"] + if !found { + t.Errorf("Expected at least one Event") + } + if expectedEvent != detailsEvent { + t.Errorf("Unexpected event. Expected %#v and got %#v\n", expectedEvent, detailsEvent) + } +} func TestLibvirtGoEventsNoConnection(t *testing.T) { libvirtGoMonitor := NewLibvirtGoMonitor("testURI", "testDomain") @@ -164,6 +158,8 @@ type fakeLibvirtGoMonitorInternal struct { retCode int domainEventRetCode int expectedResult string + wg *sync.WaitGroup + expectedEvent libvirt.DomainLifecycleEvent } func (fake *fakeLibvirtGoMonitorInternal) newVirConnectionInternal(uri string) (libvirt.VirConnection, error) { @@ -179,6 +175,9 @@ func (fake *fakeLibvirtGoMonitorInternal) qemuMonitorCommandInternal(domain *lib } func (fake *fakeLibvirtGoMonitorInternal) domainEventRegisterInternal(virConn *libvirt.VirConnection, domain *libvirt.VirDomain, callback *libvirt.DomainEventCallback, fn func()) int { + if fake.domainEventRetCode != -1 { + go fake.simulateSendingEvents(*callback) + } return fake.domainEventRetCode } @@ -198,20 +197,6 @@ func (fake *fakeLibvirtGoMonitorInternal) eventRunDefaultImplInternal() int { return fake.retCode } -// type fakeDomainRegisterOK struct { -// wg *sync.WaitGroup -// expectedEvent *libvirt.DomainLifecycleEvent -// } - -// func (fake *fakeDomainRegisterOK) domainEventRegisterInternal( -// callback *libvirt.DomainEventCallback, fn func()) int { -// fake.wg.Add(1) -// go fake.simulateSendingEvents(*callback) -// return 0 -// } - -// func (fake *fakeDomainRegisterOK) simulateSendingEvents(callback libvirt.DomainEventCallback) { -// fmt.Println("Sending event...") -// callback(nil, nil, fake.expectedEvent, func() {}) -// fake.wg.Done() -// } +func (fake *fakeLibvirtGoMonitorInternal) simulateSendingEvents(callback libvirt.DomainEventCallback) { + callback(nil, nil, fake.expectedEvent, func() {}) +} From 42ad391f5229e273656fe0eb1cbf9c452cef6508 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Mon, 24 Oct 2016 10:13:48 -0400 Subject: [PATCH 29/30] qmp/libvirtgo: add events loop interval parameter --- qmp/libvirtgo_linux.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/qmp/libvirtgo_linux.go b/qmp/libvirtgo_linux.go index 6d97546..3efa6d1 100644 --- a/qmp/libvirtgo_linux.go +++ b/qmp/libvirtgo_linux.go @@ -44,6 +44,8 @@ type LibvirtGoMonitorLinux struct { var errNoLibvirtConnection = errors.New("need a established connection to proceed") +const eventsLoopIntervalDefault = 1 * time.Second + type libvirtGoMonitorInternal interface { lookupDomainByNameInternal(virConn *libvirt.VirConnection, domainName string) (libvirt.VirDomain, error) newVirConnectionInternal(uri string) (libvirt.VirConnection, error) @@ -60,18 +62,29 @@ type libvirtGoMonitorInternalImpl struct { } // NewLibvirtGoMonitor configures a connection to the provided hypervisor -// and domain. +// and domain. Defaults events loop interval to 1 second. // An error is returned if the provided libvirt connection URI is invalid. // // Hypervisor URIs may be local or remote, e.g., // qemu:///system // qemu+ssh://libvirt@example.com/system func NewLibvirtGoMonitor(uri, domain string) *LibvirtGoMonitorLinux { + return NewLibvirtGoMonitorEventsLoopInterval(uri, domain, eventsLoopIntervalDefault) +} + +// NewLibvirtGoMonitorEventsLoopInterval configures a connection to the provided hypervisor +// and domain. Sets events loop interval to the Duration provided. +// An error is returned if the provided libvirt connection URI is invalid. +// +// Hypervisor URIs may be local or remote, e.g., +// qemu:///system +// qemu+ssh://libvirt@example.com/system +func NewLibvirtGoMonitorEventsLoopInterval(uri, domain string, eventsLoopInterval time.Duration) *LibvirtGoMonitorLinux { return &LibvirtGoMonitorLinux{ uri: uri, domainName: domain, libvirtGoMonitorInternal: &libvirtGoMonitorInternalImpl{}, - eventsLoopInterval: 1 * time.Second, + eventsLoopInterval: eventsLoopIntervalDefault, } } @@ -143,7 +156,7 @@ func (mon *LibvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { return nil, errNoLibvirtConnection } - result, err := mon.qemuMonitorCommandInternal(mon.domain, string(cmd)) + result, err := mon.libvirtGoMonitorInternal.qemuMonitorCommandInternal(mon.domain, string(cmd)) if err != nil { return nil, err } @@ -151,13 +164,6 @@ func (mon *LibvirtGoMonitorLinux) Run(cmd []byte) ([]byte, error) { return []byte(result), nil } -var qemuMonitorCommandInternal = func( - domain *libvirt.VirDomain, cmd string) (string, error) { - return domain.QemuMonitorCommand( - libvirt.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT, - cmd) -} - // Events streams QEMU QMP Events. // If a problem is encountered setting up the event monitor connection // an error will be returned. Errors encountered during streaming will From aa14b77ee9bfcab55b34911556348e9812660149 Mon Sep 17 00:00:00 2001 From: Roberto J Rojas Date: Mon, 24 Oct 2016 12:43:46 -0400 Subject: [PATCH 30/30] qmp/libvirtgo: add libvirtgo to README --- qmp/README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/qmp/README.md b/qmp/README.md index 729280b..1a8c40e 100644 --- a/qmp/README.md +++ b/qmp/README.md @@ -7,7 +7,7 @@ Package `qmp` enables interaction with QEMU instances via the QEMU Machine Proto ### Libvirt -If your environment is managed by Libvirt, QMP interaction must be proxied through the Libvirt daemon. This can be be done through two available drivers: +If your environment is managed by Libvirt, QMP interaction must be proxied through the Libvirt daemon. This can be done through three available drivers: #### RPC @@ -19,6 +19,15 @@ conn, err := net.DialTimeout("tcp", "192.168.1.1:16509", 2*time.Second) monitor := libvirtrpc.New("stage-lb-1", conn) ``` +### Libvirt-go + +This monitor provides communication with the Libvirt daemon using the [libvirt-go](https://github.com/rgbkrk/libvirt-go) package as its internal implementation. +At the moment, libvirt-go only supports `Linux`. + +```go +monitor, err := qmp.NewLibvirtGoMonitor("qemu:///system", "centos7") +``` + #### virsh A connection to the monitor socket is provided by proxing requests through the `virsh` executable.