From cac7c588b49cbd67fe44ea554175090404551ef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Meireles?= Date: Tue, 5 Jul 2016 14:42:49 +0100 Subject: [PATCH] allow the creation of > 128 VMs over the lifetime a given corectld session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit according to https://developer.apple.com/library/mac/documentation/DriversKernelHardware/Reference/vmnet/ one only "can create a maximum of 32 interfaces with a limit of 4 per guest operating system" which in practice means that a given Pid/corectld instance in aggregate can't create more than 128 VMs (interfaces). by doing the UUIDs lookup/validation as an external process "unrelated" from its parent we get around this limitation and so each corectld session stops having an 2ˆ7 upper bound on the number the VMs it can create over its lifetime this commit handles the first part of #77 Signed-off-by: António Meireles --- cmd/server/main.go | 2 + cmd/server/server.go | 29 ++++++++++++- components/server/rpcservices.go | 72 ++++++++++++++++++++++++-------- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 61488c2..e2aed75 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -47,6 +47,8 @@ func init() { return } session.Caller.User = usr + } else if cmd.Name() == "uuid2mac" { + return } else { return fmt.Errorf("too many privileges invoking corectl. " + "running directly as root, or via 'sudo', only " + diff --git a/cmd/server/server.go b/cmd/server/server.go index 90fecf6..c9bc78a 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -19,9 +19,12 @@ import ( "fmt" "github.com/TheNewNormal/corectl/components/common" + "github.com/TheNewNormal/corectl/components/host/darwin/misc/uuid2ip" "github.com/TheNewNormal/corectl/components/host/session" "github.com/TheNewNormal/corectl/components/server" "github.com/TheNewNormal/corectl/release" + "github.com/helm/helm/log" + "github.com/satori/go.uuid" "github.com/everdev/mack" "github.com/spf13/cobra" @@ -44,8 +47,32 @@ var ( Short: "Shows corectld status", RunE: common.PScommand, } + uuidToMacCmd = &cobra.Command{ + Use: "uuid2mac", + Short: "returns the MAC address that will assembled from the " + + "given UUID", + RunE: uuidToMacCommand, + Hidden: true, + } ) +func uuidToMacCommand(cmd *cobra.Command, args []string) (err error) { + var macAddr string + if _, err = uuid.FromString(args[0]); err != nil { + log.Warn("%s not a valid UUID as it doesn't follow RFC "+ + "4122", args[0]) + // given that we only call this with dats generated with + // uuid.NewV4().String() ... + err = fmt.Errorf("Something went very wrong, as we're unable to "+ + "generate a MAC address from the provided UUID (%s). Please fill "+ + "a bug at https://github.com/TheNewNormal/corectl/issues with "+ + "this error and wait there for our feedback...", args[0]) + } else if macAddr, err = uuid2ip.GuestMACfromUUID(args[0]); err == nil { + fmt.Println(macAddr) + } + return +} + func shutdownCommand(cmd *cobra.Command, args []string) (err error) { if _, err = server.Daemon.Running(); err != nil { return @@ -87,5 +114,5 @@ func init() { "sets the user that will 'own' the corectld instance") serverStartCmd.Flags().BoolP("force", "f", false, "rebuilds config drive iso even if a suitable one is already present") - rootCmd.AddCommand(shutdownCmd, statusCmd, serverStartCmd) + rootCmd.AddCommand(shutdownCmd, statusCmd, serverStartCmd, uuidToMacCmd) } diff --git a/components/server/rpcservices.go b/components/server/rpcservices.go index 079834e..ede7d87 100644 --- a/components/server/rpcservices.go +++ b/components/server/rpcservices.go @@ -16,24 +16,26 @@ package server import ( + "bufio" "bytes" "fmt" + "io" "net/http" "os/exec" "path/filepath" "strings" + "syscall" "os" "path" "time" - "github.com/TheNewNormal/corectl/components/host/darwin/misc/uuid2ip" "github.com/TheNewNormal/corectl/components/host/session" "github.com/TheNewNormal/corectl/release" "github.com/blang/semver" - "github.com/helm/helm/log" "github.com/gorilla/rpc" "github.com/gorilla/rpc/json" + "github.com/helm/helm/log" "github.com/satori/go.uuid" ) @@ -116,37 +118,71 @@ func (s *RPCservice) RemoveImage(r *http.Request, func (s *RPCservice) UUIDtoMACaddr(r *http.Request, args *RPCquery, reply *RPCreply) (err error) { var ( - MAC string + i int + macAddr string + stdout io.ReadCloser UUID, original = args.Input[0], args.Input[1] ) - log.Debug("vm:uuid2mac") + log.Debug("vm:uuid2mac (%v:%v)", args.Input[0], args.Input[1]) // handles UUIDs if _, found := Daemon.Active[UUID]; found { - err = fmt.Errorf("Aborted: Another VM is "+ - "already running with the exact same UUID (%s)", UUID) + err = fmt.Errorf("Aborted: Another VM is already running with the "+ + "exact same UUID [%s]", UUID) } else { - for { - // we keep the loop just in case as the check - // above is supposed to avoid most failures... - // XXX - if MAC, err = - uuid2ip.GuestMACfromUUID(UUID); err == nil { - // var ip string - // if ip, err = uuid2ip.GuestIPfromMAC(MAC); err == nil { - // log.Info("GUEST IP will be %v", ip) + for i < 3 { + // + // we just can't call uuid2ip.GuestMACfromUUID(UUID) directly here. + // + // according to https://developer.apple.com/library/mac/documentation/DriversKernelHardware/Reference/vmnet/ + // one "can create a maximum of 32 interfaces with a limit of + // 4 per guest operating system" which in practice means that a + // given Pid/corectld instance in aggregate can't create more than + // 128 VMs (interfaces). + // by doing the lookup as an external process that we "unrelate" + // from its parent we get around this limitation and so each + // corectld session stops having an 2ˆ7 upper bound on the number + // the VMs it can create over its lifetime + // + cmd := exec.Command(session.Executable(), "uuid2mac", UUID) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + Setsid: false, + Pgid: 0, + } + if stdout, err = cmd.StdoutPipe(); err != nil { break - // } } - fmt.Println("=>", original, err) + rd := bufio.NewReader(stdout) + if err = cmd.Start(); err != nil { + break + } + macAddr, _ = rd.ReadString('\n') + macAddr = strings.TrimSuffix(macAddr, "\n") + if err = cmd.Wait(); err == nil { + if len(macAddr) > 0 { + if _, found := Daemon.Active[UUID]; !found { + // unlikely statistically but ... + break + } + } + } + log.Debug("=> %v:%v [%v]", original, err, i) if original != "random" { log.Warn("unable to guess the MAC Address from the provided "+ "UUID (%s). Using a randomly generated one\n", original) } UUID = uuid.NewV4().String() + i += 1 + } + if i == 3 && err != nil { + err = fmt.Errorf("Something went very wrong, as we're unable to " + + "generate a MAC address from the provided UUID. Please fill " + + "a bug at https://github.com/TheNewNormal/corectl/issues with " + + "this error and wait there for our feedback...") } } - reply.Output = []string{MAC, strings.ToUpper(UUID)} + reply.Output = []string{macAddr, strings.ToUpper(UUID)} return }