<commit_before>package main

import (
	//"fmt"
	"encoding/gob"
	"log"
	"net"
	"os"
	"time"

	"github.com/udhos/fugo/msg"
)

type world struct {
	playerTab []*player
	playerAdd chan *player
	playerDel chan *player
	input     chan inputMsg
}

type inputMsg struct {
	player *player
	msg    interface{}
}

type player struct {
	conn      net.Conn
	output    chan msg.Update
	fuelStart time.Time
}

func main() {
	addr := ":8080"
	if len(os.Args) > 1 {
		addr = os.Args[1]
	}
	w := world{
		playerTab: []*player{},
		playerAdd: make(chan *player),
		playerDel: make(chan *player),
	}
	go listenAndServe(&w, addr)

	ticker := time.NewTicker(3000 * time.Millisecond)

	log.Printf("main: entering service loop")
SERVICE:
	for {
		select {
		case p := <-w.playerAdd:
			log.Printf("player add: %v", p)
			w.playerTab = append(w.playerTab, p)
			p.fuelStart = time.Now() // reset fuel
		case p := <-w.playerDel:
			log.Printf("player del: %v", p)
			for i, pl := range w.playerTab {
				if pl == p {
					w.playerTab = append(w.playerTab[:i], w.playerTab[i+1:]...)
					log.Printf("player removed: %v", p)
					continue SERVICE
				}
			}
			log.Printf("player not found: %v", p)
		case i := <-w.input:
			log.Printf("input: %v", i)
		case t := <-ticker.C:
			log.Printf("tick: %v", t)
			for i, c := range w.playerTab {
				// calculate fuel for player c
				rechargeRate := float32(1.0 / 3.0) // 1 unit every 3 seconds
				fuel := rechargeRate * float32(int64(time.Since(c.fuelStart))/1000000000)
				if fuel > 10.0 {
					fuel = 10.0 // clamp max fuel
				}
				update := msg.Update{Fuel: fuel}
				log.Printf("sending update=%v to player %d=%v", update, i, c)
				c.output <- update // send update to player c
			}
		}
	}
}

func listenAndServe(w *world, addr string) {

	log.Printf("serving on TCP %s", addr)

	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Printf("listen on TCP %s: %s", addr, err)
		return
	}

	defer listener.Close()

	gob.Register(msg.Update{})

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Printf("accept on TCP %s: %s", addr, err)
			continue
		}
		go connHandler(w, conn)
	}
}

func connHandler(w *world, conn net.Conn) {
	log.Printf("handler for connection %v", conn)

	defer conn.Close()

	p := &player{
		conn:   conn,
		output: make(chan msg.Update),
	}

	w.playerAdd <- p // register player
	quitWriter := make(chan int)

	go func() {
		// copy from socket into input channel
		dec := gob.NewDecoder(conn)
		for {
			var m msg.Update
			if err := dec.Decode(&m); err != nil {
				log.Printf("handler: Decode: %v", err)
				break
			}
			w.input <- inputMsg{player: p, msg: m}
		}
		close(quitWriter) // send quit request to output goroutine
		log.Printf("handler: reader goroutine exiting")
	}()

	// copy from output channel into socket
	enc := gob.NewEncoder(conn)
LOOP:
	for {
		select {
		case <-quitWriter:
			log.Printf("handler: quit request")
			break LOOP
		case m := <-p.output:
			if err := enc.Encode(&m); err != nil {
				log.Printf("handler: Encode: %v", err)
				break LOOP
			}
		}
	}
	w.playerDel <- p // deregister player
	log.Printf("handler: writer goroutine exiting")
}
<commit_msg>Arena server exits on listen error.<commit_after>package main

import (
	"encoding/gob"
	"fmt"
	"log"
	"net"
	"os"
	"time"

	"github.com/udhos/fugo/msg"
)

type world struct {
	playerTab []*player
	playerAdd chan *player
	playerDel chan *player
	input     chan inputMsg
}

type inputMsg struct {
	player *player
	msg    interface{}
}

type player struct {
	conn      net.Conn
	output    chan msg.Update
	fuelStart time.Time
}

func main() {
	addr := ":8080"
	if len(os.Args) > 1 {
		addr = os.Args[1]
	}
	w := world{
		playerTab: []*player{},
		playerAdd: make(chan *player),
		playerDel: make(chan *player),
	}
	if errListen := listenAndServe(&w, addr); errListen != nil {
		log.Printf("main: %v", errListen)
		return
	}

	ticker := time.NewTicker(3000 * time.Millisecond)

	log.Printf("main: entering service loop")
SERVICE:
	for {
		select {
		case p := <-w.playerAdd:
			log.Printf("player add: %v", p)
			w.playerTab = append(w.playerTab, p)
			p.fuelStart = time.Now() // reset fuel
		case p := <-w.playerDel:
			log.Printf("player del: %v", p)
			for i, pl := range w.playerTab {
				if pl == p {
					w.playerTab = append(w.playerTab[:i], w.playerTab[i+1:]...)
					log.Printf("player removed: %v", p)
					continue SERVICE
				}
			}
			log.Printf("player not found: %v", p)
		case i := <-w.input:
			log.Printf("input: %v", i)
		case t := <-ticker.C:
			log.Printf("tick: %v", t)
			for i, c := range w.playerTab {
				// calculate fuel for player c
				rechargeRate := float32(1.0 / 3.0) // 1 unit every 3 seconds
				fuel := rechargeRate * float32(int64(time.Since(c.fuelStart))/1000000000)
				if fuel > 10.0 {
					fuel = 10.0 // clamp max fuel
				}
				update := msg.Update{Fuel: fuel}
				log.Printf("sending update=%v to player %d=%v", update, i, c)
				c.output <- update // send update to player c
			}
		}
	}
}

func listenAndServe(w *world, addr string) error {

	log.Printf("serving on TCP %s", addr)

	listener, errListen := net.Listen("tcp", addr)
	if errListen != nil {
		return fmt.Errorf("listenAndServe: %s: %v", addr, errListen)
	}

	gob.Register(msg.Update{})

	go func() {
		for {
			conn, err := listener.Accept()
			if err != nil {
				log.Printf("accept on TCP %s: %s", addr, err)
				continue
			}
			go connHandler(w, conn)
		}
	}()

	return nil
}

func connHandler(w *world, conn net.Conn) {
	log.Printf("handler for connection %v", conn)

	defer conn.Close()

	p := &player{
		conn:   conn,
		output: make(chan msg.Update),
	}

	w.playerAdd <- p // register player
	quitWriter := make(chan int)

	go func() {
		// copy from socket into input channel
		dec := gob.NewDecoder(conn)
		for {
			var m msg.Update
			if err := dec.Decode(&m); err != nil {
				log.Printf("handler: Decode: %v", err)
				break
			}
			w.input <- inputMsg{player: p, msg: m}
		}
		close(quitWriter) // send quit request to output goroutine
		log.Printf("handler: reader goroutine exiting")
	}()

	// copy from output channel into socket
	enc := gob.NewEncoder(conn)
LOOP:
	for {
		select {
		case <-quitWriter:
			log.Printf("handler: quit request")
			break LOOP
		case m := <-p.output:
			if err := enc.Encode(&m); err != nil {
				log.Printf("handler: Encode: %v", err)
				break LOOP
			}
		}
	}
	w.playerDel <- p // deregister player
	log.Printf("handler: writer goroutine exiting")
}
<|endoftext|>


<commit_before>package jigo

import (
	"fmt"
	"strings"
)

// Tree is the representation of a single parsed template.
type Tree struct {
	Name      string    // name of the template represented by the tree.
	ParseName string    // name of the top-level template during parsing, for error messages.
	Root      *ListNode // top-level root of the tree.
	text      string    // text parsed to create the template (or its parent)
	// Parsing only; cleared after parse.
	funcs []map[string]interface{}
	lex   *lexer
	// FIXME: the peek max is based on the way that the grammar works,
	// but I don't know enough about the expression grammar i've loosely
	// described to know if 3 is sufficient.
	token     [3]item // three-token lookahead for parser.
	peekCount int
	vars      []string // variables defined at the moment.
}

// next returns the next token.
func (t *Tree) next() item {
	if t.peekCount > 0 {
		t.peekCount--
	} else {
		t.token[0] = t.lex.nextItem()
	}
	return t.token[t.peekCount]
}

// backup backs the input stream up one token.
func (t *Tree) backup() {
	t.peekCount++
}

// backup2 backs the input stream up two tokens.
// The zeroth token is already there.
func (t *Tree) backup2(t1 item) {
	t.token[1] = t1
	t.peekCount = 2
}

// backup3 backs the input stream up three tokens
// The zeroth token is already there.
func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
	t.token[1] = t1
	t.token[2] = t2
	t.peekCount = 3
}

// peek returns but does not consume the next token.
func (t *Tree) peek() item {
	if t.peekCount > 0 {
		return t.token[t.peekCount-1]
	}
	t.peekCount = 1
	t.token[0] = t.lex.nextItem()
	return t.token[0]
}

// nextNonSpace returns the next non-space token.
func (t *Tree) nextNonSpace() (token item) {
	for {
		token = t.next()
		if token.typ != tokenWhitespace {
			break
		}
	}
	return token
}

// peekNonSpace returns but does not consume the next non-space token.
func (t *Tree) peekNonSpace() (token item) {
	for {
		token = t.next()
		if token.typ != tokenWhitespace {
			break
		}
	}
	t.backup()
	return token
}

// Parsing.

// New allocates a new parse tree with the given name.
func New(name string, funcs ...map[string]interface{}) *Tree {
	return &Tree{
		Name:  name,
		funcs: funcs,
	}
}

// ErrorContext returns a textual representation of the location of the node in the input text.
func (t *Tree) ErrorContext(n Node) (location, context string) {
	pos := int(n.Position())
	text := t.text[:pos]
	byteNum := strings.LastIndex(text, "\n")
	if byteNum == -1 {
		byteNum = pos // On first line.
	} else {
		byteNum++ // After the newline.
		byteNum = pos - byteNum
	}
	lineNum := 1 + strings.Count(text, "\n")
	context = n.String()
	if len(context) > 20 {
		context = fmt.Sprintf("%.20s...", context)
	}
	return fmt.Sprintf("%s:%d:%d", t.ParseName, lineNum, byteNum), context
}

// errorf formats the error and terminates processing.
func (t *Tree) errorf(format string, args ...interface{}) {
	t.Root = nil
	format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
	panic(fmt.Errorf(format, args...))
}
<commit_msg>continue to pull parts of the text/template parser, discarding things like treeMap, funcs, and vars which dont apply to jinja<commit_after>package jigo

import (
	"fmt"
	"runtime"
	"strings"
)

// Tree is the representation of a single parsed template.
type Tree struct {
	Name      string    // name of the template represented by the tree.
	ParseName string    // name of the top-level template during parsing, for error messages.
	Root      *ListNode // top-level root of the tree.
	text      string    // text parsed to create the template (or its parent)
	// Parsing only; cleared after parse.
	// funcs []map[string]interface{}
	lex *lexer
	// FIXME: the peek max is based on the way that the grammar works,
	// but I don't know enough about the expression grammar i've loosely
	// described to know if 3 is sufficient.
	token     [3]item // three-token lookahead for parser.
	peekCount int
	// vars      []string // variables defined at the moment.
}

// Copy returns a copy of the Tree. Any parsing state is discarded.
func (t *Tree) Copy() *Tree {
	if t == nil {
		return nil
	}
	return &Tree{
		Name:      t.Name,
		ParseName: t.ParseName,
		Root:      t.Root.CopyList(),
		text:      t.text,
	}
}

