Skip to content

Commit

Permalink
[all] YAML topology loading/saving; refactorings to move node-config …
Browse files Browse the repository at this point in the history
…into simulation pkg; bugfix in creating SSEDs of different versions (use distinct CSL param configuration); unit tests added
  • Loading branch information
EskoDijk committed Feb 12, 2024
1 parent ad7b6ef commit 44fd579
Show file tree
Hide file tree
Showing 15 changed files with 553 additions and 143 deletions.
52 changes: 15 additions & 37 deletions cli/CmdRunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import (

"github.com/openthread/ot-ns/dispatcher"
"github.com/openthread/ot-ns/logger"
"github.com/openthread/ot-ns/prng"
"github.com/openthread/ot-ns/progctx"
"github.com/openthread/ot-ns/radiomodel"
"github.com/openthread/ot-ns/simulation"
Expand Down Expand Up @@ -382,6 +381,7 @@ func (rt *CmdRunner) executeAddNode(cc *CommandContext, cmd *AddCmd) {
simCfg := cc.rt.sim.GetConfig()
cfg := simCfg.NewNodeConfig // copy current new-node config for simulation, and modify it.

cfg.Type = cmd.Type.Val
if cmd.X != nil {
cfg.X = *cmd.X
cfg.IsAutoPlaced = false
Expand All @@ -394,42 +394,22 @@ func (rt *CmdRunner) executeAddNode(cc *CommandContext, cmd *AddCmd) {
cfg.Z = *cmd.Z
cfg.IsAutoPlaced = false
}

if cmd.Id != nil {
cfg.ID = cmd.Id.Val
}

if cmd.RadioRange != nil {
cfg.RadioRange = cmd.RadioRange.Val
}

cfg.Type = cmd.Type.Val
cfg.UpdateNodeConfigFromType()

if cmd.Executable != nil {
cfg.ExecutablePath = simCfg.ExeConfig.FindExecutable(cmd.Executable.Path)
} else if cmd.Version != nil {
cfg.Version = cmd.Version.Val
}

cfg.Restore = cmd.Restore != nil

// in case of specified simulation random seed, each node gets a PRNG-predictable random seed assigned.
if simCfg.RandomSeed != 0 {
cfg.RandomSeed = prng.NewNodeRandomSeed()
}

// for a BR, do extra init steps to set prefix/routes/etc.
if cfg.IsBorderRouter {
cfg.InitScript = append(cfg.InitScript, simulation.DefaultBrScript...)
if cmd.Version != nil {
cfg.Version = cmd.Version.Val
}

// for SSED, do extra CSL init command.
if cfg.Type == SSED {
cfg.InitScript = append(cfg.InitScript, simulation.DefaultCslScript...)
if cmd.Executable != nil {
cfg.ExecutablePath = cmd.Executable.Path
}

rt.postAsyncWait(cc, func(sim *simulation.Simulation) {
sim.NodeConfigFinalize(&cfg)
node, err := sim.AddNode(&cfg)
if err != nil {
cc.error(err)
Expand Down Expand Up @@ -1288,9 +1268,9 @@ func (rt *CmdRunner) executeSave(cc *CommandContext, cmd *SaveCmd) {
networkConfig := sim.ExportNetwork()
nodesConfig := sim.ExportNodes(&networkConfig)

root := map[string]interface{}{
"network": networkConfig,
"nodes": nodesConfig,
root := simulation.YamlConfigFile{
NetworkConfig: networkConfig,
NodesList: nodesConfig,
}
err = rootYaml.Encode(root)
logger.PanicIfError(err)
Expand All @@ -1306,8 +1286,6 @@ func (rt *CmdRunner) executeSave(cc *CommandContext, cmd *SaveCmd) {
}

func (rt *CmdRunner) executeLoad(cc *CommandContext, cmd *LoadCmd) {
var rootYaml map[string]interface{}

// test filename if valid
fileInfo, err := os.Stat(cmd.Filename)
if err != nil || fileInfo.IsDir() {
Expand All @@ -1321,16 +1299,16 @@ func (rt *CmdRunner) executeLoad(cc *CommandContext, cmd *LoadCmd) {
cc.errorf("Could not load file '%s': %v", cmd.Filename, err)
return
}
err = yaml.Unmarshal(b, rootYaml)
cfgFile := simulation.YamlConfigFile{}
err = yaml.Unmarshal(b, &cfgFile)
if err != nil {
cc.errorf("Error in YAML file: %v", err)
return
}

networkConfig := simulation.YamlNodeConfig(rootYaml["network"])
err = sim.ImportNodes(cfgFile.NetworkConfig, cfgFile.NodesList)
if err != nil {
cc.outputf("Warning: %v\n", err)
}
})

if err != nil {
cc.errorf("Error writing file '%s': %v", cmd.Filename, err)
}
}
77 changes: 77 additions & 0 deletions cli/yaml_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) 2024, The OTNS Authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package cli

import (
"testing"

"github.com/openthread/ot-ns/simulation"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)

var testYamlArray = `
[4,5,6]
`

var testYamlFile = `
network:
pos-shift: [0, 0, 0]
nodes:
- id: 1
type: router
pos: [100, 100, 0]
- id: 2
type: fed
pos: [60, 150, 0]
- id: 3
type: med
version: v11
pos: [100, 150, 0]
- id: 4
type: router
version: v12
pos: [200, 100, 0]
`

func TestYamlArrayUnmarshall(t *testing.T) {
myArray := [3]int{0, 0, 0}
err := yaml.Unmarshal([]byte(testYamlArray), &myArray)
assert.Nil(t, err)
assert.Equal(t, 4, myArray[0])
assert.Equal(t, 5, myArray[1])
assert.Equal(t, 6, myArray[2])
}

func TestYamlConfigUnmarshall(t *testing.T) {
cfgFile := simulation.YamlConfigFile{}
err := yaml.Unmarshal([]byte(testYamlFile), &cfgFile)
assert.Nil(t, err)
assert.Equal(t, 3, len(cfgFile.NetworkConfig.Position))
assert.Equal(t, 4, len(cfgFile.NodesList))
assert.Equal(t, "v11", *cfgFile.NodesList[2].Version)
}
1 change: 0 additions & 1 deletion otns_main/otns_main.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,6 @@ func createSimulation(simId int, ctx *progctx.ProgCtx) *simulation.Simulation {

simcfg.ExeConfig.Ftd = args.OtCliPath
simcfg.ExeConfig.Mtd = args.OtCliMtdPath
simcfg.NewNodeConfig.InitScript = simulation.DefaultNodeInitScript
simcfg.NewNodeConfig.NodeLogFile = !args.NoLogFile
args.Speed = strings.ToLower(args.Speed)
if args.Speed == "max" {
Expand Down
6 changes: 3 additions & 3 deletions otnstester/tests/consts.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020-2022, The OTNS Authors.
// Copyright (c) 2020-2024, The OTNS Authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -27,7 +27,7 @@
package main

import (
"github.com/openthread/ot-ns/types"
"github.com/openthread/ot-ns/simulation"
)

const (
Expand All @@ -37,5 +37,5 @@ const (
)

var (
DefaultRadioRange = types.DefaultNodeConfig().RadioRange
DefaultRadioRange = simulation.DefaultNodeConfig().RadioRange
)
14 changes: 14 additions & 0 deletions pylibs/otns/cli/OTNS.py
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,20 @@ def kpi(self) -> bool:
status = self._expect_str(self._do_command('kpi'))
return status == 'on'

def load(self, filename: str) -> None:
"""
Load new nodes / network topology from a YAML file.
:param filename: file name to load from
"""
self._do_command(f'load "{filename}"')

def save(self, filename:str) -> None:
"""
Save nodes / network topology to a YAML file.
:param filename: file name to save to
"""
self._do_command(f'save "{filename}"')

@staticmethod
def _expect_int(output: List[str]) -> int:
assert len(output) == 1, output
Expand Down
Loading

0 comments on commit 44fd579

Please sign in to comment.