Skip to content

Dynamically add/delete flows or protocol elements to existing applied configuration. #410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: master
Choose a base branch
from

Conversation

anjan-keysight
Copy link
Contributor

@anjan-keysight anjan-keysight commented Feb 21, 2025

Issue #409

Redocly View:

Config:
https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/open-traffic-generator/models/dev-multi-endpoint/artifacts/openapi.yaml&nocors#tag/Configuration/operation/append_config

Objectives:

There is a long pending request for support for appending and deleting configuration resources to existing configuration.
One such end use case is when we want to provide support for multiple endpoint for traffic. At present, due to maximum stream limitation per port, we can not support RSVP traffic with 64K LSPs. With multiple endpoint support in same flow configuration, we can pack multiple LSPs into one stream.

Furthermore, in such a scenario, resolving "auto" mode for LSP fields for multiple sub-streams, which involves learning LSP data and then internally crafting all the sub-streams into a composite data pattern. This becomes complex to implement and difficult for end user to follow and have control over. The complexities and potential solutions are outlined in the document, in details.

The simplest approach is to be able to allow user to configure and start protocol, then collect control plane parameters
like MPLS label, DHCP IPs etc. Finally these learned information can then be used to define and configure the sub-flows,
likely for a full mesh traffic endpoints. User can then append the traffic portion of the configuration and start the traffic.

Another use case is to provide flexibility to user to configure and run batches traffic streams one after another, after
removing the previous batch of streams for the same running protocol endpoints. Append config in conjunction with delete
config can be used as outlined in the examples below.

Note: This PR outlines how we would like to add model support for appending and deleting config of various type, but
for initial delivery we start with config type flow only.

Sample Code Snippet

config := gosnappi.NewConfig()
RsvpIngressTunnel := 10

// add ports
p1 := config.Ports().Add().SetName("p1").SetLocation(opts.IxiaCPorts()[0])
p2 := config.Ports().Add().SetName("p2").SetLocation(opts.IxiaCPorts()[1])

// add devices
d1 := config.Devices().Add().SetName("p1d1")
d2 := config.Devices().Add().SetName("p2d1")

// add protocol stacks for device d1
d1Eth1 := d1.Ethernets().
	Add().
	SetName("p1d1Eth1").
	SetMac("00:21:00:00:00:11").
	SetMtu(1450)

d1Eth1.
	Connection().
	SetPortName(p1.Name())

d1Eth1.
	Ipv4Addresses().
	Add().
	SetName("p1d1ipv4").
	SetAddress("21.1.1.1").
	SetGateway("21.1.1.2").
	SetPrefix(24)

// isis router
d1isis := d1.Isis().
	SetName("p1d1isis").
	SetSystemId("640000000001")

d1isis.Basic().SetIpv4TeRouterId("21.1.1.1")
d1isis.Basic().SetHostname("ixia-Ingress")
d1isis.Basic().SetEnableWideMetric(true)
d1isis.Basic().SetLearnedLspFilter(true)
d1isis.Advanced().SetAreaAddresses([]string{"490001"})
d1isis.Advanced().SetCsnpInterval(10000)
d1isis.Advanced().SetEnableHelloPadding(false)
d1isis.Advanced().SetLspLifetime(1200)
d1isis.Advanced().SetLspMgroupMinTransInterval(5000)
d1isis.Advanced().SetLspRefreshRate(900)
d1isis.Advanced().SetMaxAreaAddresses(3)
d1isis.Advanced().SetMaxLspSize(1000)
d1isis.Advanced().SetPsnpInterval(2000)
d1isis.Advanced().SetEnableAttachedBit(true)

//isis interface
d1isisint := d1isis.Interfaces().
	Add().
	SetName("p1d1int").
	SetEthName("p1d1Eth1").
	SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT).
	SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2).
	SetMetric(10)

//isis advanced settings
d1isisint.
	Advanced().SetAutoAdjustSupportedProtocols(false)
// RSVP
d1rsvp := d1.Rsvp().SetName("IxiaIngress")
d1rsvp.Ipv4Interfaces().
	Add().SetIpv4Name("p1d1ipv4").
	SetNeighborIp("21.1.1.2").
	SetBundleThreshold(50).
	SetEnableHello(false).
	SetEnableRefreshReduction(true).
	SetHelloInterval(9).
	SetLabelSpaceStart(1048500).
	SetLabelSpaceEnd(1048575).
	SetSendBundle(true).
	SetSummaryRefreshInterval(30).
	SetTimeoutMultiplier(3)