// next returns the next token.
func (t *Tree) next() item {
	if t.peekCount > 0 {
		t.peekCount--
	} else {
		t.token[0] = t.lex.nextItem()
	}
	return t.token[t.peekCount]
}

// backup backs the input stream up one token.
func (t *Tree) backup() {
	t.peekCount++
}

// backup2 backs the input stream up two tokens.
// The zeroth token is already there.
func (t *Tree) backup2(t1 item) {
	t.token[1] = t1
	t.peekCount = 2
}

// backup3 backs the input stream up three tokens
// The zeroth token is already there.
func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
	t.token[1] = t1
	t.token[2] = t2
	t.peekCount = 3
}

// peek returns but does not consume the next token.
func (t *Tree) peek() item {
	if t.peekCount > 0 {
		return t.token[t.peekCount-1]
	}
	t.peekCount = 1
	t.token[0] = t.lex.nextItem()
	return t.token[0]
}

// nextNonSpace returns the next non-space token.
func (t *Tree) nextNonSpace() (token item) {
	for {
		token = t.next()
		if token.typ != tokenWhitespace {
			break
		}
	}
	return token
}

// peekNonSpace returns but does not consume the next non-space token.
func (t *Tree) peekNonSpace() (token item) {
	for {
		token = t.next()
		if token.typ != tokenWhitespace {
			break
		}
	}
	t.backup()
	return token
}

// Parsing.

// New allocates a new parse tree with the given name.
func newParser(name string) *Tree {
	return &Tree{
		Name: name,
	}
}

// ErrorContext returns a textual representation of the location of the node in the input text.
func (t *Tree) ErrorContext(n Node) (location, context string) {
	pos := int(n.Position())
	text := t.text[:pos]
	byteNum := strings.LastIndex(text, "\n")
	if byteNum == -1 {
		byteNum = pos // On first line.
	} else {
		byteNum++ // After the newline.
		byteNum = pos - byteNum
	}
	lineNum := 1 + strings.Count(text, "\n")
	context = n.String()
	if len(context) > 20 {
		context = fmt.Sprintf("%.20s...", context)
	}
	return fmt.Sprintf("%s:%d:%d", t.ParseName, lineNum, byteNum), context
}

// errorf formats the error and terminates processing.
func (t *Tree) errorf(format string, args ...interface{}) {
	t.Root = nil
	format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
	panic(fmt.Errorf(format, args...))
}

// recover is the handler that turns panics into returns from the top level of Parse.
func (t *Tree) recover(errp *error) {
	e := recover()
	if e != nil {
		if _, ok := e.(runtime.Error); ok {
			panic(e)
		}
		if t != nil {
			t.stopParse()
		}
		*errp = e.(error)
	}
	return
}

// startParse initializes the parser, using the lexer.
func (t *Tree) startParse(lex *lexer) {
	t.Root = nil
	t.lex = lex
}

// stopParse terminates parsing.
func (t *Tree) stopParse() {
	t.lex = nil
}

// Parse parses the template given the lexer.
func (t *Tree) Parse(lex *lexer) (tree *Tree, err error) {
	defer t.recover(&err)
	t.ParseName = t.Name
	t.startParse(lex)
	t.text = lex.input
	t.parse()
	t.stopParse()
	return t, nil
}

// -- parsing --

// parse is the top-level parser for a template, essentially the same
// as itemList except it also parses {{define}} actions.
// It runs to EOF.
func (t *Tree) parse() (next Node) {
	t.Root = newList(t.peek().pos)
	for t.peek().typ != tokenEOF {
		if t.peek().typ == tokenBlockBegin {
			/*
				delim := t.next()
				if t.nextNonSpace().typ == itemDefine {
					newT := New("definition") // name will be updated once we know it.
					newT.text = t.text
					newT.ParseName = t.ParseName
					newT.startParse(t.funcs, t.lex)
					newT.parseDefinition(treeSet)
					continue

				}
				t.backup2(delim)
			*/
		}
		/*
			n := t.textOrAction()
			if n.Type() == nodeEnd {
				t.errorf("unexpected %s", n)
			}
			t.Root.append(n)
		*/
	}
	return nil
}
<|endoftext|>


<commit_before>package net

import (
	"bufio"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/heidi-ann/ios/config"
	"github.com/heidi-ann/ios/msgs"

	"github.com/golang/glog"
)

type peerHandler struct {
	id       int
	peers    []config.NetAddress
	failures *msgs.FailureNotifier
	net      *msgs.PeerNet
}

// iterative through peers and check if there is a handler for each
// try to create one if not, report failure if not possible
func (ph *peerHandler) checkPeer() {
	for i := range ph.peers {
		if !ph.failures.IsConnected(i) {
			cn, err := net.Dial("tcp", ph.peers[i].ToString())
			if err == nil {
				go ph.handlePeer(cn, true)
			} else {
				go ph.net.OutgoingUnicast[i].Discard()
			}
		}
	}
}

// handlePeer handles a peer connection until closed
func (ph *peerHandler) handlePeer(cn net.Conn, init bool) {
	addr := cn.RemoteAddr().String()
	if init {
		glog.Info("Outgoing peer connection to ", addr)
	} else {
		glog.Info("Incoming peer connection from ", addr)
	}

	defer cn.Close()
	defer glog.Warningf("Connection closed from %s ", addr)

	// handle requests
	reader := bufio.NewReader(cn)
	writer := bufio.NewWriter(cn)

	// exchange peer ID's via handshake
	_, _ = writer.WriteString(strconv.Itoa(ph.id) + "\n")
	_ = writer.Flush()
	text, _ := reader.ReadString('\n')
	glog.V(1).Info("Received ", text)
	peerID, err := strconv.Atoi(strings.Trim(text, "\n"))
	if err != nil {
		glog.Warning(err)
		return
	}

	// check ID is expected
	if peerID < 0 || peerID >= len(ph.peers) || peerID == ph.id  {
		glog.Warning("Unexpected peer ID ", peerID)
		return
	}

	// check IP address is as expected
	// TODO: allow dynamic changes of IP
	actualAddr := strings.Split(addr, ":")[0]
	if ph.peers[peerID].Address != actualAddr {
		glog.Warning("Peer ID ", peerID, " has connected from an unexpected address ", actualAddr,
			" expected ", ph.peers[peerID].Address)
		return
	}

	glog.Infof("Ready to handle traffic from peer %d at %s ", peerID, addr)
	err = ph.failures.NowConnected(peerID)
	if err != nil {
		glog.Warning(err)
		return
	}

	closeErr := make(chan error)
	go func() {
		for {
			// read request
			glog.V(1).Infof("Ready for next message from %d", peerID)
			text, err := reader.ReadBytes(byte('\n'))
			if err != nil {
				glog.Warning(err)
				closeErr <- err
				break
			}
			glog.V(1).Infof("Read from peer %d: ", peerID, string(text))
			err = ph.net.Incoming.BytesToProtoMsg(text)
			if err != nil {
				glog.Warning(err)
			}

		}
	}()

	go func() {
		for {
			// send reply
			glog.V(1).Infof("Ready to send message to %d", peerID)
			b, err := ph.net.OutgoingUnicast[peerID].ProtoMsgToBytes()
			if err != nil {
				glog.Fatal("Could not marshal message")
			}
			glog.V(1).Infof("Sending to %d: %s", peerID, string(b))
			_, err = writer.Write(b)
			if err != nil {
				glog.Warning(err)
				// return packet for retry
				ph.net.OutgoingUnicast[peerID].BytesToProtoMsg(b)
				closeErr <- err
				break
			}
			_, err = writer.Write([]byte("\n"))
			if err != nil {
				glog.Warning(err)
				// return packet for retry
				ph.net.OutgoingUnicast[peerID].BytesToProtoMsg(b)
				closeErr <- err
				break
			}
			// TODO: BUG need to retry packet
			err = writer.Flush()
			if err != nil {
				glog.Warning(err)
				// return packet for retry
				ph.net.OutgoingUnicast[peerID].BytesToProtoMsg(b)
				closeErr <- err
				break
			}
			glog.V(1).Info("Sent")
		}
	}()

	// block until connection fails
	<-closeErr

	// tidy up
	glog.Warningf("No longer able to handle traffic from peer %d at %s ", peerID, addr)
	ph.failures.NowDisconnected(peerID)
}

// SetupPeers is an async function to handle/start peer connections
// TODO: switch to sync function
func SetupPeers(localId int, addresses []config.NetAddress, peerNet *msgs.PeerNet, fail *msgs.FailureNotifier) error {
	peerHandler := peerHandler{
		id:       localId,
		peers:    addresses,
		failures: fail,
		net:      peerNet,
	}

	//set up peer server
	glog.Info("Starting up peer server on ", addresses[peerHandler.id].Port)
	listeningPort := ":" + strconv.Itoa(addresses[peerHandler.id].Port)
	lnPeers, err := net.Listen("tcp", listeningPort)
	if err != nil {
		glog.Info("Unable to start listen for peers")
		return err
	}

	// handle local peer (without sending network traffic)
	peerHandler.failures.NowConnected(peerHandler.id)
	from := &(peerHandler.net.Incoming)
	go from.Forward(peerHandler.net.OutgoingUnicast[peerHandler.id])

	// handle for incoming peers
	go func() {
		for {
			conn, err := lnPeers.Accept()
			if err != nil {
				glog.Fatal(err)
			}
			go (&peerHandler).handlePeer(conn, false)
		}
	}()

	// regularly check if all peers are connected and retry if not
	go func() {
		for {
			(&peerHandler).checkPeer()
			time.Sleep(500 * time.Millisecond)
		}
	}()

	return nil
}
<commit_msg>allowing multiple peer connections<commit_after>package net

import (
	"bufio"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/heidi-ann/ios/config"
	"github.com/heidi-ann/ios/msgs"

	"github.com/golang/glog"
)

type peerHandler struct {
	id       int
	peers    []config.NetAddress
	failures *msgs.FailureNotifier
	net      *msgs.PeerNet
}

// iterative through peers and check if there is a handler for each
// try to create one if not, report failure if not possible
func (ph *peerHandler) checkPeer() {
	for i := range ph.peers {
		if !ph.failures.IsConnected(i) {
			cn, err := net.Dial("tcp", ph.peers[i].ToString())
			if err == nil {
				go ph.handlePeer(cn, true)
			} else {
				go ph.net.OutgoingUnicast[i].Discard()
			}
		}
	}
}

