<commit_before>package swp

import (
	"fmt"
	"sync/atomic"
	"time"
)

// sliding window protocol
//
// Reference: pp118-120, Computer Networks: A Systems Approach
//  by Peterson and Davie, Morgan Kaufmann Publishers, 1996.
//
// In addition to sliding window, we implement flow-control
// similar to how tcp does for throttling senders.
// See pp296-301 of Peterson and Davie.
//
// Most of the implementation is in sender.go and recv.go.

//go:generate msgp

//msgp:ignore TxqSlot RxqSlot Semaphore SenderState RecvState SWP Session NatsNet SimNet

// Seqno is the sequence number used in the sliding window.
type Seqno int64

// Packet is what is transmitted between Sender and Recver.
type Packet struct {
	From string
	Dest string

	SeqNum    Seqno
	AckNum    Seqno
	AckOnly   bool
	KeepAlive bool

	// AvailReaderByteCap and AvailReaderMsgCap are
	// like the byte count AdvertisedWindow in TCP, but
	// since nats has both byte and message count
	// limits, we want convey these instead.
	AvailReaderBytesCap int64
	AvailReaderMsgCap   int64

	// on data payloads, allows the receiver to figure out how
	// big any gaps are, so as to give accurate flow control
	// byte count info. Including this packet's len(Data),
	// CumulBytesTransmitted should give the total accumulated
	// count of bytes ever transmitted on this session
	// from `From` to `Dest`.
	CumulBytesTransmitted int64

	Data []byte
}

// TxqSlot is the sender's sliding window element.
type TxqSlot struct {
	RetryDeadline time.Time
	Pack          *Packet
}

// RxqSlot is the receiver's sliding window element.
type RxqSlot struct {
	Received bool
	Pack     *Packet
}

// SWP holds the Sliding Window Protocol state
type SWP struct {
	Sender *SenderState
	Recver *RecvState
}

// NewSWP makes a new sliding window protocol manager, holding
// both sender and receiver components.
func NewSWP(net Network, windowMsgCount int64, windowByteCount int64,
	timeout time.Duration, inbox string, destInbox string) *SWP {

	snd := NewSenderState(net, windowMsgCount, timeout, inbox, destInbox)
	rcv := NewRecvState(net, windowMsgCount, windowByteCount, timeout, inbox, snd)
	swp := &SWP{
		Sender: snd,
		Recver: rcv,
	}

	return swp
}

// Session tracks a given point-to-point sesssion and its
// sliding window state for one of the end-points.
type Session struct {
	Swp         *SWP
	Destination string
	MyInbox     string

	Net            Network
	ReadMessagesCh chan InOrderSeq

	packetsConsumed uint64
	packetsSent     uint64
}

// NewSession makes a new Session, and calls
// Swp.Start to begin the sliding-window-protocol.
//
// If windowByteSz is negative or less than windowMsgSz,
// we estimate a byte size based on 10kb messages and the given windowMsgSz.
//
func NewSession(net Network,
	localInbox string,
	destInbox string,
	windowMsgSz int64,
	windowByteSz int64,
	timeout time.Duration) (*Session, error) {

	if windowMsgSz < 1 {
		return nil, fmt.Errorf("windowMsgSz must be 1 or more")
	}

	if windowByteSz < windowMsgSz {
		// guestimate
		windowByteSz = windowMsgSz * 10 * 1024
	}

	sess := &Session{
		Swp:         NewSWP(net, windowMsgSz, windowByteSz, timeout, localInbox, destInbox),
		MyInbox:     localInbox,
		Destination: destInbox,
		Net:         net,
	}
	sess.Swp.Start()
	sess.ReadMessagesCh = sess.Swp.Recver.ReadMessagesCh

	return sess, nil
}

var ErrShutdown = fmt.Errorf("shutting down")

// Push sends a message packet, blocking until that is done.
// You can use sess.CountPacketsSentForTransfer() to get
// the total count of packets Push()-ed so far.
func (sess *Session) Push(pack *Packet) {
	select {
	case sess.Swp.Sender.BlockingSend <- pack:
		q("%v Push succeeded on payload '%s' into BlockingSend", sess.MyInbox, string(pack.Data))
		sess.IncrPacketsSentForTransfer(1)
	case <-sess.Swp.Sender.ReqStop:
		// give up, Sender is shutting down.
	}
}

// SelfConsumeForTesting sets up a reader to read all produced
// messages automatically. You can use CountPacketsReadConsumed() to
// see the total number consumed thus far.
func (sess *Session) SelfConsumeForTesting() {
	go func() {
		for {
			select {
			case <-sess.Swp.Recver.ReqStop:
				return
			case read := <-sess.ReadMessagesCh:
				sess.IncrPacketsReadConsumed(int64(len(read.Seq)))
			}
		}
	}()
}

// InWindow returns true iff seqno is in [min, max].
func InWindow(seqno, min, max Seqno) bool {
	if seqno < min {
		return false
	}
	if seqno > max {
		return false
	}
	return true
}

// Stop shutsdown the session
func (s *Session) Stop() {
	s.Swp.Stop()
}

// Stop the sliding window protocol
func (s *SWP) Stop() {
	s.Recver.Stop()
	s.Sender.Stop()
}

// Start the sliding window protocol
func (s *SWP) Start() {
	//q("SWP Start() called")
	s.Recver.Start()
	s.Sender.Start()
}

// CountPacketsReadConsumed reports on how many packets
// the application has read from the session.
func (sess *Session) CountPacketsReadConsumed() int64 {
	return int64(atomic.LoadUint64(&sess.packetsConsumed))
}

// IncrPacketsReadConsumed increment packetsConsumed and return the new total.
func (sess *Session) IncrPacketsReadConsumed(n int64) int64 {
	return int64(atomic.AddUint64(&sess.packetsConsumed, uint64(n)))
}

// CountPacketsSentForTransfer reports on how many packets.
// the application has written to the session.
func (sess *Session) CountPacketsSentForTransfer() int64 {
	return int64(atomic.LoadUint64(&sess.packetsSent))
}

// IncrPacketsSentForTransfer increment packetsConsumed and return the new total.
func (sess *Session) IncrPacketsSentForTransfer(n int64) int64 {
	return int64(atomic.AddUint64(&sess.packetsSent, uint64(n)))
}
<commit_msg>atg. polish/doc<commit_after>package swp

import (
	"fmt"
	"sync/atomic"
	"time"
)

// sliding window protocol
//
// Reference: pp118-120, Computer Networks: A Systems Approach
//  by Peterson and Davie, Morgan Kaufmann Publishers, 1996.
//
// In addition to sliding window, we implement flow-control
// similar to how tcp does for throttling senders.
// See pp296-301 of Peterson and Davie.
//
// Most of the implementation is in sender.go and recv.go.

//go:generate msgp

//msgp:ignore TxqSlot RxqSlot Semaphore SenderState RecvState SWP Session NatsNet SimNet

// Seqno is the sequence number used in the sliding window.
type Seqno int64

// Packet is what is transmitted between Sender A and
// Recver B, where A and B are the two endpoints in a
// given Session. (Endpoints are specified by the strings localInbox and
// destInbox in the in the NewSession constructor.)
//
// Packets also flow symmetrically from Sender B to Recver A.
//
// Special packets are AckOnly and KeepAlive
// flagged; otherwise normal packets are data
// segments that have neither of these flags
// set. Only normal data packets are tracked
// for timeout and retry purposes.
type Packet struct {
	From string
	Dest string

	SeqNum    Seqno
	AckNum    Seqno
	AckOnly   bool
	KeepAlive bool

	// AvailReaderByteCap and AvailReaderMsgCap are
	// like the byte count AdvertisedWindow in TCP, but
	// since nats has both byte and message count
	// limits, we want convey these instead.
	AvailReaderBytesCap int64
	AvailReaderMsgCap   int64

	// CumulBytesTransmitted should give the total accumulated
	// count of bytes ever transmitted on this session
	// from `From` to `Dest`.
	// On data payloads, CumulBytesTransmitted allows
	// the receiver to figure out how
	// big any gaps are, so as to give accurate flow control
	// byte count info. The CumulBytesTransmitted count
	// should include this packet's len(Data), assuming
	// this is a data packet.
	CumulBytesTransmitted int64

	Data []byte
}

// TxqSlot is the sender's sliding window element.
type TxqSlot struct {
	RetryDeadline time.Time
	Pack          *Packet
}

// RxqSlot is the receiver's sliding window element.
type RxqSlot struct {
	Received bool
	Pack     *Packet
}

// SWP holds the Sliding Window Protocol state
type SWP struct {
	Sender *SenderState
	Recver *RecvState
}

// NewSWP makes a new sliding window protocol manager, holding
// both sender and receiver components.
func NewSWP(net Network, windowMsgCount int64, windowByteCount int64,
	timeout time.Duration, inbox string, destInbox string) *SWP {

	snd := NewSenderState(net, windowMsgCount, timeout, inbox, destInbox)
	rcv := NewRecvState(net, windowMsgCount, windowByteCount, timeout, inbox, snd)
	swp := &SWP{
		Sender: snd,
		Recver: rcv,
	}

	return swp
}

// Session tracks a given point-to-point sesssion and its
// sliding window state for one of the end-points.
type Session struct {
	Swp         *SWP
	Destination string
	MyInbox     string

	Net            Network
	ReadMessagesCh chan InOrderSeq

	packetsConsumed uint64
	packetsSent     uint64
}

// NewSession makes a new Session, and calls
// Swp.Start to begin the sliding-window-protocol.
//
// If windowByteSz is negative or less than windowMsgSz,
// we estimate a byte size based on 10kb messages and the given windowMsgSz.
//
func NewSession(net Network,
	localInbox string,
	destInbox string,
	windowMsgSz int64,
	windowByteSz int64,
	timeout time.Duration) (*Session, error) {

	if windowMsgSz < 1 {
		return nil, fmt.Errorf("windowMsgSz must be 1 or more")
	}

	if windowByteSz < windowMsgSz {
		// guestimate
		windowByteSz = windowMsgSz * 10 * 1024
	}

	sess := &Session{
		Swp:         NewSWP(net, windowMsgSz, windowByteSz, timeout, localInbox, destInbox),
		MyInbox:     localInbox,
		Destination: destInbox,
		Net:         net,
	}
	sess.Swp.Start()
	sess.ReadMessagesCh = sess.Swp.Recver.ReadMessagesCh

	return sess, nil
}

var ErrShutdown = fmt.Errorf("shutting down")

// Push sends a message packet, blocking until that is done.
// You can use sess.CountPacketsSentForTransfer() to get
// the total count of packets Push()-ed so far.
func (sess *Session) Push(pack *Packet) {
	select {
	case sess.Swp.Sender.BlockingSend <- pack:
		q("%v Push succeeded on payload '%s' into BlockingSend", sess.MyInbox, string(pack.Data))
		sess.IncrPacketsSentForTransfer(1)
	case <-sess.Swp.Sender.ReqStop:
		// give up, Sender is shutting down.
	}
}

// SelfConsumeForTesting sets up a reader to read all produced
// messages automatically. You can use CountPacketsReadConsumed() to
// see the total number consumed thus far.
func (sess *Session) SelfConsumeForTesting() {
	go func() {
		for {
			select {
			case <-sess.Swp.Recver.ReqStop:
				return
			case read := <-sess.ReadMessagesCh:
				sess.IncrPacketsReadConsumed(int64(len(read.Seq)))
			}
		}
	}()
}

// InWindow returns true iff seqno is in [min, max].
func InWindow(seqno, min, max Seqno) bool {
	if seqno < min {
		return false
	}
	if seqno > max {
		return false
	}
	return true
}

// Stop shutsdown the session
func (s *Session) Stop() {
	s.Swp.Stop()
}

// Stop the sliding window protocol
func (s *SWP) Stop() {
	s.Recver.Stop()
	s.Sender.Stop()
}

