Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kvm2 driver: add dedicated network & static ip #10792

Merged
merged 3 commits into from
Mar 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions cmd/minikube/cmd/start_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func initMinikubeFlags() {
startCmd.Flags().Bool(preload, true, "If set, download tarball of preloaded images if available to improve start time. Defaults to true.")
startCmd.Flags().Bool(deleteOnFailure, false, "If set, delete the current cluster if start fails and try again. Defaults to false.")
startCmd.Flags().Bool(forceSystemd, false, "If set, force the container runtime to use sytemd as cgroup manager. Defaults to false.")
startCmd.Flags().StringP(network, "", "", "network to run minikube with. Only available with the docker/podman drivers. If left empty, minikube will create a new network.")
startCmd.Flags().StringP(network, "", "", "network to run minikube with. Now it is used by docker/podman and KVM drivers. If left empty, minikube will create a new network.")
startCmd.Flags().StringVarP(&outputFormat, "output", "o", "text", "Format to print stdout in. Options include: [text,json]")
startCmd.Flags().StringP(trace, "", "", "Send trace events. Options include: [gcp]")
}
Expand Down Expand Up @@ -191,7 +191,7 @@ func initDriverFlags() {
startCmd.Flags().Bool("vm", false, "Filter to use only VM Drivers")

// kvm2
startCmd.Flags().String(kvmNetwork, "default", "The KVM network name. (kvm2 driver only)")
startCmd.Flags().String(kvmNetwork, "default", "The KVM default network name. (kvm2 driver only)")
startCmd.Flags().String(kvmQemuURI, "qemu:///system", "The KVM QEMU connection URI. (kvm2 driver only)")
startCmd.Flags().Bool(kvmGPU, false, "Enable experimental NVIDIA GPU support in minikube")
startCmd.Flags().Bool(kvmHidden, false, "Hide the hypervisor signature from the guest in minikube (kvm2 driver only)")
Expand Down Expand Up @@ -311,8 +311,8 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k
out.WarningT("With --network-plugin=cni, you will need to provide your own CNI. See --cni flag as a user-friendly alternative")
}