// handlePeer handles a peer connection until closed
func (ph *peerHandler) handlePeer(cn net.Conn, init bool) {
	addr := cn.RemoteAddr().String()
	if init {
		glog.Info("Outgoing peer connection to ", addr)
	} else {
		glog.Info("Incoming peer connection from ", addr)
	}

	defer cn.Close()
	defer glog.Warningf("Connection closed from %s ", addr)

	// handle requests
	reader := bufio.NewReader(cn)
	writer := bufio.NewWriter(cn)

	// exchange peer ID's via handshake
	_, _ = writer.WriteString(strconv.Itoa(ph.id) + "\n")
	_ = writer.Flush()
	text, _ := reader.ReadString('\n')
	glog.V(1).Info("Received ", text)
	peerID, err := strconv.Atoi(strings.Trim(text, "\n"))
	if err != nil {
		glog.Warning(err)
		return
	}

	// check ID is expected
	if peerID < 0 || peerID >= len(ph.peers) || peerID == ph.id  {
		glog.Warning("Unexpected peer ID ", peerID)
		return
	}

	// check IP address is as expected
	// TODO: allow dynamic changes of IP
	actualAddr := strings.Split(addr, ":")[0]
	if ph.peers[peerID].Address != actualAddr {
		glog.Warning("Peer ID ", peerID, " has connected from an unexpected address ", actualAddr,
			" expected ", ph.peers[peerID].Address)
		return
	}

	glog.Infof("Ready to handle traffic from peer %d at %s ", peerID, addr)
	err = ph.failures.NowConnected(peerID)
	if err != nil {
		glog.Warning(err)
	}

	closeErr := make(chan error)
	go func() {
		for {
			// read request
			glog.V(1).Infof("Ready for next message from %d", peerID)
			text, err := reader.ReadBytes(byte('\n'))
			if err != nil {
				glog.Warning(err)
				closeErr <- err
				break
			}
			glog.V(1).Infof("Read from peer %d: ", peerID, string(text))
			err = ph.net.Incoming.BytesToProtoMsg(text)
			if err != nil {
				glog.Warning(err)
			}

		}
	}()

	go func() {
		for {
			// send reply
			glog.V(1).Infof("Ready to send message to %d", peerID)
			b, err := ph.net.OutgoingUnicast[peerID].ProtoMsgToBytes()
			if err != nil {
				glog.Fatal("Could not marshal message")
			}
			glog.V(1).Infof("Sending to %d: %s", peerID, string(b))
			_, err = writer.Write(b)
			if err != nil {
				glog.Warning(err)
				// return packet for retry
				ph.net.OutgoingUnicast[peerID].BytesToProtoMsg(b)
				closeErr <- err
				break
			}
			_, err = writer.Write([]byte("\n"))
			if err != nil {
				glog.Warning(err)
				// return packet for retry
				ph.net.OutgoingUnicast[peerID].BytesToProtoMsg(b)
				closeErr <- err
				break
			}
			// TODO: BUG need to retry packet
			err = writer.Flush()
			if err != nil {
				glog.Warning(err)
				// return packet for retry
				ph.net.OutgoingUnicast[peerID].BytesToProtoMsg(b)
				closeErr <- err
				break
			}
			glog.V(1).Info("Sent")
		}
	}()

	// block until connection fails
	<-closeErr

	// tidy up
	glog.Warningf("No longer able to handle traffic from peer %d at %s ", peerID, addr)
	ph.failures.NowDisconnected(peerID)
}

// SetupPeers is an async function to handle/start peer connections
// TODO: switch to sync function
func SetupPeers(localId int, addresses []config.NetAddress, peerNet *msgs.PeerNet, fail *msgs.FailureNotifier) error {
	peerHandler := peerHandler{
		id:       localId,
		peers:    addresses,
		failures: fail,
		net:      peerNet,
	}

	//set up peer server
	glog.Info("Starting up peer server on ", addresses[peerHandler.id].Port)
	listeningPort := ":" + strconv.Itoa(addresses[peerHandler.id].Port)
	lnPeers, err := net.Listen("tcp", listeningPort)
	if err != nil {
		glog.Info("Unable to start listen for peers")
		return err
	}

	// handle local peer (without sending network traffic)
	peerHandler.failures.NowConnected(peerHandler.id)
	from := &(peerHandler.net.Incoming)
	go from.Forward(peerHandler.net.OutgoingUnicast[peerHandler.id])

	// handle for incoming peers
	go func() {
		for {
			conn, err := lnPeers.Accept()
			if err != nil {
				glog.Fatal(err)
			}
			go (&peerHandler).handlePeer(conn, false)
		}
	}()

	// regularly check if all peers are connected and retry if not
	go func() {
		for {
			(&peerHandler).checkPeer()
			time.Sleep(500 * time.Millisecond)
		}
	}()

	return nil
}
<|endoftext|>


<commit_before>package peerstore

import (
	"testing"
	"time"

	"github.com/libp2p/go-libp2p-peer"
	ma "github.com/multiformats/go-multiaddr"
)

func IDS(t *testing.T, ids string) peer.ID {
	id, err := peer.IDB58Decode(ids)
	if err != nil {
		t.Fatalf("id %q is bad: %s", ids, err)
	}
	return id
}

func MA(t *testing.T, m string) ma.Multiaddr {
	maddr, err := ma.NewMultiaddr(m)
	if err != nil {
		t.Fatal(err)
	}
	return maddr
}

func testHas(t *testing.T, exp, act []ma.Multiaddr) {
	if len(exp) != len(act) {
		t.Fatal("lengths not the same")
	}

	for _, a := range exp {
		found := false

		for _, b := range act {
			if a.Equal(b) {
				found = true
				break
			}
		}

		if !found {
			t.Fatalf("expected address %s not found", a)
		}
	}
}

func TestAddresses(t *testing.T) {

	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	id2 := IDS(t, "QmRmPL3FDZKE3Qiwv1RosLdwdvbvg17b2hB39QPScgWKKZ")
	id3 := IDS(t, "QmPhi7vBsChP7sjRoZGgg7bcKqF6MmCcQwvRbDte8aJ6Kn")
	id4 := IDS(t, "QmPhi7vBsChP7sjRoZGgg7bcKqF6MmCcQwvRbDte8aJ5Kn")
	id5 := IDS(t, "QmPhi7vBsChP7sjRoZGgg7bcKqF6MmCcQwvRbDte8aJ5Km")

	ma11 := MA(t, "/ip4/1.2.3.1/tcp/1111")
	ma21 := MA(t, "/ip4/2.2.3.2/tcp/1111")
	ma22 := MA(t, "/ip4/2.2.3.2/tcp/2222")
	ma31 := MA(t, "/ip4/3.2.3.3/tcp/1111")
	ma32 := MA(t, "/ip4/3.2.3.3/tcp/2222")
	ma33 := MA(t, "/ip4/3.2.3.3/tcp/3333")
	ma41 := MA(t, "/ip4/4.2.3.3/tcp/1111")
	ma42 := MA(t, "/ip4/4.2.3.3/tcp/2222")
	ma43 := MA(t, "/ip4/4.2.3.3/tcp/3333")
	ma44 := MA(t, "/ip4/4.2.3.3/tcp/4444")
	ma51 := MA(t, "/ip4/5.2.3.3/tcp/1111")
	ma52 := MA(t, "/ip4/5.2.3.3/tcp/2222")
	ma53 := MA(t, "/ip4/5.2.3.3/tcp/3333")
	ma54 := MA(t, "/ip4/5.2.3.3/tcp/4444")
	ma55 := MA(t, "/ip4/5.2.3.3/tcp/5555")

	ttl := time.Hour
	m := AddrManager{}
	m.AddAddr(id1, ma11, ttl)

	m.AddAddrs(id2, []ma.Multiaddr{ma21, ma22}, ttl)
	m.AddAddrs(id2, []ma.Multiaddr{ma21, ma22}, ttl) // idempotency

	m.AddAddr(id3, ma31, ttl)
	m.AddAddr(id3, ma32, ttl)
	m.AddAddr(id3, ma33, ttl)
	m.AddAddr(id3, ma33, ttl) // idempotency
	m.AddAddr(id3, ma33, ttl)

	m.AddAddrs(id4, []ma.Multiaddr{ma41, ma42, ma43, ma44}, ttl) // multiple

	m.AddAddrs(id5, []ma.Multiaddr{ma21, ma22}, ttl)             // clearing
	m.AddAddrs(id5, []ma.Multiaddr{ma41, ma42, ma43, ma44}, ttl) // clearing
	m.ClearAddrs(id5)
	m.AddAddrs(id5, []ma.Multiaddr{ma51, ma52, ma53, ma54, ma55}, ttl) // clearing

	if len(m.Peers()) != 5 {
		t.Fatal("should have exactly two peers in the address book")
	}

	// test the Addresses return value
	testHas(t, []ma.Multiaddr{ma11}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma21, ma22}, m.Addrs(id2))
	testHas(t, []ma.Multiaddr{ma31, ma32, ma33}, m.Addrs(id3))
	testHas(t, []ma.Multiaddr{ma41, ma42, ma43, ma44}, m.Addrs(id4))
	testHas(t, []ma.Multiaddr{ma51, ma52, ma53, ma54, ma55}, m.Addrs(id5))

}

