Your ROOT_URL in app.ini is unix://git.lalonde.me:3000/ but you are visiting https://git.lalonde.me/matth/subgraph-oz/commit/93ef716d948f4fdbf09b27d4b90c33b89abd47cb You should set ROOT_URL correctly, otherwise the web may not work correctly.

Adding reloading of profiles on SIGHUP, cleanup network code preparation for reconfigure routines

master
xSmurf 10 years ago
parent b72d77b891
commit 93ef716d94

@ -11,15 +11,16 @@ import (
"time"
//Internal
"github.com/op/go-logging"
//External
"github.com/op/go-logging"
"github.com/j-keck/arping"
"github.com/milosgajdos83/tenus"
)
func BridgeInit(bridgeMAC string, nmIgnoreFile string, log *logging.Logger) (*HostNetwork, error) {
if os.Getpid() == 1 {
panic(errors.New("Cannot use netinit from child."))
panic(errors.New("Cannot use BridgeInit from child."))
}
htn := &HostNetwork{
@ -39,8 +40,9 @@ func BridgeInit(bridgeMAC string, nmIgnoreFile string, log *logging.Logger) (*Ho
return nil, fmt.Errorf("Unable to create bridge %+v", err)
}
}
htn.Interface = br
if err := htn.configureBridgeInterface(br, log); err != nil {
if err := htn.configureBridgeInterface(log); err != nil {
return nil, err
}
@ -56,7 +58,17 @@ func BridgeInit(bridgeMAC string, nmIgnoreFile string, log *logging.Logger) (*Ho
}
return htn, nil
}
func (htn *HostNetwork) BridgeReconfigure(log *logging.Logger) error {
if os.Getpid() == 1 {
panic(errors.New("Cannot use BridgeReconfigure from child."))
}
htn.Gateway = nil
return htn.configureBridgeInterface(log)
// TODO: Reconfigure guest networks
}
func PrepareSandboxNetwork(htn *HostNetwork, log *logging.Logger) (*SandboxNetwork, error) {
@ -79,25 +91,25 @@ func PrepareSandboxNetwork(htn *HostNetwork, log *logging.Logger) (*SandboxNetwo
func NetInit(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.Logger) error {
if os.Getpid() == 1 {
panic(errors.New("Cannot use netSetup from child."))
panic(errors.New("Cannot use NetInit from child."))
}
// Seed random number generator (poorly but we're not doing crypto)
rand.Seed(time.Now().Unix() ^ int64((os.Getpid() + childPid)))
log.Info("Configuring host veth pair '%s' with: %s", stn.VethHost, stn.Ip+"/"+htn.Class)
/*
// Fetch the bridge from the ifname
br, err := tenus.BridgeFromName(ozDefaultInterfaceBridge)
if err != nil {
return fmt.Errorf("Unable to attach to bridge interface %, %s.", ozDefaultInterfaceBridge, err)
}
*/
// Make sure the bridge is configured and the link is up
// This really shouldn't be needed, but Network-Manager is a PITA
// and even if you actualy ignore the interface there's a race
// between the interface being created and setting it's hwaddr
//if err := htn.configureBridgeInterface(br, log); err != nil {
//if err := htn.configureBridgeInterface(log); err != nil {
// return fmt.Errorf("Unable to reconfigure bridge: %+v", err)
//}
@ -114,7 +126,7 @@ func NetInit(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.L
}
// Add the host side veth to the bridge
if err := br.AddSlaveIfc(vethIf); err != nil {
if err := htn.Interface.AddSlaveIfc(vethIf); err != nil {
return fmt.Errorf("Unable to add veth pair %s to bridge, %s.", stn.VethHost, err)
}
@ -124,8 +136,7 @@ func NetInit(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.L
}
// Assign the veth path to the namespace
pid := childPid
if err := veth.SetPeerLinkNsPid(pid); err != nil {
if err := veth.SetPeerLinkNsPid(childPid); err != nil {
return fmt.Errorf("Unable to add veth pair %s to namespace, %s.", stn.VethHost, err)
}
@ -136,14 +147,35 @@ func NetInit(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.L
}
// Set interface address in the namespace
if err := veth.SetPeerLinkNetInNs(pid, vethGuestIp, vethGuestIpNet, nil); err != nil {
if err := veth.SetPeerLinkNetInNs(childPid, vethGuestIp, vethGuestIpNet, nil); err != nil {
return fmt.Errorf("Unable to parse ip link in namespace, %s.", err)
}
stn.Veth = veth
return nil
}
func NetReconfigure(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.Logger) error {
if os.Getpid() == 1 {
panic(errors.New("Cannot use NetInit from child."))
}
// Parse the ip/class into the the appropriate formats
vethGuestIp, vethGuestIpNet, err := net.ParseCIDR(stn.Ip + "/" + htn.Class)
if err != nil {
return fmt.Errorf("Unable to parse ip %s, %s.", stn.Ip, err)
}
// Set interface address in the namespace
if err := stn.Veth.SetPeerLinkNetInNs(childPid, vethGuestIp, vethGuestIpNet, nil); err != nil {
return fmt.Errorf("Unable to parse ip link in namespace, %s.", err)
}
return nil
}
func (stn *SandboxNetwork) Cleanup(log *logging.Logger) {
if os.Getpid() == 1 {
panic(errors.New("Cannot use Cleanup from child."))
@ -157,10 +189,10 @@ func (stn *SandboxNetwork) Cleanup(log *logging.Logger) {
tenus.DeleteLink(stn.VethHost)
}
func (htn *HostNetwork) configureBridgeInterface(br tenus.Bridger, log *logging.Logger) error {
func (htn *HostNetwork) configureBridgeInterface(log *logging.Logger) error {
// Set the bridge mac address so it can be fucking ignored by Network-Manager.
if htn.BridgeMAC != "" {
if err := br.SetLinkMacAddress(htn.BridgeMAC); err != nil {
if err := htn.Interface.SetLinkMacAddress(htn.BridgeMAC); err != nil {
return fmt.Errorf("Unable to set MAC address for gateway", err)
}
}
@ -176,7 +208,7 @@ func (htn *HostNetwork) configureBridgeInterface(br tenus.Bridger, log *logging.
log.Info("Found available range: %+v", htn.GatewayNet.String())
}
if err := br.SetLinkIp(htn.Gateway, htn.GatewayNet); err != nil {
if err := htn.Interface.SetLinkIp(htn.Gateway, htn.GatewayNet); err != nil {
if os.IsExist(err) {
log.Info("Bridge IP appears to be already assigned")
} else {
@ -185,7 +217,7 @@ func (htn *HostNetwork) configureBridgeInterface(br tenus.Bridger, log *logging.
}
// Bridge the interface up
if err := br.SetLinkUp(); err != nil {
if err := htn.Interface.SetLinkUp(); err != nil {
return fmt.Errorf("Unable to bring bridge '%+v' up: %+v", ozDefaultInterfaceBridge, err)
}
@ -227,6 +259,10 @@ func (htn *HostNetwork) buildBridgeNetwork(addrs []net.Addr) error {
return nil
}
func FindEmptyRange() (net.IP, *net.IPNet, error) {
return findEmptyRange()
}
// Look at all the assigned IP address and try to find an available range
// Returns a ip range in the CIDR form if found or an empty string
func findEmptyRange() (net.IP, *net.IPNet, error) {
@ -246,7 +282,8 @@ func findEmptyRange() (net.IP, *net.IPNet, error) {
for _, netif := range ifs {
// Disable loopback and our bridge
if netif.Name == ozDefaultInterfaceBridge ||
strings.HasPrefix(netif.Name, "lo") {
strings.HasPrefix(netif.Name, "lo") ||
strings.HasPrefix(netif.Name, ozDefaultInterfacePrefix) {
continue
}
@ -307,3 +344,73 @@ func findEmptyRange() (net.IP, *net.IPNet, error) {
return nil, nil, errors.New("Could not find an available range")
}
// Try to find an unassigned IP address
// Do this by first trying ozMaxRandTries random IPs or, if that fails, sequentially
func getFreshIP(min, max uint64, log *logging.Logger) string {
var newIP string
for i := 0; i < ozMaxRandTries; i++ {
newIP = getRandIP(min, max)
if newIP != "" {
break
}
}
if newIP == "" {
log.Notice("Random IP lookup failed %d times, reverting to sequential select", ozMaxRandTries)
newIP = getScanIP(min, max)
}
return newIP
}
// Generate a random ip and arping it to see if it is available
// Returns the ip on success or an ip string is the ip is already taken
func getRandIP(min, max uint64) string {
if min > max {
return ""
}
dstIP := inet_ntoa(uint64(rand.Int63n(int64(max-min))) + min)
arping.SetTimeout(time.Millisecond * 150)
_, _, err := arping.PingOverIfaceByName(dstIP, ozDefaultInterfaceBridge)
if err == arping.ErrTimeout {
return dstIP.String()
} else if err != nil {
return dstIP.String()
}
return ""
}
// Go through all possible ips between min and max
// and arping each one until a free one is found
func getScanIP(min, max uint64) string {
if min > max {
return ""
}
for i := min; i < max; i++ {
dstIP := inet_ntoa(i)
arping.SetTimeout(time.Millisecond * 150)
_, _, err := arping.PingOverIfaceByName(dstIP, ozDefaultInterfaceBridge)
if err == arping.ErrTimeout {
return dstIP.String()
} else if err != nil {
return dstIP.String()
}
}
return ""
}

@ -8,6 +8,7 @@ import (
"strings"
"github.com/op/go-logging"
"github.com/milosgajdos83/tenus"
)
const (
@ -20,13 +21,15 @@ const (
type NetType string
const (
TYPE_HOST NetType = "host"
TYPE_EMPTY NetType = "empty"
const(
TYPE_HOST NetType = "host"
TYPE_EMPTY NetType = "empty"
TYPE_BRIDGE NetType = "bridge"
)
type HostNetwork struct {
// Bridge interface
Interface tenus.Bridger
// Gateway ip (bridge ip)
Gateway net.IP
// Gateway ip (bridge ip)
@ -43,11 +46,13 @@ type HostNetwork struct {
Max uint64
// Bridge interface MAC Address
BridgeMAC string
//
// The type of network configuration
Nettype NetType
}
type SandboxNetwork struct {
// veth interface is present
Interface tenus.Linker
// Name of the veth in the host
VethHost string
// Temporary name of the guest' veth in the host
@ -58,8 +63,10 @@ type SandboxNetwork struct {
Gateway net.IP
// IP class (ie: /24)
Class string
//
// The type of network configuration
Nettype NetType
// Host side virtual interface
Veth tenus.Vether
}
var privateNetworkRanges []string
@ -100,6 +107,9 @@ func NetPrint(log *logging.Logger) {
log.Info(strHeader)
for _, netif := range ifs {
if strings.HasPrefix(netif.Name, ozDefaultInterfacePrefix) {
continue
}
addrs, _ := netif.Addrs()
strLine = fmt.Sprintf("%-15.14s", netif.Name)

@ -4,15 +4,12 @@ import (
//Builtin
"errors"
"fmt"
"math/rand"
"os"
"time"
// Internal
"github.com/op/go-logging"
//"github.com/op/go-logging"
//External
"github.com/j-keck/arping"
"github.com/milosgajdos83/tenus"
)
@ -21,7 +18,7 @@ import (
// and the veth interface if requested
func NetSetup(stn *SandboxNetwork) error {
if os.Getpid() != 1 {
panic(errors.New("Cannot use NetSetip from parent."))
panic(errors.New("Cannot use NetSetup from parent."))
}
if err := setupLoopback(stn); err != nil {
@ -38,6 +35,19 @@ func NetSetup(stn *SandboxNetwork) error {
return nil
}
func (stn *SandboxNetwork) NetReconfigure() error {
if os.Getpid() != 1 {
panic(errors.New("Cannot use NetReconfigure from parent."))
}
// Set the link's default gateway
if err := stn.Interface.SetLinkDefaultGw(&stn.Gateway); err != nil {
return fmt.Errorf("Unable to set default route %s.", err)
}
return nil
}
func setupLoopback(stn *SandboxNetwork) error {
// Bring loopback interface up
lo, err := tenus.NewLinkFrom("lo")
@ -87,75 +97,7 @@ func setupVEth(stn *SandboxNetwork) error {
return fmt.Errorf("Unable to set default route %s.", err)
}
return nil
}
// Try to find an unassigned IP address
// Do this by first trying two random IPs or, if that fails, sequentially
func getFreshIP(min, max uint64, log *logging.Logger) string {
var newIP string
for i := 0; i < ozMaxRandTries; i++ {
newIP = getRandIP(min, max)
if newIP != "" {
break
}
}
if newIP == "" {
log.Notice("Random IP lookup failed %d times, reverting to sequential select", ozMaxRandTries)
newIP = getScanIP(min, max)
}
return newIP
}
// Generate a random ip and arping it to see if it is available
// Returns the ip on success or an ip string is the ip is already taken
func getRandIP(min, max uint64) string {
if min > max {
return ""
}
dstIP := inet_ntoa(uint64(rand.Int63n(int64(max-min))) + min)
arping.SetTimeout(time.Millisecond * 150)
_, _, err := arping.PingOverIfaceByName(dstIP, ozDefaultInterfaceBridge)
if err == arping.ErrTimeout {
return dstIP.String()
} else if err != nil {
return dstIP.String()
}
return ""
}
// Go through all possible ips between min and max
// and arping each one until a free one is found
func getScanIP(min, max uint64) string {
if min > max {
return ""
}
for i := min; i < max; i++ {
dstIP := inet_ntoa(i)
arping.SetTimeout(time.Millisecond * 150)
_, _, err := arping.PingOverIfaceByName(dstIP, ozDefaultInterfaceBridge)
if err == arping.ErrTimeout {
return dstIP.String()
} else if err != nil {
return dstIP.String()
}
}
return ""
stn.Interface = ifc
return nil
}

@ -1,6 +1,6 @@
package network
import (
import(
//Builtin
"fmt"
"io"
@ -16,16 +16,16 @@ import (
type ProxyType string
const (
const(
PROXY_CLIENT ProxyType = "client"
PROXY_SERVER ProxyType = "server"
)
type ProtoType string
const (
PROTO_TCP ProtoType = "tcp"
PROTO_UDP ProtoType = "udp"
const(
PROTO_TCP ProtoType = "tcp"
PROTO_UDP ProtoType = "udp"
PROTO_SOCKET ProtoType = "socket"
)
@ -146,7 +146,7 @@ func nsSocketListener(fd uintptr, proto ProtoType, lAddr string) (net.Listener,
/**
* Connect/Server
**/
func proxyServerConn(pid int, conn *net.Conn, proto ProtoType, rAddr string, log *logging.Logger, ready sync.WaitGroup) error {
func proxyServerConn(pid int, conn *net.Conn, proto ProtoType, rAddr string, log *logging.Logger, ready sync.WaitGroup) (error) {
rConn, err := socketConnect(pid, proto, rAddr)
if err != nil {
log.Error("Socket: %+v.", err)
@ -159,7 +159,7 @@ func proxyServerConn(pid int, conn *net.Conn, proto ProtoType, rAddr string, log
return nil
}
func newProxyServer(pid int, proto ProtoType, dest string, port int, log *logging.Logger, ready sync.WaitGroup) error {
func newProxyServer(pid int, proto ProtoType, dest string, port int, log *logging.Logger, ready sync.WaitGroup) (error) {
if dest == "" {
dest = "127.0.0.1"
}

@ -2,6 +2,9 @@ package daemon
import (
"fmt"
"os"
"os/signal"
"path"
"strings"
"syscall"
@ -10,8 +13,6 @@ import (
"github.com/subgraph/oz/network"
"github.com/op/go-logging"
"os"
"path"
)
type daemonState struct {
@ -39,31 +40,27 @@ func Main() {
d.handleLogs,
)
if err != nil {
d.log.Warning("Error running server: %v", err)
d.log.Error("Error running server: %v", err)
}
}
func initialize() *daemonState {
sigs := make(chan os.Signal)
signal.Notify(sigs, syscall.SIGHUP, syscall.SIGUSR1)
d := &daemonState{}
d.initializeLogging()
var config *oz.Config
config, err := oz.LoadConfig(oz.DefaultConfigPath)
config, err := d.loadConfig()
if err != nil {
if os.IsNotExist(err) {
d.log.Info("Configuration file (%s) is missing, using defaults.", oz.DefaultConfigPath)
config = oz.NewDefaultConfig()
} else {
d.log.Error("Could not load configuration: %s", oz.DefaultConfigPath, err)
os.Exit(1)
}
d.log.Error("Could not load configuration: %s", oz.DefaultConfigPath, err)
os.Exit(1)
}
d.log.Info("Oz Global Config: %+v", config)
d.config = config
ps, err := oz.LoadProfiles(config.ProfileDir)
ps, err := d.loadProfiles(d.config.ProfileDir)
if err != nil {
d.log.Fatalf("Failed to load profiles: %v", err)
os.Exit(1)
}
d.Debug("%d profiles loaded", len(ps))
d.profiles = ps
oz.ReapChildProcs(d.log, d.handleChildExit)
d.nextSboxId = 1
@ -91,9 +88,55 @@ func initialize() *daemonState {
d.log.Fatalf("Failed to create sockets directory: %v", err)
}
go d.processSignals(sigs)
return d
}
func (d *daemonState) loadConfig() (*oz.Config, error) {
config, err := oz.LoadConfig(oz.DefaultConfigPath)
if err != nil {
if os.IsNotExist(err) {
d.log.Info("Configuration file (%s) is missing, using defaults.", oz.DefaultConfigPath)
config = oz.NewDefaultConfig()
} else {
return nil, err
}
}
d.log.Info("Oz Global Config: %+v", config)
return config, nil
}
func (d *daemonState) loadProfiles(profileDir string) (oz.Profiles, error) {
ps, err := oz.LoadProfiles(profileDir)
if err != nil {
return nil, err
}
d.Debug("%d profiles loaded", len(ps))
return ps, nil
}
func (d *daemonState) processSignals(c <-chan os.Signal) {
for {
sig := <-c
switch sig {
case syscall.SIGHUP:
d.log.Notice("Received HUP signal, reloading profiles.")
ps, err := d.loadProfiles(d.config.ProfileDir)
if err != nil {
d.log.Error("Failed to reload profiles: %v", err)
return
}
d.profiles = ps
case syscall.SIGUSR1:
d.handleNetworkReconfigure()
}
}
}
func (d *daemonState) handleChildExit(pid int, wstatus syscall.WaitStatus) {
d.Debug("Child process pid=%d exited with status %d", pid, wstatus.ExitStatus())
@ -111,6 +154,7 @@ func runServer(log *logging.Logger, args ...interface{}) error {
if err != nil {
return err
}
return s.Run()
}
@ -288,3 +332,37 @@ func (d *daemonState) handleLogs(logs *LogsMsg, msg *ipc.Message) error {
msg.Respond(&OkMsg{})
return nil
}
func (d *daemonState) handleNetworkReconfigure() {
brIP, brNet, err := network.FindEmptyRange()
if err != nil {
return
}
if brIP.Equal(d.network.Gateway) {
d.log.Notice("Range is still available, not reconfiguring.")
return
}
d.log.Notice("Network has changed, reconfiguring with %s %s", brIP.String(), brNet.String())
if err := d.network.BridgeReconfigure(d.log); err != nil {
d.log.Error("Unable to reconfigure bridge network: %v", err)
return
}
/*
for _, sbox := range d.sandboxes {
if sbox.profile.Networking.Nettype == network.TYPE_BRIDGE {
sbox.network, err := network.PrepareSandboxNetwork(d.network, d.log)
if err != nil {
d.log.Error("Unable to prepare reconfigure of sandbox `%s` networking: %v", sbox.profile.Name, err)
continue
}
if err := d.network.NetReconfigure(d.network, sbox.network, sbox.Pid, d.log); err != nil {
d.log.Error("Unable to reconfigure sandbox `%s` networking: %v", sbox.profile.Name, err)
continue
}
// TODO: Reconfigure default gateway inside sandbox
}
}
*/
return
}

@ -188,6 +188,14 @@ func (d *daemonState) launch(p *oz.Profile, msg *LaunchMsg, uid, gid uint32, log
}()
}
cmd2 := exec.Command("/bin/mount")
stdout, err := cmd2.Output()
if err != nil {
println(err.Error())
}
log.Debug(string(stdout))
d.nextSboxId += 1
d.sandboxes = append(d.sandboxes, sbox)
return sbox, nil

@ -207,6 +207,7 @@ func handleKill(c *cli.Context) {
fmt.Fprintf(os.Stderr, "Kill command failed:", err)
os.Exit(1)
}
return
}
id, err := strconv.Atoi(c.Args()[0])
if err != nil {

Loading…
Cancel
Save