if !driver.IsKIC(drvName) && viper.GetString(network) != "" {
out.WarningT("--network flag is only valid with the docker/podman drivers, it will be ignored")
if !(driver.IsKIC(drvName) || driver.IsKVM(drvName)) && viper.GetString(network) != "" {
out.WarningT("--network flag is only valid with the docker/podman and KVM drivers, it will be ignored")
}

checkNumaCount(k8sVersion)
Expand Down
51 changes: 12 additions & 39 deletions pkg/drivers/kvm/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ package kvm

import (
"bytes"
"crypto/rand"
"fmt"
"net"
"text/template"

libvirt "github.com/libvirt/libvirt-go"
Expand Down Expand Up @@ -68,12 +66,10 @@ const domainTmpl = `
</disk>
<interface type='network'>
<source network='{{.Network}}'/>
<mac address='{{.MAC}}'/>
<model type='virtio'/>
</interface>
<interface type='network'>
<source network='{{.PrivateNetwork}}'/>
<mac address='{{.PrivateMAC}}'/>
<model type='virtio'/>
</interface>
<serial type='pty'>
Expand All @@ -92,25 +88,6 @@ const domainTmpl = `
</domain>
`

func randomMAC() (net.HardwareAddr, error) {
buf := make([]byte, 6)
_, err := rand.Read(buf)
if err != nil {
return nil, err
}
// We unset the first and second least significant bits (LSB) of the MAC
//
// The LSB of the first octet
// 0 for unicast
// 1 for multicast
//
// The second LSB of the first octet
// 0 for universally administered addresses
// 1 for locally administered addresses
buf[0] &= 0xfc
return buf, nil
}

func (d *Driver) getDomain() (*libvirt.Domain, *libvirt.Connect, error) {
conn, err := getConnection(d.ConnectionURI)
if err != nil {
Expand Down Expand Up @@ -146,22 +123,6 @@ func closeDomain(dom *libvirt.Domain, conn *libvirt.Connect) error {
}

func (d *Driver) createDomain() (*libvirt.Domain, error) {
// create random MAC addresses first for our NICs
if d.MAC == "" {
mac, err := randomMAC()
if err != nil {
return nil, errors.Wrap(err, "generating mac address")
}
d.MAC = mac.String()
}

if d.PrivateMAC == "" {
mac, err := randomMAC()
if err != nil {
return nil, errors.Wrap(err, "generating mac address")
}
d.PrivateMAC = mac.String()
}
// create the XML for the domain using our domainTmpl template
tmpl := template.Must(template.New("domain").Parse(domainTmpl))
var domainXML bytes.Buffer
Expand All @@ -180,5 +141,17 @@ func (d *Driver) createDomain() (*libvirt.Domain, error) {
return nil, errors.Wrapf(err, "error defining domain xml: %s", domainXML.String())
}

// save MAC address
dmac, err := macFromXML(conn, d.MachineName, d.Network)
if err != nil {
return nil, fmt.Errorf("failed saving MAC address: %w", err)
}
d.MAC = dmac
pmac, err := macFromXML(conn, d.MachineName, d.PrivateNetwork)
if err != nil {
return nil, fmt.Errorf("failed saving MAC address: %w", err)
}
d.PrivateMAC = pmac

return dom, nil
}
66 changes: 43 additions & 23 deletions pkg/drivers/kvm/kvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
libvirt "github.com/libvirt/libvirt-go"
"github.com/pkg/errors"
pkgdrivers "k8s.io/minikube/pkg/drivers"
"k8s.io/minikube/pkg/util/retry"
)

// Driver is the machine driver for KVM
Expand Down Expand Up @@ -209,12 +210,14 @@ func (d *Driver) GetIP() (string, error) {
if s != state.Running {
return "", errors.New("host is not running")
}
ip, err := d.lookupIP()

conn, err := getConnection(d.ConnectionURI)
if err != nil {
return "", errors.Wrap(err, "getting IP")
return "", errors.Wrap(err, "getting libvirt connection")
}
defer conn.Close()

return ip, nil
return ipFromXML(conn, d.MachineName, d.PrivateNetwork)
}

// GetSSHHostname returns hostname for use with ssh
Expand Down Expand Up @@ -272,32 +275,43 @@ func (d *Driver) Start() (err error) {
}

log.Info("Waiting to get IP...")
for i := 0; i <= 40; i++ {
ip, err := d.GetIP()
if err := d.waitForStaticIP(conn); err != nil {
return errors.Wrap(err, "IP not available after waiting")
}

log.Info("Waiting for SSH to be available...")
if err := drivers.WaitForSSH(d); err != nil {
return errors.Wrap(err, "SSH not available after waiting")
}

return nil
}

// waitForStaticIP waits for IP address of domain that has been created & starting and then makes that IP static.
func (d *Driver) waitForStaticIP(conn *libvirt.Connect) error {
query := func() error {
sip, err := ipFromAPI(conn, d.MachineName, d.PrivateNetwork)
if err != nil {
return errors.Wrap(err, "getting ip during machine start")
return fmt.Errorf("failed getting IP during machine start, will retry: %w", err)
}
if ip == "" {
log.Debugf("Waiting for machine to come up %d/%d", i, 40)
time.Sleep(3 * time.Second)
continue
if sip == "" {
return fmt.Errorf("waiting for machine to come up")
}

if ip != "" {
log.Infof("Found IP for machine: %s", ip)
d.IPAddress = ip
break
}
}
log.Infof("Found IP for machine: %s", sip)
d.IPAddress = sip

if d.IPAddress == "" {
return errors.New("machine didn't return an IP after 120 seconds")
return nil
}
if err := retry.Local(query, 1*time.Minute); err != nil {
return fmt.Errorf("machine %s didn't return IP after 1 minute", d.MachineName)
}

log.Info("Waiting for SSH to be available...")
if err := drivers.WaitForSSH(d); err != nil {
d.IPAddress = ""
return errors.Wrap(err, "SSH not available after waiting")
log.Info("Reserving static IP address...")
if err := addStaticIP(conn, d.PrivateNetwork, d.MachineName, d.PrivateMAC, d.IPAddress); err != nil {
log.Warnf("Failed reserving static IP %s for host %s, will continue anyway: %v", d.IPAddress, d.MachineName, err)
} else {
log.Infof("Reserved static IP address: %s", d.IPAddress)
}

return nil
Expand Down Expand Up @@ -385,7 +399,6 @@ func ensureDirPermissions(store string) error {

// Stop a host gracefully
func (d *Driver) Stop() (err error) {
d.IPAddress = ""
s, err := d.GetState()
if err != nil {
return errors.Wrap(err, "getting state of VM")
Expand Down Expand Up @@ -458,6 +471,13 @@ func (d *Driver) Remove() error {
return errors.Wrap(err, "undefine domain")
}

log.Info("Removing static IP address...")
if err := delStaticIP(conn, d.PrivateNetwork, "", "", d.IPAddress); err != nil {
log.Warnf("failed removing static IP %s for host %s, will continue anyway: %v", d.IPAddress, d.MachineName, err)
} else {
log.Info("Removed static IP address")
}

return nil
}

Expand Down
Loading