func TestAddressesExpire(t *testing.T) {

	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	id2 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQM")
	ma11 := MA(t, "/ip4/1.2.3.1/tcp/1111")
	ma12 := MA(t, "/ip4/2.2.3.2/tcp/2222")
	ma13 := MA(t, "/ip4/3.2.3.3/tcp/3333")
	ma24 := MA(t, "/ip4/4.2.3.3/tcp/4444")
	ma25 := MA(t, "/ip4/5.2.3.3/tcp/5555")

	m := AddrManager{}
	m.AddAddr(id1, ma11, time.Hour)
	m.AddAddr(id1, ma12, time.Hour)
	m.AddAddr(id1, ma13, time.Hour)
	m.AddAddr(id2, ma24, time.Hour)
	m.AddAddr(id2, ma25, time.Hour)

	if len(m.Peers()) != 2 {
		t.Fatal("should have exactly two peers in the address book")
	}

	testHas(t, []ma.Multiaddr{ma11, ma12, ma13}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.SetAddr(id1, ma11, 2*time.Hour)
	m.SetAddr(id1, ma12, 2*time.Hour)
	m.SetAddr(id1, ma13, 2*time.Hour)
	m.SetAddr(id2, ma24, 2*time.Hour)
	m.SetAddr(id2, ma25, 2*time.Hour)

	testHas(t, []ma.Multiaddr{ma11, ma12, ma13}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.SetAddr(id1, ma11, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, []ma.Multiaddr{ma12, ma13}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.SetAddr(id1, ma13, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, []ma.Multiaddr{ma12}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.SetAddr(id2, ma24, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, []ma.Multiaddr{ma12}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma25}, m.Addrs(id2))

	m.SetAddr(id2, ma25, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, []ma.Multiaddr{ma12}, m.Addrs(id1))
	testHas(t, nil, m.Addrs(id2))

	m.SetAddr(id1, ma12, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, nil, m.Addrs(id1))
	testHas(t, nil, m.Addrs(id2))
}

func TestClearWorks(t *testing.T) {

	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	id2 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQM")
	ma11 := MA(t, "/ip4/1.2.3.1/tcp/1111")
	ma12 := MA(t, "/ip4/2.2.3.2/tcp/2222")
	ma13 := MA(t, "/ip4/3.2.3.3/tcp/3333")
	ma24 := MA(t, "/ip4/4.2.3.3/tcp/4444")
	ma25 := MA(t, "/ip4/5.2.3.3/tcp/5555")

	m := AddrManager{}
	m.AddAddr(id1, ma11, time.Hour)
	m.AddAddr(id1, ma12, time.Hour)
	m.AddAddr(id1, ma13, time.Hour)
	m.AddAddr(id2, ma24, time.Hour)
	m.AddAddr(id2, ma25, time.Hour)

	testHas(t, []ma.Multiaddr{ma11, ma12, ma13}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.ClearAddrs(id1)
	m.ClearAddrs(id2)

	testHas(t, nil, m.Addrs(id1))
	testHas(t, nil, m.Addrs(id2))
}

func TestSetNegativeTTLClears(t *testing.T) {
	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	ma11 := MA(t, "/ip4/1.2.3.1/tcp/1111")

	m := AddrManager{}
	m.SetAddr(id1, ma11, time.Hour)

	testHas(t, []ma.Multiaddr{ma11}, m.Addrs(id1))

	m.SetAddr(id1, ma11, -1)

	testHas(t, nil, m.Addrs(id1))
}

func TestNilAddrsDontBreak(t *testing.T) {
	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	m := AddrManager{}
	m.SetAddr(id1, nil, time.Hour)
	m.AddAddr(id1, nil, time.Hour)
}
<commit_msg>mark test helpers<commit_after>package peerstore

import (
	"testing"
	"time"

	"github.com/libp2p/go-libp2p-peer"
	ma "github.com/multiformats/go-multiaddr"
)

func IDS(t *testing.T, ids string) peer.ID {
	t.Helper()
	id, err := peer.IDB58Decode(ids)
	if err != nil {
		t.Fatalf("id %q is bad: %s", ids, err)
	}
	return id
}

func MA(t *testing.T, m string) ma.Multiaddr {
	t.Helper()
	maddr, err := ma.NewMultiaddr(m)
	if err != nil {
		t.Fatal(err)
	}
	return maddr
}

func testHas(t *testing.T, exp, act []ma.Multiaddr) {
	t.Helper()
	if len(exp) != len(act) {
		t.Fatal("lengths not the same")
	}

	for _, a := range exp {
		found := false

		for _, b := range act {
			if a.Equal(b) {
				found = true
				break
			}
		}

		if !found {
			t.Fatalf("expected address %s not found", a)
		}
	}
}

func TestAddresses(t *testing.T) {

	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	id2 := IDS(t, "QmRmPL3FDZKE3Qiwv1RosLdwdvbvg17b2hB39QPScgWKKZ")
	id3 := IDS(t, "QmPhi7vBsChP7sjRoZGgg7bcKqF6MmCcQwvRbDte8aJ6Kn")
	id4 := IDS(t, "QmPhi7vBsChP7sjRoZGgg7bcKqF6MmCcQwvRbDte8aJ5Kn")
	id5 := IDS(t, "QmPhi7vBsChP7sjRoZGgg7bcKqF6MmCcQwvRbDte8aJ5Km")

	ma11 := MA(t, "/ip4/1.2.3.1/tcp/1111")
	ma21 := MA(t, "/ip4/2.2.3.2/tcp/1111")
	ma22 := MA(t, "/ip4/2.2.3.2/tcp/2222")
	ma31 := MA(t, "/ip4/3.2.3.3/tcp/1111")
	ma32 := MA(t, "/ip4/3.2.3.3/tcp/2222")
	ma33 := MA(t, "/ip4/3.2.3.3/tcp/3333")
	ma41 := MA(t, "/ip4/4.2.3.3/tcp/1111")
	ma42 := MA(t, "/ip4/4.2.3.3/tcp/2222")
	ma43 := MA(t, "/ip4/4.2.3.3/tcp/3333")
	ma44 := MA(t, "/ip4/4.2.3.3/tcp/4444")
	ma51 := MA(t, "/ip4/5.2.3.3/tcp/1111")
	ma52 := MA(t, "/ip4/5.2.3.3/tcp/2222")
	ma53 := MA(t, "/ip4/5.2.3.3/tcp/3333")
	ma54 := MA(t, "/ip4/5.2.3.3/tcp/4444")
	ma55 := MA(t, "/ip4/5.2.3.3/tcp/5555")

	ttl := time.Hour
	m := AddrManager{}
	m.AddAddr(id1, ma11, ttl)

	m.AddAddrs(id2, []ma.Multiaddr{ma21, ma22}, ttl)
	m.AddAddrs(id2, []ma.Multiaddr{ma21, ma22}, ttl) // idempotency

	m.AddAddr(id3, ma31, ttl)
	m.AddAddr(id3, ma32, ttl)
	m.AddAddr(id3, ma33, ttl)
	m.AddAddr(id3, ma33, ttl) // idempotency
	m.AddAddr(id3, ma33, ttl)

	m.AddAddrs(id4, []ma.Multiaddr{ma41, ma42, ma43, ma44}, ttl) // multiple

	m.AddAddrs(id5, []ma.Multiaddr{ma21, ma22}, ttl)             // clearing
	m.AddAddrs(id5, []ma.Multiaddr{ma41, ma42, ma43, ma44}, ttl) // clearing
	m.ClearAddrs(id5)
	m.AddAddrs(id5, []ma.Multiaddr{ma51, ma52, ma53, ma54, ma55}, ttl) // clearing

	if len(m.Peers()) != 5 {
		t.Fatal("should have exactly two peers in the address book")
	}

	// test the Addresses return value
	testHas(t, []ma.Multiaddr{ma11}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma21, ma22}, m.Addrs(id2))
	testHas(t, []ma.Multiaddr{ma31, ma32, ma33}, m.Addrs(id3))
	testHas(t, []ma.Multiaddr{ma41, ma42, ma43, ma44}, m.Addrs(id4))
	testHas(t, []ma.Multiaddr{ma51, ma52, ma53, ma54, ma55}, m.Addrs(id5))

}

func TestAddressesExpire(t *testing.T) {

	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	id2 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQM")
	ma11 := MA(t, "/ip4/1.2.3.1/tcp/1111")
	ma12 := MA(t, "/ip4/2.2.3.2/tcp/2222")
	ma13 := MA(t, "/ip4/3.2.3.3/tcp/3333")
	ma24 := MA(t, "/ip4/4.2.3.3/tcp/4444")
	ma25 := MA(t, "/ip4/5.2.3.3/tcp/5555")

	m := AddrManager{}
	m.AddAddr(id1, ma11, time.Hour)
	m.AddAddr(id1, ma12, time.Hour)
	m.AddAddr(id1, ma13, time.Hour)
	m.AddAddr(id2, ma24, time.Hour)
	m.AddAddr(id2, ma25, time.Hour)

	if len(m.Peers()) != 2 {
		t.Fatal("should have exactly two peers in the address book")
	}

	testHas(t, []ma.Multiaddr{ma11, ma12, ma13}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.SetAddr(id1, ma11, 2*time.Hour)
	m.SetAddr(id1, ma12, 2*time.Hour)
	m.SetAddr(id1, ma13, 2*time.Hour)
	m.SetAddr(id2, ma24, 2*time.Hour)
	m.SetAddr(id2, ma25, 2*time.Hour)

	testHas(t, []ma.Multiaddr{ma11, ma12, ma13}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.SetAddr(id1, ma11, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, []ma.Multiaddr{ma12, ma13}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.SetAddr(id1, ma13, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, []ma.Multiaddr{ma12}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.SetAddr(id2, ma24, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, []ma.Multiaddr{ma12}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma25}, m.Addrs(id2))

	m.SetAddr(id2, ma25, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, []ma.Multiaddr{ma12}, m.Addrs(id1))
	testHas(t, nil, m.Addrs(id2))

	m.SetAddr(id1, ma12, time.Millisecond)
	<-time.After(time.Millisecond)
	testHas(t, nil, m.Addrs(id1))
	testHas(t, nil, m.Addrs(id2))
}

func TestClearWorks(t *testing.T) {

	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	id2 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQM")
	ma11 := MA(t, "/ip4/1.2.3.1/tcp/1111")
	ma12 := MA(t, "/ip4/2.2.3.2/tcp/2222")
	ma13 := MA(t, "/ip4/3.2.3.3/tcp/3333")
	ma24 := MA(t, "/ip4/4.2.3.3/tcp/4444")
	ma25 := MA(t, "/ip4/5.2.3.3/tcp/5555")

	m := AddrManager{}
	m.AddAddr(id1, ma11, time.Hour)
	m.AddAddr(id1, ma12, time.Hour)
	m.AddAddr(id1, ma13, time.Hour)
	m.AddAddr(id2, ma24, time.Hour)
	m.AddAddr(id2, ma25, time.Hour)

	testHas(t, []ma.Multiaddr{ma11, ma12, ma13}, m.Addrs(id1))
	testHas(t, []ma.Multiaddr{ma24, ma25}, m.Addrs(id2))

	m.ClearAddrs(id1)
	m.ClearAddrs(id2)

	testHas(t, nil, m.Addrs(id1))
	testHas(t, nil, m.Addrs(id2))
}

func TestSetNegativeTTLClears(t *testing.T) {
	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	ma11 := MA(t, "/ip4/1.2.3.1/tcp/1111")

	m := AddrManager{}
	m.SetAddr(id1, ma11, time.Hour)

	testHas(t, []ma.Multiaddr{ma11}, m.Addrs(id1))

	m.SetAddr(id1, ma11, -1)

	testHas(t, nil, m.Addrs(id1))
}

func TestNilAddrsDontBreak(t *testing.T) {
	id1 := IDS(t, "QmcNstKuwBBoVTpSCSDrwzjgrRcaYXK833Psuz2EMHwyQN")
	m := AddrManager{}
	m.SetAddr(id1, nil, time.Hour)
	m.AddAddr(id1, nil, time.Hour)
}
<|endoftext|>


<commit_before>// +build js,wasm

package websocket

import (
	"bytes"
	"errors"
	"io"
	"net"
	"os"
	"strings"
	"sync"
	"syscall/js"
	"time"
)

const (
	webSocketStateConnecting = 0
	webSocketStateOpen       = 1
	webSocketStateClosing    = 2
	webSocketStateClosed     = 3
)

const incomingDataBufferSize = 100

var errConnectionClosed = errors.New("connection is closed")

// Conn implements net.Conn interface for WebSockets in js/wasm.
type Conn struct {
	js.Value
	messageHandler *js.Func
	closeHandler   *js.Func
	mut            sync.Mutex
	incomingData   chan []byte
	currDataMut    sync.RWMutex
	currData       bytes.Buffer
	closeSignal    chan struct{}
	dataSignal     chan struct{}
}

// NewConn creates a Conn given a regular js/wasm WebSocket Conn.
func NewConn(raw js.Value) *Conn {
	conn := &Conn{
		Value:        raw,
		incomingData: make(chan []byte, incomingDataBufferSize),
		closeSignal:  make(chan struct{}),
		dataSignal:   make(chan struct{}),
	}
	conn.setUpHandlers()
	go func() {
		// TODO(albrow): Handle error appropriately
		err := conn.readLoop()
		if err != nil {
			panic(err)
		}
	}()
	return conn
}

func (c *Conn) Read(b []byte) (int, error) {
	if err := c.checkOpen(); err != nil {
		return 0, io.EOF
	}

	for {
		c.currDataMut.RLock()
		n, err := c.currData.Read(b)
		c.currDataMut.RUnlock()
		if err != nil && err != io.EOF {
			// Return any unexpected errors immediately.
			return n, err
		} else if n == 0 || err == io.EOF {
			// There is no data ready to be read. Wait for more data or for the
			// connection to be closed.
			select {
			case <-c.dataSignal:
				continue
			case <-c.closeSignal:
				return 0, io.EOF
			}
		} else {
			return n, err
		}
	}
}

// checkOpen returns an error if the connection is not open. Otherwise, it
// returns nil.
func (c *Conn) checkOpen() error {
	state := c.Get("readyState").Int()
	switch state {
	case webSocketStateClosed, webSocketStateClosing:
		return errConnectionClosed
	}
	return nil
}

func (c *Conn) Write(b []byte) (n int, err error) {
	if err := c.checkOpen(); err != nil {
		return 0, err
	}
	uint8Array := js.Global().Get("Uint8Array").New(len(b))
	for i := 0; i < len(b); i++ {
		uint8Array.SetIndex(i, b[i])
	}
	c.Call("send", uint8Array.Get("buffer"))
	return len(b), nil
}

// Close closes the connection. Only the first call to Close will receive the
// close error, subsequent and concurrent calls will return nil.
// This method is thread-safe.
func (c *Conn) Close() error {
	c.mut.Lock()
	defer c.mut.Unlock()
	c.Call("close")
	if c.messageHandler != nil {
		c.Call("removeEventListener", "message", *c.messageHandler)
		c.messageHandler.Release()
	}
	if c.closeHandler != nil {
		c.Call("removeEventListener", "close", *c.closeHandler)
		c.closeHandler.Release()
	}
	return nil
}

func (c *Conn) LocalAddr() net.Addr {
	return NewAddr("0.0.0.0:0")
}

func (c *Conn) RemoteAddr() net.Addr {
	rawURL := c.Get("url").String()
	withoutPrefix := strings.TrimPrefix(rawURL, "ws://")
	withoutSuffix := strings.TrimSuffix(withoutPrefix, "/")
	return NewAddr(withoutSuffix)
}

func (c *Conn) SetDeadline(t time.Time) error {
	return os.ErrNoDeadline
}

func (c *Conn) SetReadDeadline(t time.Time) error {
	return os.ErrNoDeadline
}

func (c *Conn) SetWriteDeadline(t time.Time) error {
	return os.ErrNoDeadline
}

func (c *Conn) setUpHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		// Message handlers already created. Nothing to do.
		return
	}
	messageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		go func() {
			// TODO(albrow): Currently we assume data is of type Blob. Really we
			// should check binaryType and then decode accordingly.
			blob := args[0].Get("data")
			data := readBlob(blob)
			c.incomingData <- data
		}()
		return nil
	})
	c.messageHandler = &messageHandler
	c.Call("addEventListener", "message", messageHandler)

	closeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		close(c.closeSignal)
		return nil
	})
	c.closeHandler = &closeHandler
	c.Call("addEventListener", "close", closeHandler)
}

