Giter Club home page Giter Club logo

go-libvirt's Introduction

libvirt GoDoc Build Status Report Card

Package go-libvirt provides a pure Go interface for interacting with libvirt.

Rather than using libvirt's C bindings, this package makes use of libvirt's RPC interface, as documented here. Connections to the libvirt server may be local, or remote. RPC packets are encoded using the XDR standard as defined by RFC 4506.

libvirt's RPC interface is quite extensive, and changes from one version to the next, so this project uses a pair of code generators to build the go bindings. The code generators should be run whenever you want to build go-libvirt for a new version of libvirt. See the next section for directions on re-generating go-libvirt.

Pull requests are welcome!

Feel free to join us in #go-libvirt on libera chat if you'd like to discuss the project.

Running the Code Generators

The code generator doesn't run automatically when you build go-libvirt. It's meant to be run manually any time you change the version of libvirt you're using. When you download go-libvirt it will come with generated files corresponding to a particular version of libvirt. You can use the library as-is, but the generated code may be missing libvirt functions, if you're using a newer version of libvirt, or it may have extra functions that will return 'unimplemented' errors if you try to call them. If this is a problem, you should re-run the code generator. To do this, follow these steps:

  • First, download a copy of the libvirt sources corresponding to the version you want to use.
  • Change directories into where you've unpacked your distribution of libvirt.
  • The second step depends on the version of libvirt you'd like to build against. It's not necessary to actually build libvirt, but it is necessary to run libvirt's "configure" step because it generates required files.
    • For libvirt < v6.7.0:
      • $ mkdir build; cd build
      • $ ../autogen.sh
    • For libvirt >= v6.7.0:
      • $ meson setup build
  • Finally, set the environment variable LIBVIRT_SOURCE to the directory you put libvirt into, and run go generate ./... from the go-libvirt directory. This runs both of the go-libvirt's code generators.

How to Use This Library

Once you've vendored go-libvirt into your project, you'll probably want to call some libvirt functions. There's some example code below showing how to connect to libvirt and make one such call, but once you get past the introduction you'll next want to call some other libvirt functions. How do you find them?

Start with the libvirt API reference. Let's say you want to gracefully shutdown a VM, and after reading through the libvirt docs you determine that virDomainShutdown() is the function you want to call to do that. Where's that function in go-libvirt? We transform the names slightly when building the go bindings. There's no need for a global prefix like "vir" in Go, since all our functions are inside the package namespace, so we drop it. That means the Go function for virDomainShutdown() is just DomainShutdown(), and sure enough, you can find the Go function DomainShutdown() in libvirt.gen.go, with parameters and return values equivalent to those documented in the API reference.

Suppose you then decide you need more control over your shutdown, so you switch over to virDomainShutdownFlags(). As its name suggests, this function takes a flag parameter which has possible values specified in an enum called virDomainShutdownFlagValues. Flag types like this are a little tricky for the code generator, because the C functions just take an integer type - only the libvirt documentation actually ties the flags to the enum types. In most cases though we're able to generate a wrapper function with a distinct flag type, making it easier for Go tooling to suggest possible flag values while you're working. Checking the documentation for this function:

godoc github.com/digitalocean/go-libvirt DomainShutdownFlags

returns this:

func (l *Libvirt) DomainShutdownFlags(Dom Domain, Flags DomainShutdownFlagValues) (err error)

If you want to see the possible flag values, godoc can help again:

$ godoc github.com/digitalocean/go-libvirt DomainShutdownFlagValues

type DomainShutdownFlagValues int32
    DomainShutdownFlagValues as declared in libvirt/libvirt-domain.h:1121

const (
    DomainShutdownDefault      DomainShutdownFlagValues = iota
    DomainShutdownAcpiPowerBtn DomainShutdownFlagValues = 1
    DomainShutdownGuestAgent   DomainShutdownFlagValues = 2
    DomainShutdownInitctl      DomainShutdownFlagValues = 4
    DomainShutdownSignal       DomainShutdownFlagValues = 8
    DomainShutdownParavirt     DomainShutdownFlagValues = 16
)
    DomainShutdownFlagValues enumeration from libvirt/libvirt-domain.h:1121

One other suggestion: most of the code in go-libvirt is now generated, but a few hand-written routines still exist in libvirt.go, and wrap calls to the generated code with slightly different parameters or return values. We suggest avoiding these hand-written routines and calling the generated routines in libvirt.gen.go instead. Over time these handwritten routines will be removed from go-libvirt.

Warning

While these package are reasonably well-tested and have seen some use inside of DigitalOcean, there may be subtle bugs which could cause the packages to act in unexpected ways. Use at your own risk!

In addition, the API is not considered stable at this time. If you would like to include package libvirt in a project, we highly recommend vendoring it into your project.

Example

package main

import (
	"fmt"
	"log"
	"net/url"

	"github.com/digitalocean/go-libvirt"
)

func main() {
	uri, _ := url.Parse(string(libvirt.QEMUSystem))
	l, err := libvirt.ConnectToURI(uri)
	if err != nil {
		log.Fatalf("failed to connect: %v", err)
	}

	v, err := l.ConnectGetLibVersion()
	if err != nil {
		log.Fatalf("failed to retrieve libvirt version: %v", err)
	}
	fmt.Println("Version:", v)

	flags := libvirt.ConnectListDomainsActive | libvirt.ConnectListDomainsInactive
	domains, _, err := l.ConnectListAllDomains(1, flags)
	if err != nil {
		log.Fatalf("failed to retrieve domains: %v", err)
	}

	fmt.Println("ID\tName\t\tUUID")
	fmt.Printf("--------------------------------------------------------\n")
	for _, d := range domains {
		fmt.Printf("%d\t%s\t%x\n", d.ID, d.Name, d.UUID)
	}

	if err = l.Disconnect(); err != nil {
		log.Fatalf("failed to disconnect: %v", err)
	}
}
Version: 1.3.4
ID	Name		UUID
--------------------------------------------------------
1	Test-1		dc329f87d4de47198cfd2e21c6105b01
2	Test-2		dc229f87d4de47198cfd2e21c6105b01

Example (Connect to libvirt via TLS over TCP)

package main

import (
        "crypto/tls"
        "crypto/x509"

        "fmt"
        "io/ioutil"
        "log"

        "github.com/digitalocean/go-libvirt"
        "github.com/digitalocean/go-libvirt/socket/dialers"
)

func main() {
        // This dials libvirt on the local machine
        // It connects to libvirt via TLS over TCP
        // To connect to a remote machine, you need to have the ca/cert/key of it.
        // The private key is at ~/.pki/libvirt/clientkey.pem
        // or /etc/pki/libvirt/private/clientkey.pem
        // The Client Cert is at ~/.pki/libvirt/clientcert.pem
        // or /etc/pki/libvirt/clientcert.pem
        // The CA Cert is at ~/.pki/libvirt/cacert.pem
        // or /etc/pki/CA/cacert.pem

        // Use host name or IP which is valid in certificate
        addr := "10.10.10.10"

        l := libvirt.NewWithDialer(dialers.NewTLS(addr))
        if err := l.Connect(); err != nil {
                log.Fatalf("failed to connect: %v", err)
        }

        v, err := l.Version()
        if err != nil {
                log.Fatalf("failed to retrieve libvirt version: %v", err)
        }
        fmt.Println("Version:", v)

        // Return both running and stopped VMs
        flags := libvirt.ConnectListDomainsActive | libvirt.ConnectListDomainsInactive
        domains, _, err := l.ConnectListAllDomains(1, flags)
        if err != nil {
                log.Fatalf("failed to retrieve domains: %v", err)
        }

        fmt.Println("ID\tName\t\tUUID")
        fmt.Println("--------------------------------------------------------")
        for _, d := range domains {
                fmt.Printf("%d\t%s\t%x\n", d.ID, d.Name, d.UUID)
        }

        if err := l.Disconnect(); err != nil {
                log.Fatalf("failed to disconnect: %v", err)
        }
}

Running the Integration Tests

GitHub actions workflows are defined in .github/workflows and can be triggered manually in the GitHub UI after pushing a branch. There are not currently convenient scripts for setting up and running integration tests locally, but installing libvirt and defining only the artifacts described by the files in testdata should be sufficient to be able to run the integration test file against.

go-libvirt's People

Contributors

ahothan avatar benlemasurier avatar berrange avatar cdrage avatar cfergeau avatar clarkwalcott avatar connorkuehl avatar dependabot[bot] avatar dmacvicar avatar dvob avatar jennigriesmann avatar kat-co avatar katrinae avatar lebauce avatar mamercad avatar mdlayher avatar mkoppmann avatar mpontillo avatar nemca avatar paco0x avatar polachok avatar r-medina avatar sam-github avatar simar7 avatar trapgate avatar ustcweizhou avatar weizhouapache avatar yoriksar avatar yunginnanet avatar zaneb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-libvirt's Issues

is there any way to extract error code?

When calling a RPC method of libvirt, the returned error contains an error code used to indicate the error types, which is helpful.

I found there's no way to extract the error code from method call, cause the error returned in this package only contains the error message, which is not friendly for error cause tracing.

Error handling in this package(https://github.com/digitalocean/go-libvirt/blob/master/rpc.go#L355):

// decodeError extracts an error message from the provider buffer.
func decodeError(buf []byte) error {
    var e libvirtError

    dec := xdr.NewDecoder(bytes.NewReader(buf))
    _, err := dec.Decode(&e)
    if err != nil {
        return err
    }

    if strings.Contains(e.Message, "unknown procedure") {
        return ErrUnsupported
    }

    return errors.New(e.Message)
}

As the code above shows, the actual error returned was constructed by only the e.Message, e.Code was dropped.

I want to write a code to lookup domain first, then compare the error code to determine whether to define a new domain, something like this:

dom, err := l.DomainLookupByName("test")
if err != nil {
    if libvirtError, ok := err.(libvirt.Error); ok {
        if libvirtError.Code == libvirt.ERR_NO_DOMAIN {
            ...
        }
    }
}

Is there any plan to support error code extraction?

cannot acquire state change lock

failed to get DomainBlockStats: Timed out during operation: cannot acquire state change lock (held by remoteDispatchDomainBlockStats)

my code:

`

isActive, err := l.DomainIsActive(*domain)
var rRdReq, rRdBytes, rWrReq, rWrBytes int64
if isActive == 1 {
rRdReq, rRdBytes, rWrReq, rWrBytes, _, err = l.DomainBlockStats(*domain, disk.Target.Device) }`

	if err != nil {
		log.Fatalf("failed to get DomainBlockStats: %v", err)
		return err
	}

`

LookupDomainByUUID

I want to add LookupDomainByUUID as i see payload.UUID needs to be 16 bytes long.
But when i'm convert string uuid to payload.UUID and try to get domain i have error

2017/04/03 14:58:44 failed to retrieve domain info: Domain not found: no domain with matching uuid '37653332-3532-3634-3135-623931383038'
exit status 1

But i'm provide uuid 7e32526415b918081a6b000008f85d7f

ci: remove scripts/golint.sh, replace with built-in golint flag

golint has a flag that causes it to exit with non-zero status, rendering our CI build script for this purpose obsolete.

Replace with golint -set_exit_status ./... in CI.

This is an easy Hacktoberfest contribution, so I'll leave this unfixed for a few days. Feel free to submit a PR to fix this and work toward that t-shirt!

StorageVolUpload corrupts image

I try to upload a Ubuntu image to libvirt via StorageVolUpload. If I upload the image from a file it does work but if I upload it from a http stream the image gets corrupted.
Strangely it does work from http stream as well if I use a big buffered reader in between.
Does anyone have an idea whats going on here?

Here are the steps to reproduce the behaviour:

  • download the image
curl -O https://cloud-images.ubuntu.com/minimal/releases/disco/release-20190605/ubuntu-19.04-minimal-cloudimg-amd64.img
curl -o main.go https://github.com/digitalocean/go-libvirt/files/3311212/main.txt
  • run test
go run main.go
  • compare downloaded files in /var/lib/libvirt/images (or where ever your default pool stores the files)
sha256sum /var/lib/libvirt/images/image_from_*

Question regarding to Error Handling

I see that in the rpc package, there is a private libvirtError struct, which contains the Code of the error. I would want to access it, to distinguish connection problems from errors like "domain does not exist". I find it in combination with helper functions like e.g. IsNotFound(err error) bool pretty helpful.

Are there some ideas floating around on how proper error handling is done? Would it make sense to make LibvirtError public and add the error code constants to the project?

connect: resource temporarily unavailable

log: 2020-05-09T00:57:19Z E! failed to dial libvirt: dial unix /var/run/libvirt/libvirt-sock: connect: resource temporarily unavailable
2020-05-09T00:57:19Z E! failed to retry dial libvirt: dial unix /var/run/libvirt/libvirt-sock: connect: resource temporarily unavailable

Tunnel via SSH?

Since #140 was implemented and New() deprecated, how is one supposed to do a qemu+ssh connection?

I was under the impression this should work:

package main

import (
	"fmt"

	"github.com/digitalocean/go-libvirt"
	`github.com/digitalocean/go-libvirt/socket`
	`github.com/digitalocean/go-libvirt/socket/dialers`
)

func main() {

	var conn *libvirt.Libvirt
	var dialer socket.Dialer
	var err error

	dialer = dialers.NewRemote("<IP_ADDRESS_HERE>", dialers.UsePort("22"))

	conn = libvirt.NewWithDialer(dialer)
	if err = conn.ConnectToURI("qemu+ssh://user@<IP_ADDRESS_HERE>/system"); err != nil {
		fmt.Println(err)
		return
	}
}

But it just hangs.

I find it hard to believe SSH tunneling was broken by oversight considering it's treated as a first-class connection URI upstream.

TLS connections hang after PR #86

Uncertain exactly why this occurs, but between the 6/26 commit and 7/15 commit (PR #86) the TLS connectivity no longer works. Commit hashes are shown in the go.mod file below if that assists.

Please let me know if I need to alter behavior of the code. For now, I'll lock into the 6/26 code.

My environment:

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.2 LTS"
$ virsh version
Compiled against library: libvirt 4.0.0
Using library: libvirt 4.0.0
Using API: QEMU 4.0.0
Running hypervisor: QEMU 2.11.1

Bad run sample output (the hang lasts for hours - we left town today and it was still hanging when I returned):

$ go run main.go
TCP connection to 'odin:16514' established
Libvirt client created
^Csignal: interrupt

Good run sample output:

$ go run main.go
TCP connection to 'odin:16514' established
Libvirt client created
Connected to libvirt
Version: 4.0.0
ID	Name		UUID
--------------------------------------------------------
17	vm-852ea340-3a46-471e-7b33-16b30df9bcba	852ea3403a46471e7b3316b30df9bcba

go.mod file:

module tls

go 1.12

// GOOD
require github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437

// BAD
//require github.com/digitalocean/go-libvirt v0.0.0-20190715144809-7b622097a793

main.go sample code (sorry, a bit long and hard-coded to locations on my filesystem):

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"log"

	libvirt "github.com/digitalocean/go-libvirt"
)

const connectstring = "odin:16514"
const keyfile = "/home/rob/libvirt-cert/clientkey.pem"
const certfile = "/home/rob/libvirt-cert/clientcert.pem"
const cafile = "/home/rob/libvirt-cert/cacert.pem"

func main() {
	keyfilexml, err := ioutil.ReadFile(keyfile)
	if err != nil {
		panic(err)
	}

	certfilexml, err := ioutil.ReadFile(certfile)
	if err != nil {
		panic(err)
	}

	cafilexml, err := ioutil.ReadFile(cafile)
	if err != nil {
		panic(err)
	}

	cert, err := tls.X509KeyPair([]byte(certfilexml), []byte(keyfilexml))
	if err != nil {
		panic(err)
	}

	roots := x509.NewCertPool()
	roots.AppendCertsFromPEM([]byte(cafilexml))

	cfg := &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      roots,
	}

	conn, err := tls.Dial("tcp", connectstring, cfg)
	defer conn.Close()
	if err != nil {
		panic(err)
	}
	fmt.Printf("TCP connection to '%s' established\n", connectstring)

	client := libvirt.New(conn)
	fmt.Printf("Libvirt client created\n")
	err = client.Connect()
	defer client.Disconnect()
	if err != nil {
		panic(err)
	}
	fmt.Printf("Connected to libvirt\n")

	v, err := client.Version()
	if err != nil {
		log.Fatalf("failed to retrieve libvirt version: %v", err)
	}
	fmt.Println("Version:", v)

	domains, err := client.Domains()
	if err != nil {
		log.Fatalf("failed to retrieve domains: %v", err)
	}

	fmt.Println("ID\tName\t\tUUID")
	fmt.Printf("--------------------------------------------------------\n")
	for _, d := range domains {
		fmt.Printf("%d\t%s\t%x\n", d.ID, d.Name, d.UUID)
	}

	if err := client.Disconnect(); err != nil {
		log.Fatalf("failed to disconnect: %v", err)
	}
}

Disk usage shown by ConnectGetAllDomainStats is incorrect

Hi, I've been playing around with this library all day and I can't seem to get a correct read on the disk usage of a VM.

From ConnectGetAllDomainStats I get the following for a particular VM:

block.0.allocation: 1302527488
block.0.capacity: 8589934592
block.0.physical: 1285488640

And with virsh domstats I get the same info (since the source might be the same)

But inside the VM, with df, I get the following:

Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/vda1        8377344 2203900   6173444  27% /

When the VM is created, the data from ConnectGetAllDomainStats seems to be close to correct, or correct, but it seems to never update, since after creating a 1G file with fallocate in the VM, it still shows the old values.

I'm using libvirt v4.5.0

Is there a way of getting the real disk usage of a VM with this library? I've been hunting around the documentation but no function seems to provide this.

Support for QEMU Agent Command

Hi! I found there is QEMUProcDomainAgentCommand = 3 have been defined but have no methods implement, is there any plans? thanks here!

Support for connection URIs

As part of integrating go-libvirt into terraform-provider-libvirt, I have implemented support for connection URIs, including the tcp, tls, unix and ssh transports.

I think this could make go-libvirt easier to use for those using the original bindings, and probably belongs outside of the terraform provider.

Is there interest in having this functionality in go-libvirt?. If that is the case, I could merge the type with go-libvirt and work on a patch.

Looking forward for your feedback. //cc @trapgate

Define new domain

How plans define the domain?
Can I implement methods DomainDefineXML(xml []byte) error and DomainDefineXMLFlags(xml []byte, flags DomainDefineFlags) error like libvirt documentation

Migrate Operations missing

In official api document, there are DomainMigrate operations.

However, in this repository they are missing. There are only some basic operations such as DomainMigratePrepare3, DomainMigratePerform3...

Is there anyone can help me finish the migrate operations ?

Document minimum (or maximum) supported toolchain requirements

Newer versions of goyacc try to optimize the representation of certain things. If using the latest goyacc, one of the generated files will be updated to use thinner ints which breaks the build like so:

diff --git a/internal/lvgen/y.go b/internal/lvgen/y.go
index 1ea031b..389c36a 100644
--- a/internal/lvgen/y.go
+++ b/internal/lvgen/y.go
@@ -118,7 +118,7 @@ const yyInitialStackSize = 16
 //line sunrpc.y:279
 
 //line yacctab:1
-var yyExca = [...]int{
+var yyExca = [...]int8{
        -1, 1,
        1, -1,
        -2, 0,
@@ -128,7 +128,7 @@ const yyPrivate = 57344
$ go generate ./...
  processing libvirt.yml done.
# github.com/digitalocean/go-libvirt/internal/lvgen
./lvlexer.go:75:12: cannot use yyTok2[tokType - yyPrivate] (type int8) as type int in assignment
internal/lvgen/lv-gen.go:29: running "go": exit status 2

For now, I've installed goyacc@c6776771dde7a49828feb54fd8be11695a32b558 for local development.

goyacc seems to perform this optimization since commit 59f1f2c5a8fd844a6cc55e9a773a7003bd8e5480.


edit: the following components could be addressed:

  • goyacc
  • go/go-get/go-install (the CI seems to be pinned to 1.16.2 and uses go-get, as does ./scripts/gen-consts.sh; go-get is deprecated in Go 1.17)

ci: add integration tests

We could really use integration testing to validate request parameters and multiple versions of libvirt.

libvirttest.MockLibvirt does not implement net.Conn

Trying to build github.com/digitalocean/go-qemu with the current snapshot of this library results in the following compilation error:

github.com/digitalocean/go-qemu/qmp/raw
# github.com/digitalocean/go-qemu/qmp [github.com/digitalocean/go-qemu/qmp.test]
src/github.com/digitalocean/go-qemu/qmp/rpc_test.go:76:38: cannot use conn (variable of type *libvirttest.MockLibvirt) as type net.Conn in argument to NewLibvirtRPCMonitor:
	*libvirttest.MockLibvirt does not implement net.Conn (missing Close method)
src/github.com/digitalocean/go-qemu/qmp/rpc_test.go:123:38: cannot use conn (variable of type *libvirttest.MockLibvirt) as type net.Conn in argument to NewLibvirtRPCMonitor:
	*libvirttest.MockLibvirt does not implement net.Conn (missing Close method)
src/github.com/digitalocean/go-qemu/qmp/rpc_test.go:133:38: cannot use conn (variable of type *libvirttest.MockLibvirt) as type net.Conn in argument to NewLibvirtRPCMonitor:
	*libvirttest.MockLibvirt does not implement net.Conn (missing Close method)
src/github.com/digitalocean/go-qemu/qmp/rpc_test.go:143:38: cannot use conn (variable of type *libvirttest.MockLibvirt) as type net.Conn in argument to NewLibvirtRPCMonitor:
	*libvirttest.MockLibvirt does not implement net.Conn (missing Close method)
src/github.com/digitalocean/go-qemu/qmp/rpc_test.go:176:38: cannot use conn (variable of type *libvirttest.MockLibvirt) as type net.Conn in argument to NewLibvirtRPCMonitor:
	*libvirttest.MockLibvirt does not implement net.Conn (missing Close method)
# github.com/digitalocean/go-qemu/hypervisor [github.com/digitalocean/go-qemu/hypervisor.test]
src/github.com/digitalocean/go-qemu/hypervisor/rpc_test.go:27:9: cannot use m (variable of type *libvirttest.MockLibvirt) as type net.Conn in return statement:
	*libvirttest.MockLibvirt does not implement net.Conn (missing Close method)

I think it's correct to file the issue here, rather than against go-qemu. (If not, please feel free to move it.)

Support polkit authentication

Currently there is no way to use these bindings with a libvirtd that is configured to use the polkit authentication method.

To fix this issue, a simple call to AuthPolkit() before opening the connection should be enough

Up for PR's?

Will anyone be allowed to do PR to this repo? We have used libvirt with rpc for a while (better than creating and using binding) but in a different language (Rust) but thought about bringing it to Go and then I saw this and thought it would be cool to just contribute instead of doing the same thing

libvirt stream support

Does you have any plans to implement libvirt streams ? They are useful for volume upload and volume upload.

Allow to specify libvirt driver URI to connect to

First of all: I really appreciate all your efforts to get a libvirt integration without C bindings!

Currently the Connect() error method uses the hardcoded libvirt driver URI qemu:///system which is kind of a showstopper for projects where I'd like to use this library.

After playing around a bit with the code I could not notice any problem with adding an overload like

ConnectToURI(uri string) error that allows for instance to use the mock driver test:///default or other libvirt drivers.

I'll open a PR and would be happy about any feedback!

Kind regards
Peter

Libvirt locks on `getResponse()`

Hi,

I have some goroutines:

  • One for listening and processing domain lifecycle events. Call DomainGetState for each domain from event;
  • Multiple for handling start/stop VM's and wait until state become to required sequentially call DomainGetState;
  • One for collecting and processing metrics for every domains. Use DomainBlockStats, DomainInterfaceStats and etc. function for collecting metrics.

I faced with an next issue: sometimes, when I request to start or stop multiple domain at once, all those goroutines is stuck. I try to dig into this issue and find out that all goroutines is blocked on digitalocean/go-libvirt.Libvirt.getResponse() because cannot read message from channel. Unfortunately this is a floating bug and I cannot find how to reproduce it.

For now I use this workaround:

const getResponseTimeout = 5 * time.Second

func (l *Libvirt) getResponse(c chan response) (response, error) {
	timer := time.NewTimer(getResponseTimeout)
	defer timer.Stop()

	var resp response
	select {
	case <-timer.C:
		return response{}, fmt.Errorf("cannot get response from libvirt within %s", getResponseTimeout)
	case resp = <-c:
	}
	if resp.Status == StatusError {
		return resp, decodeError(resp.Payload)
	}

	return resp, nil
}

Anyone have some thoughts why this happens?

libvirt: reporting the current state of the domain

Hello,

Awesome work on this new project so far! I was wondering if it would be possible to add the the current domain state in the info that can be accessed? I'm willing to work on this but just need to know where to start poking.

It seems like that we could use the REMOTE_PROC_DOMAIN_GET_STATE = 212 remote event as highlighted in this doc.

Event refactor breaks github.com/digitalocean/go-qemu/qmp

The package github.com/digitalocean/go-qemu/qmp is no longer compilable and gives the error:

# github.com/digitalocean/go-qemu/qmp
../../digitalocean/go-qemu/qmp/rpc.go:62:22: rpc.l.Events undefined (type *libvirt.Libvirt has no field or method Events)

I suspect its since this commit e2a69bc

NetworkUpdate: Operation not supported: can't update 'ip' section of network 'default'

I'm not entirely certain if this is a bug in my code or something is up with the NetworkUpdate call from this library.

I'm writing some code that will ultimately be installed into a VM in order to manage multiple VM's. The base OS layer does not have any libvirt dependencies, and this is the best solution for my needs. (So thanks for that!)

When I do a NetworkUpdate to add an IP>DHCP>Host entry, an error is generated: Operation not supported: can't update 'ip' section of network 'default'.

What I've done:

  • verified the project is on the latest code.
  • extracted the specific code, and it fails.
  • used TLS connections (thinking there may be a different socket file to utilize)
  • implemented with the libvirt/libvirt-go library, and it works.
  • verified this also works from the virsh command-line.

Therefore, I think I've got a valid setup but haven't been able to identify what may be wrong (in either code base).

Running on Ubuntu 18.04.2.

$ virsh version
Compiled against library: libvirt 4.0.0
Using library: libvirt 4.0.0
Using API: QEMU 4.0.0
Running hypervisor: QEMU 2.11.1

Using this library (and it fails):

const networkDhcpXML = "<host name='vm-8029d8c8-ee6c-45b9-6476-3ba7381c3cb5' ip='192.168.123.7'/>"
const networkName = "default"
const socket = "/var/run/libvirt/libvirt-sock"

func main() {
	conn, err := net.DialTimeout("unix", socket, 2*time.Second)
	defer conn.Close()
	if err != nil {
		panic(err)
	}
	fmt.Printf("Connected to '%s'\n", socket)

	client := libvirt.New(conn)
	err = client.Connect()
	if err != nil {
		panic(err)
	}
	fmt.Printf("Connected to libvirt\n")

	net, err := client.NetworkLookupByName(networkName)
	if err != nil {
		panic(err)
	}
	fmt.Printf("net = %v\n", net)

	xmlDesc, err := client.NetworkGetXMLDesc(net, 0)
	if err != nil {
		panic(err)
	}
	fmt.Printf("networkXMLDesc=%s\n", xmlDesc)

	cmd := uint32(libvirt.NetworkUpdateCommandAddLast)
	section := uint32(libvirt.NetworkSectionIPDhcpHost)
	//netflags := libvirt.NetworkUpdateAffectLive | libvirt.NetworkUpdateAffectConfig
	fmt.Printf("NetworkUpdate(%v, %d, %d, -1, %s, %d)\n", net, cmd, section, networkDhcpXML, 0)
	err = client.NetworkUpdate(net, cmd, section, -1, networkDhcpXML, 0)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Setup network DHCP\n")
}

Using the libvirt/libvirt-go library (and it works):

const networkDhcpXML = "<host name='vm-8029d8c8-ee6c-45b9-6476-3ba7381c3cb5' ip='192.168.123.7'/>"
const networkName = "default"

func main() {
	conn, err := libvirt.NewConnect("qemu:///system")
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	net, err := conn.LookupNetworkByName(networkName)
	defer net.Free()
	if err != nil {
		panic(err)
	}
	fmt.Printf("net = %v\n", net)

	xmlDesc, err := net.GetXMLDesc(0)
	if err != nil {
		panic(err)
	}
	fmt.Printf("networkXMLDesc=%s\n", xmlDesc)

	cmd := libvirt.NETWORK_UPDATE_COMMAND_ADD_LAST
	section := libvirt.NETWORK_SECTION_IP_DHCP_HOST
	//netflags := libvirt.NetworkUpdateAffectLive | libvirt.NetworkUpdateAffectConfig
	fmt.Printf("NetworkUpdate(%v, %d, %d, -1, %s, %d)\n", net, cmd, section, networkDhcpXML, 0)
	err = net.Update(cmd, section, -1, networkDhcpXML, 0)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Setup network DHCP\n")
}

Using virsh:

$ virsh net-update default add --section ip-dhcp-host --xml "<host name='vm-8029d8c8-ee6c-45b9-6476-3ba7381c3cb5' ip='192.168.123.7'/>"
Updated network default live state

If it's useful, the specific usage is in the libvirtManager here. It's been rejiggered into the samples to work out whatever issue is occurring.

Thanks for any information you may provide!

failed to get DomainInterfaceStats: invalid argument: 'tap2c714c60-c8' is not a known interface

libvirt version: 4.0.0
go-libvrit version: v0.0.0

code:
`
// Report network interface statistics. 网络

for _, iface := range libvirtSchema.Devices.Interfaces {
	if iface.Target.Device == "" {
		continue
	}



	isActive, err := l.DomainIsActive(*domain)
	if err != nil {
		log.Printf("E! [domain:%s] failed to get domain active state for interface: %s", domain.Name, err.Error())
		return err
	}



	var rRxBytes, rRxPackets, rRxErrs, rRxDrop, rTxBytes, rTxPackets, rTxErrs, rTxDrop int64


	if isActive == 1 {
		rRxBytes, rRxPackets, rRxErrs, rRxDrop, rTxBytes, rTxPackets, rTxErrs, rTxDrop, err = l.DomainInterfaceStats(*domain, iface.Target.Device)


		if err != nil {
			log.Printf("E! [domain:%s] failed to get DomainInterfaceStats: %s", domain.Name, err.Error())
			return err
		}`	

Inability to subscribe `metdata-change`, `device-added` and `device-removed` events

I am trying to subscribe to these events (in the title) like following:

events, err := l.SubscribeEvents(ctx, libvirt.DomainEventIDDeviceAdded, []libvirt.Domain{})
if err != nil {
	log.Fatal(err)
}

and then reading from that channel, but there no event comes no matter what I do..

I can see all these in virsh event --all --loop:

event 'lifecycle' for domain instance-000005ad: Defined Added
event 'lifecycle' for domain instance-000005ad: Started Booted
event 'lifecycle' for domain instance-000005ad: Suspended Paused
event 'lifecycle' for domain instance-000005ad: Resumed Unpaused
event 'device-added' for domain instance-000005ad: net1
event 'metdata-change' for domain instance-000005ad: element http://openstack.org/xmlns/libvirt/nova/1.1

but only lifecycle events work fine and can be subscribe/read from:

events, err := l.SubscribeEvents(ctx, libvirt.DomainEventIDLifecycle, []libvirt.Domain{})
if err != nil {
	log.Fatal(err)
}

any chance I am just missing something? Btw trying to use this on a openstack host with libvirt 5:

# virsh version
Compiled against library: libvirt 5.0.0
Using library: libvirt 5.0.0
Using API: QEMU 5.0.0
Running hypervisor: QEMU 2.11.1

redesign api with more align to libvirt api

I'm interesting in this package (i have about 400 servers with 100-150 vms on each host, so i'm not small libvirt user).
As i see now api needs more rewrite, because for example all methods recieve domain via string. But this is not very usable, because all the time we need to send additional lookup for domain resource. Also this design has some drawbacks - for example i can start domain, get it resource and use it to attach devices, get stats, reboot and watch events. But in you case this all need to do additional lookup, but why? I'm already have domain.
Also for function naming - events, define, undefine start, stop all needs prefix, because pool, volume, domain all have the same actions. Yes, we can decide via passed params, but i think this is not human frendly.

This is part of #31

@mdlayher @benlemasurier

Do not use this digitalocean!

Hi.
I have been waiting for VPC functional for several weeks. https://github.com/terraform-providers/terraform-provider-digitalocean/issues/415
When it was added, I immediately used it in my project.
And Digitalocean blocked my account without explaining the reasons and did not tell me anything.
I lost work on which I worked for several weeks.
I urge everyone to use anything AWS, GCP, Azure, but not this garbage ocean.
Digitalocean doesn't care about its customers!

telegraf libvirt plugin crashes in rpc.go for some VMs

Here is the back trace:

Jul 6 09:35:00 CVIM-compute-1 telegraf: panic: runtime error: makeslice: len out of range
Jul 6 09:35:00 CVIM-compute-1 telegraf: goroutine 189 [running]:
Jul 6 09:35:00 CVIM-compute-1 telegraf: github.com/influxdata/telegraf/vendor/github.com/digitalocean/go-libvirt.(*Libvirt).listen(0xc42073c230)
Jul 6 09:35:00 CVIM-compute-1 telegraf: /builddir/build/src/github.com/influxdata/telegraf/vendor/github.com/digitalocean/go-libvirt/rpc.go:179 +0xb9
Jul 6 09:35:00 CVIM-compute-1 telegraf: created by github.com/influxdata/telegraf/vendor/github.com/digitalocean/go-libvirt.New
Jul 6 09:35:00 CVIM-compute-1 telegraf: /builddir/build/src/github.com/influxdata/telegraf/vendor/github.com/digitalocean/go-libvirt/libvirt.go:549 +0x25d
Jul 6 09:35:00 CVIM-compute-1 systemd: telegraf.service: main process exited, code=exited, status=2/INVALIDARGUMENT
Jul 6 09:35:00 CVIM-compute-1 systemd: Unit telegraf.service entered failed state.

This only happens for some VMs and not other VMs.

Use d-bus instead of RPC?

Does you plan provide support from digitalocean to this package or after some times move to dbus based? (i think you know that libvirt devs write dbus interface to libvirt and in this case this generated wire binding can be relaced with dbus based)

connect resource cannot be released

libvirt log :
<30>2020-06-11T08:42:47.577912+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.489+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.581094+08:00 cmp001 libvirtd[18541]: message repeated 95 times: [ 2020-06-11 00:42:47.489+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.581120+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.490+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.584284+08:00 cmp001 libvirtd[18541]: message repeated 98 times: [ 2020-06-11 00:42:47.490+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.584310+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.491+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.586997+08:00 cmp001 libvirtd[18541]: message repeated 91 times: [ 2020-06-11 00:42:47.491+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.587023+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.492+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.590406+08:00 cmp001 libvirtd[18541]: message repeated 98 times: [ 2020-06-11 00:42:47.492+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.590467+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.493+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.593430+08:00 cmp001 libvirtd[18541]: message repeated 94 times: [ 2020-06-11 00:42:47.493+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.593456+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.494+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.596393+08:00 cmp001 libvirtd[18541]: message repeated 87 times: [ 2020-06-11 00:42:47.494+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.596419+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.495+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.598657+08:00 cmp001 libvirtd[18541]: message repeated 71 times: [ 2020-06-11 00:42:47.495+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.598683+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.496+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.600824+08:00 cmp001 libvirtd[18541]: message repeated 71 times: [ 2020-06-11 00:42:47.496+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.600885+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.497+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.603485+08:00 cmp001 libvirtd[18541]: message repeated 83 times: [ 2020-06-11 00:42:47.497+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.603511+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.498+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.606121+08:00 cmp001 libvirtd[18541]: message repeated 87 times: [ 2020-06-11 00:42:47.498+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.606142+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.499+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.608683+08:00 cmp001 libvirtd[18541]: message repeated 106 times: [ 2020-06-11 00:42:47.499+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.608710+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.500+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.610636+08:00 cmp001 libvirtd[18541]: message repeated 74 times: [ 2020-06-11 00:42:47.500+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.610669+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.501+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.613128+08:00 cmp001 libvirtd[18541]: message repeated 100 times: [ 2020-06-11 00:42:47.501+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.613154+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.502+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.615599+08:00 cmp001 libvirtd[18541]: message repeated 98 times: [ 2020-06-11 00:42:47.502+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.615626+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.503+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.617977+08:00 cmp001 libvirtd[18541]: message repeated 91 times: [ 2020-06-11 00:42:47.503+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.617996+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.504+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.620380+08:00 cmp001 libvirtd[18541]: message repeated 93 times: [ 2020-06-11 00:42:47.504+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.620400+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.505+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.622342+08:00 cmp001 libvirtd[18541]: message repeated 76 times: [ 2020-06-11 00:42:47.505+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.622369+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.506+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.624397+08:00 cmp001 libvirtd[18541]: message repeated 82 times: [ 2020-06-11 00:42:47.506+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.624425+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.507+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.626356+08:00 cmp001 libvirtd[18541]: message repeated 85 times: [ 2020-06-11 00:42:47.507+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.626376+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.508+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.628627+08:00 cmp001 libvirtd[18541]: message repeated 88 times: [ 2020-06-11 00:42:47.508+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.628647+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.509+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.630766+08:00 cmp001 libvirtd[18541]: message repeated 99 times: [ 2020-06-11 00:42:47.509+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]
<30>2020-06-11T08:42:47.630785+08:00 cmp001 libvirtd[18541]: 2020-06-11 00:42:47.510+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error
<30>2020-06-11T08:42:47.632915+08:00 cmp001 libvirtd[18541]: message repeated 78 times: [ 2020-06-11 00:42:47.510+0000: 18541: error : virNetSocketReadWire:1811 : End of file while reading data: Input/output error]

Bugs caused:
In my monitoring collector program, there will be stuck, when encountering the above situation in libvirt.

use of closed network connection

DomainGetXMLDesc: write unix @->/var/run/libvirt/libvirt-sock: use of closed network connection.

code:
xmlDesc, err := l.DomainGetXMLDesc(*domain, 0)
if err != nil {
log.Printf("failed to DomainGetXMLDesc: %v", err)
return err
}

Make error public, to allow more fine grained control for clients

This issue is a followup from: #56

We are evaluating to use go-libvirt library in the upstream dmacvicar/terraform-provider-libvirt#813

Right now, not having acess to specific error is problematic, since we leveraged these errors with the other libvirt golang
library. see

Is any chance to expose this errors? What is the valid rationale? we would be able to help to improve the library if needed.

I still agree also with @rmohr #56 (comment)

see for example:

https://github.com/dmacvicar/terraform-provider-libvirt/blob/master/libvirt/volume.go#L22

failed to get DomainBlockStats: invalid argument: invalid path: vdl

libvirt version: 4.0.0
go-libvrit version: v0.0.0

code:
` // Report block device statistics. storage
for _, disk := range libvirtSchema.Devices.Disks {
if disk.Device == "cdrom" || disk.Device == "fd" {
continue
}

	isActive, err := l.DomainIsActive(*domain)
	if err != nil {
		log.Printf("E! [domain:%s] failed to get domain active state for block: %s", domain.Name, err.Error())
		return err
	}
	var rRdReq, rRdBytes, rWrReq, rWrBytes int64
	if isActive == 1 {
		rRdReq, rRdBytes, rWrReq, rWrBytes, _, err = l.DomainBlockStats(*domain, disk.Target.Device)
		if err != nil {
			log.Printf("E! [domain:%s] failed to get DomainBlockStats: %v", domain.Name, err.Error())
			return err
		}`

get monitor data timeout

It is found that the problematic virtual machine monitoring data is stuck, and the response is not obtained for a long time.

log: Guest agent is not responding.

Mentioned method Disconnected() does not exist

The documentation for the Connect() method says:

// Connect establishes communication with the libvirt server.
// The underlying libvirt socket connection will be created via the dialer.
// Since the connection can be lost, the Disconnected function can be used
// to monitor for a lost connection.

There is however no such method on libvirt.Libvirt. There is a method on socket.Socket, but the socket used is not exposed by libvirt.Libvirt.

calculate cpu usage

How to calculate cpu usage by the following two interfaces:
DomainGetCPUStats()
DomainGetInfo()

DomainGetCPUStats param

// DomainGetCPUStats is the go wrapper for REMOTE_PROC_DOMAIN_GET_CPU_STATS.
func (l *Libvirt) DomainGetCPUStats(Dom Domain, Nparams uint32, StartCPU int32, Ncpus uint32, Flags TypedParameterFlags) (rParams []TypedParam, rNparams int32, err error) {
}
Nparams ,StartCPU,TypedParameterFlags
How to finish the above three parameters?
And What can I get back? rParams ,rNparams

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.