// Start the sliding window protocol
func (s *SWP) Start() {
	//q("SWP Start() called")
	s.Recver.Start()
	s.Sender.Start()
}

// CountPacketsReadConsumed reports on how many packets
// the application has read from the session.
func (sess *Session) CountPacketsReadConsumed() int64 {
	return int64(atomic.LoadUint64(&sess.packetsConsumed))
}

// IncrPacketsReadConsumed increment packetsConsumed and return the new total.
func (sess *Session) IncrPacketsReadConsumed(n int64) int64 {
	return int64(atomic.AddUint64(&sess.packetsConsumed, uint64(n)))
}

// CountPacketsSentForTransfer reports on how many packets.
// the application has written to the session.
func (sess *Session) CountPacketsSentForTransfer() int64 {
	return int64(atomic.LoadUint64(&sess.packetsSent))
}

// IncrPacketsSentForTransfer increment packetsConsumed and return the new total.
func (sess *Session) IncrPacketsSentForTransfer(n int64) int64 {
	return int64(atomic.AddUint64(&sess.packetsSent, uint64(n)))
}
<|endoftext|>


<commit_before>package main

import (
	"strings"
	"sync/atomic"
	"unsafe"

	r "github.com/dancannon/gorethink"
	"github.com/jaracil/ei"
)

func (nc *NexusConn) handleSysReq(req *JsonRpcReq) {
	switch req.Method {
	case "sys.ping":
		req.Result("pong")
	case "sys.watchdog":
		wdt := ei.N(req.Params).Int64Z()
		if wdt < 10 {
			wdt = 10
		}
		atomic.StoreInt64(&nc.wdog, wdt)
		req.Result(ei.M{"ok": true, "watchdog": wdt})
	case "sys.login":
		user, err := ei.N(req.Params).M("user").String()
		if err != nil {
			req.Error(ErrInvalidParams, "user", nil)
			return
		}
		pass, err := ei.N(req.Params).M("pass").String()
		if err != nil {
			req.Error(ErrInvalidParams, "pass", nil)
			return
		}
		var suser string
		split := strings.Split(user, ">")
		switch len(split) {
		case 1:
		case 2:
			if len(split[0]) > 0 && len(split[1]) > 0 {
				user = split[0]
				suser = split[1]
			} else {
				req.Error(ErrInvalidParams, "", nil)
				return
			}
		default:
			req.Error(ErrInvalidParams, "", nil)
			return
		}
		ud := &UserData{}
		cur, err := r.Table("users").Get(strings.ToLower(user)).Run(db)
		if err != nil {
			req.Error(ErrInternal, "", nil)
			return
		}
		defer cur.Close()
		err = cur.One(ud)
		if err != nil {
			if err == r.ErrEmptyResult {
				req.Error(ErrPermissionDenied, "", nil)
				return
			}
			req.Error(ErrInternal, "", nil)
			return
		}
		dk, err := HashPass(pass, ud.Salt)
		if err != nil {
			req.Error(ErrInternal, "", nil)
			return
		}
		if ud.Pass != dk {
			req.Error(ErrPermissionDenied, "", nil)
			return
		}
		atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&nc.user)), unsafe.Pointer(ud))

		if suser != "" {
			tags := nc.getTags(suser)
			if !(ei.N(tags).M("@admin").BoolZ()) {
				req.Error(ErrPermissionDenied, "", nil)
				return
			}
			sud := &UserData{}
			scur, err := r.Table("users").Get(strings.ToLower(suser)).Run(db)
			if err != nil {
				req.Error(ErrInternal, "", nil)
				return
			}
			defer scur.Close()
			err = scur.One(sud)
			if err != nil {
				if err == r.ErrEmptyResult {
					req.Error(ErrPermissionDenied, "", nil)
					return
				}
				req.Error(ErrInternal, "", nil)
				return
			}
			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&nc.user)), unsafe.Pointer(sud))
		}
		nc.updateSession()
		req.Result(ei.M{"ok": true, "user": nc.user.User})
	case "sys.nodes":
		tags := nc.getTags("sys.nodes")
		if !(ei.N(tags).M("@sys.nodes").BoolZ() || ei.N(tags).M("@admin").BoolZ()) {
			req.Error(ErrPermissionDenied, "", nil)
			return
		}
		cur, err := r.Table("nodes").Pluck("id", "clients", "load").Run(db)
		if err != nil {
			req.Error(ErrInternal, "", nil)
			return
		}
		var all []interface{}
		cur.All(&all)
		req.Result(all)

	default:
		req.Error(ErrMethodNotFound, "", nil)
	}
}
<commit_msg>Remove unused code<commit_after>package main

import (
	"strings"
	"sync/atomic"
	"unsafe"

	r "github.com/dancannon/gorethink"
	"github.com/jaracil/ei"
)

func (nc *NexusConn) handleSysReq(req *JsonRpcReq) {
	switch req.Method {
	case "sys.ping":
		req.Result("pong")
	case "sys.watchdog":
		wdt := ei.N(req.Params).Int64Z()
		if wdt < 10 {
			wdt = 10
		}
		atomic.StoreInt64(&nc.wdog, wdt)
		req.Result(ei.M{"ok": true, "watchdog": wdt})
	case "sys.login":
		user, err := ei.N(req.Params).M("user").String()
		if err != nil {
			req.Error(ErrInvalidParams, "user", nil)
			return
		}
		pass, err := ei.N(req.Params).M("pass").String()
		if err != nil {
			req.Error(ErrInvalidParams, "pass", nil)
			return
		}
		var suser string
		split := strings.Split(user, ">")
		switch len(split) {
		case 1:
		case 2:
			if len(split[0]) > 0 && len(split[1]) > 0 {
				user = split[0]
				suser = split[1]
			} else {
				req.Error(ErrInvalidParams, "", nil)
				return
			}
		default:
			req.Error(ErrInvalidParams, "", nil)
			return
		}
		ud := &UserData{}
		cur, err := r.Table("users").Get(strings.ToLower(user)).Run(db)
		if err != nil {
			req.Error(ErrInternal, "", nil)
			return
		}
		defer cur.Close()
		err = cur.One(ud)
		if err != nil {
			if err == r.ErrEmptyResult {
				req.Error(ErrPermissionDenied, "", nil)
				return
			}
			req.Error(ErrInternal, "", nil)
			return
		}
		dk, err := HashPass(pass, ud.Salt)
		if err != nil {
			req.Error(ErrInternal, "", nil)
			return
		}
		if ud.Pass != dk {
			req.Error(ErrPermissionDenied, "", nil)
			return
		}
		atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&nc.user)), unsafe.Pointer(ud))

		if suser != "" {
			tags := nc.getTags(suser)
			if !(ei.N(tags).M("@admin").BoolZ()) {
				req.Error(ErrPermissionDenied, "", nil)
				return
			}
			sud := &UserData{}
			scur, err := r.Table("users").Get(strings.ToLower(suser)).Run(db)
			if err != nil {
				req.Error(ErrInternal, "", nil)
				return
			}
			defer scur.Close()
			err = scur.One(sud)
			if err != nil {
				if err == r.ErrEmptyResult {
					req.Error(ErrPermissionDenied, "", nil)
					return
				}
				req.Error(ErrInternal, "", nil)
				return
			}
			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&nc.user)), unsafe.Pointer(sud))
		}
		nc.updateSession()
		req.Result(ei.M{"ok": true, "user": nc.user.User})

	default:
		req.Error(ErrMethodNotFound, "", nil)
	}
}
<|endoftext|>


<commit_before>package citrixadc

import (
	"github.com/chiradeep/go-nitro/config/lb"

	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceCitrixAdcLbvserver_appfwpolicy_binding() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createLbvserver_appfwpolicy_bindingFunc,
		Read:          readLbvserver_appfwpolicy_bindingFunc,
		Update:        updateLbvserver_appfwpolicy_bindingFunc,
		Delete:        deleteLbvserver_appfwpolicy_bindingFunc,
		Schema: map[string]*schema.Schema{
			"bindpoint": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"gotopriorityexpression": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"invoke": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
			"labelname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"labeltype": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"policyname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"priority": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func createLbvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In createLbvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	var lbvserver_appfwpolicy_bindingName string
	if v, ok := d.GetOk("name"); ok {
		lbvserver_appfwpolicy_bindingName = v.(string)
	} else {
		lbvserver_appfwpolicy_bindingName = resource.PrefixedUniqueId("tf-lbvserver_appfwpolicy_binding-")
		d.Set("name", lbvserver_appfwpolicy_bindingName)
	}
	lbvserver_appfwpolicy_binding := lb.Lbvserverappfwpolicybinding{
		Bindpoint:              d.Get("bindpoint").(string),
		Gotopriorityexpression: d.Get("gotopriorityexpression").(string),
		Invoke:                 d.Get("invoke").(bool),
		Labelname:              d.Get("labelname").(string),
		Labeltype:              d.Get("labeltype").(string),
		Name:                   d.Get("name").(string),
		Policyname:             d.Get("policyname").(string),
		Priority:               d.Get("priority").(int),
	}

	_, err := client.AddResource(netscaler.Lbvserver_appfwpolicy_binding.Type(), lbvserver_appfwpolicy_bindingName, &lbvserver_appfwpolicy_binding)
	if err != nil {
		return err
	}

	d.SetId(lbvserver_appfwpolicy_bindingName)

	err = readLbvserver_appfwpolicy_bindingFunc(d, meta)
	if err != nil {
		log.Printf("[ERROR] netscaler-provider: ?? we just created this lbvserver_appfwpolicy_binding but we can't read it ?? %s", lbvserver_appfwpolicy_bindingName)
		return nil
	}
	return nil
}

func readLbvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] citrixadc-provider:  In readLbvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	lbvserver_appfwpolicy_bindingName := d.Id()
	log.Printf("[DEBUG] citrixadc-provider: Reading lbvserver_appfwpolicy_binding state %s", lbvserver_appfwpolicy_bindingName)
	data, err := client.FindResource(netscaler.Lbvserver_appfwpolicy_binding.Type(), lbvserver_appfwpolicy_bindingName)
	if err != nil {
		log.Printf("[WARN] citrixadc-provider: Clearing lbvserver_appfwpolicy_binding state %s", lbvserver_appfwpolicy_bindingName)
		d.SetId("")
		return nil
	}
	d.Set("name", data["name"])
	d.Set("bindpoint", data["bindpoint"])
	d.Set("gotopriorityexpression", data["gotopriorityexpression"])
	d.Set("invoke", data["invoke"])
	d.Set("labelname", data["labelname"])
	d.Set("labeltype", data["labeltype"])
	d.Set("name", data["name"])
	d.Set("policyname", data["policyname"])
	d.Set("priority", data["priority"])

	return nil

}

func updateLbvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In updateLbvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	lbvserver_appfwpolicy_bindingName := d.Get("name").(string)

	lbvserver_appfwpolicy_binding := lb.Lbvserverappfwpolicybinding{
		Name: d.Get("name").(string),
	}
	hasChange := false
	if d.HasChange("bindpoint") {
		log.Printf("[DEBUG]  citrixadc-provider: Bindpoint has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Bindpoint = d.Get("bindpoint").(string)
		hasChange = true
	}
	if d.HasChange("gotopriorityexpression") {
		log.Printf("[DEBUG]  citrixadc-provider: Gotopriorityexpression has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Gotopriorityexpression = d.Get("gotopriorityexpression").(string)
		hasChange = true
	}
	if d.HasChange("invoke") {
		log.Printf("[DEBUG]  citrixadc-provider: Invoke has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Invoke = d.Get("invoke").(bool)
		hasChange = true
	}
	if d.HasChange("labelname") {
		log.Printf("[DEBUG]  citrixadc-provider: Labelname has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Labelname = d.Get("labelname").(string)
		hasChange = true
	}
	if d.HasChange("labeltype") {
		log.Printf("[DEBUG]  citrixadc-provider: Labeltype has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Labeltype = d.Get("labeltype").(string)
		hasChange = true
	}
	if d.HasChange("name") {
		log.Printf("[DEBUG]  citrixadc-provider: Name has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Name = d.Get("name").(string)
		hasChange = true
	}
	if d.HasChange("policyname") {
		log.Printf("[DEBUG]  citrixadc-provider: Policyname has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Policyname = d.Get("policyname").(string)
		hasChange = true
	}
	if d.HasChange("priority") {
		log.Printf("[DEBUG]  citrixadc-provider: Priority has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Priority = d.Get("priority").(int)
		hasChange = true
	}

	if hasChange {
		_, err := client.UpdateResource(netscaler.Lbvserver_appfwpolicy_binding.Type(), lbvserver_appfwpolicy_bindingName, &lbvserver_appfwpolicy_binding)
		if err != nil {
			return fmt.Errorf("Error updating lbvserver_appfwpolicy_binding %s", lbvserver_appfwpolicy_bindingName)
		}
	}
	return readLbvserver_appfwpolicy_bindingFunc(d, meta)
}

func deleteLbvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In deleteLbvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	args := make(map[string]string)
	args["policyname"] = d.Get("policyname").(string)
	err := client.DeleteResourceWithArgsMap(netscaler.Lbvserver_appfwpolicy_binding.Type(), d.Get("name").(string), args)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}
<commit_msg>make name and policyname as primary attributes<commit_after>package citrixadc

import (
	"github.com/chiradeep/go-nitro/config/lb"

	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceCitrixAdcLbvserver_appfwpolicy_binding() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createLbvserver_appfwpolicy_bindingFunc,
		Read:          readLbvserver_appfwpolicy_bindingFunc,
		Update:        updateLbvserver_appfwpolicy_bindingFunc,
		Delete:        deleteLbvserver_appfwpolicy_bindingFunc,
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"policyname": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"bindpoint": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"gotopriorityexpression": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"invoke": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
			"labelname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"labeltype": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"priority": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func createLbvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In createLbvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	lbvserver_appfwpolicy_bindingName := d.Get("name").(string)
	lbvserver_appfwpolicy_binding := lb.Lbvserverappfwpolicybinding{
		Bindpoint:              d.Get("bindpoint").(string),
		Gotopriorityexpression: d.Get("gotopriorityexpression").(string),
		Invoke:                 d.Get("invoke").(bool),
		Labelname:              d.Get("labelname").(string),
		Labeltype:              d.Get("labeltype").(string),
		Name:                   d.Get("name").(string),
		Policyname:             d.Get("policyname").(string),
		Priority:               d.Get("priority").(int),
	}

	_, err := client.AddResource(netscaler.Lbvserver_appfwpolicy_binding.Type(), lbvserver_appfwpolicy_bindingName, &lbvserver_appfwpolicy_binding)
	if err != nil {
		return err
	}

	d.SetId(lbvserver_appfwpolicy_bindingName)

	err = readLbvserver_appfwpolicy_bindingFunc(d, meta)
	if err != nil {
		log.Printf("[ERROR] netscaler-provider: ?? we just created this lbvserver_appfwpolicy_binding but we can't read it ?? %s", lbvserver_appfwpolicy_bindingName)
		return nil
	}
	return nil
}

func readLbvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] citrixadc-provider:  In readLbvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	lbvserver_appfwpolicy_bindingName := d.Id()
	log.Printf("[DEBUG] citrixadc-provider: Reading lbvserver_appfwpolicy_binding state %s", lbvserver_appfwpolicy_bindingName)
	data, err := client.FindResource(netscaler.Lbvserver_appfwpolicy_binding.Type(), lbvserver_appfwpolicy_bindingName)
	if err != nil {
		log.Printf("[WARN] citrixadc-provider: Clearing lbvserver_appfwpolicy_binding state %s", lbvserver_appfwpolicy_bindingName)
		d.SetId("")
		return nil
	}
	d.Set("name", data["name"])
	d.Set("bindpoint", data["bindpoint"])
	d.Set("gotopriorityexpression", data["gotopriorityexpression"])
	d.Set("invoke", data["invoke"])
	d.Set("labelname", data["labelname"])
	d.Set("labeltype", data["labeltype"])
	d.Set("name", data["name"])
	d.Set("policyname", data["policyname"])
	d.Set("priority", data["priority"])

	return nil

}

func updateLbvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In updateLbvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	lbvserver_appfwpolicy_bindingName := d.Get("name").(string)

	lbvserver_appfwpolicy_binding := lb.Lbvserverappfwpolicybinding{
		Name: d.Get("name").(string),
	}
	hasChange := false
	if d.HasChange("bindpoint") {
		log.Printf("[DEBUG]  citrixadc-provider: Bindpoint has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Bindpoint = d.Get("bindpoint").(string)
		hasChange = true
	}
	if d.HasChange("gotopriorityexpression") {
		log.Printf("[DEBUG]  citrixadc-provider: Gotopriorityexpression has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Gotopriorityexpression = d.Get("gotopriorityexpression").(string)
		hasChange = true
	}
	if d.HasChange("invoke") {
		log.Printf("[DEBUG]  citrixadc-provider: Invoke has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Invoke = d.Get("invoke").(bool)
		hasChange = true
	}
	if d.HasChange("labelname") {
		log.Printf("[DEBUG]  citrixadc-provider: Labelname has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Labelname = d.Get("labelname").(string)
		hasChange = true
	}
	if d.HasChange("labeltype") {
		log.Printf("[DEBUG]  citrixadc-provider: Labeltype has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Labeltype = d.Get("labeltype").(string)
		hasChange = true
	}
	if d.HasChange("name") {
		log.Printf("[DEBUG]  citrixadc-provider: Name has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Name = d.Get("name").(string)
		hasChange = true
	}
	if d.HasChange("policyname") {
		log.Printf("[DEBUG]  citrixadc-provider: Policyname has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Policyname = d.Get("policyname").(string)
		hasChange = true
	}
	if d.HasChange("priority") {
		log.Printf("[DEBUG]  citrixadc-provider: Priority has changed for lbvserver_appfwpolicy_binding %s, starting update", lbvserver_appfwpolicy_bindingName)
		lbvserver_appfwpolicy_binding.Priority = d.Get("priority").(int)
		hasChange = true
	}

	if hasChange {
		_, err := client.UpdateResource(netscaler.Lbvserver_appfwpolicy_binding.Type(), lbvserver_appfwpolicy_bindingName, &lbvserver_appfwpolicy_binding)
		if err != nil {
			return fmt.Errorf("Error updating lbvserver_appfwpolicy_binding %s", lbvserver_appfwpolicy_bindingName)
		}
	}
	return readLbvserver_appfwpolicy_bindingFunc(d, meta)
}

func deleteLbvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In deleteLbvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	args := make(map[string]string)
	args["policyname"] = d.Get("policyname").(string)
	err := client.DeleteResourceWithArgsMap(netscaler.Lbvserver_appfwpolicy_binding.Type(), d.Get("name").(string), args)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}
<|endoftext|>


<commit_before>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// query command tests
// Author: bratseth

package cmd

import (
    "github.com/stretchr/testify/assert"
    "testing"
)

func TestSimpleQuery(t *testing.T) {
    reset()

    nextBody = "query result"
    query := "select from * where title contains foo"
	assert.Equal(t,
	             "query result\n",
	             executeCommand(t, []string{"query", "?query=" + query},[]string{}),
	             "query output")
    assert.Equal(t, GetTarget(queryContext).query + "/search/?query=" + query, lastRequest.URL.String())
}

func TestQueryWithParameters(t *testing.T) {
    reset()

    nextBody = "query result"
    query := "select from * where title contains foo"
	assert.Equal(t,
	             "query result\n",
	             executeCommand(t, []string{"query", "?hits=4&query=" + query},[]string{}),
	             "query output")
    assert.Equal(t, GetTarget(queryContext).query + "/search/?hits=4&query=" + query, lastRequest.URL.String())
}

func TestSimpleQueryMissingQuestionMark(t *testing.T) {
    reset()

    nextBody = "query result"
    query := "select from * where title contains foo"
	assert.Equal(t,
	             "query result\n",
	             executeCommand(t, []string{"query", "query=" + query},[]string{}),
	             "query output")
    assert.Equal(t, GetTarget(queryContext).query + "/search/?query=" + query, lastRequest.URL.String())
}

func TestSimpleQueryMissingQuestionMarkAndQueryEquals(t *testing.T) {
    reset()

    nextBody = "query result"
    query := "select from * where title contains foo"
	assert.Equal(t,
	             "query result\n",
	             executeCommand(t, []string{"query", query},[]string{}),
	             "query output")
    assert.Equal(t, GetTarget(queryContext).query + "/search/?query=" + query, lastRequest.URL.String())
}
<commit_msg>Cleanup<commit_after>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// query command tests
// Author: bratseth

package cmd

import (
    "github.com/stretchr/testify/assert"
    "testing"
)

func TestQuery(t *testing.T) {
    assertQuery("", "?query=select from * where title contains foo&hits=5", t)
}

func TestQueryWithParameters(t *testing.T) {
    assertQuery("?", "query=select from * where title contains foo&hits=5", t)
}

func TestSimpleQueryMissingQuestionMark(t *testing.T) {
    assertQuery("?", "query=select from * where title contains foo", t)
}

func TestSimpleQueryMissingQuestionMarkAndQueryEquals(t *testing.T) {
    assertQuery("?query=", "select from * where title contains foo", t)
}

func assertQuery(expectedPrefix string, query string, t *testing.T) {
    reset()

    nextBody = "query result"
	assert.Equal(t,
	             "query result\n",
	             executeCommand(t, []string{"query", query},[]string{}),
	             "query output")
    assert.Equal(t, GetTarget(queryContext).query + "/search/" + expectedPrefix + query, lastRequest.URL.String())
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package media

import (
	"fmt"
	"time"

	"github.com/godbus/dbus"
)

// mprisPlayer stores the dbus, player object, and an error together,
// to simplify checking for errors after each call.
type mprisPlayer struct {
	bus    dbus.BusObject
	player dbus.BusObject
	info   *Info
	err    error
}

func newMprisPlayer(sessionBus *dbus.Conn, playerName string, info *Info) *mprisPlayer {
	// dbus name for the player.
	dest := fmt.Sprintf("org.mpris.MediaPlayer2.%s", playerName)
	// Get the dbus objects for the session bus and the player.
	player := &mprisPlayer{
		player: sessionBus.Object(dest, "/org/mpris/MediaPlayer2"),
		bus:    sessionBus.BusObject(),
		info:   info,
	}
	// Check if the player is already running.
	res, ok := player.Call(methodNameHasOwner, dest)
	if ok && res.(bool) {
		// Player is running.
		res, ok := player.Call(methodGetNameOwner, dest)
		if ok {
			// Get initial media info.
			player.getInitialInfo()
			// Add signal matches for metadata change and seek.
			player.addMatches(res.(string))
		}
	}
	// If the player is not running, do nothing,
	// and the NameOwnerChanged listener will take care of it.
	// Add listeners for player startup/shutdown to keep track of it's bus id.
	player.Call(methodAddMatch, signalNameOwnerChanged.buildMatchString("", dest))
	return player
}

func (m *mprisPlayer) addMatches(sender string) {
	m.Call(methodAddMatch, signalSeeked.buildMatchString(sender))
	m.Call(methodAddMatch, signalPropChanged.buildMatchString(sender, mprisInterface))
}

func (m *mprisPlayer) removeMatches(sender string) {
	m.Call(methodRemoveMatch, signalSeeked.buildMatchString(sender))
	m.Call(methodRemoveMatch, signalPropChanged.buildMatchString(sender, mprisInterface))
}

func (m *mprisPlayer) Play() {
	m.Call(mprisPlay)
}

func (m *mprisPlayer) Pause() {
	m.Call(mprisPause)
}

func (m *mprisPlayer) PlayPause() {
	m.Call(mprisPlayPause)
}

func (m *mprisPlayer) Stop() {
	m.Call(mprisStop)
}

func (m *mprisPlayer) Next() {
	m.Call(mprisNext)
}

func (m *mprisPlayer) Previous() {
	m.Call(mprisPrev)
}

func (m *mprisPlayer) Seek(offset time.Duration) {
	micros := int64(offset / time.Microsecond)
	m.Call(mprisSeek, micros)
}

// Call forwards a method call to either the bus or the player as appropriate,
// and returns the first returned value (or nil if nothing was returned).
func (m *mprisPlayer) Call(method name, args ...interface{}) (interface{}, bool) {
	if m.err != nil {
		return nil, false
	}
	var call *dbus.Call
	if method.iface == mprisInterface {
		// m.player's interface != mprisInterface, so full method name is required.
		call = m.player.Call(method.String(), 0, args...)
	} else {
		call = m.bus.Call(method.member, 0, args...)
	}
	m.err = call.Err
	if m.err != nil {
		return nil, false
	}
	if len(call.Body) > 0 {
		return call.Body[0], true
	}
	return nil, true
}

// getInitialInfo gets the initial media info from a player.
func (m *mprisPlayer) getInitialInfo() {
	i := m.infoReader(m)
	i.updatePlaybackStatus()
	i.updateMetadata()
	i.updateShuffle()
	i.updatePosition()
	i.updateRate()
}

// updates keeps track of the types of updates caused by a dbus signal.
type updates struct {
	// Includes position due to explicit seek and play state.
	position bool
	// Includes all metadata, including art.
	metadata bool
	// Only when going from playing <-> something else. That is,
	// stopped -> paused will not mark this true.
	playingState bool
}

// any returns true if any updates occurred when handing the dbus signal.
func (u updates) any() bool {
	return u.position || u.metadata || u.playingState
}

// handleDbusSignal handles dbus signals for track changes, seeking, and
// name owner changes (player appearing on or disappearing from session bus).
func (m *mprisPlayer) handleDbusSignal(signal *dbus.Signal) (updates, error) {
	switch signal.Name {
	case signalPropChanged.String():
		i := m.infoReader(dbusMap(signal.Body[1].(map[string]dbus.Variant)))
		i.updatePlaybackStatus()
		i.updateMetadata()
		i.updateShuffle()
		i.updatePosition()
		i.updateRate()
		return i.updates, m.err

	case signalSeeked.String():
		i := m.infoReader(dbusMap{
			mprisPosition.member: dbus.MakeVariant(signal.Body[0]),
		})
		i.updatePosition()
		return i.updates, m.err

	case signalNameOwnerChanged.String():
		oldName := signal.Body[1].(string)
		newName := signal.Body[2].(string)
		if len(oldName) > 0 {
			m.removeMatches(oldName)
		} else {
			// Clear cached info on new name acquisition, since some players
			// don't send empty info on start.
			*m.info = Info{}
		}
		if len(newName) > 0 {
			m.addMatches(newName)
			m.getInitialInfo()
		} else {
			// Empty newName => player disconnected from dbus.
			m.info.PlaybackStatus = Disconnected
			return updates{true, true, true}, m.err
		}
	}
	return updates{}, m.err
}

func (m *mprisPlayer) infoReader(getter dbusGetter) infoReader {
	return infoReader{
		Info:       m.info,
		dbusGetter: getter,
	}
}

// dbusGetter allows getting named dbus properties.
type dbusGetter interface {
	Get(name) (interface{}, bool)
}

// dbusMap implements dbusGetter for a map of string -> variants.
type dbusMap map[string]dbus.Variant

func (i dbusMap) Get(n name) (interface{}, bool) {
	if v, ok := i[n.member]; ok {
		return v.Value(), true
	}
	return nil, false
}

// Get gets a player property from the mpris player object.
// This satisfies the dbusGetter interface via queries to the player.
func (m *mprisPlayer) Get(prop name) (interface{}, bool) {
	if m.err != nil {
		return nil, false
	}
	var v dbus.Variant
	v, m.err = m.player.GetProperty(prop.String())
	if m.err == nil {
		return v.Value(), true
	}
	return nil, false
}

// infoReader providers helper methods to update media info
// from dbus signals or properties.
type infoReader struct {
	*Info
	dbusGetter
	updates updates
}

func (i *infoReader) updatePlaybackStatus() {
	status, ok := i.Get(mprisStatus)
	if !ok {
		return
	}
	oldState := i.PlaybackStatus
	i.PlaybackStatus = PlaybackStatus(status.(string))
	if i.PlaybackStatus == oldState {
		return
	}
	i.updates.position = true
	// Mark playing state as changed if we went from playing to something else,
	// or if we went from anything else to playing.
	// We use this to determine whether the scheduler for the current position
	// needs to be updated.
	if i.PlaybackStatus == Playing || oldState == Playing {
		i.updates.playingState = true
	}
	switch i.PlaybackStatus {
	case Playing:
		if oldState == Paused {
			// If we resumed playing, keep current position
			// but mark it as just updated.
			i.lastUpdated = time.Now()
		}
	case Paused:
		if oldState == Playing {
			// If we paused from playing, then snapshot the
			// playback position, since effectively the rate
			// just changed to 0.
			i.snapshotPosition()
		}
	case Stopped:
		// mpris suggests that Stop should reset the position
		i.lastPosition = 0
		i.lastUpdated = time.Now()
	}
}

func (i *infoReader) updateShuffle() {
	if shuffle, ok := i.Get(mprisShuffle); ok {
		i.Shuffle = shuffle.(bool)
		i.updates.metadata = true
	}
}

func (i *infoReader) updatePosition() {
	if !i.Playing() && !i.Paused() {
		// Some players throw errors when asked for position while stopped.
		return
	}
	if position, ok := i.Get(mprisPosition); ok {
		i.lastUpdated = time.Now()
		i.lastPosition = time.Duration(getLong(position)) * time.Microsecond
		i.updates.position = true
	}
}

func (i *infoReader) updateRate() {
	if rate, ok := i.Get(mprisRate); ok {
		// Position computed based on new rate will not be valid earlier,
		// so snapshot it now for correct values later.
		i.snapshotPosition()
		i.rate = getDouble(rate)
		// Do not mark as updated, will be picked up at next tick.
	}
}

func (i *infoReader) updateMetadata() {
	metadataMap, ok := i.Get(mprisMetadata)
	if !ok {
		return
	}
	metadata := metadataMap.(map[string]dbus.Variant)
	if length, ok := metadata["mpris:length"]; ok {
		i.Length = time.Duration(getLong(length)) * time.Microsecond
		i.updates.metadata = true
	}
	if artist, ok := metadata["xesam:artist"]; ok {
		artists := artist.Value().([]string)
		if len(artists) > 0 {
			i.Artist = artists[0]
			i.updates.metadata = true
		}
	}
	if aArtist, ok := metadata["xesam:albumArtist"]; ok {
		artists := aArtist.Value().([]string)
		if len(artists) > 0 {
			i.AlbumArtist = artists[0]
			i.updates.metadata = true
		}
	}
	if album, ok := metadata["xesam:album"]; ok {
		i.Album = album.Value().(string)
		i.updates.metadata = true
	}
	if title, ok := metadata["xesam:title"]; ok {
		i.Title = title.Value().(string)
		i.updates.metadata = true
	}
	if ArtURL, ok := metadata["mpris:ArtURL"]; ok {
		i.ArtURL = ArtURL.Value().(string)
		i.updates.metadata = true
	}
	if id, ok := metadata["mpris:trackid"]; ok {
		trackID := id.Value().(string)
		if trackID != i.trackID {
			// mpris suggests that position should be reset on track change.
			i.lastPosition = 0
			i.lastUpdated = time.Now()
			i.trackID = trackID
		}
		i.updates.metadata = true
	}
}
<commit_msg>Don't assume mpris track ID is a string<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package media

import (
	"fmt"
	"time"

	"github.com/godbus/dbus"
)

// mprisPlayer stores the dbus, player object, and an error together,
// to simplify checking for errors after each call.
type mprisPlayer struct {
	bus    dbus.BusObject
	player dbus.BusObject
	info   *Info
	err    error
}

func newMprisPlayer(sessionBus *dbus.Conn, playerName string, info *Info) *mprisPlayer {
	// dbus name for the player.
	dest := fmt.Sprintf("org.mpris.MediaPlayer2.%s", playerName)
	// Get the dbus objects for the session bus and the player.
	player := &mprisPlayer{
		player: sessionBus.Object(dest, "/org/mpris/MediaPlayer2"),
		bus:    sessionBus.BusObject(),
		info:   info,
	}
	// Check if the player is already running.
	res, ok := player.Call(methodNameHasOwner, dest)
	if ok && res.(bool) {
		// Player is running.
		res, ok := player.Call(methodGetNameOwner, dest)
		if ok {
			// Get initial media info.
			player.getInitialInfo()
			// Add signal matches for metadata change and seek.
			player.addMatches(res.(string))
		}
	}
	// If the player is not running, do nothing,
	// and the NameOwnerChanged listener will take care of it.
	// Add listeners for player startup/shutdown to keep track of it's bus id.
	player.Call(methodAddMatch, signalNameOwnerChanged.buildMatchString("", dest))
	return player
}

func (m *mprisPlayer) addMatches(sender string) {
	m.Call(methodAddMatch, signalSeeked.buildMatchString(sender))
	m.Call(methodAddMatch, signalPropChanged.buildMatchString(sender, mprisInterface))
}

func (m *mprisPlayer) removeMatches(sender string) {
	m.Call(methodRemoveMatch, signalSeeked.buildMatchString(sender))
	m.Call(methodRemoveMatch, signalPropChanged.buildMatchString(sender, mprisInterface))
}

func (m *mprisPlayer) Play() {
	m.Call(mprisPlay)
}

func (m *mprisPlayer) Pause() {
	m.Call(mprisPause)
}

func (m *mprisPlayer) PlayPause() {
	m.Call(mprisPlayPause)
}

func (m *mprisPlayer) Stop() {
	m.Call(mprisStop)
}

func (m *mprisPlayer) Next() {
	m.Call(mprisNext)
}

func (m *mprisPlayer) Previous() {
	m.Call(mprisPrev)
}

func (m *mprisPlayer) Seek(offset time.Duration) {
	micros := int64(offset / time.Microsecond)
	m.Call(mprisSeek, micros)
}

// Call forwards a method call to either the bus or the player as appropriate,
// and returns the first returned value (or nil if nothing was returned).
func (m *mprisPlayer) Call(method name, args ...interface{}) (interface{}, bool) {
	if m.err != nil {
		return nil, false
	}
	var call *dbus.Call
	if method.iface == mprisInterface {
		// m.player's interface != mprisInterface, so full method name is required.
		call = m.player.Call(method.String(), 0, args...)
	} else {
		call = m.bus.Call(method.member, 0, args...)
	}
	m.err = call.Err
	if m.err != nil {
		return nil, false
	}
	if len(call.Body) > 0 {
		return call.Body[0], true
	}
	return nil, true
}

// getInitialInfo gets the initial media info from a player.
func (m *mprisPlayer) getInitialInfo() {
	i := m.infoReader(m)
	i.updatePlaybackStatus()
	i.updateMetadata()
	i.updateShuffle()
	i.updatePosition()
	i.updateRate()
}

// updates keeps track of the types of updates caused by a dbus signal.
type updates struct {
	// Includes position due to explicit seek and play state.
	position bool
	// Includes all metadata, including art.
	metadata bool
	// Only when going from playing <-> something else. That is,
	// stopped -> paused will not mark this true.
	playingState bool
}

// any returns true if any updates occurred when handing the dbus signal.
func (u updates) any() bool {
	return u.position || u.metadata || u.playingState
}

// handleDbusSignal handles dbus signals for track changes, seeking, and
// name owner changes (player appearing on or disappearing from session bus).
func (m *mprisPlayer) handleDbusSignal(signal *dbus.Signal) (updates, error) {
	switch signal.Name {
	case signalPropChanged.String():
		i := m.infoReader(dbusMap(signal.Body[1].(map[string]dbus.Variant)))
		i.updatePlaybackStatus()
		i.updateMetadata()
		i.updateShuffle()
		i.updatePosition()
		i.updateRate()
		return i.updates, m.err

	case signalSeeked.String():
		i := m.infoReader(dbusMap{
			mprisPosition.member: dbus.MakeVariant(signal.Body[0]),
		})
		i.updatePosition()
		return i.updates, m.err

	case signalNameOwnerChanged.String():
		oldName := signal.Body[1].(string)
		newName := signal.Body[2].(string)
		if len(oldName) > 0 {
			m.removeMatches(oldName)
		} else {
			// Clear cached info on new name acquisition, since some players
			// don't send empty info on start.
			*m.info = Info{}
		}
		if len(newName) > 0 {
			m.addMatches(newName)
			m.getInitialInfo()
		} else {
			// Empty newName => player disconnected from dbus.
			m.info.PlaybackStatus = Disconnected
			return updates{true, true, true}, m.err
		}
	}
	return updates{}, m.err
}

func (m *mprisPlayer) infoReader(getter dbusGetter) infoReader {
	return infoReader{
		Info:       m.info,
		dbusGetter: getter,
	}
}

// dbusGetter allows getting named dbus properties.
type dbusGetter interface {
	Get(name) (interface{}, bool)
}

// dbusMap implements dbusGetter for a map of string -> variants.
type dbusMap map[string]dbus.Variant

func (i dbusMap) Get(n name) (interface{}, bool) {
	if v, ok := i[n.member]; ok {
		return v.Value(), true
	}
	return nil, false
}

// Get gets a player property from the mpris player object.
// This satisfies the dbusGetter interface via queries to the player.
func (m *mprisPlayer) Get(prop name) (interface{}, bool) {
	if m.err != nil {
		return nil, false
	}
	var v dbus.Variant
	v, m.err = m.player.GetProperty(prop.String())
	if m.err == nil {
		return v.Value(), true
	}
	return nil, false
}

// infoReader providers helper methods to update media info
// from dbus signals or properties.
type infoReader struct {
	*Info
	dbusGetter
	updates updates
}

func (i *infoReader) updatePlaybackStatus() {
	status, ok := i.Get(mprisStatus)
	if !ok {
		return
	}
	oldState := i.PlaybackStatus
	i.PlaybackStatus = PlaybackStatus(status.(string))
	if i.PlaybackStatus == oldState {
		return
	}
	i.updates.position = true
	// Mark playing state as changed if we went from playing to something else,
	// or if we went from anything else to playing.
	// We use this to determine whether the scheduler for the current position
	// needs to be updated.
	if i.PlaybackStatus == Playing || oldState == Playing {
		i.updates.playingState = true
	}
	switch i.PlaybackStatus {
	case Playing:
		if oldState == Paused {
			// If we resumed playing, keep current position
			// but mark it as just updated.
			i.lastUpdated = time.Now()
		}
	case Paused:
		if oldState == Playing {
			// If we paused from playing, then snapshot the
			// playback position, since effectively the rate
			// just changed to 0.
			i.snapshotPosition()
		}
	case Stopped:
		// mpris suggests that Stop should reset the position
		i.lastPosition = 0
		i.lastUpdated = time.Now()
	}
}

func (i *infoReader) updateShuffle() {
	if shuffle, ok := i.Get(mprisShuffle); ok {
		i.Shuffle = shuffle.(bool)
		i.updates.metadata = true
	}
}

func (i *infoReader) updatePosition() {
	if !i.Playing() && !i.Paused() {
		// Some players throw errors when asked for position while stopped.
		return
	}
	if position, ok := i.Get(mprisPosition); ok {
		i.lastUpdated = time.Now()
		i.lastPosition = time.Duration(getLong(position)) * time.Microsecond
		i.updates.position = true
	}
}

func (i *infoReader) updateRate() {
	if rate, ok := i.Get(mprisRate); ok {
		// Position computed based on new rate will not be valid earlier,
		// so snapshot it now for correct values later.
		i.snapshotPosition()
		i.rate = getDouble(rate)
		// Do not mark as updated, will be picked up at next tick.
	}
}

func (i *infoReader) updateMetadata() {
	metadataMap, ok := i.Get(mprisMetadata)
	if !ok {
		return
	}
	metadata := metadataMap.(map[string]dbus.Variant)
	if length, ok := metadata["mpris:length"]; ok {
		i.Length = time.Duration(getLong(length)) * time.Microsecond
		i.updates.metadata = true
	}
	if artist, ok := metadata["xesam:artist"]; ok {
		artists := artist.Value().([]string)
		if len(artists) > 0 {
			i.Artist = artists[0]
			i.updates.metadata = true
		}
	}
	if aArtist, ok := metadata["xesam:albumArtist"]; ok {
		artists := aArtist.Value().([]string)
		if len(artists) > 0 {
			i.AlbumArtist = artists[0]
			i.updates.metadata = true
		}
	}
	if album, ok := metadata["xesam:album"]; ok {
		i.Album = album.Value().(string)
		i.updates.metadata = true
	}
	if title, ok := metadata["xesam:title"]; ok {
		i.Title = title.Value().(string)
		i.updates.metadata = true
	}
	if ArtURL, ok := metadata["mpris:ArtURL"]; ok {
		i.ArtURL = ArtURL.Value().(string)
		i.updates.metadata = true
	}
	if id, ok := metadata["mpris:trackid"]; ok {
		trackID := id.String()
		if trackID != i.trackID {
			// mpris suggests that position should be reset on track change.
			i.lastPosition = 0
			i.lastUpdated = time.Now()
			i.trackID = trackID
		}
		i.updates.metadata = true
	}
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package relation_test

import (
	"time"

	jc "github.com/juju/testing/checkers"
	"gopkg.in/juju/charm.v3/hooks"
	gc "launchpad.net/gocheck"

	"github.com/juju/juju/apiserver/params"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker/uniter/hook"
	"github.com/juju/juju/worker/uniter/relation"
)

type HookQueueSuite struct{}

var _ = gc.Suite(&HookQueueSuite{})

type msi map[string]int64

type hookQueueTest struct {
	summary string
	initial *relation.State
	steps   []checker
}

func fullTest(summary string, steps ...checker) hookQueueTest {
	return hookQueueTest{summary, &relation.State{21345, nil, ""}, steps}
}

func reconcileTest(summary string, members msi, joined string, steps ...checker) hookQueueTest {
	return hookQueueTest{summary, &relation.State{21345, members, joined}, steps}
}

var aliveHookQueueTests = []hookQueueTest{
	fullTest(
		"Empty initial change causes no hooks.",
		send{nil, nil},
	), fullTest(
		"Joined and changed are both run when unit is first detected.",
		send{msi{"u/0": 0}, nil},
		expect{hooks.RelationJoined, "u/0", 0},
		expect{hooks.RelationChanged, "u/0", 0},
	), fullTest(
		"Automatic changed is run with latest settings.",
		send{msi{"u/0": 0}, nil},
		expect{hooks.RelationJoined, "u/0", 0},
		send{msi{"u/0": 7}, nil},
		expect{hooks.RelationChanged, "u/0", 7},
	), fullTest(
		"Joined is also run with latest settings.",
		send{msi{"u/0": 0}, nil},
		send{msi{"u/0": 7}, nil},
		expect{hooks.RelationJoined, "u/0", 7},
		expect{hooks.RelationChanged, "u/0", 7},
	), fullTest(
		"Nothing happens if a unit departs before its joined is run.",
		send{msi{"u/0": 0}, nil},
		send{msi{"u/0": 7}, nil},
		send{nil, []string{"u/0"}},
	), fullTest(
		"A changed is run after a joined, even if a departed is known.",
		send{msi{"u/0": 0}, nil},
		expect{hooks.RelationJoined, "u/0", 0},
		send{nil, []string{"u/0"}},
		expect{hooks.RelationChanged, "u/0", 0},
		expect{hooks.RelationDeparted, "u/0", 0},
	), fullTest(
		"A departed replaces a changed.",
		send{msi{"u/0": 0}, nil},
		advance{2},
		send{msi{"u/0": 7}, nil},
		send{nil, []string{"u/0"}},
		expect{hooks.RelationDeparted, "u/0", 7},
	), fullTest(
		"Changed events are ignored if the version has not changed.",
		send{msi{"u/0": 0}, nil},
		advance{2},
		send{msi{"u/0": 0}, nil},
	), fullTest(
		"Redundant changed events are elided.",
		send{msi{"u/0": 0}, nil},
		advance{2},
		send{msi{"u/0": 3}, nil},
		send{msi{"u/0": 7}, nil},
		send{msi{"u/0": 79}, nil},
		expect{hooks.RelationChanged, "u/0", 79},
	), fullTest(
		"Latest hooks are run in the original unit order.",
		send{msi{"u/0": 0, "u/1": 1}, nil},
		advance{4},
		send{msi{"u/0": 3}, nil},
		send{msi{"u/1": 7}, nil},
		send{nil, []string{"u/0"}},
		expect{hooks.RelationDeparted, "u/0", 3},
		expect{hooks.RelationChanged, "u/1", 7},
	), fullTest(
		"Test everything we can think of at the same time.",
		send{msi{"u/0": 0, "u/1": 0, "u/2": 0, "u/3": 0, "u/4": 0}, nil},
		advance{6},
		// u/0, u/1, u/2 are now up to date; u/3, u/4 are untouched.
		send{msi{"u/0": 1}, nil},
		send{msi{"u/1": 1, "u/2": 1, "u/3": 1, "u/5": 0}, []string{"u/0", "u/4"}},
		send{msi{"u/3": 2}, nil},
		// - Finish off the rest of the initial state, ignoring u/4, but using
		// the latest known settings.
		expect{hooks.RelationJoined, "u/3", 2},
		expect{hooks.RelationChanged, "u/3", 2},
		// - u/0 was queued for change by the first RUC, but this change is
		// no longer relevant; it's departed in the second RUC, so we run
		// that hook instead.
		expect{hooks.RelationDeparted, "u/0", 1},
		// - Handle the remaining changes in the second RUC, still ignoring u/4.
		// We do run new changed hooks for u/1 and u/2, because the latest settings
		// are newer than those used in their original changed events.
		expect{hooks.RelationChanged, "u/1", 1},
		expect{hooks.RelationChanged, "u/2", 1},
		expect{hooks.RelationJoined, "u/5", 0},
		expect{hooks.RelationChanged, "u/5", 0},
		// - Ignore the third RUC, because the original joined/changed on u/3
		// was executed after we got the latest settings version.
	), reconcileTest(
		"Check that matching settings versions cause no changes.",
		msi{"u/0": 0}, "",
		send{msi{"u/0": 0}, nil},
	), reconcileTest(
		"Check that new settings versions cause appropriate changes.",
		msi{"u/0": 0}, "",
		send{msi{"u/0": 1}, nil},
		expect{hooks.RelationChanged, "u/0", 1},
	), reconcileTest(
		"Check that a just-joined unit gets its changed hook run first.",
		msi{"u/0": 0}, "u/0",
		send{msi{"u/0": 0}, nil},
		expect{hooks.RelationChanged, "u/0", 0},
	), reconcileTest(
		"Check that missing units are queued for depart as early as possible.",
		msi{"u/0": 0}, "",
		send{msi{"u/1": 0}, nil},
		expect{hooks.RelationDeparted, "u/0", 0},
		expect{hooks.RelationJoined, "u/1", 0},
		expect{hooks.RelationChanged, "u/1", 0},
	), reconcileTest(
		"Double-check that a pending changed happens before an injected departed.",
		msi{"u/0": 0}, "u/0",
		send{nil, nil},
		expect{hooks.RelationChanged, "u/0", 0},
		expect{hooks.RelationDeparted, "u/0", 0},
	), reconcileTest(
		"Check that missing units don't slip in front of required changed hooks.",
		msi{"u/0": 0}, "u/0",
		send{msi{"u/1": 0}, nil},
		expect{hooks.RelationChanged, "u/0", 0},
		expect{hooks.RelationDeparted, "u/0", 0},
		expect{hooks.RelationJoined, "u/1", 0},
		expect{hooks.RelationChanged, "u/1", 0},
	),
}

func (s *HookQueueSuite) TestAliveHookQueue(c *gc.C) {
	for i, t := range aliveHookQueueTests {
		c.Logf("test %d: %s", i, t.summary)
		out := make(chan hook.Info)
		in := make(chan params.RelationUnitsChange)
		ruw := &RUW{in, false}
		q := relation.NewAliveHookQueue(t.initial, out, ruw)
		for i, step := range t.steps {
			c.Logf("  step %d", i)
			step.check(c, in, out)
		}
		expect{}.check(c, in, out)
		q.Stop()
		c.Assert(ruw.stopped, gc.Equals, true)
	}
}

var dyingHookQueueTests = []hookQueueTest{
	fullTest(
		"Empty state just gets a broken hook.",
		expect{hook: hooks.RelationBroken},
	), reconcileTest(
		"Each current member is departed before broken is sent.",
		msi{"u/1": 7, "u/4": 33}, "",
		expect{hooks.RelationDeparted, "u/1", 7},
		expect{hooks.RelationDeparted, "u/4", 33},
		expect{hook: hooks.RelationBroken},
	), reconcileTest(
		"If there's a pending changed, that must still be respected.",
		msi{"u/0": 3}, "u/0",
		expect{hooks.RelationChanged, "u/0", 3},
		expect{hooks.RelationDeparted, "u/0", 3},
		expect{hook: hooks.RelationBroken},
	),
}

func (s *HookQueueSuite) TestDyingHookQueue(c *gc.C) {
	for i, t := range dyingHookQueueTests {
		c.Logf("test %d: %s", i, t.summary)
		out := make(chan hook.Info)
		q := relation.NewDyingHookQueue(t.initial, out)
		for i, step := range t.steps {
			c.Logf("  step %d", i)
			step.check(c, nil, out)
		}
		expect{}.check(c, nil, out)
		q.Stop()
	}
}

// RUW exists entirely to send RelationUnitsChanged events to a tested
// HookQueue in a synchronous and predictable fashion.
type RUW struct {
	in      chan params.RelationUnitsChange
	stopped bool
}

func (w *RUW) Changes() <-chan params.RelationUnitsChange {
	return w.in
}

func (w *RUW) Stop() error {
	close(w.in)
	w.stopped = true
	return nil
}

func (w *RUW) Err() error {
	return nil
}

type checker interface {
	check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info)
	checkDirect(c *gc.C, q relation.HookSource)
}

type send struct {
	changed  msi
	departed []string
}

func (d send) event() params.RelationUnitsChange {
	ruc := params.RelationUnitsChange{Changed: map[string]params.UnitSettings{}}
	for name, version := range d.changed {
		ruc.Changed[name] = params.UnitSettings{Version: version}
	}
	for _, name := range d.departed {
		ruc.Departed = append(ruc.Departed, name)
	}
	return ruc
}

func (d send) check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) {
	in <- d.event()
}

func (d send) checkDirect(c *gc.C, q relation.HookSource) {
	q.Update(d.event())
}

type advance struct {
	count int
}

func (d advance) check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) {
	for i := 0; i < d.count; i++ {
		select {
		case <-out:
		case <-time.After(coretesting.LongWait):
			c.Fatalf("timed out waiting for event %d", i)
		}
	}
}

func (d advance) checkDirect(c *gc.C, q relation.HookSource) {
	for i := 0; i < d.count; i++ {
		q.Pop()
	}
}

type expect struct {
	hook    hooks.Kind
	unit    string
	version int64
}

func (d expect) info() hook.Info {
	return hook.Info{
		Kind:          d.hook,
		RelationId:    21345,
		RemoteUnit:    d.unit,
		ChangeVersion: d.version,
	}
}

func (d expect) check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) {
	if d.hook == "" {
		select {
		case unexpected := <-out:
			c.Fatalf("got %#v", unexpected)
		case <-time.After(coretesting.ShortWait):
		}
		return
	}
	select {
	case actual := <-out:
		c.Assert(actual, gc.DeepEquals, d.info())
	case <-time.After(coretesting.LongWait):
		c.Fatalf("timed out waiting for %#v", d.info())
	}
}

func (d expect) checkDirect(c *gc.C, q relation.HookSource) {
	if d.hook == "" {
		c.Check(q.Empty(), jc.IsTrue)
	} else {
		c.Check(q.Empty(), jc.IsFalse)
		c.Check(q.Next(), gc.DeepEquals, d.info())
		q.Pop()
	}
}
<commit_msg>test less panicky on failure<commit_after>// Copyright 2012-2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package relation_test

import (
	"time"

	jc "github.com/juju/testing/checkers"
	"gopkg.in/juju/charm.v3/hooks"
	gc "launchpad.net/gocheck"

	"github.com/juju/juju/apiserver/params"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker/uniter/hook"
	"github.com/juju/juju/worker/uniter/relation"
)

type HookQueueSuite struct{}

var _ = gc.Suite(&HookQueueSuite{})

type msi map[string]int64

type hookQueueTest struct {
	summary string
	initial *relation.State
	steps   []checker
}

func fullTest(summary string, steps ...checker) hookQueueTest {
	return hookQueueTest{summary, &relation.State{21345, nil, ""}, steps}
}

func reconcileTest(summary string, members msi, joined string, steps ...checker) hookQueueTest {
	return hookQueueTest{summary, &relation.State{21345, members, joined}, steps}
}

var aliveHookQueueTests = []hookQueueTest{
	fullTest(
		"Empty initial change causes no hooks.",
		send{nil, nil},
	), fullTest(
		"Joined and changed are both run when unit is first detected.",
		send{msi{"u/0": 0}, nil},
		expect{hooks.RelationJoined, "u/0", 0},
		expect{hooks.RelationChanged, "u/0", 0},
	), fullTest(
		"Automatic changed is run with latest settings.",
		send{msi{"u/0": 0}, nil},
		expect{hooks.RelationJoined, "u/0", 0},
		send{msi{"u/0": 7}, nil},
		expect{hooks.RelationChanged, "u/0", 7},
	), fullTest(
		"Joined is also run with latest settings.",
		send{msi{"u/0": 0}, nil},
		send{msi{"u/0": 7}, nil},
		expect{hooks.RelationJoined, "u/0", 7},
		expect{hooks.RelationChanged, "u/0", 7},
	), fullTest(
		"Nothing happens if a unit departs before its joined is run.",
		send{msi{"u/0": 0}, nil},
		send{msi{"u/0": 7}, nil},
		send{nil, []string{"u/0"}},
	), fullTest(
		"A changed is run after a joined, even if a departed is known.",
		send{msi{"u/0": 0}, nil},
		expect{hooks.RelationJoined, "u/0", 0},
		send{nil, []string{"u/0"}},
		expect{hooks.RelationChanged, "u/0", 0},
		expect{hooks.RelationDeparted, "u/0", 0},
	), fullTest(
		"A departed replaces a changed.",
		send{msi{"u/0": 0}, nil},
		advance{2},
		send{msi{"u/0": 7}, nil},
		send{nil, []string{"u/0"}},
		expect{hooks.RelationDeparted, "u/0", 7},
	), fullTest(
		"Changed events are ignored if the version has not changed.",
		send{msi{"u/0": 0}, nil},
		advance{2},
		send{msi{"u/0": 0}, nil},
	), fullTest(
		"Redundant changed events are elided.",
		send{msi{"u/0": 0}, nil},
		advance{2},
		send{msi{"u/0": 3}, nil},
		send{msi{"u/0": 7}, nil},
		send{msi{"u/0": 79}, nil},
		expect{hooks.RelationChanged, "u/0", 79},
	), fullTest(
		"Latest hooks are run in the original unit order.",
		send{msi{"u/0": 0, "u/1": 1}, nil},
		advance{4},
		send{msi{"u/0": 3}, nil},
		send{msi{"u/1": 7}, nil},
		send{nil, []string{"u/0"}},
		expect{hooks.RelationDeparted, "u/0", 3},
		expect{hooks.RelationChanged, "u/1", 7},
	), fullTest(
		"Test everything we can think of at the same time.",
		send{msi{"u/0": 0, "u/1": 0, "u/2": 0, "u/3": 0, "u/4": 0}, nil},
		advance{6},
		// u/0, u/1, u/2 are now up to date; u/3, u/4 are untouched.
		send{msi{"u/0": 1}, nil},
		send{msi{"u/1": 1, "u/2": 1, "u/3": 1, "u/5": 0}, []string{"u/0", "u/4"}},
		send{msi{"u/3": 2}, nil},
		// - Finish off the rest of the initial state, ignoring u/4, but using
		// the latest known settings.
		expect{hooks.RelationJoined, "u/3", 2},
		expect{hooks.RelationChanged, "u/3", 2},
		// - u/0 was queued for change by the first RUC, but this change is
		// no longer relevant; it's departed in the second RUC, so we run
		// that hook instead.
		expect{hooks.RelationDeparted, "u/0", 1},
		// - Handle the remaining changes in the second RUC, still ignoring u/4.
		// We do run new changed hooks for u/1 and u/2, because the latest settings
		// are newer than those used in their original changed events.
		expect{hooks.RelationChanged, "u/1", 1},
		expect{hooks.RelationChanged, "u/2", 1},
		expect{hooks.RelationJoined, "u/5", 0},
		expect{hooks.RelationChanged, "u/5", 0},
		// - Ignore the third RUC, because the original joined/changed on u/3
		// was executed after we got the latest settings version.
	), reconcileTest(
		"Check that matching settings versions cause no changes.",
		msi{"u/0": 0}, "",
		send{msi{"u/0": 0}, nil},
	), reconcileTest(
		"Check that new settings versions cause appropriate changes.",
		msi{"u/0": 0}, "",
		send{msi{"u/0": 1}, nil},
		expect{hooks.RelationChanged, "u/0", 1},
	), reconcileTest(
		"Check that a just-joined unit gets its changed hook run first.",
		msi{"u/0": 0}, "u/0",
		send{msi{"u/0": 0}, nil},
		expect{hooks.RelationChanged, "u/0", 0},
	), reconcileTest(
		"Check that missing units are queued for depart as early as possible.",
		msi{"u/0": 0}, "",
		send{msi{"u/1": 0}, nil},
		expect{hooks.RelationDeparted, "u/0", 0},
		expect{hooks.RelationJoined, "u/1", 0},
		expect{hooks.RelationChanged, "u/1", 0},
	), reconcileTest(
		"Double-check that a pending changed happens before an injected departed.",
		msi{"u/0": 0}, "u/0",
		send{nil, nil},
		expect{hooks.RelationChanged, "u/0", 0},
		expect{hooks.RelationDeparted, "u/0", 0},
	), reconcileTest(
		"Check that missing units don't slip in front of required changed hooks.",
		msi{"u/0": 0}, "u/0",
		send{msi{"u/1": 0}, nil},
		expect{hooks.RelationChanged, "u/0", 0},
		expect{hooks.RelationDeparted, "u/0", 0},
		expect{hooks.RelationJoined, "u/1", 0},
		expect{hooks.RelationChanged, "u/1", 0},
	),
}

func (s *HookQueueSuite) TestAliveHookQueue(c *gc.C) {
	for i, t := range aliveHookQueueTests {
		c.Logf("test %d: %s", i, t.summary)
		out := make(chan hook.Info)
		in := make(chan params.RelationUnitsChange)
		ruw := &RUW{in, false}
		q := relation.NewAliveHookQueue(t.initial, out, ruw)
		for i, step := range t.steps {
			c.Logf("  step %d", i)
			step.check(c, in, out)
		}
		expect{}.check(c, in, out)
		q.Stop()
		c.Assert(ruw.stopped, gc.Equals, true)
	}
}

var dyingHookQueueTests = []hookQueueTest{
	fullTest(
		"Empty state just gets a broken hook.",
		expect{hook: hooks.RelationBroken},
	), reconcileTest(
		"Each current member is departed before broken is sent.",
		msi{"u/1": 7, "u/4": 33}, "",
		expect{hooks.RelationDeparted, "u/1", 7},
		expect{hooks.RelationDeparted, "u/4", 33},
		expect{hook: hooks.RelationBroken},
	), reconcileTest(
		"If there's a pending changed, that must still be respected.",
		msi{"u/0": 3}, "u/0",
		expect{hooks.RelationChanged, "u/0", 3},
		expect{hooks.RelationDeparted, "u/0", 3},
		expect{hook: hooks.RelationBroken},
	),
}

func (s *HookQueueSuite) TestDyingHookQueue(c *gc.C) {
	for i, t := range dyingHookQueueTests {
		c.Logf("test %d: %s", i, t.summary)
		out := make(chan hook.Info)
		q := relation.NewDyingHookQueue(t.initial, out)
		for i, step := range t.steps {
			c.Logf("  step %d", i)
			step.check(c, nil, out)
		}
		expect{}.check(c, nil, out)
		q.Stop()
	}
}

// RUW exists entirely to send RelationUnitsChanged events to a tested
// HookQueue in a synchronous and predictable fashion.
type RUW struct {
	in      chan params.RelationUnitsChange
	stopped bool
}

func (w *RUW) Changes() <-chan params.RelationUnitsChange {
	return w.in
}

func (w *RUW) Stop() error {
	close(w.in)
	w.stopped = true
	return nil
}

func (w *RUW) Err() error {
	return nil
}

type checker interface {
	check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info)
	checkDirect(c *gc.C, q relation.HookSource)
}

type send struct {
	changed  msi
	departed []string
}

func (d send) event() params.RelationUnitsChange {
	ruc := params.RelationUnitsChange{Changed: map[string]params.UnitSettings{}}
	for name, version := range d.changed {
		ruc.Changed[name] = params.UnitSettings{Version: version}
	}
	for _, name := range d.departed {
		ruc.Departed = append(ruc.Departed, name)
	}
	return ruc
}

func (d send) check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) {
	in <- d.event()
}

func (d send) checkDirect(c *gc.C, q relation.HookSource) {
	q.Update(d.event())
}

type advance struct {
	count int
}

func (d advance) check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) {
	for i := 0; i < d.count; i++ {
		select {
		case <-out:
		case <-time.After(coretesting.LongWait):
			c.Fatalf("timed out waiting for event %d", i)
		}
	}
}

func (d advance) checkDirect(c *gc.C, q relation.HookSource) {
	for i := 0; i < d.count; i++ {
		c.Assert(q.Empty(), jc.IsFalse)
		q.Pop()
	}
}

type expect struct {
	hook    hooks.Kind
	unit    string
	version int64
}

func (d expect) info() hook.Info {
	return hook.Info{
		Kind:          d.hook,
		RelationId:    21345,
		RemoteUnit:    d.unit,
		ChangeVersion: d.version,
	}
}

func (d expect) check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) {
	if d.hook == "" {
		select {
		case unexpected := <-out:
			c.Fatalf("got %#v", unexpected)
		case <-time.After(coretesting.ShortWait):
		}
		return
	}
	select {
	case actual := <-out:
		c.Assert(actual, gc.DeepEquals, d.info())
	case <-time.After(coretesting.LongWait):
		c.Fatalf("timed out waiting for %#v", d.info())
	}
}

func (d expect) checkDirect(c *gc.C, q relation.HookSource) {
	if d.hook == "" {
		c.Check(q.Empty(), jc.IsTrue)
	} else {
		c.Check(q.Empty(), jc.IsFalse)
		c.Check(q.Next(), gc.DeepEquals, d.info())
		q.Pop()
	}
}
<|endoftext|>


<commit_before>package cli

import (
  "fmt"
  "bufio"
  "os"
  "io"
  "strings"
)

type Cli struct {
  // List of commands
  Commands []Command

  // The action to execute
	Action func(context *Context)

  Writer io.Writer
}

func newCli() *Cli {
	return &Cli{
    Writer: os.Stdout,
  }
}

func (c *Cli) parseInput(str string) {
  str = strings.TrimSpace(str)
  context := NewContext(c)

  cm := c.Command(str)
  cm.Action(context)
}

func Listen() {
  var _cli = newCli()
  _cli.Commands = []Command{
    {
      Name: "exit",
      Action: func(c *Context) {
        os.Exit(1)
      },
    },
  }

  for {
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("> ")
    text, _ := reader.ReadString('\n')

    _cli.parseInput(text)
  }
}

func (c *Cli) Command(name string) *Command {
	for _, cm := range c.Commands {
		if cm.HasName(name) {
			return &cm
		}
	}

	return nil
}
<commit_msg>Minimal fix<commit_after>package cli

import (
  "fmt"
  "bufio"
  "os"
  "io"
  "strings"
)

type Cli struct {
  // List of commands
  Commands []Command

  // The action to execute
	Action func(context *Context)

  Writer io.Writer
}

func newCli() *Cli {
	return &Cli{
    Writer: os.Stdout,
  }
}

func (c *Cli) parseInput(str string) {
  str = strings.TrimSpace(str)
  context := NewContext(c)

  cm := c.Command(str)

  if cm != nil {
    cm.Action(context)
  }
}

func Listen() {
  var _cli = newCli()
  _cli.Commands = []Command{
    {
      Name: "exit",
      Action: func(c *Context) {
        os.Exit(1)
      },
    },
  }

  for {
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("> ")
    text, _ := reader.ReadString('\n')

    _cli.parseInput(text)
  }
}

func (c *Cli) Command(name string) *Command {
	for _, cm := range c.Commands {
		if cm.HasName(name) {
			return &cm
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"fmt"
	"net"
	"time"

	"github.com/vtolstov/gopacket"
	"github.com/vtolstov/gopacket/layers"
	"golang.org/x/net/ipv4"
)

func cidr2bcast(cidr string) string {
	var a uint32
	var m uint32
	var s string
	ip, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return s
	}
	a |= uint32(ip.To4()[0])
	a |= uint32(ip.To4()[1]) << 8
	a |= uint32(ip.To4()[2]) << 16
	a |= uint32(ip.To4()[3]) << 24
	m |= uint32(net.IP(ipnet.Mask).To4()[0])
	m |= uint32(net.IP(ipnet.Mask).To4()[1]) << 8
	m |= uint32(net.IP(ipnet.Mask).To4()[2]) << 16
	m |= uint32(net.IP(ipnet.Mask).To4()[3]) << 24
	b := a | ^m
	s = fmt.Sprintf("%d.%d.%d.%d", byte(b), byte(b>>8), byte(b>>16), byte(b>>24))
	return s
}

func (s *Server) ListenAndServeUDPv4() {
	ipAddr := &net.IPAddr{IP: net.IPv4zero}
	conn, err := net.ListenIP("ip4:udp", ipAddr)
	if err != nil {
		l.Info(err.Error())
		return
	}
	defer conn.Close()
	if err = bindToDevice(conn, "tap"+s.name); err != nil {
		l.Info(err.Error())
		return
	}

	s.ipv4conn, err = ipv4.NewRawConn(conn)
	if err != nil {
		l.Info(err.Error())
		return
	}

	if err = s.ipv4conn.SetControlMessage(ipv4.FlagDst, true); err != nil {
		l.Warning(err.Error())
		return
	}

	buffer := make([]byte, 1500)

	var gw net.IP
	for _, addr := range s.metadata.Network.IP {
		if addr.Family == "ipv4" && addr.Host == "true" && addr.Gateway == "true" {
			gw = net.ParseIP(addr.Address)
		}
	}
	iface, err := net.InterfaceByName("tap" + s.name)
	if err != nil {
		l.Info(fmt.Sprintf("failed to get iface: %s", err.Error()))
		return
	}

	_ = gw
	_ = iface
	for {
		//		s.RLock()
		if s.shutdown {
			//		s.RUnlock()
			return
		}
		//s.RUnlock()

		s.ipv4conn.SetReadDeadline(time.Now().Add(time.Second))

		hdr, _, _, err := s.ipv4conn.ReadFrom(buffer)

		if err != nil {
			switch v := err.(type) {
			case *net.OpError:
				if v.Timeout() {
					continue
				}
			case *net.AddrError:
				if v.Timeout() {
					continue
				}
			case *net.UnknownNetworkError:
				if v.Timeout() {
					continue
				}
			default:
				l.Warning(err.Error())
				return
			}
		}
		var ip4 layers.IPv4
		var udp layers.UDP
		var dhcp4req layers.DHCPv4
		parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip4, &udp, &dhcp4req)
		decoded := []gopacket.LayerType{}
		err = parser.DecodeLayers(buffer, &decoded)
		for _, layerType := range decoded {
			switch layerType {
			/*
				case layers.LayerTypeIPv4:
					fmt.Printf("IP4: %+v\n", ip4)
				case layers.LayerTypeUDP:
					fmt.Printf("UDP: %+v\n", udp)
			*/
			case layers.LayerTypeDHCPv4:
				//			fmt.Printf("DHCP4: %+v\n", dhcp4req)
				if dhcp4req.Operation == layers.DHCP_MSG_REQ {

					dhcp4res, err := s.ServeUDPv4(&dhcp4req)
					if err != nil {
						l.Warning(err.Error())
						continue
					}
					if dhcp4res == nil {
						// ignore empty dhcp packets
						continue
					}

					buf := gopacket.NewSerializeBuffer()
					opts := gopacket.SerializeOptions{true, true}
					gopacket.SerializeLayers(buf, opts,
						&layers.UDP{SrcPort: 67, DstPort: 68},
						dhcp4res)

					wcm := ipv4.ControlMessage{TTL: 255}
					wcm.Dst = net.IPv4bcast.To4()
					wcm.Src = gw.To4()
					wcm.IfIndex = iface.Index
					err = s.ipv4conn.WriteTo(&ipv4.Header{Len: 20, TOS: hdr.TOS, TotalLen: 20 + int(len(buf.Bytes())), FragOff: 0, TTL: 255, Protocol: int(layers.IPProtocolUDP), Src: gw.To4(), Dst: net.IPv4bcast.To4()}, buf.Bytes(), &wcm)
					if err != nil {
						l.Warning(err.Error())
						continue
					}
				} else {
					//l.Warning("unknown dhcp operation %#+v\n", dhcp4req)
					// ignore response packets
					continue
				}
			}
		}
	}
}

func (s *Server) ServeUDPv4(dhcpreq *layers.DHCPv4) (*layers.DHCPv4, error) {
	dhcpres := &layers.DHCPv4{}

	l.Info(fmt.Sprintf("%s dhcpv4 req: %+v\n", s.name, dhcpreq))

	leaseTime := 6000
	var ip net.IP
	var gw net.IP
	var mac net.HardwareAddr
	var ipnet *net.IPNet
	var err error
	var cidr string
	mac = dhcpreq.ClientHWAddr

	if s.metadata == nil {
		return nil, fmt.Errorf("err: metadata is nil")
	}

	for _, addr := range s.metadata.Network.IP {
		if addr.Family == "ipv4" && addr.Host == "false" {
			cidr = addr.Address + "/" + addr.Prefix
			ip, ipnet, err = net.ParseCIDR(cidr)
			if err != nil {
				return nil, err
			}
		}
		if addr.Family == "ipv4" && addr.Host == "true" && addr.Gateway == "true" && ipnet != nil && ipnet.Contains(net.ParseIP(addr.Address)) {
			gw = net.ParseIP(addr.Address)
			break
		}

	}
	if ipnet == nil || ipnet.Mask == nil || gw == nil || ip == nil {
		return nil, fmt.Errorf("failed to get ipv4 info")
	}

	opt := dhcpreq.Options[0]
	switch opt.Type {
	case layers.DHCP_OPT_MESSAGE_TYPE:
		switch layers.DHCPOperation(opt.Data[0]) {
		case layers.DHCPOperation(layers.DHCP_MSG_DISCOVER):
			dhcpres, err = layers.NewDHCPOffer(dhcpreq.Xid)
			if err != nil {
				return nil, err
			}
			copy(dhcpres.ClientHWAddr, mac[:dhcpres.HardwareLen])
			copy(dhcpres.YourIP, ip.To4())
			copy(dhcpres.ServerIP, gw.To4())
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(1, []byte(net.IP(ipnet.Mask).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(3, []byte(gw.To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(5, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(6, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(28, []byte(net.ParseIP(cidr2bcast(cidr)).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(15, []byte(s.metadata.Network.DomainName)))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(12, []byte(s.name)))
			var b [8]byte
			var bs []byte
			bs = b[:4]
			binary.BigEndian.PutUint32(bs, uint32(leaseTime))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(51, bs))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(54, []byte(gw.To4())))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*50))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T1, bs))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*88))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T2, bs))
			bs = b[:2]
			binary.BigEndian.PutUint16(bs, uint16(1500))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_INTERFACE_MTU, bs))
		case layers.DHCPOperation(layers.DHCP_MSG_REQUEST):
			dhcpres, err = layers.NewDHCPAck(dhcpreq.Xid)
			if err != nil {
				return nil, err
			}
			copy(dhcpres.ClientHWAddr, mac[:dhcpres.HardwareLen])
			copy(dhcpres.YourIP, ip.To4())
			copy(dhcpres.ServerIP, gw.To4())
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(1, []byte(net.IP(ipnet.Mask).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(3, []byte(gw.To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(5, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(6, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(28, []byte(net.ParseIP(cidr2bcast(cidr)).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(15, []byte(s.metadata.Network.DomainName)))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(12, []byte(s.name)))
			var b [8]byte
			var bs []byte
			bs = b[:4]
			binary.BigEndian.PutUint32(bs, uint32(leaseTime))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(51, bs))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(54, []byte(gw.To4())))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*50))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T1, bs))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*88))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T2, bs))
			bs = b[:2]
			binary.BigEndian.PutUint16(bs, uint16(1500))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_INTERFACE_MTU, bs))
		case layers.DHCPOperation(layers.DHCP_MSG_OFFER), layers.DHCPOperation(layers.DHCP_MSG_ACK):
			return nil, nil
		default:
			return nil, fmt.Errorf("unk dhcp msg: %d\n", layers.DHCPOperation(opt.Data[0]))
		}
	}
	l.Info(fmt.Sprintf("%s dhcpv4 res: %+v\n", s.name, dhcpres))
	return dhcpres, nil
}
<commit_msg>now catch own packets<commit_after>package main

import (
	"encoding/binary"
	"fmt"
	"net"
	"time"

	"github.com/vtolstov/gopacket"
	"github.com/vtolstov/gopacket/layers"
	"golang.org/x/net/ipv4"
)

func cidr2bcast(cidr string) string {
	var a uint32
	var m uint32
	var s string
	ip, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return s
	}
	a |= uint32(ip.To4()[0])
	a |= uint32(ip.To4()[1]) << 8
	a |= uint32(ip.To4()[2]) << 16
	a |= uint32(ip.To4()[3]) << 24
	m |= uint32(net.IP(ipnet.Mask).To4()[0])
	m |= uint32(net.IP(ipnet.Mask).To4()[1]) << 8
	m |= uint32(net.IP(ipnet.Mask).To4()[2]) << 16
	m |= uint32(net.IP(ipnet.Mask).To4()[3]) << 24
	b := a | ^m
	s = fmt.Sprintf("%d.%d.%d.%d", byte(b), byte(b>>8), byte(b>>16), byte(b>>24))
	return s
}

func (s *Server) ListenAndServeUDPv4() {
	ipAddr := &net.IPAddr{IP: net.IPv4zero}
	conn, err := net.ListenIP("ip4:udp", ipAddr)
	if err != nil {
		l.Info(err.Error())
		return
	}
	defer conn.Close()
	if err = bindToDevice(conn, "tap"+s.name); err != nil {
		l.Info(err.Error())
		return
	}

	s.ipv4conn, err = ipv4.NewRawConn(conn)
	if err != nil {
		l.Info(err.Error())
		return
	}

	if err = s.ipv4conn.SetControlMessage(ipv4.FlagDst, true); err != nil {
		l.Warning(err.Error())
		return
	}

	buffer := make([]byte, 1500)

	var gw net.IP
	for _, addr := range s.metadata.Network.IP {
		if addr.Family == "ipv4" && addr.Host == "true" && addr.Gateway == "true" {
			gw = net.ParseIP(addr.Address)
		}
	}
	iface, err := net.InterfaceByName("tap" + s.name)
	if err != nil {
		l.Info(fmt.Sprintf("failed to get iface: %s", err.Error()))
		return
	}

	_ = gw
	_ = iface
	for {
		//		s.RLock()
		if s.shutdown {
			//		s.RUnlock()
			return
		}
		//s.RUnlock()

		s.ipv4conn.SetReadDeadline(time.Now().Add(time.Second))

		hdr, _, _, err := s.ipv4conn.ReadFrom(buffer)

		if err != nil {
			switch v := err.(type) {
			case *net.OpError:
				if v.Timeout() {
					continue
				}
			case *net.AddrError:
				if v.Timeout() {
					continue
				}
			case *net.UnknownNetworkError:
				if v.Timeout() {
					continue
				}
			default:
				l.Warning(err.Error())
				return
			}
		}
		var ip4 layers.IPv4
		var udp layers.UDP
		var dhcp4req layers.DHCPv4
		parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip4, &udp, &dhcp4req)
		decoded := []gopacket.LayerType{}
		err = parser.DecodeLayers(buffer, &decoded)
		for _, layerType := range decoded {
			switch layerType {
			/*
				case layers.LayerTypeIPv4:
					fmt.Printf("IP4: %+v\n", ip4)
				case layers.LayerTypeUDP:
					fmt.Printf("UDP: %+v\n", udp)
			*/
			case layers.LayerTypeDHCPv4:
				//			fmt.Printf("DHCP4: %+v\n", dhcp4req)
				if dhcp4req.Operation == layers.DHCP_MSG_REQ {

					dhcp4res, err := s.ServeUDPv4(&dhcp4req)
					if err != nil {
						l.Warning(err.Error())
						continue
					}
					if dhcp4res == nil {
						// ignore empty dhcp packets
						continue
					}

					buf := gopacket.NewSerializeBuffer()
					opts := gopacket.SerializeOptions{true, true}
					gopacket.SerializeLayers(buf, opts,
						&layers.UDP{SrcPort: 67, DstPort: 68},
						dhcp4res)

					wcm := ipv4.ControlMessage{TTL: 255}
					wcm.Dst = net.IPv4bcast.To4()
					wcm.Src = gw.To4()
					wcm.IfIndex = iface.Index
					err = s.ipv4conn.WriteTo(&ipv4.Header{Len: 20, TOS: hdr.TOS, TotalLen: 20 + int(len(buf.Bytes())), FragOff: 0, TTL: 255, Protocol: int(layers.IPProtocolUDP), Src: gw.To4(), Dst: net.IPv4bcast.To4()}, buf.Bytes(), &wcm)
					if err != nil {
						l.Warning(err.Error())
						continue
					}
				} else {
					//l.Warning("unknown dhcp operation %#+v\n", dhcp4req)
					// ignore response packets
					continue
				}
			}
		}
	}
}

func (s *Server) ServeUDPv4(dhcpreq *layers.DHCPv4) (*layers.DHCPv4, error) {
	dhcpres := &layers.DHCPv4{}

	l.Info(fmt.Sprintf("%s dhcpv4 req: %+v\n", s.name, dhcpreq))

	leaseTime := 6000
	var ip net.IP
	var gw net.IP
	var mac net.HardwareAddr
	var ipnet *net.IPNet
	var err error
	var cidr string
	mac = dhcpreq.ClientHWAddr

	if s.metadata == nil {
		return nil, fmt.Errorf("err: metadata is nil")
	}

	for _, addr := range s.metadata.Network.IP {
		if addr.Family == "ipv4" && addr.Host == "false" {
			cidr = addr.Address + "/" + addr.Prefix
			ip, ipnet, err = net.ParseCIDR(cidr)
			if err != nil {
				return nil, err
			}
		}
		if addr.Family == "ipv4" && addr.Host == "true" && addr.Gateway == "true" && ipnet != nil && ipnet.Contains(net.ParseIP(addr.Address)) {
			gw = net.ParseIP(addr.Address)
			break
		}

	}
	if ipnet == nil || ipnet.Mask == nil || gw == nil || ip == nil {
		return nil, fmt.Errorf("failed to get ipv4 info")
	}

	opt := dhcpreq.Options[0]
	switch opt.Type {
	case layers.DHCP_OPT_MESSAGE_TYPE:
		switch layers.DHCPOperation(opt.Data[0]) {
		case layers.DHCPOperation(layers.DHCP_MSG_DISCOVER):
			dhcpres, err = layers.NewDHCPOffer(dhcpreq.Xid)
			if err != nil {
				return nil, err
			}
			copy(dhcpres.ClientHWAddr, mac[:dhcpres.HardwareLen])
			copy(dhcpres.YourIP, ip.To4())
			copy(dhcpres.ServerIP, gw.To4())
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(1, []byte(net.IP(ipnet.Mask).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(3, []byte(gw.To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(5, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(6, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(28, []byte(net.ParseIP(cidr2bcast(cidr)).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(15, []byte(s.metadata.Network.DomainName)))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(12, []byte(s.name)))
			var b [8]byte
			var bs []byte
			bs = b[:4]
			binary.BigEndian.PutUint32(bs, uint32(leaseTime))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(51, bs))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(54, []byte(gw.To4())))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*50))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T1, bs))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*88))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T2, bs))
			bs = b[:2]
			binary.BigEndian.PutUint16(bs, uint16(1500))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_INTERFACE_MTU, bs))
		case layers.DHCPOperation(layers.DHCP_MSG_REQUEST):
			dhcpres, err = layers.NewDHCPAck(dhcpreq.Xid)
			if err != nil {
				return nil, err
			}
			copy(dhcpres.ClientHWAddr, mac[:dhcpres.HardwareLen])
			copy(dhcpres.YourIP, ip.To4())
			copy(dhcpres.ServerIP, gw.To4())
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(1, []byte(net.IP(ipnet.Mask).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(3, []byte(gw.To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(5, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(6, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(28, []byte(net.ParseIP(cidr2bcast(cidr)).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(15, []byte(s.metadata.Network.DomainName)))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(12, []byte(s.name)))
			var b [8]byte
			var bs []byte
			bs = b[:4]
			binary.BigEndian.PutUint32(bs, uint32(leaseTime))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(51, bs))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(54, []byte(gw.To4())))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*50))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T1, bs))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*88))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T2, bs))
			bs = b[:2]
			binary.BigEndian.PutUint16(bs, uint16(1500))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_INTERFACE_MTU, bs))
		case layers.DHCPOperation(layers.DHCP_MSG_ACK):
			return nil, nil
		default:
			return nil, fmt.Errorf("unk dhcp msg: %d\n", layers.DHCPOperation(opt.Data[0]))
		}
	}
	l.Info(fmt.Sprintf("%s dhcpv4 res: %+v\n", s.name, dhcpres))
	return dhcpres, nil
}
<|endoftext|>