// readLoop continuosly reads from the c.incoming data channel and writes to the
// current data buffer.
func (c *Conn) readLoop() error {
	for data := range c.incomingData {
		c.currDataMut.Lock()
		_, err := c.currData.Write(data)
		if err != nil {
			c.currDataMut.Unlock()
			return err
		}
		c.currDataMut.Unlock()
		c.dataSignal <- struct{}{}
	}
	return nil
}

func (c *Conn) waitForOpen() error {
	openSignal := make(chan struct{})
	handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		close(openSignal)
		return nil
	})
	defer c.Call("removeEventListener", "open", handler)
	defer handler.Release()
	c.Call("addEventListener", "open", handler)
	<-openSignal
	return nil
}

// readBlob converts a JavaScript Blob into a slice of bytes. It uses the
// FileReader API under the hood.
func readBlob(blob js.Value) []byte {
	reader := js.Global().Get("FileReader").New()
	dataChan := make(chan []byte)
	loadEndFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// event.result is of type ArrayBuffer. We can convert it to a JavaScript
		// Uint8Array, which is directly translatable to the Go type []byte.
		buffer := reader.Get("result")
		view := js.Global().Get("Uint8Array").New(buffer)
		dataLen := view.Get("length").Int()
		data := make([]byte, dataLen)
		for i := 0; i < dataLen; i++ {
			data[i] = byte(view.Index(i).Int())
		}
		dataChan <- data
		return nil
	})
	defer loadEndFunc.Release()
	reader.Call("addEventListener", "loadend", loadEndFunc)
	reader.Call("readAsArrayBuffer", blob)
	return <-dataChan
}
<commit_msg>Revert to returning nil instead of os.ErrNoDeadline<commit_after>// +build js,wasm

package websocket

import (
	"bytes"
	"errors"
	"io"
	"net"
	"strings"
	"sync"
	"syscall/js"
	"time"
)

const (
	webSocketStateConnecting = 0
	webSocketStateOpen       = 1
	webSocketStateClosing    = 2
	webSocketStateClosed     = 3
)

const incomingDataBufferSize = 100

var errConnectionClosed = errors.New("connection is closed")

// Conn implements net.Conn interface for WebSockets in js/wasm.
type Conn struct {
	js.Value
	messageHandler *js.Func
	closeHandler   *js.Func
	mut            sync.Mutex
	incomingData   chan []byte
	currDataMut    sync.RWMutex
	currData       bytes.Buffer
	closeSignal    chan struct{}
	dataSignal     chan struct{}
}

// NewConn creates a Conn given a regular js/wasm WebSocket Conn.
func NewConn(raw js.Value) *Conn {
	conn := &Conn{
		Value:        raw,
		incomingData: make(chan []byte, incomingDataBufferSize),
		closeSignal:  make(chan struct{}),
		dataSignal:   make(chan struct{}),
	}
	conn.setUpHandlers()
	go func() {
		// TODO(albrow): Handle error appropriately
		err := conn.readLoop()
		if err != nil {
			panic(err)
		}
	}()
	return conn
}

func (c *Conn) Read(b []byte) (int, error) {
	if err := c.checkOpen(); err != nil {
		return 0, io.EOF
	}

	for {
		c.currDataMut.RLock()
		n, err := c.currData.Read(b)
		c.currDataMut.RUnlock()
		if err != nil && err != io.EOF {
			// Return any unexpected errors immediately.
			return n, err
		} else if n == 0 || err == io.EOF {
			// There is no data ready to be read. Wait for more data or for the
			// connection to be closed.
			select {
			case <-c.dataSignal:
				continue
			case <-c.closeSignal:
				return 0, io.EOF
			}
		} else {
			return n, err
		}
	}
}

// checkOpen returns an error if the connection is not open. Otherwise, it
// returns nil.
func (c *Conn) checkOpen() error {
	state := c.Get("readyState").Int()
	switch state {
	case webSocketStateClosed, webSocketStateClosing:
		return errConnectionClosed
	}
	return nil
}

func (c *Conn) Write(b []byte) (n int, err error) {
	if err := c.checkOpen(); err != nil {
		return 0, err
	}
	uint8Array := js.Global().Get("Uint8Array").New(len(b))
	for i := 0; i < len(b); i++ {
		uint8Array.SetIndex(i, b[i])
	}
	c.Call("send", uint8Array.Get("buffer"))
	return len(b), nil
}

// Close closes the connection. Only the first call to Close will receive the
// close error, subsequent and concurrent calls will return nil.
// This method is thread-safe.
func (c *Conn) Close() error {
	c.mut.Lock()
	defer c.mut.Unlock()
	c.Call("close")
	if c.messageHandler != nil {
		c.Call("removeEventListener", "message", *c.messageHandler)
		c.messageHandler.Release()
	}
	if c.closeHandler != nil {
		c.Call("removeEventListener", "close", *c.closeHandler)
		c.closeHandler.Release()
	}
	return nil
}

func (c *Conn) LocalAddr() net.Addr {
	return NewAddr("0.0.0.0:0")
}

func (c *Conn) RemoteAddr() net.Addr {
	rawURL := c.Get("url").String()
	withoutPrefix := strings.TrimPrefix(rawURL, "ws://")
	withoutSuffix := strings.TrimSuffix(withoutPrefix, "/")
	return NewAddr(withoutSuffix)
}

func (c *Conn) SetDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetReadDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetWriteDeadline(t time.Time) error {
	return nil
}

func (c *Conn) setUpHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		// Message handlers already created. Nothing to do.
		return
	}
	messageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		go func() {
			// TODO(albrow): Currently we assume data is of type Blob. Really we
			// should check binaryType and then decode accordingly.
			blob := args[0].Get("data")
			data := readBlob(blob)
			c.incomingData <- data
		}()
		return nil
	})
	c.messageHandler = &messageHandler
	c.Call("addEventListener", "message", messageHandler)

	closeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		close(c.closeSignal)
		return nil
	})
	c.closeHandler = &closeHandler
	c.Call("addEventListener", "close", closeHandler)
}

// readLoop continuosly reads from the c.incoming data channel and writes to the
// current data buffer.
func (c *Conn) readLoop() error {
	for data := range c.incomingData {
		c.currDataMut.Lock()
		_, err := c.currData.Write(data)
		if err != nil {
			c.currDataMut.Unlock()
			return err
		}
		c.currDataMut.Unlock()
		c.dataSignal <- struct{}{}
	}
	return nil
}

func (c *Conn) waitForOpen() error {
	openSignal := make(chan struct{})
	handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		close(openSignal)
		return nil
	})
	defer c.Call("removeEventListener", "open", handler)
	defer handler.Release()
	c.Call("addEventListener", "open", handler)
	<-openSignal
	return nil
}

// readBlob converts a JavaScript Blob into a slice of bytes. It uses the
// FileReader API under the hood.
func readBlob(blob js.Value) []byte {
	reader := js.Global().Get("FileReader").New()
	dataChan := make(chan []byte)
	loadEndFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// event.result is of type ArrayBuffer. We can convert it to a JavaScript
		// Uint8Array, which is directly translatable to the Go type []byte.
		buffer := reader.Get("result")
		view := js.Global().Get("Uint8Array").New(buffer)
		dataLen := view.Get("length").Int()
		data := make([]byte, dataLen)
		for i := 0; i < dataLen; i++ {
			data[i] = byte(view.Index(i).Int())
		}
		dataChan <- data
		return nil
	})
	defer loadEndFunc.Release()
	reader.Call("addEventListener", "loadend", loadEndFunc)
	reader.Call("readAsArrayBuffer", blob)
	return <-dataChan
}
<|endoftext|>


<commit_before>package network

import (
	"fmt"
	"net"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/validate"
)

// physical represents a LXD physical network.
type physical struct {
	common
}

// Type returns the network type.
func (n *physical) Type() string {
	return "physical"
}

// DBType returns the network type DB ID.
func (n *physical) DBType() db.NetworkType {
	return db.NetworkTypePhysical
}

// Validate network config.
func (n *physical) Validate(config map[string]string) error {
	rules := map[string]func(value string) error{
		"parent":              validate.Required(validate.IsNotEmpty, validate.IsInterfaceName),
		"mtu":                 validate.Optional(validate.IsNetworkMTU),
		"vlan":                validate.Optional(validate.IsNetworkVLAN),
		"gvrp":                validate.Optional(validate.IsBool),
		"maas.subnet.ipv4":    validate.IsAny,
		"maas.subnet.ipv6":    validate.IsAny,
		"ipv4.gateway":        validate.Optional(validate.IsNetworkAddressCIDRV4),
		"ipv6.gateway":        validate.Optional(validate.IsNetworkAddressCIDRV6),
		"ipv4.ovn.ranges":     validate.Optional(validate.IsNetworkRangeV4List),
		"ipv6.ovn.ranges":     validate.Optional(validate.IsNetworkRangeV6List),
		"ipv4.routes":         validate.Optional(validate.IsNetworkV4List),
		"ipv4.routes.anycast": validate.Optional(validate.IsBool),
		"ipv6.routes":         validate.Optional(validate.IsNetworkV6List),
		"ipv6.routes.anycast": validate.Optional(validate.IsBool),
		"dns.nameservers":     validate.Optional(validate.IsNetworkAddressList),
		"ovn.ingress_mode": validate.Optional(func(value string) error {
			return validate.IsOneOf(value, []string{"l2proxy", "routed"})
		}),
		"volatile.last_state.created": validate.Optional(validate.IsBool),
	}

	err := n.validate(config, rules)
	if err != nil {
		return err
	}

	return nil
}

