From 76ca8ba9388a8b3517add447dea09cd3498287e9 Mon Sep 17 00:00:00 2001 From: Sukhesh Halemane Date: Mon, 29 Aug 2016 18:48:57 -0700 Subject: [PATCH] Add a tool to modify cfg objects fix a typo add usage to cfgtool --- netmaster/docknet/docknet.go | 26 +- netmaster/docknet/docknet_test.go | 4 +- state/cfgtool/cfgtool.go | 387 ++++++++++++++++++++++++++++ test/integration/netprofile_test.go | 8 +- test/integration/utils_test.go | 17 ++ 5 files changed, 423 insertions(+), 19 deletions(-) create mode 100644 state/cfgtool/cfgtool.go diff --git a/netmaster/docknet/docknet.go b/netmaster/docknet/docknet.go index e1d253b43..b73ccb547 100644 --- a/netmaster/docknet/docknet.go +++ b/netmaster/docknet/docknet.go @@ -38,8 +38,8 @@ const ( var netDriverName = "netplugin" var ipamDriverName = "netplugin" -// OperState has oper state of docker network -type OperState struct { +// DnetOperState has oper state of docker network +type DnetOperState struct { core.CommonState TenantName string `json:"tenantName"` NetworkName string `json:"networkName"` @@ -48,30 +48,30 @@ type OperState struct { } // Write the state. -func (s *OperState) Write() error { +func (s *DnetOperState) Write() error { key := fmt.Sprintf(docknetOperPath, s.ID) return s.StateDriver.WriteState(key, s, json.Marshal) } // Read the state for a given identifier -func (s *OperState) Read(id string) error { +func (s *DnetOperState) Read(id string) error { key := fmt.Sprintf(docknetOperPath, id) return s.StateDriver.ReadState(key, s, json.Unmarshal) } // ReadAll state and return the collection. -func (s *OperState) ReadAll() ([]core.State, error) { +func (s *DnetOperState) ReadAll() ([]core.State, error) { return s.StateDriver.ReadAllState(docknetOperPrefix, s, json.Unmarshal) } // WatchAll state transitions and send them through the channel. -func (s *OperState) WatchAll(rsps chan core.WatchState) error { +func (s *DnetOperState) WatchAll(rsps chan core.WatchState) error { return s.StateDriver.WatchAllState(docknetOperPrefix, s, json.Unmarshal, rsps) } // Clear removes the state. -func (s *OperState) Clear() error { +func (s *DnetOperState) Clear() error { key := fmt.Sprintf(docknetOperPath, s.ID) return s.StateDriver.ClearState(key) } @@ -186,7 +186,7 @@ func CreateDockNet(tenantName, networkName, serviceName string, nwCfg *mastercfg } // save docknet oper state - dnetOper := OperState{ + dnetOper := DnetOperState{ TenantName: tenantName, NetworkName: networkName, ServiceName: serviceName, @@ -233,7 +233,7 @@ func DeleteDockNet(tenantName, networkName, serviceName string) error { } // save docknet oper state - dnetOper := OperState{} + dnetOper := DnetOperState{} dnetOper.ID = fmt.Sprintf("%s.%s.%s", tenantName, networkName, serviceName) dnetOper.StateDriver = stateDriver @@ -247,7 +247,7 @@ func DeleteDockNet(tenantName, networkName, serviceName string) error { } // FindDocknetByUUID find the docknet by UUID -func FindDocknetByUUID(dnetID string) (*OperState, error) { +func FindDocknetByUUID(dnetID string) (*DnetOperState, error) { // Get the state driver stateDriver, err := utils.GetStateDriver() if err != nil { @@ -255,7 +255,7 @@ func FindDocknetByUUID(dnetID string) (*OperState, error) { return nil, err } - tmpDnet := OperState{} + tmpDnet := DnetOperState{} tmpDnet.StateDriver = stateDriver dnetOperList, err := tmpDnet.ReadAll() if err != nil { @@ -265,8 +265,8 @@ func FindDocknetByUUID(dnetID string) (*OperState, error) { // Walk all dnets and find the matching UUID for _, dnet := range dnetOperList { - if dnet.(*OperState).DocknetUUID == dnetID { - return dnet.(*OperState), nil + if dnet.(*DnetOperState).DocknetUUID == dnetID { + return dnet.(*DnetOperState), nil } } diff --git a/netmaster/docknet/docknet_test.go b/netmaster/docknet/docknet_test.go index ea08cb0bf..5a1ac874b 100644 --- a/netmaster/docknet/docknet_test.go +++ b/netmaster/docknet/docknet_test.go @@ -36,7 +36,7 @@ func initStateDriver() (core.StateDriver, error) { } // getDocknetState gets docknet oper state -func getDocknetState(tenantName, networkName, serviceName string) *OperState { +func getDocknetState(tenantName, networkName, serviceName string) *DnetOperState { // Get the state driver stateDriver, err := utils.GetStateDriver() if err != nil { @@ -45,7 +45,7 @@ func getDocknetState(tenantName, networkName, serviceName string) *OperState { } // save docknet oper state - dnetOper := OperState{} + dnetOper := DnetOperState{} dnetOper.StateDriver = stateDriver // write the dnet oper state diff --git a/state/cfgtool/cfgtool.go b/state/cfgtool/cfgtool.go new file mode 100644 index 000000000..67e1fbfbb --- /dev/null +++ b/state/cfgtool/cfgtool.go @@ -0,0 +1,387 @@ +/*** +Copyright 2014 Cisco Systems Inc. All rights reserved. + +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 main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "reflect" + "strconv" + "strings" + + log "github.com/Sirupsen/logrus" + "github.com/contiv/netplugin/core" + "github.com/contiv/netplugin/drivers" + "github.com/contiv/netplugin/netmaster/docknet" + "github.com/contiv/netplugin/netmaster/gstate" + "github.com/contiv/netplugin/netmaster/mastercfg" + "github.com/contiv/netplugin/netmaster/resources" + "github.com/contiv/netplugin/utils" +) + +// initStateDriver creates a state driver based on the cluster store URL +func initStateDriver(clusterStore string) (core.StateDriver, error) { + // parse the state store URL + parts := strings.Split(clusterStore, "://") + if len(parts) < 2 { + return nil, core.Errorf("Invalid state-store URL %q", clusterStore) + } + stateStore := parts[0] + + // Make sure we support the statestore type + switch stateStore { + case utils.EtcdNameStr: + case utils.ConsulNameStr: + default: + return nil, core.Errorf("Unsupported state-store %q", stateStore) + } + + // Setup instance info + instInfo := core.InstanceInfo{ + DbURL: clusterStore, + } + + return utils.NewStateDriver(stateStore, &instInfo) +} + +// parseRange parses a string in "1,2-3,4-10" format and returns an array of values +func parseRange(rangeStr string) ([]uint, error) { + var values []uint + if rangeStr == "" { + return []uint{}, nil + } + + // split ranges based on "," char + rangeList := strings.Split(rangeStr, ",") + + for _, subrange := range rangeList { + minMax := strings.Split(strings.TrimSpace(subrange), "-") + if len(minMax) == 2 { + min, err := strconv.Atoi(minMax[0]) + if err != nil { + log.Errorf("Invalid range: %v", subrange) + return nil, err + } + max, err := strconv.Atoi(minMax[1]) + if err != nil { + log.Errorf("Invalid range: %v", subrange) + return nil, err + } + + // some error checking + if min > max || min < 0 || max < 0 { + log.Errorf("Invalid range values: %v", subrange) + return nil, fmt.Errorf("Invalid range values") + } + + for i := min; i <= max; i++ { + values = append(values, uint(i)) + } + } else if len(minMax) == 1 { + val, err := strconv.Atoi(minMax[0]) + if err != nil { + log.Errorf("Invalid range: %v", subrange) + return nil, err + } + + values = append(values, uint(val)) + } else { + log.Errorf("Invalid range: %v", subrange) + return nil, fmt.Errorf("Invalid range format") + } + } + + return values, nil +} + +// processResource handles resource commands +func processResource(stateDriver core.StateDriver, rsrcName, rsrcVal string) error { + // Read global config + gCfg := gstate.Cfg{} + gCfg.StateDriver = stateDriver + err := gCfg.Read("") + if err != nil { + log.Errorf("error reading tenant cfg state. Error: %s", err) + return err + } + + // process resource based on name + if rsrcName == "vlan" { + numVlans, vlansInUse := gCfg.GetVlansInUse() + fmt.Printf("Num Vlans: %d\n Current Vlans in Use: %s\n", numVlans, vlansInUse) + + // see if we need to set the resource + if rsrcVal != "" { + values, err := parseRange(rsrcVal) + if err != nil { + log.Errorf("Error parsing range: %v", err) + return err + } + log.Infof("Setting vlan values: %v", values) + + // set vlan values + for _, val := range values { + _, err = gCfg.AllocVLAN(val) + if err != nil { + log.Errorf("Error setting vlan: %d. Err: %v", val, err) + } + } + + log.Infof("Finished setting VLANs") + } + } else if rsrcName == "vxlan" { + numVxlans, vxlansInUse := gCfg.GetVxlansInUse() + fmt.Printf("Num Vxlans: %d\n Current Vxlans in Use: %s\n", numVxlans, vxlansInUse) + + // see if we need to set the resource + if rsrcVal != "" { + values, err := parseRange(rsrcVal) + if err != nil { + log.Errorf("Error parsing range: %v", err) + return err + } + log.Infof("Setting vxlan values: %v", values) + + // set vlan values + for _, val := range values { + _, _, err = gCfg.AllocVXLAN(val) + if err != nil { + log.Errorf("Error setting vxlan: %d. Err: %v", val, err) + } + } + + log.Infof("Finished setting VXLANs") + } + } else { + log.Errorf("Unknown resource: %v", rsrcName) + return fmt.Errorf("Unknown resource") + } + + return nil +} + +// processState handles `-state` command +func processState(stateDriver core.StateDriver, stateName, stateID, fieldName, setVal string) error { + var typeRegistry = make(map[string]core.State) + + // build the type registery + typeRegistry[reflect.TypeOf(mastercfg.CfgEndpointState{}).Name()] = &mastercfg.CfgEndpointState{} + typeRegistry[reflect.TypeOf(mastercfg.CfgNetworkState{}).Name()] = &mastercfg.CfgNetworkState{} + typeRegistry[reflect.TypeOf(mastercfg.CfgBgpState{}).Name()] = &mastercfg.CfgBgpState{} + typeRegistry[reflect.TypeOf(mastercfg.EndpointGroupState{}).Name()] = &mastercfg.EndpointGroupState{} + typeRegistry[reflect.TypeOf(mastercfg.GlobConfig{}).Name()] = &mastercfg.GlobConfig{} + typeRegistry[reflect.TypeOf(mastercfg.EpgPolicy{}).Name()] = &mastercfg.EpgPolicy{} + typeRegistry[reflect.TypeOf(mastercfg.SvcProvider{}).Name()] = &mastercfg.SvcProvider{} + typeRegistry[reflect.TypeOf(mastercfg.CfgServiceLBState{}).Name()] = &mastercfg.CfgServiceLBState{} + typeRegistry[reflect.TypeOf(resources.AutoVLANCfgResource{}).Name()] = &resources.AutoVLANCfgResource{} + typeRegistry[reflect.TypeOf(resources.AutoVLANOperResource{}).Name()] = &resources.AutoVLANOperResource{} + typeRegistry[reflect.TypeOf(resources.AutoVXLANCfgResource{}).Name()] = &resources.AutoVXLANCfgResource{} + typeRegistry[reflect.TypeOf(resources.AutoVXLANOperResource{}).Name()] = &resources.AutoVXLANOperResource{} + typeRegistry[reflect.TypeOf(drivers.OvsDriverOperState{}).Name()] = &drivers.OvsDriverOperState{} + typeRegistry[reflect.TypeOf(drivers.OvsOperEndpointState{}).Name()] = &drivers.OvsOperEndpointState{} + typeRegistry[reflect.TypeOf(docknet.DnetOperState{}).Name()] = &docknet.DnetOperState{} + + // find the type by name + cfgType := typeRegistry[stateName] + + // Some reflect magic to set the state driver + s := reflect.ValueOf(cfgType).Elem() + if s.Kind() == reflect.Struct { + f := s.FieldByName("StateDriver") + if f.IsValid() && f.CanSet() { + if f.Kind() == reflect.Interface { + f.Set(reflect.ValueOf(stateDriver)) + log.Debugf("Set: %+v", reflect.ValueOf(cfgType).Elem().FieldByName("StateDriver")) + } else { + log.Errorf("Invalid kind") + return fmt.Errorf("Can not set state driver") + } + } else { + log.Errorf("Could not find the field.") + return fmt.Errorf("Can not set state driver") + } + } else { + log.Errorf("Invalid type: %v", s.Kind()) + return fmt.Errorf("Can not set state driver") + } + + // read the object + log.Debugf("cfgType: %+v", cfgType) + err := cfgType.Read(stateID) + if err != nil { + log.Errorf("Error reading state %s{id: %s}. Err: %v", stateName, stateID, err) + return err + } + + // print the object + content, err := json.MarshalIndent(cfgType, "", " ") + if err != nil { + log.Errorf("Error marshalling json: %+v", cfgType) + return err + } + fmt.Printf("Current value of id: %s{ id: %s }\n%s\n", stateName, stateID, content) + + // See if we need to set a field + if fieldName != "" && setVal != "" { + // more reflect magic to set a field + s := reflect.ValueOf(cfgType).Elem() + if s.Kind() == reflect.Struct { + f := s.FieldByName(fieldName) + if f.IsValid() && f.CanSet() { + switch f.Kind() { + case reflect.String: + f.SetString(setVal) + case reflect.Int: + intVal, err := strconv.ParseInt(setVal, 10, 0) + if err != nil { + log.Errorf("Can not convert %s to int. Err: %v", setVal, err) + return err + } + f.SetInt(intVal) + case reflect.Uint: + intVal, err := strconv.ParseUint(setVal, 10, 0) + if err != nil { + log.Errorf("Can not convert %s to uint. Err: %v", setVal, err) + return err + } + f.SetUint(intVal) + case reflect.Bool: + boolVal, err := strconv.ParseBool(setVal) + if err != nil { + log.Errorf("Can not convert %s to bool. Err: %v", setVal, err) + return err + } + f.SetBool(boolVal) + default: + log.Errorf("Invalid kind") + return fmt.Errorf("Invalid kind") + } + } else { + log.Errorf("Could not find the field. or its not setable") + return fmt.Errorf("Could not find the field. or its not setable") + } + } else { + log.Errorf("Invalid type: %v", s.Kind()) + } + + // print the modified object + content, err := json.MarshalIndent(cfgType, "", " ") + if err != nil { + log.Errorf("Error marshalling json: %+v", cfgType) + return err + } + fmt.Printf("Writing values:\n%s\n", content) + + // write it + err = cfgType.Write() + if err != nil { + log.Errorf("Error writing %s{ id: %s }. Err: %v", stateName, stateID, err) + return err + } + } + + return nil +} + +func main() { + var rsrcName string + var clusterStore string + var setVal string + var stateName string + var stateID string + var fieldName string + + // parse all commandline args + flagSet := flag.NewFlagSet("cfgtool", flag.ExitOnError) + flagSet.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + flagSet.PrintDefaults() + fmt.Fprintf(os.Stderr, "\nExamples:\n") + fmt.Fprintf(os.Stderr, "%s -state -id -field -set \n", os.Args[0]) + fmt.Fprintf(os.Stderr, " %s -state GlobConfig -id global -field FwdMode -set routing\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "%s -resource -set \n", os.Args[0]) + fmt.Fprintf(os.Stderr, " %s -resource vlan -set 1-10\n", os.Args[0]) + } + + flagSet.StringVar(&rsrcName, + "resource", + "", + "Resource to modify [vlan|vxlan]") + flagSet.StringVar(&setVal, + "set", + "", + "Resource value") + flagSet.StringVar(&clusterStore, + "cluster-store", + "etcd://127.0.0.1:2379", + "Etcd or Consul cluster store url.") + flagSet.StringVar(&stateName, + "state", + "", + "State to modify") + flagSet.StringVar(&stateID, + "id", + "", + "State ID to modify") + flagSet.StringVar(&fieldName, + "field", + "", + "State Field to modify") + if err := flagSet.Parse(os.Args[1:]); err != nil { + log.Errorf("Error parsing commandline args: %v", err) + return + } + + // check if we have sufficient args + if (rsrcName == "" && stateName == "") || + (stateName != "" && stateID == "") || + (stateName != "" && stateID != "" && setVal != "" && fieldName == "") { + flagSet.Usage() + os.Exit(2) + } + + // initialize state driver + stateDriver, err := initStateDriver(clusterStore) + if err != nil { + log.Fatalf("Failed to init state-store. Error: %s", err) + } + + // Initialize resource manager + resmgr, err := resources.NewStateResourceManager(stateDriver) + if err != nil || resmgr == nil { + log.Fatalf("Failed to init resource manager. Error: %s", err) + } + + // process `-resource` command + if rsrcName != "" { + err = processResource(stateDriver, rsrcName, setVal) + if err != nil { + log.Errorf("Error processing resource %s. Err: %v", rsrcName, err) + } + + return + } + + // handle `-state` command + if stateName != "" && stateID != "" { + err = processState(stateDriver, stateName, stateID, fieldName, setVal) + if err != nil { + log.Errorf("Error processing state %s{ id : %s }. Err: %v", stateName, stateID, err) + } + } +} diff --git a/test/integration/netprofile_test.go b/test/integration/netprofile_test.go index a07e0d3e8..224612408 100644 --- a/test/integration/netprofile_test.go +++ b/test/integration/netprofile_test.go @@ -218,7 +218,7 @@ func (its *integTestSuite) TestNetprofileBandwidth(c *C) { time.Sleep(100 * time.Millisecond) // verify TC bandwidth - c.Assert(tcFilterCheckBw(10, 100), IsNil) + c.Assert(tcFilterCheckBwRetry(10, 100), IsNil) // change bandwidthvalue c.Assert(its.client.NetprofilePost(&client.Netprofile{ @@ -232,7 +232,7 @@ func (its *integTestSuite) TestNetprofileBandwidth(c *C) { time.Sleep(100 * time.Millisecond) // verify TC bandwidth - c.Assert(tcFilterCheckBw(20, 200), IsNil) + c.Assert(tcFilterCheckBwRetry(20, 200), IsNil) // clear bandwidth value c.Assert(its.client.NetprofilePost(&client.Netprofile{ @@ -260,7 +260,7 @@ func (its *integTestSuite) TestNetprofileBandwidth(c *C) { time.Sleep(100 * time.Millisecond) // verify TC bandwidth - c.Assert(tcFilterCheckBw(30, 300), IsNil) + c.Assert(tcFilterCheckBwRetry(30, 300), IsNil) // create a new net profile c.Assert(its.client.NetprofilePost(&client.Netprofile{ @@ -282,7 +282,7 @@ func (its *integTestSuite) TestNetprofileBandwidth(c *C) { time.Sleep(100 * time.Millisecond) // verify TC bandwidth - c.Assert(tcFilterCheckBw(40, 400), IsNil) + c.Assert(tcFilterCheckBwRetry(40, 400), IsNil) // remove net profile from epg c.Assert(its.client.EndpointGroupPost(&client.EndpointGroup{ diff --git a/test/integration/utils_test.go b/test/integration/utils_test.go index 823c42160..36aff0b64 100644 --- a/test/integration/utils_test.go +++ b/test/integration/utils_test.go @@ -23,6 +23,7 @@ import ( "runtime/debug" "strconv" "strings" + "time" "github.com/contiv/netplugin/netmaster/intent" "github.com/contiv/netplugin/netmaster/master" @@ -303,6 +304,22 @@ func ofctlFlowAssert(flowList []string, tableID int, matchStr string, expMatch b } } +// tcFilterCheckBwRetry check for tc bw with retry +func tcFilterCheckBwRetry(expBw, expBurst int64) error { + var err error + for i := 0; i < 3; i++ { + err = tcFilterCheckBw(expBw, expBurst) + if err == nil { + return err + } + + // wait a little and retry again + time.Sleep(300 * time.Millisecond) + } + + return err +} + // tcFilterCheckBw checks bandwidth using `tc` command func tcFilterCheckBw(expBw, expBurst int64) error { qdiscShow, err := runCmd("tc qdisc show")