Skip to content

Commit

Permalink
[TEST]: Improvement: Switch: New interaction approach
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuliia Miroshnychenko committed Jan 17, 2025
1 parent f16cf57 commit c5758c8
Show file tree
Hide file tree
Showing 23 changed files with 2,148 additions and 1,033 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.openkilda.functionaltests.helpers

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component

@Component
class KildaProperties {

public static int DISCOVERY_EXHAUSTED_INTERVAL
public static int ANTIFLAP_MIN
public static int ANTIFLAP_COOLDOWN
public static int DISCOVERY_TIMEOUT
public static double BURST_COEFFICIENT
public static String TOPO_DISCO_TOPIC
public static Properties PRODUCER_PROPS

@Autowired
KildaProperties( @Value('${discovery.exhausted.interval}') int discoveryExhaustedInterval,
@Value('${antiflap.min}') int antiflapMin,
@Value('${antiflap.cooldown}') int antiflapCooldown,
@Value('${discovery.timeout}') int discoveryTimeout,
@Value('${burst.coefficient}') double burstCoefficient,
@Autowired @Qualifier("kafkaProducerProperties") Properties producerProps,
@Value("#{kafkaTopicsConfig.getTopoDiscoTopic()}") String topoDiscoTopic) {

DISCOVERY_EXHAUSTED_INTERVAL = discoveryExhaustedInterval
ANTIFLAP_MIN = antiflapMin
ANTIFLAP_COOLDOWN = antiflapCooldown
DISCOVERY_TIMEOUT = discoveryTimeout
BURST_COEFFICIENT = burstCoefficient
TOPO_DISCO_TOPIC = topoDiscoTopic
PRODUCER_PROPS = producerProps

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import static org.springframework.beans.factory.config.ConfigurableBeanFactory.S

import org.openkilda.functionaltests.helpers.builder.FlowBuilder
import org.openkilda.functionaltests.helpers.model.FlowExtended
import org.openkilda.functionaltests.helpers.model.SwitchExtended
import org.openkilda.functionaltests.helpers.model.SwitchPair
import org.openkilda.functionaltests.helpers.model.SwitchPortVlan
import org.openkilda.functionaltests.model.cleanup.CleanupManager
Expand Down Expand Up @@ -52,6 +53,14 @@ class FlowFactory {
return new FlowBuilder(srcSwitch, dstSwitch, northbound, northboundV2, topology, cleanupManager, database, useTraffgenPorts, busyEndpoints)
}

FlowBuilder getBuilder(SwitchExtended srcSwitch, SwitchExtended dstSwitch, boolean useTraffgenPorts = true, List<SwitchPortVlan> busyEndpoints = []) {
getBuilder(srcSwitch.sw, dstSwitch.sw, useTraffgenPorts, busyEndpoints)
}

FlowBuilder getSingleSwBuilder(SwitchExtended srcSwitch, boolean useTraffgenPorts = true, List<SwitchPortVlan> busyEndpoints = []) {
getBuilder(srcSwitch.sw, srcSwitch.sw, useTraffgenPorts, busyEndpoints)
}

/*
This method allows random Flow creation on specified switches and waits for it
to become UP by default or to be in an expected state.
Expand All @@ -66,6 +75,16 @@ class FlowFactory {
return getBuilder(srcSwitch, dstSwitch, useTraffgenPorts, busyEndpoints).build().create(expectedFlowState)
}

FlowExtended getRandom(SwitchExtended srcSwitch, SwitchExtended dstSwitch, boolean useTraffgenPorts = true, FlowState expectedFlowState = UP,
List<SwitchPortVlan> busyEndpoints = []) {
getRandom(srcSwitch.sw, dstSwitch.sw)
}

FlowExtended getSingleSwRandom(SwitchExtended srcSwitch, boolean useTraffgenPorts = true, FlowState expectedFlowState = UP,
List<SwitchPortVlan> busyEndpoints = []) {
getRandom(srcSwitch.sw, srcSwitch.sw)
}

FlowExtended getRandomV1(Switch srcSwitch, Switch dstSwitch, boolean useTraffgenPorts = true, FlowState expectedFlowState = UP,
List<SwitchPortVlan> busyEndpoints = []) {
return getBuilder(srcSwitch, dstSwitch, useTraffgenPorts, busyEndpoints).build().createV1(expectedFlowState)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.openkilda.functionaltests.helpers.factory

import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE

import org.openkilda.functionaltests.helpers.model.SwitchExtended
import org.openkilda.functionaltests.model.cleanup.CleanupManager
import org.openkilda.testing.model.topology.TopologyDefinition
import org.openkilda.testing.model.topology.TopologyDefinition.Switch
import org.openkilda.testing.service.database.Database
import org.openkilda.testing.service.lockkeeper.LockKeeperService
import org.openkilda.testing.service.northbound.NorthboundService
import org.openkilda.testing.service.northbound.NorthboundServiceV2

import groovy.util.logging.Slf4j
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.annotation.Scope
import org.springframework.stereotype.Component

@Slf4j
@Component
@Scope(SCOPE_PROTOTYPE)
class SwitchFactory {

@Autowired @Qualifier("islandNb")
NorthboundService northbound
@Autowired @Qualifier("islandNbV2")
NorthboundServiceV2 northboundV2
@Autowired
TopologyDefinition topology
@Autowired
Database database
@Autowired
LockKeeperService lockKeeper
@Autowired
CleanupManager cleanupManager

SwitchExtended get(Switch sw) {
List<Integer> islPorts = []
topology.getRelatedIsls(sw).each {
it?.srcSwitch?.dpId != sw.dpId ?: islPorts.add(it.srcPort)
it?.dstSwitch?.dpId != sw.dpId ?: islPorts.add(it.dstPort)
}
List<Integer> traffGen = topology.traffGens.findAll{ it.switchConnected.dpId == sw.dpId }.switchPort
return new SwitchExtended(sw, islPorts, traffGen,
northbound, northboundV2, database, lockKeeper, cleanupManager)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.openkilda.functionaltests.helpers.model

import static org.openkilda.functionaltests.model.cleanup.CleanupActionType.DELETE_LAG_LOGICAL_PORT

import org.openkilda.functionaltests.model.cleanup.CleanupManager
import org.openkilda.model.SwitchId
import org.openkilda.northbound.dto.v2.switches.LagPortRequest
import org.openkilda.testing.service.northbound.NorthboundServiceV2

import com.fasterxml.jackson.annotation.JsonIgnore
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.util.logging.Slf4j

@Slf4j
@EqualsAndHashCode(excludes = 'northboundV2, cleanupManager')
@ToString(includeNames = true, excludes = 'northboundV2, cleanupManager')
class LagPort {

SwitchId switchId
Set<Integer> portNumbers
int logicalPortNumber
boolean lacpReply

@JsonIgnore
NorthboundServiceV2 northboundV2
@JsonIgnore
CleanupManager cleanupManager

LagPort(SwitchId switchId,
Set<Integer> portNumbers,
NorthboundServiceV2 northboundV2,
CleanupManager cleanupManager) {
this.switchId = switchId
this.portNumbers = portNumbers
this.northboundV2 = northboundV2
this.cleanupManager = cleanupManager
}

LagPort(SwitchId switchId,
int logicalPortNumber,
List<Integer> portNumbers,
boolean lacpReply,
NorthboundServiceV2 northboundV2,
CleanupManager cleanupManager) {
this.switchId = switchId
this.logicalPortNumber = logicalPortNumber
this.lacpReply = lacpReply
this.portNumbers = portNumbers
this.northboundV2 = northboundV2
this.cleanupManager = cleanupManager
}


LagPort create(boolean lacpReply = null) {
def lagDetails = northboundV2.createLagLogicalPort(switchId, new LagPortRequest(portNumbers , lacpReply))
cleanupManager.addAction(DELETE_LAG_LOGICAL_PORT, { northboundV2.deleteLagLogicalPort(switchId, lagDetails.logicalPortNumber) })
new LagPort(switchId, lagDetails.logicalPortNumber, lagDetails.portNumbers, lagDetails.lacpReply, northboundV2, cleanupManager)
}

LagPort update(LagPortRequest updateRequest) {
def lagDetails = northboundV2.updateLagLogicalPort(switchId, logicalPortNumber, updateRequest)
new LagPort(switchId, lagDetails.logicalPortNumber, lagDetails.portNumbers, lagDetails.lacpReply, northboundV2, cleanupManager)
}

LagPort delete() {
def lagDetails = northboundV2.deleteLagLogicalPort(switchId, logicalPortNumber)
new LagPort(switchId, lagDetails.logicalPortNumber, lagDetails.portNumbers, lagDetails.lacpReply, northboundV2, cleanupManager)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package org.openkilda.functionaltests.helpers.model

import static org.openkilda.functionaltests.model.cleanup.CleanupActionType.PORT_UP
import static org.openkilda.functionaltests.model.cleanup.CleanupActionType.RESTORE_ISL
import static org.openkilda.functionaltests.model.cleanup.CleanupAfter.TEST
import static org.openkilda.testing.Constants.WAIT_OFFSET

import org.openkilda.functionaltests.helpers.KildaProperties
import org.openkilda.functionaltests.helpers.Wrappers
import org.openkilda.functionaltests.helpers.thread.PortBlinker
import org.openkilda.functionaltests.model.cleanup.CleanupAfter
import org.openkilda.functionaltests.model.cleanup.CleanupManager
import org.openkilda.messaging.info.switches.PortDescription
import org.openkilda.model.SwitchId
import org.openkilda.northbound.dto.v2.switches.PortPropertiesDto
import org.openkilda.testing.model.topology.TopologyDefinition.Switch
import org.openkilda.testing.service.northbound.NorthboundService
import org.openkilda.testing.service.northbound.NorthboundServiceV2

import com.fasterxml.jackson.annotation.JsonIgnore
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.util.logging.Slf4j

@Slf4j
@EqualsAndHashCode(excludes = 'northbound, northboundV2, cleanupManager')
@ToString(includeNames = true, excludes = 'northbound, northboundV2, cleanupManager')
class PortExtended {

Switch sw
Integer port

@JsonIgnore
NorthboundService northbound
@JsonIgnore
NorthboundServiceV2 northboundV2
@JsonIgnore
CleanupManager cleanupManager

Map<Tuple2<SwitchId, Integer>, Long> history = [:]

PortExtended(Switch sw,
Integer portNumber,
NorthboundService northbound,
NorthboundServiceV2 northboundV2,
CleanupManager cleanupManager) {
this.sw = sw
this.port = portNumber
this.northbound = northbound
this.northboundV2 = northboundV2
this.cleanupManager = cleanupManager
}

def up() {
def swPort = new Tuple2(sw.dpId, port)
def lastEvent = history.get(swPort)
if (lastEvent) {
Wrappers.silent { //Don't fail hard on this check. In rare cases we may miss the history entry
waitForStabilization(lastEvent)
}
history.remove(swPort)
}
northbound.portUp(sw.dpId, port)
}

def safeUp() {
if (northbound.getPort(sw.dpId, port).getState().first() != "LIVE") {
up()
}
Wrappers.wait(WAIT_OFFSET) {
assert northbound.getActiveLinks().findAll {
it.source.switchId == sw.dpId && it.source.portNo == port ||
it.destination.switchId == sw.dpId && it.destination.portNo == port
}.size() == 2
}
}

def down(CleanupAfter cleanupAfter = TEST, boolean isNotInScopeOfIslBreak = true) {
if (isNotInScopeOfIslBreak) {
cleanupManager.addAction(PORT_UP, { safeUp() }, cleanupAfter)
}
def response = northbound.portDown(sw.dpId, port)
sleep(KildaProperties.ANTIFLAP_MIN * 1000)
history.put(new Tuple2(sw.dpId, port), System.currentTimeMillis())
response
}

/**
* Wait till the current port is in a stable state (deactivated antiflap) by analyzing its history.
*/
void waitForStabilization(Long since = 0) {
// '* 2' it takes more time on a hardware env for link via 'a-switch'
Wrappers.wait(KildaProperties.ANTIFLAP_COOLDOWN + WAIT_OFFSET * 2) {
def history = northboundV2.getPortHistory(sw.dpId, port, since, null)

if (!history.empty) {
def antiflapEvents = history.collect { PortHistoryEvent.valueOf(it.event) }.findAll {
it in [PortHistoryEvent.ANTI_FLAP_ACTIVATED, PortHistoryEvent.ANTI_FLAP_DEACTIVATED]
}

if (!antiflapEvents.empty) {
assert antiflapEvents.last() == PortHistoryEvent.ANTI_FLAP_DEACTIVATED
} else {
false
}
} else {
false
}
}
}

def setDiscovery(boolean expectedStatus) {
if (!expectedStatus) {
cleanupManager.addAction(RESTORE_ISL, { setDiscovery(true) })
}
return northboundV2.updatePortProperties(sw.dpId, port, new PortPropertiesDto(discoveryEnabled: expectedStatus))
}

PortBlinker getBlinker(long interval, Properties producerProps) {
new PortBlinker(KildaProperties.PRODUCER_PROPS, KildaProperties.TOPO_DISCO_TOPIC, sw, port, interval)
}

static def closeBlinker(PortBlinker blinker) {
blinker?.isRunning() && blinker.stop(true)
}

PortDescription retrieveDetails() {
northbound.getPort(sw.dpId, port)
}
}
Loading

0 comments on commit c5758c8

Please sign in to comment.