// checkParentUse checks if parent is already in use by another network or instance device.
func (n *physical) checkParentUse(ourConfig map[string]string) (bool, error) {
	// Get all managed networks across all projects.
	var err error
	var projectNetworks map[string]map[int64]api.Network

	err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		projectNetworks, err = tx.GetCreatedNetworks()
		return err
	})
	if err != nil {
		return false, errors.Wrapf(err, "Failed to load all networks")
	}

	for projectName, networks := range projectNetworks {
		if projectName != project.Default {
			continue // Only default project networks can possibly reference a physical interface.
		}

		for _, network := range networks {
			if network.Name == n.name {
				continue // Ignore our own DB record.
			}

			// Check if another network is using our parent.
			if network.Config["parent"] == ourConfig["parent"] {
				// If either network doesn't specify a vlan, or both specify same vlan,
				// then we can't use this parent.
				if (network.Config["vlan"] == "" || ourConfig["vlan"] == "") || network.Config["vlan"] == ourConfig["vlan"] {
					return true, nil
				}
			}
		}
	}

	return false, nil
}

// Create checks whether the referenced parent interface is used by other networks or instance devices, as we
// need to have exclusive access to the interface.
func (n *physical) Create(clientType request.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		inUse, err := n.checkParentUse(n.config)
		if err != nil {
			return err
		}
		if inUse {
			return fmt.Errorf("Parent interface %q in use by another network", n.config["parent"])
		}
	}

	return n.common.create(clientType)
}

// Delete deletes a network.
func (n *physical) Delete(clientType request.ClientType) error {
	n.logger.Debug("Delete", log.Ctx{"clientType": clientType})

	err := n.Stop()
	if err != nil {
		return err
	}

	return n.common.delete(clientType)
}

// Rename renames a network.
func (n *physical) Rename(newName string) error {
	n.logger.Debug("Rename", log.Ctx{"newName": newName})

	// Rename common steps.
	err := n.common.rename(newName)
	if err != nil {
		return err
	}

	return nil
}

// Start starts is a no-op.
func (n *physical) Start() error {
	n.logger.Debug("Start")

	revert := revert.New()
	defer revert.Fail()

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])
	created, err := VLANInterfaceCreate(n.config["parent"], hostName, n.config["vlan"], shared.IsTrue(n.config["gvrp"]))
	if err != nil {
		return err
	}
	if created {
		revert.Add(func() { InterfaceRemove(hostName) })
	}

	// Check no global unicast IPs defined on parent, as that may indicate it is in use by another application.
	addresses, _, err := InterfaceStatus(hostName)
	if err != nil {
		return errors.Wrapf(err, "Failed getting interface status for %q", hostName)
	}

	if len(addresses) > 0 {
		return fmt.Errorf("Cannot start network as parent interface %q has one or more IP addresses configured on it", hostName)
	}

	// Set the MTU.
	if n.config["mtu"] != "" {
		phyLink := &ip.Link{Name: hostName}
		err = phyLink.SetMTU(n.config["mtu"])
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", n.config["mtu"], phyLink.Name)
		}
	}

	// Record if we created this device or not (if we have not already recorded that we created it previously),
	// so it can be removed on stop. This way we won't overwrite the setting on LXD restart.
	if !shared.IsTrue(n.config["volatile.last_state.created"]) {
		n.config["volatile.last_state.created"] = fmt.Sprintf("%t", created)
		err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
			return tx.UpdateNetwork(n.id, n.description, n.config)
		})
		if err != nil {
			return errors.Wrapf(err, "Failed saving volatile config")
		}
	}

	revert.Success()
	return nil
}

// Stop stops is a no-op.
func (n *physical) Stop() error {
	n.logger.Debug("Stop")

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])

	// Only try and remove created VLAN interfaces.
	if n.config["vlan"] != "" && shared.IsTrue(n.config["volatile.last_state.created"]) && InterfaceExists(hostName) {
		err := InterfaceRemove(hostName)
		if err != nil {
			return err
		}
	}

	// Reset MTU back to 1500 if overridden in config.
	if n.config["mtu"] != "" && InterfaceExists(hostName) {
		resetMTU := "1500"
		link := &ip.Link{Name: hostName}
		err := link.SetMTU(resetMTU)
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", link, link.Name)
		}
	}

	// Remove last state config.
	delete(n.config, "volatile.last_state.created")
	err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.UpdateNetwork(n.id, n.description, n.config)
	})
	if err != nil {
		return errors.Wrapf(err, "Failed removing volatile config")
	}

	return nil
}

// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
// cluster notification, in which case do not update the database, just apply local changes needed.
func (n *physical) Update(newNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	n.logger.Debug("Update", log.Ctx{"clientType": clientType, "newNetwork": newNetwork})

	dbUpdateNeeeded, changedKeys, oldNetwork, err := n.common.configChanged(newNetwork)
	if err != nil {
		return err
	}

	if !dbUpdateNeeeded {
		return nil // Nothing changed.
	}

	// If the network as a whole has not had any previous creation attempts, or the node itself is still
	// pending, then don't apply the new settings to the node, just to the database record (ready for the
	// actual global create request to be initiated).
	if n.Status() == api.NetworkStatusPending || n.LocalStatus() == api.NetworkStatusPending {
		return n.common.update(newNetwork, targetNode, clientType)
	}

	revert := revert.New()
	defer revert.Fail()

	hostNameChanged := shared.StringInSlice("vlan", changedKeys) || shared.StringInSlice("parent", changedKeys)

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		if hostNameChanged {
			isUsed, err := n.IsUsed()
			if isUsed || err != nil {
				return fmt.Errorf("Cannot update network parent interface when in use")
			}

			inUse, err := n.checkParentUse(newNetwork.Config)
			if err != nil {
				return err
			}
			if inUse {
				return fmt.Errorf("Parent interface %q in use by another network", newNetwork.Config["parent"])
			}
		}
	}

	if hostNameChanged {
		err = n.Stop()
		if err != nil {
			return err
		}

		// Remove the volatile last state from submitted new config if present.
		delete(newNetwork.Config, "volatile.last_state.created")
	}

	// Define a function which reverts everything.
	revert.Add(func() {
		// Reset changes to all nodes and database.
		n.common.update(oldNetwork, targetNode, clientType)
	})

	// Apply changes to all nodes and databse.
	err = n.common.update(newNetwork, targetNode, clientType)
	if err != nil {
		return err
	}

	err = n.Start()
	if err != nil {
		return err
	}

	revert.Success()

	// Notify dependent networks (those using this network as their uplink) of the changes.
	// Do this after the network has been successfully updated so that a failure to notify a dependent network
	// doesn't prevent the network itself from being updated.
	if clientType == request.ClientTypeNormal && len(changedKeys) > 0 {
		n.common.notifyDependentNetworks(changedKeys)
	}

	return nil
}

// DHCPv4Subnet returns the DHCPv4 subnet (if DHCP is enabled on network).
func (n *physical) DHCPv4Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv4.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}

// DHCPv6Subnet returns the DHCPv6 subnet (if DHCP or SLAAC is enabled on network).
func (n *physical) DHCPv6Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv6.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}
<commit_msg>Revert "lxd/network/driver/physical: Check that physical parent interface has no global unicast IPs configured on it before starting"<commit_after>package network

import (
	"fmt"
	"net"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/validate"
)

// physical represents a LXD physical network.
type physical struct {
	common
}

// Type returns the network type.
func (n *physical) Type() string {
	return "physical"
}

// DBType returns the network type DB ID.
func (n *physical) DBType() db.NetworkType {
	return db.NetworkTypePhysical
}

// Validate network config.
func (n *physical) Validate(config map[string]string) error {
	rules := map[string]func(value string) error{
		"parent":              validate.Required(validate.IsNotEmpty, validate.IsInterfaceName),
		"mtu":                 validate.Optional(validate.IsNetworkMTU),
		"vlan":                validate.Optional(validate.IsNetworkVLAN),
		"gvrp":                validate.Optional(validate.IsBool),
		"maas.subnet.ipv4":    validate.IsAny,
		"maas.subnet.ipv6":    validate.IsAny,
		"ipv4.gateway":        validate.Optional(validate.IsNetworkAddressCIDRV4),
		"ipv6.gateway":        validate.Optional(validate.IsNetworkAddressCIDRV6),
		"ipv4.ovn.ranges":     validate.Optional(validate.IsNetworkRangeV4List),
		"ipv6.ovn.ranges":     validate.Optional(validate.IsNetworkRangeV6List),
		"ipv4.routes":         validate.Optional(validate.IsNetworkV4List),
		"ipv4.routes.anycast": validate.Optional(validate.IsBool),
		"ipv6.routes":         validate.Optional(validate.IsNetworkV6List),
		"ipv6.routes.anycast": validate.Optional(validate.IsBool),
		"dns.nameservers":     validate.Optional(validate.IsNetworkAddressList),
		"ovn.ingress_mode": validate.Optional(func(value string) error {
			return validate.IsOneOf(value, []string{"l2proxy", "routed"})
		}),
		"volatile.last_state.created": validate.Optional(validate.IsBool),
	}

	err := n.validate(config, rules)
	if err != nil {
		return err
	}

	return nil
}

// checkParentUse checks if parent is already in use by another network or instance device.
func (n *physical) checkParentUse(ourConfig map[string]string) (bool, error) {
	// Get all managed networks across all projects.
	var err error
	var projectNetworks map[string]map[int64]api.Network

	err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		projectNetworks, err = tx.GetCreatedNetworks()
		return err
	})
	if err != nil {
		return false, errors.Wrapf(err, "Failed to load all networks")
	}

	for projectName, networks := range projectNetworks {
		if projectName != project.Default {
			continue // Only default project networks can possibly reference a physical interface.
		}

		for _, network := range networks {
			if network.Name == n.name {
				continue // Ignore our own DB record.
			}

			// Check if another network is using our parent.
			if network.Config["parent"] == ourConfig["parent"] {
				// If either network doesn't specify a vlan, or both specify same vlan,
				// then we can't use this parent.
				if (network.Config["vlan"] == "" || ourConfig["vlan"] == "") || network.Config["vlan"] == ourConfig["vlan"] {
					return true, nil
				}
			}
		}
	}

	return false, nil
}

// Create checks whether the referenced parent interface is used by other networks or instance devices, as we
// need to have exclusive access to the interface.
func (n *physical) Create(clientType request.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		inUse, err := n.checkParentUse(n.config)
		if err != nil {
			return err
		}
		if inUse {
			return fmt.Errorf("Parent interface %q in use by another network", n.config["parent"])
		}
	}

	return n.common.create(clientType)
}

// Delete deletes a network.
func (n *physical) Delete(clientType request.ClientType) error {
	n.logger.Debug("Delete", log.Ctx{"clientType": clientType})

	err := n.Stop()
	if err != nil {
		return err
	}

	return n.common.delete(clientType)
}

// Rename renames a network.
func (n *physical) Rename(newName string) error {
	n.logger.Debug("Rename", log.Ctx{"newName": newName})

	// Rename common steps.
	err := n.common.rename(newName)
	if err != nil {
		return err
	}

	return nil
}

