Skip to content

Commit

Permalink
feat: Adjust the setting for MAC OS X
Browse files Browse the repository at this point in the history
*feat: Add the IP adress calculation
*feat: Adjust the documentation and err handling
  • Loading branch information
ZPascal committed Aug 4, 2023
1 parent 4b47298 commit 7d7a047
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 22 deletions.
94 changes: 79 additions & 15 deletions src/bosh-virtualbox-cpi/vm/network/add_host_only.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package network
import (
"bosh-virtualbox-cpi/driver"
"fmt"
"net"
"regexp"
)

Expand All @@ -11,19 +12,30 @@ var (
)

func (n Networks) AddHostOnly(name, gateway, netmask string) (bool, error) {
// VB does not allow naming host-only networks, exit if it's not the first one
systemInfo, err := n.NewSystemInfo()
if err != nil {
return false, err
}

// VB does not allow naming host-only networks inside version <= 6 , exit if it's not the first one
if len(name) > 0 && name != "vboxnet0" {
return false, nil
}

createdName, err := n.createHostOnly()
var createdName string
if systemInfo.IsMacOSXVBoxSpecial6or7Case() {
createdName, err = n.createHostOnly(gateway, netmask)
} else {
createdName, err = n.createHostOnly("", "")
}

if err != nil {
return true, err
}

if len(name) > 0 && createdName != name {
n.cleanUpPartialHostOnlyCreate(createdName)
return true, fmt.Errorf("Expected created host-only network '%s' to have name '%s'", createdName, name)
return true, fmt.Errorf("expected created host-only network '%s' to have name '%s'", createdName, name)
}

err = n.configureHostOnly(createdName, gateway, netmask)
Expand All @@ -35,8 +47,36 @@ func (n Networks) AddHostOnly(name, gateway, netmask string) (bool, error) {
return true, nil
}

func (n Networks) createHostOnly() (string, error) {
output, err := n.driver.Execute("hostonlyif", "create")
func (n Networks) createHostOnly(gateway, netmask string) (string, error) {
systemInfo, err := n.NewSystemInfo()
if err != nil {
return "", err
}

args := []string{"hostonlyif", "create"}
if systemInfo.IsMacOSXVBoxSpecial6or7Case() {
addr := net.ParseIP(netmask).To4()
subnet := &net.IPNet{
IP: net.ParseIP(gateway),
Mask: net.IPv4Mask(addr[0], addr[1], addr[2], addr[3]),
}

lowerIp, errGetFirstIP := systemInfo.GetFirstIP(subnet)
if errGetFirstIP != nil {
return "", errGetFirstIP
}
upperIp, errGetLastIP := systemInfo.GetLastIP(subnet)
if errGetLastIP != nil {
return "", errGetLastIP
}

args = []string{"hostonlynets",
"add", fmt.Sprintf("--name=%s", "vboxnet0"),
fmt.Sprintf("--netmask=%s", netmask), fmt.Sprintf("--lower-ip=%s", lowerIp.String()),
fmt.Sprintf("--upper-ip=%s", upperIp.String()), "--disable"}
}

output, err := n.driver.ExecuteComplex(args, driver.ExecuteOpts{})
if err != nil {
return "", err
}
Expand All @@ -50,25 +90,49 @@ func (n Networks) createHostOnly() (string, error) {
}

func (n Networks) configureHostOnly(name, gateway, netmask string) error {
args := []string{"hostonlyif", "ipconfig", name}

if len(gateway) > 0 {
args = append(args, []string{"--ip", gateway, "--netmask", netmask}...)
} else {
args = append(args, "--dhcp")
systemInfo, err := n.NewSystemInfo()
if err != nil {
return err
}

_, err := n.driver.ExecuteComplex(args, driver.ExecuteOpts{})
if systemInfo.IsMacOSXVBoxSpecial6or7Case() == false {
args := []string{"hostonlyif", "ipconfig", name}

if len(gateway) > 0 {
args = append(args, []string{"--ip", gateway, "--netmask", netmask}...)
} else {
args = append(args, "--dhcp")
}

return err
_, err := n.driver.ExecuteComplex(args, driver.ExecuteOpts{})

return err
} else {
return nil
}
}

func (n Networks) cleanUpPartialHostOnlyCreate(name string) {
_, err := n.driver.ExecuteComplex([]string{
systemInfo, err := n.NewSystemInfo()
if err != nil {
n.logger.Error("vm.network.SystemInfo",
"Failed to get the SystemInfo: %s", err)
}

args := []string{
"hostonlyif",
"remove",
name,
}, driver.ExecuteOpts{})
}
if systemInfo.IsMacOSXVBoxSpecial6or7Case() {
args = []string{
"hostonlynet",
"remove",
fmt.Sprintf("--name=%s", name),
}
}

_, err = n.driver.ExecuteComplex(args, driver.ExecuteOpts{})
if err != nil {
n.logger.Error("vm.network.Networks",
"Failed to clean up partially created host-only network '%s': %s", name, err)
Expand Down
23 changes: 17 additions & 6 deletions src/bosh-virtualbox-cpi/vm/network/host_only.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package network

import (
"bosh-virtualbox-cpi/driver"
"fmt"
boshlog "github.com/cloudfoundry/bosh-utils/logger"
"net"

"bosh-virtualbox-cpi/driver"
"os"
)

type HostOnly struct {
Expand All @@ -31,15 +32,25 @@ func (n HostOnly) IsEnabled() bool { return n.status == "Up" }
func (n HostOnly) EnabledDescription() string { return "have status 'Up'" }

func (n HostOnly) Enable() error {
args := []string{"hostonlyif", "ipconfig", n.name}
logger := boshlog.NewWriterLogger(boshlog.LevelDebug, os.Stderr)
systemInfo, err := Networks{driver: n.driver, logger: logger}.NewSystemInfo()
if err != nil {
return err
}

var finalArgs []string
if systemInfo.IsMacOSXVBoxSpecial6or7Case() {
finalArgs = []string{"hostonlynets", "modify", fmt.Sprintf("--name=%s", n.name), "--enable"}
}

args := []string{"hostonlyif", "ipconfig", n.name}
if len(n.ipAddress) > 0 {
args = append(args, []string{"--ip", n.ipAddress, "--netmask", n.networkMask}...)
finalArgs = append(args, []string{"--ip", n.ipAddress, "--netmask", n.networkMask}...)
} else {
args = append(args, "--dhcp")
finalArgs = append(args, "--dhcp")
}

_, err := n.driver.Execute(args...)
_, err = n.driver.ExecuteComplex(finalArgs, driver.ExecuteOpts{})

return err
}
Expand Down
16 changes: 15 additions & 1 deletion src/bosh-virtualbox-cpi/vm/network/networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,17 @@ func (n Networks) BridgedNetworks() ([]Network, error) {
}

func (n Networks) HostOnlys() ([]Network, error) {
output, err := n.driver.Execute("list", "hostonlyifs")
systemInfo, err := n.NewSystemInfo()
if err != nil {
return nil, err
}

commandName := "hostonlyifs"
if systemInfo.IsMacOSXVBoxSpecial6or7Case() {
commandName = "hostonlynets"
}

output, err := n.driver.Execute("list", fmt.Sprintf("%s", commandName))
if err != nil {
return nil, err
}
Expand All @@ -175,10 +185,14 @@ func (n Networks) HostOnlys() ([]Network, error) {
net.dhcp, err = n.toBool(matches[2])
case "IPAddress":
net.ipAddress = matches[2]
case "LowerIP":
net.ipAddress = matches[2]
case "NetworkMask":
net.networkMask = matches[2]
case "Status":
net.status = matches[2]
case "State":
net.status = matches[2]
}

if err != nil {
Expand Down
112 changes: 112 additions & 0 deletions src/bosh-virtualbox-cpi/vm/network/system_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package network

import (
"fmt"
"math/big"
"net"
"runtime"
"strings"
)

type SystemInfo struct {
osVersion string
vBoxMajorVersion string
VBoxMinorVersion string
}

func (n Networks) NewSystemInfo() (SystemInfo, error) {
vBoxMajorVersion, VBoxMinorVersion, err := n.getVboxVersion()
if err != nil {
return SystemInfo{}, err
}

return SystemInfo{getOSVersion(), vBoxMajorVersion, VBoxMinorVersion}, nil
}

// GetFirstIP Get the first usable IP address of a subnet
func (s SystemInfo) GetFirstIP(subnet *net.IPNet) (net.IP, error) {
return getIndexedIP(subnet, 0)
}

// GetLastIP Get the last usable IP address of a subnet
func (s SystemInfo) GetLastIP(subnet *net.IPNet) (net.IP, error) {
size := rangeSize(subnet)
if size <= 0 {
return nil, fmt.Errorf("can't get range size of subnet. subnet: %q", subnet)
}
return getIndexedIP(subnet, int(size-1))
}

// IsMacOSXVBoxSpecial6or7Case Identify if you are system is running on MAC OS X and the used
// VirtualBox version is 6.1 or 7
func (s SystemInfo) IsMacOSXVBoxSpecial6or7Case() bool {
if s.osVersion == "darwin" && (s.vBoxMajorVersion == "7" ||
(s.vBoxMajorVersion == "6" && s.VBoxMinorVersion == "1")) {
return true
} else {
return false
}
}

// getVboxVersion Extract the corresponding used Virtual Box version
func (n Networks) getVboxVersion() (string, string, error) {
output, err := n.driver.Execute("--version")
if err != nil {
return "", "", err
}

output = strings.TrimSpace(output)
matches := strings.Split(output, ".")

if len(matches) > 3 {
panic(fmt.Sprintf("Internal inconsistency: Expected len(%s matches) >= 3:", createdHostOnlyMatch))
}

return matches[0], matches[1], nil
}

// getOSVersion Extract the corresponding used operational system
func getOSVersion() string {
return runtime.GOOS
}

// rangeSize Identify the range size of valid subnet addresses.
// The functionality is copied from https://github.com/tkestack/tke/blob/v1.9.2/pkg/util/ipallocator/allocator.go
func rangeSize(subnet *net.IPNet) int64 {
ones, bits := subnet.Mask.Size()
if bits == 32 && (bits-ones) >= 31 || bits == 128 && (bits-ones) >= 127 {
return 0
}

if bits == 128 && (bits-ones) >= 16 {
return int64(1) << uint(16)
}
return int64(1) << uint(bits-ones)
}

// addIPOffset adds the provided integer offset to a base big.Int representing a net.IP.
// The functionality is copied from https://github.com/tkestack/tke/blob/v1.9.2/pkg/util/ipallocator/allocator.go
func addIPOffset(base *big.Int, offset int) net.IP {
return big.NewInt(0).Add(base, big.NewInt(int64(offset))).Bytes()
}

// bigForIP Creates a big.Int based on the provided net.IP.
// The functionality is copied from https://github.com/tkestack/tke/blob/v1.9.2/pkg/util/ipallocator/allocator.go
func bigForIP(ip net.IP) *big.Int {
b := ip.To4()
if b == nil {
b = ip.To16()
}
return big.NewInt(0).SetBytes(b)
}

// getIndexedIP Get a net.IP that is subnet.IP + index in the contiguous IP space.
// The functionality is copied from https://github.com/tkestack/tke/blob/v1.9.2/pkg/util/ipallocator/allocator.go
func getIndexedIP(subnet *net.IPNet, index int) (net.IP, error) {
ip := addIPOffset(bigForIP(subnet.IP), index)
if !subnet.Contains(ip) {
return nil,
fmt.Errorf("can't generate IP with index %d from subnet. subnet too small. subnet: %q", index, subnet)
}
return ip, nil
}

0 comments on commit 7d7a047

Please sign in to comment.