d1rsvpLsp := d1rsvp.LspIpv4Interfaces().Add().SetIpv4Name("p1d1ipv4")
// d1rsvpLsp.P2PEgressIpv4Lsps().SetName("Rsvp-EG-1")

for i := int32(1); i <= RsvpIngressTunnel; i++ {
	name := fmt.Sprint("Ingress_lsp", i)
	d1RsvpIngress := d1rsvpLsp.P2PIngressIpv4Lsps().Add()
	d1RsvpIngress.SetName(name)
	d1RsvpIngress.SetRemoteAddress("22.1.1.1").
		SetRefreshInterval(30).
		SetTimeoutMultiplier(3).
		SetTunnelId(uint32(i))
	d1RsvpIngress.Ero().SetPrependNeighborIp("prepend_loose")
	d1RsvpIngress.Ero().Subobjects().Add().SetType("ipv4").
		SetIpv4Address("22.1.1.1").
		SetPrefixLength(24).
		SetHopType("loose")

}
// add protocol stacks for device d2
d2Eth1 := d2.Ethernets().
	Add().
	SetName("p2d1Eth1").
	SetMac("22:00:00:00:00:12").
	SetMtu(1450)

d2Eth1.
	Connection().
	SetPortName(p2.Name())

d2Eth1.
	Ipv4Addresses().
	Add().
	SetName("p2d1ipv4").
	SetAddress("22.1.1.1").
	SetGateway("22.1.1.2").
	SetPrefix(24)

// isis router
d2isis := d2.Isis().
	SetName("p2d1isis").
	SetSystemId("650000000001")

d2isis.Basic().SetIpv4TeRouterId("22.1.1.1")
d2isis.Basic().SetHostname("ixia-Egress")
d2isis.Basic().SetEnableWideMetric(true)
d2isis.Basic().SetLearnedLspFilter(true)
d2isis.Advanced().SetAreaAddresses([]string{"490001"})
d2isis.Advanced().SetCsnpInterval(10000)
d2isis.Advanced().SetEnableHelloPadding(false)
d2isis.Advanced().SetLspLifetime(1200)
d2isis.Advanced().SetLspMgroupMinTransInterval(5000)
d2isis.Advanced().SetLspRefreshRate(900)
d2isis.Advanced().SetMaxAreaAddresses(3)
d2isis.Advanced().SetMaxLspSize(1000)
d2isis.Advanced().SetPsnpInterval(2000)
d2isis.Advanced().SetEnableAttachedBit(true)

//isis interface
d2isisint := d2isis.Interfaces().
	Add().
	SetName("p2d1int").
	SetEthName("p2d1Eth1").
	SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT).
	SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2).
	SetMetric(10)
//isis advanced settings
d2isisint.
	Advanced().SetAutoAdjustSupportedProtocols(false)

// RSVP
d2rsvp := d2.Rsvp().SetName("IxiaEgress")
d2rsvp.Ipv4Interfaces().
	Add().SetIpv4Name("p2d1ipv4").
	SetNeighborIp("22.1.1.2").
	SetBundleThreshold(50).
	SetEnableHello(false).
	SetEnableRefreshReduction(true).
	SetHelloInterval(9).
	SetLabelSpaceStart(10).
	SetLabelSpaceEnd(900).
	SetSendBundle(true).
	SetSummaryRefreshInterval(30).
	SetTimeoutMultiplier(3)

d2rsvpLsp := d2rsvp.LspIpv4Interfaces().Add().SetIpv4Name("p2d1ipv4")
d2RsvpEgress := d2rsvpLsp.P2PEgressIpv4Lsps()

d2RsvpEgress.SetName("Rsvp-EG-2").
	SetEnableFixedLabel(false).
	SetRefreshInterval(30).
	SetReservationStyle("fixed_filter").
	SetTimeoutMultiplier(3)

flowCount := int(RsvpIngressTunnel)
if _, err := client.SetConfig(config); err != nil {
	t.Fatal(err)
}
if err := client.StartProtocol(); err != nil {
	t.Fatal(err)
}

//ISIS metrics
err = api.WaitFor(
	func() (bool, error) {
		return client.AllIsisSessionUp(config, gosnappi.IsisInterfaceLevelType.LEVEL_2, 3)
	}, opts, nil,
)