// Start starts is a no-op.
func (n *physical) Start() error {
	n.logger.Debug("Start")

	revert := revert.New()
	defer revert.Fail()

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])
	created, err := VLANInterfaceCreate(n.config["parent"], hostName, n.config["vlan"], shared.IsTrue(n.config["gvrp"]))
	if err != nil {
		return err
	}
	if created {
		revert.Add(func() { InterfaceRemove(hostName) })
	}

	// Set the MTU.
	if n.config["mtu"] != "" {
		phyLink := &ip.Link{Name: hostName}
		err = phyLink.SetMTU(n.config["mtu"])
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", n.config["mtu"], phyLink.Name)
		}
	}

	// Record if we created this device or not (if we have not already recorded that we created it previously),
	// so it can be removed on stop. This way we won't overwrite the setting on LXD restart.
	if !shared.IsTrue(n.config["volatile.last_state.created"]) {
		n.config["volatile.last_state.created"] = fmt.Sprintf("%t", created)
		err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
			return tx.UpdateNetwork(n.id, n.description, n.config)
		})
		if err != nil {
			return errors.Wrapf(err, "Failed saving volatile config")
		}
	}

	revert.Success()
	return nil
}

// Stop stops is a no-op.
func (n *physical) Stop() error {
	n.logger.Debug("Stop")

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])

	// Only try and remove created VLAN interfaces.
	if n.config["vlan"] != "" && shared.IsTrue(n.config["volatile.last_state.created"]) && InterfaceExists(hostName) {
		err := InterfaceRemove(hostName)
		if err != nil {
			return err
		}
	}

	// Reset MTU back to 1500 if overridden in config.
	if n.config["mtu"] != "" && InterfaceExists(hostName) {
		resetMTU := "1500"
		link := &ip.Link{Name: hostName}
		err := link.SetMTU(resetMTU)
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", link, link.Name)
		}
	}

	// Remove last state config.
	delete(n.config, "volatile.last_state.created")
	err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.UpdateNetwork(n.id, n.description, n.config)
	})
	if err != nil {
		return errors.Wrapf(err, "Failed removing volatile config")
	}

	return nil
}

// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
// cluster notification, in which case do not update the database, just apply local changes needed.
func (n *physical) Update(newNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	n.logger.Debug("Update", log.Ctx{"clientType": clientType, "newNetwork": newNetwork})

	dbUpdateNeeeded, changedKeys, oldNetwork, err := n.common.configChanged(newNetwork)
	if err != nil {
		return err
	}

	if !dbUpdateNeeeded {
		return nil // Nothing changed.
	}

	// If the network as a whole has not had any previous creation attempts, or the node itself is still
	// pending, then don't apply the new settings to the node, just to the database record (ready for the
	// actual global create request to be initiated).
	if n.Status() == api.NetworkStatusPending || n.LocalStatus() == api.NetworkStatusPending {
		return n.common.update(newNetwork, targetNode, clientType)
	}

	revert := revert.New()
	defer revert.Fail()

	hostNameChanged := shared.StringInSlice("vlan", changedKeys) || shared.StringInSlice("parent", changedKeys)

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		if hostNameChanged {
			isUsed, err := n.IsUsed()
			if isUsed || err != nil {
				return fmt.Errorf("Cannot update network parent interface when in use")
			}

			inUse, err := n.checkParentUse(newNetwork.Config)
			if err != nil {
				return err
			}
			if inUse {
				return fmt.Errorf("Parent interface %q in use by another network", newNetwork.Config["parent"])
			}
		}
	}

	if hostNameChanged {
		err = n.Stop()
		if err != nil {
			return err
		}

		// Remove the volatile last state from submitted new config if present.
		delete(newNetwork.Config, "volatile.last_state.created")
	}

	// Define a function which reverts everything.
	revert.Add(func() {
		// Reset changes to all nodes and database.
		n.common.update(oldNetwork, targetNode, clientType)
	})

	// Apply changes to all nodes and databse.
	err = n.common.update(newNetwork, targetNode, clientType)
	if err != nil {
		return err
	}

	err = n.Start()
	if err != nil {
		return err
	}

	revert.Success()

	// Notify dependent networks (those using this network as their uplink) of the changes.
	// Do this after the network has been successfully updated so that a failure to notify a dependent network
	// doesn't prevent the network itself from being updated.
	if clientType == request.ClientTypeNormal && len(changedKeys) > 0 {
		n.common.notifyDependentNetworks(changedKeys)
	}

	return nil
}

// DHCPv4Subnet returns the DHCPv4 subnet (if DHCP is enabled on network).
func (n *physical) DHCPv4Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv4.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}

// DHCPv6Subnet returns the DHCPv6 subnet (if DHCP or SLAAC is enabled on network).
func (n *physical) DHCPv6Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv6.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}
<|endoftext|>


<commit_before>package network

import (
	"fmt"
	"net"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	dbCluster "github.com/lxc/lxd/lxd/db/cluster"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/warnings"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/validate"
)

// physical represents a LXD physical network.
type physical struct {
	common
}

// Type returns the network type.
func (n *physical) Type() string {
	return "physical"
}

// DBType returns the network type DB ID.
func (n *physical) DBType() db.NetworkType {
	return db.NetworkTypePhysical
}

// Validate network config.
func (n *physical) Validate(config map[string]string) error {
	rules := map[string]func(value string) error{
		"parent":                      validate.Required(validate.IsNotEmpty, validate.IsInterfaceName),
		"mtu":                         validate.Optional(validate.IsNetworkMTU),
		"vlan":                        validate.Optional(validate.IsNetworkVLAN),
		"gvrp":                        validate.Optional(validate.IsBool),
		"maas.subnet.ipv4":            validate.IsAny,
		"maas.subnet.ipv6":            validate.IsAny,
		"ipv4.gateway":                validate.Optional(validate.IsNetworkAddressCIDRV4),
		"ipv6.gateway":                validate.Optional(validate.IsNetworkAddressCIDRV6),
		"ipv4.ovn.ranges":             validate.Optional(validate.IsNetworkRangeV4List),
		"ipv6.ovn.ranges":             validate.Optional(validate.IsNetworkRangeV6List),
		"ipv4.routes":                 validate.Optional(validate.IsNetworkV4List),
		"ipv4.routes.anycast":         validate.Optional(validate.IsBool),
		"ipv6.routes":                 validate.Optional(validate.IsNetworkV6List),
		"ipv6.routes.anycast":         validate.Optional(validate.IsBool),
		"dns.nameservers":             validate.Optional(validate.IsNetworkAddressList),
		"ovn.ingress_mode":            validate.Optional(validate.IsOneOf("l2proxy", "routed")),
		"volatile.last_state.created": validate.Optional(validate.IsBool),
	}

	err := n.validate(config, rules)
	if err != nil {
		return err
	}

	return nil
}

// checkParentUse checks if parent is already in use by another network or instance device.
func (n *physical) checkParentUse(ourConfig map[string]string) (bool, error) {
	// Get all managed networks across all projects.
	var err error
	var projectNetworks map[string]map[int64]api.Network

	err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		projectNetworks, err = tx.GetCreatedNetworks()
		return err
	})
	if err != nil {
		return false, errors.Wrapf(err, "Failed to load all networks")
	}

	for projectName, networks := range projectNetworks {
		if projectName != project.Default {
			continue // Only default project networks can possibly reference a physical interface.
		}

		for _, network := range networks {
			if network.Name == n.name {
				continue // Ignore our own DB record.
			}

			// Check if another network is using our parent.
			if network.Config["parent"] == ourConfig["parent"] {
				// If either network doesn't specify a vlan, or both specify same vlan,
				// then we can't use this parent.
				if (network.Config["vlan"] == "" || ourConfig["vlan"] == "") || network.Config["vlan"] == ourConfig["vlan"] {
					return true, nil
				}
			}
		}
	}

	return false, nil
}

// Create checks whether the referenced parent interface is used by other networks or instance devices, as we
// need to have exclusive access to the interface.
func (n *physical) Create(clientType request.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		inUse, err := n.checkParentUse(n.config)
		if err != nil {
			return err
		}
		if inUse {
			return fmt.Errorf("Parent interface %q in use by another network", n.config["parent"])
		}
	}

	return nil
}

// Delete deletes a network.
func (n *physical) Delete(clientType request.ClientType) error {
	n.logger.Debug("Delete", log.Ctx{"clientType": clientType})

	err := n.Stop()
	if err != nil {
		return err
	}

	return n.common.delete(clientType)
}

// Rename renames a network.
func (n *physical) Rename(newName string) error {
	n.logger.Debug("Rename", log.Ctx{"newName": newName})

	// Rename common steps.
	err := n.common.rename(newName)
	if err != nil {
		return err
	}

	return nil
}

// Start starts is a no-op.
func (n *physical) Start() error {
	err := n.start()
	if err != nil {
		err := n.state.Cluster.UpsertWarningLocalNode(n.project, dbCluster.TypeNetwork, int(n.id), db.WarningNetworkStartupFailure, err.Error())
		if err != nil {
			n.logger.Warn("Failed to create warning", log.Ctx{"err": err})
		}
	} else {
		err := warnings.ResolveWarningsByLocalNodeAndProjectAndTypeAndEntity(n.state.Cluster, n.project, db.WarningNetworkStartupFailure, dbCluster.TypeNetwork, int(n.id))
		if err != nil {
			n.logger.Warn("Failed to resolve warning", log.Ctx{"err": err})
		}
	}

	return err
}

func (n *physical) start() error {
	n.logger.Debug("Start")

	revert := revert.New()
	defer revert.Fail()

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])
	created, err := VLANInterfaceCreate(n.config["parent"], hostName, n.config["vlan"], shared.IsTrue(n.config["gvrp"]))
	if err != nil {
		return err
	}
	if created {
		revert.Add(func() { InterfaceRemove(hostName) })
	}

	// Set the MTU.
	if n.config["mtu"] != "" {
		phyLink := &ip.Link{Name: hostName}
		err = phyLink.SetMTU(n.config["mtu"])
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", n.config["mtu"], phyLink.Name)
		}
	}

	// Record if we created this device or not (if we have not already recorded that we created it previously),
	// so it can be removed on stop. This way we won't overwrite the setting on LXD restart.
	if !shared.IsTrue(n.config["volatile.last_state.created"]) {
		n.config["volatile.last_state.created"] = fmt.Sprintf("%t", created)
		err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
			return tx.UpdateNetwork(n.id, n.description, n.config)
		})
		if err != nil {
			return errors.Wrapf(err, "Failed saving volatile config")
		}
	}

	revert.Success()
	return nil
}

// Stop stops is a no-op.
func (n *physical) Stop() error {
	n.logger.Debug("Stop")

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])

	// Only try and remove created VLAN interfaces.
	if n.config["vlan"] != "" && shared.IsTrue(n.config["volatile.last_state.created"]) && InterfaceExists(hostName) {
		err := InterfaceRemove(hostName)
		if err != nil {
			return err
		}
	}

	// Reset MTU back to 1500 if overridden in config.
	if n.config["mtu"] != "" && InterfaceExists(hostName) {
		resetMTU := "1500"
		link := &ip.Link{Name: hostName}
		err := link.SetMTU(resetMTU)
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", link, link.Name)
		}
	}

	// Remove last state config.
	delete(n.config, "volatile.last_state.created")
	err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.UpdateNetwork(n.id, n.description, n.config)
	})
	if err != nil {
		return errors.Wrapf(err, "Failed removing volatile config")
	}

	return nil
}

// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
// cluster notification, in which case do not update the database, just apply local changes needed.
func (n *physical) Update(newNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	n.logger.Debug("Update", log.Ctx{"clientType": clientType, "newNetwork": newNetwork})

	dbUpdateNeeeded, changedKeys, oldNetwork, err := n.common.configChanged(newNetwork)
	if err != nil {
		return err
	}

	if !dbUpdateNeeeded {
		return nil // Nothing changed.
	}

	// If the network as a whole has not had any previous creation attempts, or the node itself is still
	// pending, then don't apply the new settings to the node, just to the database record (ready for the
	// actual global create request to be initiated).
	if n.Status() == api.NetworkStatusPending || n.LocalStatus() == api.NetworkStatusPending {
		return n.common.update(newNetwork, targetNode, clientType)
	}

	revert := revert.New()
	defer revert.Fail()

	hostNameChanged := shared.StringInSlice("vlan", changedKeys) || shared.StringInSlice("parent", changedKeys)

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		if hostNameChanged {
			isUsed, err := n.IsUsed()
			if isUsed || err != nil {
				return fmt.Errorf("Cannot update network parent interface when in use")
			}

			inUse, err := n.checkParentUse(newNetwork.Config)
			if err != nil {
				return err
			}
			if inUse {
				return fmt.Errorf("Parent interface %q in use by another network", newNetwork.Config["parent"])
			}
		}
	}

	if hostNameChanged {
		err = n.Stop()
		if err != nil {
			return err
		}

		// Remove the volatile last state from submitted new config if present.
		delete(newNetwork.Config, "volatile.last_state.created")
	}

	// Define a function which reverts everything.
	revert.Add(func() {
		// Reset changes to all nodes and database.
		n.common.update(oldNetwork, targetNode, clientType)
	})

	// Apply changes to all nodes and databse.
	err = n.common.update(newNetwork, targetNode, clientType)
	if err != nil {
		return err
	}

	err = n.Start()
	if err != nil {
		return err
	}

	revert.Success()

	// Notify dependent networks (those using this network as their uplink) of the changes.
	// Do this after the network has been successfully updated so that a failure to notify a dependent network
	// doesn't prevent the network itself from being updated.
	if clientType == request.ClientTypeNormal && len(changedKeys) > 0 {
		n.common.notifyDependentNetworks(changedKeys)
	}

	return nil
}

// DHCPv4Subnet returns the DHCPv4 subnet (if DHCP is enabled on network).
func (n *physical) DHCPv4Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv4.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}

// DHCPv6Subnet returns the DHCPv6 subnet (if DHCP or SLAAC is enabled on network).
func (n *physical) DHCPv6Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv6.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}
<commit_msg>lxd/network/physical: Align with bridged<commit_after>package network

import (
	"fmt"
	"net"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	dbCluster "github.com/lxc/lxd/lxd/db/cluster"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/warnings"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/validate"
)

// physical represents a LXD physical network.
type physical struct {
	common
}

// Type returns the network type.
func (n *physical) Type() string {
	return "physical"
}

// DBType returns the network type DB ID.
func (n *physical) DBType() db.NetworkType {
	return db.NetworkTypePhysical
}

// Validate network config.
func (n *physical) Validate(config map[string]string) error {
	rules := map[string]func(value string) error{
		"parent":                      validate.Required(validate.IsNotEmpty, validate.IsInterfaceName),
		"mtu":                         validate.Optional(validate.IsNetworkMTU),
		"vlan":                        validate.Optional(validate.IsNetworkVLAN),
		"gvrp":                        validate.Optional(validate.IsBool),
		"maas.subnet.ipv4":            validate.IsAny,
		"maas.subnet.ipv6":            validate.IsAny,
		"ipv4.gateway":                validate.Optional(validate.IsNetworkAddressCIDRV4),
		"ipv6.gateway":                validate.Optional(validate.IsNetworkAddressCIDRV6),
		"ipv4.ovn.ranges":             validate.Optional(validate.IsNetworkRangeV4List),
		"ipv6.ovn.ranges":             validate.Optional(validate.IsNetworkRangeV6List),
		"ipv4.routes":                 validate.Optional(validate.IsNetworkV4List),
		"ipv4.routes.anycast":         validate.Optional(validate.IsBool),
		"ipv6.routes":                 validate.Optional(validate.IsNetworkV6List),
		"ipv6.routes.anycast":         validate.Optional(validate.IsBool),
		"dns.nameservers":             validate.Optional(validate.IsNetworkAddressList),
		"ovn.ingress_mode":            validate.Optional(validate.IsOneOf("l2proxy", "routed")),
		"volatile.last_state.created": validate.Optional(validate.IsBool),
	}

	err := n.validate(config, rules)
	if err != nil {
		return err
	}

	return nil
}

// checkParentUse checks if parent is already in use by another network or instance device.
func (n *physical) checkParentUse(ourConfig map[string]string) (bool, error) {
	// Get all managed networks across all projects.
	var err error
	var projectNetworks map[string]map[int64]api.Network

	err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		projectNetworks, err = tx.GetCreatedNetworks()
		return err
	})
	if err != nil {
		return false, errors.Wrapf(err, "Failed to load all networks")
	}

	for projectName, networks := range projectNetworks {
		if projectName != project.Default {
			continue // Only default project networks can possibly reference a physical interface.
		}

		for _, network := range networks {
			if network.Name == n.name {
				continue // Ignore our own DB record.
			}

			// Check if another network is using our parent.
			if network.Config["parent"] == ourConfig["parent"] {
				// If either network doesn't specify a vlan, or both specify same vlan,
				// then we can't use this parent.
				if (network.Config["vlan"] == "" || ourConfig["vlan"] == "") || network.Config["vlan"] == ourConfig["vlan"] {
					return true, nil
				}
			}
		}
	}

	return false, nil
}

// Create checks whether the referenced parent interface is used by other networks or instance devices, as we
// need to have exclusive access to the interface.
func (n *physical) Create(clientType request.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		inUse, err := n.checkParentUse(n.config)
		if err != nil {
			return err
		}
		if inUse {
			return fmt.Errorf("Parent interface %q in use by another network", n.config["parent"])
		}
	}

	return nil
}

// Delete deletes a network.
func (n *physical) Delete(clientType request.ClientType) error {
	n.logger.Debug("Delete", log.Ctx{"clientType": clientType})

	err := n.Stop()
	if err != nil {
		return err
	}

	return n.common.delete(clientType)
}

// Rename renames a network.
func (n *physical) Rename(newName string) error {
	n.logger.Debug("Rename", log.Ctx{"newName": newName})

	// Rename common steps.
	err := n.common.rename(newName)
	if err != nil {
		return err
	}

	return nil
}

// Start sets up some global configuration.
func (n *physical) Start() error {
	err := n.setup(nil)
	if err != nil {
		err := n.state.Cluster.UpsertWarningLocalNode(n.project, dbCluster.TypeNetwork, int(n.id), db.WarningNetworkStartupFailure, err.Error())
		if err != nil {
			n.logger.Warn("Failed to create warning", log.Ctx{"err": err})
		}
	} else {
		err := warnings.ResolveWarningsByLocalNodeAndProjectAndTypeAndEntity(n.state.Cluster, n.project, db.WarningNetworkStartupFailure, dbCluster.TypeNetwork, int(n.id))
		if err != nil {
			n.logger.Warn("Failed to resolve warning", log.Ctx{"err": err})
		}
	}

	return err
}

func (n *physical) setup(oldConfig map[string]string) error {
	n.logger.Debug("Start")

	revert := revert.New()
	defer revert.Fail()

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])
	created, err := VLANInterfaceCreate(n.config["parent"], hostName, n.config["vlan"], shared.IsTrue(n.config["gvrp"]))
	if err != nil {
		return err
	}
	if created {
		revert.Add(func() { InterfaceRemove(hostName) })
	}

	// Set the MTU.
	if n.config["mtu"] != "" {
		phyLink := &ip.Link{Name: hostName}
		err = phyLink.SetMTU(n.config["mtu"])
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", n.config["mtu"], phyLink.Name)
		}
	}

	// Record if we created this device or not (if we have not already recorded that we created it previously),
	// so it can be removed on stop. This way we won't overwrite the setting on LXD restart.
	if !shared.IsTrue(n.config["volatile.last_state.created"]) {
		n.config["volatile.last_state.created"] = fmt.Sprintf("%t", created)
		err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
			return tx.UpdateNetwork(n.id, n.description, n.config)
		})
		if err != nil {
			return errors.Wrapf(err, "Failed saving volatile config")
		}
	}

	revert.Success()
	return nil
}

// Stop stops is a no-op.
func (n *physical) Stop() error {
	n.logger.Debug("Stop")

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])

	// Only try and remove created VLAN interfaces.
	if n.config["vlan"] != "" && shared.IsTrue(n.config["volatile.last_state.created"]) && InterfaceExists(hostName) {
		err := InterfaceRemove(hostName)
		if err != nil {
			return err
		}
	}

	// Reset MTU back to 1500 if overridden in config.
	if n.config["mtu"] != "" && InterfaceExists(hostName) {
		resetMTU := "1500"
		link := &ip.Link{Name: hostName}
		err := link.SetMTU(resetMTU)
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", link, link.Name)
		}
	}

	// Remove last state config.
	delete(n.config, "volatile.last_state.created")
	err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.UpdateNetwork(n.id, n.description, n.config)
	})
	if err != nil {
		return errors.Wrapf(err, "Failed removing volatile config")
	}

	return nil
}

// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
// cluster notification, in which case do not update the database, just apply local changes needed.
func (n *physical) Update(newNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	n.logger.Debug("Update", log.Ctx{"clientType": clientType, "newNetwork": newNetwork})

	dbUpdateNeeeded, changedKeys, oldNetwork, err := n.common.configChanged(newNetwork)
	if err != nil {
		return err
	}

	if !dbUpdateNeeeded {
		return nil // Nothing changed.
	}

	// If the network as a whole has not had any previous creation attempts, or the node itself is still
	// pending, then don't apply the new settings to the node, just to the database record (ready for the
	// actual global create request to be initiated).
	if n.Status() == api.NetworkStatusPending || n.LocalStatus() == api.NetworkStatusPending {
		return n.common.update(newNetwork, targetNode, clientType)
	}

	revert := revert.New()
	defer revert.Fail()

	hostNameChanged := shared.StringInSlice("vlan", changedKeys) || shared.StringInSlice("parent", changedKeys)

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		if hostNameChanged {
			isUsed, err := n.IsUsed()
			if isUsed || err != nil {
				return fmt.Errorf("Cannot update network parent interface when in use")
			}

			inUse, err := n.checkParentUse(newNetwork.Config)
			if err != nil {
				return err
			}
			if inUse {
				return fmt.Errorf("Parent interface %q in use by another network", newNetwork.Config["parent"])
			}
		}
	}

	if hostNameChanged {
		err = n.Stop()
		if err != nil {
			return err
		}

		// Remove the volatile last state from submitted new config if present.
		delete(newNetwork.Config, "volatile.last_state.created")
	}

	// Define a function which reverts everything.
	revert.Add(func() {
		// Reset changes to all nodes and database.
		n.common.update(oldNetwork, targetNode, clientType)
	})

	// Apply changes to all nodes and databse.
	err = n.common.update(newNetwork, targetNode, clientType)
	if err != nil {
		return err
	}

	err = n.setup(oldNetwork.Config)
	if err != nil {
		return err
	}

	revert.Success()

	// Notify dependent networks (those using this network as their uplink) of the changes.
	// Do this after the network has been successfully updated so that a failure to notify a dependent network
	// doesn't prevent the network itself from being updated.
	if clientType == request.ClientTypeNormal && len(changedKeys) > 0 {
		n.common.notifyDependentNetworks(changedKeys)
	}

	return nil
}

// DHCPv4Subnet returns the DHCPv4 subnet (if DHCP is enabled on network).
func (n *physical) DHCPv4Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv4.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}

// DHCPv6Subnet returns the DHCPv6 subnet (if DHCP or SLAAC is enabled on network).
func (n *physical) DHCPv6Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv6.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}
<|endoftext|>