diff --git a/pkg/agent/server/guest.go b/pkg/agent/server/guest.go index 11be19d5..ef55c02c 100644 --- a/pkg/agent/server/guest.go +++ b/pkg/agent/server/guest.go @@ -80,7 +80,7 @@ func (g *Guest) reloadDesc(ctx context.Context) error { if g.NeedsSync() { go func() { // desc change will be picked up by watcher - log.Infof("guest sync %s", g.Id) + log.Debugf("guest sync %s", g.Id) hc := g.watcher.hostConfig s := auth.GetAdminSession(ctx, hc.Region) _, err := mcclient_modules.Servers.PerformAction(s, g.Id, "sync", nil) @@ -245,15 +245,15 @@ func (g *Guest) clearOvn(ctx context.Context) { func (g *Guest) UpdateSettings(ctx context.Context, sync bool) { start := time.Now() err := g.refresh(ctx) - log.Infof("guest UpdateSettings refresh %f", time.Since(start).Seconds()) + log.Debugf("guest UpdateSettings refresh %f", time.Since(start).Seconds()) switch err { case nil: g.updateClassicFlows(ctx) - log.Infof("guest UpdateSettings updateClassicFlows %f", time.Since(start).Seconds()) + log.Debugf("guest UpdateSettings updateClassicFlows %f", time.Since(start).Seconds()) g.updateTc(ctx, sync) - log.Infof("guest UpdateSettings updateTc %f", time.Since(start).Seconds()) + log.Debugf("guest UpdateSettings updateTc %f", time.Since(start).Seconds()) g.updateOvn(ctx) - log.Infof("guest UpdateSettings updateOvn %f", time.Since(start).Seconds()) + log.Debugf("guest UpdateSettings updateOvn %f", time.Since(start).Seconds()) if g.HostId != "" { g.watcher.agent.HostId(g.HostId) } diff --git a/pkg/agent/server/tcman.go b/pkg/agent/server/tcman.go index 553e513b..54363e7b 100644 --- a/pkg/agent/server/tcman.go +++ b/pkg/agent/server/tcman.go @@ -19,7 +19,6 @@ import ( "fmt" "os" "os/exec" - "strings" "sync" "time" @@ -80,7 +79,7 @@ func NewTcMan() *TcMan { return &TcMan{ book: map[string]*TcManSection{}, tcCli: tc.NewTcCli().Details(true).Force(true), - cmdChan: make(chan *TcManCmd), + cmdChan: make(chan *TcManCmd, 1024), } } @@ -205,7 +204,7 @@ func (tm *TcMan) doCheckGuestIfbTcData(ctx context.Context, tcdata *utils.TcData expectTree := tcdata.GuestIfbQdiscTree() cmds := expectTree.Delta(qt, tcdata.IfbIfname()) if len(cmds) > 0 { - output, stderr, err := tm.tcCli.Batch(ctx, strings.Join(cmds, "\n")) + output, stderr, err := tm.tcCli.Batch(ctx, cmds) if err != nil { log.Errorf("tcman: batch failed: %s cmds: %s\n%s\nstderr:\n%s", err, cmds, output, stderr) return errors.Wrapf(err, "batch failed: %s cmds: %s\n%s\nstderr:\n%s", err, cmds, output, stderr) @@ -229,7 +228,7 @@ func (tm *TcMan) doCheckGuestTcData(ctx context.Context, tcdata *utils.TcData) e cmds := expectTree.Delta(qt, tcdata.Ifname) if len(cmds) > 0 { - output, stderr, err := tm.tcCli.Batch(ctx, strings.Join(cmds, "\n")) + output, stderr, err := tm.tcCli.Batch(ctx, cmds) if err != nil { log.Errorf("tcman: batch failed: %s cmds: %s\n%s\nstderr:\n%s", err, cmds, output, stderr) return errors.Wrapf(err, "batch failed: %s cmds: %s\n%s\nstderr:\n%s", err, cmds, output, stderr) @@ -269,9 +268,9 @@ func (tm *TcMan) doCheckHostTcData(ctx context.Context, tcdata *utils.TcData) { cmds := expectTree.Delta(qt, tcdata.Ifname) if len(cmds) > 0 { - output, stderr, err := tm.tcCli.Batch(ctx, strings.Join(cmds, "\n")) + output, stderr, err := tm.tcCli.Batch(ctx, cmds) if err != nil { - log.Errorf("tcman: batch failed: %s cnds: %s\n%s\nstderr:\n%s", err, cmds, output, stderr) + log.Errorf("tcman: batch failed: %s cmds: %s\n%s\nstderr:\n%s", err, cmds, output, stderr) for _, cmd := range cmds { log.Debugf("tcman: %s", cmd) } diff --git a/pkg/agent/server/watch.go b/pkg/agent/server/watch.go index fd228057..a90b3dc0 100644 --- a/pkg/agent/server/watch.go +++ b/pkg/agent/server/watch.go @@ -147,15 +147,15 @@ func (w *serversWatcher) scan(ctx context.Context) { id := fi.Name() if REGEX_UUID.MatchString(id) { guestStart := time.Now() - log.Infof("scan guest %s", id) + log.Debugf("scan guest %s", id) path := path.Join(serversPath, id) g, err := w.addGuestWatch(id, path) if err != nil { log.Errorf("inotify events watch guest failed during scan: %s: %s", path, err) } - log.Infof("end of scan guest %s addGuestWatch: %f", id, time.Since(guestStart).Seconds()) + log.Debugf("end of scan guest %s addGuestWatch: %f", id, time.Since(guestStart).Seconds()) g.UpdateSettings(ctx, false) - log.Infof("end of scan guest %s: %f", id, time.Since(guestStart).Seconds()) + log.Debugf("end of scan guest %s: %f", id, time.Since(guestStart).Seconds()) } } } @@ -186,9 +186,9 @@ func (w *serversWatcher) withWait(ctx context.Context, f func(context.Context)) ctx = context.WithValue(ctx, "waitData", waitData) start := time.Now() funcName := GetFunctionName(f) - log.Debugf("[serversWatcher] start wait %s context ....", funcName) + log.Debugf("serversWatcher.withWait start wait %s context ....", funcName) f(ctx) - log.Debugf("[serversWatcher] end wait %s context %f....", funcName, time.Since(start).Seconds()) + log.Debugf("serversWatcher.withWait end wait %s context %f....", funcName, time.Since(start).Seconds()) for _, wd := range waitData { wd.FlowMan.waitDecr(wd.Count) wd.FlowMan.SyncFlows(ctx) @@ -266,7 +266,6 @@ func (w *serversWatcher) Start(ctx context.Context, agent *AgentServer) { log.Errorf("fsnotity.watch.Events error") goto out } - log.Infof("receive inotify events!") wev := w.watchEvent(&ev) if wev == nil { log.Debugf("inotify events ignored: %s", ev) @@ -276,7 +275,7 @@ func (w *serversWatcher) Start(ctx context.Context, agent *AgentServer) { guestPath := wev.guestPath switch wev.evType { case watchEventTypeAddServerDir: - log.Infof("received guest path add event: %s", guestPath) + log.Debugf("received guest path add event: %s", guestPath) g, err := w.addGuestWatch(guestId, guestPath) if err != nil { log.Errorf("watch guest failed: %s: %s", guestPath, err) @@ -288,9 +287,7 @@ func (w *serversWatcher) Start(ctx context.Context, agent *AgentServer) { g.ClearSettings(ctx) delete(w.guests, guestId) } - log.Infof("guest path deleted: %s", guestPath) case watchEventTypeUpdServer: - log.Infof("watchEventTypeUpdServer %s", guestId) if g, ok := w.guests[guestId]; ok { g.UpdateSettings(ctx, true) } else { @@ -298,7 +295,6 @@ func (w *serversWatcher) Start(ctx context.Context, agent *AgentServer) { } case watchEventTypeDelServer: if g, ok := w.guests[guestId]; ok { - log.Infof("remove guest settings %s", guestId) g.ClearSettings(ctx) } else { log.Warningf("unexpected guest down event: %s", guestPath) @@ -306,7 +302,6 @@ func (w *serversWatcher) Start(ctx context.Context, agent *AgentServer) { } } case <-pendingChan: - log.Infof("watcher refresh pendings") w.withWait(ctx, func(ctx context.Context) { for _, g := range w.guests { if g.IsPending() { @@ -315,13 +310,12 @@ func (w *serversWatcher) Start(ctx context.Context, agent *AgentServer) { } }) case <-refreshTicker.C: - log.Infof("watcher refresh time ;)") w.withWait(ctx, func(ctx context.Context) { w.hostLocal.UpdateSettings(ctx, false) w.scan(ctx) - // for _, g := range w.guests { - // g.UpdateSettings(ctx) - // } + for _, g := range w.guests { + g.UpdateSettings(ctx, false) + } }) case err, ok := <-w.watcher.Errors: if !ok { diff --git a/pkg/agent/utils/cache_port_stats.go b/pkg/agent/utils/cache_port_stats.go index edc2b3af..b1e9bdee 100644 --- a/pkg/agent/utils/cache_port_stats.go +++ b/pkg/agent/utils/cache_port_stats.go @@ -5,6 +5,8 @@ import ( "time" "github.com/digitalocean/go-openvswitch/ovs" + + "yunion.io/x/log" ) const ( @@ -57,6 +59,7 @@ func (cache *PortStatsCache) DumpPort(bridge, port string) (*ovs.PortStats, erro if data, ok := cache.store[key]; ok && !data.staled() { ps := data.get() cache.rw.RUnlock() + log.Debugf("DumpPort %s, %s, %d from cache", bridge, port, ps.PortID) return ps, nil } cache.rw.RUnlock() @@ -65,6 +68,7 @@ func (cache *PortStatsCache) DumpPort(bridge, port string) (*ovs.PortStats, erro if err != nil { return ps, err } + log.Debugf("DumpPort %s, %s, %d from ovs", bridge, port, ps.PortID) cache.rw.Lock() defer cache.rw.Unlock() diff --git a/pkg/agent/utils/cls_md.go b/pkg/agent/utils/cls_md.go index 2a1bd3bf..ab06b3d9 100644 --- a/pkg/agent/utils/cls_md.go +++ b/pkg/agent/utils/cls_md.go @@ -121,8 +121,8 @@ type sClassicMetadataDescGetter struct { func (g *sClassicMetadataDescGetter) Get(ip string) *desc.SGuestDesc { start := time.Now() - log.Infof("Get guest desc by ip %s", ip) + log.Debugf("Get guest desc by ip %s", ip) guestDesc := g.watcher.FindGuestDescByHostLocalIp(g.hostLocal, ip) - log.Infof("Get guest desc by ip %s cost %f seconds", ip, time.Since(start).Seconds()) + log.Debugf("Get guest desc by ip %s cost %f seconds", ip, time.Since(start).Seconds()) return guestDesc } diff --git a/pkg/tc/class.go b/pkg/tc/class.go index 1c2b42a4..91e5e1bf 100644 --- a/pkg/tc/class.go +++ b/pkg/tc/class.go @@ -105,19 +105,19 @@ func (q *SBaseTcClass) Equals(qi IComparable) bool { return q.Compare(qi) == 0 } -func (cls *SBaseTcClass) AddLine(ifname string) string { +func (cls *SBaseTcClass) AddLine(ifname string) []string { elms := cls.basicLineElements("add", ifname) - return strings.Join(elms, " ") + return elms } -func (cls *SBaseTcClass) ReplaceLine(ifname string) string { +func (cls *SBaseTcClass) ReplaceLine(ifname string) []string { elms := cls.basicLineElements("replace", ifname) - return strings.Join(elms, " ") + return elms } -func (cls *SBaseTcClass) DeleteLine(ifname string) string { +func (cls *SBaseTcClass) DeleteLine(ifname string) []string { elms := cls.basicLineElements("delete", ifname) - return strings.Join(elms, " ") + return elms } func (cls *SBaseTcClass) Base() *SBaseTcClass { @@ -220,19 +220,19 @@ func (cls *SHtbClass) basicLineElements(action string, ifname string) []string { return elms } -func (cls *SHtbClass) AddLine(ifname string) string { +func (cls *SHtbClass) AddLine(ifname string) []string { elms := cls.basicLineElements("add", ifname) - return strings.Join(elms, " ") + return elms } -func (cls *SHtbClass) ReplaceLine(ifname string) string { +func (cls *SHtbClass) ReplaceLine(ifname string) []string { elms := cls.basicLineElements("replace", ifname) - return strings.Join(elms, " ") + return elms } -func (cls *SHtbClass) DeleteLine(ifname string) string { +func (cls *SHtbClass) DeleteLine(ifname string) []string { elms := cls.basicLineElements("delete", ifname) - return strings.Join(elms, " ") + return elms } func parseHtbClass(chunks []string) (*SHtbClass, error) { diff --git a/pkg/tc/class_test.go b/pkg/tc/class_test.go index d2d0b662..c524596f 100644 --- a/pkg/tc/class_test.go +++ b/pkg/tc/class_test.go @@ -21,8 +21,8 @@ func TestParseClassLines(t *testing.T) { ifname string in []string want []IClass - delLine []string - replaceLine []string + delLine [][]string + replaceLine [][]string }{ { ifname: "eth0", @@ -52,15 +52,15 @@ func TestParseClassLines(t *testing.T) { Ceil: 100000000, }, }, - delLine: []string{ - "class delete dev eth0 parent 1: classid 1:1 htb rate 10Gbit ceil 10Gbit", - "class delete dev eth0 parent 1:1 classid 1:2 htb rate 1Gbit ceil 10Gbit", - "class delete dev eth0 parent 1:1 classid 1:3 htb rate 100Mbit ceil 100Mbit", + delLine: [][]string{ + []string{"class", "delete", "dev", "eth0", "parent", "1:", "classid", "1:1", "htb", "rate", "10Gbit", "ceil", "10Gbit"}, + []string{"class", "delete", "dev", "eth0", "parent", "1:1", "classid", "1:2", "htb", "rate", "1Gbit", "ceil", "10Gbit"}, + []string{"class", "delete", "dev", "eth0", "parent", "1:1", "classid", "1:3", "htb", "rate", "100Mbit", "ceil", "100Mbit"}, }, - replaceLine: []string{ - "class add dev eth0 parent 1: classid 1:1 htb rate 10Gbit ceil 10Gbit", - "class add dev eth0 parent 1:1 classid 1:2 htb rate 1Gbit ceil 10Gbit", - "class add dev eth0 parent 1:1 classid 1:3 htb rate 100Mbit ceil 100Mbit", + replaceLine: [][]string{ + []string{"class", "add", "dev", "eth0", "parent", "1:", "classid", "1:1", "htb", "rate", "10Gbit", "ceil", "10Gbit"}, + []string{"class", "add", "dev", "eth0", "parent", "1:1", "classid", "1:2", "htb", "rate", "1Gbit", "ceil", "10Gbit"}, + []string{"class", "add", "dev", "eth0", "parent", "1:1", "classid", "1:3", "htb", "rate", "100Mbit", "ceil", "100Mbit"}, }, }, } @@ -78,14 +78,14 @@ func TestParseClassLines(t *testing.T) { t.Fatalf("class %d: want %s, got %s", i, jsonutils.Marshal(c.want[i]), jsonutils.Marshal(got[i])) } } - delLines := []string{} + delLines := [][]string{} for _, cls := range got { delLines = append(delLines, cls.DeleteLine(c.ifname)) } if !reflect.DeepEqual(delLines, c.delLine) { t.Fatalf("want %v, got %v", c.delLine, delLines) } - replaceLines := []string{} + replaceLines := [][]string{} for _, cls := range got { replaceLines = append(replaceLines, cls.AddLine(c.ifname)) } diff --git a/pkg/tc/filter.go b/pkg/tc/filter.go index 449d0113..88df028b 100644 --- a/pkg/tc/filter.go +++ b/pkg/tc/filter.go @@ -95,8 +95,11 @@ func (f *SBaseTcFilter) Equals(fi IComparable) bool { } // tc filter replace dev eth0 parent 1: prio 1 handle 10: flower src_ip 192.168.1.10 action mirred egress redirect dev ifb0 -func (f *SBaseTcFilter) basicLineElements(action string, ifname string) []string { +func (f *SBaseTcFilter) basicLineElements(action string, ifname string, isRoot bool) []string { elms := []string{"filter", action, "dev", ifname} + if isRoot { + elms = append(elms, "root") + } if f.Parent != nil { elms = append(elms, "parent", f.Parent.Id()) } @@ -192,26 +195,26 @@ func (f *SFwFilter) Equals(fi IComparable) bool { } func (f *SFwFilter) basicLineElements(action string, ifname string) []string { - elms := f.SBaseTcFilter.basicLineElements(action, ifname) + elms := f.SBaseTcFilter.basicLineElements(action, ifname, false) elms = append(elms, "handle", fmt.Sprintf("0x%x", f.Handle)) elms = append(elms, "fw") elms = append(elms, "classid", f.ClassId) return elms } -func (f *SFwFilter) AddLine(ifname string) string { +func (f *SFwFilter) AddLine(ifname string) []string { elms := f.basicLineElements("add", ifname) - return strings.Join(elms, " ") + return elms } -func (f *SFwFilter) ReplaceLine(ifname string) string { +func (f *SFwFilter) ReplaceLine(ifname string) []string { elms := f.basicLineElements("replace", ifname) - return strings.Join(elms, " ") + return elms } -func (f *SFwFilter) DeleteLine(ifname string) string { +func (f *SFwFilter) DeleteLine(ifname string) []string { elms := f.basicLineElements("delete", ifname) - return strings.Join(elms, " ") + return elms } func parseFwFilter(chunks []string) (*SFwFilter, error) { @@ -254,6 +257,8 @@ func parseFwFilter(chunks []string) (*SFwFilter, error) { type SU32Filter struct { *SBaseTcFilter RedirectDev string + + Handle string } func (f *SU32Filter) Base() *SBaseTcFilter { @@ -281,7 +286,7 @@ func (f *SU32Filter) Equals(fi IComparable) bool { } func (f *SU32Filter) basicLineElements(action string, ifname string) []string { - elms := f.SBaseTcFilter.basicLineElements(action, ifname) + elms := f.SBaseTcFilter.basicLineElements(action, ifname, false) if len(f.RedirectDev) > 0 { elms = append(elms, "u32", @@ -300,23 +305,28 @@ func (f *SU32Filter) basicLineElements(action string, ifname string) []string { return elms } -func (f *SU32Filter) AddLine(ifname string) string { +func (f *SU32Filter) AddLine(ifname string) []string { elms := f.basicLineElements("add", ifname) - return strings.Join(elms, " ") + return elms } -func (f *SU32Filter) ReplaceLine(ifname string) string { +func (f *SU32Filter) ReplaceLine(ifname string) []string { elms := f.basicLineElements("replace", ifname) - return strings.Join(elms, " ") + return elms } -func (f *SU32Filter) DeleteLine(ifname string) string { - elms := f.basicLineElements("delete", ifname) - return strings.Join(elms, " ") +// tc filter delete dev GUESTNET-170 root protocol ip prio 49152 handle 800::1 u32 +func (f *SU32Filter) DeleteLine(ifname string) []string { + elms := f.SBaseTcFilter.basicLineElements("delete", ifname, true) + if len(f.Handle) > 0 { + elms = append(elms, "handle", f.Handle) + } + elms = append(elms, "u32") + return elms } var ( - ingressMatchReg = regexp.MustCompile(`mirred \(Egress Redirect to device (?P[a-z0-9._-]+)\)`) + ingressMatchReg = regexp.MustCompile(`mirred \(Egress Redirect to device (?P[A-Za-z0-9._*-]+)\)`) ) func parseU32Filter(chunks []string) (*SU32Filter, error) { @@ -324,6 +334,16 @@ func parseU32Filter(chunks []string) (*SU32Filter, error) { if m != nil { f := &SU32Filter{} f.RedirectDev = m[1] + for i := range chunks { + switch chunks[i] { + case "fh": + i++ + if i >= len(chunks) { + return nil, errors.Wrap(errors.ErrInvalidFormat, "eol before getting fh") + } + f.Handle = chunks[i] + } + } return f, nil } return nil, errors.Wrapf(errors.ErrInvalidFormat, "unknown u32 filter") @@ -358,7 +378,7 @@ func parseFilterLines(lines []string, parents []IQdisc) ([]IFilter, error) { for i := 0; i < len(lines); { line := strings.TrimSpace(lines[i]) i++ - for i < len(lines) && !strings.HasPrefix(strings.TrimSpace(lines[i]), "filter parent ") { + for i < len(lines) && !strings.HasPrefix(strings.TrimSpace(lines[i]), "filter ") { line += " " + strings.TrimSpace(lines[i]) i++ } diff --git a/pkg/tc/filter_test.go b/pkg/tc/filter_test.go index 186dbbc6..316fed07 100644 --- a/pkg/tc/filter_test.go +++ b/pkg/tc/filter_test.go @@ -28,8 +28,8 @@ func TestParseFilterLines(t *testing.T) { ifname string in []string want []IFilter - delLine []string - replaceLine []string + delLine [][]string + replaceLine [][]string }{ { parent: parentHtbQdisc, @@ -50,11 +50,11 @@ func TestParseFilterLines(t *testing.T) { Handle: 0x257, }, }, - delLine: []string{ - "filter delete dev eth0 parent 1: protocol ip prio 1 handle 0x257 fw classid 1:3", + delLine: [][]string{ + {"filter", "delete", "dev", "eth0", "parent", "1:", "protocol", "ip", "prio", "1", "handle", "0x257", "fw", "classid", "1:3"}, }, - replaceLine: []string{ - "filter add dev eth0 parent 1: protocol ip prio 1 handle 0x257 fw classid 1:3", + replaceLine: [][]string{ + {"filter", "add", "dev", "eth0", "parent", "1:", "protocol", "ip", "prio", "1", "handle", "0x257", "fw", "classid", "1:3"}, }, }, { @@ -79,11 +79,57 @@ func TestParseFilterLines(t *testing.T) { RedirectDev: "reth0", }, }, - delLine: []string{ - "filter delete dev eth0 parent ffff: protocol ip prio 49152 u32 match u32 0 0 action mirred egress redirect dev reth0", + delLine: [][]string{ + {"filter", "delete", "dev", "eth0", "root", "parent", "ffff:", "protocol", "ip", "prio", "49152", "handle", "800::800", "u32"}, }, - replaceLine: []string{ - "filter add dev eth0 parent ffff: protocol ip prio 49152 u32 match u32 0 0 action mirred egress redirect dev reth0", + replaceLine: [][]string{ + {"filter", "add", "dev", "eth0", "parent", "ffff:", "protocol", "ip", "prio", "49152", "u32", "match", "u32", "0", "0", "action", "mirred", "egress", "redirect", "dev", "reth0"}, + }, + }, + { + parent: parentIngressQdisc, + ifname: "eth0", + in: []string{ + "filter parent ffff: protocol ip pref 49152 u32 chain 0", + "filter parent ffff: protocol ip pref 49152 u32 chain 0 fh 800: ht divisor 1", + "filter parent ffff: protocol ip pref 49152 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 terminal flowid ??? not_in_hw", + " match 00000000/00000000 at 0", + " action order 1: mirred (Egress Redirect to device reth0) stolen", + " index 1 ref 1 bind 1", + "filter parent ffff: protocol ip pref 49152 u32 chain 0 fh 800::801 order 2048 key ht 800 bkt 0 terminal flowid ??? not_in_hw", + " match 00000000/00000000 at 0", + " action order 1: mirred (Egress Redirect to device reth0) stolen", + " index 1 ref 1 bind 1", + }, + want: []IFilter{ + &SU32Filter{ + SBaseTcFilter: &SBaseTcFilter{ + Kind: "u32", + Prio: 49152, + Protocol: "ip", + Parent: parentIngressQdisc, + }, + RedirectDev: "reth0", + Handle: "800::800", + }, + &SU32Filter{ + SBaseTcFilter: &SBaseTcFilter{ + Kind: "u32", + Prio: 49152, + Protocol: "ip", + Parent: parentIngressQdisc, + }, + RedirectDev: "reth0", + Handle: "800::801", + }, + }, + delLine: [][]string{ + {"filter", "delete", "dev", "eth0", "root", "parent", "ffff:", "protocol", "ip", "prio", "49152", "handle", "800::800", "u32"}, + {"filter", "delete", "dev", "eth0", "root", "parent", "ffff:", "protocol", "ip", "prio", "49152", "handle", "800::801", "u32"}, + }, + replaceLine: [][]string{ + {"filter", "add", "dev", "eth0", "parent", "ffff:", "protocol", "ip", "prio", "49152", "u32", "match", "u32", "0", "0", "action", "mirred", "egress", "redirect", "dev", "reth0"}, + {"filter", "add", "dev", "eth0", "parent", "ffff:", "protocol", "ip", "prio", "49152", "u32", "match", "u32", "0", "0", "action", "mirred", "egress", "redirect", "dev", "reth0"}, }, }, } @@ -94,7 +140,7 @@ func TestParseFilterLines(t *testing.T) { continue } if len(filters) != len(c.want) { - t.Errorf("want %d filters, got %d", len(c.want), len(filters)) + t.Errorf("parse %s want %d filters, got %d", c.in, len(c.want), len(filters)) continue } for i := range filters { @@ -103,14 +149,14 @@ func TestParseFilterLines(t *testing.T) { continue } } - delLines := []string{} + delLines := [][]string{} for _, filter := range filters { delLines = append(delLines, filter.DeleteLine(c.ifname)) } if !reflect.DeepEqual(delLines, c.delLine) { t.Errorf("want %v, got %v", c.delLine, delLines) } - replaceLines := []string{} + replaceLines := [][]string{} for _, filter := range filters { replaceLines = append(replaceLines, filter.AddLine(c.ifname)) } @@ -119,3 +165,33 @@ func TestParseFilterLines(t *testing.T) { } } } + +func TestIngressMatchReg(t *testing.T) { + cases := []struct { + line string + want string + }{ + { + line: "mirred (Egress Redirect to device reth0) stolen", + want: "reth0", + }, + { + line: "mirred (Egress Redirect to device rGUESTNET1-162) stolen", + want: "rGUESTNET1-162", + }, + { + line: "mirred (Egress Redirect to device bond0.512) stolen", + want: "bond0.512", + }, + { + line: "mirred (Egress Redirect to device *) stolen", + want: "*", + }, + } + for _, c := range cases { + got := ingressMatchReg.FindStringSubmatch(c.line) + if got[1] != c.want { + t.Errorf("want %s, got %s", c.want, got[1]) + } + } +} diff --git a/pkg/tc/qdisc.go b/pkg/tc/qdisc.go index 78fc402f..d8edbbd1 100644 --- a/pkg/tc/qdisc.go +++ b/pkg/tc/qdisc.go @@ -114,10 +114,9 @@ func (q *SBaseTcQdisc) basicLineElements(action string, ifname string) []string return elms } -func (q *SBaseTcQdisc) DeleteLine(ifname string) string { +func (q *SBaseTcQdisc) DeleteLine(ifname string) []string { elms := q.basicLineElements("delete", ifname) - line := strings.Join(elms, " ") - return line + return elms } func parseBaseQdisc(chunks []string) (*SBaseTcQdisc, error) { diff --git a/pkg/tc/qdisc_htb.go b/pkg/tc/qdisc_htb.go index ccc5dca2..209ac3f4 100644 --- a/pkg/tc/qdisc_htb.go +++ b/pkg/tc/qdisc_htb.go @@ -96,17 +96,17 @@ func parseQdiscHtb(chunks []string) (*QdiscHtb, error) { return q, nil } -func (q *QdiscHtb) basicLine(action string, ifname string) string { +func (q *QdiscHtb) basicLine(action string, ifname string) []string { elms := q.SBaseTcQdisc.basicLineElements(action, ifname) elms = append(elms, q.Kind) elms = append(elms, "default", fmt.Sprintf("0x%x", q.DefaultClass)) - return strings.Join(elms, " ") + return elms } -func (q *QdiscHtb) AddLine(ifname string) string { +func (q *QdiscHtb) AddLine(ifname string) []string { return q.basicLine("add", ifname) } -func (q *QdiscHtb) ReplaceLine(ifname string) string { +func (q *QdiscHtb) ReplaceLine(ifname string) []string { return q.basicLine("replace", ifname) } diff --git a/pkg/tc/qdisc_ingress.go b/pkg/tc/qdisc_ingress.go index 612527cf..e60fdf4e 100644 --- a/pkg/tc/qdisc_ingress.go +++ b/pkg/tc/qdisc_ingress.go @@ -1,9 +1,5 @@ package tc -import ( - "strings" -) - /* * modprobe ifb numifbs=0 * ip link add dev rvnet2202-232 type ifb @@ -48,16 +44,16 @@ func parseQdiscIngress(chunks []string) (*QdiscIngress, error) { return q, nil } -func (q *QdiscIngress) basicLine(action string, ifname string) string { +func (q *QdiscIngress) basicLine(action string, ifname string) []string { elms := q.SBaseTcQdisc.basicLineElements(action, ifname) elms = append(elms, q.Kind) - return strings.Join(elms, " ") + return elms } -func (q *QdiscIngress) AddLine(ifname string) string { +func (q *QdiscIngress) AddLine(ifname string) []string { return q.basicLine("add", ifname) } -func (q *QdiscIngress) ReplaceLine(ifname string) string { +func (q *QdiscIngress) ReplaceLine(ifname string) []string { return q.basicLine("replace", ifname) } diff --git a/pkg/tc/qdisc_tbf.go b/pkg/tc/qdisc_tbf.go index 95feb989..69e030c3 100644 --- a/pkg/tc/qdisc_tbf.go +++ b/pkg/tc/qdisc_tbf.go @@ -9,8 +9,8 @@ import ( type QdiscTbf struct { *SBaseTcQdisc Rate uint64 - Burst uint64 - Latency uint64 + Burst uint64 // bytes + Latency uint64 // us } func (q *QdiscTbf) Base() *SBaseTcQdisc { @@ -38,9 +38,9 @@ func (q *QdiscTbf) Compare(itc IComparable) int { } else if q.Burst > q2.Burst && q.Burst-500 > q2.Burst { return 1 } - if q.Latency < q2.Latency { + if q.Latency < q2.Latency && q.Latency+70000 < q2.Latency { return -1 - } else if q.Latency > q2.Latency { + } else if q.Latency > q2.Latency && q.Latency-70000 > q2.Latency { return 1 } return 0 @@ -54,7 +54,7 @@ func (q *QdiscTbf) Equals(qi IComparable) bool { return q.Compare(qi) == 0 } -func (q *QdiscTbf) basicLine(action string, ifname string) string { +func (q *QdiscTbf) basicLine(action string, ifname string) []string { elms := q.SBaseTcQdisc.basicLineElements(action, ifname) elms = append(elms, q.Kind) elms = append(elms, "rate", PrintRate(q.Rate)) @@ -63,14 +63,14 @@ func (q *QdiscTbf) basicLine(action string, ifname string) string { if q.Latency != 0 { elms = append(elms, "latency", PrintTime(q.Latency)) } - return strings.Join(elms, " ") + return elms } -func (q *QdiscTbf) AddLine(ifname string) string { +func (q *QdiscTbf) AddLine(ifname string) []string { return q.basicLine("add", ifname) } -func (q *QdiscTbf) ReplaceLine(ifname string) string { +func (q *QdiscTbf) ReplaceLine(ifname string) []string { return q.basicLine("replace", ifname) } diff --git a/pkg/tc/qdisc_test.go b/pkg/tc/qdisc_test.go index 1799c348..1ec16cd6 100644 --- a/pkg/tc/qdisc_test.go +++ b/pkg/tc/qdisc_test.go @@ -15,6 +15,8 @@ package tc import ( + "reflect" + "strings" "testing" "yunion.io/x/jsonutils" @@ -22,9 +24,9 @@ import ( type tcCase struct { ifname string - line string - lineDelete string - lineReplace string + line []string + lineDelete []string + lineReplace []string isRoot bool wantQdisc IQdisc } @@ -33,9 +35,9 @@ func TestQdiscTbf(t *testing.T) { cases := []tcCase{ { ifname: "wp1-136", - line: "qdisc tbf 1: root refcnt 2 rate 500000Kbit burst 64000b/1 mpu 0b lat 100.0ms", - lineDelete: "qdisc delete dev wp1-136 root handle 1:", - lineReplace: "qdisc add dev wp1-136 root handle 1: tbf rate 500Mbit burst 64Kb latency 100ms", + line: []string{"qdisc", "tbf", "1:", "root", "refcnt", "2", "rate", "500000Kbit", "burst", "64000b/1", "mpu", "0b", "lat", "100.0ms"}, + lineDelete: []string{"qdisc", "delete", "dev", "wp1-136", "root", "handle", "1:"}, + lineReplace: []string{"qdisc", "add", "dev", "wp1-136", "root", "handle", "1:", "tbf", "rate", "500Mbit", "burst", "64Kb", "latency", "100ms"}, isRoot: true, wantQdisc: &QdiscTbf{ SBaseTcQdisc: &SBaseTcQdisc{ @@ -51,9 +53,9 @@ func TestQdiscTbf(t *testing.T) { }, { ifname: "wp1-136", - line: "qdisc tbf 1: root refcnt 2 rate 500000Kbit burst 64000b/4 mpu 0b lat 100.0ms", - lineDelete: "qdisc delete dev wp1-136 root handle 1:", - lineReplace: "qdisc add dev wp1-136 root handle 1: tbf rate 500Mbit burst 64Kb latency 100ms", + line: []string{"qdisc", "tbf", "1:", "root", "refcnt", "2", "rate", "500000Kbit", "burst", "64000b/4", "mpu", "0b", "lat", "100.0ms"}, + lineDelete: []string{"qdisc", "delete", "dev", "wp1-136", "root", "handle", "1:"}, + lineReplace: []string{"qdisc", "add", "dev", "wp1-136", "root", "handle", "1:", "tbf", "rate", "500Mbit", "burst", "64Kb", "latency", "100ms"}, isRoot: true, wantQdisc: &QdiscTbf{ SBaseTcQdisc: &SBaseTcQdisc{ @@ -69,9 +71,9 @@ func TestQdiscTbf(t *testing.T) { }, { ifname: "br0", - line: "qdisc htb 1: root refcnt 2 r2q 10 default 0x2 direct_packets_stat 6 direct_qlen 1000", - lineDelete: "qdisc delete dev br0 root handle 1:", - lineReplace: "qdisc add dev br0 root handle 1: htb default 0x2", + line: []string{"qdisc", "htb", "1:", "root", "refcnt", "2", "r2q", "10", "default", "0x2", "direct_packets_stat", "6", "direct_qlen", "1000"}, + lineDelete: []string{"qdisc", "delete", "dev", "br0", "root", "handle", "1:"}, + lineReplace: []string{"qdisc", "add", "dev", "br0", "root", "handle", "1:", "htb", "default", "0x2"}, isRoot: true, wantQdisc: &QdiscHtb{ SBaseTcQdisc: &SBaseTcQdisc{ @@ -85,9 +87,9 @@ func TestQdiscTbf(t *testing.T) { }, { ifname: "eth0", - line: "qdisc tbf 1: root refcnt 2 rate 100Mbit burst 12500b lat 100ms", - lineDelete: "qdisc delete dev eth0 root handle 1:", - lineReplace: "qdisc add dev eth0 root handle 1: tbf rate 100Mbit burst 12500b latency 100ms", + line: []string{"qdisc", "tbf", "1:", "root", "refcnt", "2", "rate", "100Mbit", "burst", "12500b", "lat", "100ms"}, + lineDelete: []string{"qdisc", "delete", "dev", "eth0", "root", "handle", "1:"}, + lineReplace: []string{"qdisc", "add", "dev", "eth0", "root", "handle", "1:", "tbf", "rate", "100Mbit", "burst", "12500b", "latency", "100ms"}, isRoot: true, wantQdisc: &QdiscTbf{ SBaseTcQdisc: &SBaseTcQdisc{ @@ -104,7 +106,7 @@ func TestQdiscTbf(t *testing.T) { } for _, c := range cases { - qs, err := parseQdiscLines([]string{c.line}) + qs, err := parseQdiscLines([]string{strings.Join(c.line, " ")}) if err != nil { t.Errorf("parseQdiscLines: %s", err) continue @@ -112,11 +114,11 @@ func TestQdiscTbf(t *testing.T) { if !qs[0].Equals(c.wantQdisc) { t.Errorf("Qdisc want %v, got %v", jsonutils.Marshal(c.wantQdisc), jsonutils.Marshal(qs[0])) } - if lineDelete := qs[0].DeleteLine(c.ifname); lineDelete != c.lineDelete { + if lineDelete := qs[0].DeleteLine(c.ifname); !reflect.DeepEqual(lineDelete, c.lineDelete) { t.Errorf("delete line want: %s, got: %s", c.lineDelete, lineDelete) continue } - if lineReplace := qs[0].AddLine(c.ifname); lineReplace != c.lineReplace { + if lineReplace := qs[0].AddLine(c.ifname); !reflect.DeepEqual(lineReplace, c.lineReplace) { t.Errorf("add line want: %s, got: %s", c.lineReplace, lineReplace) continue } diff --git a/pkg/tc/tc.go b/pkg/tc/tc.go index f8331391..0295f2d7 100644 --- a/pkg/tc/tc.go +++ b/pkg/tc/tc.go @@ -17,7 +17,9 @@ package tc import ( "context" "os/exec" + "strings" + "yunion.io/x/log" "yunion.io/x/pkg/errors" ) @@ -59,7 +61,31 @@ func (tc *TcCli) QdiscShow(ctx context.Context, ifname string) (*QdiscTree, erro return qt, err } -func (tc *TcCli) Batch(ctx context.Context, input string) (stdout string, stderr string, err error) { +func (tc *TcCli) Batch(ctx context.Context, cmdlines [][]string) (string, string, error) { + var errs []error + var stdout strings.Builder + var stderr strings.Builder + var err error + for i := range cmdlines { + cmdline := cmdlines[i] + sout, serr, e := tc.singleCmd(ctx, cmdline) + if len(sout) > 0 { + stdout.WriteString(sout) + } + if len(serr) > 0 { + stderr.WriteString(serr) + } + if e != nil { + errs = append(errs, e) + } + } + if len(errs) > 0 { + err = errors.NewAggregate(errs) + } + return stdout.String(), stderr.String(), err +} + +func (tc *TcCli) singleCmd(ctx context.Context, cmdline []string) (stdout string, stderr string, err error) { args := make([]string, 0, 4) if tc.details { args = append(args, "-details") @@ -67,24 +93,18 @@ func (tc *TcCli) Batch(ctx context.Context, input string) (stdout string, stderr if tc.force { args = append(args, "-force") } - args = append(args, "-batch", "-") + args = append(args, cmdline...) cmd := exec.CommandContext(ctx, "tc", args...) - stdin, err := cmd.StdinPipe() - if err != nil { - return - } - n, err := stdin.Write([]byte(input)) - if n != len(input) { - return - } - stdin.Close() output, err := cmd.Output() if err == nil { stdout = string(output) - } else if ee, ok := err.(*exec.ExitError); ok { - stderr = string(ee.Stderr) + } else { + log.Errorf("tc: %s failed: %s", strings.Join(cmdline, " "), err) + if ee, ok := err.(*exec.ExitError); ok { + stderr = string(ee.Stderr) + } } - return + return stdout, stderr, err } func NewTcCli() *TcCli { diff --git a/pkg/tc/tcobj.go b/pkg/tc/tcobj.go index 7dbb5f6b..9898b220 100644 --- a/pkg/tc/tcobj.go +++ b/pkg/tc/tcobj.go @@ -13,7 +13,7 @@ type ITcObj interface { } type ITcObjAlter interface { - DeleteLine(ifname string) string - AddLine(ifname string) string - ReplaceLine(ifname string) string + DeleteLine(ifname string) []string + AddLine(ifname string) []string + ReplaceLine(ifname string) []string } diff --git a/pkg/tc/tree.go b/pkg/tc/tree.go index f9afc7ad..252f3989 100644 --- a/pkg/tc/tree.go +++ b/pkg/tc/tree.go @@ -74,8 +74,8 @@ func (qt *QdiscTree) Merge(qt2 *QdiscTree) { Sort(qt.filters) } -func (qt *QdiscTree) Delta(qt2 *QdiscTree, ifname string) []string { - lines := []string{} +func (qt *QdiscTree) Delta(qt2 *QdiscTree, ifname string) [][]string { + lines := [][]string{} addedQdisc, updatedQdisc1, updatedQdisc2, removedQdisc := Split(qt.qdisc, qt2.qdisc, true) addedClass, updatedClass1, updatedClass2, removedClass := Split(qt.classes, qt2.classes, true) addedFilter, _, _, removedFilter := Split(qt.filters, qt2.filters, false) diff --git a/pkg/tc/tree_test.go b/pkg/tc/tree_test.go index ac0e38fc..a309f246 100644 --- a/pkg/tc/tree_test.go +++ b/pkg/tc/tree_test.go @@ -27,7 +27,7 @@ func TestQdiscTree(t *testing.T) { class string filter string wantQdiscTree *QdiscTree - deltaLines []string + deltaLines [][]string }{ { qdisc: `qdisc htb 1: root refcnt 2 r2q 10 default 0x2 direct_packets_stat 6 direct_qlen 1000 @@ -89,7 +89,7 @@ filter parent 1: protocol ip pref 1 fw chain 0 handle 0x257 classid 1:3`, }, }) }(), - deltaLines: []string{}, + deltaLines: [][]string{}, }, { qdisc: `qdisc htb 1: root refcnt 2 r2q 10 default 0x2 direct_packets_stat 6 direct_qlen 1000 @@ -151,8 +151,8 @@ filter parent 1: protocol ip pref 1 fw chain 0 handle 0x257 classid 1:3`, }, }) }(), - deltaLines: []string{ - "class replace dev eth0 parent 1:1 classid 1:3 htb rate 100Mbit ceil 100Mbit", + deltaLines: [][]string{ + {"class", "replace", "dev", "eth0", "parent", "1:1", "classid", "1:3", "htb", "rate", "100Mbit", "ceil", "100Mbit"}, }, }, { @@ -211,12 +211,12 @@ filter parent 1: protocol ip pref 1 fw chain 0 handle 0x257 classid 1:3`, }, }) }(), - deltaLines: []string{ - "qdisc add dev eth0 root handle 1: htb default 0x2", - "class add dev eth0 parent 1: classid 1:1 htb rate 10Gbit ceil 10Gbit", - "class add dev eth0 parent 1:1 classid 1:2 htb rate 1Gbit ceil 10Gbit", - "class add dev eth0 parent 1:1 classid 1:3 htb rate 100Mbit ceil 100Mbit", - "filter add dev eth0 parent 1: protocol ip prio 1 handle 0x257 fw classid 1:3", + deltaLines: [][]string{ + {"qdisc", "add", "dev", "eth0", "root", "handle", "1:", "htb", "default", "0x2"}, + {"class", "add", "dev", "eth0", "parent", "1:", "classid", "1:1", "htb", "rate", "10Gbit", "ceil", "10Gbit"}, + {"class", "add", "dev", "eth0", "parent", "1:1", "classid", "1:2", "htb", "rate", "1Gbit", "ceil", "10Gbit"}, + {"class", "add", "dev", "eth0", "parent", "1:1", "classid", "1:3", "htb", "rate", "100Mbit", "ceil", "100Mbit"}, + {"filter", "add", "dev", "eth0", "parent", "1:", "protocol", "ip", "prio", "1", "handle", "0x257", "fw", "classid", "1:3"}, }, }, { @@ -238,7 +238,7 @@ qdisc fq_codel 10: parent 1: limit 10240p flows 1024 quantum 1514 target 5ms int } return NewQdiscTree([]IQdisc{qdisc}, []IClass{}, []IFilter{}) }(), - deltaLines: []string{}, + deltaLines: [][]string{}, }, { qdisc: ``, @@ -258,8 +258,8 @@ qdisc fq_codel 10: parent 1: limit 10240p flows 1024 quantum 1514 target 5ms int } return NewQdiscTree([]IQdisc{qdisc}, []IClass{}, []IFilter{}) }(), - deltaLines: []string{ - "qdisc add dev eth0 root handle 1: tbf rate 100Mbit burst 12500b latency 100ms", + deltaLines: [][]string{ + {"qdisc", "add", "dev", "eth0", "root", "handle", "1:", "tbf", "rate", "100Mbit", "burst", "12500b", "latency", "100ms"}, }, }, { @@ -296,10 +296,10 @@ qdisc fq_codel 10: parent 1: limit 10240p flows 1024 quantum 1514 target 5ms int } return NewQdiscTree([]IQdisc{tbfQdisc, ingressQdisc}, []IClass{}, []IFilter{ingressFilter}) }(), - deltaLines: []string{ - "qdisc add dev eth0 root handle 1: tbf rate 100Mbit burst 12500b latency 100ms", - "qdisc add dev eth0 handle ffff: ingress", - "filter add dev eth0 parent ffff: protocol ip prio 49152 u32 match u32 0 0 action mirred egress redirect dev reth0", + deltaLines: [][]string{ + {"qdisc", "add", "dev", "eth0", "root", "handle", "1:", "tbf", "rate", "100Mbit", "burst", "12500b", "latency", "100ms"}, + {"qdisc", "add", "dev", "eth0", "handle", "ffff:", "ingress"}, + {"filter", "add", "dev", "eth0", "parent", "ffff:", "protocol", "ip", "prio", "49152", "u32", "match", "u32", "0", "0", "action", "mirred", "egress", "redirect", "dev", "reth0"}, }, }, }