reqLabel := gosnappi.NewStatesRequest()
reqLabel.RsvpLsps().SetRsvpRouterNames([]string{"IxiaIngress"})
resLabel, err := client.GetStates(reqLabel)
if err != nil {
	t.Fatal(err)
}
learnedLabels := []uint32{}
for _, rsvpLsp := range resLabel.Items() {
	if rsvpLsp.RsvpRouterName() == "IxiaIngress" {
		for _, lsp := range rsvpLsp.Ipv4Lsps.Items() {
			if lsp.SourceAddress() == "21.1.1.1" && lsp.DestinationAddress() == "22.1.1.1" {
				learnedLabels[lsp.TunnelId()] = lsp.LabelIn()
			}
		}
	}
}
// misc error checking and learned info data integrity goes here

// reset initial config and specifically add traffic details only
ca := gosnappi.NewConfigAppend()
ai := ca.Add()
flow := ai.Flows().Add()
flow.Metrics().SetEnable(true)
flow.Duration().FixedPackets().SetPackets(500)
flow.Rate().SetPps(10)

// add endpoints and packet description flow
flow.SetName("f1").
	TxRx().Device().
	SetTxNames([]string{d1Eth1.Name()}).
	SetRxNames([]string{d2RsvpEgress.Name()})

f1Eth := flow.Packet().Add().Ethernet()
f1Eth.Src().SetValue(d1Eth1.Mac())
f1Eth.Dst().Auto()

f1Mpls := flow.Packet().Add().Mpls()
f1Mpls.Label().SetValues(learnedLabels)

f1Ip := flow.Packet().Add().Ipv4()
f1Ip.Src().SetValue("21.1.1.1")
f1Ip.Dst().SetValue("22.1.1.1")


if _, err := client.AppendConfig(ca); err != nil {
	t.Fatal(err)
}
if err := client.StartTransmit(); err != nil {
	t.Fatal(err)
}

// After traffic metrics measurement and verification, we remove streams
if err := client.StopTransmit(); err != nil {
	t.Fatal(err)
}

cd= gosnappi.NewConfigDelete()
cd.Add().SetFlows([]string{"f1"}
if _, err := client.DeleteConfig(cd); err != nil {
	t.Fatal(err)
}

// Now we can reconfigure and start a new stream
ca = gosnappi.NewConfigAppend()
ai = ca.Add()
flow := ai.Flows().Add()
flow.Metrics().SetEnable(true)
flow.Duration().FixedPackets().SetPackets(500)
flow.Rate().SetPps(10)

// add endpoints and packet description flow
flow.SetName("f1").
	TxRx().Device().
	SetTxNames([]string{d2RsvpEgress.Name()}).
	SetRxNames([]string{d1Eth1.Name()})

f1Eth := flow.Packet().Add().Ethernet()
f1Eth.Src().SetValue(d2Eth1.Mac())
f1Eth.Dst().Auto()

f1Ip := flow.Packet().Add().Ipv4()
f1Ip.Src().SetValue("22.1.1.1")
f1Ip.Dst().SetValue("21.1.1.1")

if _, err := client.AppendConfig(ca); err != nil {
	t.Fatal(err)
}
if err := client.StartTransmit(); err != nil {
	t.Fatal(err)
}
.

items:
$ref: '../device/device.yaml#/components/schemas/Device'
x-field-uid: 2
ethernets:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is only ethernets? why not IPs too as basic & essential Ethernet & IP for most protocols to run. Or once new device append control is given, why append ethernets to be exposed separately?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is only ethernets?
This is just provided as an example on how this model could potentially handle addition of new config items at any level in the config which was missing earlier. This could be BGP peer or ISIS route range or even a VLAN, depending on the requirements for addition of config outside of flows. ( These need a parent ) .
Or once new device append control is given, why append ethernets to be exposed separately?
Since it is very possible I want to add another emulated interface on a device which is already created just like I might want to add another 10000 route replays within a BGP peer ; use-cases can come up at any level . I could also need to add a new port altogether. Attempt here is to have a model which can handle all of these unknown future needs for device configurations or capture or anything else in a generic form.
Note: Final PR ( probably a different PR with link to this for showing how generic add/delete can be done with this design ) for external review/merge/implementation will only have choice of adding/deleting flows.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was added as an example of how we can extend the support for other resource type,

@@ -0,0 +1,80 @@
components:
schemas:
Config.Append:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be Append/Add.Config is more matching with subsequent "AppendXxx"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added AppendResource node (reference from Append node)

@apratimmukherjee apratimmukherjee changed the title Dev multi endpoint Dynamically add/delete flows or protocol elements to existing applied configuration. Apr 10, 2025
Copy link
Contributor

@apratimmukherjee apratimmukherjee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants