View
@@ -11,6 +11,22 @@ const (
DefaultBridgeName = "br1"
)
func SetDefaults_Devices(devices *Devices) {
// Set default controllers, "none" means that controller disabled
devices.Controllers = []Controller{
{
Type: "usb",
Index: "0",
Model: "none",
},
}
// Set default memballoon, "none" means that controller disabled
devices.Ballooning = &Ballooning{
Model: "none",
}
}
func SetDefaults_OSType(ostype *OSType) {
ostype.OS = "hvm"
View
@@ -238,18 +238,30 @@ type HugePage struct {
}
type Devices struct {
Emulator string `xml:"emulator,omitempty"`
Interfaces []Interface `xml:"interface"`
Channels []Channel `xml:"channel"`
Video []Video `xml:"video"`
Graphics []Graphics `xml:"graphics"`
Ballooning *Ballooning `xml:"memballoon,omitempty"`
Disks []Disk `xml:"disk"`
Serials []Serial `xml:"serial"`
Consoles []Console `xml:"console"`
Watchdog *Watchdog `xml:"watchdog,omitempty"`
Emulator string `xml:"emulator,omitempty"`
Interfaces []Interface `xml:"interface"`
Channels []Channel `xml:"channel"`
Controllers []Controller `xml:"controller,omitempty"`
Video []Video `xml:"video"`
Graphics []Graphics `xml:"graphics"`
Ballooning *Ballooning `xml:"memballoon,omitempty"`
Disks []Disk `xml:"disk"`
Serials []Serial `xml:"serial"`
Consoles []Console `xml:"console"`
Watchdog *Watchdog `xml:"watchdog,omitempty"`
}
// BEGIN Controller -----------------------------
// Controller represens libvirt controller element https://libvirt.org/formatdomain.html#elementsControllers
type Controller struct {
Type string `xml:"type,attr"`
Index string `xml:"index,attr"`
Model string `xml:"model,attr,omitempty"`
}
// END Controller -----------------------------
// BEGIN Disk -----------------------------
type Disk struct {
View
@@ -21,6 +21,7 @@ package api
import (
"encoding/xml"
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -40,9 +41,11 @@ var exampleXML = `<domain type="kvm" xmlns:qemu="http://libvirt.org/schemas/doma
<baseBoard></baseBoard>
</sysinfo>
<devices>
<controller type="usb" index="0" model="none"></controller>
<video>
<model type="vga" heads="1" vram="16384"></model>
</video>
<memballoon model="none"></memballoon>
<disk device="disk" type="network">
<source protocol="iscsi" name="iqn.2013-07.com.example:iscsi-nopool/2">
<host name="example.com" port="3260"></host>
@@ -176,6 +179,7 @@ var _ = Describe("Schema", func() {
It("Marshal into xml", func() {
buf, err := xml.MarshalIndent(exampleDomain.Spec, "", " ")
Expect(err).To(BeNil())
fmt.Printf(string(buf))
Expect(string(buf)).To(Equal(exampleXML))
})
View
@@ -39,6 +39,7 @@ func SetObjectDefaults_Domain(in *Domain) {
if in.Spec.SysInfo != nil {
SetDefaults_SysInfo(in.Spec.SysInfo)
}
SetDefaults_Devices(&in.Spec.Devices)
}
func SetObjectDefaults_DomainList(in *DomainList) {
View
@@ -96,10 +96,10 @@ func (l *LibvirtConnection) NewStream(flags libvirt.StreamFlags) (Stream, error)
if err := l.reconnectIfNecessary(); err != nil {
return nil, err
}
defer l.checkConnectionLost()
s, err := l.Connect.NewStream(flags)
if err != nil {
l.checkConnectionLost(err)
return nil, err
}
return &VirStream{Stream: s}, nil
@@ -114,40 +114,41 @@ func (l *LibvirtConnection) DomainEventLifecycleRegister(callback libvirt.Domain
if err = l.reconnectIfNecessary(); err != nil {
return
}
defer l.checkConnectionLost()
l.callbacks = append(l.callbacks, callback)
_, err = l.Connect.DomainEventLifecycleRegister(nil, callback)
l.checkConnectionLost(err)
return
}
func (l *LibvirtConnection) LookupDomainByName(name string) (dom VirDomain, err error) {
if err = l.reconnectIfNecessary(); err != nil {
return
}
defer l.checkConnectionLost()
return l.Connect.LookupDomainByName(name)
domain, err := l.Connect.LookupDomainByName(name)
l.checkConnectionLost(err)
return domain, err
}
func (l *LibvirtConnection) DomainDefineXML(xml string) (dom VirDomain, err error) {
if err = l.reconnectIfNecessary(); err != nil {
return
}
defer l.checkConnectionLost()
dom, err = l.Connect.DomainDefineXML(xml)
l.checkConnectionLost(err)
return
}
func (l *LibvirtConnection) ListAllDomains(flags libvirt.ConnectListAllDomainsFlags) ([]VirDomain, error) {
if err := l.reconnectIfNecessary(); err != nil {
return nil, err
}
defer l.checkConnectionLost()
virDoms, err := l.Connect.ListAllDomains(flags)
if err != nil {
l.checkConnectionLost(err)
return nil, err
}
doms := make([]VirDomain, len(virDoms))
@@ -184,7 +185,7 @@ func (l *LibvirtConnection) installWatchdog(checkInterval time.Duration) {
l.reconnectLock.Unlock()
} else {
// Do the usual error check to determine if the connection is lost
l.checkConnectionLost()
l.checkConnectionLost(err)
}
}
}
@@ -214,16 +215,20 @@ func (l *LibvirtConnection) reconnectIfNecessary() (err error) {
return nil
}
func (l *LibvirtConnection) checkConnectionLost() {
func (l *LibvirtConnection) checkConnectionLost(err error) {
l.reconnectLock.Lock()
defer l.reconnectLock.Unlock()
err := libvirt.GetLastError()
if errors.IsOk(err) {
return
}
switch err.Code {
libvirtError, ok := err.(libvirt.Error)
if !ok {
return
}
switch libvirtError.Code {
case
libvirt.ERR_INTERNAL_ERROR,
libvirt.ERR_INVALID_CONN,
@@ -233,7 +238,7 @@ func (l *LibvirtConnection) checkConnectionLost() {
libvirt.ERR_SYSTEM_ERROR,
libvirt.ERR_RPC:
l.alive = false
log.Log.With("code", err.Code).Reason(err).Error("Connection to libvirt lost.")
log.Log.With("code", libvirtError.Code).Reason(libvirtError).Error("Connection to libvirt lost.")
}
}
View
@@ -11,8 +11,17 @@ import (
"github.com/libvirt/libvirt-go"
k8sv1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os"
"syscall"
"os/signal"
"k8s.io/apimachinery/pkg/util/wait"
"kubevirt.io/kubevirt/pkg/api/v1"
"kubevirt.io/kubevirt/pkg/log"
"kubevirt.io/kubevirt/pkg/virt-launcher"
"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/cli"
)
@@ -140,11 +149,10 @@ func StartLibvirt(stopChan chan struct{}) {
// doesn't exit until virt-launcher is ready for it to. Virt-launcher traps signals
// to perform special shutdown logic. These processes need to live in the same
// container.
go func() {
for {
exitChan := make(chan struct{})
cmd := exec.Command("/usr/share/kubevirt/virt-launcher/libvirtd.sh")
cmd := exec.Command("/usr/sbin/libvirtd")
err := cmd.Start()
if err != nil {
@@ -162,7 +170,11 @@ func StartLibvirt(stopChan chan struct{}) {
cmd.Process.Kill()
return
case <-exitChan:
log.Log.Errorf("libvirtd exited, restarting")
output, e := cmd.CombinedOutput()
if e == nil {
e = fmt.Errorf(string(output))
}
log.Log.Reason(e).Errorf("libvirtd exited, restarting")
}
// this sleep is to avoid consumming all resources in the
@@ -237,3 +249,102 @@ func NewDomain(dom cli.VirDomain) (*api.Domain, error) {
domain.GetObjectMeta().SetUID(domain.Spec.Metadata.KubeVirt.UID)
return domain, nil
}
// ForkAndMonitor itself to give qemu an extra grace period to properly terminate
// in case of virt-launcher crashes
func ForkAndMonitor(commandNamePrefix string) error {
cmd := exec.Command(os.Args[0], append(os.Args[1:], "--no-fork", "true")...)
err := cmd.Start()
if err != nil {
log.Log.Reason(err).Error("failed to fork virt-launcher")
return err
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
for sig := range sigs {
log.Log.V(3).Log("signalling virt-launcher to shut down")
err := cmd.Process.Kill()
if err != nil {
log.Log.Reason(err).Errorf("received signal %s but can't signal virt-launcher to shut down", sig.String())
}
}
}()
if err := cmd.Wait(); err != nil {
exitCode := 1
if exiterr, ok := err.(*exec.ExitError); ok {
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
exitCode = status.ExitStatus()
}
}
log.Log.Reason(err).Error("dirty virt-launcher shutdown")
pid, _ := virtlauncher.FindPid(commandNamePrefix)
if pid > 0 {
p, err := os.FindProcess(pid)
if err != nil {
return err
}
// Signal qemu to shutdown
err = p.Signal(syscall.SIGTERM)
if err != nil {
return err
}
// Wait for 10 seconds for the qemu process to disappear
err = wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
pid, _ := virtlauncher.FindPid(commandNamePrefix)
if pid == 0 {
return true, nil
}
return false, nil
})
}
os.Exit(exitCode)
}
return nil
}
func SetupLibvirt() error {
// TODO: setting permissions and owners is not part of device plugins.
// Configure these manually right now on "/dev/kvm"
stats, err := os.Stat("/dev/kvm")
if err == nil {
s, ok := stats.Sys().(*syscall.Stat_t)
if !ok {
return fmt.Errorf("can't convert file stats to unix/linux stats")
}
err := os.Chown("/dev/kvm", int(s.Uid), 107)
if err != nil {
return err
}
err = os.Chmod("/dev/kvm", 0660)
if err != nil {
return err
}
} else if !os.IsNotExist(err) {
return err
}
qemuConf, err := os.OpenFile("/etc/libvirt/qemu.conf", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer qemuConf.Close()
// We are in a container, don't try to stuff qemu inside special cgroups
_, err = qemuConf.WriteString("cgroup_controllers = [ ]\n")
if err != nil {
return err
}
// If hugepages exist, tell libvirt about them
_, err = os.Stat("/dev/hugepages")
if err == nil {
_, err = qemuConf.WriteString("hugetlbfs_mount = \"/dev/hugepages\"\n")
} else if !os.IsNotExist(err) {
return err
}
return nil
}
View
@@ -1387,21 +1387,13 @@ func RunKubectlCommand(args ...string) (string, error) {
func SkipIfNoOc() {
if KubeVirtOcPath == "" {
var err error
KubeVirtOcPath, err = exec.LookPath("oc")
if err != nil {
Skip("Skip test that requires oc binary")
}
Skip("Skip test that requires oc binary")
}
}
func RunOcCommand(args ...string) (string, error) {
if KubeVirtOcPath == "" {
var err error
KubeVirtOcPath, err = exec.LookPath("oc")
if err != nil {
return "", fmt.Errorf("can not find oc binary")
}
return "", fmt.Errorf("no oc binary specified")
}
kubeconfig := flag.Lookup("kubeconfig").Value
@@ -1421,10 +1413,9 @@ func RunOcCommand(args ...string) (string, error) {
stdOutErrBytes, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf("%s %s %s\n%s%v\n", kubeconfEnv, KubeVirtOcPath, strings.Join(args, " "), string(stdOutErrBytes), err)
return string(stdOutErrBytes), err
log.Log.Reason(err).With("output", string(stdOutErrBytes)).Errorf("oc command failed: %s %s,", KubeVirtOcPath, strings.Join(args, " "))
}
return string(stdOutErrBytes), nil
return string(stdOutErrBytes), err
}
func GenerateVMIJson(vmi *v1.VirtualMachineInstance) (string, error) {
@@ -1537,6 +1528,17 @@ func SkipIfVersionBelow(message string, expectedVersion string) {
}
}
func SkipIfOpenShift(message string) {
virtClient, err := kubecli.GetKubevirtClient()
PanicOnError(err)
result := virtClient.RestClient().Get().AbsPath("/version/openshift").Do()
if result.Error() == nil {
Skip("Openshift detected: " + message)
}
}
// StartVmOnNode starts a VMI on the specified node
func StartVmOnNode(vmi *v1.VirtualMachineInstance, nodeName string) {
virtClient, err := kubecli.GetKubevirtClient()
@@ -1601,13 +1603,25 @@ func GetNodeCPUInfo(nodeName string) string {
// KubevirtFailHandler call ginkgo.Fail with printing the additional information
func KubevirtFailHandler(message string, callerSkip ...int) {
if len(callerSkip) > 0 {
callerSkip[0]++
}
virtClient, err := kubecli.GetKubevirtClient()
PanicOnError(err)
if err != nil {
fmt.Println(err)
Fail(message, callerSkip...)
return
}
for _, ns := range []string{metav1.NamespaceSystem, NamespaceTestDefault} {
// Get KubeVirt specific pods information
pods, err := virtClient.CoreV1().Pods(ns).List(metav1.ListOptions{LabelSelector: "kubevirt.io"})
PanicOnError(err)
if err != nil {
fmt.Println(err)
Fail(message, callerSkip...)
return
}
for _, pod := range pods.Items {
fmt.Printf("\nPod name: %s\t Pod phase: %s\n\n", pod.Name, pod.Status.Phase)
@@ -1627,9 +1641,5 @@ func KubevirtFailHandler(message string, callerSkip ...int) {
}
}
}
if len(callerSkip) > 0 {
callerSkip[0]++
}
Fail(message, callerSkip...)
}
View
@@ -158,6 +158,10 @@ var _ = Describe("Networking", func() {
table.DescribeTable("should be able to reach", func(destination string) {
var cmdCheck, addrShow, addr string
if destination == "InboundVMIWithCustomMacAddress" {
tests.SkipIfOpenShift("Custom MAC addresses on pod networks are not suppored")
}
// assuming pod network is of standard MTU = 1500 (minus 50 bytes for vxlan overhead)
expectedMtu := 1450
ipHeaderSize := 28 // IPv4 specific
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
Oops, something went wrong.